diff options
| author | mdxd44 <ogurec332@mail.ru> | 2021-12-17 19:31:55 +0900 |
|---|---|---|
| committer | mdxd44 <ogurec332@mail.ru> | 2021-12-17 19:31:55 +0900 |
| commit | cff1b4a22bb47c8bcf064d5e8da8c7d7ef67ea52 (patch) | |
| tree | 9fc8614b144288af2749c2376c8ca523cd2a0cc0 /src/main/java/net/elytrium/limboauth/handler | |
| parent | f11b09654cc33f4c3d9239c04be5978cbe3cad2d (diff) | |
| download | LimboAuth-cff1b4a22bb47c8bcf064d5e8da8c7d7ef67ea52.tar.gz LimboAuth-cff1b4a22bb47c8bcf064d5e8da8c7d7ef67ea52.tar.bz2 LimboAuth-cff1b4a22bb47c8bcf064d5e8da8c7d7ef67ea52.zip | |
Split projects.
Diffstat (limited to 'src/main/java/net/elytrium/limboauth/handler')
| -rw-r--r-- | src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java new file mode 100644 index 0000000..38a464d --- /dev/null +++ b/src/main/java/net/elytrium/limboauth/handler/AuthSessionHandler.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2021 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.handler; + +import at.favre.lib.crypto.bcrypt.BCrypt; +import com.j256.ormlite.dao.Dao; +import com.velocitypowered.api.proxy.Player; +import dev.samstevens.totp.code.CodeVerifier; +import dev.samstevens.totp.code.DefaultCodeGenerator; +import dev.samstevens.totp.code.DefaultCodeVerifier; +import dev.samstevens.totp.time.SystemTimeProvider; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.text.MessageFormat; +import java.util.List; +import java.util.Locale; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import net.elytrium.limboapi.api.Limbo; +import net.elytrium.limboapi.api.LimboSessionHandler; +import net.elytrium.limboapi.api.player.LimboPlayer; +import net.elytrium.limboauth.LimboAuth; +import net.elytrium.limboauth.Settings; +import net.elytrium.limboauth.migration.MigrationHash; +import net.elytrium.limboauth.model.RegisteredPlayer; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; + +public class AuthSessionHandler implements LimboSessionHandler { + + private static final CodeVerifier verifier = new DefaultCodeVerifier(new DefaultCodeGenerator(), new SystemTimeProvider()); + + private final Dao<RegisteredPlayer, String> playerDao; + private final Player proxyPlayer; + private final RegisteredPlayer playerInfo; + + private LimboPlayer player; + private String ip; + private int attempts = Settings.IMP.MAIN.LOGIN_ATTEMPTS; + private boolean totp = false; + + public AuthSessionHandler(Dao<RegisteredPlayer, String> playerDao, Player proxyPlayer, String lowercaseNickname) { + this.playerDao = playerDao; + this.proxyPlayer = proxyPlayer; + this.playerInfo = this.fetchInfo(lowercaseNickname); + } + + @Override + public void onSpawn(Limbo server, LimboPlayer player) { + this.player = player; + this.player.disableFalling(); + this.ip = this.proxyPlayer.getRemoteAddress().getAddress().getHostAddress(); + + if (this.playerInfo == null) { + this.checkIp(); + } else { + this.checkCase(); + } + + this.sendMessage(); + } + + @Override + public void onChat(String message) { + String[] args = message.split(" "); + if (args.length != 0 && this.checkArgsLength(args.length)) { + switch (args[0]) { + case "/reg": + case "/register": + case "/r": { + if (!this.totp && this.playerInfo == null && this.checkPasswordsRepeat(args)) { + this.register(args[1]); + this.finishAuth(); + } else { + this.sendMessage(); + } + break; + } + case "/log": + case "/login": + case "/l": { + if (!this.totp && this.playerInfo != null) { + if (this.checkPassword(args[1])) { + this.finishOrTotp(); + } else if (--this.attempts != 0) { + this.proxyPlayer.sendMessage( + LegacyComponentSerializer.legacyAmpersand().deserialize( + MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN_WRONG_PASSWORD, this.attempts) + ) + ); + } else { + this.proxyPlayer.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.KICK_PASSWORD_WRONG)); + } + } else { + this.sendMessage(); + } + break; + } + case "/totp": + case "/2fa": { + if (this.totp) { + if (verifier.isValidCode(this.playerInfo.getTotpToken(), args[1])) { + this.finishAuth(); + } else { + this.sendMessage(); + } + } else { + this.sendMessage(); + } + break; + } + default: { + this.sendMessage(); + break; + } + } + } else { + this.sendMessage(); + } + } + + 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); + } + + private RegisteredPlayer fetchInfo(String nickname) { + return fetchInfo(this.playerDao, nickname); + } + + public static CodeVerifier getVerifier() { + return verifier; + } + + public static boolean checkPassword(String password, RegisteredPlayer player, Dao<RegisteredPlayer, String> playerDao) { + boolean isCorrect = BCrypt.verifyer().verify( + password.getBytes(StandardCharsets.UTF_8), player.getHash().getBytes(StandardCharsets.UTF_8) + ).verified; + + 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 isCorrect; + } + + private boolean checkPassword(String password) { + return checkPassword(password, this.playerInfo, this.playerDao); + } + + private void checkIp() { + try { + List<RegisteredPlayer> alreadyRegistered = this.playerDao.queryForEq("IP", this.ip); + + if (alreadyRegistered == null) { + return; + } + + 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() < checkDate) + .forEach(e -> { + try { + e.setIP(""); + this.playerDao.update(e); + sizeOfValid.decrementAndGet(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + }); + } + + if (sizeOfValid.get() >= Settings.IMP.MAIN.IP_LIMIT_REGISTRATIONS) { + this.proxyPlayer.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.IP_LIMIT)); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void checkCase() { + if (!this.proxyPlayer.getUsername().equals(this.playerInfo.getNickname())) { + this.proxyPlayer.disconnect(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.WRONG_NICKNAME_CASE)); + } + } + + 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(), + "" + ); + + try { + this.playerDao.create(registeredPlayer); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void finishOrTotp() { + if (this.playerInfo.getTotpToken().isEmpty()) { + this.finishAuth(); + } else { + this.totp = true; + this.sendMessage(); + } + } + + private void finishAuth() { + this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.LOGIN_SUCCESS)); + LimboAuth.getInstance().cacheAuthUser(this.proxyPlayer); + this.player.disconnect(); + } + + private void sendMessage() { + if (this.totp) { + this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.TOTP)); + } else if (this.playerInfo == null) { + this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.REGISTER)); + } else { + this.proxyPlayer.sendMessage( + LegacyComponentSerializer.legacyAmpersand().deserialize(MessageFormat.format(Settings.IMP.MAIN.STRINGS.LOGIN, this.attempts)) + ); + } + } + + private boolean checkPasswordsRepeat(String[] args) { + if (Settings.IMP.MAIN.REGISTER_NEED_REPEAT_PASSWORD && !args[1].equals(args[2])) { + this.proxyPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(Settings.IMP.MAIN.STRINGS.DIFFERENT_PASSWORDS)); + return false; + } + + return true; + } + + 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 String genHash(String password) { + return BCrypt.withDefaults().hashToString(Settings.IMP.MAIN.BCRYPT_COST, password.toCharArray()); + } +} |
