Merge branch 'master' into 137-xenforo-support

Conflicts:
	src/main/java/fr/xephi/authme/datasource/CacheDataSource.java
	src/main/java/fr/xephi/authme/datasource/DataSource.java
	src/main/java/fr/xephi/authme/datasource/SQLite.java
	src/main/java/fr/xephi/authme/security/crypts/BCRYPT.java
This commit is contained in:
DNx5 2016-01-06 12:15:27 +07:00
commit e0c3affa33
49 changed files with 1727 additions and 1577 deletions

1461
pom.xml

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
package fr.xephi.authme;
import com.earth2me.essentials.Essentials;
import com.google.common.base.Charsets;
import com.google.common.io.Resources;
import com.onarandombox.MultiverseCore.MultiverseCore;
import fr.xephi.authme.api.API;
import fr.xephi.authme.api.NewAPI;
@ -30,7 +32,6 @@ import fr.xephi.authme.listener.AuthMePlayerListener;
import fr.xephi.authme.listener.AuthMePlayerListener16;
import fr.xephi.authme.listener.AuthMePlayerListener18;
import fr.xephi.authme.listener.AuthMeServerListener;
import fr.xephi.authme.listener.AuthMeServerStop;
import fr.xephi.authme.listener.AuthMeTabCompletePacketAdapter;
import fr.xephi.authme.mail.SendMailSSL;
import fr.xephi.authme.modules.ModuleManager;
@ -66,11 +67,8 @@ import org.bukkit.scheduler.BukkitTask;
import org.mcstats.Metrics;
import org.mcstats.Metrics.Graph;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
@ -133,6 +131,7 @@ public class AuthMe extends JavaPlugin {
/**
* Get the plugin's instance.
*
* @return AuthMe
*/
public static AuthMe getInstance() {
@ -141,6 +140,7 @@ public class AuthMe extends JavaPlugin {
/**
* Get the plugin's name.
*
* @return The plugin's name.
*/
public static String getPluginName() {
@ -149,6 +149,7 @@ public class AuthMe extends JavaPlugin {
/**
* Get the plugin's version.
*
* @return The plugin's version.
*/
public static String getPluginVersion() {
@ -157,6 +158,7 @@ public class AuthMe extends JavaPlugin {
/**
* Get the plugin's build number.
*
* @return The plugin's build number.
*/
public static String getPluginBuildNumber() {
@ -165,6 +167,7 @@ public class AuthMe extends JavaPlugin {
/**
* Get the plugin's Settings.
*
* @return Plugin's settings.
*/
public Settings getSettings() {
@ -173,6 +176,7 @@ public class AuthMe extends JavaPlugin {
/**
* Get the Messages instance.
*
* @return Plugin's messages.
*/
public Messages getMessages() {
@ -272,7 +276,6 @@ public class AuthMe extends JavaPlugin {
new PerformBackup(plugin).doBackup(PerformBackup.BackupCause.START);
// Setup the inventory backup
playerBackup = new JsonCache();
@ -303,13 +306,6 @@ public class AuthMe extends JavaPlugin {
// Show settings warnings
showSettingsWarnings();
// Register a server shutdown hook
try {
Runtime.getRuntime().addShutdownHook(new AuthMeServerStop(this));
} catch (Exception e){
e.printStackTrace();
}
// Sponsor messages
ConsoleLogger.info("AuthMe hooks perfectly with the VeryGames server hosting!");
ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt.");
@ -435,7 +431,7 @@ public class AuthMe extends JavaPlugin {
// Set up the API
api = new NewAPI(this);
// Setup the old deprecated API
// Set up the deprecated API
new API(this);
}
@ -517,10 +513,10 @@ public class AuthMe extends JavaPlugin {
public void onDisable() {
// Save player data
Collection<? extends Player> players = Utils.getOnlinePlayers();
if (players != null) {
for (Player player : players) {
this.savePlayer(player);
}
for (Player player : players) {
savePlayer(player);
// TODO: add a MessageKey
player.kickPlayer("Server is restarting or AuthMe plugin was disabled.");
}
// Do backup on stop if enabled
@ -593,16 +589,16 @@ public class AuthMe extends JavaPlugin {
// TODO: Move this to another place maybe ?
if (Settings.getPasswordHash == HashAlgorithm.PLAINTEXT) {
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated; " +
ConsoleLogger.showError("Your HashAlgorithm has been detected as plaintext and is now deprecated; " +
"it will be changed and hashed now to the AuthMe default hashing method");
for (PlayerAuth auth : database.getAllAuths()) {
for (PlayerAuth auth : database.getAllAuths()) {
HashedPassword hashedPassword = passwordSecurity.computeHash(
HashAlgorithm.SHA256, auth.getPassword().getHash(), auth.getNickname());
auth.setPassword(hashedPassword);
database.updatePassword(auth);
}
Settings.setValue("settings.security.passwordHash", "SHA256");
Settings.reload();
database.updatePassword(auth);
}
Settings.setValue("settings.security.passwordHash", "SHA256");
Settings.reload();
}
if (Settings.isCachingEnabled) {
@ -715,10 +711,9 @@ public class AuthMe extends JavaPlugin {
inventoryProtector = null;
}
}
if (tabComplete == null)
{
tabComplete = new AuthMeTabCompletePacketAdapter(this);
tabComplete.register();
if (tabComplete == null) {
tabComplete = new AuthMeTabCompletePacketAdapter(this);
tabComplete.register();
}
}
@ -889,50 +884,34 @@ public class AuthMe extends JavaPlugin {
* Gets a player's real IP through VeryGames method.
*
* @param player The player to process.
*
*/
@Deprecated
public void getVerygamesIp(final Player player) {
final String name = player.getName().toLowerCase();
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable(){
@Override
public void run() {
String realIP = player.getAddress().getAddress().getHostAddress();
if (realIp.containsKey(name))
realIP = realIp.get(name);
String sUrl = "http://monitor-1.verygames.net/api/?action=ipclean-real-ip&out=raw&ip=%IP%&port=%PORT%";
sUrl = sUrl.replace("%IP%", realIP)
.replace("%PORT%", "" + player.getAddress().getPort());
try {
URL url = new URL(sUrl);
URLConnection urlCon = url.openConnection();
urlCon.setConnectTimeout(5000);
urlCon.setReadTimeout(5000);
try (BufferedReader in = new BufferedReader(new InputStreamReader(urlCon.getInputStream()))) {
String inputLine = in.readLine();
if (!StringUtils.isEmpty(inputLine) && !inputLine.equalsIgnoreCase("error")
&& !inputLine.contains("error")) {
realIP = inputLine;
}
} catch (IOException e) {
ConsoleLogger.showError("Could not read from Very Games API - " + StringUtils.formatException(e));
}
} catch (IOException e) {
ConsoleLogger.showError("Could not fetch Very Games API with URL '" + sUrl + "' - "
+ StringUtils.formatException(e));
}
if (realIp.containsKey(name))
realIp.remove(name);
realIp.putIfAbsent(name, realIP);
}
});
final String name = player.getName().toLowerCase();
String currentIp = player.getAddress().getAddress().getHostAddress();
if (realIp.containsKey(name)) {
currentIp = realIp.get(name);
}
String sUrl = "http://monitor-1.verygames.net/api/?action=ipclean-real-ip&out=raw&ip=%IP%&port=%PORT%";
sUrl = sUrl.replace("%IP%", currentIp).replace("%PORT%", "" + player.getAddress().getPort());
try {
String result = Resources.toString(new URL(sUrl), Charsets.UTF_8);
if (!StringUtils.isEmpty(result) && !result.equalsIgnoreCase("error") && !result.contains("error")) {
currentIp = result;
realIp.put(name, currentIp);
}
} catch (IOException e) {
ConsoleLogger.showError("Could not fetch Very Games API with URL '" +
sUrl + "' - " + StringUtils.formatException(e));
}
}
public String getIP(final Player player) {
final String name = player.getName().toLowerCase();
String ip = player.getAddress().getAddress().getHostAddress();
if (realIp.containsKey(name))
if (realIp.containsKey(name)) {
ip = realIp.get(name);
}
return ip;
}
@ -983,6 +962,8 @@ public class AuthMe extends JavaPlugin {
/**
* Return the management instance.
*
* @return management The Management
*/
public Management getManagement() {
return management;

View File

@ -1,12 +1,5 @@
package fr.xephi.authme;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.List;
import java.util.concurrent.Callable;
@ -14,6 +7,14 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.Utils;
/**
*/
public class DataManager {
@ -65,7 +66,7 @@ public class DataManager {
/**
* Method purgeAntiXray.
*
* @param cleared List<String>
* @param cleared List of String
*/
public synchronized void purgeAntiXray(List<String> cleared) {
int i = 0;
@ -90,7 +91,7 @@ public class DataManager {
/**
* Method purgeLimitedCreative.
*
* @param cleared List<String>
* @param cleared List of String
*/
public synchronized void purgeLimitedCreative(List<String> cleared) {
int i = 0;
@ -127,7 +128,7 @@ public class DataManager {
/**
* Method purgeDat.
*
* @param cleared List<String>
* @param cleared List of String
*/
public synchronized void purgeDat(List<String> cleared) {
int i = 0;
@ -160,7 +161,7 @@ public class DataManager {
/**
* Method purgeEssentials.
*
* @param cleared List<String>
* @param cleared List of String
*/
@SuppressWarnings("deprecation")
public void purgeEssentials(List<String> cleared) {

View File

@ -1,15 +1,16 @@
package fr.xephi.authme.api;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
/**
* The current API of AuthMe.
@ -76,7 +77,7 @@ public class NewAPI {
}
/**
* @param player
* @param player a Player
*
* @return true if player is a npc
*/
@ -85,7 +86,7 @@ public class NewAPI {
}
/**
* @param player
* @param player a Player
*
* @return true if the player is unrestricted
*/

View File

@ -1,11 +1,12 @@
package fr.xephi.authme.cache.auth;
import fr.xephi.authme.security.crypts.HashedPassword;
import org.bukkit.Location;
import static com.google.common.base.Objects.firstNonNull;
import static com.google.common.base.Preconditions.checkNotNull;
import org.bukkit.Location;
import fr.xephi.authme.security.crypts.HashedPassword;
/**
*/
@ -24,7 +25,7 @@ public class PlayerAuth {
private String realName;
/**
*
* @param serialized String
*/
public PlayerAuth(String serialized) {
this.deserialize(serialized);
@ -340,6 +341,8 @@ public class PlayerAuth {
/**
* Method to deserialize PlayerAuth
*
* @param str String
*/
public void deserialize(String str) {
String[] args = str.split(";");

View File

@ -12,12 +12,13 @@ import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Arrays.asList;
/**
* Command description - defines which labels ("names") will lead to a command and points to the
* Command description defines which labels ("names") will lead to a command and points to the
* {@link ExecutableCommand} implementation that executes the logic of the command.
*
* CommandDescription instances are built hierarchically and have one parent or {@code null} for base commands
* (main commands such as /authme) and may have multiple children extending the mapping of the parent: e.g. if
* /authme has a child whose label is "register", then "/authme register" is the command that the child defines.
* CommandDescription instances are built hierarchically: they have one parent, or {@code null} for base commands
* (main commands such as {@code /authme}), and may have multiple children extending the mapping of the parent: e.g. if
* {@code /authme} has a child whose label is {@code "register"}, then {@code /authme register} is the command that
* the child defines.
*/
public class CommandDescription {
@ -102,10 +103,11 @@ public class CommandDescription {
}
/**
* Get all relative labels of this command. For example, if this object describes "/authme register" and
* "/authme r", then "r" and "register" are the relative labels, whereas "authme" is the label of the parent.
* Return all relative labels of this command. For example, if this object describes {@code /authme register} and
* {@code /authme r}, then it will return a list with {@code register} and {@code r}. The parent label
* {@code authme} is not returned.
*
* @return All relative labels.
* @return All labels of the command description.
*/
public List<String> getLabels() {
return labels;

View File

@ -1,11 +1,12 @@
package fr.xephi.authme.command;
import fr.xephi.authme.util.StringUtils;
import org.bukkit.command.CommandSender;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.command.CommandSender;
import fr.xephi.authme.util.StringUtils;
/**
* The AuthMe command handler, responsible for mapping incoming commands to the correct {@link CommandDescription}
* or to display help messages for unknown invocations.
@ -16,6 +17,8 @@ public class CommandHandler {
/**
* Create a command handler.
*
* @param commandService The CommandService instance
*/
public CommandHandler(CommandService commandService) {
this.commandService = commandService;
@ -45,6 +48,12 @@ public class CommandHandler {
return !FoundResultStatus.MISSING_BASE_COMMAND.equals(result.getResultStatus());
}
/**
* Execute the command for the given command sender.
*
* @param sender The sender which initiated the command
* @param result The mapped result
*/
private void executeCommand(CommandSender sender, FoundCommandResult result) {
ExecutableCommand executableCommand = result.getCommandDescription().getExecutableCommand();
List<String> arguments = result.getArguments();

View File

@ -1,5 +1,9 @@
package fr.xephi.authme.command;
import java.util.List;
import org.bukkit.command.CommandSender;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.command.help.HelpProvider;
import fr.xephi.authme.datasource.DataSource;
@ -8,9 +12,6 @@ import fr.xephi.authme.output.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.security.PasswordSecurity;
import org.bukkit.command.CommandSender;
import java.util.List;
/**
* Service for implementations of {@link ExecutableCommand} to execute some common tasks.
@ -31,6 +32,7 @@ public class CommandService {
* @param commandMapper Command mapper
* @param helpProvider Help provider
* @param messages Messages instance
* @param passwordSecurity The Password Security instance
*/
public CommandService(AuthMe authMe, CommandMapper commandMapper, HelpProvider helpProvider, Messages messages,
PasswordSecurity passwordSecurity) {
@ -51,6 +53,13 @@ public class CommandService {
messages.send(sender, messageKey);
}
/**
* Send a message to a player.
*
* @param sender The command sender to send the message to
* @param messageKey The message key to send
* @param replacements The replacement arguments for the message key's tags
*/
public void send(CommandSender sender, MessageKey messageKey, String... replacements) {
messages.send(sender, messageKey, replacements);
}
@ -119,7 +128,7 @@ public class CommandService {
}
/**
* Returns the management instance of the plugin.
* Return the management instance of the plugin.
*
* @return The Management instance linked to the AuthMe instance
*/
@ -127,11 +136,22 @@ public class CommandService {
return authMe.getManagement();
}
/**
* Return the permissions manager.
*
* @return the permissions manager
*/
public PermissionsManager getPermissionsManager() {
// TODO ljacqu 20151226: Might be nicer to pass the perm manager via constructor
return authMe.getPermissionsManager();
}
/**
* Retrieve a message by its message key.
*
* @param key The message to retrieve
* @return The message
*/
public String[] retrieveMessage(MessageKey key) {
return messages.retrieve(key);
}

View File

@ -5,7 +5,7 @@ import java.util.List;
/**
* Result of a command mapping by {@link CommandHandler}. An object of this class represents a successful mapping
* as well as erroneous ones, as communicated with {@link FoundResultStatus}.
* <p />
* <p>
* Fields other than {@link FoundResultStatus} are available depending, among other factors, on the status:
* <ul>
* <li>{@link FoundResultStatus#SUCCESS} entails that mapping the input to a command was successful. Therefore,

View File

@ -1,10 +1,10 @@
package fr.xephi.authme.command;
import java.util.List;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.List;
/**
* Common base type for player-only commands, handling the verification that the command sender is indeed a player.
*/
@ -35,7 +35,7 @@ public abstract class PlayerCommand implements ExecutableCommand {
/**
* Return an alternative command (textual representation) that is not restricted to players only.
* Example: "authme register &lt;playerName> &lt;password>"
* Example: {@code "authme register <playerName> <password>"}
*
* @return Alternative command not only for players, or null if not applicable
*/

View File

@ -7,6 +7,7 @@ import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalListeners;
import com.google.common.cache.RemovalNotification;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.cache.auth.PlayerCache;
import fr.xephi.authme.security.crypts.HashedPassword;
@ -52,15 +53,6 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method isAuthAvailable.
*
* @param user String
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#isAuthAvailable(String)
*/
@Override
public synchronized boolean isAuthAvailable(String user) {
return getAuth(user) != null;
@ -91,15 +83,6 @@ public class CacheDataSource implements DataSource {
return cachedAuths.getUnchecked(user).orNull();
}
/**
* Method saveAuth.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#saveAuth(PlayerAuth)
*/
@Override
public synchronized boolean saveAuth(PlayerAuth auth) {
boolean result = source.saveAuth(auth);
@ -109,15 +92,6 @@ public class CacheDataSource implements DataSource {
return result;
}
/**
* Method updatePassword.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updatePassword(PlayerAuth)
*/
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
boolean result = source.updatePassword(auth);
@ -137,15 +111,6 @@ public class CacheDataSource implements DataSource {
return result;
}
/**
* Method updateSession.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateSession(PlayerAuth)
*/
@Override
public boolean updateSession(PlayerAuth auth) {
boolean result = source.updateSession(auth);
@ -155,47 +120,20 @@ public class CacheDataSource implements DataSource {
return result;
}
/**
* Method updateQuitLoc.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateQuitLoc(PlayerAuth)
*/
@Override
public boolean updateQuitLoc(final PlayerAuth auth) {
boolean result = source.updateSession(auth);
boolean result = source.updateQuitLoc(auth);
if (result) {
cachedAuths.refresh(auth.getNickname());
}
return result;
}
/**
* Method getIps.
*
* @param ip String
*
* @return int
*
* @see fr.xephi.authme.datasource.DataSource#getIps(String)
*/
@Override
public int getIps(String ip) {
return source.getIps(ip);
}
/**
* Method purgeDatabase.
*
* @param until long
*
* @return int
*
* @see fr.xephi.authme.datasource.DataSource#purgeDatabase(long)
*/
@Override
public int purgeDatabase(long until) {
int cleared = source.purgeDatabase(until);
@ -209,15 +147,6 @@ public class CacheDataSource implements DataSource {
return cleared;
}
/**
* Method autoPurgeDatabase.
*
* @param until long
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override
public List<String> autoPurgeDatabase(long until) {
List<String> cleared = source.autoPurgeDatabase(until);
@ -227,15 +156,6 @@ public class CacheDataSource implements DataSource {
return cleared;
}
/**
* Method removeAuth.
*
* @param name String
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#removeAuth(String)
*/
@Override
public synchronized boolean removeAuth(String name) {
name = name.toLowerCase();
@ -246,22 +166,17 @@ public class CacheDataSource implements DataSource {
return result;
}
/**
* Method close.
*
* @see fr.xephi.authme.datasource.DataSource#close()
*/
@Override
public synchronized void close() {
exec.shutdown();
try {
exec.shutdown();
exec.awaitTermination(8, TimeUnit.SECONDS);
} catch (InterruptedException e) {
ConsoleLogger.writeStackTrace(e);
}
source.close();
}
/**
* Method reload.
*
* @see fr.xephi.authme.datasource.DataSource#reload()
*/
@Override
public void reload() { // unused method
exec.execute(new Runnable() {
@ -273,15 +188,6 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method updateEmail.
*
* @param auth PlayerAuth
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#updateEmail(PlayerAuth)
*/
@Override
public synchronized boolean updateEmail(final PlayerAuth auth) {
boolean result = source.updateEmail(auth);
@ -291,55 +197,21 @@ public class CacheDataSource implements DataSource {
return result;
}
/**
* Method getAllAuthsByName.
*
* @param auth PlayerAuth
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override
public synchronized List<String> getAllAuthsByName(PlayerAuth auth) {
return source.getAllAuthsByName(auth);
}
/**
* Method getAllAuthsByIp.
*
* @param ip String
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override
public synchronized List<String> getAllAuthsByIp(final String ip) {
return source.getAllAuthsByIp(ip);
}
/**
* Method getAllAuthsByEmail.
*
* @param email String
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override
public synchronized List<String> getAllAuthsByEmail(final String email) {
return source.getAllAuthsByEmail(email);
}
/**
* Method purgeBanned.
*
* @param banned List<String>
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List)
*/
@Override
public synchronized void purgeBanned(final List<String> banned) {
exec.execute(new Runnable() {
@ -351,39 +223,16 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method getType.
*
* @return DataSourceType
*
* @see fr.xephi.authme.datasource.DataSource#getType()
*/
@Override
public DataSourceType getType() {
return source.getType();
}
/**
* Method isLogged.
*
* @param user String
*
* @return boolean
*
* @see fr.xephi.authme.datasource.DataSource#isLogged(String)
*/
@Override
public boolean isLogged(String user) {
return PlayerCache.getInstance().isAuthenticated(user);
}
/**
* Method setLogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setLogged(String)
*/
@Override
public void setLogged(final String user) {
exec.execute(new Runnable() {
@ -394,13 +243,6 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method setUnlogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setUnlogged(String)
*/
@Override
public void setUnlogged(final String user) {
exec.execute(new Runnable() {
@ -411,11 +253,6 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method purgeLogged.
*
* @see fr.xephi.authme.datasource.DataSource#purgeLogged()
*/
@Override
public void purgeLogged() {
exec.execute(new Runnable() {
@ -427,26 +264,11 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method getAccountsRegistered.
*
* @return int
*
* @see fr.xephi.authme.datasource.DataSource#getAccountsRegistered()
*/
@Override
public int getAccountsRegistered() {
return source.getAccountsRegistered();
}
/**
* Method updateName.
*
* @param oldOne String
* @param newOne String
*
* @see fr.xephi.authme.datasource.DataSource#updateName(String, String)
*/
@Override
public void updateName(final String oldOne, final String newOne) {
exec.execute(new Runnable() {
@ -458,25 +280,11 @@ public class CacheDataSource implements DataSource {
});
}
/**
* Method getAllAuths.
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getAllAuths()
*/
@Override
public List<PlayerAuth> getAllAuths() {
return source.getAllAuths();
}
/**
* Method getLoggedPlayers.
*
* @return List
*
* @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
*/
@Override
public List<PlayerAuth> getLoggedPlayers() {
return new ArrayList<>(PlayerCache.getInstance().getCache().values());

View File

@ -79,7 +79,7 @@ public interface DataSource {
*
* @param until long
*
* @return List<String>
* @return List of String
*/
List<String> autoPurgeDatabase(long until);
@ -115,7 +115,7 @@ public interface DataSource {
*
* @param auth PlayerAuth
*
* @return List<String>
* @return List of String
*/
List<String> getAllAuthsByName(PlayerAuth auth);
@ -124,7 +124,7 @@ public interface DataSource {
*
* @param ip String
*
* @return List<String> * @throws Exception
* @return List of String * @throws Exception
*/
List<String> getAllAuthsByIp(String ip);
@ -133,7 +133,7 @@ public interface DataSource {
*
* @param email String
*
* @return List<String> * @throws Exception
* @return List of String * @throws Exception
*/
List<String> getAllAuthsByEmail(String email);
@ -153,7 +153,7 @@ public interface DataSource {
/**
* Method purgeBanned.
*
* @param banned List<String>
* @param banned List of String
*/
void purgeBanned(List<String> banned);
@ -207,14 +207,14 @@ public interface DataSource {
/**
* Method getAllAuths.
*
* @return List<PlayerAuth>
* @return List of PlayerAuth
*/
List<PlayerAuth> getAllAuths();
/**
* Method getLoggedPlayers.
*
* @return List<PlayerAuth>
* @return List of PlayerAuth
*/
List<PlayerAuth> getLoggedPlayers();

View File

@ -406,7 +406,7 @@ public class FlatFile implements DataSource {
*
* @param until long
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
* @return List of String * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override
public List<String> autoPurgeDatabase(long until) {
@ -622,7 +622,7 @@ public class FlatFile implements DataSource {
*
* @param auth PlayerAuth
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
* @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override
public List<String> getAllAuthsByName(PlayerAuth auth) {
@ -659,7 +659,7 @@ public class FlatFile implements DataSource {
*
* @param ip String
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
* @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override
public List<String> getAllAuthsByIp(String ip) {
@ -696,7 +696,7 @@ public class FlatFile implements DataSource {
*
* @param email String
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
* @return List of String * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override
public List<String> getAllAuthsByEmail(String email) {
@ -731,7 +731,7 @@ public class FlatFile implements DataSource {
/**
* Method purgeBanned.
*
* @param banned List<String>
* @param banned List of String
*
* @see fr.xephi.authme.datasource.DataSource#purgeBanned(List)
*/
@ -876,7 +876,7 @@ public class FlatFile implements DataSource {
/**
* Method getAllAuths.
*
* @return List<PlayerAuth> * @see fr.xephi.authme.datasource.DataSource#getAllAuths()
* @return List of PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getAllAuths()
*/
@Override
public List<PlayerAuth> getAllAuths() {
@ -928,7 +928,7 @@ public class FlatFile implements DataSource {
/**
* Method getLoggedPlayers.
*
* @return List<PlayerAuth> * @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
* @return List of PlayerAuth * @see fr.xephi.authme.datasource.DataSource#getLoggedPlayers()
*/
@Override
public List<PlayerAuth> getLoggedPlayers() {

View File

@ -1,11 +1,5 @@
package fr.xephi.authme.datasource;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
@ -15,6 +9,12 @@ import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.auth.PlayerAuth;
import fr.xephi.authme.security.crypts.HashedPassword;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.StringUtils;
/**
*/
public class SQLite implements DataSource {
@ -40,7 +40,8 @@ public class SQLite implements DataSource {
/**
* Constructor for SQLite.
*
* @throws ClassNotFoundException * @throws SQLException
* @throws ClassNotFoundException Exception
* @throws SQLException Exception
*/
public SQLite() throws ClassNotFoundException, SQLException {
this.database = Settings.getMySQLDatabase;
@ -69,11 +70,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method connect.
*
* @throws ClassNotFoundException * @throws SQLException
*/
private synchronized void connect() throws ClassNotFoundException, SQLException {
Class.forName("org.sqlite.JDBC");
ConsoleLogger.info("SQLite driver loaded");
@ -81,11 +77,6 @@ public class SQLite implements DataSource {
}
/**
* Method setup.
*
* @throws SQLException
*/
private synchronized void setup() throws SQLException {
Statement st = null;
ResultSet rs = null;
@ -147,13 +138,6 @@ public class SQLite implements DataSource {
ConsoleLogger.info("SQLite Setup finished");
}
/**
* Method isAuthAvailable.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#isAuthAvailable(String)
*/
@Override
public synchronized boolean isAuthAvailable(String user) {
PreparedStatement pst = null;
@ -224,13 +208,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method saveAuth.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#saveAuth(PlayerAuth)
*/
@Override
public synchronized boolean saveAuth(PlayerAuth auth) {
PreparedStatement pst = null;
@ -270,13 +247,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method updatePassword.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updatePassword(PlayerAuth)
*/
@Override
public synchronized boolean updatePassword(PlayerAuth auth) {
return updatePassword(auth.getNickname(), auth.getPassword());
@ -309,13 +279,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method updateSession.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateSession(PlayerAuth)
*/
@Override
public boolean updateSession(PlayerAuth auth) {
PreparedStatement pst = null;
@ -335,13 +298,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method purgeDatabase.
*
* @param until long
*
* @return int * @see fr.xephi.authme.datasource.DataSource#purgeDatabase(long)
*/
@Override
public int purgeDatabase(long until) {
PreparedStatement pst = null;
@ -358,13 +314,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method autoPurgeDatabase.
*
* @param until long
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#autoPurgeDatabase(long)
*/
@Override
public List<String> autoPurgeDatabase(long until) {
PreparedStatement pst = null;
@ -387,13 +336,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method removeAuth.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#removeAuth(String)
*/
@Override
public synchronized boolean removeAuth(String user) {
PreparedStatement pst = null;
@ -410,13 +352,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method updateQuitLoc.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateQuitLoc(PlayerAuth)
*/
@Override
public boolean updateQuitLoc(PlayerAuth auth) {
PreparedStatement pst = null;
@ -437,13 +372,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method getIps.
*
* @param ip String
*
* @return int * @see fr.xephi.authme.datasource.DataSource#getIps(String)
*/
@Override
public int getIps(String ip) {
PreparedStatement pst = null;
@ -467,13 +395,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method updateEmail.
*
* @param auth PlayerAuth
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#updateEmail(PlayerAuth)
*/
@Override
public boolean updateEmail(PlayerAuth auth) {
PreparedStatement pst = null;
@ -491,11 +412,6 @@ public class SQLite implements DataSource {
return true;
}
/**
* Method close.
*
* @see fr.xephi.authme.datasource.DataSource#close()
*/
@Override
public synchronized void close() {
try {
@ -505,20 +421,10 @@ public class SQLite implements DataSource {
}
}
/**
* Method reload.
*
* @see fr.xephi.authme.datasource.DataSource#reload()
*/
@Override
public void reload() {
}
/**
* Method close.
*
* @param st Statement
*/
private void close(Statement st) {
if (st != null) {
try {
@ -529,11 +435,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method close.
*
* @param rs ResultSet
*/
private void close(ResultSet rs) {
if (rs != null) {
try {
@ -544,13 +445,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method getAllAuthsByName.
*
* @param auth PlayerAuth
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByName(PlayerAuth)
*/
@Override
public List<String> getAllAuthsByName(PlayerAuth auth) {
PreparedStatement pst = null;
@ -575,13 +469,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method getAllAuthsByIp.
*
* @param ip String
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByIp(String)
*/
@Override
public List<String> getAllAuthsByIp(String ip) {
PreparedStatement pst = null;
@ -606,13 +493,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method getAllAuthsByEmail.
*
* @param email String
*
* @return List<String> * @see fr.xephi.authme.datasource.DataSource#getAllAuthsByEmail(String)
*/
@Override
public List<String> getAllAuthsByEmail(String email) {
PreparedStatement pst = null;
@ -653,23 +533,11 @@ public class SQLite implements DataSource {
}
}
/**
* Method getType.
*
* @return DataSourceType * @see fr.xephi.authme.datasource.DataSource#getType()
*/
@Override
public DataSourceType getType() {
return DataSourceType.SQLITE;
}
/**
* Method isLogged.
*
* @param user String
*
* @return boolean * @see fr.xephi.authme.datasource.DataSource#isLogged(String)
*/
@Override
public boolean isLogged(String user) {
PreparedStatement pst = null;
@ -690,13 +558,6 @@ public class SQLite implements DataSource {
return false;
}
/**
* Method setLogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setLogged(String)
*/
@Override
public void setLogged(String user) {
PreparedStatement pst = null;
@ -712,13 +573,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method setUnlogged.
*
* @param user String
*
* @see fr.xephi.authme.datasource.DataSource#setUnlogged(String)
*/
@Override
public void setUnlogged(String user) {
PreparedStatement pst = null;
@ -735,11 +589,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method purgeLogged.
*
* @see fr.xephi.authme.datasource.DataSource#purgeLogged()
*/
@Override
public void purgeLogged() {
PreparedStatement pst = null;
@ -755,11 +604,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method getAccountsRegistered.
*
* @return int * @see fr.xephi.authme.datasource.DataSource#getAccountsRegistered()
*/
@Override
public int getAccountsRegistered() {
int result = 0;
@ -795,11 +639,6 @@ public class SQLite implements DataSource {
}
}
/**
* Method getAllAuths.
*
* @return List<PlayerAuth>
*/
@Override
public List<PlayerAuth> getAllAuths() {
List<PlayerAuth> auths = new ArrayList<>();
@ -821,11 +660,6 @@ public class SQLite implements DataSource {
return auths;
}
/**
* Method getLoggedPlayers.
*
* @return List<PlayerAuth>
*/
@Override
public List<PlayerAuth> getLoggedPlayers() {
List<PlayerAuth> auths = new ArrayList<>();

View File

@ -193,7 +193,7 @@ public class AuthMePlayerListener implements Listener {
}
if (Settings.isForceSurvivalModeEnabled
&& !player.hasPermission(PlayerPermission.BYPASS_FORCE_SURVIVAL.getNode())) {
&& !player.hasPermission(PlayerPermission.BYPASS_FORCE_SURVIVAL.getNode())) {
player.setGameMode(GameMode.SURVIVAL);
}
@ -206,9 +206,6 @@ public class AuthMePlayerListener implements Listener {
joinMessage.put(name, joinMsg);
}
if (Settings.checkVeryGames)
plugin.getVerygamesIp(player);
// Shedule login task so works after the prelogin
// (Fix found by Koolaid5000)
Bukkit.getScheduler().runTask(plugin, new Runnable() {
@ -222,21 +219,24 @@ public class AuthMePlayerListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPreLogin(AsyncPlayerPreLoginEvent event) {
PlayerAuth auth = plugin.getDataSource().getAuth(event.getName());
if (auth != null && auth.getRealName() != null && !auth.getRealName().isEmpty() &&
!auth.getRealName().equals("Player") && !auth.getRealName().equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
event.setKickMessage("You should join using username: " + ChatColor.AQUA + auth.getRealName() +
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName()); // TODO: write a better message
return;
}
if (auth != null && auth.getRealName().equals("Player")) {
auth.setRealName(event.getName());
plugin.getDataSource().saveAuth(auth);
if (Settings.preventOtherCase && auth != null && auth.getRealName() != null) {
String realName = auth.getRealName();
if (!realName.isEmpty() && !realName.equals("Player") && !realName.equals(event.getName())) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
// TODO: Add a message like : MessageKey.INVALID_NAME_CASE
event.setKickMessage("You should join using username: " + ChatColor.AQUA + realName +
ChatColor.RESET + "\nnot: " + ChatColor.RED + event.getName());
return;
}
if (realName.isEmpty() || realName.equals("Player")) {
auth.setRealName(event.getName());
plugin.getDataSource().saveAuth(auth);
}
}
String playerIP = event.getAddress().getHostAddress();
if (auth == null && Settings.enableProtection) {
String countryCode = GeoLiteAPI.getCountryCode(event.getAddress().getHostAddress());
String countryCode = GeoLiteAPI.getCountryCode(playerIP);
if (!Settings.countriesBlacklist.isEmpty() && Settings.countriesBlacklist.contains(countryCode)) {
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
event.setKickMessage(m.retrieveSingle(MessageKey.COUNTRY_BANNED_ERROR));
@ -274,21 +274,26 @@ public class AuthMePlayerListener implements Listener {
// Get the permissions manager
PermissionsManager permsMan = plugin.getPermissionsManager();
if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL
&& permsMan.hasPermission(player, PlayerPermission.IS_VIP)) {
int playersOnline = Utils.getOnlinePlayers().size();
if (playersOnline > plugin.getServer().getMaxPlayers()) {
event.allow();
} else {
Player pl = plugin.generateKickPlayer(Utils.getOnlinePlayers());
if (pl != null) {
pl.kickPlayer(m.retrieveSingle(MessageKey.KICK_FOR_VIP));
if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL) {
if (permsMan.hasPermission(player, PlayerPermission.IS_VIP)) {
int playersOnline = Utils.getOnlinePlayers().size();
if (playersOnline > plugin.getServer().getMaxPlayers()) {
event.allow();
} else {
ConsoleLogger.info("The player " + event.getPlayer().getName() + " tried to join, but the server was full");
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
Player pl = plugin.generateKickPlayer(Utils.getOnlinePlayers());
if (pl != null) {
pl.kickPlayer(m.retrieveSingle(MessageKey.KICK_FOR_VIP));
event.allow();
} else {
ConsoleLogger.info("The player " + event.getPlayer().getName() + " tried to join, but the server was full");
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
}
}
} else {
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
return;
}
}
@ -296,12 +301,6 @@ public class AuthMePlayerListener implements Listener {
return;
}
if (event.getResult() == PlayerLoginEvent.Result.KICK_FULL && !permsMan.hasPermission(player, PlayerPermission.IS_VIP)) {
event.setKickMessage(m.retrieveSingle(MessageKey.KICK_FULL_SERVER));
event.setResult(PlayerLoginEvent.Result.KICK_FULL);
return;
}
final String name = player.getName().toLowerCase();
boolean isAuthAvailable = plugin.getDataSource().isAuthAvailable(name);

View File

@ -1,30 +0,0 @@
package fr.xephi.authme.listener;
import org.bukkit.entity.Player;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.settings.Settings;
public class AuthMeServerStop extends Thread {
private AuthMe plugin;
public AuthMeServerStop(AuthMe plugin) {
this.plugin = plugin;
}
public void run() {
// TODO: add a MessageKey
if (Settings.kickPlayersBeforeStopping) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new Runnable()
{
@Override
public void run() {
for (Player p : plugin.getServer().getOnlinePlayers()) {
p.kickPlayer("Server is restarting");
}
}
});
}
}
}

View File

@ -25,7 +25,11 @@ public enum DefaultPermission {
this.title = title;
}
/** Return the textual representation. */
/**
* Return the textual representation.
*
* @return String
*/
public String getTitle() {
return title;
}

View File

@ -1,10 +1,11 @@
package fr.xephi.authme.permission;
import de.bananaco.bpermissions.api.ApiLayer;
import de.bananaco.bpermissions.api.CalculableType;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.util.CollectionUtils;
import net.milkbowl.vault.permission.Permission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.anjocaido.groupmanager.GroupManager;
import org.anjocaido.groupmanager.permissions.AnjoPermissionsHandler;
import org.bukkit.Bukkit;
@ -17,24 +18,25 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.tyrannyofheaven.bukkit.zPermissions.ZPermissionsService;
import de.bananaco.bpermissions.api.ApiLayer;
import de.bananaco.bpermissions.api.CalculableType;
import fr.xephi.authme.command.CommandDescription;
import fr.xephi.authme.util.CollectionUtils;
import net.milkbowl.vault.permission.Permission;
import ru.tehkode.permissions.PermissionManager;
import ru.tehkode.permissions.PermissionUser;
import ru.tehkode.permissions.bukkit.PermissionsEx;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* <p>
* PermissionsManager.
* <p/>
* </p><p>
* A permissions manager, to manage and use various permissions systems.
* This manager supports dynamic plugin hooking and various other features.
* <p/>
* </p><p>
* Written by Tim Visée.
*
* </p>
* @author Tim Visée, http://timvisee.com
* @version 0.2.1
*/
@ -296,8 +298,8 @@ public class PermissionsManager implements PermissionsService {
}
Player player = (Player) sender;
return hasPermission(player, permissionNode.getNode(), def)
|| hasPermission(player, permissionNode.getWildcardNode().getNode(), def);
return hasPermission(player, permissionNode.getNode(), def);
// || hasPermission(player, permissionNode.getWildcardNode().getNode(), def);
}
public boolean hasPermission(Player player, Iterable<PermissionNode> nodes, boolean def) {

View File

@ -49,6 +49,10 @@ public class AsynchronousJoin {
}
public void process() {
if (Settings.checkVeryGames) {
plugin.getVerygamesIp(player);
}
if (Utils.isUnrestricted(player)) {
return;
}
@ -58,6 +62,8 @@ public class AsynchronousJoin {
}
final String ip = plugin.getIP(player);
if (Settings.isAllowRestrictedIp && !Settings.getRestrictedIp(name, ip, player.getAddress().getHostName())) {
sched.scheduleSyncDelayedTask(plugin, new Runnable() {
@ -110,7 +116,6 @@ public class AsynchronousJoin {
});
}
}
placePlayerSafely(player, spawnLoc);
LimboCache.getInstance().updateLimboPlayer(player);
@ -126,6 +131,22 @@ public class AsynchronousJoin {
}
}
if (Settings.isSessionsEnabled && (PlayerCache.getInstance().isAuthenticated(name) || database.isLogged(name))) {
if (plugin.sessions.containsKey(name)) {
plugin.sessions.get(name).cancel();
plugin.sessions.remove(name);
}
PlayerAuth auth = database.getAuth(name);
database.setUnlogged(name);
PlayerCache.getInstance().removePlayer(name);
if (auth != null && auth.getIp().equals(ip)) {
m.send(player, MessageKey.SESSION_RECONNECTION);
plugin.getManagement().performLogin(player, "dontneed", true);
return;
} else if (Settings.sessionExpireOnIpChange) {
m.send(player, MessageKey.SESSION_EXPIRED);
}
}
} else {
if (!Settings.unRegisteredGroup.isEmpty()) {
Utils.setGroup(player, Utils.GroupType.UNREGISTERED);
@ -179,7 +200,7 @@ public class AsynchronousJoin {
if (Settings.applyBlindEffect) {
int blindTimeOut;
// Allow infinite blindness effect
if(timeOut <= 0) {
if (timeOut <= 0) {
blindTimeOut = 99999;
} else {
blindTimeOut = timeOut;
@ -196,23 +217,6 @@ public class AsynchronousJoin {
LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id);
}
if (Settings.isSessionsEnabled && isAuthAvailable && (PlayerCache.getInstance().isAuthenticated(name) || database.isLogged(name))) {
if (plugin.sessions.containsKey(name)) {
plugin.sessions.get(name).cancel();
plugin.sessions.remove(name);
}
PlayerAuth auth = database.getAuth(name);
database.setUnlogged(name);
PlayerCache.getInstance().removePlayer(name);
if (auth != null && auth.getIp().equals(ip)) {
m.send(player, MessageKey.SESSION_RECONNECTION);
plugin.getManagement().performLogin(player, "dontneed", true);
return;
} else if (Settings.sessionExpireOnIpChange) {
m.send(player, MessageKey.SESSION_EXPIRED);
}
}
String[] msg;
if (isAuthAvailable) {
msg = m.retrieve(MessageKey.LOGIN_MESSAGE);

View File

@ -33,6 +33,7 @@ public class AsynchronousLogin {
private final AuthMe plugin;
private final DataSource database;
private final Messages m;
private final String ip;
/**
* Constructor for AsynchronousLogin.
@ -52,10 +53,7 @@ public class AsynchronousLogin {
this.forceLogin = forceLogin;
this.plugin = plugin;
this.database = data;
}
protected String getIP() {
return plugin.getIP(player);
this.ip = plugin.getIP(player);
}
protected boolean needsCaptcha() {
@ -87,7 +85,9 @@ public class AsynchronousLogin {
m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return null;
}
if (!database.isAuthAvailable(name)) {
PlayerAuth pAuth = database.getAuth(name);
if (pAuth == null) {
m.send(player, MessageKey.USER_NOT_REGISTERED);
if (LimboCache.getInstance().hasLimboPlayer(name)) {
LimboCache.getInstance().getLimboPlayer(name).getMessageTaskId().cancel();
@ -97,43 +97,45 @@ public class AsynchronousLogin {
} else {
msg = m.retrieve(MessageKey.REGISTER_MESSAGE);
}
BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, Settings.getWarnMessageInterval));
BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin,
new MessageTask(plugin, name, msg, Settings.getWarnMessageInterval));
LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT);
}
return null;
}
if (Settings.getMaxLoginPerIp > 0 && !plugin.getPermissionsManager().hasPermission(player, PlayerPermission.ALLOW_MULTIPLE_ACCOUNTS) && !getIP().equalsIgnoreCase("127.0.0.1") && !getIP().equalsIgnoreCase("localhost")) {
if (plugin.isLoggedIp(name, getIP())) {
m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return null;
}
}
PlayerAuth pAuth = database.getAuth(name);
if (pAuth == null) {
m.send(player, MessageKey.USER_NOT_REGISTERED);
return null;
}
if (!Settings.getMySQLColumnGroup.isEmpty() && pAuth.getGroupId() == Settings.getNonActivatedGroup) {
m.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
return null;
}
if (Settings.preventOtherCase && !player.getName().equals(pAuth.getRealName())) {
// TODO: Add a message like : MessageKey.INVALID_NAME_CASE
m.send(player, MessageKey.USERNAME_ALREADY_ONLINE_ERROR);
return null;
if (Settings.getMaxLoginPerIp > 0
&& !plugin.getPermissionsManager().hasPermission(player, PlayerPermission.ALLOW_MULTIPLE_ACCOUNTS)
&& !ip.equalsIgnoreCase("127.0.0.1") && !ip.equalsIgnoreCase("localhost")) {
if (plugin.isLoggedIp(name, ip)) {
m.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return null;
}
}
AuthMeAsyncPreLoginEvent event = new AuthMeAsyncPreLoginEvent(player);
Bukkit.getServer().getPluginManager().callEvent(event);
if (!event.canLogin())
if (!event.canLogin()) {
return null;
}
return pAuth;
}
public void process() {
PlayerAuth pAuth = preAuth();
if (pAuth == null || needsCaptcha())
if (pAuth == null || needsCaptcha()) {
return;
}
if (pAuth.getIp().equals("127.0.0.1") && !pAuth.getIp().equals(ip)) {
pAuth.setIp(ip);
database.saveAuth(pAuth);
}
String email = pAuth.getEmail();
boolean passwordVerified = forceLogin || plugin.getPasswordSecurity()
@ -143,7 +145,7 @@ public class AsynchronousLogin {
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(realName)
.ip(getIP())
.ip(ip)
.lastLogin(new Date().getTime())
.email(email)
.password(pAuth.getPassword())
@ -184,14 +186,16 @@ public class AsynchronousLogin {
// task, we schedule it in the end
// so that we can be sure, and have not to care if it might be
// processed in other order.
ProcessSyncronousPlayerLogin syncronousPlayerLogin = new ProcessSyncronousPlayerLogin(player, plugin, database);
if (syncronousPlayerLogin.getLimbo() != null) {
if (syncronousPlayerLogin.getLimbo().getTimeoutTaskId() != null)
syncronousPlayerLogin.getLimbo().getTimeoutTaskId().cancel();
if (syncronousPlayerLogin.getLimbo().getMessageTaskId() != null)
syncronousPlayerLogin.getLimbo().getMessageTaskId().cancel();
ProcessSyncPlayerLogin syncPlayerLogin = new ProcessSyncPlayerLogin(player, plugin, database);
if (syncPlayerLogin.getLimbo() != null) {
if (syncPlayerLogin.getLimbo().getTimeoutTaskId() != null) {
syncPlayerLogin.getLimbo().getTimeoutTaskId().cancel();
}
if (syncPlayerLogin.getLimbo().getMessageTaskId() != null) {
syncPlayerLogin.getLimbo().getMessageTaskId().cancel();
}
}
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, syncronousPlayerLogin);
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, syncPlayerLogin);
} else if (player.isOnline()) {
if (!Settings.noConsoleSpam)
ConsoleLogger.info(realName + " used the wrong password");

View File

@ -26,7 +26,7 @@ import fr.xephi.authme.util.Utils.GroupType;
/**
*/
public class ProcessSyncronousPlayerLogin implements Runnable {
public class ProcessSyncPlayerLogin implements Runnable {
private final LimboPlayer limbo;
private final Player player;
@ -38,14 +38,14 @@ public class ProcessSyncronousPlayerLogin implements Runnable {
private final JsonCache playerCache;
/**
* Constructor for ProcessSyncronousPlayerLogin.
* Constructor for ProcessSyncPlayerLogin.
*
* @param player Player
* @param plugin AuthMe
* @param data DataSource
*/
public ProcessSyncronousPlayerLogin(Player player, AuthMe plugin,
DataSource data) {
public ProcessSyncPlayerLogin(Player player, AuthMe plugin,
DataSource data) {
this.plugin = plugin;
this.database = data;
this.pm = plugin.getServer().getPluginManager();

View File

@ -93,6 +93,7 @@ public class AsynchronousQuit {
database.setUnlogged(name);
}
plugin.realIp.remove(name);
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new ProcessSyncronousPlayerQuit(plugin, player, isOp, needToChange));
}
}

View File

@ -3,9 +3,8 @@ package fr.xephi.authme.security;
import fr.xephi.authme.security.crypts.EncryptionMethod;
/**
* The list of hash algorithms supported by AuthMe. The implementing class must define a public
* constructor which takes either no arguments, or a DataSource object (when the salt is stored
* separately, writes to the database are necessary).
* The list of hash algorithms supported by AuthMe. The linked {@link EncryptionMethod} implementation
* must be able to be instantiated with the default constructor.
*/
public enum HashAlgorithm {

View File

@ -15,6 +15,7 @@ package fr.xephi.authme.security.crypts;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Usage;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
@ -108,7 +109,6 @@ public class BCRYPT implements EncryptionMethod {
*
* @throws IllegalArgumentException if the length is invalid
*/
private static String encode_base64(byte d[], int len)
throws IllegalArgumentException {
int off = 0;

View File

@ -10,8 +10,6 @@ import fr.xephi.authme.security.crypts.description.HasSalt;
import java.nio.charset.Charset;
import java.security.MessageDigest;
@Recommendation(Usage.DO_NOT_USE)
@HasSalt(SaltType.USERNAME)
public class CRAZYCRYPT1 extends UsernameSaltMethod {
private static final char[] CRYPTCHARS =

View File

@ -8,7 +8,7 @@ import fr.xephi.authme.security.crypts.description.Usage;
import static fr.xephi.authme.security.HashUtils.md5;
@Recommendation(Usage.DO_NOT_USE)
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 5)
public class IPB3 extends SeparateSaltMethod {

View File

@ -1,9 +1,15 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
import static fr.xephi.authme.security.HashUtils.md5;
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 8)
public class MYBB extends SeparateSaltMethod {
@Override

View File

@ -1,9 +1,15 @@
package fr.xephi.authme.security.crypts;
import fr.xephi.authme.security.RandomString;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
import static fr.xephi.authme.security.HashUtils.sha1;
@Recommendation(Usage.ACCEPTABLE)
@HasSalt(value = SaltType.TEXT, length = 40)
public class WBB3 extends SeparateSaltMethod {
@Override

View File

@ -241,8 +241,9 @@ public class WHIRLPOOL extends UnsaltedMethod {
*
* @param source plaintext data to hash.
* @param sourceBits how many bits of plaintext to process.
* <p/>
* This method maintains the invariant: bufferBits < 512
* <p>
* This method maintains the invariant: bufferBits &lt; 512
* </p>
*/
public void NESSIEadd(byte[] source, long sourceBits) {
/*
@ -322,10 +323,12 @@ public class WHIRLPOOL extends UnsaltedMethod {
}
/**
* <p>
* Get the hash value from the hashing state.
* <p/>
* This method uses the invariant: bufferBits < 512
*
* </p>
* <p>
* This method uses the invariant: bufferBits &lt; 512
* </p>
* @param digest byte[]
*/
public void NESSIEfinalize(byte[] digest) {
@ -367,7 +370,7 @@ public class WHIRLPOOL extends UnsaltedMethod {
* Delivers string input data to the hashing algorithm.
*
* @param source plaintext data to hash (ASCII text string).
* This method maintains the invariant: bufferBits < 512
* This method maintains the invariant: bufferBits &lt; 512
*/
public void NESSIEadd(String source) {
if (source.length() > 0) {

View File

@ -6,7 +6,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Denotes an encryption algorithm that is restricted to the ASCII charset.
* Denotes a hashing algorithm that is restricted to the ASCII charset.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

View File

@ -13,10 +13,18 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface HasSalt {
/** The type of the salt. */
/**
* The type of the salt.
*
* @return The salt type
*/
SaltType value();
/** For text salts, the length of the salt. */
/**
* For text salts, the length of the salt.
*
* @return The length of the salt the algorithm uses, or 0 if not defined or not applicable.
*/
int length() default 0;
}

View File

@ -6,13 +6,19 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to mark a hash algorithm with the usage recommendation, see {@link Usage}.
* Annotation to mark a hash algorithm with the usage recommendation.
*
* @see Usage
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Recommendation {
/** The recommendation for using the hash algorithm. */
/**
* The recommendation for using the hash algorithm.
*
* @return The recommended usage
*/
Usage value();
}

View File

@ -8,7 +8,7 @@ public enum SaltType {
/** Random, newly generated text. */
TEXT,
/** Salt is based on the username, including variations and repetitions. */
/** Salt is based on the username, including variations and repetitions thereof. */
USERNAME,
/** No salt. */

View File

@ -2,6 +2,12 @@ package fr.xephi.authme.security.crypts.description;
/**
* Usage recommendation that can be provided for a hash algorithm.
* <p>
* Use the following rules of thumb:
* <ul>
* <li>Hashes using MD5 may be {@link #ACCEPTABLE} but never {@link #RECOMMENDED}.</li>
* <li>Hashes using no salt or one based on the username should be {@link #DO_NOT_USE}.</li>
* </ul>
*/
public enum Usage {

View File

@ -1,15 +1,16 @@
package fr.xephi.authme.security.pbkdf2;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
/**
* Default PRF implementation based on standard javax.crypt.Mac mechanisms.
* <p>
* <hr />
* Default PRF implementation based on standard javax.crypt.Mac mechanisms.
* </p>
* <p>
* A free Java implementation of Password Based Key Derivation Function 2 as
* defined by RFC 2898. Copyright (c) 2007 Matthias G&auml;rtner

View File

@ -75,7 +75,7 @@ public interface PBKDF2 {
/**
* Allow setting of configured parameters.
*
* @param parameters
* @param parameters PBKDF2Parameters
*/
void setParameters(PBKDF2Parameters parameters);

View File

@ -8,20 +8,17 @@ import java.security.SecureRandom;
/**
* <p>
* Request for Comments: 2898 PKCS #5: Password-Based Cryptography Specification
* <p>
* </p><p>
* Version 2.0
* <p>
* </p>
* <p>
* PBKDF2 (P, S, c, dkLen)
* <p>
* <p>
* </p>
* Options:
* <ul>
* <li>PRF underlying pseudorandom function (hLen denotes the length in octets
* of the pseudorandom function output). PRF is pluggable.</li>
* </ul>
* <p>
* <p>
* Input:
* <ul>
* <li>P password, an octet string</li>
@ -30,15 +27,11 @@ import java.security.SecureRandom;
* <li>dkLen intended length in octets of the derived key, a positive integer,
* at most (2^32 - 1) * hLen</li>
* </ul>
* <p>
* <p>
* Output:
* <ul>
* <li>DK derived key, a dkLen-octet string</li>
* </ul>
* <p>
* <hr />
* <p>
* A free Java implementation of Password Based Key Derivation Function 2 as
* defined by RFC 2898. Copyright (c) 2007 Matthias G&auml;rtner
* </p>
@ -115,13 +108,14 @@ public class PBKDF2Engine implements PBKDF2 {
* ISO-8559-1 encoding. Output result as
* &quot;Salt:iteration-count:PBKDF2&quot; with binary data in hexadecimal
* encoding.
* <p/>
* <p>
* Example: Password &quot;password&quot; (without the quotes) leads to
* 48290A0B96C426C3:1000:973899B1D4AFEB3ED371060D0797E0EE0142BD04
*
* </p>
* @param args Supply the password as argument.
*
* @throws IOException * @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
* @throws IOException an ioexception occured
* @throws NoSuchAlgorithmException a NoSuchAlgorithmException occured
*/
public static void main(String[] args)
throws IOException, NoSuchAlgorithmException {
@ -266,8 +260,8 @@ public class PBKDF2Engine implements PBKDF2 {
/**
* Integer division with ceiling function.
*
* @param a
* @param b
* @param a Integer
* @param b Integer
*
* @return ceil(a/b) * @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2 Step
* 2.</a>
@ -288,7 +282,7 @@ public class PBKDF2Engine implements PBKDF2 {
* @param prf Pseudo Random Function
* @param S Salt as array of bytes
* @param c Iteration count
* @param blockIndex
* @param blockIndex Integer
*
* @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2 Step
* 3.</a>
@ -314,8 +308,8 @@ public class PBKDF2Engine implements PBKDF2 {
* Block-Xor. Xor source bytes into destination byte buffer. Destination
* buffer must be same length or less than source buffer.
*
* @param dest
* @param src
* @param dest byte array
* @param src byte array
*/
protected void xor(byte[] dest, byte[] src) {
for (int i = 0; i < dest.length; i++) {
@ -326,9 +320,9 @@ public class PBKDF2Engine implements PBKDF2 {
/**
* Four-octet encoding of the integer i, most significant octet first.
*
* @param dest
* @param offset
* @param i
* @param dest byte array
* @param offset Integer
* @param i Integer
*
* @see <a href="http://tools.ietf.org/html/rfc2898">RFC 2898 5.2 Step
* 3.</a>

View File

@ -5,8 +5,6 @@ package fr.xephi.authme.security.pbkdf2;
* Parameter data holder for PBKDF2 configuration.
* </p>
* <p>
* <hr />
* <p>
* A free Java implementation of Password Based Key Derivation Function 2 as
* defined by RFC 2898. Copyright (c) 2007 Matthias G&auml;rtner
* </p>

View File

@ -1,13 +1,13 @@
package fr.xephi.authme.settings;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* @author Xephi59
* @version $Revision: 1.0 $
@ -88,7 +88,7 @@ public class OtherAccounts extends CustomConfiguration {
*
* @param uuid UUID
*
* @return List<String>
* @return StringList
*/
public List<String> getAllPlayersByUUID(UUID uuid) {
return this.getStringList(uuid.toString());

View File

@ -1,32 +1,27 @@
package fr.xephi.authme.settings;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSource.DataSourceType;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.util.Wrapper;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import org.bukkit.configuration.file.YamlConfiguration;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSource.DataSourceType;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.util.Wrapper;
/**
*/
public final class Settings {
@ -122,7 +117,7 @@ public final class Settings {
/**
* Method reload.
*
* @throws Exception
* @throws Exception if something went wrong
*/
public static void reload() throws Exception {
plugin.getLogger().info("Loading Configuration File...");
@ -141,7 +136,6 @@ public final class Settings {
messageFile = new File(PLUGIN_FOLDER, "messages" + File.separator + "messages_" + messagesLanguage + ".yml");
}
public static void loadVariables() {
helpHeader = configFile.getString("settings.helpHeader", "AuthMeReloaded");
messagesLanguage = checkLang(configFile.getString("settings.messagesLanguage", "en").toLowerCase());
@ -311,35 +305,22 @@ public final class Settings {
}
private static String loadEmailText() {
if (!EMAIL_FILE.exists())
saveDefaultEmailText();
StringBuilder str = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new FileReader(EMAIL_FILE));
String s;
while ((s = in.readLine()) != null)
str.append(s);
in.close();
} catch (IOException ignored) {
if (!EMAIL_FILE.exists()) {
plugin.saveResource("email.html", false);
}
return str.toString();
}
private static void saveDefaultEmailText() {
InputStream file = plugin.getResource("email.html");
StringBuilder str = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(file, Charset.forName("utf-8")));
String s;
while ((s = in.readLine()) != null)
str.append(s);
in.close();
Files.touch(EMAIL_FILE);
Files.write(str.toString(), EMAIL_FILE, Charsets.UTF_8);
} catch (Exception ignored) {
return Files.toString(EMAIL_FILE, Charsets.UTF_8);
} catch (IOException e) {
ConsoleLogger.showError(e.getMessage());
ConsoleLogger.writeStackTrace(e);
return "";
}
}
/**
* @param key the key to set
* @param value the value to set
*/
public static void setValue(String key, Object value) {
instance.set(key, value);
save();
@ -380,8 +361,9 @@ public final class Settings {
* return false if ip and name doesn't match with player that join the
* server, so player has a restricted access
*
* @param name String
* @param ip String
* @param name String
* @param ip String
* @param domain String
*
* @return boolean
*/
@ -396,14 +378,12 @@ public final class Settings {
String testIp = args[1];
if (testName.equalsIgnoreCase(name)) {
nameFound = true;
if (ip != null)
{
if (ip != null) {
if (testIp.equalsIgnoreCase(ip)) {
trueOnce = true;
}
}
if (domain != null)
{
if (domain != null) {
if (testIp.equalsIgnoreCase(domain)) {
trueOnce = true;
}
@ -737,10 +717,9 @@ public final class Settings {
changes = true;
}
if (!contains("settings.preventOtherCase"))
{
set("settings.preventOtherCase", false);
changes = true;
if (!contains("settings.preventOtherCase")) {
set("settings.preventOtherCase", false);
changes = true;
}
if (contains("Email.mailText")) {
@ -749,15 +728,14 @@ public final class Settings {
}
if (!contains("Security.stop.kickPlayersBeforeStopping")) {
set("Security.stop.kickPlayersBeforeStopping", true);
changes = true;
set("Security.stop.kickPlayersBeforeStopping", true);
changes = true;
}
if (!contains("Email.emailOauth2Token"))
set("Email.emailOauth2Token", "");
set("Email.emailOauth2Token", "");
if (!contains("Hook.sendPlayerTo"))
{
if (!contains("Hook.sendPlayerTo")) {
set("Hooks.sendPlayerTo", "");
changes = true;
}
@ -768,11 +746,21 @@ public final class Settings {
}
}
/**
* @param path
*
* @return
*/
private static boolean contains(String path) {
return configFile.contains(path);
}
// public because it's used in AuthMe at one place
/**
* @param path String
* @param value String
*/
public void set(String path, Object value) {
configFile.set(path, value);
}

View File

@ -15,6 +15,8 @@ public final class CollectionUtils {
/**
* Get a range from a list based on start and count parameters in a safe way.
*
* @param <T> element
* @param list The List
* @param start The start index
* @param count The number of elements to add
*
@ -34,6 +36,8 @@ public final class CollectionUtils {
/**
* Get all elements from a list starting from the given index.
*
* @param <T> element
* @param list The List
* @param start The start index
*
* @return The sublist of all elements from index {@code start} and on; empty list
@ -46,6 +50,11 @@ public final class CollectionUtils {
return getRange(list, start, list.size() - start);
}
/**
* @param <T> element
* @param coll Collection
* @return boolean Boolean
*/
public static <T> boolean isEmpty(Collection<T> coll) {
return coll == null || coll.isEmpty();
}

View File

@ -45,120 +45,144 @@ commands:
usage: /converter <datatype>
permissions:
authme.admin.*:
description: Gives access to all authme admin commands
children:
authme.admin.reload: true
authme.admin.register: true
authme.admin.changepassword: true
authme.admin.unregister: true
authme.admin.purge: true
authme.seeOtherAccounts: true # This isn't a child of the admin section! Probably doesn't work.
authme.admin.lastlogin: true
authme.admin.getemail: true
authme.admin.chgemail: true
authme.admin.purgelastpos: true
authme.admin.switchantibot: true
authme.bypassantibot: true # This isn't a child of the admin section! Probably doesn't work.
authme.admin.getip: true
authme.admin.converter: true
authme.admin.resetposition: true
authme.admin.forcelogin: true
authme.register:
description: Register an account
default: true
authme.login:
description: Login into a account
default: true
authme.changepassword:
description: Change password of a account
default: true
authme.logout:
description: Logout
default: true
authme.email:
description: Email
default: true
authme.allow2accounts:
description: allow more accounts for same ip
default: false
authme.seeOtherAccounts:
description: display other accounts about a player when he logs in
default: false
authme.unregister:
description: unregister your account
default: true
authme.admin.reload:
description: AuthMe reload commands
default: op
description: Give access to all admin commands.
children:
authme.admin.accounts: true
authme.admin.changemail: true
authme.admin.changepassword: true
authme.admin.converter: true
authme.admin.firstspawn: true
authme.admin.forcelogin: true
authme.admin.getemail: true
authme.admin.getip: true
authme.admin.lastlogin: true
authme.admin.purge: true
authme.admin.purgebannedplayers: true
authme.admin.purgelastpos: true
authme.admin.register: true
authme.admin.reload: true
authme.admin.setfirstspawn: true
authme.admin.setspawn: true
authme.admin.spawn: true
authme.admin.switchantibot: true
authme.admin.unregister: true
authme.admin.register:
description: AuthMe register command
default: op
authme.admin.changepassword:
description: AuthMe changepassword command
description: Administrator command to register a new user.
default: op
authme.admin.unregister:
description: AuthMe unregister command
default: op
authme.admin.purge:
description: AuthMe unregister command
default: op
authme.admin.lastlogin:
description: Get last login date about a player
default: op
authme.admin.getemail:
description: Get last email about a player
default: op
authme.admin.chgemail:
description: Change a player email
default: op
authme.admin.accounts:
description: Display Players Accounts
default: op
authme.captcha:
description: Captcha
default: true
authme.admin.setspawn:
description: Set the AuthMe spawn point
default: op
authme.admin.spawn:
description: Teleport to AuthMe spawn point
default: op
authme.vip:
description: Allow vip slot when the server is full
default: op
authme.admin.purgebannedplayers:
description: Purge banned players
default: op
authme.bypassforcesurvival:
description: Bypass all ForceSurvival features
default: false
authme.admin.purgelastpos:
description: Purge last pos of players
default: op
authme.admin.switchantibot:
description: Switch AntiBot mode on/off
default: op
authme.bypassantibot:
description: Bypass the AntiBot check
default: op
authme.admin.setfirstspawn:
description: Set the AuthMe First Spawn Point
default: op
authme.admin.firstspawn:
description: Teleport to AuthMe First Spawn Point
default: op
authme.admin.getip:
description: Get IP from a player ( fake and real )
default: op
authme.admin.converter:
description: Allow /converter command
default: op
authme.admin.resetposition:
description: Reset last position for a player
description: Administrator command to unregister an existing user.
default: op
authme.admin.forcelogin:
description: Force login for that player
description: Administrator command to force-login an existing user.
default: op
authme.canbeforced:
description: Can this player be forced to login
authme.admin.changepassword:
description: Administrator command to change the password of a user.
default: op
authme.admin.lastlogin:
description: Administrator command to see the last login date and time of a user.
default: op
authme.admin.accounts:
description: Administrator command to see all accounts associated with a user.
default: op
authme.admin.getemail:
description: Administrator command to get the email address of a user, if set.
default: op
authme.admin.changemail:
description: Administrator command to set or change the email address of a user.
default: op
authme.admin.getip:
description: Administrator command to get the last known IP of a user.
default: op
authme.admin.spawn:
description: Administrator command to teleport to the AuthMe spawn.
default: op
authme.admin.setspawn:
description: Administrator command to set the AuthMe spawn.
default: op
authme.admin.firstspawn:
description: Administrator command to teleport to the first AuthMe spawn.
default: op
authme.admin.setfirstspawn:
description: Administrator command to set the first AuthMe spawn.
default: op
authme.admin.purge:
description: Administrator command to purge old user data.
default: op
authme.admin.purgelastpos:
description: Administrator command to purge the last position of a user.
default: op
authme.admin.purgebannedplayers:
description: Administrator command to purge all data associated with banned players.
default: op
authme.admin.switchantibot:
description: Administrator command to toggle the AntiBot protection status.
default: op
authme.admin.converter:
description: Administrator command to convert old or other data to AuthMe data.
default: op
authme.admin.reload:
description: Administrator command to reload the plugin configuration.
default: op
authme.player.*:
description: Permission to use all player (non-admin) commands.
children:
authme.player.allow2accounts: true
authme.player.bypassantibot: true
authme.player.bypassforcesurvival: true
authme.player.canbeforced: true
authme.player.captcha: true
authme.player.changepassword: true
authme.player.email.add: true
authme.player.email.change: true
authme.player.email.recover: true
authme.player.login: true
authme.player.logout: true
authme.player.register: true
authme.player.seeotheraccounts: true
authme.player.unregister: true
authme.player.vip: true
authme.player.bypassantibot:
description: Permission node to bypass AntiBot protection.
default: false
authme.player.vip:
description: Permission node to identify VIP users.
default: false
authme.player.login:
description: Command permission to login.
default: true
authme.player.logout:
description: Command permission to logout.
default: true
authme.player.register:
description: Command permission to register.
default: true
authme.player.unregister:
description: Command permission to unregister.
default: true
authme.player.changepassword:
description: Command permission to change the password.
default: true
authme.player.email.add:
description: Command permission to add an email address.
default: false
authme.player.email.change:
description: Command permission to change the email address.
default: false
authme.player.email.recover:
description: Command permission to recover an account using it's email address.
default: false
authme.player.captcha:
description: Command permission to use captcha.
default: false
authme.player.canbeforced:
description: Permission for users a login can be forced to.
default: false
authme.player.bypassforcesurvival:
description: Permission for users to bypass force-survival mode.
default: false
authme.player.allow2accounts:
description: Permission for users to allow two accounts.
default: false
authme.player.seeotheraccounts:
description: Permission for user to see other accounts.
default: false

View File

@ -142,30 +142,44 @@ public abstract class AbstractEncryptionMethodTest {
return method.comparePassword(password, hashes.get(password), USERNAME);
}
// @org.junit.Test public void a() { AbstractEncryptionMethodTest.generateTest(); }
// TODO #364: Remove this method
/**
* Generates a test class for a given encryption method. Simply create a test class and run the following code,
* replacing {@code XXX} with the actual class:
* <p>
* <code>@org.junit.Test public void generate() { AbstractEncryptionMethodTest.generateTest(new XXX()); }</code>
* <p>
* The output is the entire test class.
*
* @param method The method to create a test class for
*/
static void generateTest(EncryptionMethod method) {
String className = method.getClass().getSimpleName();
// Create javadoc and "public class extends" and the constructor call "super(new Class(),"
System.out.println("/**\n * Test for {@link " + className + "}.\n */");
System.out.println("public class " + className + "Test extends AbstractEncryptionMethodTest {");
System.out.println("\n\tpublic " + className + "Test() {");
System.out.println("\t\tsuper(new " + className + "(),");
// Iterate through the GIVEN_PASSWORDS and generate a hash so we can always check it later
String delim = ", ";
for (String password : GIVEN_PASSWORDS) {
if (password.equals(GIVEN_PASSWORDS[GIVEN_PASSWORDS.length - 1])) {
delim = "); ";
}
// Encr. method uses separate salt, so we need to call the constructor that takes HashedPassword instances
if (method.hasSeparateSalt()) {
HashedPassword hashedPassword = method.computeHash(password, USERNAME);
System.out.println(String.format("\t\tnew HashedPassword(\"%s\", \"%s\")%s// %s",
hashedPassword.getHash(), hashedPassword.getSalt(), delim, password));
} else {
// Encryption method doesn't have separate salt, so simply pass the generated hash to the constructor
System.out.println("\t\t\"" + method.computeHash(password, USERNAME).getHash()
+ "\"" + delim + "// " + password);
}
}
// Close the constructor and class declarations
System.out.println("\t}");
System.out.println("\n}");
}

View File

@ -0,0 +1,78 @@
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
<!-- File auto-generated on Thu Dec 31 13:41:44 CET 2015. See hashmethods/hash_algorithms.tpl.md -->
## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely.
Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
BCRYPT | Recommended | 60 | | | Text | |
BCRYPT2Y | Recommended | 60 | | | Text | 22 |
CRAZYCRYPT1 | Do not use | 128 | | | Username | |
DOUBLEMD5 | Do not use | 32 | | | None | |
IPB3 | Acceptable | 32 | | | Text | 5 | Y
JOOMLA | Recommended | 65 | | | Text | 32 |
MD5 | Do not use | 32 | | | None | |
MD5VB | Acceptable | 56 | | | Text | 16 |
MYBB | Acceptable | 32 | | | Text | 8 | Y
PBKDF2 | Does not work | 330 | | | Text | 12 |
PBKDF2DJANGO | Acceptable | 77 | Y | | Text | 12 |
PHPBB | Acceptable | 34 | | | Text | 16 |
PHPFUSION | Do not use | 64 | Y | | | | Y
ROYALAUTH | Do not use | 128 | | | None | |
SALTED2MD5 | Acceptable | 32 | | | Text | | Y
SALTEDSHA512 | Recommended | 128 | | | | | Y
SHA1 | Do not use | 40 | | | None | |
SHA256 | Recommended | 86 | | | Text | 16 |
SHA512 | Do not use | 128 | | | None | |
SMF | Do not use | 40 | | | Username | |
WBB3 | Acceptable | 40 | | | Text | 40 | Y
WBB4 | Does not work | 60 | | | Text | 8 |
WHIRLPOOL | Do not use | 128 | | | None | |
WORDPRESS | Do not use | 34 | | | Text | 9 |
XAUTH | Recommended | 140 | | | Text | 12 |
CUSTOM | | | | | | | |
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
### Columns
#### Algorithm
The algorithm is the hashing algorithm used to store passwords with. Default is SHA256 and is recommended.
You can change the hashing algorithm in the config.yml: under `security`, locate `passwordHash`.
#### Recommendation
The recommendation lists our usage recommendation in terms of how secure it is (not how _well_ the algorithm works!).
- Recommended: The hash algorithm appears to be cryptographically secure and is one we recommend.
- Acceptable: There are safer algorithms that can be chosen but using the algorithm is generally OK.
- Do not use: Hash algorithm isn't sufficiently secure. Use only if required to hook into another system.
- Does not work: The algorithm does not work properly; do not use.
#### Hash Length
The length of the hashes the algorithm produces. Note that the hash length is not (primarily) indicative of
whether an algorithm is secure or not.
#### ASCII
If denoted with a **y**, means that the algorithm is restricted to ASCII characters only, i.e. it will simply ignore
"special characters" such as `ÿ` or `Â`. Note that we do not recommend the use of "special characters" in passwords.
#### Salt Columns
Before hashing, a _salt_ may be appended to the password to make the hash more secure. The following columns describe
the salt the algorithm uses.
<!-- AUTO-GENERATED FILE! Do not edit this directly -->
##### Salt Type
We do not recommend the usage
of any algorithm that doesn't use a randomly generated text as salt. This "salt type" column indicates what type of
salt the algorithm uses:
- Text: randomly generated text (see also the following column, "Length")
- Username: the salt is constructed from the username (bad)
- None: the algorithm uses no salt (bad)
##### Length
If applicable (salt type is "Text"), indicates the length of the generated salt. The longer the better.
If this column is empty when the salt type is "Text", it typically means the salt length can be defined in config.yml.
##### Separate
If denoted with a **y**, it means that the salt is stored in a separate column in the database. This is neither good
or bad.

View File

@ -0,0 +1,135 @@
package hashmethods;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.HexSaltedMethod;
import fr.xephi.authme.security.crypts.description.AsciiRestricted;
import fr.xephi.authme.security.crypts.description.HasSalt;
import fr.xephi.authme.security.crypts.description.Recommendation;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import static com.google.common.collect.Sets.newHashSet;
/**
* Gathers information on {@link fr.xephi.authme.security.crypts.EncryptionMethod} implementations based on
* the annotations in {@link fr.xephi.authme.security.crypts.description}.
*/
public class EncryptionMethodInfoGatherer {
@SuppressWarnings("unchecked")
private final static Set<Class<? extends Annotation>> RELEVANT_ANNOTATIONS =
newHashSet(HasSalt.class, Recommendation.class, AsciiRestricted.class);
private Map<HashAlgorithm, MethodDescription> descriptions;
public EncryptionMethodInfoGatherer() {
descriptions = new LinkedHashMap<>();
constructDescriptions();
}
public Map<HashAlgorithm, MethodDescription> getDescriptions() {
return descriptions;
}
private void constructDescriptions() {
for (HashAlgorithm algorithm : HashAlgorithm.values()) {
Class<? extends EncryptionMethod> methodClazz = algorithm.getClazz();
if (!HashAlgorithm.CUSTOM.equals(algorithm) && !methodClazz.isAnnotationPresent(Deprecated.class)) {
MethodDescription description = createDescription(methodClazz);
descriptions.put(algorithm, description);
}
}
}
private static MethodDescription createDescription(Class<? extends EncryptionMethod> clazz) {
EncryptionMethod method = instantiateMethod(clazz);
MethodDescription description = new MethodDescription(clazz);
description.setHashLength(method.computeHash("test", "user").getHash().length());
description.setHasSeparateSalt(method.hasSeparateSalt());
Map<Class<?>, Annotation> annotationMap = gatherAnnotations(clazz);
if (annotationMap.containsKey(HasSalt.class)) {
setSaltInformation(description, returnTyped(annotationMap, HasSalt.class), method);
}
if (annotationMap.containsKey(Recommendation.class)) {
description.setUsage(returnTyped(annotationMap, Recommendation.class).value());
}
if (annotationMap.containsKey(AsciiRestricted.class)) {
description.setAsciiRestricted(true);
}
return description;
}
private static Map<Class<?>, Annotation> gatherAnnotations(Class<?> methodClass) {
// Note ljacqu 20151231: The map could be Map<Class<? extends Annotation>, Annotation> and it has the constraint
// that for a key Class<T>, the value is of type T. We write a simple "Class<?>" for brevity.
Map<Class<?>, Annotation> collection = new HashMap<>();
Class<?> currentMethodClass = methodClass;
while (currentMethodClass != null) {
getRelevantAnnotations(currentMethodClass, collection);
currentMethodClass = getSuperClass(currentMethodClass);
}
return collection;
}
// Parameters could be Class<? extends EncryptionMethod>; Map<Class<? extends Annotation>, Annotation>
// but the constraint doesn't have any technical relevance, so just clutters the code
private static void getRelevantAnnotations(Class<?> methodClass, Map<Class<?>, Annotation> collection) {
for (Annotation annotation : methodClass.getAnnotations()) {
if (RELEVANT_ANNOTATIONS.contains(annotation.annotationType())
&& !collection.containsKey(annotation.annotationType())) {
collection.put(annotation.annotationType(), annotation);
}
}
}
/**
* Returns the super class of the given encryption method if it is also of EncryptionMethod type.
* (Anything beyond EncryptionMethod is not of interest.)
*/
private static Class<?> getSuperClass(Class<?> methodClass) {
Class<?> zuper = methodClass.getSuperclass();
if (EncryptionMethod.class.isAssignableFrom(zuper)) {
return zuper;
}
return null;
}
/**
* Set the salt information for the given encryption method and the found {@link HasSalt} annotation.
* Also gets the salt length from {@link HexSaltedMethod#getSaltLength()} for such instances.
*
* @param description The description to update
* @param hasSalt The associated HasSalt annotation
* @param method The encryption method
*/
private static void setSaltInformation(MethodDescription description, HasSalt hasSalt, EncryptionMethod method) {
description.setSaltType(hasSalt.value());
if (hasSalt.length() != 0) {
description.setSaltLength(hasSalt.length());
} else if (method instanceof HexSaltedMethod) {
int saltLength = ((HexSaltedMethod) method).getSaltLength();
description.setSaltLength(saltLength);
}
}
private static EncryptionMethod instantiateMethod(Class<? extends EncryptionMethod> clazz) {
try {
return clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Could not instantiate " + clazz, e);
}
}
// Convenience method for retrieving an annotation in a typed fashion.
// We know implicitly that the key of the map always corresponds to the type of the value
private static <T> T returnTyped(Map<Class<?>, Annotation> map, Class<T> key) {
return key.cast(map.get(key));
}
}

View File

@ -0,0 +1,100 @@
package hashmethods;
import fr.xephi.authme.security.HashAlgorithm;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.util.WrapperMock;
import utils.ANewMap;
import utils.FileUtils;
import utils.TagReplacer;
import utils.ToolTask;
import utils.ToolsConstants;
import java.util.Map;
import java.util.Scanner;
/**
* Task for generating the markdown page describing the AuthMe hash algorithms.
*
* @see {@link fr.xephi.authme.security.HashAlgorithm}
*/
public class HashAlgorithmsDescriptionTask implements ToolTask {
private static final String CUR_FOLDER = ToolsConstants.TOOLS_SOURCE_ROOT + "hashmethods/";
private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "hash_algorithms.md";
@Override
public void execute(Scanner scanner) {
// Unfortunately, we need the Wrapper to be around to work with Settings, and certain encryption methods
// directly read from the Settings file
WrapperMock.createInstance();
Settings.bCryptLog2Rounds = 8;
Settings.saltLength = 8;
// Gather info and construct a row for each method
EncryptionMethodInfoGatherer infoGatherer = new EncryptionMethodInfoGatherer();
Map<HashAlgorithm, MethodDescription> descriptions = infoGatherer.getDescriptions();
final String methodRows = constructMethodRows(descriptions);
// Write to the docs file
Map<String, String> tags = ANewMap.with("method_rows", methodRows).build();
FileUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags);
}
private static String constructMethodRows(Map<HashAlgorithm, MethodDescription> descriptions) {
final String rowTemplate = FileUtils.readFromFile(CUR_FOLDER + "hash_algorithms_row.tpl.md");
StringBuilder result = new StringBuilder();
for (Map.Entry<HashAlgorithm, MethodDescription> entry : descriptions.entrySet()) {
MethodDescription description = entry.getValue();
Map<String, String> tags = ANewMap
.with("name", asString(entry.getKey()))
.and("recommendation", asString(description.getUsage()))
.and("hash_length", asString(description.getHashLength()))
.and("ascii_restricted", asString(description.isAsciiRestricted()))
.and("salt_type", asString(description.getSaltType()))
.and("salt_length", asString(description.getSaltLength()))
.and("separate_salt", asString(description.hasSeparateSalt()))
.build();
result.append(TagReplacer.applyReplacements(rowTemplate, tags));
}
return result.toString();
}
@Override
public String getTaskName() {
return "describeHashAlgos";
}
// ----
// String representations
// ----
private static String asString(boolean value) {
return value ? "Y" : "";
}
private static String asString(int value) {
return String.valueOf(value);
}
private static String asString(Integer value) {
if (value == null) {
return "";
}
return String.valueOf(value);
}
private static String asString(HashAlgorithm value) {
return value.toString();
}
private static <E extends Enum<E>> String asString(E value) {
if (value == null) {
return "";
}
// Get the enum name and replace something like "DO_NOT_USE" to "Do not use"
String enumName = value.toString().replace("_", " ");
return enumName.length() > 2
? enumName.substring(0, 1) + enumName.substring(1).toLowerCase()
: enumName;
}
}

View File

@ -0,0 +1,85 @@
package hashmethods;
import fr.xephi.authme.security.crypts.EncryptionMethod;
import fr.xephi.authme.security.crypts.description.SaltType;
import fr.xephi.authme.security.crypts.description.Usage;
/**
* Description of a {@link EncryptionMethod}.
*/
public class MethodDescription {
/** The implementation class the description belongs to. */
private final Class<? extends EncryptionMethod> method;
/** The type of the salt that is used. */
private SaltType saltType;
/** The length of the salt for SaltType.TEXT salts. */
private Integer saltLength;
/** The usage recommendation. */
private Usage usage;
/** Whether or not the encryption method is restricted to ASCII characters for proper functioning. */
private boolean asciiRestricted;
/** Whether or not the encryption method requires its salt stored separately. */
private boolean hasSeparateSalt;
/** The length of the hash output, based on a test hash (i.e. assumes same length for all hashes.) */
private int hashLength;
public MethodDescription(Class<? extends EncryptionMethod> method) {
this.method = method;
}
// Trivial getters and setters
public Class<? extends EncryptionMethod> getMethod() {
return method;
}
public SaltType getSaltType() {
return saltType;
}
public void setSaltType(SaltType saltType) {
this.saltType = saltType;
}
public Integer getSaltLength() {
return saltLength;
}
public void setSaltLength(int saltLength) {
this.saltLength = saltLength;
}
public Usage getUsage() {
return usage;
}
public void setUsage(Usage usage) {
this.usage = usage;
}
public boolean isAsciiRestricted() {
return asciiRestricted;
}
public void setAsciiRestricted(boolean asciiRestricted) {
this.asciiRestricted = asciiRestricted;
}
public boolean hasSeparateSalt() {
return hasSeparateSalt;
}
public void setHasSeparateSalt(boolean hasSeparateSalt) {
this.hasSeparateSalt = hasSeparateSalt;
}
public int getHashLength() {
return hashLength;
}
public void setHashLength(int hashLength) {
this.hashLength = hashLength;
}
}

View File

@ -0,0 +1,53 @@
<!-- {gen_warning} -->
<!-- File auto-generated on {gen_date}. See hashmethods/hash_algorithms.tpl.md -->
## Hash Algorithms
AuthMe supports the following hash algorithms for storing your passwords safely.
Algorithm | Recommendation | Hash length | ASCII | | Salt type | Length | Separate?
--------- | -------------- | ----------- | ----- | --- | --------- | ------ | ---------
{method_rows}CUSTOM | | | | | | | |
<!-- {gen_warning} -->
### Columns
#### Algorithm
The algorithm is the hashing algorithm used to store passwords with. Default is SHA256 and is recommended.
You can change the hashing algorithm in the config.yml: under `security`, locate `passwordHash`.
#### Recommendation
The recommendation lists our usage recommendation in terms of how secure it is (not how _well_ the algorithm works!).
- Recommended: The hash algorithm appears to be cryptographically secure and is one we recommend.
- Acceptable: There are safer algorithms that can be chosen but using the algorithm is generally OK.
- Do not use: Hash algorithm isn't sufficiently secure. Use only if required to hook into another system.
- Does not work: The algorithm does not work properly; do not use.
#### Hash Length
The length of the hashes the algorithm produces. Note that the hash length is not (primarily) indicative of
whether an algorithm is secure or not.
#### ASCII
If denoted with a **y**, means that the algorithm is restricted to ASCII characters only, i.e. it will simply ignore
"special characters" such as `ÿ` or `Â`. Note that we do not recommend the use of "special characters" in passwords.
#### Salt Columns
Before hashing, a _salt_ may be appended to the password to make the hash more secure. The following columns describe
the salt the algorithm uses.
<!-- {gen_warning} -->
##### Salt Type
We do not recommend the usage
of any algorithm that doesn't use a randomly generated text as salt. This "salt type" column indicates what type of
salt the algorithm uses:
- Text: randomly generated text (see also the following column, "Length")
- Username: the salt is constructed from the username (bad)
- None: the algorithm uses no salt (bad)
##### Length
If applicable (salt type is "Text"), indicates the length of the generated salt. The longer the better.
If this column is empty when the salt type is "Text", it typically means the salt length can be defined in config.yml.
##### Separate
If denoted with a **y**, it means that the salt is stored in a separate column in the database. This is neither good
or bad.

View File

@ -0,0 +1 @@
{name} | {recommendation} | {hash_length} | {ascii_restricted} | | {salt_type} | {salt_length} | {separate_salt}