mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-22 16:41:46 +03:00
Merge branch 'feature/removelegacysession' into dev
This commit is contained in:
commit
6430b9e2d4
18 changed files with 155 additions and 372 deletions
|
@ -8,7 +8,6 @@
|
|||
import pro.gravit.launcher.profiles.ClientProfile;
|
||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||
import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
|
||||
import pro.gravit.launchserver.binary.EXEL4JLauncherBinary;
|
||||
import pro.gravit.launchserver.binary.EXELauncherBinary;
|
||||
import pro.gravit.launchserver.binary.JARLauncherBinary;
|
||||
|
@ -94,7 +93,6 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
|||
//public static LaunchServer server = null;
|
||||
public final Class<? extends LauncherBinary> launcherEXEBinaryClass;
|
||||
// Server config
|
||||
public final SessionManager sessionManager;
|
||||
public final AuthHookManager authHookManager;
|
||||
public final LaunchServerModulesManager modulesManager;
|
||||
// Launcher binary
|
||||
|
@ -149,10 +147,8 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
|||
|
||||
runtime.verify();
|
||||
config.verify();
|
||||
if (config.sessions == null) config.sessions = new MemorySessionStorage();
|
||||
|
||||
// build hooks, anti-brutforce and other
|
||||
sessionManager = new SessionManager(this);
|
||||
mirrorManager = new MirrorManager();
|
||||
reconfigurableManager = new ReconfigurableManager();
|
||||
authHookManager = new AuthHookManager();
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.session.SessionStorage;
|
||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||
import pro.gravit.launchserver.components.Component;
|
||||
import pro.gravit.launchserver.config.LaunchServerConfig;
|
||||
|
@ -205,7 +204,6 @@ public static void registerAll() {
|
|||
GetAvailabilityAuthRequest.registerProviders();
|
||||
OptionalAction.registerProviders();
|
||||
OptionalTrigger.registerProviders();
|
||||
SessionStorage.registerProviders();
|
||||
}
|
||||
|
||||
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package pro.gravit.launchserver.auth.core;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.ClientPermissions;
|
||||
|
@ -13,6 +15,7 @@
|
|||
import pro.gravit.launchserver.auth.core.interfaces.provider.AuthSupportHardware;
|
||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.helper.LegacySessionHelper;
|
||||
import pro.gravit.launchserver.manangers.AuthManager;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
@ -21,6 +24,8 @@
|
|||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.sql.*;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Base64;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -30,6 +35,7 @@ public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHa
|
|||
private transient final Logger logger = LogManager.getLogger();
|
||||
public MySQLSourceConfig mySQLHolder;
|
||||
|
||||
public int expireSeconds = 3600;
|
||||
public String uuidColumn;
|
||||
public String usernameColumn;
|
||||
public String accessTokenColumn;
|
||||
|
@ -63,6 +69,8 @@ public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHa
|
|||
private transient String updateAuthSQL;
|
||||
private transient String updateServerIDSQL;
|
||||
|
||||
private transient LaunchServer server;
|
||||
|
||||
@Override
|
||||
public User getUserByUsername(String username) {
|
||||
try {
|
||||
|
@ -95,12 +103,38 @@ public User getUserByLogin(String login) {
|
|||
|
||||
@Override
|
||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||
return null;
|
||||
try {
|
||||
var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey);
|
||||
var user = (MySQLUser) getUserByUUID(info.uuid());
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return new MySQLUserSession(user);
|
||||
} catch (ExpiredJwtException e) {
|
||||
throw new OAuthAccessTokenExpired();
|
||||
} catch (JwtException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
||||
return null;
|
||||
String[] parts = refreshToken.split("\\.");
|
||||
if(parts.length != 2) {
|
||||
return null;
|
||||
}
|
||||
String username = parts[0];
|
||||
String token = parts[1];
|
||||
var user = (MySQLUser) getUserByUsername(username);
|
||||
if(user == null || user.password == null) {
|
||||
return null;
|
||||
}
|
||||
var realToken = LegacySessionHelper.makeRefreshTokenFromPassword(username, user.password, server.keyAgreementManager.legacySalt);
|
||||
if(!token.equals(realToken)) {
|
||||
return null;
|
||||
}
|
||||
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now().plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||
return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new MySQLUserSession(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -119,17 +153,20 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
|||
}
|
||||
}
|
||||
MySQLUserSession session = new MySQLUserSession(mySQLUser);
|
||||
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(mySQLUser, LocalDateTime.now().plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||
var refreshToken = LegacySessionHelper.makeRefreshTokenFromPassword(mySQLUser.username, mySQLUser.password, server.keyAgreementManager.legacySalt);
|
||||
if (minecraftAccess) {
|
||||
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
||||
updateAuth(mySQLUser, minecraftAccessToken);
|
||||
return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken, session);
|
||||
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||
} else {
|
||||
return AuthManager.AuthReport.ofMinecraftAccessToken(null, session);
|
||||
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server) {
|
||||
this.server = server;
|
||||
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package pro.gravit.launchserver.auth.core;
|
||||
|
||||
import io.jsonwebtoken.ExpiredJwtException;
|
||||
import io.jsonwebtoken.JwtException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.ClientPermissions;
|
||||
|
@ -10,18 +12,21 @@
|
|||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.helper.LegacySessionHelper;
|
||||
import pro.gravit.launchserver.manangers.AuthManager;
|
||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.sql.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
public class PostgresSQLCoreProvider extends AuthCoreProvider {
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
public PostgreSQLSourceConfig postgresSQLHolder;
|
||||
|
||||
public int expireSeconds = 3600;
|
||||
public String uuidColumn;
|
||||
public String usernameColumn;
|
||||
public String accessTokenColumn;
|
||||
|
@ -42,6 +47,8 @@ public class PostgresSQLCoreProvider extends AuthCoreProvider {
|
|||
private transient String updateAuthSQL;
|
||||
private transient String updateServerIDSQL;
|
||||
|
||||
private transient LaunchServer server;
|
||||
|
||||
@Override
|
||||
public User getUserByUsername(String username) {
|
||||
try {
|
||||
|
@ -74,12 +81,38 @@ public User getUserByLogin(String login) {
|
|||
|
||||
@Override
|
||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||
return null;
|
||||
try {
|
||||
var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey);
|
||||
var user = (MySQLCoreProvider.MySQLUser) getUserByUUID(info.uuid());
|
||||
if(user == null) {
|
||||
return null;
|
||||
}
|
||||
return new MySQLCoreProvider.MySQLUserSession(user);
|
||||
} catch (ExpiredJwtException e) {
|
||||
throw new OAuthAccessTokenExpired();
|
||||
} catch (JwtException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
||||
return null;
|
||||
String[] parts = refreshToken.split("\\.");
|
||||
if(parts.length != 2) {
|
||||
return null;
|
||||
}
|
||||
String username = parts[0];
|
||||
String token = parts[1];
|
||||
var user = (MySQLCoreProvider.MySQLUser) getUserByUsername(username);
|
||||
if(user == null || user.password == null) {
|
||||
return null;
|
||||
}
|
||||
var realToken = LegacySessionHelper.makeRefreshTokenFromPassword(username, user.password, server.keyAgreementManager.legacySalt);
|
||||
if(!token.equals(realToken)) {
|
||||
return null;
|
||||
}
|
||||
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(user, LocalDateTime.now().plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||
return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new MySQLCoreProvider.MySQLUserSession(user));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,17 +131,20 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
|||
}
|
||||
}
|
||||
MySQLUserSession session = new MySQLUserSession(postgresSQLUser);
|
||||
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(postgresSQLUser, LocalDateTime.now().plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||
var refreshToken = LegacySessionHelper.makeRefreshTokenFromPassword(postgresSQLUser.username, postgresSQLUser.password, server.keyAgreementManager.legacySalt);
|
||||
if (minecraftAccess) {
|
||||
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
||||
updateAuth(postgresSQLUser, minecraftAccessToken);
|
||||
return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken, session);
|
||||
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||
} else {
|
||||
return AuthManager.AuthReport.ofMinecraftAccessToken(null, session);
|
||||
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server) {
|
||||
this.server = server;
|
||||
if (postgresSQLHolder == null) logger.error("postgresSQLHolder cannot be null");
|
||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
||||
|
|
|
@ -1,172 +0,0 @@
|
|||
package pro.gravit.launchserver.auth.session;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class MemorySessionStorage extends SessionStorage implements AutoCloseable {
|
||||
|
||||
private transient final Map<UUID, Entry> clientSet = new ConcurrentHashMap<>(128);
|
||||
private transient final Map<UUID, Set<Entry>> uuidIndex = new ConcurrentHashMap<>(32);
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
public boolean autoDump = false;
|
||||
public String dumpFile = "sessions.json";
|
||||
|
||||
@Override
|
||||
public void init(LaunchServer server) {
|
||||
super.init(server);
|
||||
if (autoDump) {
|
||||
loadSessionsData();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getSessionData(UUID session) {
|
||||
|
||||
Entry e = clientSet.get(session);
|
||||
if (e == null) return null;
|
||||
return e.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<UUID> getSessionsFromUserUUID(UUID userUUID) {
|
||||
Set<Entry> set = uuidIndex.get(userUUID);
|
||||
if (set != null) return set.stream().map((e) -> e.sessionUuid);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean writeSession(UUID userUUID, UUID sessionUUID, byte[] data) {
|
||||
deleteSession(sessionUUID);
|
||||
Entry e = new Entry(data, sessionUUID);
|
||||
clientSet.put(sessionUUID, e);
|
||||
if (userUUID != null) {
|
||||
Set<Entry> uuidSet = uuidIndex.computeIfAbsent(userUUID, k -> ConcurrentHashMap.newKeySet());
|
||||
uuidSet.add(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSession(UUID sessionUUID) {
|
||||
Entry e = clientSet.remove(sessionUUID);
|
||||
if (e != null) {
|
||||
Set<Entry> set = uuidIndex.get(sessionUUID);
|
||||
if (set != null) {
|
||||
removeUuidFromIndexSet(set, e, sessionUUID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteSessionsByUserUUID(UUID userUUID) {
|
||||
Set<Entry> set = uuidIndex.get(userUUID);
|
||||
if (set != null) {
|
||||
for (Entry e : set) {
|
||||
clientSet.remove(e.sessionUuid);
|
||||
}
|
||||
set.clear();
|
||||
uuidIndex.remove(userUUID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
clientSet.clear();
|
||||
uuidIndex.clear();
|
||||
}
|
||||
|
||||
public void dumpSessionsData() {
|
||||
DumpedData dumpedData = new DumpedData(clientSet, uuidIndex);
|
||||
Path path = Paths.get(dumpFile);
|
||||
try (Writer writer = IOHelper.newWriter(path)) {
|
||||
Launcher.gsonManager.gson.toJson(dumpedData, writer);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Sessions can't be saved", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadSessionsData() {
|
||||
Path path = Paths.get(dumpFile);
|
||||
if (!Files.exists(path)) return;
|
||||
try (Reader reader = IOHelper.newReader(path)) {
|
||||
DumpedData data = Launcher.gsonManager.gson.fromJson(reader, DumpedData.class);
|
||||
clientSet.putAll(data.clientSet);
|
||||
uuidIndex.putAll(data.uuidIndex);
|
||||
} catch (Throwable e) {
|
||||
logger.error("Sessions can't be loaded", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockSession(UUID sessionUUID) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockUser(UUID userUUID) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlockSession(UUID sessionUUID) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unlockUser(UUID userUUID) {
|
||||
|
||||
}
|
||||
|
||||
private void removeUuidFromIndexSet(Set<Entry> set, Entry e, UUID session) {
|
||||
set.remove(e);
|
||||
if (set.isEmpty()) {
|
||||
uuidIndex.remove(session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (autoDump) {
|
||||
dumpSessionsData();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
public byte[] data;
|
||||
public UUID sessionUuid;
|
||||
public long timestamp;
|
||||
|
||||
public Entry(byte[] data, UUID sessionUuid) {
|
||||
this.data = data;
|
||||
this.sessionUuid = sessionUuid;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DumpedData {
|
||||
private final Map<UUID, Entry> clientSet;
|
||||
private final Map<UUID, Set<Entry>> uuidIndex;
|
||||
|
||||
private DumpedData(Map<UUID, Entry> clientSet, Map<UUID, Set<Entry>> uuidIndex) {
|
||||
this.clientSet = clientSet;
|
||||
this.uuidIndex = uuidIndex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package pro.gravit.launchserver.auth.session;
|
||||
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.utils.ProviderMap;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class SessionStorage {
|
||||
public static ProviderMap<SessionStorage> providers = new ProviderMap<>();
|
||||
private static boolean registeredProviders = false;
|
||||
protected transient LaunchServer server;
|
||||
|
||||
public static void registerProviders() {
|
||||
if (!registeredProviders) {
|
||||
providers.register("memory", MemorySessionStorage.class);
|
||||
registeredProviders = true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract byte[] getSessionData(UUID session);
|
||||
|
||||
public abstract Stream<UUID> getSessionsFromUserUUID(UUID userUUID);
|
||||
|
||||
public abstract boolean writeSession(UUID userUUID, UUID sessionUUID, byte[] data);
|
||||
|
||||
public abstract boolean deleteSession(UUID sessionUUID);
|
||||
|
||||
public boolean deleteSessionsByUserUUID(UUID userUUID) {
|
||||
getSessionsFromUserUUID(userUUID).forEach(this::deleteSession);
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract void clear();
|
||||
|
||||
public abstract void lockSession(UUID sessionUUID);
|
||||
|
||||
public abstract void lockUser(UUID userUUID);
|
||||
|
||||
public abstract void unlockSession(UUID sessionUUID);
|
||||
|
||||
public abstract void unlockUser(UUID userUUID);
|
||||
|
||||
public void init(LaunchServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
}
|
|
@ -11,8 +11,6 @@
|
|||
import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.protect.StdProtectHandler;
|
||||
import pro.gravit.launchserver.auth.session.MemorySessionStorage;
|
||||
import pro.gravit.launchserver.auth.session.SessionStorage;
|
||||
import pro.gravit.launchserver.auth.texture.RequestTextureProvider;
|
||||
import pro.gravit.launchserver.binary.tasks.exe.Launch4JTask;
|
||||
import pro.gravit.launchserver.components.AuthLimiterComponent;
|
||||
|
@ -34,7 +32,6 @@ public final class LaunchServerConfig {
|
|||
public boolean cacheUpdates = true;
|
||||
public LauncherConfig.LauncherEnvironment env;
|
||||
public Map<String, AuthProviderPair> auth;
|
||||
public SessionStorage sessions;
|
||||
// Handlers & Providers
|
||||
public ProtectHandler protectHandler;
|
||||
public Map<String, Component> components;
|
||||
|
@ -70,7 +67,6 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
|
|||
a.displayName = "Default";
|
||||
newConfig.auth.put("std", a);
|
||||
newConfig.protectHandler = new StdProtectHandler();
|
||||
newConfig.sessions = new MemorySessionStorage();
|
||||
newConfig.binaryName = "Launcher";
|
||||
|
||||
newConfig.netty = new NettyConfig();
|
||||
|
@ -191,10 +187,6 @@ public void init(LaunchServer.ReloadType type) {
|
|||
protectHandler.init(server);
|
||||
protectHandler.checkLaunchServerLicense();
|
||||
}
|
||||
if (sessions != null) {
|
||||
sessions.init(server);
|
||||
server.registerObject("sessions", sessions);
|
||||
}
|
||||
if (components != null) {
|
||||
components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
|
||||
}
|
||||
|
@ -235,16 +227,6 @@ public void close(LaunchServer.ReloadType type) {
|
|||
server.unregisterObject("protectHandler", protectHandler);
|
||||
protectHandler.close();
|
||||
}
|
||||
if (sessions != null) {
|
||||
server.unregisterObject("sessions", sessions);
|
||||
if (sessions instanceof AutoCloseable) {
|
||||
try {
|
||||
((AutoCloseable) sessions).close();
|
||||
} catch (Exception e) {
|
||||
logger.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExeConf {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package pro.gravit.launchserver.helper;
|
||||
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import pro.gravit.launchserver.auth.core.User;
|
||||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
import java.security.interfaces.ECPublicKey;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Date;
|
||||
import java.util.UUID;
|
||||
|
||||
public class LegacySessionHelper {
|
||||
public static String makeAccessJwtTokenFromString(User user, LocalDateTime expirationTime, ECPrivateKey privateKey) {
|
||||
return Jwts.builder()
|
||||
.setIssuer("LaunchServer")
|
||||
.setSubject(user.getUsername())
|
||||
.claim("uuid", user.getUUID().toString())
|
||||
.setExpiration(Date.from(expirationTime
|
||||
.toInstant(ZoneOffset.UTC)))
|
||||
.signWith(privateKey)
|
||||
.compact();
|
||||
}
|
||||
|
||||
public static JwtTokenInfo getJwtInfoFromAccessToken(String token, ECPublicKey publicKey) {
|
||||
var parser = Jwts.parserBuilder()
|
||||
.requireIssuer("LaunchServer")
|
||||
.setSigningKey(publicKey)
|
||||
.build();
|
||||
var claims = parser.parseClaimsJws(token);
|
||||
var uuid = UUID.fromString(claims.getBody().get("uuid", String.class));
|
||||
var username = claims.getBody().getSubject();
|
||||
return new JwtTokenInfo(username, uuid);
|
||||
}
|
||||
|
||||
public static String makeRefreshTokenFromPassword(String username, String rawPassword, String secretSalt) {
|
||||
if(rawPassword == null) {
|
||||
rawPassword = "";
|
||||
}
|
||||
return SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
|
||||
String.format("%s.%s.%s.%s", secretSalt, username, rawPassword, secretSalt)));
|
||||
}
|
||||
|
||||
public record JwtTokenInfo(String username, UUID uuid) {
|
||||
}
|
||||
}
|
|
@ -169,7 +169,7 @@ public AuthReport auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswor
|
|||
*/
|
||||
public void internalAuth(Client client, AuthResponse.ConnectTypes authType, AuthProviderPair pair, String username, UUID uuid, ClientPermissions permissions, boolean oauth) {
|
||||
if(!oauth) {
|
||||
pair.internalShowOAuthWarnMessage();
|
||||
throw new UnsupportedOperationException("Unsupported legacy session system");
|
||||
}
|
||||
client.isAuth = true;
|
||||
client.permissions = permissions;
|
||||
|
@ -178,7 +178,7 @@ public void internalAuth(Client client, AuthResponse.ConnectTypes authType, Auth
|
|||
client.username = username;
|
||||
client.type = authType;
|
||||
client.uuid = uuid;
|
||||
client.useOAuth = oauth;
|
||||
client.useOAuth = true;
|
||||
}
|
||||
|
||||
public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
import pro.gravit.utils.helper.SecurityHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.SecureRandom;
|
||||
|
@ -20,13 +21,15 @@ public class KeyAgreementManager {
|
|||
public final ECPrivateKey ecdsaPrivateKey;
|
||||
public final RSAPublicKey rsaPublicKey;
|
||||
public final RSAPrivateKey rsaPrivateKey;
|
||||
public final String legacySalt;
|
||||
private transient final Logger logger = LogManager.getLogger();
|
||||
|
||||
public KeyAgreementManager(ECPublicKey ecdsaPublicKey, ECPrivateKey ecdsaPrivateKey, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey) {
|
||||
public KeyAgreementManager(ECPublicKey ecdsaPublicKey, ECPrivateKey ecdsaPrivateKey, RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey, String legacySalt) {
|
||||
this.ecdsaPublicKey = ecdsaPublicKey;
|
||||
this.ecdsaPrivateKey = ecdsaPrivateKey;
|
||||
this.rsaPublicKey = rsaPublicKey;
|
||||
this.rsaPrivateKey = rsaPrivateKey;
|
||||
this.legacySalt = legacySalt;
|
||||
}
|
||||
|
||||
public KeyAgreementManager(Path keyDirectory) throws IOException, InvalidKeySpecException {
|
||||
|
@ -62,5 +65,12 @@ public KeyAgreementManager(Path keyDirectory) throws IOException, InvalidKeySpec
|
|||
IOHelper.write(rsaPublicKeyPath, rsaPublicKey.getEncoded());
|
||||
IOHelper.write(rsaPrivateKeyPath, rsaPrivateKey.getEncoded());
|
||||
}
|
||||
Path legacySaltPath = keyDirectory.resolve("legacySalt");
|
||||
if(IOHelper.isFile(legacySaltPath)) {
|
||||
legacySalt = new String(IOHelper.read(legacySaltPath), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
legacySalt = SecurityHelper.randomStringToken();
|
||||
IOHelper.write(legacySaltPath, legacySalt.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||
import pro.gravit.launchserver.auth.session.SessionStorage;
|
||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||
import pro.gravit.launchserver.components.Component;
|
||||
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager;
|
||||
|
@ -47,7 +46,6 @@ public void registerAdapters(GsonBuilder builder) {
|
|||
builder.registerTypeAdapter(GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails.class, new UniversalJsonAdapter<>(GetAvailabilityAuthRequest.providers));
|
||||
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
|
||||
builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers));
|
||||
builder.registerTypeAdapter(SessionStorage.class, new UniversalJsonAdapter<>(SessionStorage.providers));
|
||||
modulesManager.invokeEvent(new PreGsonPhase(builder));
|
||||
//ClientWebSocketService.appendTypeAdapters(builder);
|
||||
}
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package pro.gravit.launchserver.manangers;
|
||||
|
||||
import pro.gravit.launcher.Launcher;
|
||||
import pro.gravit.launchserver.LaunchServer;
|
||||
import pro.gravit.launchserver.socket.Client;
|
||||
import pro.gravit.utils.HookSet;
|
||||
import pro.gravit.utils.helper.IOHelper;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SessionManager {
|
||||
|
||||
private final LaunchServer server;
|
||||
public HookSet<Client> clientRestoreHook = new HookSet<>();
|
||||
|
||||
public SessionManager(LaunchServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
|
||||
public boolean addClient(Client client) {
|
||||
if (client == null || client.session == null) return false;
|
||||
return server.config.sessions.writeSession(client.uuid, client.session, compressClient(client));
|
||||
}
|
||||
|
||||
public Stream<UUID> findSessionsByUUID(UUID uuid) {
|
||||
return server.config.sessions.getSessionsFromUserUUID(uuid);
|
||||
}
|
||||
|
||||
public boolean removeByUUID(UUID uuid) {
|
||||
return server.config.sessions.deleteSessionsByUserUUID(uuid);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
server.config.sessions.clear();
|
||||
}
|
||||
|
||||
private byte[] compressClient(Client client) {
|
||||
return IOHelper.encode(Launcher.gsonManager.gson.toJson(client)); //Compress using later
|
||||
}
|
||||
|
||||
private Client decompressClient(byte[] client) {
|
||||
return Launcher.gsonManager.gson.fromJson(IOHelper.decode(client), Client.class); //Compress using later
|
||||
}
|
||||
|
||||
private Client restoreFromString(byte[] data) {
|
||||
Client result = decompressClient(data);
|
||||
result.updateAuth(server);
|
||||
if (result.auth != null && (result.username != null)) {
|
||||
result.coreObject = result.auth.core.getUserByUUID(result.uuid);
|
||||
}
|
||||
if (result.refCount == null) result.refCount = new AtomicInteger(1);
|
||||
clientRestoreHook.hook(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public Client getClient(UUID session) {
|
||||
if (session == null) return null;
|
||||
byte[] data = server.config.sessions.getSessionData(session);
|
||||
if (data == null) return null;
|
||||
return restoreFromString(data);
|
||||
}
|
||||
|
||||
|
||||
public Client getOrNewClient(UUID session) {
|
||||
Client client = getClient(session);
|
||||
return client == null ? new Client(session) : client;
|
||||
}
|
||||
|
||||
public boolean remove(UUID session) {
|
||||
return server.config.sessions.deleteSession(session);
|
||||
}
|
||||
}
|
|
@ -13,8 +13,10 @@
|
|||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Client {
|
||||
@Deprecated
|
||||
public UUID session;
|
||||
public boolean useOAuth;
|
||||
@Deprecated
|
||||
public boolean useOAuth; // Always true
|
||||
public String auth_id;
|
||||
public long timestamp;
|
||||
public AuthResponse.ConnectTypes type;
|
||||
|
@ -36,6 +38,7 @@ public class Client {
|
|||
|
||||
public Map<String, String> serializableProperties;
|
||||
|
||||
@Deprecated
|
||||
public transient AtomicInteger refCount;
|
||||
|
||||
public Client(UUID session) {
|
||||
|
|
|
@ -97,14 +97,6 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Client {} disconnected", IOHelper.getIP(ctx.channel().remoteAddress()));
|
||||
}
|
||||
int refCount = client.refCount.decrementAndGet();
|
||||
if (client.session != null) {
|
||||
if (refCount == 0) {
|
||||
srv.sessionManager.addClient(client);
|
||||
} else if (refCount < 0) {
|
||||
logger.warn("Client session {} reference counter invalid - {}", client.session, refCount);
|
||||
}
|
||||
}
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ public static void exit(LaunchServer server, WebSocketFrameHandler wsHandler, Ch
|
|||
Client newCusClient = new Client(null);
|
||||
newCusClient.checkSign = chClient.checkSign;
|
||||
wsHandler.setClient(newCusClient);
|
||||
if (chClient.session != null) server.sessionManager.remove(chClient.session);
|
||||
if (chClient.session != null) {
|
||||
throw new UnsupportedOperationException("Legacy session system removed");
|
||||
}
|
||||
ExitRequestEvent event = new ExitRequestEvent(reason);
|
||||
event.requestUUID = RequestEvent.eventUUID;
|
||||
wsHandler.service.sendObject(channel, event);
|
||||
|
@ -73,7 +75,9 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
|||
Client newClient = new Client(null);
|
||||
newClient.checkSign = client.checkSign;
|
||||
handler.setClient(newClient);
|
||||
if (client.session != null) server.sessionManager.remove(client.session);
|
||||
if (client.session != null) {
|
||||
throw new UnsupportedOperationException("Legacy session system removed");
|
||||
}
|
||||
if (exitAll) {
|
||||
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
|
||||
Client client1 = webSocketFrameHandler.getClient();
|
||||
|
|
|
@ -21,34 +21,6 @@ public String getType() {
|
|||
|
||||
@Override
|
||||
public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||
if (session == null) {
|
||||
sendError("Session invalid");
|
||||
return;
|
||||
}
|
||||
final Client[] rClient = {null};
|
||||
service.forEachActiveChannels((channel, handler) -> {
|
||||
Client c = handler.getClient();
|
||||
if (c != null && session.equals(c.session)) {
|
||||
rClient[0] = c;
|
||||
}
|
||||
});
|
||||
if (rClient[0] == null) {
|
||||
rClient[0] = server.sessionManager.getClient(session);
|
||||
}
|
||||
if (rClient[0] == null) {
|
||||
sendError("Session invalid");
|
||||
return;
|
||||
}
|
||||
if (rClient[0].useOAuth) {
|
||||
sendError("This session using OAuth. Session restoration not safety");
|
||||
return;
|
||||
}
|
||||
WebSocketFrameHandler frameHandler = ctx.pipeline().get(WebSocketFrameHandler.class);
|
||||
frameHandler.setClient(rClient[0]);
|
||||
if (needUserInfo) {
|
||||
sendResult(new RestoreSessionRequestEvent(CurrentUserResponse.collectUserInfoFromClient(server, rClient[0])));
|
||||
} else {
|
||||
sendResult(new RestoreSessionRequestEvent());
|
||||
}
|
||||
sendError("Legacy session system removed");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import pro.gravit.launcher.events.RequestEvent;
|
||||
|
||||
@Deprecated
|
||||
public class RestoreSessionRequestEvent extends RequestEvent {
|
||||
public CurrentUserRequestEvent.UserInfo userInfo;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import java.util.UUID;
|
||||
|
||||
@Deprecated
|
||||
public class RestoreSessionRequest extends Request<RestoreSessionRequestEvent> implements WebSocketRequest {
|
||||
@LauncherNetworkAPI
|
||||
public final UUID session;
|
||||
|
|
Loading…
Reference in a new issue