diff options
Diffstat (limited to 'src/main/java/net')
8 files changed, 254 insertions, 88 deletions
diff --git a/src/main/java/net/elytrium/limboauth/LimboAuth.java b/src/main/java/net/elytrium/limboauth/LimboAuth.java index a901bc2..ba29a20 100644 --- a/src/main/java/net/elytrium/limboauth/LimboAuth.java +++ b/src/main/java/net/elytrium/limboauth/LimboAuth.java @@ -33,6 +33,7 @@ import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.ProxyServer; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.File; import java.io.IOException; import java.net.InetAddress; @@ -40,7 +41,9 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; @@ -54,6 +57,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; +import java.util.stream.Collectors; import net.elytrium.limboapi.api.Limbo; import net.elytrium.limboapi.api.LimboFactory; import net.elytrium.limboapi.api.chunk.Dimension; @@ -82,6 +86,7 @@ import org.slf4j.Logger; authors = {"hevav", "mdxd44"}, dependencies = {@Dependency(id = "limboapi")} ) +@SuppressFBWarnings({"EI_EXPOSE_REP", "MS_EXPOSE_REP"}) public class LimboAuth { private static LimboAuth instance; @@ -92,11 +97,12 @@ public class LimboAuth { private final ProxyServer server; private final LimboFactory factory; + private final Set<String> unsafePasswords = new HashSet<>(); + private Map<String, CachedUser> cachedAuthChecks; private Dao<RegisteredPlayer, String> playerDao; + private Pattern nicknameValidationPattern; private Limbo authServer; - private Map<String, CachedUser> cachedAuthChecks; private Component nicknameInvalid; - private Pattern nicknameValidationPattern; @Inject @SuppressWarnings("OptionalGetWithoutIsPresent") @@ -110,7 +116,7 @@ public class LimboAuth { } @Subscribe - public void onProxyInitialization(ProxyInitializeEvent event) throws SQLException { + public void onProxyInitialization(ProxyInitializeEvent event) throws Exception { System.setProperty("com.j256.simplelogging.level", "ERROR"); this.reload(); @@ -119,20 +125,29 @@ public class LimboAuth { } @SuppressWarnings("SwitchStatementWithTooFewBranches") - public void reload() throws SQLException { + public void reload() throws Exception { Settings.IMP.reload(new File(this.dataDirectory.toFile().getAbsoluteFile(), "config.yml")); + if (Settings.IMP.MAIN.CHECK_PASSWORD_STRENGTH) { + this.unsafePasswords.clear(); + Path unsafePasswordsFile = Paths.get(this.dataDirectory.toFile().getAbsolutePath(), Settings.IMP.MAIN.UNSAFE_PASSWORDS_FILE); + if (!unsafePasswordsFile.toFile().exists()) { + Files.copy(Objects.requireNonNull(this.getClass().getResourceAsStream("/unsafe_passwords.txt")), unsafePasswordsFile); + } + + this.unsafePasswords.addAll(Files.lines(unsafePasswordsFile).collect(Collectors.toSet())); + } + this.cachedAuthChecks = new ConcurrentHashMap<>(); Settings.DATABASE dbConfig = Settings.IMP.DATABASE; - JdbcPooledConnectionSource connectionSource; // requireNonNull prevents the shade plugin from excluding the drivers in minimized jar. switch (dbConfig.STORAGE_TYPE.toLowerCase(Locale.ROOT)) { case "h2": { Objects.requireNonNull(org.h2.Driver.class); Objects.requireNonNull(org.h2.engine.Engine.class); - connectionSource = new JdbcPooledConnectionSource("jdbc:h2:" + this.dataDirectory.toFile().getAbsoluteFile() + "/" + "limboauth"); + connectionSource = new JdbcPooledConnectionSource("jdbc:h2:" + this.dataDirectory.toFile().getAbsoluteFile() + "/limboauth"); break; } case "mysql": { @@ -212,7 +227,7 @@ public class LimboAuth { this.authServer = this.factory.createLimbo(authWorld); - this.nicknameInvalid = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NICKNAME_INVALID); + this.nicknameInvalid = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NICKNAME_INVALID_KICK); this.server.getEventManager().unregisterListeners(this); this.server.getEventManager().register(this, new AuthListener(this.playerDao)); @@ -280,8 +295,8 @@ public class LimboAuth { this.cachedAuthChecks.put(username, new CachedUser(player.getRemoteAddress().getAddress(), System.currentTimeMillis())); } - public void removePlayerFromCache(Player player) { - this.cachedAuthChecks.remove(player.getUsername()); + public void removePlayerFromCache(String username) { + this.cachedAuthChecks.remove(username); } public boolean needAuth(Player player) { @@ -312,7 +327,7 @@ public class LimboAuth { // Send player to auth virtual server. try { - this.authServer.spawnPlayer(player, new AuthSessionHandler(this.playerDao, player, nickname)); + this.authServer.spawnPlayer(player, new AuthSessionHandler(this.playerDao, player, this, nickname)); } catch (Throwable t) { this.getLogger().error("Error", t); } @@ -331,10 +346,6 @@ public class LimboAuth { } } - public Logger getLogger() { - return this.logger; - } - private void checkCache(Map<String, CachedUser> userMap, long time) { userMap.entrySet().stream() .filter(u -> u.getValue().getCheckTime() + time <= System.currentTimeMillis()) @@ -350,6 +361,18 @@ public class LimboAuth { return instance; } + public Set<String> getUnsafePasswords() { + return this.unsafePasswords; + } + + public Logger getLogger() { + return this.logger; + } + + public ProxyServer getServer() { + return this.server; + } + private static class CachedUser { private final InetAddress inetAddress; diff --git a/src/main/java/net/elytrium/limboauth/Settings.java b/src/main/java/net/elytrium/limboauth/Settings.java index 9e59830..0886cdf 100644 --- a/src/main/java/net/elytrium/limboauth/Settings.java +++ b/src/main/java/net/elytrium/limboauth/Settings.java @@ -35,7 +35,18 @@ public class Settings extends Config { public static class MAIN { + @Comment("Maximum time for player to authenticate in milliseconds. If the player stays on the auth limbo for longer than this time, then the player will be kicked.") + public int AUTH_TIME = 60000; public boolean ENABLE_BOSSBAR = true; + @Comment("Available colors: PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE") + public String BOSSBAR_COLOR = "RED"; + @Comment("Available overlays: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20") + public String BOSSBAR_OVERLAY = "NOTCHED_20"; + public int MIN_PASSWORD_LENGTH = 4; + @Comment("Максимальная длинна пароля для BCrypt равняется 71 символу.") + public int MAX_PASSWORD_LENGTH = 71; + public boolean CHECK_PASSWORD_STRENGTH = true; + public String UNSAFE_PASSWORDS_FILE = "unsafe_passwords.txt"; public boolean ONLINE_MODE_NEED_AUTH = true; public boolean FORCE_OFFLINE_UUID = false; @Comment({ @@ -48,8 +59,8 @@ public class Settings extends Config { public boolean REGISTER_NEED_REPEAT_PASSWORD = true; public boolean CHANGE_PASSWORD_NEED_OLD_PASSWORD = true; @Comment({ - "If you want to migrate your database from another plugin, which is not using BCrypt", - "You can set an old hash algorithm to migrate from. Currently, only AUTHME is supported yet" + "If you want to migrate your database from another plugin, which is not using BCrypt.", + "You can set an old hash algorithm to migrate from. Currently, only AUTHME is supported yet." }) public String MIGRATION_HASH = ""; @Comment("Available dimensions: OVERWORLD, NETHER, THE_END") @@ -62,7 +73,7 @@ public class Settings extends Config { public int LOGIN_ATTEMPTS = 3; public int IP_LIMIT_REGISTRATIONS = 3; public int TOTP_RECOVERY_CODES_AMOUNT = 16; - @Comment("Time in milliseconds, when ip limit works, set to 0 for disable") + @Comment("Time in milliseconds, when ip limit works, set to 0 for disable.") public long IP_LIMIT_VALID_TIME = 21600000; @Comment({ "Regex of allowed nicknames", @@ -95,10 +106,23 @@ public class Settings extends Config { public int Z = 0; } + /* + @Create + public Settings.MAIN.EVENTS_PRIORITIES EVENTS_PRIORITIES; + + @Comment("Available priorities: FIRST, EARLY, NORMAL, LATE, LAST") + public static class EVENTS_PRIORITIES { + + public String PRE_LOGIN = "NORMAL"; + public String LOGIN_LIMBO_REGISTER = "NORMAL"; + public String SAFE_GAME_PROFILE_REQUEST = "NORMAL"; + } + */ + @Create public MAIN.STRINGS STRINGS; - //@Comment("Leave empty to disable.") + @Comment("Leave title fields empty to disable.") public static class STRINGS { public String RELOAD = "{PRFX} &aReloaded successfully!"; @@ -109,31 +133,41 @@ public class Settings extends Config { public String NOT_REGISTERED = "{PRFX} &cYou are not registered!"; public String WRONG_PASSWORD = "{PRFX} &cPassword is wrong!"; - public String NICKNAME_INVALID = "{NL}{NL}&cYour nickname contains forbidden characters. Please, change your nickname!"; + public String NICKNAME_INVALID_KICK = "{PRFX}{NL}&cYour nickname contains forbidden characters. Please, change your nickname!"; + @Comment("6 hours by default in ip-limit-valid-time") public String IP_LIMIT = "{PRFX} &cYour IP has reached max registered accounts. If this is an error, restart your router, or wait about 6 hours."; - public String WRONG_NICKNAME_CASE = "{NL}{NL}&cThe case of your nickname is wrong. Nickname is CaSe SeNsItIvE."; + public String WRONG_NICKNAME_CASE_KICK = "{PRFX}{NL}&cThe case of your nickname is wrong. Nickname is CaSe SeNsItIvE."; - public String LOGIN = "{PRFX} Please, login using &6/login &6<password>. You have &6{0} &cattempts."; - public String LOGIN_SUCCESS = "{PRFX} &aSuccessfully logged in!"; - public String LOGIN_WRONG_PASSWORD = "{PRFX} &cYou've entered the wrong password. You have &6{0} &cattempts left."; - public String LOGIN_TITLE = ""; - public String LOGIN_SUBTITLE = ""; - public String LOGIN_SUCCESS_TITLE = ""; - public String LOGIN_SUCCESS_SUBTITLE = ""; + public String BOSSBAR = "{PRFX} У вас осталось &6{0} &fсекунд чтобы авторизироваться."; + public String TIMES_UP = "{PRFX}{NL}&cВремя авторизации вышло."; + + public String LOGIN = "{PRFX} &aPlease, login using &6/login <password>&a, you have &6{0} &aattempts."; + public String LOGIN_WRONG_PASSWORD = "{PRFX} &cYou''ve entered the wrong password, you have &6{0} &cattempts left."; + public String LOGIN_WRONG_PASSWORD_KICK = "{PRFX}{NL}&cYou've entered the wrong password numerous times!"; + public String LOGIN_SUCCESSFUL = "{PRFX} &aSuccessfully logged in!"; + public String LOGIN_TITLE = "&fPlease, login using &6/login <password>&a."; + public String LOGIN_SUBTITLE = "&aYou have &6{0} &aattempts."; + public String LOGIN_SUCCESSFUL_TITLE = "{PRFX}"; + public String LOGIN_SUCCESSFUL_SUBTITLE = "&aSuccessfully logged in!"; @Comment("Or if register-need-repeat-password set to false remove the \"<repeat password>\" part.") public String REGISTER = "{PRFX} Please, register using &6/register <password> <repeat password>"; - public String REGISTER_TITLE = ""; - public String REGISTER_SUBTITLE = ""; - public String DIFFERENT_PASSWORDS = "{PRFX} The entered passwords differ from each other."; - public String KICK_PASSWORD_WRONG = "{NL}{NL}&cYou've entered the wrong password numerous times!"; - - public String UNREGISTER_SUCCESSFUL = "{PRFX}{NL}{NL}&aSuccessfully unregistered!"; + public String REGISTER_DIFFERENT_PASSWORDS = "{PRFX} &cThe entered passwords differ from each other!"; + public String REGISTER_PASSWORD_TOO_SHORT = "{PRFX} &cYou entered too short password, use a different one!"; + public String REGISTER_PASSWORD_TOO_LONG = "{PRFX} &cYou entered too long password, use a different one!"; + public String REGISTER_PASSWORD_UNSAFE = "{PRFX} &cYour password is unsafe, use a different one!"; + public String REGISTER_SUCCESSFUL = "{PRFX} &aSuccessfully registered!"; + public String REGISTER_TITLE = "{PRFX}"; + public String REGISTER_SUBTITLE = "&aPlease, register using &6/register <password> <repeat password>"; + public String REGISTER_SUCCESSFUL_TITLE = "{PRFX}"; + public String REGISTER_SUCCESSFUL_SUBTITLE = "&aSuccessfully registered!"; + + public String UNREGISTER_SUCCESSFUL = "{PRFX}{NL}&aSuccessfully unregistered!"; public String UNREGISTER_USAGE = "{PRFX} Usage: &6/unregister <current password> confirm"; public String FORCE_UNREGISTER_SUCCESSFUL = "{PRFX} &a{0} successfully unregistered!"; - public String FORCE_UNREGISTER_SUCCESSFUL_PLAYER = "{PRFX}{NL}{NL}&aYou have been unregistered by administrator!"; + public String FORCE_UNREGISTER_KICK = "{PRFX}{NL}&aYou have been unregistered by administrator!"; public String FORCE_UNREGISTER_NOT_SUCCESSFUL = "{PRFX} &cUnable to unregister {0}. Most likely this player has never been on this server."; public String FORCE_UNREGISTER_USAGE = "{PRFX} Usage: &6/forceunregister <nickname>"; @@ -142,6 +176,8 @@ public class Settings extends Config { public String CHANGE_PASSWORD_USAGE = "{PRFX} Usage: &6/changepassword <old password> <new password>"; public String TOTP = "{PRFX} Please, enter your 2FA key using &6/2fa <key>"; + public String TOTP_TITLE = "{PRFX}"; + public String TOTP_SUBTITLE = "&aEnter your 2FA key using &6/2fa <key>"; public String TOTP_SUCCESSFUL = "{PRFX} &aSuccessfully enabled 2FA!"; public String TOTP_DISABLED = "{PRFX} &aSuccessfully disabled 2FA!"; @Comment("Or if totp-need-pass set to false remove the \"<current password>\" part.") diff --git a/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java b/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java index 27dff72..b0e6cf4 100644 --- a/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java @@ -49,7 +49,7 @@ public class DestroySessionCommand implements SimpleCommand { return; } - this.plugin.removePlayerFromCache((Player) source); + this.plugin.removePlayerFromCache(((Player) source).getUsername()); source.sendMessage(this.successful); } diff --git a/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java b/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java index d45eae9..2b97143 100644 --- a/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java @@ -40,7 +40,7 @@ public class ForceUnregisterCommand implements SimpleCommand { private final ProxyServer server; private final Dao<RegisteredPlayer, String> playerDao; - private final Component successfulPlayer; + private final Component kick; private final String successful; private final String notSuccessful; private final Component usage; @@ -50,7 +50,7 @@ public class ForceUnregisterCommand implements SimpleCommand { this.server = server; this.playerDao = playerDao; - this.successfulPlayer = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_SUCCESSFUL_PLAYER); + this.kick = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_KICK); this.successful = Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_SUCCESSFUL; this.notSuccessful = Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_NOT_SUCCESSFUL; this.usage = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_USAGE); @@ -83,9 +83,9 @@ public class ForceUnregisterCommand implements SimpleCommand { String playerNick = args[0]; try { this.playerDao.deleteById(playerNick.toLowerCase(Locale.ROOT)); + this.plugin.removePlayerFromCache(playerNick); this.server.getPlayer(playerNick).ifPresent(player -> { - this.plugin.removePlayerFromCache(player); - player.disconnect(this.successfulPlayer); + player.disconnect(this.kick); }); source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(this.successful, playerNick))); } catch (SQLException e) { diff --git a/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java b/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java index aeab6ec..5fe8643 100644 --- a/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java @@ -67,13 +67,14 @@ public class UnregisterCommand implements SimpleCommand { if (args.length == 2) { if (args[1].equalsIgnoreCase("confirm")) { - RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, ((Player) source).getUsername()); + String username = ((Player) source).getUsername(); + RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, username); if (player == null) { source.sendMessage(this.notRegistered); } else if (AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { try { - this.playerDao.deleteById(((Player) source).getUsername().toLowerCase(Locale.ROOT)); - this.plugin.removePlayerFromCache((Player) source); + this.playerDao.deleteById(username.toLowerCase(Locale.ROOT)); + this.plugin.removePlayerFromCache(username); ((Player) source).disconnect(this.successful); } catch (SQLException e) { source.sendMessage(this.errorOccurred); diff --git a/src/main/java/net/elytrium/limboauth/config/Config.java b/src/main/java/net/elytrium/limboauth/config/Config.java index ed5b007..1cb9803 100644 --- a/src/main/java/net/elytrium/limboauth/config/Config.java +++ b/src/main/java/net/elytrium/limboauth/config/Config.java @@ -208,7 +208,7 @@ public class Config { } } - private void save(PrintWriter writer, Class<?> clazz, final Object instance, int indent) { + private void save(PrintWriter writer, Class<?> clazz, Object instance, int indent) { try { String lineSeparator = System.lineSeparator(); String spacing = this.repeat(" ", indent); @@ -356,9 +356,14 @@ public class Config { private void setAccessible(Field field) throws NoSuchFieldException, IllegalAccessException { field.setAccessible(true); if (Modifier.isFinal(field.getModifiers())) { - Field modifiersField = Field.class.getDeclaredField("modifiers"); - modifiersField.setAccessible(true); - modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + if (Runtime.version().feature() < 12) { + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + } else { + // TODO: Maybe use sun.misc.Unsafe?... + throw new UnsupportedOperationException(); + } } } @@ -377,7 +382,7 @@ public class Config { return array[0].toString(); } default: { - final StringBuilder result = new StringBuilder(); + StringBuilder result = new StringBuilder(); for (int i = 0, j = array.length; i < j; ++i) { if (i > 0) { result.append(delimiter); diff --git a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java index 38a464d..59f5074 100644 --- a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java +++ b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java @@ -20,6 +20,7 @@ package net.elytrium.limboauth.handler; import at.favre.lib.crypto.bcrypt.BCrypt; import com.j256.ormlite.dao.Dao; import com.velocitypowered.api.proxy.Player; +import com.velocitypowered.api.scheduler.ScheduledTask; import dev.samstevens.totp.code.CodeVerifier; import dev.samstevens.totp.code.DefaultCodeGenerator; import dev.samstevens.totp.code.DefaultCodeVerifier; @@ -30,6 +31,7 @@ import java.text.MessageFormat; import java.util.List; import java.util.Locale; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import net.elytrium.limboapi.api.Limbo; import net.elytrium.limboapi.api.LimboSessionHandler; @@ -38,7 +40,10 @@ import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.migration.MigrationHash; import net.elytrium.limboauth.model.RegisteredPlayer; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.title.Title; public class AuthSessionHandler implements LimboSessionHandler { @@ -47,16 +52,27 @@ public class AuthSessionHandler implements LimboSessionHandler { private final Dao<RegisteredPlayer, String> playerDao; private final Player proxyPlayer; private final RegisteredPlayer playerInfo; + private final LimboAuth plugin; + + private final long joinTime = System.currentTimeMillis(); + private final BossBar bossBar = BossBar.bossBar( + Component.empty(), + 1, + BossBar.Color.valueOf(Settings.IMP.MAIN.BOSSBAR_COLOR.toUpperCase(Locale.ROOT)), + BossBar.Overlay.valueOf(Settings.IMP.MAIN.BOSSBAR_OVERLAY.toUpperCase(Locale.ROOT)) + ); + private ScheduledTask authMainTask; private LimboPlayer player; private String ip; private int attempts = Settings.IMP.MAIN.LOGIN_ATTEMPTS; private boolean totp = false; - public AuthSessionHandler(Dao<RegisteredPlayer, String> playerDao, Player proxyPlayer, String lowercaseNickname) { + public AuthSessionHandler(Dao<RegisteredPlayer, String> playerDao, Player proxyPlayer, LimboAuth plugin, String lowercaseNickname) { this.playerDao = playerDao; this.proxyPlayer = proxyPlayer; this.playerInfo = this.fetchInfo(lowercaseNickname); + this.plugin = plugin; } @Override @@ -71,7 +87,24 @@ public class AuthSessionHandler implements LimboSessionHandler { this.checkCase(); } - this.sendMessage(); + boolean bossBarEnabled = Settings.IMP.MAIN.ENABLE_BOSSBAR; + float bossBarMultiplier = 1000F / Settings.IMP.MAIN.AUTH_TIME; + if (bossBarEnabled) { + this.proxyPlayer.showBossBar(this.bossBar); + } + this.authMainTask = this.plugin.getServer().getScheduler().buildTask(this.plugin, () -> { + if (System.currentTimeMillis() - this.joinTime > Settings.IMP.MAIN.AUTH_TIME) { + this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.TIMES_UP)); + return; + } + if (bossBarEnabled) { + long timeSinceJoin = Settings.IMP.MAIN.AUTH_TIME - (System.currentTimeMillis() - AuthSessionHandler.this.joinTime); + this.bossBar.name(this.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.BOSSBAR, (int) (timeSinceJoin / 1000)))); + this.bossBar.progress((timeSinceJoin * bossBarMultiplier) / 1000); + } + }).repeat(1, TimeUnit.SECONDS).schedule(); + + this.sendMessage(true); } @Override @@ -82,11 +115,22 @@ public class AuthSessionHandler implements LimboSessionHandler { case "/reg": case "/register": case "/r": { - if (!this.totp && this.playerInfo == null && this.checkPasswordsRepeat(args)) { - this.register(args[1]); - this.finishAuth(); + if (!this.totp && this.playerInfo == null) { + if (this.checkPasswordsRepeat(args) && this.checkPasswordLength(args[1]) && this.checkPasswordStrength(args[1])) { + this.register(args[1]); + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL)); + if (!Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_SUBTITLE.isEmpty()) { + this.proxyPlayer.showTitle( + Title.title( + this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_TITLE), + this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_SUBTITLE) + ) + ); + } + this.finishAuth(); + } } else { - this.sendMessage(); + this.sendMessage(false); } break; } @@ -95,18 +139,14 @@ public class AuthSessionHandler implements LimboSessionHandler { case "/l": { if (!this.totp && this.playerInfo != null) { if (this.checkPassword(args[1])) { - this.finishOrTotp(); + this.loginOrTotp(); } else if (--this.attempts != 0) { - this.proxyPlayer.sendMessage( - LegacyComponentSerializer.legacyAmpersand().deserialize( - MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD, this.attempts) - ) - ); + this.proxyPlayer.sendMessage(this.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD, this.attempts))); } else { - this.proxyPlayer.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.KICK_PASSWORD_WRONG)); + this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD_KICK)); } } else { - this.sendMessage(); + this.sendMessage(false); } break; } @@ -114,23 +154,31 @@ public class AuthSessionHandler implements LimboSessionHandler { case "/2fa": { if (this.totp) { if (verifier.isValidCode(this.playerInfo.getTotpToken(), args[1])) { - this.finishAuth(); + this.finishLogin(); } else { - this.sendMessage(); + this.sendMessage(false); } } else { - this.sendMessage(); + this.sendMessage(false); } break; } default: { - this.sendMessage(); - break; + this.sendMessage(false); } } } else { - this.sendMessage(); + this.sendMessage(false); + } + } + + @Override + public void onDisconnect() { + if (this.authMainTask != null) { + this.authMainTask.cancel(); } + + this.proxyPlayer.hideBossBar(this.bossBar); } public static RegisteredPlayer fetchInfo(Dao<RegisteredPlayer, String> playerDao, String nickname) { @@ -163,10 +211,12 @@ public class AuthSessionHandler implements LimboSessionHandler { return verifier; } + private boolean checkPassword(String password) { + return checkPassword(password, this.playerInfo, this.playerDao); + } + public static boolean checkPassword(String password, RegisteredPlayer player, Dao<RegisteredPlayer, String> playerDao) { - boolean isCorrect = BCrypt.verifyer().verify( - password.getBytes(StandardCharsets.UTF_8), player.getHash().getBytes(StandardCharsets.UTF_8) - ).verified; + boolean isCorrect = BCrypt.verifyer().verify(password.getBytes(StandardCharsets.UTF_8), player.getHash().getBytes(StandardCharsets.UTF_8)).verified; if (!isCorrect && !Settings.IMP.MAIN.MIGRATION_HASH.isEmpty()) { isCorrect = MigrationHash.valueOf(Settings.IMP.MAIN.MIGRATION_HASH).checkPassword(player.getHash(), password); @@ -184,10 +234,6 @@ public class AuthSessionHandler implements LimboSessionHandler { return isCorrect; } - private boolean checkPassword(String password) { - return checkPassword(password, this.playerInfo, this.playerDao); - } - private void checkIp() { try { List<RegisteredPlayer> alreadyRegistered = this.playerDao.queryForEq("IP", this.ip); @@ -215,7 +261,7 @@ public class AuthSessionHandler implements LimboSessionHandler { } if (sizeOfValid.get() >= Settings.IMP.MAIN.IP_LIMIT_REGISTRATIONS) { - this.proxyPlayer.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.IP_LIMIT)); + this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.IP_LIMIT)); } } catch (SQLException e) { e.printStackTrace(); @@ -224,7 +270,7 @@ public class AuthSessionHandler implements LimboSessionHandler { private void checkCase() { if (!this.proxyPlayer.getUsername().equals(this.playerInfo.getNickname())) { - this.proxyPlayer.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.WRONG_NICKNAME_CASE)); + this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_NICKNAME_CASE_KICK)); } } @@ -247,36 +293,86 @@ public class AuthSessionHandler implements LimboSessionHandler { } } - private void finishOrTotp() { + private void loginOrTotp() { if (this.playerInfo.getTotpToken().isEmpty()) { - this.finishAuth(); + this.finishLogin(); } else { this.totp = true; - this.sendMessage(); + this.sendMessage(true); } } + private void finishLogin() { + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL)); + if (!Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_SUBTITLE.isEmpty()) { + this.proxyPlayer.showTitle( + Title.title( + this.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_TITLE), + this.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_SUBTITLE) + ) + ); + } + this.finishAuth(); + } + private void finishAuth() { - this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESS)); - LimboAuth.getInstance().cacheAuthUser(this.proxyPlayer); + this.plugin.cacheAuthUser(this.proxyPlayer); this.player.disconnect(); } - private void sendMessage() { + private void sendMessage(boolean sendTitle) { if (this.totp) { - this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP)); + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.TOTP)); + if (sendTitle && !Settings.IMP.MAIN.STRINGS.TOTP_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.TOTP_SUBTITLE.isEmpty()) { + this.proxyPlayer.showTitle( + Title.title(this.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_TITLE), this.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_SUBTITLE)) + ); + } } else if (this.playerInfo == null) { - this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.REGISTER)); + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER)); + if (sendTitle && !Settings.IMP.MAIN.STRINGS.REGISTER_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.REGISTER_SUBTITLE.isEmpty()) { + this.proxyPlayer.showTitle( + Title.title(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_TITLE), this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUBTITLE)) + ); + } } else { - this.proxyPlayer.sendMessage( - LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN, this.attempts)) - ); + this.proxyPlayer.sendMessage(this.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN, this.attempts))); + if (sendTitle && !Settings.IMP.MAIN.STRINGS.LOGIN_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.LOGIN_SUBTITLE.isEmpty()) { + this.proxyPlayer.showTitle( + Title.title( + this.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_TITLE, this.attempts)), + this.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_SUBTITLE, this.attempts)) + ) + ); + } } } + private boolean checkPasswordLength(String password) { + int length = password.length(); + if (length > Settings.IMP.MAIN.MAX_PASSWORD_LENGTH) { + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_TOO_LONG)); + return false; + } else if (length < Settings.IMP.MAIN.MIN_PASSWORD_LENGTH) { + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_TOO_SHORT)); + return false; + } + + return true; + } + + private boolean checkPasswordStrength(String password) { + if (Settings.IMP.MAIN.CHECK_PASSWORD_STRENGTH && this.plugin.getUnsafePasswords().contains(password)) { + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_UNSAFE)); + return false; + } + + return true; + } + private boolean checkPasswordsRepeat(String[] args) { if (Settings.IMP.MAIN.REGISTER_NEED_REPEAT_PASSWORD && !args[1].equals(args[2])) { - this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.DIFFERENT_PASSWORDS)); + this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_DIFFERENT_PASSWORDS)); return false; } @@ -291,6 +387,10 @@ public class AuthSessionHandler implements LimboSessionHandler { } } + private Component deserialize(String text) { + return LegacyComponentSerializer.legacyAmpersand().deserialize(text); + } + public static String genHash(String password) { return BCrypt.withDefaults().hashToString(Settings.IMP.MAIN.BCRYPT_COST, password.toCharArray()); } diff --git a/src/main/java/net/elytrium/limboauth/listener/AuthListener.java b/src/main/java/net/elytrium/limboauth/listener/AuthListener.java index 2892d79..4189c12 100644 --- a/src/main/java/net/elytrium/limboauth/listener/AuthListener.java +++ b/src/main/java/net/elytrium/limboauth/listener/AuthListener.java @@ -31,6 +31,7 @@ import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.model.RegisteredPlayer; +// TODO: Customizable events priority public class AuthListener { private final Dao<RegisteredPlayer, String> playerDao; |