[FEATURE] DialogService, AdditionalDataRequest

This commit is contained in:
Gravita 2021-07-04 20:01:24 +07:00
parent 7ca3889bb5
commit 602a4b17f8
19 changed files with 320 additions and 98 deletions

View file

@ -1,4 +1,5 @@
package pro.gravit.launchserver.auth;
@Deprecated
public interface RequiredDAO {
}

View file

@ -0,0 +1,25 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
import java.util.Map;
public interface UserSupportAdditionalData {
String getProperty(String name);
default String getPropertyUnprivileged(String name) {
return getProperty(name);
}
default String getPropertyUnprivilegedSelf(String name) {
return getProperty(name);
}
Map<String, String> getPropertiesMap();
default Map<String, String> getPropertiesMapUnprivileged() {
return getPropertiesMap();
}
default Map<String, String> getPropertiesMapUnprivilegedSelf() {
return getPropertiesMapUnprivileged();
}
}

View file

@ -1,7 +0,0 @@
package pro.gravit.launchserver.auth.core.interfaces.user;
public interface UserSupportMoney {
long getMoney();
long getDonateMoney();
}

View file

@ -167,7 +167,7 @@ public String createPublicKeyToken(String username, byte[] publicKey) {
.setIssuer("LaunchServer")
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 8))
.claim("publicKey", Base64.getEncoder().encode(publicKey))
.claim("publicKey", Base64.getEncoder().encodeToString(publicKey))
.signWith(server.keyAgreementManager.ecdsaPrivateKey)
.compact();
}

View file

@ -1,87 +0,0 @@
package pro.gravit.launchserver.command.basic;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.cert.X509CertificateHolder;
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
import pro.gravit.launchserver.LaunchServer;
import pro.gravit.launchserver.auth.AuthException;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.command.Command;
import pro.gravit.launchserver.socket.handlers.NettyServerSocketHandler;
import pro.gravit.utils.helper.CommonHelper;
import java.nio.file.Paths;
import java.security.KeyPair;
public class TestCommand extends Command {
private transient final Logger logger = LogManager.getLogger();
private NettyServerSocketHandler handler = null;
public TestCommand(LaunchServer server) {
super(server);
}
@Override
public String getArgsDescription() {
return null;
}
@Override
public String getUsageDescription() {
return "Test command. Only developer!";
}
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
if (handler == null)
handler = new NettyServerSocketHandler(server);
if (args[0].equals("start")) {
CommonHelper.newThread("Netty Server", true, handler).start();
}
if (args[0].equals("stop")) {
handler.close();
}
if (args[0].equals("genCA")) {
server.certificateManager.generateCA();
server.certificateManager.writePrivateKey(Paths.get("ca.key"), server.certificateManager.caKey);
server.certificateManager.writeCertificate(Paths.get("ca.crt"), server.certificateManager.ca);
}
if (args[0].equals("readCA")) {
server.certificateManager.ca = server.certificateManager.readCertificate(Paths.get("ca.crt"));
server.certificateManager.caKey = server.certificateManager.readPrivateKey(Paths.get("ca.key"));
}
if (args[0].equals("genCert")) {
verifyArgs(args, 2);
String name = args[1];
KeyPair pair = server.certificateManager.generateKeyPair();
X509CertificateHolder cert = server.certificateManager.generateCertificate(name, pair.getPublic());
server.certificateManager.writePrivateKey(Paths.get(name.concat(".key")), pair.getPrivate());
server.certificateManager.writeCertificate(Paths.get(name.concat(".crt")), cert);
}
if (args[0].equals("authstresser")) {
AuthProviderPair pair = server.config.getAuthProviderPair();
AuthPlainPassword plainPassword = new AuthPlainPassword("test");
Runnable runnable = () -> {
long startTime = System.currentTimeMillis();
for (int i = 0; i < 100000; ++i) {
try {
pair.provider.auth("Test", plainPassword, "127.0.0.1");
} catch (AuthException ignored) {
} catch (Exception e) {
throw new RuntimeException(e);
}
if ((i % 10000) == 0) {
logger.info("Completed {} requests", i);
}
}
logger.info("Completed all requests. Time {} ms", System.currentTimeMillis() - startTime);
};
for (int i = 0; i < 7; ++i) {
CommonHelper.newThread(String.format("Stresser #%d", i), true, runnable).start();
}
}
}
}

View file

@ -4,7 +4,10 @@
import pro.gravit.launchserver.command.auth.AuthCommand;
import pro.gravit.launchserver.command.auth.UUIDToUsernameCommand;
import pro.gravit.launchserver.command.auth.UsernameToUUIDCommand;
import pro.gravit.launchserver.command.basic.*;
import pro.gravit.launchserver.command.basic.BuildCommand;
import pro.gravit.launchserver.command.basic.RestartCommand;
import pro.gravit.launchserver.command.basic.StopCommand;
import pro.gravit.launchserver.command.basic.VersionCommand;
import pro.gravit.launchserver.command.hash.*;
import pro.gravit.launchserver.command.modules.LoadModuleCommand;
import pro.gravit.launchserver.command.modules.ModulesCommand;
@ -29,7 +32,6 @@ public static void registerCommands(pro.gravit.utils.command.CommandHandler hand
basic.registerCommand("gc", new GCCommand());
basic.registerCommand("loadModule", new LoadModuleCommand(server));
basic.registerCommand("modules", new ModulesCommand(server));
basic.registerCommand("test", new TestCommand(server));
Category basicCategory = new Category(basic, "basic", "Base LaunchServer commands");
handler.registerCategory(basicCategory);

View file

@ -47,16 +47,24 @@
import java.util.List;
public class CertificateManager {
@Deprecated
public final int validDays = 60;
@Deprecated
public final int minusHours = 6;
private transient final Logger logger = LogManager.getLogger();
@Deprecated
public X509CertificateHolder ca;
@Deprecated
public AsymmetricKeyParameter caKey;
@Deprecated
public X509CertificateHolder server;
@Deprecated
public AsymmetricKeyParameter serverKey;
public LauncherTrustManager trustManager;
@Deprecated
public String orgName;
@Deprecated
public X509CertificateHolder generateCertificate(String subjectName, PublicKey subjectPublicKey) throws OperatorCreationException {
SubjectPublicKeyInfo subjectPubKeyInfo = SubjectPublicKeyInfo.getInstance(subjectPublicKey.getEncoded());
BigInteger serial = BigInteger.valueOf(SecurityHelper.newRandom().nextLong());
@ -76,6 +84,7 @@ public X509CertificateHolder generateCertificate(String subjectName, PublicKey s
return v3CertGen.build(sigGen);
}
@Deprecated
public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorCreationException, InvalidAlgorithmParameterException {
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("secp384k1");
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
@ -100,6 +109,7 @@ public void generateCA() throws NoSuchAlgorithmException, IOException, OperatorC
caKey = PrivateKeyFactory.createKey(pair.getPrivate().getEncoded());
}
@Deprecated
public KeyPair generateKeyPair() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException {
ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("secp384k1");
KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");

View file

@ -93,6 +93,7 @@ public static void registerResponses() {
providers.register("features", FeaturesResponse.class);
providers.register("refreshToken", RefreshTokenResponse.class);
providers.register("restore", RestoreResponse.class);
providers.register("additionalData", AdditionalDataResponse.class);
}
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {

View file

@ -0,0 +1,66 @@
package pro.gravit.launchserver.socket.response.auth;
import io.netty.channel.ChannelHandlerContext;
import pro.gravit.launcher.ClientPermissions;
import pro.gravit.launcher.events.request.AdditionalDataRequestEvent;
import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.User;
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportAdditionalData;
import pro.gravit.launchserver.socket.Client;
import pro.gravit.launchserver.socket.response.SimpleResponse;
import java.util.Map;
import java.util.UUID;
public class AdditionalDataResponse extends SimpleResponse {
public String username;
public UUID uuid;
@Override
public String getType() {
return "additionalData";
}
@Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if (!client.isAuth || !client.auth.isUseCore()) {
sendError("Access denied");
return;
}
AuthProviderPair pair = client.auth;
if (username == null && uuid == null) {
Map<String, String> properties;
User user = client.getUser();
if (user instanceof UserSupportAdditionalData) {
UserSupportAdditionalData userSupport = (UserSupportAdditionalData) user;
if (user.getPermissions().isPermission(ClientPermissions.PermissionConsts.ADMIN)) {
properties = userSupport.getPropertiesMap();
} else {
properties = userSupport.getPropertiesMapUnprivilegedSelf();
}
} else {
properties = Map.of();
}
sendResult(new AdditionalDataRequestEvent(properties));
return;
}
User user;
if (username != null) {
user = pair.core.getUserByUsername(username);
} else {
user = pair.core.getUserByUUID(uuid);
}
if (!(user instanceof UserSupportAdditionalData)) {
sendResult(new AdditionalDataRequestEvent(Map.of()));
return;
}
UserSupportAdditionalData userSupport = (UserSupportAdditionalData) user;
Map<String, String> properties;
if (client.permissions.isPermission(ClientPermissions.PermissionConsts.ADMIN)) {
properties = userSupport.getPropertiesMap();
} else {
properties = userSupport.getPropertiesMapUnprivileged();
}
sendResult(new AdditionalDataRequestEvent(properties));
}
}

View file

@ -1,6 +1,8 @@
package pro.gravit.launcher;
import pro.gravit.launcher.api.DialogService;
import pro.gravit.launcher.events.ExtendedTokenRequestEvent;
import pro.gravit.launcher.events.NotificationEvent;
import pro.gravit.launcher.events.request.SecurityReportRequestEvent;
import pro.gravit.launcher.request.Request;
import pro.gravit.launcher.request.WebSocketEvent;
@ -30,6 +32,11 @@ else if (event instanceof ExtendedTokenRequestEvent) {
if (token != null) {
Request.addExtendedToken(event1.getExtendedTokenName(), token);
}
} else if (event instanceof NotificationEvent) {
NotificationEvent n = (NotificationEvent) event;
if (DialogService.isNotificationsAvailable()) {
DialogService.createNotification(n.icon, n.head, n.message);
}
}
return false;
}

View file

@ -4,6 +4,7 @@
import java.util.concurrent.atomic.AtomicReference;
@Deprecated
public abstract class JSApplication extends Application {
private static final AtomicReference<JSApplication> INSTANCE = new AtomicReference<>();

View file

@ -4,6 +4,8 @@
import pro.gravit.launcher.client.events.ClientEngineInitPhase;
import pro.gravit.launcher.client.events.ClientExitPhase;
import pro.gravit.launcher.client.events.ClientPreGuiPhase;
import pro.gravit.launcher.console.GetPublicKeyCommand;
import pro.gravit.launcher.console.SignDataCommand;
import pro.gravit.launcher.guard.LauncherGuardInterface;
import pro.gravit.launcher.guard.LauncherGuardManager;
import pro.gravit.launcher.guard.LauncherNoGuard;
@ -131,6 +133,14 @@ public static void verifyNoAgent() {
throw new SecurityException("JavaAgent found");
}
public ECPublicKey getClientPublicKey() {
return publicKey;
}
public byte[] sign(byte[] bytes) {
return SecurityHelper.sign(bytes, privateKey);
}
public static LauncherGuardInterface tryGetStdGuard() {
switch (Launcher.getConfig().guardType) {
case "no":
@ -200,10 +210,16 @@ public void start(String... args) throws Throwable {
if (started.getAndSet(true))
throw new IllegalStateException("Launcher has been already started");
readKeys();
registerCommands();
LauncherEngine.modulesManager.invokeEvent(new ClientEngineInitPhase(this));
runtimeProvider.preLoad();
LauncherGuardManager.initGuard(clientInstance);
LogHelper.debug("Dir: %s", DirBridge.dir);
runtimeProvider.run(args);
}
private void registerCommands() {
ConsoleManager.handler.registerCommand("getpublickey", new GetPublicKeyCommand(this));
ConsoleManager.handler.registerCommand("signdata", new SignDataCommand(this));
}
}

View file

@ -0,0 +1,77 @@
package pro.gravit.launcher.api;
import pro.gravit.launcher.events.NotificationEvent;
import java.util.function.Consumer;
public class DialogService {
private static DialogServiceImplementation dialogImpl;
private static DialogServiceNotificationImplementation notificationImpl;
private DialogService() {
throw new UnsupportedOperationException();
}
public interface DialogServiceImplementation {
void showDialog(String header, String text, Runnable onApplyCallback, Runnable onCloseCallback);
void showApplyDialog(String header, String text, Runnable onApplyCallback, Runnable onDenyCallback);
void showApplyDialog(String header, String text, Runnable onApplyCallback, Runnable onDenyCallback, Runnable onCloseCallback);
void showTextDialog(String header, Consumer<String> onApplyCallback, Runnable onCloseCallback);
}
public interface DialogServiceNotificationImplementation {
void createNotification(NotificationEvent.NotificationType type, String head, String message);
}
public static void setDialogImpl(DialogServiceImplementation impl) {
DialogService.dialogImpl = impl;
}
public static void setNotificationImpl(DialogServiceNotificationImplementation impl) {
DialogService.notificationImpl = impl;
}
public static boolean isDialogsAvailable() {
return dialogImpl != null;
}
public static boolean isNotificationsAvailable() {
return notificationImpl != null;
}
private static void checkIfAvailable() {
if (!isDialogsAvailable()) {
throw new UnsupportedOperationException("DialogService dialogs implementation not available");
}
}
public static void createNotification(NotificationEvent.NotificationType type, String head, String message) {
if (isNotificationsAvailable()) {
throw new UnsupportedOperationException("DialogService notifications implementation not available");
}
notificationImpl.createNotification(type, head, message);
}
public static void showDialog(String header, String text, Runnable onApplyCallback, Runnable onCloseCallback) {
checkIfAvailable();
dialogImpl.showDialog(header, text, onApplyCallback, onCloseCallback);
}
public static void showApplyDialog(String header, String text, Runnable onApplyCallback, Runnable onDenyCallback) {
checkIfAvailable();
dialogImpl.showApplyDialog(header, text, onApplyCallback, onDenyCallback);
}
public static void showApplyDialog(String header, String text, Runnable onApplyCallback, Runnable onDenyCallback, Runnable onCloseCallback) {
checkIfAvailable();
dialogImpl.showApplyDialog(header, text, onApplyCallback, onDenyCallback, onCloseCallback);
}
public static void showTextDialog(String header, Consumer<String> onApplyCallback, Runnable onCloseCallback) {
checkIfAvailable();
dialogImpl.showTextDialog(header, onApplyCallback, onCloseCallback);
}
}

View file

@ -0,0 +1,30 @@
package pro.gravit.launcher.console;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.helper.LogHelper;
import java.util.Base64;
public class GetPublicKeyCommand extends Command {
private final LauncherEngine engine;
public GetPublicKeyCommand(LauncherEngine engine) {
this.engine = engine;
}
@Override
public String getArgsDescription() {
return "[]";
}
@Override
public String getUsageDescription() {
return "print public key in base64 format";
}
@Override
public void invoke(String... args) throws Exception {
LogHelper.info("PublicKey: %s", Base64.getEncoder().encodeToString(engine.getClientPublicKey().getEncoded()));
}
}

View file

@ -0,0 +1,34 @@
package pro.gravit.launcher.console;
import pro.gravit.launcher.LauncherEngine;
import pro.gravit.utils.command.Command;
import pro.gravit.utils.helper.LogHelper;
import java.util.Base64;
public class SignDataCommand extends Command {
private final LauncherEngine engine;
public SignDataCommand(LauncherEngine engine) {
this.engine = engine;
}
@Override
public String getArgsDescription() {
return "[base64 data]";
}
@Override
public String getUsageDescription() {
return "sign any data";
}
@Override
public void invoke(String... args) throws Exception {
verifyArgs(args, 1);
byte[] data = Base64.getDecoder().decode(args[0]);
byte[] signature = engine.sign(data);
String base64 = Base64.getEncoder().encodeToString(signature);
LogHelper.info("Signature: %s", base64);
}
}

View file

@ -0,0 +1,21 @@
package pro.gravit.launcher.events.request;
import pro.gravit.launcher.events.RequestEvent;
import java.util.Map;
public class AdditionalDataRequestEvent extends RequestEvent {
public Map<String, String> data;
public AdditionalDataRequestEvent() {
}
public AdditionalDataRequestEvent(Map<String, String> data) {
this.data = data;
}
@Override
public String getType() {
return "additionalData";
}
}

View file

@ -0,0 +1,24 @@
package pro.gravit.launcher.request.auth;
import pro.gravit.launcher.events.request.AdditionalDataRequestEvent;
import pro.gravit.launcher.request.Request;
import java.util.UUID;
public class AdditionalDataRequest extends Request<AdditionalDataRequestEvent> {
public String username;
public UUID uuid;
public AdditionalDataRequest(String username) {
this.username = username;
}
public AdditionalDataRequest(UUID uuid) {
this.uuid = uuid;
}
@Override
public String getType() {
return "additionalData";
}
}

View file

@ -113,6 +113,7 @@ public void registerResults() {
results.register("features", FeaturesRequestEvent.class);
results.register("refreshToken", RefreshTokenRequestEvent.class);
results.register("restore", RestoreRequestEvent.class);
results.register("additionalData", AdditionalDataRequestEvent.class);
}
public void waitIfNotConnected() {

@ -1 +1 @@
Subproject commit 226d71ce66add7125286b876feba9e750ef7f4ca
Subproject commit e7c32607e69d5f2368191b39d179711624853e2e