[FEATURE] Remove legacy session

This commit is contained in:
Gravita 2022-03-17 21:07:54 +07:00
parent 6aed114791
commit 62094e0cf6
12 changed files with 13 additions and 363 deletions

View file

@ -8,7 +8,6 @@
import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launcher.profiles.ClientProfile;
import pro.gravit.launchserver.auth.AuthProviderPair; import pro.gravit.launchserver.auth.AuthProviderPair;
import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider; 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.EXEL4JLauncherBinary;
import pro.gravit.launchserver.binary.EXELauncherBinary; import pro.gravit.launchserver.binary.EXELauncherBinary;
import pro.gravit.launchserver.binary.JARLauncherBinary; import pro.gravit.launchserver.binary.JARLauncherBinary;
@ -94,7 +93,6 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
//public static LaunchServer server = null; //public static LaunchServer server = null;
public final Class<? extends LauncherBinary> launcherEXEBinaryClass; public final Class<? extends LauncherBinary> launcherEXEBinaryClass;
// Server config // Server config
public final SessionManager sessionManager;
public final AuthHookManager authHookManager; public final AuthHookManager authHookManager;
public final LaunchServerModulesManager modulesManager; public final LaunchServerModulesManager modulesManager;
// Launcher binary // Launcher binary
@ -149,10 +147,8 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
runtime.verify(); runtime.verify();
config.verify(); config.verify();
if (config.sessions == null) config.sessions = new MemorySessionStorage();
// build hooks, anti-brutforce and other // build hooks, anti-brutforce and other
sessionManager = new SessionManager(this);
mirrorManager = new MirrorManager(); mirrorManager = new MirrorManager();
reconfigurableManager = new ReconfigurableManager(); reconfigurableManager = new ReconfigurableManager();
authHookManager = new AuthHookManager(); authHookManager = new AuthHookManager();

View file

@ -13,7 +13,6 @@
import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.password.PasswordVerifier; import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.auth.protect.ProtectHandler; 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.auth.texture.TextureProvider;
import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.config.LaunchServerConfig; import pro.gravit.launchserver.config.LaunchServerConfig;
@ -205,7 +204,6 @@ public static void registerAll() {
GetAvailabilityAuthRequest.registerProviders(); GetAvailabilityAuthRequest.registerProviders();
OptionalAction.registerProviders(); OptionalAction.registerProviders();
OptionalTrigger.registerProviders(); OptionalTrigger.registerProviders();
SessionStorage.registerProviders();
} }
public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException { public static void generateConfigIfNotExists(Path configFile, CommandHandler commandHandler, LaunchServer.LaunchServerEnv env) throws IOException {

View file

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

View file

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

View file

@ -11,8 +11,6 @@
import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider; import pro.gravit.launchserver.auth.core.RejectAuthCoreProvider;
import pro.gravit.launchserver.auth.protect.ProtectHandler; import pro.gravit.launchserver.auth.protect.ProtectHandler;
import pro.gravit.launchserver.auth.protect.StdProtectHandler; 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.auth.texture.RequestTextureProvider;
import pro.gravit.launchserver.binary.tasks.exe.Launch4JTask; import pro.gravit.launchserver.binary.tasks.exe.Launch4JTask;
import pro.gravit.launchserver.components.AuthLimiterComponent; import pro.gravit.launchserver.components.AuthLimiterComponent;
@ -34,7 +32,6 @@ public final class LaunchServerConfig {
public boolean cacheUpdates = true; public boolean cacheUpdates = true;
public LauncherConfig.LauncherEnvironment env; public LauncherConfig.LauncherEnvironment env;
public Map<String, AuthProviderPair> auth; public Map<String, AuthProviderPair> auth;
public SessionStorage sessions;
// Handlers & Providers // Handlers & Providers
public ProtectHandler protectHandler; public ProtectHandler protectHandler;
public Map<String, Component> components; public Map<String, Component> components;
@ -70,7 +67,6 @@ public static LaunchServerConfig getDefault(LaunchServer.LaunchServerEnv env) {
a.displayName = "Default"; a.displayName = "Default";
newConfig.auth.put("std", a); newConfig.auth.put("std", a);
newConfig.protectHandler = new StdProtectHandler(); newConfig.protectHandler = new StdProtectHandler();
newConfig.sessions = new MemorySessionStorage();
newConfig.binaryName = "Launcher"; newConfig.binaryName = "Launcher";
newConfig.netty = new NettyConfig(); newConfig.netty = new NettyConfig();
@ -191,10 +187,6 @@ public void init(LaunchServer.ReloadType type) {
protectHandler.init(server); protectHandler.init(server);
protectHandler.checkLaunchServerLicense(); protectHandler.checkLaunchServerLicense();
} }
if (sessions != null) {
sessions.init(server);
server.registerObject("sessions", sessions);
}
if (components != null) { if (components != null) {
components.forEach((k, v) -> server.registerObject("component.".concat(k), v)); components.forEach((k, v) -> server.registerObject("component.".concat(k), v));
} }
@ -235,16 +227,6 @@ public void close(LaunchServer.ReloadType type) {
server.unregisterObject("protectHandler", protectHandler); server.unregisterObject("protectHandler", protectHandler);
protectHandler.close(); 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 { public static class ExeConf {

View file

@ -170,7 +170,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) { public void internalAuth(Client client, AuthResponse.ConnectTypes authType, AuthProviderPair pair, String username, UUID uuid, ClientPermissions permissions, boolean oauth) {
if(!oauth) { if(!oauth) {
pair.internalShowOAuthWarnMessage(); throw new UnsupportedOperationException("Unsupported legacy session system");
} }
client.isAuth = true; client.isAuth = true;
client.permissions = permissions; client.permissions = permissions;
@ -179,7 +179,7 @@ public void internalAuth(Client client, AuthResponse.ConnectTypes authType, Auth
client.username = username; client.username = username;
client.type = authType; client.type = authType;
client.uuid = uuid; client.uuid = uuid;
client.useOAuth = oauth; client.useOAuth = true;
} }
public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException { public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException {

View file

@ -14,7 +14,6 @@
import pro.gravit.launchserver.auth.core.AuthCoreProvider; import pro.gravit.launchserver.auth.core.AuthCoreProvider;
import pro.gravit.launchserver.auth.password.PasswordVerifier; import pro.gravit.launchserver.auth.password.PasswordVerifier;
import pro.gravit.launchserver.auth.protect.ProtectHandler; 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.auth.texture.TextureProvider;
import pro.gravit.launchserver.components.Component; import pro.gravit.launchserver.components.Component;
import pro.gravit.launchserver.modules.impl.LaunchServerModulesManager; 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(GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails.class, new UniversalJsonAdapter<>(GetAvailabilityAuthRequest.providers));
builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers)); builder.registerTypeAdapter(OptionalAction.class, new UniversalJsonAdapter<>(OptionalAction.providers));
builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers)); builder.registerTypeAdapter(OptionalTrigger.class, new UniversalJsonAdapter<>(OptionalTrigger.providers));
builder.registerTypeAdapter(SessionStorage.class, new UniversalJsonAdapter<>(SessionStorage.providers));
modulesManager.invokeEvent(new PreGsonPhase(builder)); modulesManager.invokeEvent(new PreGsonPhase(builder));
//ClientWebSocketService.appendTypeAdapters(builder); //ClientWebSocketService.appendTypeAdapters(builder);
} }

View file

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

View file

@ -13,8 +13,10 @@
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public class Client { public class Client {
@Deprecated
public UUID session; public UUID session;
public boolean useOAuth; @Deprecated
public boolean useOAuth; // Always true
public String auth_id; public String auth_id;
public long timestamp; public long timestamp;
public AuthResponse.ConnectTypes type; public AuthResponse.ConnectTypes type;
@ -36,6 +38,7 @@ public class Client {
public Map<String, String> serializableProperties; public Map<String, String> serializableProperties;
@Deprecated
public transient AtomicInteger refCount; public transient AtomicInteger refCount;
public Client(UUID session) { public Client(UUID session) {

View file

@ -97,14 +97,6 @@ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Client {} disconnected", IOHelper.getIP(ctx.channel().remoteAddress())); 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); super.channelInactive(ctx);
} }
} }

View file

@ -21,7 +21,9 @@ public static void exit(LaunchServer server, WebSocketFrameHandler wsHandler, Ch
Client newCusClient = new Client(null); Client newCusClient = new Client(null);
newCusClient.checkSign = chClient.checkSign; newCusClient.checkSign = chClient.checkSign;
wsHandler.setClient(newCusClient); 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); ExitRequestEvent event = new ExitRequestEvent(reason);
event.requestUUID = RequestEvent.eventUUID; event.requestUUID = RequestEvent.eventUUID;
wsHandler.service.sendObject(channel, event); wsHandler.service.sendObject(channel, event);
@ -73,7 +75,9 @@ public void execute(ChannelHandlerContext ctx, Client client) {
Client newClient = new Client(null); Client newClient = new Client(null);
newClient.checkSign = client.checkSign; newClient.checkSign = client.checkSign;
handler.setClient(newClient); 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) { if (exitAll) {
service.forEachActiveChannels(((channel, webSocketFrameHandler) -> { service.forEachActiveChannels(((channel, webSocketFrameHandler) -> {
Client client1 = webSocketFrameHandler.getClient(); Client client1 = webSocketFrameHandler.getClient();

View file

@ -21,34 +21,6 @@ public String getType() {
@Override @Override
public void execute(ChannelHandlerContext ctx, Client client) throws Exception { public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
if (session == null) { sendError("Legacy session system removed");
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());
}
} }
} }