From a6c2695ccbab8dc67c67d3859d6d4d6d712f60b0 Mon Sep 17 00:00:00 2001 From: Sen Date: Sat, 31 May 2025 00:23:35 +0200 Subject: [PATCH] complete login system for now, misc fixes --- .../client/network/ClientLoginHandler.java | 3 +- .../java/client/network/ClientPlayer.java | 2 +- .../common/network/IClientLoginHandler.java | 2 +- .../java/common/network/IClientPlayer.java | 2 +- .../common/network/IHandshakeHandler.java | 2 +- .../java/common/network/ILoginHandler.java | 2 +- .../src/main/java/common/network/IPlayer.java | 2 +- .../main/java/common/network/NetHandler.java | 14 +- server/src/main/java/server/Server.java | 91 ++++++------- .../src/main/java/server/command/Command.java | 4 + .../server/command/CommandEnvironment.java | 4 + .../java/server/command/PlayerParser.java | 2 +- .../main/java/server/command/UserParser.java | 34 +++++ .../server/command/commands/CommandAdmin.java | 18 +-- .../server/command/commands/CommandKick.java | 2 +- .../command/commands/CommandOfflinetp.java | 3 +- .../command/commands/CommandPasswd.java | 25 ++-- .../command/commands/CommandPubkey.java | 21 +-- .../command/commands/CommandRegister.java | 29 +++-- .../command/commands/CommandRegkey.java | 87 +++++++++++++ .../command/commands/CommandRevoke.java | 16 ++- .../server/command/commands/CommandUsers.java | 26 ++++ .../java/server/network/HandshakeHandler.java | 3 +- .../java/server/network/LoginHandler.java | 78 ++++++----- .../src/main/java/server/network/Player.java | 77 +++-------- server/src/main/java/server/network/User.java | 121 ++++++++++++++++++ 26 files changed, 460 insertions(+), 210 deletions(-) create mode 100644 server/src/main/java/server/command/UserParser.java create mode 100644 server/src/main/java/server/command/commands/CommandRegkey.java create mode 100644 server/src/main/java/server/command/commands/CommandUsers.java create mode 100644 server/src/main/java/server/network/User.java diff --git a/client/src/main/java/client/network/ClientLoginHandler.java b/client/src/main/java/client/network/ClientLoginHandler.java index 9aefdc4..ab32ec8 100755 --- a/client/src/main/java/client/network/ClientLoginHandler.java +++ b/client/src/main/java/client/network/ClientLoginHandler.java @@ -15,7 +15,6 @@ import common.net.util.concurrent.Future; import common.net.util.concurrent.GenericFutureListener; import common.network.IClientLoginHandler; import common.network.NetConnection; -import common.network.NetHandler; import common.network.PacketRegistry; import common.packet.LPacketChallenge; import common.packet.LPacketPassword; @@ -31,7 +30,7 @@ import common.packet.RPacketResponse; import common.packet.RPacketServerConfig; import common.util.EncryptUtil; -public class ClientLoginHandler extends NetHandler implements IClientLoginHandler { +public class ClientLoginHandler implements IClientLoginHandler { private static enum LoginState { HANDSHAKE, CONFIRMING, CHALLENGE, ENCRYPTED, PROVING, AUTHENTICATING, DONE; } diff --git a/client/src/main/java/client/network/ClientPlayer.java b/client/src/main/java/client/network/ClientPlayer.java index d91d530..0a061ba 100755 --- a/client/src/main/java/client/network/ClientPlayer.java +++ b/client/src/main/java/client/network/ClientPlayer.java @@ -144,7 +144,7 @@ import common.world.Explosion; import common.world.Weather; import common.world.World; -public class ClientPlayer extends NetHandler implements IClientPlayer +public class ClientPlayer implements IClientPlayer { /** * The NetworkManager instance used to communicate with the server (used only by handlePlayerPosLook to update diff --git a/common/src/main/java/common/network/IClientLoginHandler.java b/common/src/main/java/common/network/IClientLoginHandler.java index 706a18b..4048077 100644 --- a/common/src/main/java/common/network/IClientLoginHandler.java +++ b/common/src/main/java/common/network/IClientLoginHandler.java @@ -8,7 +8,7 @@ import common.packet.RPacketRequestEncrypt; import common.packet.RPacketResponse; import common.packet.RPacketServerConfig; -public interface IClientLoginHandler { +public interface IClientLoginHandler extends NetHandler { void handleDisconnect(RPacketDisconnect packet); void handleLoginSuccess(RPacketLoginSuccess packet); void handleEnableCompression(RPacketEnableCompression packet); diff --git a/common/src/main/java/common/network/IClientPlayer.java b/common/src/main/java/common/network/IClientPlayer.java index 4c67e3d..8fb9aec 100644 --- a/common/src/main/java/common/network/IClientPlayer.java +++ b/common/src/main/java/common/network/IClientPlayer.java @@ -72,7 +72,7 @@ import common.tileentity.IInteractionObject; import common.util.BlockPos; import common.world.World; -public interface IClientPlayer { +public interface IClientPlayer extends NetHandler { void playSound(Sound sound); boolean isRenderViewEntity(Entity entity); void updatePlayerMoveState(); diff --git a/common/src/main/java/common/network/IHandshakeHandler.java b/common/src/main/java/common/network/IHandshakeHandler.java index 1a534ba..ce6426f 100644 --- a/common/src/main/java/common/network/IHandshakeHandler.java +++ b/common/src/main/java/common/network/IHandshakeHandler.java @@ -2,6 +2,6 @@ package common.network; import common.packet.HPacketHandshake; -public interface IHandshakeHandler { +public interface IHandshakeHandler extends NetHandler { void processHandshake(HPacketHandshake packet); } diff --git a/common/src/main/java/common/network/ILoginHandler.java b/common/src/main/java/common/network/ILoginHandler.java index 48d654f..ff5d881 100644 --- a/common/src/main/java/common/network/ILoginHandler.java +++ b/common/src/main/java/common/network/ILoginHandler.java @@ -6,7 +6,7 @@ import common.packet.LPacketPubkey; import common.packet.LPacketResponse; import common.packet.LPacketStartEncrypt; -public interface ILoginHandler { +public interface ILoginHandler extends NetHandler { void processEncryption(LPacketStartEncrypt packet); void processPassword(LPacketPassword packet); void processPubkey(LPacketPubkey packet); diff --git a/common/src/main/java/common/network/IPlayer.java b/common/src/main/java/common/network/IPlayer.java index bbe9652..7637c4a 100644 --- a/common/src/main/java/common/network/IPlayer.java +++ b/common/src/main/java/common/network/IPlayer.java @@ -31,7 +31,7 @@ import common.util.CharValidator; import common.util.ChunkPos; import common.util.PortalType; -public interface IPlayer { +public interface IPlayer extends NetHandler { public static class UserValidator implements CharValidator { public boolean valid(char ch) { return (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '_' || ch == '-'; diff --git a/common/src/main/java/common/network/NetHandler.java b/common/src/main/java/common/network/NetHandler.java index 2c1f59a..2d8808b 100755 --- a/common/src/main/java/common/network/NetHandler.java +++ b/common/src/main/java/common/network/NetHandler.java @@ -1,9 +1,9 @@ package common.network; -public abstract class NetHandler { - private static final ThreadQuickExitException EXIT = new ThreadQuickExitException(); - +public interface NetHandler { public static final class ThreadQuickExitException extends RuntimeException { + private static final ThreadQuickExitException EXIT = new ThreadQuickExitException(); + private ThreadQuickExitException() { this.setStackTrace(new StackTraceElement[0]); } @@ -14,9 +14,9 @@ public abstract class NetHandler { } } - public abstract void onDisconnect(String reason); + void onDisconnect(String reason); - public void update() { + default void update() { } public static void checkThread(final Packet packet, final T handler, IThreadListener listener) throws ThreadQuickExitException { @@ -26,13 +26,13 @@ public abstract class NetHandler { packet.processPacket(handler); } }); - throw EXIT; + throw ThreadQuickExitException.EXIT; } } public static void checkThread(final Packet packet, final T handler, IThreadListener listener, Object check) throws ThreadQuickExitException { if(check == null && listener.isMainThread()) - throw EXIT; + throw ThreadQuickExitException.EXIT; checkThread(packet, handler, listener); } } diff --git a/server/src/main/java/server/Server.java b/server/src/main/java/server/Server.java index 5c9e910..f3f37eb 100755 --- a/server/src/main/java/server/Server.java +++ b/server/src/main/java/server/Server.java @@ -7,12 +7,12 @@ import java.io.IOException; import java.io.InputStreamReader; import java.net.InetAddress; import java.security.KeyPair; -import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import java.text.SimpleDateFormat; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; @@ -99,6 +99,7 @@ import server.command.Executor; import server.command.FixedExecutor; import server.network.HandshakeHandler; import server.network.Player; +import server.network.User; import server.world.Converter; import server.world.Region; import server.world.WorldServer; @@ -113,7 +114,8 @@ public final class Server implements IThreadListener { private final Thread serverThread = Thread.currentThread(); private final List clients = Collections.synchronizedList(Lists.newArrayList()); private final List players = Lists.newArrayList(); - private final Map usermap = Maps.newHashMap(); + private final Map online = Maps.newHashMap(); + private final Map users = Maps.newTreeMap(); private final Queue> queue = new ArrayDeque>(); private final long[] tickTimes = new long[100]; private final Map dimensions = Maps.newTreeMap(); @@ -277,20 +279,19 @@ public final class Server implements IThreadListener { return this.scriptEnv; } - public String[] getUsers() { + public List getPlayerFilenames() { String[] list = this.debug ? null : new File("players").list(); - if(list == null) { - list = new String[0]; - } + if(list == null) + return Lists.newArrayList(); + List names = Lists.newArrayList(); for(int i = 0; i < list.length; ++i) { if(list[i].endsWith(".cdt")) { - list[i] = list[i].substring(0, list[i].length() - 4); -// Player player = this.getPlayer(list[i]); -// if(player != null) -// list[i] = player.getUser(); + String name = list[i].substring(0, list[i].length() - 4); + if(IPlayer.isValidUser(name)) + names.add(name); } } - return list; + return names; } public void saveWorldInfo() { @@ -469,6 +470,7 @@ public final class Server implements IThreadListener { Log.SYSTEM.info("Generiere neues Schlüsselpaar"); this.keyPair = EncryptUtil.createKeypair(); } + User.loadDatabase(this.users); // if(dtime == -1L) // { // dtime = World.START_TIME; //// Config.set("spawnDim", "1", null); @@ -772,7 +774,7 @@ public final class Server implements IThreadListener { this.timePassed = this.ticksTodo = this.ticksDone = 0L; } - public List getAllUsernames() { + public List getAllPlayerNames() { List list = new ArrayList(this.players.size()); for(int z = 0; z < this.players.size(); z++) { list.add(this.players.get(z).getUser()); @@ -780,12 +782,24 @@ public final class Server implements IThreadListener { return list; } + public List getAllUserNames() { + return Lists.newArrayList(this.users.keySet()); + } + public List getPlayers() { return this.players; } + public Collection getUsers() { + return this.users.values(); + } + public Player getPlayer(String user) { - return this.usermap.get(user); + return this.online.get(user); + } + + public User getUser(String user) { + return this.users.get(user); } private ListenableFuture callFromMainThread(Callable callable) { @@ -848,43 +862,16 @@ public final class Server implements IThreadListener { radius > 0 ? (-180.0f + world.rand.floatv() * 360.0f) : Config.spawnYaw, radius > 0 ? 0.0f : Config.spawnPitch, world.dimension.getDimensionId()); } + + public void addUser(User user) { + this.users.put(user.getUser(), user); + } - public String addPlayer(NetConnection connection, String loginUser, String loginPass, PublicKey loginKey) { + public void addPlayer(NetConnection connection, String loginUser) { TagObject tag = this.readPlayer(loginUser); Player conn = new Player(this, connection, loginUser); if(tag != null) conn.readTags(tag); - if(Config.authenticate) { - if(conn.getPasswordHash() == null && conn.getPubkey() == null) { - if(tag != null) - return loginKey != null ? "Falscher Pubkey" : "Falsches Passwort"; - if(!Config.register) - return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; - if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit) - return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit); - if(loginKey != null) { - conn.setPubkey(loginKey); - Log.NETWORK.info(loginUser + " registrierte sich mit Pubkey"); - } - else { - if(loginPass == null || loginPass.length() == 0) - return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)"; - if(loginPass.length() < Config.minPassLength) - return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen"; - conn.setPasswordHash(EncryptUtil.hashPassword(loginPass)); - Log.NETWORK.info(loginUser + " registrierte sich mit Passwort"); - } - } - else if(conn.getPubkey() != null ? !conn.getPubkey().equals(loginKey) : - (loginPass == null || !MessageDigest.isEqual(EncryptUtil.hashPassword(loginPass, conn.getPasswordHash().second()), conn.getPasswordHash().first()))) { - return loginKey != null ? "Falscher Pubkey" : "Falsches Passwort"; - } - else { - Log.NETWORK.info(loginUser + " loggte sich mit " + (loginKey != null ? "Pubkey" : "Passwort") + " ein"); - } - } - if(Config.playerLimit > 0 && this.players.size() >= Config.playerLimit && !conn.isAdmin()) - return String.format("Der Server ist voll (%d/%d)!", this.players.size(), Config.playerLimit); if(Config.compression >= 0) { connection.sendPacket(new RPacketEnableCompression(Config.compression), new ChannelFutureListener() { public void operationComplete(ChannelFuture future) throws Exception { @@ -895,7 +882,11 @@ public final class Server implements IThreadListener { connection.sendPacket(new RPacketLoginSuccess(this.debug)); connection.setNetHandler(conn); this.players.add(conn); - this.usermap.put(loginUser, conn); + User user = this.users.remove(loginUser); + if(user != null) + conn.copyFrom(user); + this.users.put(loginUser, conn); + this.online.put(loginUser, conn); tag = conn.readCharacter(); WorldServer world = tag == null ? this.space : this.getWorld(tag.getInt("Dimension")); @@ -936,7 +927,6 @@ public final class Server implements IThreadListener { conn.sendPacket(new SPacketPlayerAbilities(player)); conn.addSelfToInternalCraftingInventory(); conn.onConnect(); - return null; } public void removePlayer(Player conn) { @@ -947,7 +937,10 @@ public final class Server implements IThreadListener { world.removeEntity(player); world.removePlayer(player); this.players.remove(conn); - this.usermap.remove(conn.getUser()); + this.online.remove(conn.getUser()); + User user = new User(conn.getUser()); + user.copyFrom(conn); + this.users.put(conn.getUser(), user); this.sendPacket(new SPacketPlayerListItem(true, conn)); } @@ -1215,7 +1208,7 @@ public final class Server implements IThreadListener { for(Player conn : this.players) { this.writePlayer(conn); } -// this.saveUsers(); + User.saveDatabase(this.users); } private void updateTimeAndWeatherForPlayer(Player conn, WorldServer world) { diff --git a/server/src/main/java/server/command/Command.java b/server/src/main/java/server/command/Command.java index 2eefab4..96fc1f8 100644 --- a/server/src/main/java/server/command/Command.java +++ b/server/src/main/java/server/command/Command.java @@ -175,6 +175,10 @@ public abstract class Command implements Executable { return this.addParameter(shortName, new TagParser(name, null)); } + protected Command addUser(String name, boolean defaulted) { + return this.addParameter(new UserParser(name, defaulted)); + } + protected Command addPlayer(String name, boolean defaulted) { return this.addParameter(new PlayerParser(name, defaulted)); } diff --git a/server/src/main/java/server/command/CommandEnvironment.java b/server/src/main/java/server/command/CommandEnvironment.java index 7bf8f24..c667330 100644 --- a/server/src/main/java/server/command/CommandEnvironment.java +++ b/server/src/main/java/server/command/CommandEnvironment.java @@ -27,6 +27,7 @@ import server.command.commands.CommandPlayers; import server.command.commands.CommandPotion; import server.command.commands.CommandPubkey; import server.command.commands.CommandRegister; +import server.command.commands.CommandRegkey; import server.command.commands.CommandRemove; import server.command.commands.CommandRevoke; import server.command.commands.CommandSave; @@ -36,6 +37,7 @@ import server.command.commands.CommandSpawn; import server.command.commands.CommandTele; import server.command.commands.CommandTime; import server.command.commands.CommandTp; +import server.command.commands.CommandUsers; import server.command.commands.CommandWarp; import server.command.commands.CommandWeather; import server.command.commands.CommandWorld; @@ -283,8 +285,10 @@ public class CommandEnvironment { this.registerExecutable(new CommandPasswd()); this.registerExecutable(new CommandPubkey()); this.registerExecutable(new CommandPlayers()); + this.registerExecutable(new CommandUsers()); this.registerExecutable(new CommandSave()); this.registerExecutable(new CommandRegister()); + this.registerExecutable(new CommandRegkey()); this.registerExecutable(new CommandFind()); this.registerExecutable(new CommandBlock()); this.registerExecutable(new CommandSeed()); diff --git a/server/src/main/java/server/command/PlayerParser.java b/server/src/main/java/server/command/PlayerParser.java index 2ca384a..658ab92 100644 --- a/server/src/main/java/server/command/PlayerParser.java +++ b/server/src/main/java/server/command/PlayerParser.java @@ -24,7 +24,7 @@ public class PlayerParser extends CompletingParser { } public Collection getCompletions(CommandEnvironment env) { - return env.getServer().getAllUsernames(); + return env.getServer().getAllPlayerNames(); } public Class getTypeClass(boolean required) { diff --git a/server/src/main/java/server/command/UserParser.java b/server/src/main/java/server/command/UserParser.java new file mode 100644 index 0000000..ba0a36e --- /dev/null +++ b/server/src/main/java/server/command/UserParser.java @@ -0,0 +1,34 @@ +package server.command; + +import java.util.Collection; + +import server.network.Player; +import server.network.User; + +public class UserParser extends CompletingParser { + protected final boolean useSender; + + public UserParser(String name, boolean useSender) { + super(name); + this.useSender = useSender; + } + + public Object parse(CommandEnvironment env, String input) { + User user = env.getServer().getUser(input); + if(user == null) + throw new RunException("Nutzer '%s' wurde nicht gefunden", input); + return user; + } + + public Object getDefault(CommandEnvironment env) { + return this.useSender && env.getExecutor().isPlayer() ? (Player)env.getExecutor() : null; + } + + public Collection getCompletions(CommandEnvironment env) { + return env.getServer().getAllUserNames(); + } + + public Class getTypeClass(boolean required) { + return User.class; + } +} diff --git a/server/src/main/java/server/command/commands/CommandAdmin.java b/server/src/main/java/server/command/commands/CommandAdmin.java index 49c155e..756a16d 100644 --- a/server/src/main/java/server/command/commands/CommandAdmin.java +++ b/server/src/main/java/server/command/commands/CommandAdmin.java @@ -5,21 +5,23 @@ import server.command.CommandEnvironment; import server.command.Executor; import server.command.RunException; import server.network.Player; +import server.network.User; public class CommandAdmin extends Command { public CommandAdmin() { super("admin"); - this.addPlayer("player", false); + this.addUser("user", false); } - public void exec(CommandEnvironment env, Executor exec, Player player) { - if(player == exec) + public void exec(CommandEnvironment env, Executor exec, User user) { + if(user == exec) throw new RunException("Du kannst nicht deinen eigenen Admin-Status erneut setzen"); - else if(player.getAdmin()) - throw new RunException("%s ist bereits ein Admin", player.getUser()); - player.setAdmin(true); - player.logConsole("Du hast Administatorrechte von %s bekommen", exec.getExecId()); - exec.logConsole("%s ist jetzt ein Admin", player.getUser()); + else if(user.isAdmin()) + throw new RunException("%s ist bereits ein Admin", user.getUser()); + user.setAdmin(true); + if(user.isOnline()) + ((Player)user).logConsole("Du hast Administatorrechte von %s bekommen", exec.getExecId()); + exec.logConsole("%s ist jetzt ein Admin", user.getUser()); } } diff --git a/server/src/main/java/server/command/commands/CommandKick.java b/server/src/main/java/server/command/commands/CommandKick.java index 80b3b5e..e67dfa3 100644 --- a/server/src/main/java/server/command/commands/CommandKick.java +++ b/server/src/main/java/server/command/commands/CommandKick.java @@ -18,7 +18,7 @@ public class CommandKick extends Command { public void exec(CommandEnvironment env, Executor exec, Player player, String message) { if(player == exec) throw new RunException("Du kannst nicht dich nicht selbst vom Server werfen"); - else if(player.getAdmin()) + else if(player.isAdmin()) throw new RunException("%s ist ein Admin", player.getUser()); player.disconnect(message); exec.logConsole("%s wurde vom Server geworfen", player.getUser()); diff --git a/server/src/main/java/server/command/commands/CommandOfflinetp.java b/server/src/main/java/server/command/commands/CommandOfflinetp.java index 33d551b..749f158 100644 --- a/server/src/main/java/server/command/commands/CommandOfflinetp.java +++ b/server/src/main/java/server/command/commands/CommandOfflinetp.java @@ -3,7 +3,6 @@ package server.command.commands; import java.util.Collection; import java.util.List; -import common.collect.Lists; import common.entity.Entity; import common.init.UniverseRegistry; import common.util.Position; @@ -20,7 +19,7 @@ public class CommandOfflinetp extends Command { this.addString("user", false, new StringCompleter() { public Collection complete(CommandEnvironment env) { - return Lists.newArrayList(env.getServer().getUsers()); + return env.getServer().getPlayerFilenames(); } }); diff --git a/server/src/main/java/server/command/commands/CommandPasswd.java b/server/src/main/java/server/command/commands/CommandPasswd.java index 93aff3f..c83e8a4 100644 --- a/server/src/main/java/server/command/commands/CommandPasswd.java +++ b/server/src/main/java/server/command/commands/CommandPasswd.java @@ -11,26 +11,27 @@ import server.command.CommandEnvironment; import server.command.Executor; import server.command.RunException; import server.network.Player; +import server.network.User; import server.util.Form; public class CommandPasswd extends Command { public CommandPasswd() { super("passwd"); - this.addPlayer("player", true); + this.addUser("user", true); this.setParamsOptional(); this.addString("password", false); } - public void exec(CommandEnvironment env, Executor exec, Player player, String password) { + public void exec(CommandEnvironment env, Executor exec, User user, String password) { if(exec.isPlayer()) { if(password != null) throw new RunException("Bei Verwendung als Spieler darf kein Passwort angegeben werden"); - if(player.getAdmin() && player != exec) - throw new RunException("%s ist ein Admin", player.getUser()); - if(player.getPubkey() != null && player == exec) + if(user.isAdmin() && user != exec) + throw new RunException("%s ist ein Admin", user.getUser()); + if(user.getPubkey() != null && user == exec) throw new RunException("Es darf kein Pubkey vorhanden sein, um diesen Befehl an sich selbst anzuwenden"); - if(player.getPasswordHash() == null && player == exec) + if(user.getPasswordHash() == null && user == exec) throw new RunException("Es muss ein Passwort vorhanden sein, um diesen Befehl an sich selbst anzuwenden"); ((Player)exec).displayForm(new Form() { private Field checkField; @@ -38,17 +39,17 @@ public class CommandPasswd extends Command { private Field confirmField; protected void init() { - this.checkField = player != exec ? null : this.addPassword("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, ""); + this.checkField = user != exec ? null : this.addPassword("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, ""); this.passwordField = this.addPassword("Neues Passwort", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); this.confirmField = this.addPassword("Passwort bestätigen", Config.minPassLength, IPlayer.MAX_PASS_LENGTH, ""); } public String getTitle() { - return "Passwort für " + player.getUser() + " ändern"; + return "Passwort für " + user.getUser() + " ändern"; } protected void accept() { - Player plr = env.getServer().getPlayer(player.getUser()); + User plr = env.getServer().getUser(user.getUser()); if(!((Player)exec).isAdmin() || plr == null || (plr.isAdmin() && plr != exec) || (plr.getPasswordHash() == null && plr == exec) || (plr.getPubkey() != null && plr == exec)) { exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten"); return; @@ -72,9 +73,9 @@ public class CommandPasswd extends Command { throw new RunException("Bei Verwendung in der Konsole muss ein Passwort angegeben werden"); if(password.length() < 8) throw new RunException("Das Passwort ist zu kurz, mindestens 8 Zeichen sind erforderlich"); - player.setPasswordHash(EncryptUtil.hashPassword(password)); - player.setPubkey(null); - exec.logConsole(TextColor.GREEN + "Passwort für %s gesetzt", player.getUser()); + user.setPasswordHash(EncryptUtil.hashPassword(password)); + user.setPubkey(null); + exec.logConsole(TextColor.GREEN + "Passwort für %s gesetzt", user.getUser()); } } } diff --git a/server/src/main/java/server/command/commands/CommandPubkey.java b/server/src/main/java/server/command/commands/CommandPubkey.java index 80c1cc0..7f6ac0d 100644 --- a/server/src/main/java/server/command/commands/CommandPubkey.java +++ b/server/src/main/java/server/command/commands/CommandPubkey.java @@ -12,13 +12,14 @@ import server.command.CommandEnvironment; import server.command.Executor; import server.command.RunException; import server.network.Player; +import server.network.User; import server.util.Form; public class CommandPubkey extends Command { public CommandPubkey() { super("pubkey"); - this.addPlayer("player", true); + this.addUser("user", true); this.setParamsOptional(); this.addString("keySpec", false); this.addString("keyData", false); @@ -26,27 +27,27 @@ public class CommandPubkey extends Command { this.addString("keyName", false); } - public void exec(CommandEnvironment env, Executor exec, Player player, String keySpec, String keyData, String keyHash, String keyName) { + public void exec(CommandEnvironment env, Executor exec, User user, String keySpec, String keyData, String keyHash, String keyName) { if(exec.isPlayer()) { if(keySpec != null || keyData != null || keyHash != null || keyName != null) throw new RunException("Bei Verwendung als Spieler darf kein Schlüssel angegeben werden"); - if(player.getAdmin() && player != exec) - throw new RunException("%s ist ein Admin", player.getUser()); + if(user.isAdmin() && user != exec) + throw new RunException("%s ist ein Admin", user.getUser()); ((Player)exec).displayForm(new Form() { private Field checkField; private Field keyField; protected void init() { - this.checkField = player.getPasswordHash() == null || player != exec ? null : this.addPassword("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, ""); + this.checkField = user.getPasswordHash() == null || user != exec ? null : this.addPassword("Aktuelles Passwort", 0, IPlayer.MAX_PASS_LENGTH, ""); this.keyField = this.addField("Neuer Pubkey", 1, 960, ""); } public String getTitle() { - return "Schlüssel für " + player.getUser() + " ändern"; + return "Schlüssel für " + user.getUser() + " ändern"; } protected void accept() { - Player plr = env.getServer().getPlayer(player.getUser()); + User plr = env.getServer().getUser(user.getUser()); if(!((Player)exec).isAdmin() || plr == null || (plr.isAdmin() && plr != exec)) { exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten"); return; @@ -79,9 +80,9 @@ public class CommandPubkey extends Command { catch(IllegalArgumentException e) { throw new RunException(e, "Ungültiger Schlüssel"); } - player.setPasswordHash(null); - player.setPubkey(key.first()); - exec.logConsole(TextColor.GREEN + "Schlüssel für %s gesetzt", player.getUser()); + user.setPasswordHash(null); + user.setPubkey(key.first()); + exec.logConsole(TextColor.GREEN + "Schlüssel für %s gesetzt", user.getUser()); } } } diff --git a/server/src/main/java/server/command/commands/CommandRegister.java b/server/src/main/java/server/command/commands/CommandRegister.java index 8722b67..48ec952 100644 --- a/server/src/main/java/server/command/commands/CommandRegister.java +++ b/server/src/main/java/server/command/commands/CommandRegister.java @@ -3,12 +3,13 @@ package server.command.commands; import common.color.TextColor; import common.init.Config; import common.network.IPlayer; -import common.tags.TagObject; +import common.util.EncryptUtil; import server.command.Command; import server.command.CommandEnvironment; import server.command.Executor; import server.command.RunException; import server.network.Player; +import server.network.User; import server.util.Form; public class CommandRegister extends Command { @@ -22,12 +23,10 @@ public class CommandRegister extends Command { } public void exec(CommandEnvironment env, Executor exec, String username, String password, boolean admin) { - Player player = env.getServer().getPlayer(username); - if(player != null) - throw new RunException("Ein Spieler mit diesem Nutzernamen ist bereits online"); - TagObject tag = env.getServer().loadPlayerData(username); - if(tag != null) + if(env.getServer().getUser(username) != null) throw new RunException("Ein Spieler mit diesem Nutzernamen ist bereits registriert"); + if(env.getServer().loadPlayerData(username) != null) + throw new RunException("Ein Spieler mit diesem Nutzernamen hat bereits Spielerdaten, ist aber nicht registriert"); if(exec.isPlayer()) { if(password != null) throw new RunException("Bei Verwendung als Spieler darf kein Passwort angegeben werden"); @@ -45,7 +44,7 @@ public class CommandRegister extends Command { } protected void accept() { - if(!((Player)exec).isAdmin() || env.getServer().getPlayer(username) != null || env.getServer().loadPlayerData(username) != null) { + if(!((Player)exec).isAdmin() || env.getServer().getUser(username) != null || env.getServer().loadPlayerData(username) != null) { exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten"); return; } @@ -53,9 +52,10 @@ public class CommandRegister extends Command { exec.logConsole(TextColor.RED + "Passwörter stimmen nicht überein"); return; } - TagObject user = new TagObject(); - user.setString("password", this.passwordField.get()); - env.getServer().writePlayerData(username, user); + User user = new User(username); + user.setPasswordHash(EncryptUtil.hashPassword(this.passwordField.get())); + user.setAdmin(admin); + env.getServer().addUser(user); exec.logConsole(TextColor.GREEN + "Spieler %s registriert", username); } }); @@ -63,9 +63,12 @@ public class CommandRegister extends Command { else if(exec.isConsole()) { if(password == null) throw new RunException("Bei Verwendung in der Konsole muss ein Passwort angegeben werden"); - TagObject user = new TagObject(); - user.setString("password", password); - env.getServer().writePlayerData(username, user); + if(password.length() < 8) + throw new RunException("Das Passwort ist zu kurz, mindestens 8 Zeichen sind erforderlich"); + User user = new User(username); + user.setPasswordHash(EncryptUtil.hashPassword(password)); + user.setAdmin(admin); + env.getServer().addUser(user); exec.logConsole(TextColor.GREEN + "Spieler %s registriert", username); } } diff --git a/server/src/main/java/server/command/commands/CommandRegkey.java b/server/src/main/java/server/command/commands/CommandRegkey.java new file mode 100644 index 0000000..0c635f5 --- /dev/null +++ b/server/src/main/java/server/command/commands/CommandRegkey.java @@ -0,0 +1,87 @@ +package server.command.commands; + +import java.security.PublicKey; + +import common.color.TextColor; +import common.network.IPlayer; +import common.util.EncryptUtil; +import common.util.Pair; +import server.command.Command; +import server.command.CommandEnvironment; +import server.command.Executor; +import server.command.RunException; +import server.network.Player; +import server.network.User; +import server.util.Form; + +public class CommandRegkey extends Command { + public CommandRegkey() { + super("regkey"); + + this.addString("username", false, null, IPlayer.MAX_USER_LENGTH, IPlayer.VALID_USER); + this.setParamsOptional(); + this.addString("keySpec", false); + this.addString("keyData", false); + this.addString("keyHash", false); + this.addString("keyName", false); + this.addFlag("admin", 'a'); + } + + public void exec(CommandEnvironment env, Executor exec, String username, String keySpec, String keyData, String keyHash, String keyName, boolean admin) { + if(env.getServer().getUser(username) != null) + throw new RunException("Ein Spieler mit diesem Nutzernamen ist bereits registriert"); + if(env.getServer().loadPlayerData(username) != null) + throw new RunException("Ein Spieler mit diesem Nutzernamen hat bereits Spielerdaten, ist aber nicht registriert"); + if(exec.isPlayer()) { + if(keySpec != null || keyData != null || keyHash != null || keyName != null) + throw new RunException("Bei Verwendung als Spieler darf kein Schlüssel angegeben werden"); + ((Player)exec).displayForm(new Form() { + private Field keyField; + + protected void init() { + this.keyField = this.addField("Pubkey", 1, 960, ""); + } + + public String getTitle() { + return "Spieler " + username + " registrieren"; + } + + protected void accept() { + if(!((Player)exec).isAdmin() || env.getServer().getUser(username) != null || env.getServer().loadPlayerData(username) != null) { + exec.logConsole(TextColor.DRED + "Ein Fehler ist aufgetreten"); + return; + } + Pair key; + try { + key = EncryptUtil.parseArmoredPubkey(this.keyField.get()); + } + catch(IllegalArgumentException e) { + exec.logConsole(TextColor.RED + "Ungültiger Schlüssel"); + return; + } + User user = new User(username); + user.setPubkey(key.first()); + user.setAdmin(admin); + env.getServer().addUser(user); + exec.logConsole(TextColor.GREEN + "Spieler %s registriert", username); + } + }); + } + else if(exec.isConsole()) { + if(keySpec == null || keyData == null || keyHash == null) + throw new RunException("Bei Verwendung in der Konsole muss ein Schlüssel angegeben werden"); + Pair key; + try { + key = EncryptUtil.parseArmoredPubkey(keySpec + " " + keyData + " " + keyHash + (keyName == null ? null : " " + keyName)); + } + catch(IllegalArgumentException e) { + throw new RunException(e, "Ungültiger Schlüssel"); + } + User user = new User(username); + user.setPubkey(key.first()); + user.setAdmin(admin); + env.getServer().addUser(user); + exec.logConsole(TextColor.GREEN + "Spieler %s registriert", username); + } + } +} diff --git a/server/src/main/java/server/command/commands/CommandRevoke.java b/server/src/main/java/server/command/commands/CommandRevoke.java index 075dd3d..253efd8 100644 --- a/server/src/main/java/server/command/commands/CommandRevoke.java +++ b/server/src/main/java/server/command/commands/CommandRevoke.java @@ -5,23 +5,25 @@ import server.command.CommandEnvironment; import server.command.Executor; import server.command.RunException; import server.network.Player; +import server.network.User; public class CommandRevoke extends Command { public CommandRevoke() { super("revoke"); - this.addPlayer("player", false); + this.addUser("user", false); } - public void exec(CommandEnvironment env, Executor exec, Player player) { + public void exec(CommandEnvironment env, Executor exec, User user) { if(!exec.isConsole()) throw new RunException("Dieser Befehl kann nur der Konsole ausgeführt werden"); // else if(player == exec) // throw new RunException("Du kannst nicht deinen eigenen Admin-Status entfernen"); - else if(!player.getAdmin()) - throw new RunException("%s ist kein Admin", player.getUser()); - player.setAdmin(false); - player.logConsole("Der Host hat deine Administatorrechte entfernt"); - exec.logConsole("%s ist jetzt kein Admin mehr", player.getUser()); + else if(!user.isAdmin()) + throw new RunException("%s ist kein Admin", user.getUser()); + user.setAdmin(false); + if(user.isOnline()) + ((Player)user).logConsole("Der Host hat deine Administatorrechte entfernt"); + exec.logConsole("%s ist jetzt kein Admin mehr", user.getUser()); } } diff --git a/server/src/main/java/server/command/commands/CommandUsers.java b/server/src/main/java/server/command/commands/CommandUsers.java new file mode 100644 index 0000000..212ac13 --- /dev/null +++ b/server/src/main/java/server/command/commands/CommandUsers.java @@ -0,0 +1,26 @@ +package server.command.commands; + +import java.util.Collection; +import common.color.TextColor; +import server.command.Command; +import server.command.CommandEnvironment; +import server.command.Executor; +import server.network.User; + +public class CommandUsers extends Command { + public CommandUsers() { + super("users"); + } + + public void exec(CommandEnvironment env, Executor exec) { + Collection users = env.getServer().getUsers(); + if(users.isEmpty()) { + exec.logConsole(TextColor.DGRAY + "Es sind keine Spieler registriert"); + return; + } + exec.logConsole(TextColor.GREEN + "Es " + (users.size() == 1 ? "ist" : "sind") + " " + TextColor.YELLOW + "%d" + TextColor.GREEN + " Spieler registriert", users.size()); + for(User user : users) { + exec.logConsole("%s%s" + TextColor.GRAY + ": %s" + TextColor.GRAY + ", %s", user.isAdmin() ? TextColor.RED : TextColor.NEON, user.getUser(), user.isOnline() ? TextColor.DGREEN + "Online" : TextColor.DGRAY + "Offline", user.getPubkey() != null ? TextColor.YELLOW + "Pubkey" : (user.getPasswordHash() != null ? TextColor.MAGENTA + "Passwort" : TextColor.BLACK + "Kein Login")); + } + } +} diff --git a/server/src/main/java/server/network/HandshakeHandler.java b/server/src/main/java/server/network/HandshakeHandler.java index 54d51cf..fb42a35 100755 --- a/server/src/main/java/server/network/HandshakeHandler.java +++ b/server/src/main/java/server/network/HandshakeHandler.java @@ -2,14 +2,13 @@ package server.network; import common.network.IHandshakeHandler; import common.network.NetConnection; -import common.network.NetHandler; import common.network.PacketRegistry; import common.packet.HPacketHandshake; import common.packet.RPacketDisconnect; import common.util.Util; import server.Server; -public class HandshakeHandler extends NetHandler implements IHandshakeHandler +public class HandshakeHandler implements IHandshakeHandler { private final Server server; private final NetConnection networkManager; diff --git a/server/src/main/java/server/network/LoginHandler.java b/server/src/main/java/server/network/LoginHandler.java index b00729a..d35af4d 100755 --- a/server/src/main/java/server/network/LoginHandler.java +++ b/server/src/main/java/server/network/LoginHandler.java @@ -1,5 +1,6 @@ package server.network; +import java.security.MessageDigest; import java.security.PrivateKey; import java.security.PublicKey; import java.security.SecureRandom; @@ -15,7 +16,6 @@ import common.net.util.concurrent.GenericFutureListener; import common.network.ILoginHandler; import common.network.IPlayer; import common.network.NetConnection; -import common.network.NetHandler; import common.packet.LPacketChallenge; import common.packet.LPacketPassword; import common.packet.LPacketPubkey; @@ -26,9 +26,10 @@ import common.packet.RPacketDisconnect; import common.packet.RPacketRequestEncrypt; import common.packet.RPacketResponse; import common.packet.RPacketServerConfig; +import common.util.EncryptUtil; import server.Server; -public class LoginHandler extends NetHandler implements ILoginHandler +public class LoginHandler implements ILoginHandler { private static enum LoginState { INIT, ENCRYPT, PROOF, PASSWORD, CHALLENGE, AUTHENTICATED, ACCEPTED; @@ -80,38 +81,57 @@ public class LoginHandler extends NetHandler implements ILoginHandler public void update() { if(this.state == LoginState.AUTHENTICATED) - this.tryAcceptPlayer(); -// else if (this.currentLoginState == LoginState.DELAY_ACCEPT) -// { -// if (this.server.getPlayer(this.loginUser) == null) -// { -// this.currentLoginState = LoginState.ACCEPTED; -// this.server.addPlayer(this.networkManager, this.loginUser); -// } -// } + this.tryAdvance(); if(this.timer++ == 600) this.closeConnection("Anmeldung dauerte zu lange"); } - private void tryAcceptPlayer() - { - if(this.server.getPlayer(this.loginUser) != null) { - this.closeConnection("Nutzername '" + this.loginUser + "' ist bereits vergeben."); - return; - } -// if (this.server.getPlayer(this.loginUser) != null) -// { -// this.closeConnection("Nutzername '" + this.loginUser + "' ist bereits vergeben."); -// return; -//// this.currentLoginState = LoginState.DELAY_ACCEPT; -//// player.netHandler.kick("Du hast dich von einen anderen Ort verbunden"); -// } -// this.networkManager.sendPacket(new RPacketLoginSuccess()); - String kick = this.server.addPlayer(this.netManager, this.loginUser, this.loginPass, this.loginKey); - if(kick != null) + private void tryAdvance() { + String kick = this.checkPlayer(); + if(kick != null) { this.closeConnection(kick); - else - this.state = LoginState.ACCEPTED; + return; + } + this.server.addPlayer(this.netManager, this.loginUser); + this.state = LoginState.ACCEPTED; + } + + private String checkPlayer() { + if(this.server.getPlayer(this.loginUser) != null) + return "Nutzername '" + this.loginUser + "' ist bereits vergeben"; + User user = this.server.getUser(this.loginUser); + if(Config.authenticate) { + if(user == null) { + if(!Config.register) + return "Anmeldung neuer Accounts ist auf diesem Server deaktiviert (Whitelisted)"; + if(Config.playerLimit > 0 && this.server.getPlayers().size() >= Config.playerLimit) + return String.format("Der Server ist voll (%d/%d)!", this.server.getPlayers().size(), Config.playerLimit); + if(this.loginKey != null) { + this.server.addUser(user = new User(this.loginUser)); + user.setPubkey(this.loginKey); + Log.NETWORK.info(this.loginUser + " registrierte sich mit Pubkey"); + } + else { + if(this.loginPass == null || this.loginPass.length() == 0) + return "Ein neues Passwort ist erforderlich um diesen Server zu betreten (mindestens " + Config.minPassLength + " Zeichen)"; + if(this.loginPass.length() < Config.minPassLength) + return "Passwort ist zu kurz, mindestens " + Config.minPassLength + " Zeichen"; + this.server.addUser(user = new User(this.loginUser)); + user.setPasswordHash(EncryptUtil.hashPassword(this.loginPass)); + Log.NETWORK.info(this.loginUser + " registrierte sich mit Passwort"); + } + } + else if((user.getPasswordHash() == null && user.getPubkey() == null) || (user.getPubkey() != null ? !user.getPubkey().equals(this.loginKey) : + (this.loginPass == null || !MessageDigest.isEqual(EncryptUtil.hashPassword(this.loginPass, user.getPasswordHash().second()), user.getPasswordHash().first())))) { + return this.loginKey != null ? "Falscher Pubkey" : "Falsches Passwort"; + } + else { + Log.NETWORK.info(this.loginUser + " loggte sich mit " + (this.loginKey != null ? "Pubkey" : "Passwort") + " ein"); + } + } + if(Config.playerLimit > 0 && this.server.getPlayers().size() >= Config.playerLimit && (user == null || !user.isAdmin())) + return String.format("Der Server ist voll (%d/%d)!", this.server.getPlayers().size(), Config.playerLimit); + return null; } public void sendLoginPacket() { diff --git a/server/src/main/java/server/network/Player.java b/server/src/main/java/server/network/Player.java index 3021b61..538142f 100755 --- a/server/src/main/java/server/network/Player.java +++ b/server/src/main/java/server/network/Player.java @@ -1,6 +1,5 @@ package server.network; -import java.security.PublicKey; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -111,11 +110,9 @@ import common.tileentity.TileEntitySign; import common.util.BlockPos; import common.util.BoundingBox; import common.util.ChunkPos; -import common.util.EncryptUtil; import common.util.ExtMath; import common.util.Facing; import common.util.IntHashMap; -import common.util.Pair; import common.util.PortalType; import common.util.Position; import common.util.Vec3i; @@ -138,7 +135,7 @@ import server.world.ChunkServer; import server.world.Region; import server.world.WorldServer; -public class Player extends NetHandler implements ICrafting, Executor, IPlayer +public class Player extends User implements ICrafting, Executor, IPlayer { private static enum EditAction { SELECT("Auswahlmodus"), COPYPASTE("Kopiermodus"), TRANSFORM("Drehmodus"); @@ -152,7 +149,6 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer private final NetConnection connection; private final Server server; - private final String user; private final IntHashMap transactions = new IntHashMap(); private final List characters = Lists.newArrayList(); @@ -170,11 +166,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer private boolean charEditor = true; private Form form; - private boolean admin; private int ping; - private boolean deleted; - private Pair password; - private PublicKey pubkey; private boolean profiling; private int selectionDim = Integer.MIN_VALUE; @@ -217,7 +209,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer public Player(Server server, NetConnection connection, String user) { - this.user = user; + super(user); this.server = server; this.connection = connection; // this.local = connection.isLocalChannel(); @@ -255,7 +247,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer this.pingKey = (int)this.lastPingTime; this.sendPacket(new SPacketKeepAlive(this.pingKey)); } - if(this.admin && this.profiling) + if(this.isAdmin() && this.profiling) this.sendPacket(new SPacketServerTick((int)this.server.getLastTick())); if(this.respawnTimer > 0) { if(--this.respawnTimer == 0) { @@ -264,6 +256,10 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer } } + public boolean isOnline() { + return true; + } + public void displayLoading(String message) { this.sendPacket(new SPacketLoading(message)); } @@ -323,10 +319,6 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer public EntityNPC getEntity() { return this.entity; } - - public String getUser() { - return this.user; - } // public boolean isLocal() { // return this.local; @@ -335,30 +327,6 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer public int getLatency() { return this.ping; } - - public boolean isAdmin() { - return this.admin; // || this.local; - } - - public boolean getAdmin() { - return this.isAdmin(); - } - - public void setPasswordHash(Pair hash) { - this.password = hash; - } - - public Pair getPasswordHash() { - return this.password; - } - - public void setPubkey(PublicKey key) { - this.pubkey = key; - } - - public PublicKey getPubkey() { - return this.pubkey; - } @@ -628,11 +596,6 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer public void readTags(TagObject tag) { - this.admin = tag.getBool("admin"); - if(tag.hasByteArray("passwordHash") && tag.hasByteArray("passwordSalt")) - this.password = new Pair(tag.getByteArray("passwordHash"), tag.getByteArray("passwordSalt")); - if(tag.hasByteArray("pubkey")) - this.pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("pubkey")); this.selected = tag.getInt("selected"); List list = tag.getList("characters"); for(int z = 0; z < list.size(); z++) { @@ -643,14 +606,6 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer } public void writeTags(TagObject tag) { - if(this.admin) - tag.setBool("admin", this.admin); - if(this.password != null) { - tag.setByteArray("passwordHash", this.password.first()); - tag.setByteArray("passwordSalt", this.password.second()); - } - if(this.pubkey != null) - tag.setByteArray("pubkey", this.pubkey.getEncoded()); if(!this.characters.isEmpty()) { tag.setInt("selected", this.selected); List list = Lists.newArrayList(); @@ -1621,7 +1576,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer public void disconnect(String message) { - Log.NETWORK.info("Trenne %s: %s", this.user, message); + Log.NETWORK.info("Trenne %s: %s", this.getUser(), message); this.connection.sendPacket(new SPacketDisconnect(message), new GenericFutureListener < Future > () { public void operationComplete(Future p_operationComplete_1_) throws Exception @@ -1641,8 +1596,8 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer public void onDisconnect(String reason) { - Log.NETWORK.info(this.user + " wurde getrennt: " + TextColor.stripCodes(reason)); - this.server.sendPacket(new SPacketMessage(String.format("%s hat das Spiel verlassen", this.user), Type.FEED)); + Log.NETWORK.info(this.getUser() + " wurde getrennt: " + TextColor.stripCodes(reason)); + this.server.sendPacket(new SPacketMessage(String.format("%s hat das Spiel verlassen", this.getUser()), Type.FEED)); this.server.removePlayer(this); } @@ -1666,7 +1621,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer chars.add(this.getCharacterInfo(tag)); } this.sendPacket(new SPacketCharacterList(this.selected, chars)); - this.server.sendPacket(new SPacketMessage(String.format("%s hat das Spiel betreten", this.user), Type.FEED)); + this.server.sendPacket(new SPacketMessage(String.format("%s hat das Spiel betreten", this.getUser()), Type.FEED)); } public void sendPacket(final Packet packet) @@ -1779,7 +1734,7 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer } public void setAdmin(boolean admin) { - this.admin = admin; + super.setAdmin(admin); if(!this.isAdmin() && this.entity != null && this.entity.noclip) { this.entity.noclip = false; this.entity.flying &= this.entity.hasEffect(Potion.FLYING) || this.entity.canNaturallyFly(); @@ -1956,11 +1911,11 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer } public String getExecId() { - return this.user; + return this.getUser(); } public String getExecName() { - return this.entity != null ? this.entity.getCommandName() : this.user; + return this.entity != null ? this.entity.getCommandName() : this.getUser(); } public Position getExecPos() { @@ -2893,12 +2848,12 @@ public class Player extends NetHandler implements ICrafting, Executor, IPlayer break; case START_PROFILING: - if(this.admin) + if(this.isAdmin()) this.profiling = true; break; case STOP_PROFILING: - if(this.admin) + if(this.isAdmin()) this.profiling = false; break; diff --git a/server/src/main/java/server/network/User.java b/server/src/main/java/server/network/User.java new file mode 100644 index 0000000..6b52716 --- /dev/null +++ b/server/src/main/java/server/network/User.java @@ -0,0 +1,121 @@ +package server.network; + +import java.io.File; +import java.security.PublicKey; +import java.util.Map; + +import common.log.Log; +import common.network.IPlayer; +import common.tags.TagObject; +import common.util.EncryptUtil; +import common.util.Pair; + +public class User { + private final String user; + private boolean admin; + private Pair password; + private PublicKey pubkey; + + public User(String user) { + this.user = user; + } + + public boolean isOnline() { + return false; + } + + public String getUser() { + return this.user; + } + + public void setAdmin(boolean admin) { + this.admin = admin; + } + + public boolean isAdmin() { + return this.admin; + } + + public void setPasswordHash(Pair hash) { + this.password = hash; + } + + public Pair getPasswordHash() { + return this.password; + } + + public void setPubkey(PublicKey key) { + this.pubkey = key; + } + + public PublicKey getPubkey() { + return this.pubkey; + } + + public void copyFrom(User user) { + this.admin = user.admin; + this.password = user.password; + this.pubkey = user.pubkey; + } + + public void readUserTags(TagObject tag) { + this.admin = tag.getBool("admin"); + if(tag.hasByteArray("passwordHash") && tag.hasByteArray("passwordSalt")) + this.password = new Pair(tag.getByteArray("passwordHash"), tag.getByteArray("passwordSalt")); + if(tag.hasByteArray("pubkey")) + this.pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("pubkey")); + } + + public void writeUserTags(TagObject tag) { + if(this.admin) + tag.setBool("admin", this.admin); + if(this.password != null) { + tag.setByteArray("passwordHash", this.password.first()); + tag.setByteArray("passwordSalt", this.password.second()); + } + if(this.pubkey != null) + tag.setByteArray("pubkey", this.pubkey.getEncoded()); + } + + public static void saveDatabase(Map map) { + TagObject tag = new TagObject(); + for(User user : map.values()) { + TagObject data = new TagObject(); + user.writeUserTags(data); + tag.setObject(user.getUser(), data); + } + File nfile = new File("users.cdt.tmp"); + File lfile = new File("users.cdt"); + try { + TagObject.writeGZip(tag, nfile); + if(lfile.exists()) + lfile.delete(); + nfile.renameTo(lfile); + } + catch(Exception e) { + Log.IO.error(e, "Fehler beim Schreiben von " + nfile); + } + } + + public static void loadDatabase(Map map) { + File file = new File("users.cdt"); + if(!file.exists()) + file = new File("users.cdt.tmp"); + if(file.exists()) { + try { + TagObject tag = TagObject.readGZip(file); + for(String key : tag.keySet()) { + if(IPlayer.isValidUser(key) && tag.hasObject(key)) { + User user = new User(key); + user.readUserTags(tag.getObject(key)); + map.put(key, user); + } + } + } + catch(Exception e) { + Log.IO.error(e, "Fehler beim Lesen von " + file); + map.clear(); + } + } + } +}