#830 Initialize login process with more specific methods (with password vs. force login)

This commit is contained in:
ljacqu
2016-10-01 00:48:23 +02:00
parent 532dbd5921
commit 8d64c0e5bf
12 changed files with 430 additions and 98 deletions
+1 -1
View File
@@ -157,7 +157,7 @@ public class API {
* @param player The player to log in
*/
public static void forceLogin(Player player) {
management.performLogin(player, "dontneed", true);
management.forceLogin(player);
}
public AuthMe getPlugin() {
@@ -179,7 +179,7 @@ public class NewAPI {
* @param player The player to log in
*/
public void forceLogin(Player player) {
management.performLogin(player, "dontneed", true);
management.forceLogin(player);
}
/**
@@ -37,7 +37,7 @@ public class ForceLoginCommand implements ExecutableCommand {
} else if (!permissionsManager.hasPermission(player, CAN_LOGIN_BE_FORCED)) {
sender.sendMessage("You cannot force login the player " + playerName + "!");
} else {
management.performLogin(player, "dontneed", true);
management.forceLogin(player);
sender.sendMessage("Force login for " + playerName + " performed!");
}
}
@@ -18,6 +18,6 @@ public class LoginCommand extends PlayerCommand {
@Override
public void runCommand(Player player, List<String> arguments) {
final String password = arguments.get(0);
management.performLogin(player, password, false);
management.performLogin(player, password);
}
}
@@ -47,8 +47,12 @@ public class Management {
}
public void performLogin(Player player, String password, boolean forceLogin) {
runTask(() -> asynchronousLogin.login(player, password, forceLogin));
public void performLogin(Player player, String password) {
runTask(() -> asynchronousLogin.login(player, password));
}
public void forceLogin(Player player) {
runTask(() -> asynchronousLogin.forceLogin(player));
}
public void performLogout(Player player) {
@@ -140,7 +140,7 @@ public class AsynchronousJoin implements AsynchronousProcess {
playerCache.removePlayer(name);
if (auth != null && auth.getIp().equals(ip)) {
service.send(player, MessageKey.SESSION_RECONNECTION);
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.login(player, "dontneed", true));
bukkitService.runTaskOptionallyAsync(() -> asynchronousLogin.forceLogin(player));
return;
} else if (service.getProperty(PluginSettings.SESSIONS_EXPIRE_ON_IP_CHANGE)) {
service.send(player, MessageKey.SESSION_EXPIRED);
@@ -239,12 +239,8 @@ public class AsynchronousJoin implements AsynchronousProcess {
&& !"localhost".equalsIgnoreCase(ip)
&& countOnlinePlayersByIp(ip) > service.getProperty(RestrictionSettings.MAX_JOIN_PER_IP)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(new Runnable() {
@Override
public void run() {
player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE));
}
});
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.SAME_IP_ONLINE)));
return false;
}
return true;
@@ -1,5 +1,6 @@
package fr.xephi.authme.process.login;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.cache.CaptchaManager;
import fr.xephi.authme.cache.TempbanManager;
@@ -35,11 +36,12 @@ import java.util.ArrayList;
import java.util.List;
/**
* Asynchronous task for a player login.
*/
public class AsynchronousLogin implements AsynchronousProcess {
@Inject
private DataSource database;
private DataSource dataSource;
@Inject
private ProcessService service;
@@ -71,23 +73,50 @@ public class AsynchronousLogin implements AsynchronousProcess {
@Inject
private PlayerDataTaskManager playerDataTaskManager;
AsynchronousLogin() { }
AsynchronousLogin() {
}
/**
* Processes a player's login request.
*
* @param player the player to log in
* @param password the password to log in with
*/
public void login(Player player, String password) {
PlayerAuth auth = getPlayerAuth(player);
if (auth != null && checkPlayerInfo(player, auth, password)) {
performLogin(player, auth);
}
}
/**
* Logs a player in without requiring a password.
*
* @param player the player to log in
*/
public void forceLogin(Player player) {
PlayerAuth auth = getPlayerAuth(player);
if (auth != null) {
performLogin(player, auth);
}
}
/**
* Checks the precondition for authentication (like user known) and returns
* the playerAuth-State
* the player's {@link PlayerAuth} object.
*
* @return PlayerAuth
* @return the PlayerAuth object, or {@code null} if the player doesn't exist or may not log in
* (e.g. because he is already logged in)
*/
private PlayerAuth preAuth(Player player) {
private PlayerAuth getPlayerAuth(Player player) {
final String name = player.getName().toLowerCase();
if (playerCache.isAuthenticated(name)) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return null;
}
PlayerAuth pAuth = database.getAuth(name);
if (pAuth == null) {
PlayerAuth auth = dataSource.getAuth(name);
if (auth == null) {
service.send(player, MessageKey.USER_NOT_REGISTERED);
// Recreate the message task to immediately send the message again as response
// and to make sure we send the right register message (password vs. email registration)
@@ -96,19 +125,15 @@ public class AsynchronousLogin implements AsynchronousProcess {
}
if (!service.getProperty(DatabaseSettings.MYSQL_COL_GROUP).isEmpty()
&& pAuth.getGroupId() == service.getProperty(HooksSettings.NON_ACTIVATED_USERS_GROUP)) {
&& auth.getGroupId() == service.getProperty(HooksSettings.NON_ACTIVATED_USERS_GROUP)) {
service.send(player, MessageKey.ACCOUNT_NOT_ACTIVATED);
return null;
}
final String ip = Utils.getPlayerIp(player);
if (service.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP) > 0
&& !permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
&& !"127.0.0.1".equalsIgnoreCase(ip) && !"localhost".equalsIgnoreCase(ip)) {
if (isLoggedIp(name, ip)) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return null;
}
if (hasReachedMaxLoggedInPlayersForIp(player, ip)) {
service.send(player, MessageKey.ALREADY_LOGGED_IN_ERROR);
return null;
}
boolean isAsync = service.getProperty(PluginSettings.USE_ASYNC_TASKS);
@@ -117,52 +142,90 @@ public class AsynchronousLogin implements AsynchronousProcess {
if (!event.canLogin()) {
return null;
}
return pAuth;
return auth;
}
public void login(final Player player, String password, boolean forceLogin) {
PlayerAuth pAuth = preAuth(player);
if (pAuth == null) {
return;
}
/**
* Checks various conditions for regular player login (not used in force login).
*
* @param player the player requesting to log in
* @param auth the PlayerAuth object of the player
* @param password the password supplied by the player
* @return true if the password matches and all other conditions are met (e.g. no captcha required),
* false otherwise
*/
private boolean checkPlayerInfo(Player player, PlayerAuth auth, String password) {
final String name = player.getName().toLowerCase();
// If Captcha is required send a message to the player and deny to login
// If captcha is required send a message to the player and deny to log in
if (captchaManager.isCaptchaRequired(name)) {
service.send(player, MessageKey.USAGE_CAPTCHA, captchaManager.getCaptchaCodeOrGenerateNew(name));
return;
return false;
}
final String ip = Utils.getPlayerIp(player);
// Increase the counts here before knowing the result of the login.
// If the login is successful, we clear the captcha count for the player.
captchaManager.increaseCount(name);
tempbanManager.increaseCount(ip, name);
String email = pAuth.getEmail();
boolean passwordVerified = forceLogin || passwordSecurity.comparePassword(
password, pAuth.getPassword(), player.getName());
if (passwordVerified && player.isOnline()) {
PlayerAuth auth = PlayerAuth.builder()
.name(name)
.realName(player.getName())
.ip(ip)
.email(email)
.password(pAuth.getPassword())
.build();
database.updateSession(auth);
if (passwordSecurity.comparePassword(password, auth.getPassword(), player.getName())) {
return true;
} else {
handleWrongPassword(player, ip);
return false;
}
}
/**
* Handles a login with wrong password.
*
* @param player the player who attempted to log in
* @param ip the ip address of the player
*/
private void handleWrongPassword(Player player, String ip) {
ConsoleLogger.fine(player.getName() + " used the wrong password");
if (tempbanManager.shouldTempban(ip)) {
tempbanManager.tempbanPlayer(player);
} else if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD)));
} else {
service.send(player, MessageKey.WRONG_PASSWORD);
// If the authentication fails check if Captcha is required and send a message to the player
if (captchaManager.isCaptchaRequired(player.getName())) {
service.send(player, MessageKey.USAGE_CAPTCHA,
captchaManager.getCaptchaCodeOrGenerateNew(player.getName()));
}
}
}
/**
* Sets the player to the logged in state.
*
* @param player the player to log in
* @param auth the associated PlayerAuth object
*/
private void performLogin(Player player, PlayerAuth auth) {
if (player.isOnline()) {
// Update auth to reflect this new login
final String ip = Utils.getPlayerIp(player);
auth.setRealName(player.getName());
auth.setLastLogin(System.currentTimeMillis());
auth.setIp(ip);
dataSource.updateSession(auth);
// Successful login, so reset the captcha & temp ban count
final String name = player.getName();
captchaManager.resetCounts(name);
tempbanManager.resetCount(ip, name);
player.setNoDamageTicks(0);
if (!forceLogin)
service.send(player, MessageKey.LOGIN_SUCCESS);
service.send(player, MessageKey.LOGIN_SUCCESS);
displayOtherAccounts(auth, player);
final String email = auth.getEmail();
if (service.getProperty(EmailSettings.RECALL_PLAYERS)
&& (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email))) {
service.send(player, MessageKey.ADD_EMAIL_MESSAGE);
@@ -172,7 +235,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
// makes player isLoggedin via API
playerCache.addPlayer(auth);
database.setLogged(name);
dataSource.setLogged(name);
// As the scheduling executes the Task most likely after the current
// task, we schedule it in the end
@@ -183,23 +246,8 @@ public class AsynchronousLogin implements AsynchronousProcess {
playerData.clearTasks();
}
syncProcessManager.processSyncPlayerLogin(player);
} else if (player.isOnline()) {
ConsoleLogger.fine(player.getName() + " used the wrong password");
if (service.getProperty(RestrictionSettings.KICK_ON_WRONG_PASSWORD)) {
bukkitService.scheduleSyncTaskFromOptionallyAsyncTask(
() -> player.kickPlayer(service.retrieveSingleMessage(MessageKey.WRONG_PASSWORD)));
} else if (tempbanManager.shouldTempban(ip)) {
tempbanManager.tempbanPlayer(player);
} else {
service.send(player, MessageKey.WRONG_PASSWORD);
// If the authentication fails check if Captcha is required and send a message to the player
if (captchaManager.isCaptchaRequired(name)) {
service.send(player, MessageKey.USAGE_CAPTCHA, captchaManager.getCaptchaCodeOrGenerateNew(name));
}
}
} else {
ConsoleLogger.warning("Player " + name + " wasn't online during login process, aborted... ");
ConsoleLogger.warning("Player '" + player.getName() + "' wasn't online during login process, aborted...");
}
}
@@ -208,7 +256,7 @@ public class AsynchronousLogin implements AsynchronousProcess {
return;
}
List<String> auths = database.getAllAuthsByIp(auth.getIp());
List<String> auths = dataSource.getAllAuthsByIp(auth.getIp());
if (auths.size() <= 1) {
return;
}
@@ -217,13 +265,13 @@ public class AsynchronousLogin implements AsynchronousProcess {
for (String currentName : auths) {
Player currentPlayer = bukkitService.getPlayerExact(currentName);
if (currentPlayer != null && currentPlayer.isOnline()) {
formattedNames.add(ChatColor.GREEN + currentName);
formattedNames.add(ChatColor.GREEN + currentPlayer.getName() + ChatColor.GRAY);
} else {
formattedNames.add(currentName);
}
}
String message = ChatColor.GRAY + StringUtils.join(ChatColor.GRAY + ", ", formattedNames) + ".";
String message = ChatColor.GRAY + String.join(", ", formattedNames) + ".";
ConsoleLogger.fine("The user " + player.getName() + " has " + auths.size() + " accounts:");
ConsoleLogger.fine(message);
@@ -241,12 +289,31 @@ public class AsynchronousLogin implements AsynchronousProcess {
}
}
private boolean isLoggedIp(String name, String ip) {
/**
* Checks whether the maximum threshold of logged in player per IP address has been reached
* for the given player and IP address.
*
* @param player the player to process
* @param ip the associated ip address
* @return true if the threshold has been reached, false otherwise
*/
@VisibleForTesting
boolean hasReachedMaxLoggedInPlayersForIp(Player player, String ip) {
// Do not perform the check if player has multiple accounts permission or if IP is localhost
if (service.getProperty(RestrictionSettings.MAX_LOGIN_PER_IP) <= 0
|| permissionsManager.hasPermission(player, PlayerStatePermission.ALLOW_MULTIPLE_ACCOUNTS)
|| "127.0.0.1".equalsIgnoreCase(ip)
|| "localhost".equalsIgnoreCase(ip)) {
return false;
}
// Count logged in players with same IP address
final String name = player.getName();
int count = 0;
for (Player player : bukkitService.getOnlinePlayers()) {
if (ip.equalsIgnoreCase(Utils.getPlayerIp(player))
&& database.isLogged(player.getName().toLowerCase())
&& !player.getName().equalsIgnoreCase(name)) {
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
if (ip.equalsIgnoreCase(Utils.getPlayerIp(onlinePlayer))
&& !onlinePlayer.getName().equals(name)
&& dataSource.isLogged(onlinePlayer.getName().toLowerCase())) {
++count;
}
}
@@ -171,10 +171,9 @@ public class AsyncRegister implements AsynchronousProcess {
if (!service.getProperty(RegistrationSettings.FORCE_LOGIN_AFTER_REGISTER) && autoLogin) {
if (service.getProperty(PluginSettings.USE_ASYNC_TASKS)) {
bukkitService.runTaskAsynchronously(() -> asynchronousLogin.login(player, "dontneed", true));
bukkitService.runTaskAsynchronously(() -> asynchronousLogin.forceLogin(player));
} else {
bukkitService.scheduleSyncDelayedTask(
() -> asynchronousLogin.login(player, "dontneed", true), SYNC_LOGIN_DELAY);
bukkitService.scheduleSyncDelayedTask(() -> asynchronousLogin.forceLogin(player), SYNC_LOGIN_DELAY);
}
}
syncProcessManager.processSyncPasswordRegister(player);
@@ -3,7 +3,6 @@ package fr.xephi.authme.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* Utils class for collections.
@@ -60,15 +59,4 @@ public final class CollectionUtils {
public static boolean isEmpty(Collection<?> coll) {
return coll == null || coll.isEmpty();
}
public static <T> List<T> filterCommonStart(List<T> list1, List<T> list2) {
List<T> commonStart = new ArrayList<>();
int minSize = Math.min(list1.size(), list2.size());
int i = 0;
while (i < minSize && Objects.equals(list1.get(i), list2.get(i))) {
commonStart.add(list1.get(i));
++i;
}
return commonStart;
}
}