add basic user saving
This commit is contained in:
parent
a9b113a436
commit
5fadc725d0
10 changed files with 203 additions and 17 deletions
|
@ -49,11 +49,17 @@ import com.google.common.base.Charsets;
|
|||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.common.io.Files;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.ListenableFutureTask;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import proxy.network.Direction;
|
||||
import proxy.network.Decoder;
|
||||
import proxy.network.Splitter;
|
||||
|
@ -85,6 +91,8 @@ public class Proxy {
|
|||
return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
|
||||
}
|
||||
};
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final JsonParser PARSER = new JsonParser();
|
||||
|
||||
private volatile boolean isAlive;
|
||||
private volatile boolean running = true;
|
||||
|
@ -153,7 +161,44 @@ public class Proxy {
|
|||
this.registerCommands();
|
||||
}
|
||||
|
||||
private static JsonObject loadFile(File file) {
|
||||
if(!file.exists())
|
||||
return new JsonObject();
|
||||
String data;
|
||||
try {
|
||||
data = Files.toString(file, Charsets.UTF_8);
|
||||
}
|
||||
catch(IOException e) {
|
||||
Log.error(e, "Could not load %s", file);
|
||||
return new JsonObject();
|
||||
}
|
||||
JsonElement elem;
|
||||
try {
|
||||
elem = PARSER.parse(data);
|
||||
}
|
||||
catch(JsonParseException e) {
|
||||
Log.error(e, "Could not parse %s", file);
|
||||
return new JsonObject();
|
||||
}
|
||||
if(!elem.isJsonObject()) {
|
||||
Log.error("Could not parse %s: root is not a JSON object", file);
|
||||
return new JsonObject();
|
||||
}
|
||||
return elem.getAsJsonObject();
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
JsonObject obj = loadFile(new File("vproxy.json"));
|
||||
// ...
|
||||
|
||||
obj = loadFile(new File("users.json"));
|
||||
if(obj.has("users") && obj.get("users").isJsonArray()) {
|
||||
for(User user : User.fromJson(obj.get("users").getAsJsonArray())) {
|
||||
this.users.put(user.getUsername().toLowerCase(Locale.US), user);
|
||||
}
|
||||
}
|
||||
// ...
|
||||
|
||||
this.status = new ServerInfo("VLoginProxy 1.8.9", 47);
|
||||
this.status.setCapacity(this.maxPlayers);
|
||||
Collections.addAll(this.status.getMotds(), Formatter.DARK_RED + "Miau\n" + Formatter.YELLOW + "Test??", Formatter.AQUA + "Server\n" + Formatter.GREEN + "Test!!");
|
||||
|
@ -166,6 +211,33 @@ public class Proxy {
|
|||
Collections.addAll(this.status.getList(), Formatter.DARK_GREEN + "TESTTTT", "Test 2!", Formatter.BLUE + "Test numbah 3!!!!!");
|
||||
}
|
||||
|
||||
private static void saveFile(JsonObject obj, File file) {
|
||||
try {
|
||||
Files.write(GSON.toJson(obj), file, Charsets.UTF_8);
|
||||
}
|
||||
catch(IOException e) {
|
||||
Log.error(e, "Could not save %s", file);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveData() {
|
||||
this.saveConfig();
|
||||
this.saveUsers();
|
||||
}
|
||||
|
||||
public void saveConfig() {
|
||||
JsonObject obj = new JsonObject();
|
||||
// ...
|
||||
saveFile(obj, new File("vproxy.json"));
|
||||
}
|
||||
|
||||
public void saveUsers() {
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.add("users", User.toJson(this.users.values()));
|
||||
// ...
|
||||
saveFile(obj, new File("users.json"));
|
||||
}
|
||||
|
||||
public void run() {
|
||||
this.loadConfig();
|
||||
Log.info("Hosting proxy on %s:%d", this.proxyHost.isEmpty() ? "0.0.0.0" : this.proxyHost, this.proxyPort);
|
||||
|
@ -178,7 +250,7 @@ public class Proxy {
|
|||
}
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
|
||||
public void run() {
|
||||
Proxy.this.terminateEndpoints();
|
||||
Proxy.this.endProxy();
|
||||
}
|
||||
}, "Proxy shutdown thread"));
|
||||
Thread con = new Thread(new Runnable() {
|
||||
|
@ -233,9 +305,14 @@ public class Proxy {
|
|||
catch(InterruptedException e) {
|
||||
}
|
||||
}
|
||||
this.terminateEndpoints();
|
||||
this.endProxy();
|
||||
Log.info("Proxy stopped");
|
||||
}
|
||||
|
||||
private void endProxy() {
|
||||
this.terminateEndpoints();
|
||||
this.saveData();
|
||||
}
|
||||
|
||||
public void addLanEndpoint(InetAddress address, int port) throws IOException {
|
||||
synchronized(this.endpoints) {
|
||||
|
@ -538,7 +615,7 @@ public class Proxy {
|
|||
Log.error("Command '%s' not found", args[0]);
|
||||
return false;
|
||||
}
|
||||
if(player != null && !player.isAdmin()) {
|
||||
if(player != null && (cmd.getPermission() == null || (cmd.getPermission().isEmpty() && !player.isAdmin()) || !player.hasPermission(cmd.getPermission()))) {
|
||||
player.sendMessage(Formatter.DARK_RED + "You are not allowed to execute this command");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ public abstract class Command {
|
|||
public int getRedactedLogArg(int args) {
|
||||
return -1;
|
||||
}
|
||||
public String getPermission() {
|
||||
return "vproxy.cmd." + this.getName();
|
||||
}
|
||||
|
||||
protected static void sendMessage(ProxyHandler player, String msg) {
|
||||
if(player != null)
|
||||
|
|
|
@ -19,6 +19,10 @@ public class CommandAdmin extends Command {
|
|||
public String getArgs() {
|
||||
return "[username]";
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void run(Proxy proxy, ProxyHandler player, String[] args) {
|
||||
if(args.length == 0) {
|
||||
|
|
|
@ -34,6 +34,6 @@ public class CommandDelete extends Command {
|
|||
}
|
||||
|
||||
public Iterable<String> complete(Proxy proxy, ProxyHandler player, String[] args) {
|
||||
return args.length == 1 ? Collections2.filter(proxy.getUserNames(), user -> !proxy.getUser(user).isAdmin()) : null;
|
||||
return args.length == 1 ? (player == null ? proxy.getUserNames() : Collections2.filter(proxy.getUserNames(), user -> !proxy.getUser(user).isAdmin())) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package proxy.command;
|
|||
|
||||
import proxy.Proxy;
|
||||
import proxy.handler.ProxyHandler;
|
||||
import proxy.util.Permissions;
|
||||
|
||||
public class CommandPing extends Command {
|
||||
public String getName() {
|
||||
|
@ -22,12 +23,12 @@ public class CommandPing extends Command {
|
|||
ProxyHandler plr = args.length < 1 ? player : proxy.getPlayer(args[0]);
|
||||
if(plr == null)
|
||||
throw new RunException("'%s' is not online", args[0]);
|
||||
if(player != null && plr != player && !player.isAdmin())
|
||||
if(player != null && plr != player && !player.hasPermission(Permissions.PING_OTHERS))
|
||||
throw new RunException("You are not allowed to query the latency of another player");
|
||||
sendInfo(player, (plr == player ? "Your latency" : "Latency of %s") + ": %d ms", plr == player ? plr.getPing() : plr.getUsername(), plr.getPing());
|
||||
}
|
||||
|
||||
public Iterable<String> complete(Proxy proxy, ProxyHandler player, String[] args) {
|
||||
return args.length == 1 && (player == null || player.isAdmin()) ? proxy.getPlayerNames() : null;
|
||||
return args.length == 1 && (player == null || player.hasPermission(Permissions.PING_OTHERS)) ? proxy.getPlayerNames() : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package proxy.command;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
|
||||
import proxy.Proxy;
|
||||
import proxy.handler.ProxyHandler;
|
||||
import proxy.util.Formatter;
|
||||
|
@ -17,10 +19,12 @@ public class CommandRevoke extends Command {
|
|||
public String getArgs() {
|
||||
return "<username>";
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void run(Proxy proxy, ProxyHandler player, String[] args) {
|
||||
if(player != null)
|
||||
throw new RunException("Only the console can revoke admin status");
|
||||
if(args.length < 1)
|
||||
throw new RunException("Please provide a username");
|
||||
User user = proxy.getUser(args[0]);
|
||||
|
@ -33,4 +37,8 @@ public class CommandRevoke extends Command {
|
|||
((ProxyHandler)user).sendMessage(Formatter.RED + "Your admin privileges were revoked");
|
||||
sendInfo(player, "%s is no longer an admin", user.getUsername());
|
||||
}
|
||||
|
||||
public Iterable<String> complete(Proxy proxy, ProxyHandler player, String[] args) {
|
||||
return args.length == 1 ? Collections2.filter(proxy.getUserNames(), user -> proxy.getUser(user).isAdmin()) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import proxy.packet.S38PacketPlayerListItem.Action;
|
|||
import proxy.util.Formatter;
|
||||
import proxy.util.User;
|
||||
import proxy.util.Log;
|
||||
import proxy.util.Permissions;
|
||||
|
||||
public class ProxyHandler extends User implements Handler {
|
||||
public class ProxyLoginHandler implements Handler {
|
||||
|
@ -351,7 +352,7 @@ public class ProxyHandler extends User implements Handler {
|
|||
this.disconnect("You are already logged in");
|
||||
Log.info("%s was already logged in from another client", this.username);
|
||||
}
|
||||
else if(!stored.isAdmin() && this.proxy.getMaximumPlayers() > 0 && this.proxy.getOnlinePlayers() >= this.proxy.getMaximumPlayers()) {
|
||||
else if(!stored.hasPermission(Permissions.BYPASS_PLAYER_LIMIT) && this.proxy.getMaximumPlayers() > 0 && this.proxy.getOnlinePlayers() >= this.proxy.getMaximumPlayers()) {
|
||||
this.disconnect("The server is full (" + this.proxy.getOnlinePlayers() + " / " + this.proxy.getMaximumPlayers() + " players), please try again later");
|
||||
}
|
||||
else {
|
||||
|
@ -388,7 +389,7 @@ public class ProxyHandler extends User implements Handler {
|
|||
if(cmd == null)
|
||||
return null;
|
||||
List<String> list = Lists.<String>newArrayList();
|
||||
if(!this.isAdmin())
|
||||
if(cmd.getPermission() == null || (cmd.getPermission().isEmpty() && !this.isAdmin()) || !this.hasPermission(cmd.getPermission()))
|
||||
return list;
|
||||
String[] argv = new String[args.length - 1];
|
||||
System.arraycopy(args, 1, argv, 0, argv.length);
|
||||
|
@ -453,12 +454,10 @@ public class ProxyHandler extends User implements Handler {
|
|||
if(cmd.isEmpty() || cmd.indexOf(':') != -1 || this.proxy.getCommands().containsKey(cmd.substring(1).toLowerCase(Locale.US)))
|
||||
iter.remove();
|
||||
}
|
||||
if(this.admin) {
|
||||
for(String cmd : this.proxy.getCommands().keySet()) {
|
||||
String command = "/" + cmd;
|
||||
if(this.admin && command.regionMatches(true, 0, this.completion, 0, this.completion.length()))
|
||||
list.add(command);
|
||||
}
|
||||
for(Command cmd : this.proxy.getCommands().values()) {
|
||||
String command = "/" + cmd.getName();
|
||||
if(cmd.getPermission() != null && (!cmd.getPermission().isEmpty() || this.isAdmin()) && command.regionMatches(true, 0, this.completion, 0, this.completion.length()))
|
||||
list.add(command);
|
||||
}
|
||||
Collections.sort(list);
|
||||
}
|
||||
|
|
|
@ -101,6 +101,29 @@ public enum Formatter {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static String getHexString(byte[] bytes) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int z = 0; z < bytes.length; z++) {
|
||||
sb.append(String.format("%02x", bytes[z]));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] fromHexString(String str) {
|
||||
if((str.length() & 1) == 1)
|
||||
str = "0" + str;
|
||||
byte[] bytes = new byte[str.length() / 2];
|
||||
try {
|
||||
for(int z = 0; z < bytes.length; z++) {
|
||||
bytes[z] = (byte)Integer.parseUnsignedInt(str.substring(z * 2, (z + 1) * 2), 16);
|
||||
}
|
||||
}
|
||||
catch(NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private Formatter(char code) {
|
||||
this.value = "\u00a7" + code;
|
||||
}
|
||||
|
|
7
proxy/src/main/java/proxy/util/Permissions.java
Normal file
7
proxy/src/main/java/proxy/util/Permissions.java
Normal file
|
@ -0,0 +1,7 @@
|
|||
package proxy.util;
|
||||
|
||||
public class Permissions {
|
||||
public static final String BYPASS_PLAYER_LIMIT = "vproxy.bypass.playerlimit";
|
||||
|
||||
public static final String PING_OTHERS = "vproxy.cmd.ping.others";
|
||||
}
|
|
@ -4,6 +4,11 @@ import java.nio.charset.Charset;
|
|||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Collection;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class User {
|
||||
private static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
|
@ -37,10 +42,19 @@ public class User {
|
|||
return this.admin;
|
||||
}
|
||||
|
||||
public boolean hasPermission(String perm) {
|
||||
return this.isAdmin();
|
||||
}
|
||||
|
||||
public void setAdmin(boolean admin) {
|
||||
this.admin = admin;
|
||||
}
|
||||
|
||||
public void setSkinTexture(String data, String signature) {
|
||||
this.skinTexture = data;
|
||||
this.skinSignature = signature;
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return false;
|
||||
}
|
||||
|
@ -49,6 +63,56 @@ public class User {
|
|||
this.hash = user.hash;
|
||||
this.salt = user.salt;
|
||||
this.admin = user.admin;
|
||||
this.skinTexture = user.skinTexture;
|
||||
this.skinSignature = user.skinSignature;
|
||||
}
|
||||
|
||||
private JsonObject toJson() {
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("username", this.username);
|
||||
obj.addProperty("passwordHash", Formatter.getHexString(this.hash));
|
||||
obj.addProperty("passwordSalt", Formatter.getHexString(this.salt));
|
||||
obj.addProperty("admin", this.admin);
|
||||
obj.addProperty("skinTexture", this.skinTexture);
|
||||
obj.addProperty("skinSignature", this.skinSignature);
|
||||
return obj;
|
||||
}
|
||||
|
||||
private boolean fromJson(JsonObject obj) {
|
||||
if(!Formatter.isValidUser(this.username))
|
||||
return false;
|
||||
this.hash = obj.has("passwordHash") ? Formatter.fromHexString(obj.get("passwordHash").getAsString()) : null;
|
||||
this.salt = obj.has("passwordSalt") ? Formatter.fromHexString(obj.get("passwordSalt").getAsString()) : null;
|
||||
this.admin = obj.has("admin") ? obj.get("admin").getAsBoolean() : false;
|
||||
this.skinTexture = obj.has("skinTexture") ? obj.get("skinTexture").getAsString() : null;
|
||||
this.skinSignature = obj.has("skinSignature") ? obj.get("skinSignature").getAsString() : null;
|
||||
return this.hash != null && this.salt != null;
|
||||
}
|
||||
|
||||
public static JsonArray toJson(Collection<User> users) {
|
||||
JsonArray arr = new JsonArray();
|
||||
for(User user : users) {
|
||||
arr.add(user.toJson());
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static Collection<User> fromJson(JsonArray arr) {
|
||||
Collection<User> users = Lists.newArrayList();
|
||||
for(JsonElement elem : arr) {
|
||||
if(!elem.isJsonObject())
|
||||
continue;
|
||||
JsonObject obj = elem.getAsJsonObject();
|
||||
if(!obj.has("username"))
|
||||
continue;
|
||||
JsonElement name = obj.get("username");
|
||||
if(!name.isJsonPrimitive())
|
||||
continue;
|
||||
User user = new User(name.getAsString());
|
||||
if(user.fromJson(obj))
|
||||
users.add(user);
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
private static byte[] hashPassword(String pass, byte[] salt) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue