diff options
29 files changed, 1004 insertions, 1404 deletions
diff --git a/build.gradle b/build.gradle index 30dedde..f5069f6 100644 --- a/build.gradle +++ b/build.gradle @@ -3,24 +3,24 @@ plugins { id("java") id("checkstyle") - id("com.github.spotbugs").version("5.0.6") + id("maven-publish") + id("com.github.spotbugs").version("5.0.7") id("org.cadixdev.licenser").version("0.6.1") id("com.github.johnrengelman.shadow").version("7.1.2") - id("maven-publish") } setGroup("net.elytrium") setVersion("1.0.4-SNAPSHOT") -compileJava { - getOptions().setEncoding("UTF-8") -} - java { setSourceCompatibility(JavaVersion.VERSION_11) setTargetCompatibility(JavaVersion.VERSION_11) } +compileJava { + getOptions().setEncoding("UTF-8") +} + repositories { mavenCentral() @@ -38,39 +38,6 @@ repositories { } } -task javadocJar(type: Jar) { - getArchiveClassifier().set("javadoc") - from(javadoc) -} - -task sourcesJar(type: Jar) { - getArchiveClassifier().set("sources") - from(sourceSets.main.getAllSource()) -} - -publishing { - repositories { - maven { - credentials { - setUsername(System.getenv("PUBLISH_USERNAME")) - setPassword(System.getenv("PUBLISH_PASSWORD")) - } - - setName("elytrium-repo") - setUrl("https://maven.elytrium.net/repo/") - } - } - - publications { - maven(MavenPublication) { - from(components.java) - - artifact(javadocJar) - artifact(sourcesJar) - } - } -} - dependencies { compileOnly("net.elytrium:limboapi-api:1.0.4-SNAPSHOT") @@ -85,13 +52,13 @@ dependencies { implementation("com.j256.ormlite:ormlite-jdbc:6.1") implementation("com.h2database:h2:1.4.200") - implementation("mysql:mysql-connector-java:8.0.28") - implementation("org.postgresql:postgresql:42.3.3") + implementation("mysql:mysql-connector-java:8.0.29") + implementation("org.postgresql:postgresql:42.3.5") implementation("org.bstats:bstats-velocity:3.0.0") implementation("de.mkammerer:argon2-jvm-nolibs:2.11") - compileOnly("com.github.spotbugs:spotbugs-annotations:4.6.0") + compileOnly("com.github.spotbugs:spotbugs-annotations:4.7.0") } shadowJar { @@ -171,6 +138,56 @@ spotbugsMain { } } +task javadocJar(type: Jar) { + getArchiveClassifier().set("javadoc") + from(javadoc) +} + +task sourcesJar(type: Jar) { + getArchiveClassifier().set("sources") + from(sourceSets.main.getAllSource()) +} + +publishing { + repositories { + maven { + credentials { + setUsername(System.getenv("PUBLISH_USERNAME")) + setPassword(System.getenv("PUBLISH_PASSWORD")) + } + + setName("elytrium-repo") + setUrl("https://maven.elytrium.net/repo/") + } + } + + publications { + maven(MavenPublication) { + from(getComponents().java) + + artifact(javadocJar) + artifact(sourcesJar) + } + } +} + +javadoc { + getOptions().setEncoding("UTF-8") + getOptions().setCharSet("UTF-8") + getOptions().setSource("11") + getOptions().links("https://docs.oracle.com/en/java/javase/11/docs/api/") + + // Remove "undefined" from search paths when generating javadoc for a non-modular project. (JDK-8215291) + if (JavaVersion.current() == JavaVersion.VERSION_11) { + getOptions().addBooleanOption("-no-module-directories", true) + } +} + +artifacts { + archives(javadocJar) + archives(sourcesJar) +} + sourceSets.main.getJava().srcDir(getTasks().register("generateTemplates", Copy) { task -> task.getInputs().properties("version": getVersion()) diff --git a/config/spotbugs/suppressions.xml b/config/spotbugs/suppressions.xml index eb21bfb..833396e 100644 --- a/config/spotbugs/suppressions.xml +++ b/config/spotbugs/suppressions.xml @@ -7,4 +7,11 @@ <Match> <Bug pattern="EI_EXPOSE_REP2"/> </Match> + <Match> + <Bug pattern="THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"/> + </Match> + <!-- False-positive check for Try-Catch Block With Resources. --> + <Match> + <Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/> + </Match> </FindBugsFilter> diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 00e33ed..aa991fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/net/elytrium/limboauth/LimboAuth.java b/src/main/java/net/elytrium/limboauth/LimboAuth.java index afce924..55fee88 100644 --- a/src/main/java/net/elytrium/limboauth/LimboAuth.java +++ b/src/main/java/net/elytrium/limboauth/LimboAuth.java @@ -20,15 +20,15 @@ package net.elytrium.limboauth; import com.google.inject.Inject; import com.j256.ormlite.dao.Dao; import com.j256.ormlite.dao.DaoManager; +import com.j256.ormlite.dao.GenericRawResults; +import com.j256.ormlite.db.DatabaseType; import com.j256.ormlite.field.FieldType; import com.j256.ormlite.jdbc.JdbcPooledConnectionSource; import com.j256.ormlite.stmt.QueryBuilder; +import com.j256.ormlite.table.TableInfo; import com.j256.ormlite.table.TableUtils; -import com.mojang.brigadier.tree.CommandNode; import com.velocitypowered.api.command.CommandManager; -import com.velocitypowered.api.command.CommandMeta; -import com.velocitypowered.api.command.CommandSource; -import com.velocitypowered.api.command.SimpleCommand; +import com.velocitypowered.api.event.EventManager; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; import com.velocitypowered.api.plugin.Dependency; @@ -37,6 +37,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; @@ -50,8 +51,6 @@ 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.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -66,10 +65,15 @@ import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; +import net.elytrium.java.commons.mc.serialization.Serializer; +import net.elytrium.java.commons.mc.serialization.Serializers; +import net.elytrium.java.commons.updates.UpdatesChecker; import net.elytrium.limboapi.api.Limbo; import net.elytrium.limboapi.api.LimboFactory; import net.elytrium.limboapi.api.chunk.Dimension; import net.elytrium.limboapi.api.chunk.VirtualWorld; +import net.elytrium.limboapi.api.command.LimboCommandMeta; import net.elytrium.limboapi.api.file.SchematicFile; import net.elytrium.limboapi.api.file.StructureFile; import net.elytrium.limboapi.api.file.WorldFile; @@ -90,12 +94,13 @@ import net.elytrium.limboauth.floodgate.FloodgateApiHolder; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.listener.AuthListener; import net.elytrium.limboauth.model.RegisteredPlayer; -import net.elytrium.limboauth.utils.UpdatesChecker; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.ComponentSerializer; import net.kyori.adventure.title.Title; import org.bstats.charts.SimplePie; import org.bstats.charts.SingleLineChart; import org.bstats.velocity.Metrics; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; @@ -115,32 +120,52 @@ import org.slf4j.Logger; ) public class LimboAuth { + @MonotonicNonNull + private static Logger LOGGER; + @MonotonicNonNull + private static Serializer SERIALIZER; + private final Map<String, CachedSessionUser> cachedAuthChecks = new ConcurrentHashMap<>(); private final Map<String, CachedPremiumUser> premiumCache = new ConcurrentHashMap<>(); private final Map<UUID, Runnable> postLoginTasks = new ConcurrentHashMap<>(); private final Set<String> unsafePasswords = new HashSet<>(); private final HttpClient client = HttpClient.newHttpClient(); + private final ProxyServer server; - private final Logger logger; private final Metrics.Factory metricsFactory; private final Path dataDirectory; + private final File dataDirectoryFile; + private final File configFile; private final LimboFactory factory; private final FloodgateApiHolder floodgateApi; - private JdbcPooledConnectionSource connectionSource; + @Nullable + private Component loginPremium; + @Nullable + private Title loginPremiumTitle; + @Nullable + private Component loginFloodgate; + @Nullable + private Title loginFloodgateTitle; + private Component nicknameInvalidKick; + private JdbcPooledConnectionSource connectionSource; private Dao<RegisteredPlayer, String> playerDao; private Pattern nicknameValidationPattern; private Limbo authServer; @Inject - public LimboAuth(ProxyServer server, Logger logger, Metrics.Factory metricsFactory, @DataDirectory Path dataDirectory) { + public LimboAuth(Logger logger, ProxyServer server, Metrics.Factory metricsFactory, @DataDirectory Path dataDirectory) { + setLogger(logger); + this.server = server; - this.logger = logger; this.metricsFactory = metricsFactory; this.dataDirectory = dataDirectory; + this.dataDirectoryFile = dataDirectory.toFile(); + this.configFile = new File(this.dataDirectoryFile, "config.yml"); + this.factory = (LimboFactory) this.server.getPluginManager().getPlugin("limboapi").flatMap(PluginContainer::getInstance).orElseThrow(); if (this.server.getPluginManager().getPlugin("floodgate").isPresent()) { @@ -152,11 +177,11 @@ public class LimboAuth { @Subscribe public void onProxyInitialization(ProxyInitializeEvent event) throws Exception { - Metrics metrics = this.metricsFactory.make(this, 13700); System.setProperty("com.j256.simplelogging.level", "ERROR"); this.reload(); + Metrics metrics = this.metricsFactory.make(this, 13700); metrics.addCustomChart(new SimplePie("floodgate_auth", () -> String.valueOf(Settings.IMP.MAIN.FLOODGATE_NEED_AUTH))); metrics.addCustomChart(new SimplePie("premium_auth", () -> String.valueOf(Settings.IMP.MAIN.ONLINE_MODE_NEED_AUTH))); metrics.addCustomChart(new SimplePie("db_type", () -> Settings.IMP.DATABASE.STORAGE_TYPE)); @@ -166,54 +191,92 @@ public class LimboAuth { metrics.addCustomChart(new SimplePie("save_uuid", () -> String.valueOf(Settings.IMP.MAIN.SAVE_UUID))); metrics.addCustomChart(new SingleLineChart("registered_players", () -> Math.toIntExact(this.playerDao.countOf()))); - UpdatesChecker.checkForUpdates(this.getLogger()); + if (!UpdatesChecker.checkVersionByURL("https://raw.githubusercontent.com/Elytrium/LimboAuth/master/VERSION", Settings.IMP.VERSION)) { + LOGGER.error("****************************************"); + LOGGER.warn("The new LimboAuth update was found, please update."); + LOGGER.error("https://github.com/Elytrium/LimboAuth/releases/"); + LOGGER.error("****************************************"); + } } + @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH", justification = "LEGACY_AMPERSAND can't be null in velocity.") public void reload() throws Exception { - Settings.IMP.reload(new File(this.dataDirectory.toFile().getAbsoluteFile(), "config.yml")); + Settings.IMP.reload(this.configFile, Settings.IMP.PREFIX); if (this.floodgateApi == null && !Settings.IMP.MAIN.FLOODGATE_NEED_AUTH) { - throw new IllegalStateException("If you don't need to auth floodgate players please install floodgate plugin."); + throw new IllegalStateException("If you want floodgate players to automatically pass auth (floodgate-need-auth: false)," + + " please install floodgate plugin."); + } + + ComponentSerializer<Component, Component, String> serializer = Serializers.valueOf(Settings.IMP.SERIALIZER.toUpperCase(Locale.ROOT)).getSerializer(); + if (serializer == null) { + LOGGER.warn("The specified serializer could not be founded, using default. (LEGACY_AMPERSAND)"); + setSerializer(new Serializer(Objects.requireNonNull(Serializers.LEGACY_AMPERSAND.getSerializer()))); + } else { + setSerializer(new Serializer(serializer)); } + TaskEvent.reload(); + AuthSessionHandler.reload(); + + this.loginPremium = Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM.isEmpty() ? null : SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM); + if (Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_SUBTITLE.isEmpty()) { + this.loginPremiumTitle = null; + } else { + this.loginPremiumTitle = Title.title( + SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_TITLE), + SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_SUBTITLE), + Settings.IMP.MAIN.PREMIUM_TITLE_SETTINGS.toTimes() + ); + } + + this.loginFloodgate = Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE.isEmpty() ? null : SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE); + if (Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_SUBTITLE.isEmpty()) { + this.loginFloodgateTitle = null; + } else { + this.loginFloodgateTitle = Title.title( + SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_TITLE), + SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_SUBTITLE), + Settings.IMP.MAIN.PREMIUM_TITLE_SETTINGS.toTimes() + ); + } + + this.nicknameInvalidKick = SERIALIZER.deserialize(Settings.IMP.MAIN.STRINGS.NICKNAME_INVALID_KICK); + if (Settings.IMP.MAIN.CHECK_PASSWORD_STRENGTH) { this.unsafePasswords.clear(); - Path unsafePasswordsPath = Paths.get(this.dataDirectory.toFile().getAbsolutePath(), Settings.IMP.MAIN.UNSAFE_PASSWORDS_FILE); + Path unsafePasswordsPath = Paths.get(this.dataDirectoryFile.getAbsolutePath(), Settings.IMP.MAIN.UNSAFE_PASSWORDS_FILE); if (!unsafePasswordsPath.toFile().exists()) { Files.copy(Objects.requireNonNull(this.getClass().getResourceAsStream("/unsafe_passwords.txt")), unsafePasswordsPath); } - this.unsafePasswords.addAll(Files.lines(unsafePasswordsPath).collect(Collectors.toSet())); + try (Stream<String> unsafePasswordsStream = Files.lines(unsafePasswordsPath)) { + this.unsafePasswords.addAll(unsafePasswordsStream.collect(Collectors.toList())); + } } this.cachedAuthChecks.clear(); Settings.DATABASE dbConfig = Settings.IMP.DATABASE; - // 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); - this.connectionSource = new JdbcPooledConnectionSource("jdbc:h2:" + this.dataDirectory.toFile().getAbsoluteFile() + "/limboauth"); + this.connectionSource = new JdbcPooledConnectionSource("jdbc:h2:" + this.dataDirectoryFile.getAbsoluteFile() + "/limboauth"); break; } case "mysql": { - Objects.requireNonNull(com.mysql.cj.jdbc.Driver.class); - Objects.requireNonNull(com.mysql.cj.conf.url.SingleConnectionUrl.class); this.connectionSource = new JdbcPooledConnectionSource( "jdbc:mysql://" + dbConfig.HOSTNAME + "/" + dbConfig.DATABASE + dbConfig.CONNECTION_PARAMETERS, dbConfig.USER, dbConfig.PASSWORD ); break; } case "postgresql": { - Objects.requireNonNull(org.postgresql.Driver.class); this.connectionSource = new JdbcPooledConnectionSource( "jdbc:postgresql://" + dbConfig.HOSTNAME + "/" + dbConfig.DATABASE + dbConfig.CONNECTION_PARAMETERS, dbConfig.USER, dbConfig.PASSWORD ); break; } default: { - this.getLogger().error("WRONG DATABASE TYPE."); + LOGGER.error("Wrong database type."); this.server.shutdown(); return; } @@ -271,7 +334,7 @@ public class LimboAuth { break; } default: { - this.getLogger().error("Incorrect world file type."); + LOGGER.error("Incorrect world file type."); this.server.shutdown(); return; } @@ -287,74 +350,84 @@ public class LimboAuth { this.authServer = this.factory .createLimbo(authWorld) .setName("LimboAuth") - .registerCommand(new AuthCommandMeta(this, this.filterCommands(Settings.IMP.MAIN.REGISTER_COMMAND)), new AuthCommand()) - .registerCommand(new AuthCommandMeta(this, this.filterCommands(Settings.IMP.MAIN.LOGIN_COMMAND)), new AuthCommand()) - .registerCommand(new AuthCommandMeta(this, this.filterCommands(Settings.IMP.MAIN.TOTP_COMMAND)), new AuthCommand()); + .registerCommand(new LimboCommandMeta(this.filterCommands(Settings.IMP.MAIN.REGISTER_COMMAND))) + .registerCommand(new LimboCommandMeta(this.filterCommands(Settings.IMP.MAIN.LOGIN_COMMAND))) + .registerCommand(new LimboCommandMeta(this.filterCommands(Settings.IMP.MAIN.TOTP_COMMAND))); - this.server.getEventManager().unregisterListeners(this); - this.server.getEventManager().register(this, new AuthListener(this, this.playerDao, this.floodgateApi)); + EventManager eventManager = this.server.getEventManager(); + eventManager.unregisterListeners(this); + eventManager.register(this, new AuthListener(this, this.playerDao, this.floodgateApi)); - Executors.newScheduledThreadPool(1, task -> new Thread(task, "purge-cache")).scheduleAtFixedRate(() -> - this.checkCache(this.cachedAuthChecks, Settings.IMP.MAIN.PURGE_CACHE_MILLIS), + Executors.newScheduledThreadPool(1, task -> new Thread(task, "purge-cache")).scheduleAtFixedRate( + () -> this.checkCache(this.cachedAuthChecks, Settings.IMP.MAIN.PURGE_CACHE_MILLIS), Settings.IMP.MAIN.PURGE_CACHE_MILLIS, Settings.IMP.MAIN.PURGE_CACHE_MILLIS, TimeUnit.MILLISECONDS ); - Executors.newScheduledThreadPool(1, task -> new Thread(task, "purge-premium-cache")).scheduleAtFixedRate(() -> - this.checkCache(this.premiumCache, Settings.IMP.MAIN.PURGE_PREMIUM_CACHE_MILLIS), + Executors.newScheduledThreadPool(1, task -> new Thread(task, "purge-premium-cache")).scheduleAtFixedRate( + () -> this.checkCache(this.premiumCache, Settings.IMP.MAIN.PURGE_PREMIUM_CACHE_MILLIS), Settings.IMP.MAIN.PURGE_PREMIUM_CACHE_MILLIS, Settings.IMP.MAIN.PURGE_PREMIUM_CACHE_MILLIS, TimeUnit.MILLISECONDS ); - this.server.getEventManager().fireAndForget(new AuthPluginReloadEvent()); + eventManager.fireAndForget(new AuthPluginReloadEvent()); } private List<String> filterCommands(List<String> commands) { - return commands.stream().filter(e -> e.startsWith("/")).map(e -> e.substring(1)).collect(Collectors.toList()); + return commands.stream().filter(command -> command.startsWith("/")).map(command -> command.substring(1)).collect(Collectors.toList()); + } + + private void checkCache(Map<String, ? extends CachedUser> userMap, long time) { + userMap.entrySet().stream() + .filter(userEntry -> userEntry.getValue().getCheckTime() + time <= System.currentTimeMillis()) + .map(Map.Entry::getKey) + .forEach(userMap::remove); } public void migrateDb(Dao<?, ?> dao) { + TableInfo<?, ?> tableInfo = dao.getTableInfo(); + Set<FieldType> tables = new HashSet<>(); - Collections.addAll(tables, dao.getTableInfo().getFieldTypes()); + Collections.addAll(tables, tableInfo.getFieldTypes()); String findSql; + String database = Settings.IMP.DATABASE.DATABASE; + String tableName = tableInfo.getTableName(); switch (Settings.IMP.DATABASE.STORAGE_TYPE) { case "h2": { - findSql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" - + dao.getTableInfo().getTableName() + "';"; + findSql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + tableName + "';"; break; } case "postgresql": { - findSql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = '" + Settings.IMP.DATABASE.DATABASE - + "' AND TABLE_NAME = '" + dao.getTableInfo().getTableName() + "';"; + findSql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_CATALOG = '" + database + "' AND TABLE_NAME = '" + tableName + "';"; break; } case "mysql": { - findSql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '" + Settings.IMP.DATABASE.DATABASE - + "' AND TABLE_NAME = '" + dao.getTableInfo().getTableName() + "';"; + findSql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '" + database + "' AND TABLE_NAME = '" + tableName + "';"; break; } default: { - this.getLogger().error("WRONG DATABASE TYPE."); + LOGGER.error("WRONG DATABASE TYPE."); this.server.shutdown(); return; } } - try { - dao.queryRaw(findSql).forEach(e -> tables.removeIf(q -> q.getColumnName().equalsIgnoreCase(e[0]))); + try (GenericRawResults<String[]> queryResult = dao.queryRaw(findSql)) { + queryResult.forEach(result -> tables.removeIf(table -> table.getColumnName().equalsIgnoreCase(result[0]))); - tables.forEach(t -> { + tables.forEach(table -> { try { - String columnDefinition = t.getColumnDefinition(); - StringBuilder builder = new StringBuilder("ALTER TABLE \"" + dao.getTableInfo().getTableName() + "\" ADD "); - List<String> dummy = new ArrayList<>(); + StringBuilder builder = new StringBuilder("ALTER TABLE \"" + tableName + "\" ADD "); + String columnDefinition = table.getColumnDefinition(); + DatabaseType databaseType = dao.getConnectionSource().getDatabaseType(); if (columnDefinition == null) { - dao.getConnectionSource().getDatabaseType().appendColumnArg(t.getTableName(), builder, t, dummy, dummy, dummy, dummy); + List<String> dummy = List.of(); + databaseType.appendColumnArg(table.getTableName(), builder, table, dummy, dummy, dummy, dummy); } else { - dao.getConnectionSource().getDatabaseType().appendEscapedEntityName(builder, t.getColumnName()); + databaseType.appendEscapedEntityName(builder, table.getColumnName()); builder.append(" ").append(columnDefinition).append(" "); } @@ -363,7 +436,7 @@ public class LimboAuth { e.printStackTrace(); } }); - } catch (SQLException e) { + } catch (Exception e) { e.printStackTrace(); } } @@ -372,7 +445,7 @@ public class LimboAuth { String username = player.getUsername(); String lowercaseUsername = username.toLowerCase(Locale.ROOT); this.cachedAuthChecks.remove(lowercaseUsername); - this.cachedAuthChecks.put(lowercaseUsername, new CachedSessionUser(player.getRemoteAddress().getAddress(), username, System.currentTimeMillis())); + this.cachedAuthChecks.put(lowercaseUsername, new CachedSessionUser(System.currentTimeMillis(), player.getRemoteAddress().getAddress(), username)); } public void removePlayerFromCache(String username) { @@ -391,67 +464,52 @@ public class LimboAuth { } public void authPlayer(Player player) { - String nickname = player.getUsername(); boolean isFloodgate = !Settings.IMP.MAIN.FLOODGATE_NEED_AUTH && this.floodgateApi.isFloodgatePlayer(player.getUniqueId()); + String nickname = player.getUsername(); + if (this.nicknameValidationPattern.matcher((isFloodgate) ? nickname.substring(this.floodgateApi.getPrefixLength()) : nickname).matches()) { + RegisteredPlayer registeredPlayer = AuthSessionHandler.fetchInfo(this.playerDao, nickname); + boolean onlineMode = player.isOnlineMode(); + TaskEvent.Result result = TaskEvent.Result.NORMAL; - String validatorNickname = (isFloodgate) ? nickname.substring(this.floodgateApi.getPrefixLength()) : nickname; - - if (!this.nicknameValidationPattern.matcher(validatorNickname).matches()) { - player.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NICKNAME_INVALID_KICK)); - return; - } - - RegisteredPlayer registeredPlayer = AuthSessionHandler.fetchInfo(this.playerDao, nickname); - boolean onlineMode = player.isOnlineMode(); - TaskEvent.Result result = TaskEvent.Result.NORMAL; - - if (onlineMode || isFloodgate) { - if (registeredPlayer == null || registeredPlayer.getHash().isEmpty()) { - registeredPlayer = AuthSessionHandler.fetchInfo(this.playerDao, player.getUniqueId()); + if (onlineMode || isFloodgate) { if (registeredPlayer == null || registeredPlayer.getHash().isEmpty()) { - // Due to the current connection state, which is set to LOGIN there, we cannot send the packets. - // We need to wait for the PLAY connection state to set. - this.postLoginTasks.put(player.getUniqueId(), () -> { - if (onlineMode) { - if (!Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM.isEmpty()) { - player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM)); - } - if (!Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_SUBTITLE.isEmpty()) { - player.showTitle( - Title.title( - LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_TITLE), - LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_PREMIUM_SUBTITLE), - Settings.IMP.MAIN.PREMIUM_TITLE_SETTINGS.toTimes() - ) - ); - } - } else { - if (!Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE.isEmpty()) { - player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE)); + registeredPlayer = AuthSessionHandler.fetchInfo(this.playerDao, player.getUniqueId()); + if (registeredPlayer == null || registeredPlayer.getHash().isEmpty()) { + // Due to the current connection state, which is set to LOGIN there, we cannot send the packets. + // We need to wait for the PLAY connection state to set. + this.postLoginTasks.put(player.getUniqueId(), () -> { + if (onlineMode) { + if (this.loginPremium != null) { + player.sendMessage(this.loginPremium); + } + if (this.loginPremiumTitle != null) { + player.showTitle(this.loginPremiumTitle); + } + } else { + if (this.loginFloodgate != null) { + player.sendMessage(this.loginFloodgate); + } + if (this.loginFloodgateTitle != null) { + player.showTitle(this.loginFloodgateTitle); + } } - if (!Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_TITLE.isEmpty() && !Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_SUBTITLE.isEmpty()) { - player.showTitle( - Title.title( - LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_TITLE), - LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_FLOODGATE_SUBTITLE), - Settings.IMP.MAIN.PREMIUM_TITLE_SETTINGS.toTimes() - ) - ); - } - } - }); + }); - result = TaskEvent.Result.BYPASS; + result = TaskEvent.Result.BYPASS; + } } } - } - if (registeredPlayer == null) { - Consumer<TaskEvent> eventConsumer = (event) -> this.sendPlayer(event, null); - this.server.getEventManager().fire(new PreRegisterEvent(result, player, eventConsumer)).thenAcceptAsync(eventConsumer); + EventManager eventManager = this.server.getEventManager(); + if (registeredPlayer == null) { + Consumer<TaskEvent> eventConsumer = (event) -> this.sendPlayer(event, null); + eventManager.fire(new PreRegisterEvent(eventConsumer, result, player)).thenAcceptAsync(eventConsumer); + } else { + Consumer<TaskEvent> eventConsumer = (event) -> this.sendPlayer(event, ((PreAuthorizationEvent) event).getPlayerInfo()); + eventManager.fire(new PreAuthorizationEvent(eventConsumer, result, player, registeredPlayer)).thenAcceptAsync(eventConsumer); + } } else { - Consumer<TaskEvent> eventConsumer = (event) -> this.sendPlayer(event, ((PreAuthorizationEvent) event).getPlayerInfo()); - this.server.getEventManager().fire(new PreAuthorizationEvent(result, player, registeredPlayer, eventConsumer)).thenAcceptAsync(eventConsumer); + player.disconnect(this.nicknameInvalidKick); } } @@ -475,7 +533,7 @@ public class LimboAuth { try { this.authServer.spawnPlayer(player, new AuthSessionHandler(this.playerDao, player, this, registeredPlayer)); } catch (Throwable t) { - this.getLogger().error("Error", t); + t.printStackTrace(); } break; @@ -492,26 +550,23 @@ public class LimboAuth { try { int statusCode = this.client.send( HttpRequest.newBuilder() - .uri(URI.create( - String.format( - Settings.IMP.MAIN.ISPREMIUM_AUTH_URL, - URLEncoder.encode(lowercaseNickname, StandardCharsets.UTF_8)))) + .uri(URI.create(String.format(Settings.IMP.MAIN.ISPREMIUM_AUTH_URL, URLEncoder.encode(lowercaseNickname, StandardCharsets.UTF_8)))) .build(), HttpResponse.BodyHandlers.ofString() ).statusCode(); boolean isPremium = statusCode == 200; - // 429 Too Many Requests + // 429 Too Many Requests. if (statusCode != 429) { - this.premiumCache.put(lowercaseNickname, new CachedPremiumUser(isPremium, System.currentTimeMillis())); + this.premiumCache.put(lowercaseNickname, new CachedPremiumUser(System.currentTimeMillis(), isPremium)); } else { return Settings.IMP.MAIN.ON_RATE_LIMIT_PREMIUM; } return isPremium; } catch (IOException | InterruptedException e) { - this.getLogger().error("Unable to authenticate with Mojang", e); + LOGGER.error("Unable to authenticate with Mojang.", e); return Settings.IMP.MAIN.ON_RATE_LIMIT_PREMIUM; } } @@ -519,43 +574,48 @@ public class LimboAuth { public boolean isPremium(String nickname) { if (Settings.IMP.MAIN.FORCE_OFFLINE_MODE) { return false; - } - - try { - if (this.isPremiumExternal(nickname)) { - QueryBuilder<RegisteredPlayer, String> premiumRegisteredQuery = this.playerDao.queryBuilder(); - premiumRegisteredQuery.where() - .eq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)) - .and() - .ne("HASH", ""); - premiumRegisteredQuery.setCountOf(true); - - QueryBuilder<RegisteredPlayer, String> premiumUnregisteredQuery = this.playerDao.queryBuilder(); - premiumUnregisteredQuery.where() - .eq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)) - .and() - .eq("HASH", ""); - premiumUnregisteredQuery.setCountOf(true); - - if (Settings.IMP.MAIN.ONLINE_MODE_NEED_AUTH) { - return this.playerDao.countOf(premiumRegisteredQuery.prepare()) == 0 && this.playerDao.countOf(premiumUnregisteredQuery.prepare()) != 0; + } else { + try { + if (this.isPremiumExternal(nickname)) { + QueryBuilder<RegisteredPlayer, String> premiumRegisteredQuery = this.playerDao.queryBuilder(); + premiumRegisteredQuery.where() + .eq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)) + .and() + .ne("HASH", ""); + premiumRegisteredQuery.setCountOf(true); + + QueryBuilder<RegisteredPlayer, String> premiumUnregisteredQuery = this.playerDao.queryBuilder(); + premiumUnregisteredQuery.where() + .eq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)) + .and() + .eq("HASH", ""); + premiumUnregisteredQuery.setCountOf(true); + + if (Settings.IMP.MAIN.ONLINE_MODE_NEED_AUTH) { + return this.playerDao.countOf(premiumRegisteredQuery.prepare()) == 0 && this.playerDao.countOf(premiumUnregisteredQuery.prepare()) != 0; + } else { + return this.playerDao.countOf(premiumRegisteredQuery.prepare()) == 0; + } } else { - return this.playerDao.countOf(premiumRegisteredQuery.prepare()) == 0; + return false; } - } else { - return false; + } catch (Exception e) { + LOGGER.error("Unable to authenticate with Mojang.", e); + return Settings.IMP.MAIN.ON_RATE_LIMIT_PREMIUM; } - } catch (SQLException e) { - this.getLogger().error("Unable to authenticate with Mojang", e); - return Settings.IMP.MAIN.ON_RATE_LIMIT_PREMIUM; } } - private void checkCache(Map<String, ? extends CachedUser> userMap, long time) { - userMap.entrySet().stream() - .filter(u -> u.getValue().getCheckTime() + time <= System.currentTimeMillis()) - .map(Map.Entry::getKey) - .forEach(userMap::remove); + public Map<UUID, Runnable> getPostLoginTasks() { + return this.postLoginTasks; + } + + public Set<String> getUnsafePasswords() { + return this.unsafePasswords; + } + + public ProxyServer getServer() { + return this.server; } public JdbcPooledConnectionSource getConnectionSource() { @@ -566,20 +626,27 @@ public class LimboAuth { return this.playerDao; } - public Set<String> getUnsafePasswords() { - return this.unsafePasswords; + static { + // requireNonNull prevents the shade plugin from excluding the drivers in minimized jar. + Objects.requireNonNull(com.mysql.cj.jdbc.Driver.class); + Objects.requireNonNull(com.mysql.cj.conf.url.SingleConnectionUrl.class); + + Objects.requireNonNull(org.h2.Driver.class); + Objects.requireNonNull(org.h2.engine.Engine.class); + + Objects.requireNonNull(org.postgresql.Driver.class); } - public Map<UUID, Runnable> getPostLoginTasks() { - return this.postLoginTasks; + private static void setLogger(Logger logger) { + LOGGER = logger; } - public Logger getLogger() { - return this.logger; + private static void setSerializer(Serializer serializer) { + SERIALIZER = serializer; } - public ProxyServer getServer() { - return this.server; + public static Serializer getSerializer() { + return SERIALIZER; } private static class CachedUser { @@ -600,8 +667,9 @@ public class LimboAuth { private final InetAddress inetAddress; private final String username; - public CachedSessionUser(InetAddress inetAddress, String username, long checkTime) { + public CachedSessionUser(long checkTime, InetAddress inetAddress, String username) { super(checkTime); + this.inetAddress = inetAddress; this.username = username; } @@ -619,8 +687,9 @@ public class LimboAuth { private final boolean isPremium; - public CachedPremiumUser(boolean isPremium, long checkTime) { + public CachedPremiumUser(long checkTime, boolean isPremium) { super(checkTime); + this.isPremium = isPremium; } @@ -628,37 +697,4 @@ public class LimboAuth { return this.isPremium; } } - - private static class AuthCommandMeta implements CommandMeta { - - private final LimboAuth plugin; - private final Collection<String> aliases; - - AuthCommandMeta(LimboAuth plugin, Collection<String> aliases) { - this.plugin = plugin; - this.aliases = aliases; - } - - @Override - public Collection<String> getAliases() { - return this.aliases; - } - - @Override - public Collection<CommandNode<CommandSource>> getHints() { - return Collections.emptyList(); - } - - @Override - public @Nullable Object getPlugin() { - return this.plugin; - } - } - - private static class AuthCommand implements SimpleCommand { - @Override - public void execute(Invocation invocation) { - - } - } } diff --git a/src/main/java/net/elytrium/limboauth/Settings.java b/src/main/java/net/elytrium/limboauth/Settings.java index 9dbbcc4..43e1fe6 100644 --- a/src/main/java/net/elytrium/limboauth/Settings.java +++ b/src/main/java/net/elytrium/limboauth/Settings.java @@ -17,13 +17,12 @@ package net.elytrium.limboauth; -import java.io.File; import java.util.List; -import net.elytrium.limboauth.config.Config; +import net.elytrium.java.commons.config.YamlConfig; import net.kyori.adventure.title.Title; import net.kyori.adventure.util.Ticks; -public class Settings extends Config { +public class Settings extends YamlConfig { @Ignore public static final Settings IMP = new Settings(); @@ -31,11 +30,21 @@ public class Settings extends Config { @Final public String VERSION = BuildConstants.AUTH_VERSION; + @Comment({ + "Available serializers:", + "LEGACY_AMPERSAND - \"&c&lExample &c&9Text\".", + "LEGACY_SECTION - \"§c§lExample §c§9Text\".", + "MINIMESSAGE - \"<bold><red>Example</red> <blue>Text</blue></bold>\". (https://webui.adventure.kyori.net/)", + "GSON - \"[{\"text\":\"Example\",\"bold\":true,\"color\":\"red\"},{\"text\":\" \",\"bold\":true},{\"text\":\"Text\",\"bold\":true,\"color\":\"blue\"}]\". (https://minecraft.tools/en/json_text.php/)", + "GSON_COLOR_DOWNSAMPLING - Same as GSON, but uses downsampling." + }) + public String SERIALIZER = "LEGACY_AMPERSAND"; public String PREFIX = "LimboAuth &6>>&f"; @Create public MAIN MAIN; + @Comment("Don't use \\n, use {NL} for new line, and {PRFX} for prefix.") 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.") @@ -68,6 +77,8 @@ public class Settings extends Config { public boolean TOTP_NEED_PASSWORD = true; public boolean REGISTER_NEED_REPEAT_PASSWORD = true; public boolean CHANGE_PASSWORD_NEED_OLD_PASSWORD = true; + @Comment("Used in unregister and premium commands.") + public String CONFIRM_KEYWORD = "confirm"; @Comment("This prefix will be added to offline mode players nickname") public String OFFLINE_MODE_PREFIX = ""; @Comment("This prefix will be added to online mode players nickname") @@ -157,7 +168,6 @@ public class Settings extends Config { public Title.Times toTimes() { return Title.Times.times(Ticks.duration(this.FADE_IN), Ticks.duration(this.STAY), Ticks.duration(this.FADE_OUT)); } - } @Create @@ -190,12 +200,12 @@ public class Settings extends Config { @Create public MAIN.STRINGS STRINGS; - @Comment("You can left blank the fields marked with \"*\" comment.") public static class STRINGS { public String RELOAD = "{PRFX} &aReloaded successfully!"; public String RELOAD_FAILED = "{PRFX} &cReload failed, check console for details."; public String ERROR_OCCURRED = "{PRFX} &cAn internal error has occurred!"; + public String DATABASE_ERROR_KICK = "{PRFX} &cA database error has occurred!"; public String NOT_PLAYER = "{PRFX} &cСonsole is not allowed to execute this command!"; public String NOT_REGISTERED = "{PRFX} &cYou are not registered or your account is &6PREMIUM!"; @@ -205,36 +215,36 @@ public class Settings extends Config { 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 IP_LIMIT_KICK = "{PRFX}{NL}{NL}&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_KICK = "{PRFX}{NL}&cThe case of your nickname is wrong. Nickname is CaSe SeNsItIvE."; public String BOSSBAR = "{PRFX} You have &6{0} &fseconds left to log in."; public String TIMES_UP = "{PRFX}{NL}&cAuthorization time is up."; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_PREMIUM = "{PRFX} You've been logged in automatically using the premium account!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_PREMIUM_TITLE = "{PRFX} Welcome!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_PREMIUM_SUBTITLE = "&aYou has been logged in as premium player!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_FLOODGATE = "{PRFX} You've been logged in automatically using the bedrock account!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_FLOODGATE_TITLE = "{PRFX} Welcome!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_FLOODGATE_SUBTITLE = "&aYou has been logged in as bedrock player!"; 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!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_TITLE = "&fPlease, login using &6/login <password>&a."; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_SUBTITLE = "&aYou have &6{0} &aattempts."; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_SUCCESSFUL_TITLE = "{PRFX}"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String LOGIN_SUCCESSFUL_SUBTITLE = "&aSuccessfully logged in!"; @Comment("Or if register-need-repeat-password set to false remove the \"<repeat password>\" part.") @@ -244,13 +254,13 @@ public class Settings extends Config { 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!"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String REGISTER_TITLE = "{PRFX}"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String REGISTER_SUBTITLE = "&aPlease, register using &6/register <password> <repeat password>"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String REGISTER_SUCCESSFUL_TITLE = "{PRFX}"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String REGISTER_SUCCESSFUL_SUBTITLE = "&aSuccessfully registered!"; public String UNREGISTER_SUCCESSFUL = "{PRFX}{NL}&aSuccessfully unregistered!"; @@ -278,9 +288,9 @@ public class Settings extends Config { public String FORCE_CHANGE_PASSWORD_USAGE = "{PRFX} Usage: &6/forcechangepassword <nickname> <new password>"; public String TOTP = "{PRFX} Please, enter your 2FA key using &6/2fa <key>"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) public String TOTP_TITLE = "{PRFX}"; - @Comment("*") + @Comment(value = "Can be empty.", at = Comment.At.SAME_LINE) 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!"; @@ -292,7 +302,6 @@ public class Settings extends Config { public String TOTP_TOKEN = "{PRFX} &aYour 2FA token &7(Click to copy)&a: &6{0}"; public String TOTP_RECOVERY = "{PRFX} &aYour recovery codes &7(Click to copy)&a: &6{0}"; - public String DB_ERROR = "{PRFX} &aA database error has occurred."; public String DESTROY_SESSION_SUCCESSFUL = "{PRFX} &eYour session is now destroyed, you'll need to log in again after reconnecting."; } @@ -325,13 +334,4 @@ public class Settings extends Config { public String DATABASE = "limboauth"; public String CONNECTION_PARAMETERS = "?autoReconnect=true&initialTimeout=1&useSSL=false"; } - - public void reload(File file) { - if (this.load(file, this.PREFIX)) { - this.save(file); - } else { - this.save(file); - this.load(file, this.PREFIX); - } - } } diff --git a/src/main/java/net/elytrium/limboauth/command/ChangePasswordCommand.java b/src/main/java/net/elytrium/limboauth/command/ChangePasswordCommand.java index 806add0..f280053 100644 --- a/src/main/java/net/elytrium/limboauth/command/ChangePasswordCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/ChangePasswordCommand.java @@ -24,36 +24,38 @@ import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import java.sql.SQLException; +import net.elytrium.java.commons.mc.serialization.Serializer; +import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.model.RegisteredPlayer; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class ChangePasswordCommand implements SimpleCommand { private final Dao<RegisteredPlayer, String> playerDao; - private final Component notPlayer; private final boolean needOldPass; private final Component notRegistered; + private final Component crackedCommand; private final Component wrongPassword; private final Component successful; private final Component errorOccurred; private final Component usage; - private final Component crackedCommand; + private final Component notPlayer; public ChangePasswordCommand(Dao<RegisteredPlayer, String> playerDao) { this.playerDao = playerDao; - this.notPlayer = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); + Serializer serializer = LimboAuth.getSerializer(); this.needOldPass = Settings.IMP.MAIN.CHANGE_PASSWORD_NEED_OLD_PASSWORD; - this.notRegistered = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); - this.wrongPassword = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); - this.successful = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.CHANGE_PASSWORD_SUCCESSFUL); - this.errorOccurred = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); - this.usage = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.CHANGE_PASSWORD_USAGE); - this.crackedCommand = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.CRACKED_COMMAND); + this.notRegistered = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); + this.crackedCommand = serializer.deserialize(Settings.IMP.MAIN.STRINGS.CRACKED_COMMAND); + this.wrongPassword = serializer.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); + this.successful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.CHANGE_PASSWORD_SUCCESSFUL); + this.errorOccurred = serializer.deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); + this.usage = serializer.deserialize(Settings.IMP.MAIN.STRINGS.CHANGE_PASSWORD_USAGE); + this.notPlayer = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); } @Override @@ -61,45 +63,42 @@ public class ChangePasswordCommand implements SimpleCommand { CommandSource source = invocation.source(); String[] args = invocation.arguments(); - if (!(source instanceof Player)) { - source.sendMessage(this.notPlayer); - return; - } - - if (this.needOldPass ? args.length == 2 : args.length == 1) { - if (this.needOldPass) { - RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, ((Player) source).getUsername()); - if (player == null) { - source.sendMessage(this.notRegistered); - return; - } else if (player.getHash().isEmpty()) { - source.sendMessage(this.crackedCommand); - } else if (!AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { - source.sendMessage(this.wrongPassword); - return; + if (source instanceof Player) { + if (this.needOldPass ? args.length == 2 : args.length == 1) { + if (this.needOldPass) { + RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, ((Player) source).getUsername()); + if (player == null) { + source.sendMessage(this.notRegistered); + return; + } else if (player.getHash().isEmpty()) { + source.sendMessage(this.crackedCommand); + } else if (!AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { + source.sendMessage(this.wrongPassword); + return; + } } - } - try { - UpdateBuilder<RegisteredPlayer, String> updateBuilder = this.playerDao.updateBuilder(); - updateBuilder.where().eq("NICKNAME", ((Player) source).getUsername()); - updateBuilder.updateColumnValue("HASH", AuthSessionHandler.genHash(this.needOldPass ? args[1] : args[0])); - updateBuilder.update(); + try { + UpdateBuilder<RegisteredPlayer, String> updateBuilder = this.playerDao.updateBuilder(); + updateBuilder.where().eq("NICKNAME", ((Player) source).getUsername()); + updateBuilder.updateColumnValue("HASH", AuthSessionHandler.genHash(this.needOldPass ? args[1] : args[0])); + updateBuilder.update(); - source.sendMessage(this.successful); - } catch (SQLException e) { - source.sendMessage(this.errorOccurred); - e.printStackTrace(); + source.sendMessage(this.successful); + } catch (SQLException e) { + source.sendMessage(this.errorOccurred); + e.printStackTrace(); + } + } else { + source.sendMessage(this.usage); } - - return; + } else { + source.sendMessage(this.notPlayer); } - - source.sendMessage(this.usage); } @Override public boolean hasPermission(SimpleCommand.Invocation invocation) { - return invocation.source().getPermissionValue("limboauth.commands.changepassword") != Tristate.FALSE; + return invocation.source().getPermissionValue("limboauth.commands.changepassword") == Tristate.TRUE; } } diff --git a/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java b/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java index 5cabe9b..95e002c 100644 --- a/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/DestroySessionCommand.java @@ -21,40 +21,40 @@ import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; +import net.elytrium.java.commons.mc.serialization.Serializer; import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class DestroySessionCommand implements SimpleCommand { private final LimboAuth plugin; - private final Component notPlayer; private final Component successful; + private final Component notPlayer; public DestroySessionCommand(LimboAuth plugin) { this.plugin = plugin; - this.notPlayer = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); - this.successful = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.DESTROY_SESSION_SUCCESSFUL); + Serializer serializer = LimboAuth.getSerializer(); + this.successful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.DESTROY_SESSION_SUCCESSFUL); + this.notPlayer = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); } @Override public void execute(SimpleCommand.Invocation invocation) { CommandSource source = invocation.source(); - if (!(source instanceof Player)) { + if (source instanceof Player) { + this.plugin.removePlayerFromCache(((Player) source).getUsername()); + source.sendMessage(this.successful); + } else { source.sendMessage(this.notPlayer); - return; } - - this.plugin.removePlayerFromCache(((Player) source).getUsername()); - source.sendMessage(this.successful); } @Override public boolean hasPermission(SimpleCommand.Invocation invocation) { - return invocation.source().getPermissionValue("limboauth.commands.destroysession") != Tristate.FALSE; + return invocation.source().getPermissionValue("limboauth.commands.destroysession") == Tristate.TRUE; } } diff --git a/src/main/java/net/elytrium/limboauth/command/ForceChangePasswordCommand.java b/src/main/java/net/elytrium/limboauth/command/ForceChangePasswordCommand.java index dd2ee4d..2d47b61 100644 --- a/src/main/java/net/elytrium/limboauth/command/ForceChangePasswordCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/ForceChangePasswordCommand.java @@ -26,12 +26,13 @@ import java.sql.SQLException; import java.text.MessageFormat; import java.util.List; import java.util.Locale; +import net.elytrium.java.commons.mc.serialization.Serializer; +import net.elytrium.java.commons.mc.velocity.commands.SuggestUtils; +import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.model.RegisteredPlayer; -import net.elytrium.limboauth.utils.SuggestUtils; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class ForceChangePasswordCommand implements SimpleCommand { @@ -50,12 +51,12 @@ public class ForceChangePasswordCommand implements SimpleCommand { this.message = Settings.IMP.MAIN.STRINGS.FORCE_CHANGE_PASSWORD_MESSAGE; this.successful = Settings.IMP.MAIN.STRINGS.FORCE_CHANGE_PASSWORD_SUCCESSFUL; this.notSuccessful = Settings.IMP.MAIN.STRINGS.FORCE_CHANGE_PASSWORD_NOT_SUCCESSFUL; - this.usage = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.FORCE_CHANGE_PASSWORD_USAGE); + this.usage = LimboAuth.getSerializer().deserialize(Settings.IMP.MAIN.STRINGS.FORCE_CHANGE_PASSWORD_USAGE); } @Override public List<String> suggest(SimpleCommand.Invocation invocation) { - return SuggestUtils.suggestPlayers(invocation.arguments(), this.server); + return SuggestUtils.suggestPlayers(this.server, invocation.arguments(), 0); } @Override @@ -67,26 +68,23 @@ public class ForceChangePasswordCommand implements SimpleCommand { String nickname = args[0]; String newPassword = args[1]; + Serializer serializer = LimboAuth.getSerializer(); try { UpdateBuilder<RegisteredPlayer, String> updateBuilder = this.playerDao.updateBuilder(); updateBuilder.where().eq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)); updateBuilder.updateColumnValue("HASH", AuthSessionHandler.genHash(newPassword)); updateBuilder.update(); - this.server.getPlayer(nickname).ifPresent(player -> - player.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(this.message, newPassword))) - ); + this.server.getPlayer(nickname).ifPresent(player -> player.sendMessage(serializer.deserialize(MessageFormat.format(this.message, newPassword)))); - source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(this.successful, nickname))); + source.sendMessage(serializer.deserialize(MessageFormat.format(this.successful, nickname))); } catch (SQLException e) { - source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(this.notSuccessful, nickname))); + source.sendMessage(serializer.deserialize(MessageFormat.format(this.notSuccessful, nickname))); e.printStackTrace(); } - - return; + } else { + source.sendMessage(this.usage); } - - source.sendMessage(this.usage); } @Override diff --git a/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java b/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java index dabe70b..2fd12ca 100644 --- a/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/ForceUnregisterCommand.java @@ -25,12 +25,12 @@ import java.sql.SQLException; import java.text.MessageFormat; import java.util.List; import java.util.Locale; +import net.elytrium.java.commons.mc.serialization.Serializer; +import net.elytrium.java.commons.mc.velocity.commands.SuggestUtils; import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.model.RegisteredPlayer; -import net.elytrium.limboauth.utils.SuggestUtils; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class ForceUnregisterCommand implements SimpleCommand { @@ -48,15 +48,16 @@ public class ForceUnregisterCommand implements SimpleCommand { this.server = server; this.playerDao = playerDao; - this.kick = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_KICK); + Serializer serializer = LimboAuth.getSerializer(); + this.kick = serializer.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); + this.usage = serializer.deserialize(Settings.IMP.MAIN.STRINGS.FORCE_UNREGISTER_USAGE); } @Override public List<String> suggest(SimpleCommand.Invocation invocation) { - return SuggestUtils.suggestPlayers(invocation.arguments(), this.server); + return SuggestUtils.suggestPlayers(this.server, invocation.arguments(), 0); } @Override @@ -67,20 +68,19 @@ public class ForceUnregisterCommand implements SimpleCommand { if (args.length == 1) { String playerNick = args[0]; + Serializer serializer = LimboAuth.getSerializer(); try { this.playerDao.deleteById(playerNick.toLowerCase(Locale.ROOT)); this.plugin.removePlayerFromCache(playerNick); this.server.getPlayer(playerNick).ifPresent(player -> player.disconnect(this.kick)); - source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(this.successful, playerNick))); + source.sendMessage(serializer.deserialize(MessageFormat.format(this.successful, playerNick))); } catch (SQLException e) { - source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(this.notSuccessful, playerNick))); + source.sendMessage(serializer.deserialize(MessageFormat.format(this.notSuccessful, playerNick))); e.printStackTrace(); } - - return; + } else { + source.sendMessage(this.usage); } - - source.sendMessage(this.usage); } @Override diff --git a/src/main/java/net/elytrium/limboauth/command/LimboAuthCommand.java b/src/main/java/net/elytrium/limboauth/command/LimboAuthCommand.java index 49a5079..3dd4c8d 100644 --- a/src/main/java/net/elytrium/limboauth/command/LimboAuthCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/LimboAuthCommand.java @@ -21,15 +21,44 @@ import com.google.common.collect.ImmutableList; import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.SimpleCommand; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; +import net.elytrium.java.commons.mc.serialization.Serializer; import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import net.kyori.adventure.text.format.NamedTextColor; public class LimboAuthCommand implements SimpleCommand { + private static final List<Component> HELP_MESSAGE = List.of( + Component.text("This server is using LimboAuth and LimboAPI.", NamedTextColor.YELLOW), + Component.text("(C) 2021 - 2022 Elytrium", NamedTextColor.YELLOW), + Component.text("https://elytrium.net/github/", NamedTextColor.GREEN), + Component.empty() + ); + private static final Map<String, Component> SUBCOMMANDS = Map.of( + /* + "serverstats", Component.textOfChildren( + Component.text(" /limboauth serverstats", NamedTextColor.GREEN), + Component.text(" - ", NamedTextColor.DARK_GRAY), + Component.text("Query for server stats.", NamedTextColor.YELLOW) + ), + "playerstats", Component.textOfChildren( + Component.text(" /limboauth playerstats <player>", NamedTextColor.GREEN), + Component.text(" - ", NamedTextColor.DARK_GRAY), + Component.text("Query for stats of specified player.", NamedTextColor.YELLOW) + ), + */ + "reload", Component.textOfChildren( + Component.text(" /limboauth reload", NamedTextColor.GREEN), + Component.text(" - ", NamedTextColor.DARK_GRAY), + Component.text("Reload config.", NamedTextColor.YELLOW) + ) + ); + private static final Component AVAILABLE_SUBCOMMANDS_MESSAGE = Component.text("Available subcommands:", NamedTextColor.WHITE); + private static final Component NO_AVAILABLE_SUBCOMMANDS_MESSAGE = Component.text("There is no available subcommands for you.", NamedTextColor.WHITE); + private final LimboAuth plugin; public LimboAuthCommand(LimboAuth plugin) { @@ -42,17 +71,22 @@ public class LimboAuthCommand implements SimpleCommand { String[] args = invocation.arguments(); if (args.length == 0) { - return this.getSubCommands() + return SUBCOMMANDS.keySet().stream() .filter(cmd -> source.hasPermission("limboauth.admin." + cmd)) .collect(Collectors.toList()); } else if (args.length == 1) { - return this.getSubCommands() + String argument = args[0]; + return SUBCOMMANDS.keySet().stream() .filter(cmd -> source.hasPermission("limboauth.admin." + cmd)) - .filter(str -> str.regionMatches(true, 0, args[0], 0, args[0].length())) + .filter(str -> str.regionMatches(true, 0, argument, 0, argument.length())) .collect(Collectors.toList()); + /* + } else if (args[0].equalsIgnoreCase("playerstats") && source.hasPermission("limboauth.admin.playerstats")) { + return SuggestUtils.suggestPlayers(this.plugin.getServer(), args, 2); + */ + } else { + return ImmutableList.of(); } - - return ImmutableList.of(); } @Override @@ -60,40 +94,63 @@ public class LimboAuthCommand implements SimpleCommand { CommandSource source = invocation.source(); String[] args = invocation.arguments(); - if (args.length == 1) { - if (args[0].equalsIgnoreCase("reload") && source.hasPermission("limboauth.admin.reload")) { - try { - this.plugin.reload(); - source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.RELOAD)); - } catch (Exception e) { - source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.RELOAD_FAILED)); - e.printStackTrace(); + int argsAmount = args.length; + if (argsAmount > 0) { + String command = args[0]; + Serializer serializer = LimboAuth.getSerializer(); + if (argsAmount == 1) { + if (command.equalsIgnoreCase("reload") && source.hasPermission("limboauth.admin.reload")) { + try { + this.plugin.reload(); + source.sendMessage(serializer.deserialize(Settings.IMP.MAIN.STRINGS.RELOAD)); + } catch (Exception e) { + e.printStackTrace(); + source.sendMessage(serializer.deserialize(Settings.IMP.MAIN.STRINGS.RELOAD_FAILED)); + } + + return; + } + /* + else if (command.equalsIgnoreCase("serverstats") && source.hasPermission("limboauth.admin.serverstats")) { + return; + } else if (command.equalsIgnoreCase("playerstats") && source.hasPermission("limboauth.admin.playerstats")) { + source.sendMessage(Component.text("Please specify a player.")); + return; } + */ + } + /* + else if (argsAmount == 2) { + if (command.equalsIgnoreCase("playerstats") && source.hasPermission("limboauth.admin.playerstats")) { + RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.plugin.getPlayerDao(), args[1]); + if (player == null) { + source.sendMessage(Component.text("Игрок даезент екзистс.")); + } else { + source.sendMessage(Component.text("Стата геймера под ником {player}:")); + source.sendMessage(Component.empty()); + source.sendMessage(Component.text("Ласт айпи: " + player.getIP())); + source.sendMessage(Component.text("2fa: " + (player.getTotpToken().isEmpty() ? "Нет" : "Есть"))); + } - return; + return; + } } + */ } this.showHelp(source); } private void showHelp(CommandSource source) { - source.sendMessage(Component.text("§eThis server is using LimboAuth and LimboAPI")); - source.sendMessage(Component.text("§e(С) 2021 - 2022 Elytrium")); - source.sendMessage(Component.text("§ahttps://ely.su/github/")); - source.sendMessage(Component.text("§r")); - source.sendMessage(Component.text("§fAvailable subcommands:")); - // Java moment - this.getSubCommands() - .filter(cmd -> source.hasPermission("limboauth.admin." + cmd)) - .forEach(cmd -> { - if (cmd.equals("reload")) { - source.sendMessage(Component.text(" §a/limboauth reload §8- §eReload config")); - } - }); - } - - private Stream<String> getSubCommands() { - return Stream.of("reload"); + HELP_MESSAGE.forEach(source::sendMessage); + List<Map.Entry<String, Component>> availableSubcommands = SUBCOMMANDS.entrySet().stream() + .filter(command -> source.hasPermission("limboauth.admin." + command.getKey())) + .collect(Collectors.toList()); + if (availableSubcommands.size() > 0) { + source.sendMessage(AVAILABLE_SUBCOMMANDS_MESSAGE); + availableSubcommands.forEach(command -> source.sendMessage(command.getValue())); + } else { + source.sendMessage(NO_AVAILABLE_SUBCOMMANDS_MESSAGE); + } } } diff --git a/src/main/java/net/elytrium/limboauth/command/PremiumCommand.java b/src/main/java/net/elytrium/limboauth/command/PremiumCommand.java index fd62a00..78674d0 100644 --- a/src/main/java/net/elytrium/limboauth/command/PremiumCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/PremiumCommand.java @@ -23,19 +23,19 @@ import com.velocitypowered.api.command.SimpleCommand; import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import java.sql.SQLException; +import net.elytrium.java.commons.mc.serialization.Serializer; import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.model.RegisteredPlayer; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class PremiumCommand implements SimpleCommand { private final LimboAuth plugin; private final Dao<RegisteredPlayer, String> playerDao; - private final Component notPlayer; + private final String confirmKeyword; private final Component notRegistered; private final Component alreadyPremium; private final Component successful; @@ -43,19 +43,22 @@ public class PremiumCommand implements SimpleCommand { private final Component notPremium; private final Component wrongPassword; private final Component usage; + private final Component notPlayer; public PremiumCommand(LimboAuth plugin, Dao<RegisteredPlayer, String> playerDao) { this.plugin = plugin; this.playerDao = playerDao; - this.notPlayer = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); - this.notRegistered = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); - this.alreadyPremium = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.ALREADY_PREMIUM); - this.successful = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.PREMIUM_SUCCESSFUL); - this.errorOccurred = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); - this.notPremium = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_PREMIUM); - this.wrongPassword = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); - this.usage = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.PREMIUM_USAGE); + Serializer serializer = LimboAuth.getSerializer(); + this.confirmKeyword = Settings.IMP.MAIN.CONFIRM_KEYWORD; + this.notRegistered = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); + this.alreadyPremium = serializer.deserialize(Settings.IMP.MAIN.STRINGS.ALREADY_PREMIUM); + this.successful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.PREMIUM_SUCCESSFUL); + this.errorOccurred = serializer.deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); + this.notPremium = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_PREMIUM); + this.wrongPassword = serializer.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); + this.usage = serializer.deserialize(Settings.IMP.MAIN.STRINGS.PREMIUM_USAGE); + this.notPlayer = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); } @Override @@ -63,46 +66,45 @@ public class PremiumCommand implements SimpleCommand { CommandSource source = invocation.source(); String[] args = invocation.arguments(); - if (!(source instanceof Player)) { - source.sendMessage(this.notPlayer); - return; - } - - if (args.length == 2) { - if (args[1].equalsIgnoreCase("confirm")) { - String username = ((Player) source).getUsername(); - RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, username); - if (player == null) { - source.sendMessage(this.notRegistered); - } else if (player.getHash().isEmpty()) { - source.sendMessage(this.alreadyPremium); - } else if (AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { - if (this.plugin.isPremiumExternal(username)) { - try { - player.setHash(""); - this.playerDao.update(player); - this.plugin.removePlayerFromCache(username); - ((Player) source).disconnect(this.successful); - } catch (SQLException e) { - source.sendMessage(this.errorOccurred); - e.printStackTrace(); + if (source instanceof Player) { + if (args.length == 2) { + if (this.confirmKeyword.equalsIgnoreCase(args[1])) { + String username = ((Player) source).getUsername(); + RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, username); + if (player == null) { + source.sendMessage(this.notRegistered); + } else if (player.getHash().isEmpty()) { + source.sendMessage(this.alreadyPremium); + } else if (AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { + if (this.plugin.isPremiumExternal(username)) { + try { + player.setHash(""); + this.playerDao.update(player); + this.plugin.removePlayerFromCache(username); + ((Player) source).disconnect(this.successful); + } catch (SQLException e) { + source.sendMessage(this.errorOccurred); + e.printStackTrace(); + } + } else { + source.sendMessage(this.notPremium); } } else { - source.sendMessage(this.notPremium); + source.sendMessage(this.wrongPassword); } - } else { - source.sendMessage(this.wrongPassword); - } - return; + return; + } } - } - source.sendMessage(this.usage); + source.sendMessage(this.usage); + } else { + source.sendMessage(this.notPlayer); + } } @Override public boolean hasPermission(SimpleCommand.Invocation invocation) { - return invocation.source().getPermissionValue("limboauth.commands.premium") != Tristate.FALSE; + return invocation.source().getPermissionValue("limboauth.commands.premium") == Tristate.TRUE; } } diff --git a/src/main/java/net/elytrium/limboauth/command/TotpCommand.java b/src/main/java/net/elytrium/limboauth/command/TotpCommand.java index e9693ae..54d9c3e 100644 --- a/src/main/java/net/elytrium/limboauth/command/TotpCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/TotpCommand.java @@ -31,12 +31,13 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.sql.SQLException; import java.text.MessageFormat; +import net.elytrium.java.commons.mc.serialization.Serializer; +import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.model.RegisteredPlayer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class TotpCommand implements SimpleCommand { @@ -65,52 +66,49 @@ public class TotpCommand implements SimpleCommand { public TotpCommand(Dao<RegisteredPlayer, String> playerDao) { this.playerDao = playerDao; - this.notPlayer = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); - this.usage = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP_USAGE); + Serializer serializer = LimboAuth.getSerializer(); + this.notPlayer = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); + this.usage = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_USAGE); this.needPassword = Settings.IMP.MAIN.TOTP_NEED_PASSWORD; - this.notRegistered = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); - this.wrongPassword = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); - this.alreadyEnabled = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP_ALREADY_ENABLED); - this.errorOccurred = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); - this.successful = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP_SUCCESSFUL); + this.notRegistered = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); + this.wrongPassword = serializer.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); + this.alreadyEnabled = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_ALREADY_ENABLED); + this.errorOccurred = serializer.deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); + this.successful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_SUCCESSFUL); this.issuer = Settings.IMP.MAIN.TOTP_ISSUER; this.qrGeneratorUrl = Settings.IMP.MAIN.QR_GENERATOR_URL; - this.qr = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP_QR); + this.qr = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_QR); this.token = Settings.IMP.MAIN.STRINGS.TOTP_TOKEN; this.recoveryCodesAmount = Settings.IMP.MAIN.TOTP_RECOVERY_CODES_AMOUNT; this.recovery = Settings.IMP.MAIN.STRINGS.TOTP_RECOVERY; - this.disabled = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP_DISABLED); - this.wrong = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP_WRONG); - this.crackedCommand = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.CRACKED_COMMAND); + this.disabled = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_DISABLED); + this.wrong = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_WRONG); + this.crackedCommand = serializer.deserialize(Settings.IMP.MAIN.STRINGS.CRACKED_COMMAND); } + // TODO: Rewrite. @Override public void execute(SimpleCommand.Invocation invocation) { CommandSource source = invocation.source(); String[] args = invocation.arguments(); - if (!(source instanceof Player)) { - source.sendMessage(this.notPlayer); - return; - } + if (source instanceof Player) { + if (args.length == 0) { + source.sendMessage(this.usage); + } else { + String username = ((Player) source).getUsername(); - if (args.length == 0) { - source.sendMessage(this.usage); - } else { - String username = ((Player) source).getUsername(); - - RegisteredPlayer playerInfo; - UpdateBuilder<RegisteredPlayer, String> updateBuilder; - switch (args[0]) { - case "enable": { + RegisteredPlayer playerInfo; + UpdateBuilder<RegisteredPlayer, String> updateBuilder; + if (args[0].equalsIgnoreCase("enable")) { if (this.needPassword ? args.length == 2 : args.length == 1) { playerInfo = AuthSessionHandler.fetchInfo(this.playerDao, username); - if (playerInfo == null) { source.sendMessage(this.notRegistered); return; } else if (playerInfo.getHash().isEmpty()) { source.sendMessage(this.crackedCommand); + return; } else if (this.needPassword && !AuthSessionHandler.checkPassword(args[1], playerInfo, this.playerDao)) { source.sendMessage(this.wrongPassword); return; @@ -122,7 +120,6 @@ public class TotpCommand implements SimpleCommand { } String secret = this.secretGenerator.generate(); - try { updateBuilder = this.playerDao.updateBuilder(); updateBuilder.where().eq("NICKNAME", username); @@ -132,7 +129,6 @@ public class TotpCommand implements SimpleCommand { source.sendMessage(this.errorOccurred); e.printStackTrace(); } - source.sendMessage(this.successful); QrData data = new QrData.Builder() @@ -140,69 +136,54 @@ public class TotpCommand implements SimpleCommand { .secret(secret) .issuer(this.issuer) .build(); - String qrUrl = this.qrGeneratorUrl.replace("{data}", URLEncoder.encode(data.getUri(), StandardCharsets.UTF_8)); - source.sendMessage(this.qr.clickEvent(ClickEvent.openUrl(qrUrl))); - source.sendMessage( - LegacyComponentSerializer.legacyAmpersand().deserialize( - MessageFormat.format(this.token, secret) - ).clickEvent(ClickEvent.copyToClipboard(secret)) - ); - + Serializer serializer = LimboAuth.getSerializer(); + source.sendMessage(serializer.deserialize(MessageFormat.format(this.token, secret)).clickEvent(ClickEvent.copyToClipboard(secret))); String codes = String.join(", ", this.codesGenerator.generateCodes(this.recoveryCodesAmount)); - - source.sendMessage( - LegacyComponentSerializer.legacyAmpersand().deserialize( - MessageFormat.format(this.recovery, codes) - ).clickEvent(ClickEvent.copyToClipboard(codes)) - ); + source.sendMessage(serializer.deserialize(MessageFormat.format(this.recovery, codes)).clickEvent(ClickEvent.copyToClipboard(codes))); } else { source.sendMessage(this.usage); } - break; - } - case "disable": { - if (args.length != 2) { - source.sendMessage(this.usage); - return; - } - - playerInfo = AuthSessionHandler.fetchInfo(this.playerDao, username); - - if (playerInfo == null) { - source.sendMessage(this.notRegistered); - return; - } + } else if (args[0].equalsIgnoreCase("disable")) { + if (args.length == 2) { + playerInfo = AuthSessionHandler.fetchInfo(this.playerDao, username); - if (AuthSessionHandler.getVerifier().isValidCode(playerInfo.getTotpToken(), args[1])) { - try { - updateBuilder = this.playerDao.updateBuilder(); - updateBuilder.where().eq("NICKNAME", username); - updateBuilder.updateColumnValue("TOTPTOKEN", ""); - updateBuilder.update(); + if (playerInfo == null) { + source.sendMessage(this.notRegistered); + return; + } - source.sendMessage(this.disabled); - } catch (SQLException e) { - source.sendMessage(this.errorOccurred); - e.printStackTrace(); + if (AuthSessionHandler.getTotpCodeVerifier().isValidCode(playerInfo.getTotpToken(), args[1])) { + try { + updateBuilder = this.playerDao.updateBuilder(); + updateBuilder.where().eq("NICKNAME", username); + updateBuilder.updateColumnValue("TOTPTOKEN", ""); + updateBuilder.update(); + + source.sendMessage(this.disabled); + } catch (SQLException e) { + source.sendMessage(this.errorOccurred); + e.printStackTrace(); + } + } else { + source.sendMessage(this.wrong); } } else { - source.sendMessage(this.wrong); + source.sendMessage(this.usage); } - break; - } - default: { + } else { source.sendMessage(this.usage); - break; } } + } else { + source.sendMessage(this.notPlayer); } } @Override public boolean hasPermission(SimpleCommand.Invocation invocation) { - return invocation.source().getPermissionValue("limboauth.commands.totp") != Tristate.FALSE; + return invocation.source().getPermissionValue("limboauth.commands.totp") == Tristate.TRUE; } } diff --git a/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java b/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java index 62edd0b..b6c11ff 100644 --- a/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java +++ b/src/main/java/net/elytrium/limboauth/command/UnregisterCommand.java @@ -24,19 +24,20 @@ import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.proxy.Player; import java.sql.SQLException; import java.util.Locale; +import net.elytrium.java.commons.mc.serialization.Serializer; import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.elytrium.limboauth.event.AuthUnregisterEvent; import net.elytrium.limboauth.handler.AuthSessionHandler; import net.elytrium.limboauth.model.RegisteredPlayer; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; public class UnregisterCommand implements SimpleCommand { private final LimboAuth plugin; private final Dao<RegisteredPlayer, String> playerDao; + private final String confirmKeyword; private final Component notPlayer; private final Component notRegistered; private final Component successful; @@ -49,13 +50,15 @@ public class UnregisterCommand implements SimpleCommand { this.plugin = plugin; this.playerDao = playerDao; - this.notPlayer = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); - this.notRegistered = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); - this.successful = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.UNREGISTER_SUCCESSFUL); - this.errorOccurred = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); - this.wrongPassword = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); - this.usage = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.UNREGISTER_USAGE); - this.crackedCommand = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.CRACKED_COMMAND); + Serializer serializer = LimboAuth.getSerializer(); + this.confirmKeyword = Settings.IMP.MAIN.CONFIRM_KEYWORD; + this.notPlayer = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_PLAYER); + this.notRegistered = serializer.deserialize(Settings.IMP.MAIN.STRINGS.NOT_REGISTERED); + this.successful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.UNREGISTER_SUCCESSFUL); + this.errorOccurred = serializer.deserialize(Settings.IMP.MAIN.STRINGS.ERROR_OCCURRED); + this.wrongPassword = serializer.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_PASSWORD); + this.usage = serializer.deserialize(Settings.IMP.MAIN.STRINGS.UNREGISTER_USAGE); + this.crackedCommand = serializer.deserialize(Settings.IMP.MAIN.STRINGS.CRACKED_COMMAND); } @Override @@ -63,42 +66,41 @@ public class UnregisterCommand implements SimpleCommand { CommandSource source = invocation.source(); String[] args = invocation.arguments(); - if (!(source instanceof Player)) { - source.sendMessage(this.notPlayer); - return; - } - - if (args.length == 2) { - if (args[1].equalsIgnoreCase("confirm")) { - String username = ((Player) source).getUsername(); - RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, username); - if (player == null) { - source.sendMessage(this.notRegistered); - } else if (player.getHash().isEmpty()) { - source.sendMessage(this.crackedCommand); - } else if (AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { - try { - this.plugin.getServer().getEventManager().fireAndForget(new AuthUnregisterEvent(username)); - this.playerDao.deleteById(username.toLowerCase(Locale.ROOT)); - this.plugin.removePlayerFromCache(username); - ((Player) source).disconnect(this.successful); - } catch (SQLException e) { - source.sendMessage(this.errorOccurred); - e.printStackTrace(); + if (source instanceof Player) { + if (args.length == 2) { + if (this.confirmKeyword.equalsIgnoreCase(args[1])) { + String username = ((Player) source).getUsername(); + RegisteredPlayer player = AuthSessionHandler.fetchInfo(this.playerDao, username); + if (player == null) { + source.sendMessage(this.notRegistered); + } else if (player.getHash().isEmpty()) { + source.sendMessage(this.crackedCommand); + } else if (AuthSessionHandler.checkPassword(args[0], player, this.playerDao)) { + try { + this.plugin.getServer().getEventManager().fireAndForget(new AuthUnregisterEvent(username)); + this.playerDao.deleteById(username.toLowerCase(Locale.ROOT)); + this.plugin.removePlayerFromCache(username); + ((Player) source).disconnect(this.successful); + } catch (SQLException e) { + source.sendMessage(this.errorOccurred); + e.printStackTrace(); + } + } else { + source.sendMessage(this.wrongPassword); } - } else { - source.sendMessage(this.wrongPassword); - } - return; + return; + } } - } - source.sendMessage(this.usage); + source.sendMessage(this.usage); + } else { + source.sendMessage(this.notPlayer); + } } @Override public boolean hasPermission(SimpleCommand.Invocation invocation) { - return invocation.source().getPermissionValue("limboauth.commands.unregister") != Tristate.FALSE; + return invocation.source().getPermissionValue("limboauth.commands.unregister") == Tristate.TRUE; } } diff --git a/src/main/java/net/elytrium/limboauth/config/Config.java b/src/main/java/net/elytrium/limboauth/config/Config.java deleted file mode 100644 index 48912c6..0000000 --- a/src/main/java/net/elytrium/limboauth/config/Config.java +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2021 - 2022 Elytrium - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package net.elytrium.limboauth.config; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.yaml.snakeyaml.Yaml; - -public class Config { - - private final Logger logger = LoggerFactory.getLogger(Config.class); - - private String oldPrefix = ""; - private String currentPrefix = ""; - - /** - * Set the value of a specific node. Probably throws some error if you supply non-existing keys or invalid values. - * - * @param key config node - * @param value value - */ - private void set(String key, Object value, Class<?> root) { - String[] split = key.split("\\."); - Object instance = this.getInstance(split, root); - if (instance != null) { - Field field = this.getField(split, instance); - if (field != null) { - try { - if (field.getAnnotation(Final.class) != null) { - return; - } - if (field.getType() == String.class && !(value instanceof String)) { - value = value + ""; - } - field.set(instance, value); - return; - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - this.logger.debug("Failed to set config option: " + key + ": " + value + " | " + instance + " | " + root.getSimpleName() + ".yml"); - } - - @SuppressWarnings("unchecked") - public void set(Map<String, Object> input, String oldPath) { - for (Map.Entry<String, Object> entry : input.entrySet()) { - String key = oldPath + (oldPath.isEmpty() ? "" : ".") + entry.getKey(); - Object value = entry.getValue(); - - if (value instanceof Map) { - this.set((Map<String, Object>) value, key); - } else if (value instanceof String) { - if (key.equalsIgnoreCase("prefix") && !this.currentPrefix.equals(value)) { - this.currentPrefix = (String) value; - } - - this.set(key, ((String) value).replace("{NL}", "\n").replace("{PRFX}", this.currentPrefix), this.getClass()); - } else { - this.set(key, value, this.getClass()); - } - } - } - - public boolean load(File file, String prefix) { - this.oldPrefix = this.currentPrefix.isEmpty() ? prefix : this.currentPrefix; - this.currentPrefix = prefix; - if (!file.exists()) { - return false; - } - - try (InputStreamReader reader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) { - this.set(new Yaml().load(reader), ""); - } catch (IOException e) { - this.logger.warn("Unable to load config", e); - return false; - } - - return true; - } - - /** - * Indicates that a field should be instantiated / created. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD}) - public @interface Create { - - } - - /** - * Indicates that a field cannot be modified. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD}) - public @interface Final { - - } - - /** - * Creates a comment. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.TYPE}) - public @interface Comment { - - String[] value(); - } - - /** - * Any field or class with is not part of the config. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ElementType.FIELD, ElementType.TYPE}) - public @interface Ignore { - - } - - private String toYamlString(Object value, String spacing, String fieldName) { - if (value instanceof List) { - Collection<?> listValue = (Collection<?>) value; - if (listValue.isEmpty()) { - return "[]"; - } - StringBuilder m = new StringBuilder(); - for (Object obj : listValue) { - m.append(System.lineSeparator()).append(spacing).append("- ").append(this.toYamlString(obj, spacing, fieldName)); - } - - return m.toString(); - } - - if (value instanceof String) { - String stringValue = (String) value; - if (stringValue.isEmpty()) { - return "\"\""; - } - - String quoted = "\"" + stringValue + "\""; - if (fieldName.equalsIgnoreCase("prefix")) { - return quoted; - } else { - return quoted.replace("\n", "{NL}").replace(this.currentPrefix.equals(this.oldPrefix) ? this.oldPrefix : this.currentPrefix, "{PRFX}"); - } - } - - return value != null ? value.toString() : "null"; - } - - /** - * Set all values in the file (load first to avoid overwriting). - */ - @SuppressWarnings("ResultOfMethodCallIgnored") - @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") - public void save(File file) { - try { - if (!file.exists()) { - File parent = file.getParentFile(); - if (parent != null) { - file.getParentFile().mkdirs(); - } - file.createNewFile(); - } - - PrintWriter writer = new PrintWriter(file, StandardCharsets.UTF_8); - Object instance = this; - this.save(writer, this.getClass(), instance, 0); - writer.close(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - private void save(PrintWriter writer, Class<?> clazz, Object instance, int indent) { - try { - String lineSeparator = System.lineSeparator(); - String spacing = IntStream.range(0, indent).mapToObj(i -> " ").collect(Collectors.joining()); - - for (Field field : clazz.getFields()) { - if (field.getAnnotation(Ignore.class) != null) { - continue; - } - Class<?> current = field.getType(); - if (field.getAnnotation(Ignore.class) != null) { - continue; - } - - Comment comment = field.getAnnotation(Comment.class); - if (comment != null) { - for (String commentLine : comment.value()) { - writer.write(spacing + "# " + commentLine + lineSeparator); - } - } - - Create create = field.getAnnotation(Create.class); - if (create != null) { - Object value = field.get(instance); - this.setAccessible(field); - if (indent == 0) { - writer.write(lineSeparator); - } - comment = current.getAnnotation(Comment.class); - if (comment != null) { - for (String commentLine : comment.value()) { - writer.write(spacing + "# " + commentLine + lineSeparator); - } - } - writer.write(spacing + this.toNodeName(current.getSimpleName()) + ":" + lineSeparator); - if (value == null) { - field.set(instance, value = current.getDeclaredConstructor().newInstance()); - } - this.save(writer, current, value, indent + 2); - } else { - String value = this.toYamlString(field.get(instance), spacing, field.getName()); - writer.write(spacing + this.toNodeName(field.getName() + ": ") + value + lineSeparator); - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - - /** - * Get the field for a specific config node and instance. - * - * <p>As expiry can have multiple blocks there will be multiple instances - * - * @param split the node (split by period) - * @param instance the instance - */ - private Field getField(String[] split, Object instance) { - try { - Field field = instance.getClass().getField(this.toFieldName(split[split.length - 1])); - this.setAccessible(field); - return field; - } catch (Throwable ignored) { - this.logger.debug("Invalid config field: " + this.join(split, ".") + " for " + this.toNodeName(instance.getClass().getSimpleName())); - return null; - } - } - - /** - * Get the instance for a specific config node. - * - * @param split the node (split by period) - * @return The instance or null - */ - private Object getInstance(String[] split, Class<?> root) { - try { - Class<?> clazz = root == null ? MethodHandles.lookup().lookupClass() : root; - Object instance = this; - while (split.length > 0) { - if (split.length == 1) { - return instance; - } else { - Class<?> found = null; - if (clazz == null) { - return null; - } - - Class<?>[] classes = clazz.getDeclaredClasses(); - for (Class<?> current : classes) { - if (Objects.equals(current.getSimpleName(), this.toFieldName(split[0]))) { - found = current; - break; - } - } - - if (found == null) { - return null; - } - - try { - Field instanceField = clazz.getDeclaredField(this.toFieldName(split[0])); - this.setAccessible(instanceField); - Object value = instanceField.get(instance); - if (value == null) { - value = found.getDeclaredConstructor().newInstance(); - instanceField.set(instance, value); - } - - clazz = found; - instance = value; - split = Arrays.copyOfRange(split, 1, split.length); - continue; - } catch (NoSuchFieldException e) { - // - } - - split = Arrays.copyOfRange(split, 1, split.length); - clazz = found; - instance = clazz.getDeclaredConstructor().newInstance(); - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - - return null; - } - - /** - * Translate a node to a java field name. - */ - private String toFieldName(String node) { - return node.toUpperCase(Locale.ROOT).replaceAll("-", "_"); - } - - /** - * Translate a field to a config node. - */ - private String toNodeName(String field) { - return field.toLowerCase(Locale.ROOT).replace("_", "-"); - } - - /** - * Set some field to be accessible. - */ - private void setAccessible(Field field) throws NoSuchFieldException, IllegalAccessException { - field.setAccessible(true); - if (Modifier.isFinal(field.getModifiers())) { - 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(); - } - } - } - - @SuppressWarnings("SameParameterValue") - private String join(Object[] array, String delimiter) { - switch (array.length) { - case 0: { - return ""; - } - case 1: { - return array[0].toString(); - } - default: { - StringBuilder result = new StringBuilder(); - for (int i = 0, j = array.length; i < j; ++i) { - if (i > 0) { - result.append(delimiter); - } - result.append(array[i]); - } - - return result.toString(); - } - } - } -} diff --git a/src/main/java/net/elytrium/limboauth/event/AuthPluginReloadEvent.java b/src/main/java/net/elytrium/limboauth/event/AuthPluginReloadEvent.java index c166e2d..049de11 100644 --- a/src/main/java/net/elytrium/limboauth/event/AuthPluginReloadEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/AuthPluginReloadEvent.java @@ -18,4 +18,5 @@ package net.elytrium.limboauth.event; public class AuthPluginReloadEvent { + } diff --git a/src/main/java/net/elytrium/limboauth/event/AuthUnregisterEvent.java b/src/main/java/net/elytrium/limboauth/event/AuthUnregisterEvent.java index 9895d08..6a5b348 100644 --- a/src/main/java/net/elytrium/limboauth/event/AuthUnregisterEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/AuthUnregisterEvent.java @@ -28,5 +28,4 @@ public class AuthUnregisterEvent { public String getNickname() { return this.nickname; } - } diff --git a/src/main/java/net/elytrium/limboauth/event/PostAuthorizationEvent.java b/src/main/java/net/elytrium/limboauth/event/PostAuthorizationEvent.java index 342c8ca..0fed613 100644 --- a/src/main/java/net/elytrium/limboauth/event/PostAuthorizationEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/PostAuthorizationEvent.java @@ -22,11 +22,8 @@ import net.elytrium.limboapi.api.player.LimboPlayer; import net.elytrium.limboauth.model.RegisteredPlayer; public class PostAuthorizationEvent extends PostEvent { - public PostAuthorizationEvent(LimboPlayer player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(player, playerInfo, onComplete); - } - public PostAuthorizationEvent(Result result, LimboPlayer player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(result, player, playerInfo, onComplete); + public PostAuthorizationEvent(Consumer<TaskEvent> onComplete, LimboPlayer player, RegisteredPlayer playerInfo) { + super(onComplete, player, playerInfo); } } diff --git a/src/main/java/net/elytrium/limboauth/event/PostEvent.java b/src/main/java/net/elytrium/limboauth/event/PostEvent.java index 9902ab5..e3f590a 100644 --- a/src/main/java/net/elytrium/limboauth/event/PostEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/PostEvent.java @@ -26,25 +26,25 @@ public abstract class PostEvent extends TaskEvent { private final LimboPlayer player; private final RegisteredPlayer playerInfo; - protected PostEvent(LimboPlayer player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { + protected PostEvent(Consumer<TaskEvent> onComplete, LimboPlayer player, RegisteredPlayer playerInfo) { super(onComplete); this.player = player; this.playerInfo = playerInfo; } - protected PostEvent(Result result, LimboPlayer player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(result, onComplete); + protected PostEvent(Consumer<TaskEvent> onComplete, Result result, LimboPlayer player, RegisteredPlayer playerInfo) { + super(onComplete, result); this.player = player; this.playerInfo = playerInfo; } - public RegisteredPlayer getPlayerInfo() { - return this.playerInfo; - } - public LimboPlayer getPlayer() { return this.player; } + + public RegisteredPlayer getPlayerInfo() { + return this.playerInfo; + } } diff --git a/src/main/java/net/elytrium/limboauth/event/PostRegisterEvent.java b/src/main/java/net/elytrium/limboauth/event/PostRegisterEvent.java index c0e0504..9c3a6d5 100644 --- a/src/main/java/net/elytrium/limboauth/event/PostRegisterEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/PostRegisterEvent.java @@ -22,11 +22,8 @@ import net.elytrium.limboapi.api.player.LimboPlayer; import net.elytrium.limboauth.model.RegisteredPlayer; public class PostRegisterEvent extends PostEvent { - public PostRegisterEvent(LimboPlayer player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(player, playerInfo, onComplete); - } - public PostRegisterEvent(Result result, LimboPlayer player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(result, player, playerInfo, onComplete); + public PostRegisterEvent(Consumer<TaskEvent> onComplete, LimboPlayer player, RegisteredPlayer playerInfo) { + super(onComplete, player, playerInfo); } } diff --git a/src/main/java/net/elytrium/limboauth/event/PreAuthorizationEvent.java b/src/main/java/net/elytrium/limboauth/event/PreAuthorizationEvent.java index 295c767..e96b0d0 100644 --- a/src/main/java/net/elytrium/limboauth/event/PreAuthorizationEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/PreAuthorizationEvent.java @@ -22,15 +22,12 @@ import java.util.function.Consumer; import net.elytrium.limboauth.model.RegisteredPlayer; public class PreAuthorizationEvent extends PreEvent { + private final RegisteredPlayer playerInfo; - public PreAuthorizationEvent(Player player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(player, onComplete); - this.playerInfo = playerInfo; - } + public PreAuthorizationEvent(Consumer<TaskEvent> onComplete, Result result, Player player, RegisteredPlayer playerInfo) { + super(onComplete, result, player); - public PreAuthorizationEvent(Result result, Player player, RegisteredPlayer playerInfo, Consumer<TaskEvent> onComplete) { - super(result, player, onComplete); this.playerInfo = playerInfo; } diff --git a/src/main/java/net/elytrium/limboauth/event/PreEvent.java b/src/main/java/net/elytrium/limboauth/event/PreEvent.java index a925c5d..1fab6ab 100644 --- a/src/main/java/net/elytrium/limboauth/event/PreEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/PreEvent.java @@ -21,15 +21,12 @@ import com.velocitypowered.api.proxy.Player; import java.util.function.Consumer; public abstract class PreEvent extends TaskEvent { + private final Player player; - protected PreEvent(Player player, Consumer<TaskEvent> onComplete) { - super(onComplete); - this.player = player; - } + protected PreEvent(Consumer<TaskEvent> onComplete, Result result, Player player) { + super(onComplete, result); - protected PreEvent(Result result, Player player, Consumer<TaskEvent> onComplete) { - super(result, onComplete); this.player = player; } diff --git a/src/main/java/net/elytrium/limboauth/event/PreRegisterEvent.java b/src/main/java/net/elytrium/limboauth/event/PreRegisterEvent.java index 7d6aa15..e340fe0 100644 --- a/src/main/java/net/elytrium/limboauth/event/PreRegisterEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/PreRegisterEvent.java @@ -21,11 +21,8 @@ import com.velocitypowered.api.proxy.Player; import java.util.function.Consumer; public class PreRegisterEvent extends PreEvent { - public PreRegisterEvent(Player player, Consumer<TaskEvent> onComplete) { - super(player, onComplete); - } - public PreRegisterEvent(Result result, Player player, Consumer<TaskEvent> onComplete) { - super(result, player, onComplete); + public PreRegisterEvent(Consumer<TaskEvent> onComplete, Result result, Player player) { + super(onComplete, result, player); } } diff --git a/src/main/java/net/elytrium/limboauth/event/TaskEvent.java b/src/main/java/net/elytrium/limboauth/event/TaskEvent.java index 1f37e0f..5562cb2 100644 --- a/src/main/java/net/elytrium/limboauth/event/TaskEvent.java +++ b/src/main/java/net/elytrium/limboauth/event/TaskEvent.java @@ -18,46 +18,30 @@ package net.elytrium.limboauth.event; import java.util.function.Consumer; +import net.elytrium.limboauth.LimboAuth; import net.elytrium.limboauth.Settings; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.jetbrains.annotations.NotNull; public abstract class TaskEvent { - private static final Component defaultReason - = LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.EVENT_CANCELLED); - private Result result = Result.NORMAL; - private Component reason = defaultReason; + + private static Component DEFAULT_REASON; private final Consumer<TaskEvent> onComplete; + private Result result = Result.NORMAL; + private Component reason = DEFAULT_REASON; + public TaskEvent(Consumer<TaskEvent> onComplete) { this.onComplete = onComplete; } - public TaskEvent(Result result, Consumer<TaskEvent> onComplete) { - this.result = result; + public TaskEvent(Consumer<TaskEvent> onComplete, Result result) { this.onComplete = onComplete; - } - - public Result getResult() { - return this.result; - } - - public void setResult(@NotNull Result result) { this.result = result; } - public void cancel(@NotNull Component reason) { - this.result = Result.CANCEL; - this.reason = reason; - } - - public Component getReason() { - return this.reason; - } - - public void complete(Result result) { + public void complete(@NotNull Result result) { if (this.result != Result.WAIT) { return; } @@ -66,16 +50,38 @@ public abstract class TaskEvent { this.onComplete.accept(this); } - public void completeAndCancel(@NotNull Component c) { + public void completeAndCancel(@NotNull Component reason) { if (this.result != Result.WAIT) { return; } - this.cancel(c); + this.cancel(reason); this.onComplete.accept(this); } + public void cancel(@NotNull Component reason) { + this.result = Result.CANCEL; + this.reason = reason; + } + + public void setResult(@NotNull Result result) { + this.result = result; + } + + public Result getResult() { + return this.result; + } + + public Component getReason() { + return this.reason; + } + + public static void reload() { + DEFAULT_REASON = LimboAuth.getSerializer().deserialize(Settings.IMP.MAIN.STRINGS.EVENT_CANCELLED); + } + public enum Result { + CANCEL, BYPASS, NORMAL, diff --git a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java index 9a795b5..75fd862 100644 --- a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java +++ b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java @@ -32,7 +32,8 @@ import java.util.List; import java.util.Locale; import java.util.UUID; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; +import net.elytrium.java.commons.mc.serialization.Serializer; import net.elytrium.limboapi.api.Limbo; import net.elytrium.limboapi.api.LimboSessionHandler; import net.elytrium.limboapi.api.player.LimboPlayer; @@ -45,33 +46,67 @@ 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; +import org.checkerframework.checker.nullness.qual.Nullable; public class AuthSessionHandler implements LimboSessionHandler { - private static final CodeVerifier verifier = new DefaultCodeVerifier(new DefaultCodeGenerator(), new SystemTimeProvider()); + private static final CodeVerifier TOTP_CODE_VERIFIER = new DefaultCodeVerifier(new DefaultCodeGenerator(), new SystemTimeProvider()); + private static final BCrypt.Verifyer HASH_VERIFIER = BCrypt.verifyer(); + private static final BCrypt.Hasher HASHER = BCrypt.withDefaults(); + + private static BossBar.Color bossbarColor; + private static BossBar.Overlay bossbarOverlay; + private static Component ipLimitKick; + private static Component databaseErrorKick; + private static Component wrongNicknameCaseKick; + private static Component timesUp; + private static Component registerSuccessful; + @Nullable + private static Title registerSuccessfulTitle; + private static Component[] loginWrongPassword; + private static Component loginWrongPasswordKick; + private static Component totp; + @Nullable + private static Title totpTitle; + private static Component register; + @Nullable + private static Title registerTitle; + private static Component[] login; + @Nullable + private static Title loginTitle; + private static Component registerDifferentPasswords; + private static Component registerPasswordTooLong; + private static Component registerPasswordTooShort; + private static Component registerPasswordUnsafe; + private static Component loginSuccessful; + @Nullable + private static Title loginSuccessfulTitle; + private static MigrationHash migrationHash; 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)) + 1.0F, + bossbarColor, + bossbarOverlay ); + + @Nullable + private RegisteredPlayer playerInfo; + private ScheduledTask authMainTask; private LimboPlayer player; private String ip; private int attempts = Settings.IMP.MAIN.LOGIN_ATTEMPTS; - private boolean totp = false; + private boolean totpState; - public AuthSessionHandler(Dao<RegisteredPlayer, String> playerDao, Player proxyPlayer, LimboAuth plugin, RegisteredPlayer playerInfo) { + public AuthSessionHandler(Dao<RegisteredPlayer, String> playerDao, Player proxyPlayer, LimboAuth plugin, @Nullable RegisteredPlayer playerInfo) { this.playerDao = playerDao; this.proxyPlayer = proxyPlayer; this.plugin = plugin; @@ -81,33 +116,62 @@ public class AuthSessionHandler implements LimboSessionHandler { @Override public void onSpawn(Limbo server, LimboPlayer player) { this.player = player; - this.player.disableFalling(); this.ip = this.proxyPlayer.getRemoteAddress().getAddress().getHostAddress(); + this.player.disableFalling(); + if (this.playerInfo == null) { - this.checkIp(); + try { + List<RegisteredPlayer> alreadyRegistered = this.playerDao.queryForEq("IP", this.ip); + if (alreadyRegistered != null) { + int sizeOfValidRegistrations = alreadyRegistered.size(); + if (Settings.IMP.MAIN.IP_LIMIT_VALID_TIME > 0) { + for (RegisteredPlayer registeredPlayer : alreadyRegistered.stream() + .filter(registeredPlayer -> registeredPlayer.getRegDate() < System.currentTimeMillis() - Settings.IMP.MAIN.IP_LIMIT_VALID_TIME) + .collect(Collectors.toList())) { + registeredPlayer.setIP(""); + this.playerDao.update(registeredPlayer); + --sizeOfValidRegistrations; + } + } + + if (sizeOfValidRegistrations >= Settings.IMP.MAIN.IP_LIMIT_REGISTRATIONS) { + this.proxyPlayer.disconnect(ipLimitKick); + return; + } + } + } catch (SQLException e) { + e.printStackTrace(); + this.proxyPlayer.disconnect(databaseErrorKick); + return; + } } else { - this.checkCase(); + if (!this.proxyPlayer.getUsername().equals(this.playerInfo.getNickname())) { + this.proxyPlayer.disconnect(wrongNicknameCaseKick); + return; + } } boolean bossBarEnabled = Settings.IMP.MAIN.ENABLE_BOSSBAR; - float bossBarMultiplier = 1000F / Settings.IMP.MAIN.AUTH_TIME; - if (bossBarEnabled) { - this.proxyPlayer.showBossBar(this.bossBar); - } + Serializer serializer = LimboAuth.getSerializer(); + int authTime = Settings.IMP.MAIN.AUTH_TIME; + float multiplier = authTime / 1000.0F; 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 / 1000F)))); - // It is possible, that the progress value can overcome 1, e.g. 1.0000001. - this.bossBar.progress(Math.min((timeSinceJoin * bossBarMultiplier) / 1000F, 1F)); + if (System.currentTimeMillis() - this.joinTime > authTime) { + this.proxyPlayer.disconnect(timesUp); + } else { + if (bossBarEnabled) { + float secondsLeft = ((System.currentTimeMillis() - this.joinTime) - authTime) / 1000.0F; + this.bossBar.name(serializer.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.BOSSBAR, (int) secondsLeft))); + this.bossBar.progress((int) (secondsLeft * multiplier)); + } } }).repeat(1, TimeUnit.SECONDS).schedule(); + if (bossBarEnabled) { + this.proxyPlayer.showBossBar(this.bossBar); + } + this.sendMessage(true); } @@ -116,62 +180,67 @@ public class AuthSessionHandler implements LimboSessionHandler { String[] args = message.split(" "); if (args.length != 0 && this.checkArgsLength(args.length)) { Command command = Command.parse(args[0]); - switch (command) { - case REGISTER: { - 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), - Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() - ) - ); - } - this.finishRegister(); - } - } else { - this.sendMessage(false); + if (command == Command.REGISTER && !this.totpState && this.playerInfo == null) { + if (this.checkPasswordsRepeat(args) && this.checkPasswordLength(args[1]) && this.checkPasswordStrength(args[1])) { + String username = this.proxyPlayer.getUsername(); + RegisteredPlayer registeredPlayer = new RegisteredPlayer( + username, + username.toLowerCase(Locale.ROOT), + genHash(args[1]), + this.ip, + "", + System.currentTimeMillis(), + this.proxyPlayer.getUniqueId().toString(), + "" + ); + + try { + this.playerDao.create(registeredPlayer); + this.playerInfo = registeredPlayer; + } catch (SQLException e) { + e.printStackTrace(); + this.proxyPlayer.disconnect(databaseErrorKick); } - break; - } - case LOGIN: { - if (!this.totp && this.playerInfo != null) { - if (this.checkPassword(args[1])) { - this.loginOrTotp(); - } else if (--this.attempts != 0) { - this.proxyPlayer.sendMessage(this.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD, this.attempts))); - } else { - this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD_KICK)); - } - } else { - this.sendMessage(false); + + this.proxyPlayer.sendMessage(registerSuccessful); + if (registerSuccessfulTitle != null) { + this.proxyPlayer.showTitle(registerSuccessfulTitle); } - break; + + this.plugin.getServer().getEventManager() + .fire(new PostRegisterEvent(this::finishAuth, this.player, this.playerInfo)) + .thenAcceptAsync(this::finishAuth); } - case TOTP: { - if (this.totp) { - if (verifier.isValidCode(this.playerInfo.getTotpToken(), args[1])) { - this.finishLogin(); - } else { - this.sendMessage(false); - } + + // {@code return} placed here (not above), because + // AuthSessionHandler#checkPasswordsRepeat, AuthSessionHandler#checkPasswordLength, and AuthSessionHandler#checkPasswordStrength methods are + // invoking Player#sendMessage that sends its own message in case if the return value is false. + // If we don't place {@code return} here, an another message (AuthSessionHandler#sendMessage) will be sent. + return; + } else if (command == Command.LOGIN && !this.totpState && this.playerInfo != null) { + if (args[1].length() > 0 && checkPassword(args[1], this.playerInfo, this.playerDao)) { + if (this.playerInfo.getTotpToken().isEmpty()) { + this.finishLogin(); } else { - this.sendMessage(false); + this.totpState = true; + this.sendMessage(true); } - break; + } else if (--this.attempts != 0) { + this.proxyPlayer.sendMessage(loginWrongPassword[this.attempts - 1]); + } else { + this.proxyPlayer.disconnect(loginWrongPasswordKick); } - case INVALID: - default: { - this.sendMessage(false); + + return; + } else if (command == Command.TOTP && this.totpState && this.playerInfo != null) { + if (TOTP_CODE_VERIFIER.isValidCode(this.playerInfo.getTotpToken(), args[1])) { + this.finishLogin(); + return; } } - } else { - this.sendMessage(false); } + + this.sendMessage(false); } @Override @@ -183,151 +252,72 @@ public class AuthSessionHandler implements LimboSessionHandler { this.proxyPlayer.hideBossBar(this.bossBar); } - public static RegisteredPlayer fetchInfo(Dao<RegisteredPlayer, String> playerDao, String nickname) { - List<RegisteredPlayer> playerList = null; - try { - playerList = playerDao.queryForEq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)); - } catch (SQLException e) { - e.printStackTrace(); - } - - return (playerList != null ? playerList.size() : 0) == 0 ? null : playerList.get(0); - } - - public static RegisteredPlayer fetchInfo(Dao<RegisteredPlayer, String> playerDao, UUID uuid) { - List<RegisteredPlayer> playerList = null; - try { - playerList = playerDao.queryForEq("PREMIUMUUID", uuid.toString()); - } catch (SQLException e) { - e.printStackTrace(); - } - - return (playerList != null ? playerList.size() : 0) == 0 ? null : playerList.get(0); - } - - public static CodeVerifier getVerifier() { - 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().replace("BCRYPT$", "$2a$").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); - - if (isCorrect) { - player.setHash(genHash(password)); - try { - playerDao.update(player); - } catch (SQLException e) { - e.printStackTrace(); - return false; - } - } - } - - return isCorrect; - } - - private void checkIp() { - try { - List<RegisteredPlayer> alreadyRegistered = this.playerDao.queryForEq("IP", this.ip); - - if (alreadyRegistered == null) { - return; + private void sendMessage(boolean sendTitle) { + if (this.totpState) { + this.proxyPlayer.sendMessage(totp); + if (sendTitle && totpTitle != null) { + this.proxyPlayer.showTitle(totpTitle); } - - AtomicInteger sizeOfValid = new AtomicInteger(alreadyRegistered.size()); - - if (Settings.IMP.MAIN.IP_LIMIT_VALID_TIME != 0) { - long checkDate = System.currentTimeMillis() - Settings.IMP.MAIN.IP_LIMIT_VALID_TIME; - - alreadyRegistered.stream() - .filter(e -> e.getRegDate() != null) - .filter(e -> e.getRegDate() < checkDate) - .forEach(e -> { - try { - e.setIP(""); - this.playerDao.update(e); - sizeOfValid.decrementAndGet(); - } catch (SQLException ex) { - ex.printStackTrace(); - this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.DB_ERROR)); - } - }); + } else if (this.playerInfo == null) { + this.proxyPlayer.sendMessage(register); + if (sendTitle && registerTitle != null) { + this.proxyPlayer.showTitle(registerTitle); } - - if (sizeOfValid.get() >= Settings.IMP.MAIN.IP_LIMIT_REGISTRATIONS) { - this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.IP_LIMIT)); + } else { + this.proxyPlayer.sendMessage(login[this.attempts - 1]); + if (sendTitle && loginTitle != null) { + this.proxyPlayer.showTitle(loginTitle); } - } catch (SQLException e) { - e.printStackTrace(); - this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.DB_ERROR)); } } - private void checkCase() { - if (!this.proxyPlayer.getUsername().equals(this.playerInfo.getNickname())) { - this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_NICKNAME_CASE_KICK)); + private boolean checkArgsLength(int argsLength) { + if (this.playerInfo == null && Settings.IMP.MAIN.REGISTER_NEED_REPEAT_PASSWORD) { + return argsLength == 3; + } else { + return argsLength == 2; } } - private void register(String password) { - RegisteredPlayer registeredPlayer = new RegisteredPlayer( - this.proxyPlayer.getUsername(), - this.proxyPlayer.getUsername().toLowerCase(Locale.ROOT), - genHash(password), - this.ip, - "", - System.currentTimeMillis(), - this.proxyPlayer.getUniqueId().toString(), - "" - ); + private boolean checkPasswordsRepeat(String[] args) { + if (!Settings.IMP.MAIN.REGISTER_NEED_REPEAT_PASSWORD || args[1].equals(args[2])) { + return true; + } else { + this.proxyPlayer.sendMessage(registerDifferentPasswords); + return false; + } + } - try { - this.playerDao.create(registeredPlayer); - } catch (SQLException e) { - e.printStackTrace(); - this.proxyPlayer.disconnect(this.deserialize(Settings.IMP.MAIN.STRINGS.DB_ERROR)); + private boolean checkPasswordLength(String password) { + int length = password.length(); + if (length > Settings.IMP.MAIN.MAX_PASSWORD_LENGTH) { + this.proxyPlayer.sendMessage(registerPasswordTooLong); + return false; + } else if (length < Settings.IMP.MAIN.MIN_PASSWORD_LENGTH) { + this.proxyPlayer.sendMessage(registerPasswordTooShort); + return false; + } else { + return true; } } - private void loginOrTotp() { - if (this.playerInfo.getTotpToken().isEmpty()) { - this.finishLogin(); + private boolean checkPasswordStrength(String password) { + if (Settings.IMP.MAIN.CHECK_PASSWORD_STRENGTH && this.plugin.getUnsafePasswords().contains(password)) { + this.proxyPlayer.sendMessage(registerPasswordUnsafe); + return false; } else { - this.totp = true; - this.sendMessage(true); + return 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), - Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() - ) - ); + this.proxyPlayer.sendMessage(loginSuccessful); + if (loginSuccessfulTitle != null) { + this.proxyPlayer.showTitle(loginSuccessfulTitle); } this.plugin.getServer().getEventManager() - .fire(new PostAuthorizationEvent(this.player, this.playerInfo, this::finishAuth)) - .thenAcceptAsync(this::finishAuth); - } - - private void finishRegister() { - this.plugin.getServer().getEventManager() - .fire(new PostRegisterEvent(this.player, this.playerInfo, this::finishAuth)) + .fire(new PostAuthorizationEvent(this::finishAuth, this.player, this.playerInfo)) .thenAcceptAsync(this::finishAuth); } @@ -347,89 +337,135 @@ public class AuthSessionHandler implements LimboSessionHandler { this.player.disconnect(); } - private void sendMessage(boolean sendTitle) { - if (this.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), - Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes()) - ); - } - } else if (this.playerInfo == null) { - 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), - Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes()) - ); - } + public static void reload() { + Serializer serializer = LimboAuth.getSerializer(); + bossbarColor = BossBar.Color.valueOf(Settings.IMP.MAIN.BOSSBAR_COLOR.toUpperCase(Locale.ROOT)); + bossbarOverlay = BossBar.Overlay.valueOf(Settings.IMP.MAIN.BOSSBAR_OVERLAY.toUpperCase(Locale.ROOT)); + ipLimitKick = serializer.deserialize(Settings.IMP.MAIN.STRINGS.IP_LIMIT_KICK); + databaseErrorKick = serializer.deserialize(Settings.IMP.MAIN.STRINGS.DATABASE_ERROR_KICK); + wrongNicknameCaseKick = serializer.deserialize(Settings.IMP.MAIN.STRINGS.WRONG_NICKNAME_CASE_KICK); + timesUp = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TIMES_UP); + registerSuccessful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL); + if (Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_SUBTITLE.isEmpty()) { + registerSuccessfulTitle = null; } else { - 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)), - Settings.IMP.MAIN.PREMIUM_TITLE_SETTINGS.toTimes() - ) - ); - } + registerSuccessfulTitle = Title.title( + serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_TITLE), + serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUCCESSFUL_SUBTITLE), + Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() + ); } - } - - 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; + int loginAttempts = Settings.IMP.MAIN.LOGIN_ATTEMPTS; + loginWrongPassword = new Component[loginAttempts]; + for (int i = 0; i < loginAttempts; ++i) { + loginWrongPassword[i] = serializer.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD, i)); } - - return true; + loginWrongPasswordKick = serializer.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD_KICK); + totp = serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP); + if (Settings.IMP.MAIN.STRINGS.TOTP_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.TOTP_SUBTITLE.isEmpty()) { + totpTitle = null; + } else { + totpTitle = Title.title( + serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_TITLE), + serializer.deserialize(Settings.IMP.MAIN.STRINGS.TOTP_SUBTITLE), + Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() + ); + } + register = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER); + if (Settings.IMP.MAIN.STRINGS.REGISTER_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.REGISTER_SUBTITLE.isEmpty()) { + registerTitle = null; + } else { + registerTitle = Title.title( + serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_TITLE), + serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_SUBTITLE), + Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() + ); + } + login = new Component[loginAttempts]; + for (int i = 0; i < loginAttempts; ++i) { + login[i] = serializer.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN, i)); + } + if (Settings.IMP.MAIN.STRINGS.LOGIN_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.LOGIN_SUBTITLE.isEmpty()) { + loginTitle = null; + } else { + loginTitle = Title.title( + serializer.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_TITLE, loginAttempts)), + serializer.deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_SUBTITLE, loginAttempts)), + Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() + ); + } + registerDifferentPasswords = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_DIFFERENT_PASSWORDS); + registerPasswordTooLong = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_TOO_LONG); + registerPasswordTooShort = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_TOO_SHORT); + registerPasswordUnsafe = serializer.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_PASSWORD_UNSAFE); + loginSuccessful = serializer.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL); + if (Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_TITLE.isEmpty() && Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_SUBTITLE.isEmpty()) { + loginSuccessfulTitle = null; + } else { + loginSuccessfulTitle = Title.title( + serializer.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_TITLE), + serializer.deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESSFUL_SUBTITLE), + Settings.IMP.MAIN.CRACKED_TITLE_SETTINGS.toTimes() + ); + } + migrationHash = MigrationHash.valueOf(Settings.IMP.MAIN.MIGRATION_HASH); } - 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; + public static boolean checkPassword(String password, RegisteredPlayer player, Dao<RegisteredPlayer, String> playerDao) { + String hash = player.getHash(); + boolean isCorrect = HASH_VERIFIER.verify( + password.getBytes(StandardCharsets.UTF_8), + hash.replace("BCRYPT$", "$2a$").getBytes(StandardCharsets.UTF_8) + ).verified; + + if (!isCorrect && !Settings.IMP.MAIN.MIGRATION_HASH.isEmpty()) { + isCorrect = migrationHash.checkPassword(hash, password); + if (isCorrect) { + player.setHash(genHash(password)); + try { + playerDao.update(player); + } catch (SQLException e) { + e.printStackTrace(); + return false; + } + } } - return true; + return isCorrect; } - private boolean checkPasswordsRepeat(String[] args) { - if (Settings.IMP.MAIN.REGISTER_NEED_REPEAT_PASSWORD && !args[1].equals(args[2])) { - this.proxyPlayer.sendMessage(this.deserialize(Settings.IMP.MAIN.STRINGS.REGISTER_DIFFERENT_PASSWORDS)); - return false; + public static RegisteredPlayer fetchInfo(Dao<RegisteredPlayer, String> playerDao, UUID uuid) { + List<RegisteredPlayer> playerList = null; + try { + playerList = playerDao.queryForEq("PREMIUMUUID", uuid.toString()); + } catch (SQLException e) { + e.printStackTrace(); } - return true; + return (playerList != null ? playerList.size() : 0) == 0 ? null : playerList.get(0); } - private boolean checkArgsLength(int argsLength) { - if (this.playerInfo == null && Settings.IMP.MAIN.REGISTER_NEED_REPEAT_PASSWORD) { - return argsLength == 3; - } else { - return argsLength == 2; + public static RegisteredPlayer fetchInfo(Dao<RegisteredPlayer, String> playerDao, String nickname) { + List<RegisteredPlayer> playerList = null; + try { + playerList = playerDao.queryForEq("LOWERCASENICKNAME", nickname.toLowerCase(Locale.ROOT)); + } catch (SQLException e) { + e.printStackTrace(); } - } - private Component deserialize(String text) { - return LegacyComponentSerializer.legacyAmpersand().deserialize(text); + return (playerList != null ? playerList.size() : 0) == 0 ? null : playerList.get(0); } public static String genHash(String password) { - return BCrypt.withDefaults().hashToString(Settings.IMP.MAIN.BCRYPT_COST, password.toCharArray()); + return HASHER.hashToString(Settings.IMP.MAIN.BCRYPT_COST, password.toCharArray()); + } + + public static CodeVerifier getTotpCodeVerifier() { + return TOTP_CODE_VERIFIER; } private enum Command { + INVALID, REGISTER, LOGIN, @@ -438,17 +474,13 @@ public class AuthSessionHandler implements LimboSessionHandler { static Command parse(String command) { if (Settings.IMP.MAIN.REGISTER_COMMAND.contains(command)) { return Command.REGISTER; - } - - if (Settings.IMP.MAIN.LOGIN_COMMAND.contains(command)) { + } else if (Settings.IMP.MAIN.LOGIN_COMMAND.contains(command)) { return Command.LOGIN; - } - - if (Settings.IMP.MAIN.TOTP_COMMAND.contains(command)) { + } else if (Settings.IMP.MAIN.TOTP_COMMAND.contains(command)) { return Command.TOTP; + } else { + return Command.INVALID; } - - return Command.INVALID; } } } diff --git a/src/main/java/net/elytrium/limboauth/listener/AuthListener.java b/src/main/java/net/elytrium/limboauth/listener/AuthListener.java index c06d5f4..2c4b668 100644 --- a/src/main/java/net/elytrium/limboauth/listener/AuthListener.java +++ b/src/main/java/net/elytrium/limboauth/listener/AuthListener.java @@ -133,15 +133,11 @@ public class AuthListener { } if (!event.isOnlineMode() && !Settings.IMP.MAIN.OFFLINE_MODE_PREFIX.isEmpty()) { - event.setGameProfile(event.getOriginalProfile().withName( - Settings.IMP.MAIN.OFFLINE_MODE_PREFIX + event.getUsername() - )); + event.setGameProfile(event.getOriginalProfile().withName(Settings.IMP.MAIN.OFFLINE_MODE_PREFIX + event.getUsername())); } if (event.isOnlineMode() && !Settings.IMP.MAIN.ONLINE_MODE_PREFIX.isEmpty()) { - event.setGameProfile(event.getOriginalProfile().withName( - Settings.IMP.MAIN.ONLINE_MODE_PREFIX + event.getUsername() - )); + event.setGameProfile(event.getOriginalProfile().withName(Settings.IMP.MAIN.ONLINE_MODE_PREFIX + event.getUsername())); } } } diff --git a/src/main/java/net/elytrium/limboauth/migration/MigrationHash.java b/src/main/java/net/elytrium/limboauth/migration/MigrationHash.java index 669a82d..8707a88 100644 --- a/src/main/java/net/elytrium/limboauth/migration/MigrationHash.java +++ b/src/main/java/net/elytrium/limboauth/migration/MigrationHash.java @@ -19,61 +19,58 @@ package net.elytrium.limboauth.migration; import de.mkammerer.argon2.Argon2; import de.mkammerer.argon2.Argon2Factory; -import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import org.apache.commons.codec.binary.Hex; @SuppressWarnings("unused") public enum MigrationHash { + AUTHME((hash, password) -> { - String[] arr = hash.split("\\$"); // $SHA$salt$hash - return arr.length == 4 - && arr[3].equals(MigrationHash.getDigest(MigrationHash.getDigest(password, "SHA-256") + arr[2], "SHA-256")); + String[] args = hash.split("\\$"); // $SHA$salt$hash + return args.length == 4 && args[3].equals(getDigest(getDigest(password, "SHA-256") + args[2], "SHA-256")); }), AUTHME_NP((hash, password) -> { - String[] arr = hash.split("\\$"); // SHA$salt$hash - return arr.length == 3 - && arr[2].equals(MigrationHash.getDigest(MigrationHash.getDigest(password, "SHA-256") + arr[1], "SHA-256")); + String[] args = hash.split("\\$"); // SHA$salt$hash + return args.length == 3 && args[2].equals(getDigest(getDigest(password, "SHA-256") + args[1], "SHA-256")); }), ARGON2(new Argon2Verifier()), SHA512_DBA((hash, password) -> { - String[] arr = hash.split("\\$"); // SHA$salt$hash - return arr.length == 3 && arr[2].equals(MigrationHash.getDigest(MigrationHash.getDigest(password, "SHA-512") + arr[1], "SHA-512")); + String[] args = hash.split("\\$"); // SHA$salt$hash + return args.length == 3 && args[2].equals(getDigest(getDigest(password, "SHA-512") + args[1], "SHA-512")); }), SHA512_NP((hash, password) -> { - String[] arr = hash.split("\\$"); // SHA$salt$hash - return arr.length == 3 && arr[2].equals(MigrationHash.getDigest(password + arr[1], "SHA-512")); + String[] args = hash.split("\\$"); // SHA$salt$hash + return args.length == 3 && args[2].equals(getDigest(password + args[1], "SHA-512")); }), SHA512_P((hash, password) -> { - String[] arr = hash.split("\\$"); // $SHA$salt$hash - return arr.length == 4 && arr[3].equals(MigrationHash.getDigest(password + arr[2], "SHA-512")); + String[] args = hash.split("\\$"); // $SHA$salt$hash + return args.length == 4 && args[3].equals(getDigest(password + args[2], "SHA-512")); }), SHA256_NP((hash, password) -> { - String[] arr = hash.split("\\$"); // SHA$salt$hash - return arr.length == 3 && arr[2].equals(MigrationHash.getDigest(password + arr[1], "SHA-256")); + String[] args = hash.split("\\$"); // SHA$salt$hash + return args.length == 3 && args[2].equals(getDigest(password + args[1], "SHA-256")); }), SHA256_P((hash, password) -> { - String[] arr = hash.split("\\$"); // $SHA$salt$hash - return arr.length == 4 && arr[3].equals(MigrationHash.getDigest(password + arr[2], "SHA-256")); - }), - MD5((hash, password) -> { - return hash.equals(MigrationHash.getDigest(password, "MD5")); + String[] args = hash.split("\\$"); // $SHA$salt$hash + return args.length == 4 && args[3].equals(getDigest(password + args[2], "SHA-256")); }), + MD5((hash, password) -> hash.equals(getDigest(password, "MD5"))), MOON_SHA256((hash, password) -> { - String[] arr = hash.split("\\$"); // $SHA$hash - return arr.length == 3 && arr[2].equals(MigrationHash.getDigest(MigrationHash.getDigest(password, "SHA-256"), "SHA-256")); + String[] args = hash.split("\\$"); // $SHA$hash + return args.length == 3 && args[2].equals(getDigest(getDigest(password, "SHA-256"), "SHA-256")); }), SHA256_NO_SALT((hash, password) -> { - String[] arr = hash.split("\\$"); // $SHA$hash - return arr.length == 3 && arr[2].equals(MigrationHash.getDigest(password, "SHA-256")); + String[] args = hash.split("\\$"); // $SHA$hash + return args.length == 3 && args[2].equals(getDigest(password, "SHA-256")); }), SHA512_NO_SALT((hash, password) -> { - String[] arr = hash.split("\\$"); // $SHA$hash - return arr.length == 3 && arr[2].equals(MigrationHash.getDigest(password, "SHA-512")); + String[] args = hash.split("\\$"); // $SHA$hash + return args.length == 3 && args[2].equals(getDigest(password, "SHA-512")); }); - final MigrationHashVerifier verifier; + private final MigrationHashVerifier verifier; MigrationHash(MigrationHashVerifier verifier) { this.verifier = verifier; @@ -83,27 +80,23 @@ public enum MigrationHash { return this.verifier.checkPassword(hash, password); } - private static String getDigest(String string, String algo) { + private static String getDigest(String string, String algorithm) { try { - MessageDigest messageDigest = MessageDigest.getInstance(algo); - messageDigest.reset(); + MessageDigest messageDigest = MessageDigest.getInstance(algorithm); messageDigest.update(string.getBytes(StandardCharsets.UTF_8)); byte[] array = messageDigest.digest(); - return String.format("%0" + (array.length << 1) + "x", new BigInteger(1, array)); + return Hex.encodeHexString(array); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException(e); } } private static class Argon2Verifier implements MigrationHashVerifier { - private Argon2 argon2; + + private final Argon2 argon2 = Argon2Factory.create(); @Override public boolean checkPassword(String hash, String password) { - if (this.argon2 == null) { - this.argon2 = Argon2Factory.create(); - } - return this.argon2.verify(hash, password.getBytes(StandardCharsets.UTF_8)); } } diff --git a/src/main/java/net/elytrium/limboauth/model/RegisteredPlayer.java b/src/main/java/net/elytrium/limboauth/model/RegisteredPlayer.java index 7567f61..b54f9b8 100644 --- a/src/main/java/net/elytrium/limboauth/model/RegisteredPlayer.java +++ b/src/main/java/net/elytrium/limboauth/model/RegisteredPlayer.java @@ -20,7 +20,6 @@ package net.elytrium.limboauth.model; import com.j256.ormlite.field.DatabaseField; import com.j256.ormlite.table.DatabaseTable; -@SuppressWarnings("unused") @DatabaseTable(tableName = "AUTH") public class RegisteredPlayer { @@ -109,7 +108,7 @@ public class RegisteredPlayer { } public Long getRegDate() { - return this.regDate; + return this.regDate == null ? (Long) Long.MIN_VALUE : this.regDate; } public void setUuid(String uuid) { diff --git a/src/main/java/net/elytrium/limboauth/utils/SuggestUtils.java b/src/main/java/net/elytrium/limboauth/utils/SuggestUtils.java deleted file mode 100644 index 83f3687..0000000 --- a/src/main/java/net/elytrium/limboauth/utils/SuggestUtils.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2021 - 2022 Elytrium - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package net.elytrium.limboauth.utils; - -import com.google.common.collect.ImmutableList; -import com.velocitypowered.api.proxy.Player; -import com.velocitypowered.api.proxy.ProxyServer; -import java.util.List; -import java.util.stream.Collectors; - -public class SuggestUtils { - - public static List<String> suggestPlayers(String[] args, ProxyServer server) { - if (args.length == 0) { - return server.getAllPlayers().stream() - .map(Player::getUsername) - .collect(Collectors.toList()); - } else if (args.length == 1) { - return server.getAllPlayers().stream() - .map(Player::getUsername) - .filter(str -> str.regionMatches(true, 0, args[0], 0, args[0].length())) - .collect(Collectors.toList()); - } - - return ImmutableList.of(); - } -} diff --git a/src/main/java/net/elytrium/limboauth/utils/UpdatesChecker.java b/src/main/java/net/elytrium/limboauth/utils/UpdatesChecker.java deleted file mode 100644 index f7b7df3..0000000 --- a/src/main/java/net/elytrium/limboauth/utils/UpdatesChecker.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2021 - 2022 Elytrium - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package net.elytrium.limboauth.utils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.net.URLConnection; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; -import net.elytrium.limboauth.Settings; -import org.slf4j.Logger; - -public class UpdatesChecker { - - public static void checkForUpdates(Logger logger) { - try { - URLConnection conn = new URL("https://raw.githubusercontent.com/Elytrium/LimboAuth/master/VERSION").openConnection(); - int timeout = (int) TimeUnit.SECONDS.toMillis(5); - conn.setConnectTimeout(timeout); - conn.setReadTimeout(timeout); - try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { - String latestVersion = in.readLine(); - if (latestVersion == null) { - logger.warn("Unable to check for updates."); - return; - } - String latestVersion0 = getCleanVersion(latestVersion.trim()); - String currentVersion0 = getCleanVersion(Settings.IMP.VERSION); - int latestVersionId = Integer.parseInt(latestVersion0.replace(".", "").replace("$", "")); - int currentVersionId = Integer.parseInt(currentVersion0.replace(".", "").replace("$", "")); - if (latestVersion0.endsWith("$")) { - --latestVersionId; - } - if (currentVersion0.endsWith("$")) { - --currentVersionId; - } - - if (currentVersionId < latestVersionId) { - logger.error("****************************************"); - logger.warn("The new LimboAuth update was found, please update."); - logger.error("https://github.com/Elytrium/LimboAuth/releases/"); - logger.error("****************************************"); - } - } - } catch (IOException e) { - logger.warn("Unable to check for updates.", e); - } - } - - private static String getCleanVersion(String version) { - int indexOf = version.indexOf("-"); - if (indexOf > 0) { - return version.substring(0, indexOf) + "$"; // "$" - Indicates that the version is not release. - } else { - return version; - } - } -} |