complete login system for now, misc fixes

This commit is contained in:
Sen 2025-05-31 00:23:35 +02:00
parent 06a14ae645
commit a6c2695ccb
Signed by: sen
GPG key ID: 3AC50A6F47D1B722
26 changed files with 460 additions and 210 deletions

View file

@ -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<NetConnection> clients = Collections.<NetConnection>synchronizedList(Lists.<NetConnection>newArrayList());
private final List<Player> players = Lists.<Player>newArrayList();
private final Map<String, Player> usermap = Maps.<String, Player>newHashMap();
private final Map<String, Player> online = Maps.<String, Player>newHashMap();
private final Map<String, User> users = Maps.newTreeMap();
private final Queue<FutureTask<?>> queue = new ArrayDeque<FutureTask<?>>();
private final long[] tickTimes = new long[100];
private final Map<Integer, WorldServer> dimensions = Maps.newTreeMap();
@ -277,20 +279,19 @@ public final class Server implements IThreadListener {
return this.scriptEnv;
}
public String[] getUsers() {
public List<String> getPlayerFilenames() {
String[] list = this.debug ? null : new File("players").list();
if(list == null) {
list = new String[0];
}
if(list == null)
return Lists.newArrayList();
List<String> 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<String> getAllUsernames() {
public List<String> getAllPlayerNames() {
List<String> list = new ArrayList<String>(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<String> getAllUserNames() {
return Lists.newArrayList(this.users.keySet());
}
public List<Player> getPlayers() {
return this.players;
}
public Collection<User> 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 <V> ListenableFuture<V> callFromMainThread(Callable<V> 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) {

View file

@ -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));
}

View file

@ -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());

View file

@ -24,7 +24,7 @@ public class PlayerParser extends CompletingParser {
}
public Collection<String> getCompletions(CommandEnvironment env) {
return env.getServer().getAllUsernames();
return env.getServer().getAllPlayerNames();
}
public Class<?> getTypeClass(boolean required) {

View file

@ -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<String> getCompletions(CommandEnvironment env) {
return env.getServer().getAllUserNames();
}
public Class<?> getTypeClass(boolean required) {
return User.class;
}
}

View file

@ -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());
}
}

View file

@ -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());

View file

@ -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<String> complete(CommandEnvironment env) {
return Lists.newArrayList(env.getServer().getUsers());
return env.getServer().getPlayerFilenames();
}
});

View file

@ -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());
}
}
}

View file

@ -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());
}
}
}

View file

@ -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);
}
}

View file

@ -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<PublicKey, String> 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<PublicKey, String> 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);
}
}
}

View file

@ -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());
}
}

View file

@ -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<User> 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"));
}
}
}

View file

@ -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;

View file

@ -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() {

View file

@ -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<Short> transactions = new IntHashMap();
private final List<TagObject> 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<byte[], byte[]> 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<byte[], byte[]> hash) {
this.password = hash;
}
public Pair<byte[], byte[]> 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<byte[], byte[]>(tag.getByteArray("passwordHash"), tag.getByteArray("passwordSalt"));
if(tag.hasByteArray("pubkey"))
this.pubkey = EncryptUtil.decodePublicKey(tag.getByteArray("pubkey"));
this.selected = tag.getInt("selected");
List<TagObject> 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<TagObject> 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 <? super Void >> ()
{
public void operationComplete(Future <? super Void > 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;

View file

@ -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<byte[], byte[]> 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<byte[], byte[]> hash) {
this.password = hash;
}
public Pair<byte[], byte[]> 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<byte[], byte[]>(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<String, User> 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<String, User> 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();
}
}
}
}