package launchserver.auth.handler;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import launcher.LauncherAPI;
import launcher.helper.IOHelper;
import launcher.helper.SecurityHelper;
import launcher.request.auth.JoinServerRequest;
import launcher.serialize.config.entry.BlockConfigEntry;
public abstract class CachedAuthHandler extends AuthHandler {
private final Map<UUID, Entry> entryCache = new HashMap<>(IOHelper.BUFFER_SIZE);
private final Map<String, UUID> usernamesCache = new HashMap<>(IOHelper.BUFFER_SIZE);
@LauncherAPI
protected CachedAuthHandler(BlockConfigEntry block) {
super(block);
}
@Override
public final synchronized UUID auth(String username, String accessToken) throws IOException {
Entry entry = getEntry(username);
if (entry == null || !updateAccessToken(entry.uuid, accessToken)) {
return null; // Account doesn't exist
}
// Update cached access token (and username case)
entry.username = username;
entry.accessToken = accessToken;
return entry.uuid;
}
@Override
public final synchronized UUID checkServer(String username, String serverID) throws IOException {
Entry entry = getEntry(username);
return entry != null && username.equals(entry.username) &&
serverID.equals(entry.serverID) ? entry.uuid : null;
}
@Override
public final synchronized boolean joinServer(String username, String accessToken, String serverID) throws IOException {
Entry entry = getEntry(username);
if (entry == null || !username.equals(entry.username) ||
!accessToken.equals(entry.accessToken) || !updateServerID(entry.uuid, serverID)) {
return false; // Account doesn't exist or invalid access token
}
// Update cached server ID
entry.serverID = serverID;
return true;
}
@Override
public final synchronized UUID usernameToUUID(String username) throws IOException {
Entry entry = getEntry(username);
return entry == null ? null : entry.uuid;
}
@Override
public final synchronized String uuidToUsername(UUID uuid) throws IOException {
Entry entry = getEntry(uuid);
return entry == null ? null : entry.username;
}
@LauncherAPI
protected void addEntry(Entry entry) {
entryCache.putIfAbsent(entry.uuid, entry);
usernamesCache.put(low(entry.username), entry.uuid);
}
@LauncherAPI
protected abstract Entry fetchEntry(UUID uuid) throws IOException;
@LauncherAPI
protected abstract Entry fetchEntry(String username) throws IOException;
@LauncherAPI
protected abstract boolean updateAccessToken(UUID uuid, String accessToken) throws IOException;
@LauncherAPI
protected abstract boolean updateServerID(UUID uuid, String serverID) throws IOException;
private Entry getEntry(UUID uuid) throws IOException {
Entry entry = entryCache.get(uuid);
if (entry == null) {
entry = fetchEntry(uuid);
if (entry != null) {
addEntry(entry);
}
}
return entry;
}
private Entry getEntry(String username) throws IOException {
UUID uuid = usernamesCache.get(low(username));
if (uuid != null) {
return getEntry(uuid);
}
// Fetch entry by username
Entry entry = fetchEntry(username);
if (entry != null) {
addEntry(entry);
}
// Return what we got
return entry;
}
private static String low(String username) {
return username.toLowerCase(Locale.US);
}
public final class Entry {
@LauncherAPI public final UUID uuid;
private String username;
private String accessToken;
private String serverID;
@LauncherAPI
public Entry(UUID uuid, String username, String accessToken, String serverID) {
this.uuid = Objects.requireNonNull(uuid, "uuid");
this.username = Objects.requireNonNull(username, "username");
this.accessToken = accessToken == null ?
null : SecurityHelper.verifyToken(accessToken);
this.serverID = serverID == null ?
null : JoinServerRequest.verifyServerID(serverID);
}
}
}