mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-11-15 11:39:11 +03:00
Merge branch 'release/5.2.9'
This commit is contained in:
commit
b57118cd20
47 changed files with 372 additions and 1004 deletions
2
.github/workflows/push.yml
vendored
2
.github/workflows/push.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
||||||
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
|
cp LaunchServer.jar ../../../artifacts/LaunchServer.jar
|
||||||
cd ../../..
|
cd ../../..
|
||||||
cp ServerWrapper/build/libs/ServerWrapper.jar artifacts/ServerWrapper.jar
|
cp ServerWrapper/build/libs/ServerWrapper.jar artifacts/ServerWrapper.jar
|
||||||
cp LauncherAuthlib/build/libs/LauncherAuthlib.jar artifacts/LauncherAuthlib.jar
|
cp LauncherAuthlib/build/libs/LauncherAuthlib.jar artifacts/LauncherAuthlib.jar || true
|
||||||
cp modules/*_module/build/libs/*.jar artifacts/modules || true
|
cp modules/*_module/build/libs/*.jar artifacts/modules || true
|
||||||
cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true
|
cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
import pro.gravit.launchserver.HttpRequester;
|
import pro.gravit.launchserver.HttpRequester;
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportProperties;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
||||||
import pro.gravit.launchserver.helper.HttpHelper;
|
import pro.gravit.launchserver.helper.HttpHelper;
|
||||||
import pro.gravit.launchserver.manangers.AuthManager;
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
|
@ -18,7 +19,9 @@
|
||||||
import pro.gravit.utils.helper.CommonHelper;
|
import pro.gravit.utils.helper.CommonHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class HttpAuthCoreProvider extends AuthCoreProvider {
|
public class HttpAuthCoreProvider extends AuthCoreProvider {
|
||||||
|
@ -243,14 +246,18 @@ public RefreshTokenRequest(String refreshToken, AuthResponse.AuthContext context
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class HttpUser implements User, UserSupportTextures {
|
public static class HttpUser implements User, UserSupportTextures, UserSupportProperties {
|
||||||
private String username;
|
private String username;
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
private String serverId;
|
private String serverId;
|
||||||
private String accessToken;
|
private String accessToken;
|
||||||
private ClientPermissions permissions;
|
private ClientPermissions permissions;
|
||||||
|
@Deprecated
|
||||||
private Texture skin;
|
private Texture skin;
|
||||||
|
@Deprecated
|
||||||
private Texture cloak;
|
private Texture cloak;
|
||||||
|
private Map<String, Texture> assets;
|
||||||
|
private Map<String, String> properties;
|
||||||
|
|
||||||
public HttpUser() {
|
public HttpUser() {
|
||||||
}
|
}
|
||||||
|
@ -273,6 +280,27 @@ public HttpUser(String username, UUID uuid, String serverId, String accessToken,
|
||||||
this.cloak = cloak;
|
this.cloak = cloak;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, Map<String, String> properties) {
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.skin = skin;
|
||||||
|
this.cloak = cloak;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Map<String, Texture> assets, Map<String, String> properties) {
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.assets = assets;
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return username;
|
return username;
|
||||||
|
@ -300,12 +328,40 @@ public ClientPermissions getPermissions() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Texture getSkinTexture() {
|
public Texture getSkinTexture() {
|
||||||
return skin;
|
if(assets == null) {
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
|
return assets.get("SKIN");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Texture getCloakTexture() {
|
public Texture getCloakTexture() {
|
||||||
return cloak;
|
if(assets == null) {
|
||||||
|
return cloak;
|
||||||
|
}
|
||||||
|
return assets.get("CAPE");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Texture> getAssets() {
|
||||||
|
if(assets == null) {
|
||||||
|
Map<String, Texture> map = new HashMap<>();
|
||||||
|
if(skin != null) {
|
||||||
|
map.put("SKIN", skin);
|
||||||
|
}
|
||||||
|
if(cloak != null) {
|
||||||
|
map.put("CAPE", cloak);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return assets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getProperties() {
|
||||||
|
if(properties == null) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
return properties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package pro.gravit.launchserver.auth.core;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
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.provider.AuthSupportHardware;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware;
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware;
|
||||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||||
|
import pro.gravit.launchserver.helper.LegacySessionHelper;
|
||||||
import pro.gravit.launchserver.manangers.AuthManager;
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
|
@ -21,6 +24,9 @@
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -30,6 +36,7 @@ public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHa
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
public MySQLSourceConfig mySQLHolder;
|
public MySQLSourceConfig mySQLHolder;
|
||||||
|
|
||||||
|
public int expireSeconds = 3600;
|
||||||
public String uuidColumn;
|
public String uuidColumn;
|
||||||
public String usernameColumn;
|
public String usernameColumn;
|
||||||
public String accessTokenColumn;
|
public String accessTokenColumn;
|
||||||
|
@ -63,6 +70,8 @@ public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHa
|
||||||
private transient String updateAuthSQL;
|
private transient String updateAuthSQL;
|
||||||
private transient String updateServerIDSQL;
|
private transient String updateServerIDSQL;
|
||||||
|
|
||||||
|
private transient LaunchServer server;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public User getUserByUsername(String username) {
|
||||||
try {
|
try {
|
||||||
|
@ -95,12 +104,38 @@ public User getUserByLogin(String login) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
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
|
@Override
|
||||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
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(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||||
|
return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new MySQLUserSession(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -119,17 +154,20 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MySQLUserSession session = new MySQLUserSession(mySQLUser);
|
MySQLUserSession session = new MySQLUserSession(mySQLUser);
|
||||||
|
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(mySQLUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||||
|
var refreshToken = mySQLUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(mySQLUser.username, mySQLUser.password, server.keyAgreementManager.legacySalt));
|
||||||
if (minecraftAccess) {
|
if (minecraftAccess) {
|
||||||
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
||||||
updateAuth(mySQLUser, minecraftAccessToken);
|
updateAuth(mySQLUser, minecraftAccessToken);
|
||||||
return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken, session);
|
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||||
} else {
|
} else {
|
||||||
return AuthManager.AuthReport.ofMinecraftAccessToken(null, session);
|
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LaunchServer server) {
|
public void init(LaunchServer server) {
|
||||||
|
this.server = server;
|
||||||
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
||||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
||||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
||||||
|
@ -145,9 +183,9 @@ public void init(LaunchServer server) {
|
||||||
userInfoCols, table, usernameColumn);
|
userInfoCols, table, usernameColumn);
|
||||||
queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL;
|
queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL;
|
||||||
|
|
||||||
updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=? LIMIT 1",
|
updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=?",
|
||||||
table, accessTokenColumn, serverIDColumn, uuidColumn);
|
table, accessTokenColumn, serverIDColumn, uuidColumn);
|
||||||
updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=? LIMIT 1",
|
updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=?",
|
||||||
table, serverIDColumn, uuidColumn);
|
table, serverIDColumn, uuidColumn);
|
||||||
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
|
String hardwareInfoCols = "id, hwDiskId, baseboardSerialNumber, displayId, bitness, totalMemory, logicalProcessors, physicalProcessors, processorMaxFreq, battery, id, graphicCard, banned, publicKey";
|
||||||
if (sqlFindHardwareByPublicKey == null)
|
if (sqlFindHardwareByPublicKey == null)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package pro.gravit.launchserver.auth.core;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
|
@ -10,18 +12,22 @@
|
||||||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||||
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
|
import pro.gravit.launchserver.auth.PostgreSQLSourceConfig;
|
||||||
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||||
|
import pro.gravit.launchserver.helper.LegacySessionHelper;
|
||||||
import pro.gravit.launchserver.manangers.AuthManager;
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class PostgresSQLCoreProvider extends AuthCoreProvider {
|
public class PostgresSQLCoreProvider extends AuthCoreProvider {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
public PostgreSQLSourceConfig postgresSQLHolder;
|
public PostgreSQLSourceConfig postgresSQLHolder;
|
||||||
|
|
||||||
|
public int expireSeconds = 3600;
|
||||||
public String uuidColumn;
|
public String uuidColumn;
|
||||||
public String usernameColumn;
|
public String usernameColumn;
|
||||||
public String accessTokenColumn;
|
public String accessTokenColumn;
|
||||||
|
@ -42,6 +48,8 @@ public class PostgresSQLCoreProvider extends AuthCoreProvider {
|
||||||
private transient String updateAuthSQL;
|
private transient String updateAuthSQL;
|
||||||
private transient String updateServerIDSQL;
|
private transient String updateServerIDSQL;
|
||||||
|
|
||||||
|
private transient LaunchServer server;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public User getUserByUsername(String username) {
|
||||||
try {
|
try {
|
||||||
|
@ -74,12 +82,38 @@ public User getUserByLogin(String login) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||||
return null;
|
try {
|
||||||
|
var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey);
|
||||||
|
var user = (PostgresSQLUser) getUserByUUID(info.uuid());
|
||||||
|
if(user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new PostgresSQLCoreProvider.MySQLUserSession(user);
|
||||||
|
} catch (ExpiredJwtException e) {
|
||||||
|
throw new OAuthAccessTokenExpired();
|
||||||
|
} catch (JwtException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
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 = (PostgresSQLUser) 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(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||||
|
return new AuthManager.AuthReport(null, accessToken, refreshToken, expireSeconds * 1000L, new PostgresSQLCoreProvider.MySQLUserSession(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,17 +132,20 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MySQLUserSession session = new MySQLUserSession(postgresSQLUser);
|
MySQLUserSession session = new MySQLUserSession(postgresSQLUser);
|
||||||
|
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(postgresSQLUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||||
|
var refreshToken = postgresSQLUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(postgresSQLUser.username, postgresSQLUser.password, server.keyAgreementManager.legacySalt));
|
||||||
if (minecraftAccess) {
|
if (minecraftAccess) {
|
||||||
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
||||||
updateAuth(postgresSQLUser, minecraftAccessToken);
|
updateAuth(postgresSQLUser, minecraftAccessToken);
|
||||||
return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken, session);
|
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||||
} else {
|
} else {
|
||||||
return AuthManager.AuthReport.ofMinecraftAccessToken(null, session);
|
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LaunchServer server) {
|
public void init(LaunchServer server) {
|
||||||
|
this.server = server;
|
||||||
if (postgresSQLHolder == null) logger.error("postgresSQLHolder cannot be null");
|
if (postgresSQLHolder == null) logger.error("postgresSQLHolder cannot be null");
|
||||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
||||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package pro.gravit.launchserver.auth.core.interfaces.user;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface UserSupportProperties {
|
||||||
|
Map<String, String> getProperties();
|
||||||
|
}
|
|
@ -3,6 +3,9 @@
|
||||||
import pro.gravit.launcher.profiles.ClientProfile;
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
import pro.gravit.launcher.profiles.Texture;
|
import pro.gravit.launcher.profiles.Texture;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface UserSupportTextures {
|
public interface UserSupportTextures {
|
||||||
Texture getSkinTexture();
|
Texture getSkinTexture();
|
||||||
|
|
||||||
|
@ -15,4 +18,17 @@ default Texture getSkinTexture(ClientProfile profile) {
|
||||||
default Texture getCloakTexture(ClientProfile profile) {
|
default Texture getCloakTexture(ClientProfile profile) {
|
||||||
return getCloakTexture();
|
return getCloakTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default Map<String, Texture> getUserAssets() {
|
||||||
|
var skin = getSkinTexture();
|
||||||
|
var cape = getCloakTexture();
|
||||||
|
Map<String, Texture> map = new HashMap<>();
|
||||||
|
if(skin != null) {
|
||||||
|
map.put("SKIN", skin);
|
||||||
|
}
|
||||||
|
if(cape != null) {
|
||||||
|
map.put("CAPE", cape);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
package pro.gravit.launchserver.auth.texture;
|
package pro.gravit.launchserver.auth.texture;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import pro.gravit.launcher.HTTPRequest;
|
import pro.gravit.launcher.HTTPRequest;
|
||||||
|
@ -7,12 +8,16 @@
|
||||||
import pro.gravit.launcher.profiles.Texture;
|
import pro.gravit.launcher.profiles.Texture;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class JsonTextureProvider extends TextureProvider {
|
public class JsonTextureProvider extends TextureProvider {
|
||||||
public String url;
|
public String url;
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
|
private transient static final Type MAP_TYPE = new TypeToken<Map<String, Texture>>() {}.getType();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
@ -22,23 +27,36 @@ public void close() throws IOException {
|
||||||
@Override
|
@Override
|
||||||
public Texture getCloakTexture(UUID uuid, String username, String client) throws IOException {
|
public Texture getCloakTexture(UUID uuid, String username, String client) throws IOException {
|
||||||
logger.warn("Ineffective get cloak texture for {}", username);
|
logger.warn("Ineffective get cloak texture for {}", username);
|
||||||
return getTextures(uuid, username, client).cloak;
|
return getAssets(uuid, username, client).get("CAPE");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Texture getSkinTexture(UUID uuid, String username, String client) throws IOException {
|
public Texture getSkinTexture(UUID uuid, String username, String client) throws IOException {
|
||||||
logger.warn("Ineffective get skin texture for {}", username);
|
logger.warn("Ineffective get skin texture for {}", username);
|
||||||
return getTextures(uuid, username, client).skin;
|
return getAssets(uuid, username, client).get("SKIN");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
public Map<String, Texture> getAssets(UUID uuid, String username, String client) {
|
||||||
try {
|
try {
|
||||||
var result = HTTPRequest.jsonRequest(null, "GET", new URL(RequestTextureProvider.getTextureURL(url, uuid, username, client)));
|
var result = HTTPRequest.jsonRequest(null, "GET", new URL(RequestTextureProvider.getTextureURL(url, uuid, username, client)));
|
||||||
return Launcher.gsonManager.gson.fromJson(result, SkinAndCloakTextures.class);
|
|
||||||
|
Map<String, Texture> map = Launcher.gsonManager.gson.fromJson(result, MAP_TYPE);
|
||||||
|
if(map == null) {
|
||||||
|
return new HashMap<>();
|
||||||
|
}
|
||||||
|
if(map.get("skin") != null) { // Legacy script
|
||||||
|
map.put("SKIN", map.get("skin"));
|
||||||
|
map.remove("skin");
|
||||||
|
}
|
||||||
|
if(map.get("cloak") != null) {
|
||||||
|
map.put("CAPE", map.get("cloak"));
|
||||||
|
map.remove("cloak");
|
||||||
|
}
|
||||||
|
return map;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("JsonTextureProvider", e);
|
logger.error("JsonTextureProvider", e);
|
||||||
return new SkinAndCloakTextures(null, null);
|
return new HashMap<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import pro.gravit.utils.ProviderMap;
|
import pro.gravit.utils.ProviderMap;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class TextureProvider implements AutoCloseable {
|
public abstract class TextureProvider implements AutoCloseable {
|
||||||
|
@ -31,6 +33,7 @@ public static void registerProviders() {
|
||||||
|
|
||||||
public abstract Texture getSkinTexture(UUID uuid, String username, String client) throws IOException;
|
public abstract Texture getSkinTexture(UUID uuid, String username, String client) throws IOException;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static class SkinAndCloakTextures {
|
public static class SkinAndCloakTextures {
|
||||||
public final Texture skin;
|
public final Texture skin;
|
||||||
public final Texture cloak;
|
public final Texture cloak;
|
||||||
|
@ -41,6 +44,7 @@ public SkinAndCloakTextures(Texture skin, Texture cloak) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
||||||
|
|
||||||
Texture skin;
|
Texture skin;
|
||||||
|
@ -60,4 +64,32 @@ public SkinAndCloakTextures getTextures(UUID uuid, String username, String clien
|
||||||
|
|
||||||
return new SkinAndCloakTextures(skin, cloak);
|
return new SkinAndCloakTextures(skin, cloak);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Texture> getAssets(UUID uuid, String username, String client) {
|
||||||
|
|
||||||
|
Texture skin;
|
||||||
|
try {
|
||||||
|
skin = getSkinTexture(uuid, username, client);
|
||||||
|
} catch (IOException e) {
|
||||||
|
skin = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get cloak texture
|
||||||
|
Texture cloak;
|
||||||
|
try {
|
||||||
|
cloak = getCloakTexture(uuid, username, client);
|
||||||
|
} catch (IOException e) {
|
||||||
|
cloak = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Texture> map = new HashMap<>();
|
||||||
|
if(skin != null) {
|
||||||
|
map.put("SKIN", skin);
|
||||||
|
}
|
||||||
|
if(cloak != null) {
|
||||||
|
map.put("CAPE", cloak);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,6 +111,7 @@ protected void initProps() {
|
||||||
properties.put("launcher.guardType", server.config.launcher.guardType);
|
properties.put("launcher.guardType", server.config.launcher.guardType);
|
||||||
properties.put("launchercore.env", server.config.env);
|
properties.put("launchercore.env", server.config.env);
|
||||||
properties.put("launcher.memory", server.config.launcher.memoryLimit);
|
properties.put("launcher.memory", server.config.launcher.memoryLimit);
|
||||||
|
properties.put("launcher.customJvmOptions", server.config.launcher.customJvmOptions);
|
||||||
if (server.config.launcher.encryptRuntime) {
|
if (server.config.launcher.encryptRuntime) {
|
||||||
if (server.runtime.runtimeEncryptKey == null)
|
if (server.runtime.runtimeEncryptKey == null)
|
||||||
server.runtime.runtimeEncryptKey = SecurityHelper.randomStringToken();
|
server.runtime.runtimeEncryptKey = SecurityHelper.randomStringToken();
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -23,9 +21,7 @@
|
||||||
import pro.gravit.utils.helper.JVMHelper;
|
import pro.gravit.utils.helper.JVMHelper;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public final class LaunchServerConfig {
|
public final class LaunchServerConfig {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
|
@ -36,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;
|
||||||
|
@ -72,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();
|
||||||
|
@ -193,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));
|
||||||
}
|
}
|
||||||
|
@ -237,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 {
|
||||||
|
@ -292,6 +272,7 @@ public static class LauncherConf {
|
||||||
public boolean deleteTempFiles;
|
public boolean deleteTempFiles;
|
||||||
public boolean certificatePinning;
|
public boolean certificatePinning;
|
||||||
public boolean encryptRuntime;
|
public boolean encryptRuntime;
|
||||||
|
public List<String> customJvmOptions = new ArrayList<>();
|
||||||
public int memoryLimit = 256;
|
public int memoryLimit = 256;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
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.Clock;
|
||||||
|
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")
|
||||||
|
.setClock(() -> new Date(Clock.systemUTC().millis()))
|
||||||
|
.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) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||||
import pro.gravit.launcher.profiles.ClientProfile;
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
import pro.gravit.launcher.profiles.PlayerProfile;
|
||||||
|
import pro.gravit.launcher.profiles.Texture;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
import pro.gravit.launcher.request.auth.password.*;
|
import pro.gravit.launcher.request.auth.password.*;
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||||
import pro.gravit.launchserver.auth.core.User;
|
import pro.gravit.launchserver.auth.core.User;
|
||||||
import pro.gravit.launchserver.auth.core.UserSession;
|
import pro.gravit.launchserver.auth.core.UserSession;
|
||||||
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportProperties;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures;
|
||||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||||
import pro.gravit.launchserver.socket.Client;
|
import pro.gravit.launchserver.socket.Client;
|
||||||
|
@ -26,10 +28,7 @@
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class AuthManager {
|
public class AuthManager {
|
||||||
private transient final LaunchServer server;
|
private transient final LaunchServer server;
|
||||||
|
@ -170,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) {
|
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 +178,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 {
|
||||||
|
@ -206,10 +205,10 @@ public PlayerProfile getPlayerProfile(Client client) {
|
||||||
if (playerProfile != null) return playerProfile;
|
if (playerProfile != null) return playerProfile;
|
||||||
}
|
}
|
||||||
if (client.auth.textureProvider != null) {
|
if (client.auth.textureProvider != null) {
|
||||||
return getPlayerProfile(client.uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider);
|
return getPlayerProfile(client.uuid, client.username, client.profile == null ? null : client.profile.getTitle(), client.auth.textureProvider, new HashMap<>());
|
||||||
}
|
}
|
||||||
// Return combined profile
|
// Return combined profile
|
||||||
return new PlayerProfile(client.uuid, client.username, null, null);
|
return new PlayerProfile(client.uuid, client.username, null, null, new HashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerProfile getPlayerProfile(AuthProviderPair pair, String username) {
|
public PlayerProfile getPlayerProfile(AuthProviderPair pair, String username) {
|
||||||
|
@ -229,9 +228,9 @@ public PlayerProfile getPlayerProfile(AuthProviderPair pair, String username, Cl
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (pair.textureProvider != null) {
|
if (pair.textureProvider != null) {
|
||||||
return getPlayerProfile(uuid, username, profile == null ? null : profile.getTitle(), pair.textureProvider);
|
return getPlayerProfile(uuid, username, profile == null ? null : profile.getTitle(), pair.textureProvider, new HashMap<>());
|
||||||
}
|
}
|
||||||
return new PlayerProfile(uuid, username, null, null);
|
return new PlayerProfile(uuid, username, null, null, new HashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerProfile getPlayerProfile(AuthProviderPair pair, UUID uuid) {
|
public PlayerProfile getPlayerProfile(AuthProviderPair pair, UUID uuid) {
|
||||||
|
@ -251,27 +250,33 @@ public PlayerProfile getPlayerProfile(AuthProviderPair pair, UUID uuid, ClientPr
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (pair.textureProvider != null) {
|
if (pair.textureProvider != null) {
|
||||||
return getPlayerProfile(uuid, username, profile == null ? null : profile.getTitle(), pair.textureProvider);
|
return getPlayerProfile(uuid, username, profile == null ? null : profile.getTitle(), pair.textureProvider, new HashMap<>());
|
||||||
}
|
}
|
||||||
return new PlayerProfile(uuid, username, null, null);
|
return new PlayerProfile(uuid, username, null, null, new HashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PlayerProfile getPlayerProfile(AuthProviderPair pair, User user) {
|
public PlayerProfile getPlayerProfile(AuthProviderPair pair, User user) {
|
||||||
if (user instanceof UserSupportTextures) {
|
Map<String, String> properties;
|
||||||
return new PlayerProfile(user.getUUID(), user.getUsername(), ((UserSupportTextures) user).getSkinTexture(), ((UserSupportTextures) user).getCloakTexture());
|
if(user instanceof UserSupportProperties userSupportProperties) {
|
||||||
|
properties = userSupportProperties.getProperties();
|
||||||
|
} else {
|
||||||
|
properties = new HashMap<>();
|
||||||
|
}
|
||||||
|
if (user instanceof UserSupportTextures userSupportTextures) {
|
||||||
|
return new PlayerProfile(user.getUUID(), user.getUsername(), userSupportTextures.getUserAssets(), properties);
|
||||||
}
|
}
|
||||||
if (pair.textureProvider == null) {
|
if (pair.textureProvider == null) {
|
||||||
throw new NullPointerException("TextureProvider not found");
|
throw new NullPointerException("TextureProvider not found");
|
||||||
}
|
}
|
||||||
return getPlayerProfile(user.getUUID(), user.getUsername(), "", pair.textureProvider);
|
return getPlayerProfile(user.getUUID(), user.getUsername(), "", pair.textureProvider, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PlayerProfile getPlayerProfile(UUID uuid, String username, String client, TextureProvider textureProvider) {
|
private PlayerProfile getPlayerProfile(UUID uuid, String username, String client, TextureProvider textureProvider, Map<String, String> properties) {
|
||||||
// Get skin texture
|
// Get skin texture
|
||||||
TextureProvider.SkinAndCloakTextures textures = textureProvider.getTextures(uuid, username, client);
|
var assets = textureProvider.getAssets(uuid, username, client);
|
||||||
|
|
||||||
// Return combined profile
|
// Return combined profile
|
||||||
return new PlayerProfile(uuid, username, textures.skin, textures.cloak);
|
return new PlayerProfile(uuid, username, assets, properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthRequest.AuthPasswordInterface decryptPassword(AuthRequest.AuthPasswordInterface password) throws AuthException {
|
public AuthRequest.AuthPasswordInterface decryptPassword(AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
@ -20,13 +21,15 @@ public class KeyAgreementManager {
|
||||||
public final ECPrivateKey ecdsaPrivateKey;
|
public final ECPrivateKey ecdsaPrivateKey;
|
||||||
public final RSAPublicKey rsaPublicKey;
|
public final RSAPublicKey rsaPublicKey;
|
||||||
public final RSAPrivateKey rsaPrivateKey;
|
public final RSAPrivateKey rsaPrivateKey;
|
||||||
|
public final String legacySalt;
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
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.ecdsaPublicKey = ecdsaPublicKey;
|
||||||
this.ecdsaPrivateKey = ecdsaPrivateKey;
|
this.ecdsaPrivateKey = ecdsaPrivateKey;
|
||||||
this.rsaPublicKey = rsaPublicKey;
|
this.rsaPublicKey = rsaPublicKey;
|
||||||
this.rsaPrivateKey = rsaPrivateKey;
|
this.rsaPrivateKey = rsaPrivateKey;
|
||||||
|
this.legacySalt = legacySalt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyAgreementManager(Path keyDirectory) throws IOException, InvalidKeySpecException {
|
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(rsaPublicKeyPath, rsaPublicKey.getEncoded());
|
||||||
IOHelper.write(rsaPrivateKeyPath, rsaPrivateKey.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.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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
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) {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ public class ClientLauncherWrapper {
|
||||||
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
|
public static boolean waitProcess = Boolean.getBoolean(WAIT_PROCESS_PROPERTY);
|
||||||
@LauncherInject("launcher.memory")
|
@LauncherInject("launcher.memory")
|
||||||
public static int launcherMemoryLimit;
|
public static int launcherMemoryLimit;
|
||||||
|
@LauncherInject("launcher.customJvmOptions")
|
||||||
|
public static List<String> customJvmOptions;
|
||||||
|
|
||||||
public static void main(String[] arguments) throws IOException, InterruptedException {
|
public static void main(String[] arguments) throws IOException, InterruptedException {
|
||||||
LogHelper.printVersion("Launcher");
|
LogHelper.printVersion("Launcher");
|
||||||
|
@ -138,6 +140,9 @@ public static void main(String[] arguments) throws IOException, InterruptedExcep
|
||||||
if (context.memoryLimit != 0) {
|
if (context.memoryLimit != 0) {
|
||||||
args.add(String.format("-Xmx%dM", context.memoryLimit));
|
args.add(String.format("-Xmx%dM", context.memoryLimit));
|
||||||
}
|
}
|
||||||
|
if(customJvmOptions != null) {
|
||||||
|
args.addAll(customJvmOptions);
|
||||||
|
}
|
||||||
args.add("-cp");
|
args.add("-cp");
|
||||||
args.add(String.join(IOHelper.PLATFORM_SEPARATOR, context.classpath));
|
args.add(String.join(IOHelper.PLATFORM_SEPARATOR, context.classpath));
|
||||||
args.add(context.mainClass);
|
args.add(context.mainClass);
|
||||||
|
|
|
@ -108,8 +108,7 @@ public static void main(String[] args) throws Throwable {
|
||||||
Request.addAllExtendedToken(params.extendedTokens);
|
Request.addAllExtendedToken(params.extendedTokens);
|
||||||
}
|
}
|
||||||
} else if (params.session != null) {
|
} else if (params.session != null) {
|
||||||
LogHelper.info("Using Sessions");
|
throw new UnsupportedOperationException("Legacy session not supported");
|
||||||
Request.setSession(params.session);
|
|
||||||
}
|
}
|
||||||
checkJVMBitsAndVersion(params.profile.getMinJavaVersion(), params.profile.getRecommendJavaVersion(), params.profile.getMaxJavaVersion(), params.profile.isWarnMissJavaVersion());
|
checkJVMBitsAndVersion(params.profile.getMinJavaVersion(), params.profile.getRecommendJavaVersion(), params.profile.getMaxJavaVersion(), params.profile.isWarnMissJavaVersion());
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessInitPhase(engine, params));
|
LauncherEngine.modulesManager.invokeEvent(new ClientProcessInitPhase(engine, params));
|
||||||
|
|
|
@ -121,7 +121,7 @@ private void applyClientProfile() {
|
||||||
}
|
}
|
||||||
this.params.oauth = Request.getOAuth();
|
this.params.oauth = Request.getOAuth();
|
||||||
if (this.params.oauth == null) {
|
if (this.params.oauth == null) {
|
||||||
this.params.session = Request.getSession();
|
throw new UnsupportedOperationException("Legacy session not supported");
|
||||||
} else {
|
} else {
|
||||||
this.params.authId = Request.getAuthId();
|
this.params.authId = Request.getAuthId();
|
||||||
this.params.oauthExpiredTime = Request.getTokenExpiredTime();
|
this.params.oauthExpiredTime = Request.getTokenExpiredTime();
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class AuthRequestEvent extends RequestEvent {
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public String protectToken;
|
public String protectToken;
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
|
@Deprecated // Always null
|
||||||
public UUID session;
|
public UUID session;
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public OAuthRequestEvent oauth;
|
public OAuthRequestEvent oauth;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import pro.gravit.launcher.events.RequestEvent;
|
import pro.gravit.launcher.events.RequestEvent;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class RestoreSessionRequestEvent extends RequestEvent {
|
public class RestoreSessionRequestEvent extends RequestEvent {
|
||||||
public CurrentUserRequestEvent.UserInfo userInfo;
|
public CurrentUserRequestEvent.UserInfo userInfo;
|
||||||
|
|
||||||
|
|
|
@ -483,7 +483,8 @@ public enum Version {
|
||||||
MC117("1.17", 755),
|
MC117("1.17", 755),
|
||||||
MC1171("1.17.1", 756),
|
MC1171("1.17.1", 756),
|
||||||
MC118("1.18", 757),
|
MC118("1.18", 757),
|
||||||
MC1181("1.18.1", 757);
|
MC1181("1.18.1", 757),
|
||||||
|
MC1182("1.18.2", 758);
|
||||||
private static final Map<String, Version> VERSIONS;
|
private static final Map<String, Version> VERSIONS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.VerifyHelper;
|
import pro.gravit.utils.helper.VerifyHelper;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -10,18 +12,44 @@ public final class PlayerProfile {
|
||||||
|
|
||||||
public final UUID uuid;
|
public final UUID uuid;
|
||||||
public final String username;
|
public final String username;
|
||||||
|
@Deprecated
|
||||||
public final Texture skin, cloak;
|
public final Texture skin, cloak;
|
||||||
|
public final Map<String, Texture> assets;
|
||||||
|
public final Map<String, String> properties;
|
||||||
|
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public PlayerProfile(UUID uuid, String username, Texture skin, Texture cloak) {
|
public PlayerProfile(UUID uuid, String username, Texture skin, Texture cloak) {
|
||||||
|
this(uuid, username, skin, cloak, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public PlayerProfile(UUID uuid, String username, Texture skin, Texture cloak, Map<String, String> properties) {
|
||||||
this.uuid = Objects.requireNonNull(uuid, "uuid");
|
this.uuid = Objects.requireNonNull(uuid, "uuid");
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
this.cloak = cloak;
|
this.cloak = cloak;
|
||||||
|
this.assets = new HashMap<>();
|
||||||
|
if(skin != null) {
|
||||||
|
this.assets.put("SKIN", skin);
|
||||||
|
}
|
||||||
|
if(cloak != null) {
|
||||||
|
this.assets.put("CAPE", cloak);
|
||||||
|
}
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerProfile(UUID uuid, String username, Map<String, Texture> assets, Map<String, String> properties) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.username = username;
|
||||||
|
this.assets = assets;
|
||||||
|
this.properties = properties;
|
||||||
|
this.skin = assets.get("SKIN");
|
||||||
|
this.cloak = assets.get("CAPE");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlayerProfile newOfflineProfile(String username) {
|
public static PlayerProfile newOfflineProfile(String username) {
|
||||||
return new PlayerProfile(offlineUUID(username), username, null, null);
|
return new PlayerProfile(offlineUUID(username), username, new HashMap<>(), new HashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UUID offlineUUID(String username) {
|
public static UUID offlineUUID(String username) {
|
||||||
|
|
|
@ -22,6 +22,7 @@ public abstract class Request<R extends WebSocketEvent> implements WebSocketRequ
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static StdWebSocketService service;
|
public static StdWebSocketService service;
|
||||||
private static RequestService requestService;
|
private static RequestService requestService;
|
||||||
|
@Deprecated
|
||||||
private static UUID session;
|
private static UUID session;
|
||||||
private static AuthRequestEvent.OAuthRequestEvent oauth;
|
private static AuthRequestEvent.OAuthRequestEvent oauth;
|
||||||
private static Map<String, String> extendedTokens;
|
private static Map<String, String> extendedTokens;
|
||||||
|
@ -46,10 +47,12 @@ public static boolean isAvailable() {
|
||||||
return requestService != null;
|
return requestService != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static UUID getSession() {
|
public static UUID getSession() {
|
||||||
return Request.session;
|
return Request.session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public static void setSession(UUID session) {
|
public static void setSession(UUID session) {
|
||||||
Request.session = session;
|
Request.session = session;
|
||||||
}
|
}
|
||||||
|
@ -146,9 +149,7 @@ public RequestRestoreReport(boolean legacySession, boolean refreshed, List<Strin
|
||||||
|
|
||||||
public static RequestRestoreReport restore() throws Exception {
|
public static RequestRestoreReport restore() throws Exception {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
RestoreSessionRequest request = new RestoreSessionRequest(session);
|
throw new UnsupportedOperationException("Legacy session system not supported");
|
||||||
request.request();
|
|
||||||
return new RequestRestoreReport(true, false, null);
|
|
||||||
} else {
|
} else {
|
||||||
boolean refreshed = false;
|
boolean refreshed = false;
|
||||||
RestoreRequest request;
|
RestoreRequest request;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public class RestoreSessionRequest extends Request<RestoreSessionRequestEvent> implements WebSocketRequest {
|
public class RestoreSessionRequest extends Request<RestoreSessionRequestEvent> implements WebSocketRequest {
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public final UUID session;
|
public final UUID session;
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
sourceCompatibility = '1.8'
|
|
||||||
targetCompatibility = '1.8'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api project(':LauncherAPI')
|
|
||||||
compileOnly group: 'com.google.guava', name: 'guava', version: rootProject['verGuavaC']
|
|
||||||
api files('../compat/authlib/authlib-clean.jar')
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package com.mojang.authlib.minecraft;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class MinecraftProfileTexture {
|
|
||||||
public static final Set<Type> PROFILE_TEXTURE_TYPES = Collections.unmodifiableSet(EnumSet.allOf(Type.class));
|
|
||||||
public static final int PROFILE_TEXTURE_COUNT = PROFILE_TEXTURE_TYPES.size();
|
|
||||||
// Instance
|
|
||||||
private final String url;
|
|
||||||
private final String hash;
|
|
||||||
private final Map<String, String> metadata;
|
|
||||||
|
|
||||||
public MinecraftProfileTexture(String url) {
|
|
||||||
this(url, baseName(url));
|
|
||||||
}
|
|
||||||
|
|
||||||
public MinecraftProfileTexture(String url, String hash) {
|
|
||||||
this.url = url;
|
|
||||||
this.hash = hash;
|
|
||||||
this.metadata = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MinecraftProfileTexture(String url, String hash, Map<String, String> metadata) {
|
|
||||||
this.url = url;
|
|
||||||
this.hash = hash;
|
|
||||||
this.metadata = metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String baseName(String url) {
|
|
||||||
String name = url.substring(url.lastIndexOf('/') + 1);
|
|
||||||
|
|
||||||
// Remove index
|
|
||||||
int extensionIndex = name.lastIndexOf('.');
|
|
||||||
if (extensionIndex >= 0)
|
|
||||||
name = name.substring(0, extensionIndex);
|
|
||||||
|
|
||||||
// We're done
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHash() {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMetadata(String key) {
|
|
||||||
if (metadata == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return metadata.get(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("MinecraftProfileTexture{url='%s',hash=%s}", url, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
SKIN,
|
|
||||||
CAPE,
|
|
||||||
ELYTRA
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package com.mojang.authlib.yggdrasil;
|
|
||||||
|
|
||||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
import pro.gravit.launcher.request.auth.CheckServerRequest;
|
|
||||||
import pro.gravit.launcher.request.auth.JoinServerRequest;
|
|
||||||
import pro.gravit.launcher.request.uuid.BatchProfileByUsernameRequest;
|
|
||||||
import pro.gravit.launcher.request.uuid.ProfileByUUIDRequest;
|
|
||||||
import pro.gravit.launcher.request.uuid.ProfileByUsernameRequest;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
// Used to bypass Launcher's class name obfuscation and access API
|
|
||||||
|
|
||||||
public class CompatBridge {
|
|
||||||
public static final int PROFILES_MAX_BATCH_SIZE = 128;
|
|
||||||
|
|
||||||
private CompatBridge() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompatProfile checkServer(String username, String serverID) throws Exception {
|
|
||||||
LogHelper.debug("CompatBridge.checkServer, Username: '%s', Server ID: %s", username, serverID);
|
|
||||||
return CompatProfile.fromPlayerProfile(new CheckServerRequest(username, serverID).request().playerProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean joinServer(String username, String accessToken, String serverID) throws Exception {
|
|
||||||
|
|
||||||
// Join server
|
|
||||||
LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
|
||||||
return new JoinServerRequest(username, accessToken, serverID).request().allow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompatProfile profileByUsername(String username) throws Exception {
|
|
||||||
return CompatProfile.fromPlayerProfile(new ProfileByUsernameRequest(username).request().playerProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompatProfile profileByUUID(UUID uuid) throws Exception {
|
|
||||||
return CompatProfile.fromPlayerProfile(new ProfileByUUIDRequest(uuid).request().playerProfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompatProfile[] profilesByUsername(String... usernames) throws Exception {
|
|
||||||
PlayerProfile[] profiles = new BatchProfileByUsernameRequest(usernames).request().playerProfiles;
|
|
||||||
|
|
||||||
// Convert profiles
|
|
||||||
CompatProfile[] resultProfiles = new CompatProfile[profiles.length];
|
|
||||||
for (int i = 0; i < profiles.length; i++)
|
|
||||||
resultProfiles[i] = CompatProfile.fromPlayerProfile(profiles[i]);
|
|
||||||
|
|
||||||
// We're dones
|
|
||||||
return resultProfiles;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package com.mojang.authlib.yggdrasil;
|
|
||||||
|
|
||||||
import pro.gravit.launcher.Launcher;
|
|
||||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
|
|
||||||
public class CompatProfile {
|
|
||||||
public static final String SKIN_URL_PROPERTY = Launcher.SKIN_URL_PROPERTY;
|
|
||||||
public static final String SKIN_DIGEST_PROPERTY = Launcher.SKIN_DIGEST_PROPERTY;
|
|
||||||
public static final String CLOAK_URL_PROPERTY = Launcher.CLOAK_URL_PROPERTY;
|
|
||||||
public static final String CLOAK_DIGEST_PROPERTY = Launcher.CLOAK_DIGEST_PROPERTY;
|
|
||||||
// Instance
|
|
||||||
public final UUID uuid;
|
|
||||||
public final String uuidHash, username;
|
|
||||||
public final String skinURL, skinDigest;
|
|
||||||
public final String cloakURL, cloakDigest;
|
|
||||||
|
|
||||||
public CompatProfile(UUID uuid, String username, String skinURL, String skinDigest, String cloakURL, String cloakDigest) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
uuidHash = Launcher.toHash(uuid);
|
|
||||||
this.username = username;
|
|
||||||
this.skinURL = skinURL;
|
|
||||||
this.skinDigest = skinDigest;
|
|
||||||
this.cloakURL = cloakURL;
|
|
||||||
this.cloakDigest = cloakDigest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CompatProfile fromPlayerProfile(PlayerProfile profile) {
|
|
||||||
return profile == null ? null : new CompatProfile(profile.uuid, profile.username,
|
|
||||||
profile.skin == null ? null : profile.skin.url,
|
|
||||||
profile.skin == null ? null : SecurityHelper.toHex(profile.skin.digest),
|
|
||||||
profile.cloak == null ? null : profile.cloak.url,
|
|
||||||
profile.cloak == null ? null : SecurityHelper.toHex(profile.cloak.digest)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int countProperties() {
|
|
||||||
int count = 0;
|
|
||||||
if (skinURL != null)
|
|
||||||
count++;
|
|
||||||
if (skinDigest != null)
|
|
||||||
count++;
|
|
||||||
if (cloakURL != null)
|
|
||||||
count++;
|
|
||||||
if (cloakDigest != null)
|
|
||||||
count++;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package com.mojang.authlib.yggdrasil;
|
|
||||||
|
|
||||||
import pro.gravit.launcher.request.auth.CheckServerRequest;
|
|
||||||
import pro.gravit.launcher.request.auth.JoinServerRequest;
|
|
||||||
import pro.gravit.utils.helper.CommonHelper;
|
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
// Used by 1.6.4 and below versions
|
|
||||||
|
|
||||||
public class LegacyBridge {
|
|
||||||
private LegacyBridge() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean checkServer(String username, String serverID) throws Exception {
|
|
||||||
LogHelper.debug("LegacyBridge.checkServer, Username: '%s', Server ID: %s", username, serverID);
|
|
||||||
return new CheckServerRequest(username, serverID).request() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getCloakURL(String username) {
|
|
||||||
LogHelper.debug("LegacyBridge.getCloakURL: '%s'", username);
|
|
||||||
return CommonHelper.replace(System.getProperty("launcher.legacy.cloaksURL",
|
|
||||||
"http://skins.minecraft.net/MinecraftCloaks/%username%.png"), "username", IOHelper.urlEncode(username));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getSkinURL(String username) {
|
|
||||||
LogHelper.debug("LegacyBridge.getSkinURL: '%s'", username);
|
|
||||||
return CommonHelper.replace(System.getProperty("launcher.legacy.skinsURL",
|
|
||||||
"http://skins.minecraft.net/MinecraftSkins/%username%.png"), "username", IOHelper.urlEncode(username));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String joinServer(String username, String accessToken, String serverID) {
|
|
||||||
|
|
||||||
// Join server
|
|
||||||
LogHelper.debug("LegacyBridge.joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
|
||||||
try {
|
|
||||||
return new JoinServerRequest(username, accessToken, serverID).request().allow ? "OK" : "Bad Login (Clientside)";
|
|
||||||
} catch (Exception e) {
|
|
||||||
return e.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package com.mojang.authlib.yggdrasil;
|
|
||||||
|
|
||||||
import com.mojang.authlib.Agent;
|
|
||||||
import com.mojang.authlib.AuthenticationService;
|
|
||||||
import com.mojang.authlib.GameProfileRepository;
|
|
||||||
import com.mojang.authlib.UserAuthentication;
|
|
||||||
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
import java.net.Proxy;
|
|
||||||
|
|
||||||
public class YggdrasilAuthenticationService implements AuthenticationService {
|
|
||||||
public YggdrasilAuthenticationService(Proxy proxy, String clientToken) {
|
|
||||||
LogHelper.debug("Patched AuthenticationService created: '%s'", clientToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MinecraftSessionService createMinecraftSessionService() {
|
|
||||||
return new YggdrasilMinecraftSessionService(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameProfileRepository createProfileRepository() {
|
|
||||||
return new YggdrasilGameProfileRepository();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserAuthentication createUserAuthentication(Agent agent) {
|
|
||||||
throw new UnsupportedOperationException("createUserAuthentication is used only by Mojang Launcher");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
package com.mojang.authlib.yggdrasil;
|
|
||||||
|
|
||||||
import com.mojang.authlib.Agent;
|
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import com.mojang.authlib.GameProfileRepository;
|
|
||||||
import com.mojang.authlib.ProfileLookupCallback;
|
|
||||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
import pro.gravit.launcher.request.uuid.BatchProfileByUsernameRequest;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
|
||||||
import pro.gravit.utils.helper.VerifyHelper;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class YggdrasilGameProfileRepository implements GameProfileRepository {
|
|
||||||
private static final long BUSY_WAIT_MS = VerifyHelper.verifyLong(
|
|
||||||
Long.parseLong(System.getProperty("launcher.com.mojang.authlib.busyWait", Long.toString(100L))),
|
|
||||||
VerifyHelper.L_NOT_NEGATIVE, "launcher.com.mojang.authlib.busyWait can't be < 0");
|
|
||||||
private static final long ERROR_BUSY_WAIT_MS = VerifyHelper.verifyLong(
|
|
||||||
Long.parseLong(System.getProperty("launcher.com.mojang.authlib.errorBusyWait", Long.toString(500L))),
|
|
||||||
VerifyHelper.L_NOT_NEGATIVE, "launcher.com.mojang.authlib.errorBusyWait can't be < 0");
|
|
||||||
|
|
||||||
public YggdrasilGameProfileRepository() {
|
|
||||||
LogHelper.debug("Patched GameProfileRepository created");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void busyWait(long ms) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(ms);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void findProfilesByNames(String[] usernames, Agent agent, ProfileLookupCallback callback) {
|
|
||||||
int offset = 0;
|
|
||||||
while (offset < usernames.length) {
|
|
||||||
String[] sliceUsernames = Arrays.copyOfRange(usernames, offset, Math.min(offset + 128, usernames.length));
|
|
||||||
offset += 128;
|
|
||||||
|
|
||||||
// Batch Username-To-UUID request
|
|
||||||
PlayerProfile[] sliceProfiles;
|
|
||||||
try {
|
|
||||||
sliceProfiles = new BatchProfileByUsernameRequest(sliceUsernames).request().playerProfiles;
|
|
||||||
} catch (Exception e) {
|
|
||||||
boolean debug = LogHelper.isDebugEnabled();
|
|
||||||
for (String username : sliceUsernames) {
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("Couldn't find profile '%s': %s", username, e);
|
|
||||||
}
|
|
||||||
callback.onProfileLookupFailed(new GameProfile((UUID) null, username), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Busy wait, like in standard com.mojang.authlib
|
|
||||||
busyWait(ERROR_BUSY_WAIT_MS);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request succeeded!
|
|
||||||
int len = sliceProfiles.length;
|
|
||||||
boolean debug = len > 0 && LogHelper.isDebugEnabled();
|
|
||||||
for (int i = 0; i < len; i++) {
|
|
||||||
PlayerProfile pp = sliceProfiles[i];
|
|
||||||
if (pp == null) {
|
|
||||||
String username = sliceUsernames[i];
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("Couldn't find profile '%s'", username);
|
|
||||||
}
|
|
||||||
callback.onProfileLookupFailed(new GameProfile((UUID) null, username), new ProfileNotFoundException("Server did not find the requested profile"));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report as looked up
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("Successfully looked up profile '%s'", pp.username);
|
|
||||||
}
|
|
||||||
callback.onProfileLookupSucceeded(YggdrasilMinecraftSessionService.toGameProfile(pp));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Busy wait, like in standard com.mojang.authlib
|
|
||||||
busyWait(BUSY_WAIT_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,250 +0,0 @@
|
||||||
package com.mojang.authlib.yggdrasil;
|
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import com.google.gson.JsonParser;
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
import com.mojang.authlib.AuthenticationService;
|
|
||||||
import com.mojang.authlib.GameProfile;
|
|
||||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
|
||||||
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
|
|
||||||
import com.mojang.authlib.minecraft.BaseMinecraftSessionService;
|
|
||||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
|
||||||
import com.mojang.authlib.properties.Property;
|
|
||||||
import com.mojang.authlib.properties.PropertyMap;
|
|
||||||
import pro.gravit.launcher.Launcher;
|
|
||||||
import pro.gravit.launcher.profiles.PlayerProfile;
|
|
||||||
import pro.gravit.launcher.request.auth.CheckServerRequest;
|
|
||||||
import pro.gravit.launcher.request.auth.JoinServerRequest;
|
|
||||||
import pro.gravit.launcher.request.uuid.ProfileByUUIDRequest;
|
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class YggdrasilMinecraftSessionService extends BaseMinecraftSessionService {
|
|
||||||
public static final boolean NO_TEXTURES = Boolean.getBoolean("launcher.com.mojang.authlib.noTextures");
|
|
||||||
private static final Gson gson = new Gson();
|
|
||||||
|
|
||||||
public YggdrasilMinecraftSessionService(AuthenticationService service) {
|
|
||||||
super(service);
|
|
||||||
LogHelper.debug("Patched MinecraftSessionService created");
|
|
||||||
}
|
|
||||||
|
|
||||||
public YggdrasilMinecraftSessionService(YggdrasilAuthenticationService service) {
|
|
||||||
super(service);
|
|
||||||
LogHelper.debug("Patched MinecraftSessionService created");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void fillTextureProperties(GameProfile profile, PlayerProfile pp) {
|
|
||||||
boolean debug = LogHelper.isDebugEnabled();
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("fillTextureProperties, Username: '%s'", profile.getName());
|
|
||||||
}
|
|
||||||
if (NO_TEXTURES)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Fill textures map
|
|
||||||
PropertyMap properties = profile.getProperties();
|
|
||||||
if (pp.skin != null) {
|
|
||||||
properties.put(Launcher.SKIN_URL_PROPERTY, new Property(Launcher.SKIN_URL_PROPERTY, pp.skin.url, ""));
|
|
||||||
properties.put(Launcher.SKIN_DIGEST_PROPERTY, new Property(Launcher.SKIN_DIGEST_PROPERTY, SecurityHelper.toHex(pp.skin.digest), ""));
|
|
||||||
if (pp.skin.metadata != null) {
|
|
||||||
String metadata = serializeMetadataMap(pp.skin.metadata);
|
|
||||||
properties.put(Launcher.SKIN_METADATA_PROPERTY, new Property(Launcher.SKIN_METADATA_PROPERTY, metadata, ""));
|
|
||||||
}
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("fillTextureProperties, Has skin texture for username '%s'", profile.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pp.cloak != null) {
|
|
||||||
properties.put(Launcher.CLOAK_URL_PROPERTY, new Property(Launcher.CLOAK_URL_PROPERTY, pp.cloak.url, ""));
|
|
||||||
properties.put(Launcher.CLOAK_DIGEST_PROPERTY, new Property(Launcher.CLOAK_DIGEST_PROPERTY, SecurityHelper.toHex(pp.cloak.digest), ""));
|
|
||||||
if (pp.cloak.metadata != null) {
|
|
||||||
String metadata = serializeMetadataMap(pp.cloak.metadata);
|
|
||||||
properties.put(Launcher.CLOAK_METADATA_PROPERTY, new Property(Launcher.SKIN_METADATA_PROPERTY, metadata, ""));
|
|
||||||
}
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("fillTextureProperties, Has cloak texture for username '%s'", profile.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String serializeMetadataMap(Map<String, String> map) {
|
|
||||||
if (map == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return gson.toJson(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, String> deserializeMetadataMap(String value) {
|
|
||||||
Type typeOfMap = new TypeToken<Map<String, String>>() {
|
|
||||||
}.getType();
|
|
||||||
return gson.fromJson(value, typeOfMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void getTexturesMojang(Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures, String texturesBase64, GameProfile profile) {
|
|
||||||
// Decode textures payload
|
|
||||||
JsonObject texturesJSON;
|
|
||||||
try {
|
|
||||||
byte[] decoded = Base64.getDecoder().decode(texturesBase64);
|
|
||||||
texturesJSON = JsonParser.parseString(new String(decoded, IOHelper.UNICODE_CHARSET)).getAsJsonObject().getAsJsonObject("textures");
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
LogHelper.error("Could not decode textures payload, Username: '%s', UUID: '%s'", profile.getName(), profile.getUUID());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch textures from textures JSON
|
|
||||||
for (MinecraftProfileTexture.Type type : MinecraftProfileTexture.PROFILE_TEXTURE_TYPES) {
|
|
||||||
if (textures.containsKey(type))
|
|
||||||
continue; // Overriden by launcher
|
|
||||||
|
|
||||||
// Get texture from JSON
|
|
||||||
JsonElement textureJSON = texturesJSON.get(type.name());
|
|
||||||
if (textureJSON != null && textureJSON.isJsonObject()) {
|
|
||||||
JsonElement urlValue = textureJSON.getAsJsonObject().get("url");
|
|
||||||
if (urlValue.isJsonPrimitive())
|
|
||||||
textures.put(type, new MinecraftProfileTexture(urlValue.getAsString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GameProfile toGameProfile(PlayerProfile pp) {
|
|
||||||
GameProfile profile = new GameProfile(pp.uuid, pp.username);
|
|
||||||
fillTextureProperties(profile, pp);
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
|
|
||||||
// Verify has UUID
|
|
||||||
UUID uuid = profile.getUUID();
|
|
||||||
boolean debug = LogHelper.isDebugEnabled();
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("fillProfileProperties, UUID: %s", uuid);
|
|
||||||
}
|
|
||||||
if (uuid == null)
|
|
||||||
return profile;
|
|
||||||
|
|
||||||
// Make profile request
|
|
||||||
PlayerProfile pp;
|
|
||||||
try {
|
|
||||||
pp = new ProfileByUUIDRequest(uuid).request().playerProfile;
|
|
||||||
} catch (Exception e) {
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("Couldn't fetch profile properties for '%s': %s", profile, e);
|
|
||||||
}
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify is found
|
|
||||||
if (pp == null) {
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("Couldn't fetch profile properties for '%s' as the profile does not exist", profile);
|
|
||||||
}
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new game profile from player profile
|
|
||||||
if (debug) {
|
|
||||||
LogHelper.debug("Successfully fetched profile properties for '%s'", profile);
|
|
||||||
}
|
|
||||||
fillTextureProperties(profile, pp);
|
|
||||||
return toGameProfile(pp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
|
|
||||||
if (LogHelper.isDebugEnabled()) {
|
|
||||||
LogHelper.debug("getTextures, Username: '%s', UUID: '%s'", profile.getName(), profile.getUUID());
|
|
||||||
}
|
|
||||||
Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures = new EnumMap<>(MinecraftProfileTexture.Type.class);
|
|
||||||
|
|
||||||
// Add textures
|
|
||||||
if (!NO_TEXTURES) {
|
|
||||||
// Add skin URL to textures map
|
|
||||||
Property skinURL = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_URL_PROPERTY), null);
|
|
||||||
Property skinDigest = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_DIGEST_PROPERTY), null);
|
|
||||||
Property skinMetadata = Iterables.getFirst(profile.getProperties().get(Launcher.SKIN_METADATA_PROPERTY), null);
|
|
||||||
if (skinURL != null && skinDigest != null)
|
|
||||||
textures.put(MinecraftProfileTexture.Type.SKIN, new MinecraftProfileTexture(skinURL.getValue(), skinDigest.getValue(), skinMetadata == null ? null : deserializeMetadataMap(skinMetadata.getValue())));
|
|
||||||
|
|
||||||
// Add cloak URL to textures map
|
|
||||||
Property cloakURL = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_URL_PROPERTY), null);
|
|
||||||
Property cloakDigest = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_DIGEST_PROPERTY), null);
|
|
||||||
Property cloakMetadata = Iterables.getFirst(profile.getProperties().get(Launcher.CLOAK_METADATA_PROPERTY), null);
|
|
||||||
if (cloakURL != null && cloakDigest != null)
|
|
||||||
textures.put(MinecraftProfileTexture.Type.CAPE, new MinecraftProfileTexture(cloakURL.getValue(), cloakDigest.getValue(), cloakMetadata == null ? null : deserializeMetadataMap(cloakMetadata.getValue())));
|
|
||||||
|
|
||||||
// Try to find missing textures in textures payload (now always true because launcher is not passing elytra skins)
|
|
||||||
if (textures.size() != MinecraftProfileTexture.PROFILE_TEXTURE_COUNT) {
|
|
||||||
Property texturesMojang = Iterables.getFirst(profile.getProperties().get("textures"), null);
|
|
||||||
if (texturesMojang != null)
|
|
||||||
getTexturesMojang(textures, texturesMojang.getValue(), profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return filled textures
|
|
||||||
return textures;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameProfile hasJoinedServer(GameProfile profile, String serverID) throws AuthenticationUnavailableException {
|
|
||||||
String username = profile.getName();
|
|
||||||
if (LogHelper.isDebugEnabled()) {
|
|
||||||
LogHelper.debug("checkServer, Username: '%s', Server ID: %s", username, serverID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make checkServer request
|
|
||||||
PlayerProfile pp;
|
|
||||||
try {
|
|
||||||
pp = new CheckServerRequest(username, serverID).request().playerProfile;
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
throw new AuthenticationUnavailableException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return profile if found
|
|
||||||
return pp == null ? null : toGameProfile(pp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameProfile hasJoinedServer(GameProfile profile, String serverID, InetAddress address) throws AuthenticationUnavailableException {
|
|
||||||
return hasJoinedServer(profile, serverID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public YggdrasilAuthenticationService getAuthenticationService() {
|
|
||||||
return (YggdrasilAuthenticationService) super.getAuthenticationService();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void joinServer(GameProfile profile, String accessToken, String serverID) throws AuthenticationException {
|
|
||||||
|
|
||||||
// Join server
|
|
||||||
String username = profile.getName();
|
|
||||||
if (LogHelper.isDebugEnabled()) {
|
|
||||||
LogHelper.debug("joinServer, Username: '%s', Access token: %s, Server ID: %s", username, accessToken, serverID);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make joinServer request
|
|
||||||
boolean success;
|
|
||||||
try {
|
|
||||||
success = new JoinServerRequest(username, accessToken, serverID).request().allow;
|
|
||||||
} catch (Exception e) {
|
|
||||||
LogHelper.error(e);
|
|
||||||
throw new AuthenticationUnavailableException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify is success
|
|
||||||
if (!success)
|
|
||||||
throw new AuthenticationException("Bad Login (Clientside)");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,6 +27,7 @@ public static JsonElement jsonRequest(JsonElement request, String method, URL ur
|
||||||
if (request != null) connection.setDoOutput(true);
|
if (request != null) connection.setDoOutput(true);
|
||||||
connection.setRequestMethod(method);
|
connection.setRequestMethod(method);
|
||||||
if (request != null) connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
if (request != null) connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
|
||||||
|
if (request != null) connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36");
|
||||||
connection.setRequestProperty("Accept", "application/json");
|
connection.setRequestProperty("Accept", "application/json");
|
||||||
if (TIMEOUT > 0)
|
if (TIMEOUT > 0)
|
||||||
connection.setConnectTimeout(TIMEOUT);
|
connection.setConnectTimeout(TIMEOUT);
|
||||||
|
|
|
@ -6,7 +6,7 @@ public final class Version implements Comparable<Version> {
|
||||||
|
|
||||||
public static final int MAJOR = 5;
|
public static final int MAJOR = 5;
|
||||||
public static final int MINOR = 2;
|
public static final int MINOR = 2;
|
||||||
public static final int PATCH = 8;
|
public static final int PATCH = 9;
|
||||||
public static final int BUILD = 1;
|
public static final int BUILD = 1;
|
||||||
public static final Version.Type RELEASE = Type.STABLE;
|
public static final Version.Type RELEASE = Type.STABLE;
|
||||||
public final int major;
|
public final int major;
|
||||||
|
|
|
@ -39,6 +39,7 @@ public static JsonElement jsonRequest(JsonElement request, String method, URL ur
|
||||||
.uri(url.toURI())
|
.uri(url.toURI())
|
||||||
.header("Content-Type", "application/json; charset=UTF-8")
|
.header("Content-Type", "application/json; charset=UTF-8")
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
|
.header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36")
|
||||||
.timeout(Duration.ofMillis(TIMEOUT))
|
.timeout(Duration.ofMillis(TIMEOUT))
|
||||||
.build();
|
.build();
|
||||||
HttpResponse<InputStream> response = client.send(request1, HttpResponse.BodyHandlers.ofInputStream());
|
HttpResponse<InputStream> response = client.send(request1, HttpResponse.BodyHandlers.ofInputStream());
|
||||||
|
|
|
@ -37,7 +37,7 @@ task javadocJar(type: Jar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
pack project(':LauncherAuthlib')
|
pack project(':LauncherAPI')
|
||||||
pack group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
|
pack group: 'io.netty', name: 'netty-codec-http', version: rootProject['verNetty']
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
|
id 'org.openjfx.javafxplugin' version '0.0.10' apply false
|
||||||
}
|
}
|
||||||
group = 'pro.gravit.launcher'
|
group = 'pro.gravit.launcher'
|
||||||
version = '5.2.8'
|
version = '5.2.9'
|
||||||
|
|
||||||
apply from: 'props.gradle'
|
apply from: 'props.gradle'
|
||||||
|
|
||||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
||||||
Subproject commit 4eb5b32678202efcff2d7cf8f87bdf32eab5384d
|
Subproject commit aea93d6803e4770f23ff4a2ea4b5f8752d053d8b
|
|
@ -3,7 +3,6 @@
|
||||||
include 'Launcher'
|
include 'Launcher'
|
||||||
include 'LauncherCore'
|
include 'LauncherCore'
|
||||||
include 'LauncherAPI'
|
include 'LauncherAPI'
|
||||||
include 'LauncherAuthlib'
|
|
||||||
include 'ServerWrapper'
|
include 'ServerWrapper'
|
||||||
include 'LaunchServer'
|
include 'LaunchServer'
|
||||||
include 'modules'
|
include 'modules'
|
||||||
|
|
Loading…
Reference in a new issue