diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java index 3c5fcaa4..281e5d46 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/LaunchServer.java @@ -40,31 +40,67 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; +/** + * The main LaunchServer class. Contains links to all necessary objects + * Not a singletron + */ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurable { public static final Class defaultLauncherEXEBinaryClass = null; + /** + * Working folder path + */ public final Path dir; + /** + * Environment type (test / production) + */ public final LaunchServerEnv env; + /** + * The path to the folder with libraries for the launcher + */ public final Path launcherLibraries; + /** + * The path to the folder with compile-only libraries for the launcher + */ public final Path launcherLibrariesCompile; - public final Path caCertFile; // Constant paths - public final Path caKeyFile; - public final Path serverCertFile; - public final Path serverKeyFile; + /** + * The path to the folder with updates/webroot + */ public final Path updatesDir; + /** + * Save/Reload LaunchServer config + */ public final LaunchServerConfigManager launchServerConfigManager; + /** + * The path to the folder with profiles + */ public final Path profilesDir; + /** + * This object contains runtime configuration + */ public final LaunchServerRuntimeConfig runtime; + /** + * Public ECDSA LaunchServer key + */ public final ECPublicKey publicKey; + /** + * Private ECDSA LaunchServer key + */ public final ECPrivateKey privateKey; + /** + * Pipeline for building JAR + */ public final JARLauncherBinary launcherBinary; + /** + * Pipeline for building EXE + */ + public final LauncherBinary launcherEXEBinary; //public static LaunchServer server = null; public final Class launcherEXEBinaryClass; // Server config - public final LauncherBinary launcherEXEBinary; public final SessionManager sessionManager; public final AuthHookManager authHookManager; public final LaunchServerModulesManager modulesManager; @@ -106,12 +142,6 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La config.setLaunchServer(this); - caCertFile = dir.resolve("ca.crt"); - caKeyFile = dir.resolve("ca.key"); - - serverCertFile = dir.resolve("server.crt"); - serverKeyFile = dir.resolve("server.key"); - modulesManager.invokeEvent(new NewLaunchServerInstanceEvent(this)); // Print keypair fingerprints diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/asm/InjectClassAcceptor.java b/LaunchServer/src/main/java/pro/gravit/launchserver/asm/InjectClassAcceptor.java index 3e7a5db0..8bad18d9 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/asm/InjectClassAcceptor.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/asm/InjectClassAcceptor.java @@ -72,73 +72,79 @@ private static void visit(ClassNode classNode, Map values) { classNode.fields.forEach(field -> { // Notice that fields that will be used with this algo should not have default // value by = ...; - AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream() - .filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst() - .orElse(null) : null; - if (valueAnnotation == null) { - return; - } - field.invisibleAnnotations.remove(valueAnnotation); - AtomicReference valueName = new AtomicReference<>(null); - valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) { - @Override - public void visit(final String name, final Object value) { - if ("value".equals(name)) { - if (value.getClass() != String.class) - throw new IllegalArgumentException( - String.format("Invalid annotation with value class %s", field.getClass().getName())); - valueName.set(value.toString()); - } - } - }); - if (valueName.get() == null) { - throw new IllegalArgumentException("Annotation should always contains 'value' key"); - } - if (!values.containsKey(valueName.get())) { - return; - } - Object value = values.get(valueName.get()); - if ((field.access & Opcodes.ACC_STATIC) != 0) { - if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) { - field.value = value; - return; - } - List putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray()) - .filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p) - .filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList()); - InsnList setter = serializeValue(value); - if (putStaticNodes.isEmpty()) { - setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc)); - Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN) - .forEach(node -> clinitMethod.instructions.insertBefore(node, setter)); - } else { - setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2)); - for (FieldInsnNode fieldInsnNode : putStaticNodes) { - clinitMethod.instructions.insertBefore(fieldInsnNode, setter); - } - } - } else { - if (initMethod == null) { - throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name)); - } - List putFieldNodes = Arrays.stream(initMethod.instructions.toArray()) - .filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p) - .filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList()); - InsnList setter = serializeValue(value); - if (putFieldNodes.isEmpty()) { - setter.insert(new VarInsnNode(Opcodes.ALOAD, 0)); - setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc)); - Arrays.stream(initMethod.instructions.toArray()) - .filter(node -> node.getOpcode() == Opcodes.RETURN) - .forEach(node -> initMethod.instructions.insertBefore(node, setter)); - } else { - setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2)); - for (FieldInsnNode fieldInsnNode : putFieldNodes) { - initMethod.instructions.insertBefore(fieldInsnNode, setter); - } + boolean isStatic = (field.access & Opcodes.ACC_STATIC) != 0; + injectTo(isStatic ? clinitMethod : initMethod, classNode, field, isStatic, values); + }); + } + + public static void injectTo(MethodNode initMethod, ClassNode classNode, FieldNode field, boolean isStatic, Map values) { + AnnotationNode valueAnnotation = field.invisibleAnnotations != null ? field.invisibleAnnotations.stream() + .filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst() + .orElse(null) : null; + if (valueAnnotation == null) { + return; + } + field.invisibleAnnotations.remove(valueAnnotation); + AtomicReference valueName = new AtomicReference<>(null); + valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) { + @Override + public void visit(final String name, final Object value) { + if ("value".equals(name)) { + if (value.getClass() != String.class) + throw new IllegalArgumentException( + String.format("Invalid annotation with value class %s", field.getClass().getName())); + valueName.set(value.toString()); } } }); + if (valueName.get() == null) { + throw new IllegalArgumentException("Annotation should always contains 'value' key"); + } + if (!values.containsKey(valueName.get())) { + return; + } + Object value = values.get(valueName.get()); + //if ((field.access & Opcodes.ACC_STATIC) != 0) { + if (isStatic) { + if (primitiveLDCDescriptors.contains(field.desc) && primitiveLDCClasses.contains(value.getClass())) { + field.value = value; + return; + } + List putStaticNodes = Arrays.stream(initMethod.instructions.toArray()) + .filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p) + .filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList()); + InsnList setter = serializeValue(value); + if (putStaticNodes.isEmpty()) { + setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc)); + Arrays.stream(initMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN) + .forEach(node -> initMethod.instructions.insertBefore(node, setter)); + } else { + setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2)); + for (FieldInsnNode fieldInsnNode : putStaticNodes) { + initMethod.instructions.insertBefore(fieldInsnNode, setter); + } + } + } else { + if (initMethod == null) { + throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name)); + } + List putFieldNodes = Arrays.stream(initMethod.instructions.toArray()) + .filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p) + .filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList()); + InsnList setter = serializeValue(value); + if (putFieldNodes.isEmpty()) { + setter.insert(new VarInsnNode(Opcodes.ALOAD, 0)); + setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc)); + Arrays.stream(initMethod.instructions.toArray()) + .filter(node -> node.getOpcode() == Opcodes.RETURN) + .forEach(node -> initMethod.instructions.insertBefore(node, setter)); + } else { + setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2)); + for (FieldInsnNode fieldInsnNode : putFieldNodes) { + initMethod.instructions.insertBefore(fieldInsnNode, setter); + } + } + } } private static Serializer serializerClass(int opcode) { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/dao/provider/HibernateDaoProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/dao/provider/HibernateDaoProvider.java index d7ce76e3..4710da36 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/dao/provider/HibernateDaoProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/dao/provider/HibernateDaoProvider.java @@ -16,6 +16,10 @@ import java.util.HashMap; import java.util.Map; +/** + * Deprecated from 5.2.0 + */ +@Deprecated public abstract class HibernateDaoProvider extends DaoProvider implements Reconfigurable, AutoCloseable { public String driver; public String url; diff --git a/LaunchServer/src/test/java/pro/gravit/launchserver/ConfigurationTest.java b/LaunchServer/src/test/java/pro/gravit/launchserver/ConfigurationTest.java new file mode 100644 index 00000000..a594adef --- /dev/null +++ b/LaunchServer/src/test/java/pro/gravit/launchserver/ConfigurationTest.java @@ -0,0 +1,87 @@ +package pro.gravit.launchserver; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launcher.Launcher; +import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launcher.request.auth.password.AuthPlainPassword; +import pro.gravit.launchserver.auth.AuthException; +import pro.gravit.launchserver.auth.AuthProviderPair; +import pro.gravit.launchserver.auth.handler.MemoryAuthHandler; +import pro.gravit.launchserver.auth.provider.AuthProvider; +import pro.gravit.launchserver.auth.provider.AuthProviderResult; +import pro.gravit.launchserver.auth.texture.NullTextureProvider; +import pro.gravit.launchserver.config.LaunchServerConfig; +import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; +import pro.gravit.launchserver.impl.TestLaunchServerConfigManager; +import pro.gravit.launchserver.manangers.CertificateManager; +import pro.gravit.launchserver.manangers.LaunchServerGsonManager; +import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; +import pro.gravit.utils.command.StdCommandHandler; +import pro.gravit.utils.helper.SecurityHelper; + +import java.io.IOException; +import java.nio.file.Path; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; + +public class ConfigurationTest { + @TempDir + public static Path modulesDir; + @TempDir + public static Path configDir; + @TempDir + public static Path dir; + public static LaunchServer launchServer; + public static TestLaunchServerConfigManager launchServerConfigManager; + + @BeforeAll + public static void prepare() throws Throwable { + LaunchServerModulesManager modulesManager = new LaunchServerModulesManager(modulesDir, configDir, null); + LaunchServerConfig config = LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST); + Launcher.gsonManager = new LaunchServerGsonManager(modulesManager); + Launcher.gsonManager.initGson(); + LaunchServerRuntimeConfig runtimeConfig = new LaunchServerRuntimeConfig(); + LaunchServerBuilder builder = new LaunchServerBuilder(); + KeyPair pair = SecurityHelper.genECKeyPair(new SecureRandom()); + ECPublicKey publicKey = (ECPublicKey) pair.getPublic(); + ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate(); + launchServerConfigManager = new TestLaunchServerConfigManager(); + builder.setDir(dir) + .setEnv(LaunchServer.LaunchServerEnv.TEST) + .setConfig(config) + .setRuntimeConfig(runtimeConfig) + .setPublicKey(publicKey) + .setPrivateKey(privateKey) + .setCertificateManager(new CertificateManager()) + .setLaunchServerConfigManager(launchServerConfigManager) + .setModulesManager(modulesManager) + .setCommandHandler(new StdCommandHandler(false)); + launchServer = builder.build(); + } + @Test + public static void reloadTest() throws Exception { + AuthProvider provider = new AuthProvider() { + @Override + public AuthProviderResult auth(String login, AuthRequest.AuthPasswordInterface password, String ip) throws Exception { + if(!(password instanceof AuthPlainPassword)) throw new UnsupportedOperationException(); + if(login.equals("test") && ((AuthPlainPassword) password).password.equals("test")) { + return new AuthProviderResult(login, SecurityHelper.randomStringToken(), new ClientPermissions()); + } + throw new AuthException("Incorrect password"); + } + + @Override + public void close() throws IOException { + + } + }; + AuthProviderPair pair = new AuthProviderPair(provider, new MemoryAuthHandler(), new NullTextureProvider()); + launchServerConfigManager.config.auth.put("std", pair); + launchServer.reload(LaunchServer.ReloadType.FULL); + } +} diff --git a/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java b/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java index bdebfc90..a2882655 100644 --- a/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java +++ b/LaunchServer/src/test/java/pro/gravit/launchserver/StartLaunchServerTest.java @@ -7,6 +7,7 @@ import pro.gravit.launcher.Launcher; import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; +import pro.gravit.launchserver.impl.TestLaunchServerConfigManager; import pro.gravit.launchserver.manangers.CertificateManager; import pro.gravit.launchserver.manangers.LaunchServerGsonManager; import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; @@ -46,29 +47,7 @@ public static void prepare() throws Throwable { .setPublicKey(publicKey) .setPrivateKey(privateKey) .setCertificateManager(new CertificateManager()) - .setLaunchServerConfigManager(new LaunchServer.LaunchServerConfigManager() { - @Override - public LaunchServerConfig readConfig() { - return LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST); - } - - @Override - public LaunchServerRuntimeConfig readRuntimeConfig() { - LaunchServerRuntimeConfig r = new LaunchServerRuntimeConfig(); - r.reset(); - return r; - } - - @Override - public void writeConfig(LaunchServerConfig config) { - - } - - @Override - public void writeRuntimeConfig(LaunchServerRuntimeConfig config) { - - } - }) + .setLaunchServerConfigManager(new TestLaunchServerConfigManager()) .setModulesManager(modulesManager) .setCommandHandler(new StdCommandHandler(false)); launchServer = builder.build(); diff --git a/LaunchServer/src/test/java/pro/gravit/launchserver/impl/TestLaunchServerConfigManager.java b/LaunchServer/src/test/java/pro/gravit/launchserver/impl/TestLaunchServerConfigManager.java new file mode 100644 index 00000000..56dc4e27 --- /dev/null +++ b/LaunchServer/src/test/java/pro/gravit/launchserver/impl/TestLaunchServerConfigManager.java @@ -0,0 +1,37 @@ +package pro.gravit.launchserver.impl; + +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.config.LaunchServerConfig; +import pro.gravit.launchserver.config.LaunchServerRuntimeConfig; + +import java.io.IOException; + +public class TestLaunchServerConfigManager implements LaunchServer.LaunchServerConfigManager { + public LaunchServerConfig config; + public LaunchServerRuntimeConfig runtimeConfig; + @Override + public LaunchServerConfig readConfig() throws IOException { + return config; + } + + @Override + public LaunchServerRuntimeConfig readRuntimeConfig() throws IOException { + return runtimeConfig; + } + + @Override + public void writeConfig(LaunchServerConfig config) throws IOException { + + } + + @Override + public void writeRuntimeConfig(LaunchServerRuntimeConfig config) throws IOException { + + } + + public TestLaunchServerConfigManager() { + config = LaunchServerConfig.getDefault(LaunchServer.LaunchServerEnv.TEST); + runtimeConfig = new LaunchServerRuntimeConfig(); + runtimeConfig.reset(); + } +}