mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-11-15 11:39:11 +03:00
Merge branch 'release/5.3.2'
This commit is contained in:
commit
dd5ce00c3f
88 changed files with 1029 additions and 1172 deletions
|
@ -17,25 +17,6 @@ public class HttpRequester {
|
||||||
public HttpRequester() {
|
public HttpRequester() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SimpleErrorHandler<T> implements HttpHelper.HttpJsonErrorHandler<T, SimpleError> {
|
|
||||||
private final Type type;
|
|
||||||
|
|
||||||
private SimpleErrorHandler(Type type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpHelper.HttpOptional<T, SimpleError> applyJson(JsonElement response, int statusCode) {
|
|
||||||
if(statusCode < 200 || statusCode >= 300) {
|
|
||||||
return new HttpHelper.HttpOptional<>(null, Launcher.gsonManager.gson.fromJson(response, SimpleError.class), statusCode);
|
|
||||||
}
|
|
||||||
if(type == Void.class) {
|
|
||||||
return new HttpHelper.HttpOptional<>(null, null, statusCode);
|
|
||||||
}
|
|
||||||
return new HttpHelper.HttpOptional<>(Launcher.gsonManager.gson.fromJson(response, type), null, statusCode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> SimpleErrorHandler<T> makeEH(Class<T> clazz) {
|
public <T> SimpleErrorHandler<T> makeEH(Class<T> clazz) {
|
||||||
return new SimpleErrorHandler<>(clazz);
|
return new SimpleErrorHandler<>(clazz);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +29,7 @@ public <T> HttpRequest get(String url, String token) {
|
||||||
.header("Content-Type", "application/json; charset=UTF-8")
|
.header("Content-Type", "application/json; charset=UTF-8")
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.timeout(Duration.ofMillis(10000));
|
.timeout(Duration.ofMillis(10000));
|
||||||
if(token != null) {
|
if (token != null) {
|
||||||
requestBuilder.header("Authorization", "Bearer ".concat(token));
|
requestBuilder.header("Authorization", "Bearer ".concat(token));
|
||||||
}
|
}
|
||||||
return requestBuilder.build();
|
return requestBuilder.build();
|
||||||
|
@ -65,7 +46,7 @@ public <T> HttpRequest post(String url, T request, String token) {
|
||||||
.header("Content-Type", "application/json; charset=UTF-8")
|
.header("Content-Type", "application/json; charset=UTF-8")
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.timeout(Duration.ofMillis(10000));
|
.timeout(Duration.ofMillis(10000));
|
||||||
if(token != null) {
|
if (token != null) {
|
||||||
requestBuilder.header("Authorization", "Bearer ".concat(token));
|
requestBuilder.header("Authorization", "Bearer ".concat(token));
|
||||||
}
|
}
|
||||||
return requestBuilder.build();
|
return requestBuilder.build();
|
||||||
|
@ -78,6 +59,26 @@ public <T> HttpHelper.HttpOptional<T, SimpleError> send(HttpRequest request, Cla
|
||||||
return HttpHelper.send(httpClient, request, makeEH(clazz));
|
return HttpHelper.send(httpClient, request, makeEH(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class SimpleErrorHandler<T> implements HttpHelper.HttpJsonErrorHandler<T, SimpleError> {
|
||||||
|
private final Type type;
|
||||||
|
|
||||||
|
private SimpleErrorHandler(Type type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpHelper.HttpOptional<T, SimpleError> applyJson(JsonElement response, int statusCode) {
|
||||||
|
if (statusCode < 200 || statusCode >= 300) {
|
||||||
|
return new HttpHelper.HttpOptional<>(null, Launcher.gsonManager.gson.fromJson(response, SimpleError.class), statusCode);
|
||||||
|
}
|
||||||
|
if (type == Void.class) {
|
||||||
|
return new HttpHelper.HttpOptional<>(null, null, statusCode);
|
||||||
|
}
|
||||||
|
return new HttpHelper.HttpOptional<>(Launcher.gsonManager.gson.fromJson(response, type), null, statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class SimpleError {
|
public static class SimpleError {
|
||||||
public String error;
|
public String error;
|
||||||
public int code;
|
public int code;
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
import pro.gravit.launcher.managers.ConfigManager;
|
import pro.gravit.launcher.managers.ConfigManager;
|
||||||
import pro.gravit.launcher.modules.events.ClosePhase;
|
import pro.gravit.launcher.modules.events.ClosePhase;
|
||||||
import pro.gravit.launcher.profiles.ClientProfile;
|
import pro.gravit.launcher.profiles.ClientProfile;
|
||||||
import pro.gravit.launcher.request.Request;
|
|
||||||
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.binary.EXEL4JLauncherBinary;
|
import pro.gravit.launchserver.binary.EXEL4JLauncherBinary;
|
||||||
|
@ -68,6 +67,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
||||||
* The path to the folder with compile-only libraries for the launcher
|
* The path to the folder with compile-only libraries for the launcher
|
||||||
*/
|
*/
|
||||||
public final Path launcherLibrariesCompile;
|
public final Path launcherLibrariesCompile;
|
||||||
|
public final Path launcherPack;
|
||||||
/**
|
/**
|
||||||
* The path to the folder with updates/webroot
|
* The path to the folder with updates/webroot
|
||||||
*/
|
*/
|
||||||
|
@ -138,6 +138,10 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
||||||
this.service = Executors.newScheduledThreadPool(config.netty.performance.schedulerThread);
|
this.service = Executors.newScheduledThreadPool(config.netty.performance.schedulerThread);
|
||||||
launcherLibraries = directories.launcherLibrariesDir;
|
launcherLibraries = directories.launcherLibrariesDir;
|
||||||
launcherLibrariesCompile = directories.launcherLibrariesCompileDir;
|
launcherLibrariesCompile = directories.launcherLibrariesCompileDir;
|
||||||
|
launcherPack = directories.launcherPackDir;
|
||||||
|
if(!Files.isDirectory(launcherPack)) {
|
||||||
|
Files.createDirectories(launcherPack);
|
||||||
|
}
|
||||||
|
|
||||||
config.setLaunchServer(this);
|
config.setLaunchServer(this);
|
||||||
|
|
||||||
|
@ -379,18 +383,18 @@ public void syncProfilesDir() throws IOException {
|
||||||
// Sort and set new profiles
|
// Sort and set new profiles
|
||||||
newProfies.sort(Comparator.comparing(a -> a));
|
newProfies.sort(Comparator.comparing(a -> a));
|
||||||
profilesList = Set.copyOf(newProfies);
|
profilesList = Set.copyOf(newProfies);
|
||||||
if(config.netty.sendProfileUpdatesEvent) {
|
if (config.netty.sendProfileUpdatesEvent) {
|
||||||
sendUpdateProfilesEvent();
|
sendUpdateProfilesEvent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendUpdateProfilesEvent() {
|
private void sendUpdateProfilesEvent() {
|
||||||
if(nettyServerSocketHandler == null || nettyServerSocketHandler.nettyServer == null || nettyServerSocketHandler.nettyServer.service == null) {
|
if (nettyServerSocketHandler == null || nettyServerSocketHandler.nettyServer == null || nettyServerSocketHandler.nettyServer.service == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nettyServerSocketHandler.nettyServer.service.forEachActiveChannels((ch, handler) -> {
|
nettyServerSocketHandler.nettyServer.service.forEachActiveChannels((ch, handler) -> {
|
||||||
Client client = handler.getClient();
|
Client client = handler.getClient();
|
||||||
if(client == null || !client.isAuth) {
|
if (client == null || !client.isAuth) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProfilesRequestEvent event = new ProfilesRequestEvent(ProfilesResponse.getListVisibleProfiles(this, client));
|
ProfilesRequestEvent event = new ProfilesRequestEvent(ProfilesResponse.getListVisibleProfiles(this, client));
|
||||||
|
@ -487,11 +491,12 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
||||||
public static class LaunchServerDirectories {
|
public static class LaunchServerDirectories {
|
||||||
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
|
public static final String UPDATES_NAME = "updates", PROFILES_NAME = "profiles",
|
||||||
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
TRUSTSTORE_NAME = "truststore", LAUNCHERLIBRARIES_NAME = "launcher-libraries",
|
||||||
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", KEY_NAME = ".keys";
|
LAUNCHERLIBRARIESCOMPILE_NAME = "launcher-libraries-compile", LAUNCHERPACK_NAME = "launcher-pack", KEY_NAME = ".keys";
|
||||||
public Path updatesDir;
|
public Path updatesDir;
|
||||||
public Path profilesDir;
|
public Path profilesDir;
|
||||||
public Path launcherLibrariesDir;
|
public Path launcherLibrariesDir;
|
||||||
public Path launcherLibrariesCompileDir;
|
public Path launcherLibrariesCompileDir;
|
||||||
|
public Path launcherPackDir;
|
||||||
public Path keyDirectory;
|
public Path keyDirectory;
|
||||||
public Path dir;
|
public Path dir;
|
||||||
public Path trustStore;
|
public Path trustStore;
|
||||||
|
@ -504,6 +509,8 @@ public void collect() {
|
||||||
if (launcherLibrariesDir == null) launcherLibrariesDir = getPath(LAUNCHERLIBRARIES_NAME);
|
if (launcherLibrariesDir == null) launcherLibrariesDir = getPath(LAUNCHERLIBRARIES_NAME);
|
||||||
if (launcherLibrariesCompileDir == null)
|
if (launcherLibrariesCompileDir == null)
|
||||||
launcherLibrariesCompileDir = getPath(LAUNCHERLIBRARIESCOMPILE_NAME);
|
launcherLibrariesCompileDir = getPath(LAUNCHERLIBRARIESCOMPILE_NAME);
|
||||||
|
if(launcherPackDir == null)
|
||||||
|
launcherPackDir = getPath(LAUNCHERPACK_NAME);
|
||||||
if (keyDirectory == null) keyDirectory = getPath(KEY_NAME);
|
if (keyDirectory == null) keyDirectory = getPath(KEY_NAME);
|
||||||
if (tmpDir == null)
|
if (tmpDir == null)
|
||||||
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve(String.format("launchserver-%s", SecurityHelper.randomStringToken()));
|
tmpDir = Paths.get(System.getProperty("java.io.tmpdir")).resolve(String.format("launchserver-%s", SecurityHelper.randomStringToken()));
|
||||||
|
|
|
@ -67,7 +67,7 @@ private static void visit(ClassNode classNode, Map<String, Object> values) {
|
||||||
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
|
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
|
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
|
||||||
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
|
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
|
||||||
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
|
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
|
||||||
classNode.fields.forEach(field -> {
|
classNode.fields.forEach(field -> {
|
||||||
// Notice that fields that will be used with this algo should not have default
|
// Notice that fields that will be used with this algo should not have default
|
||||||
|
|
|
@ -38,15 +38,6 @@ public static Set<String> getFeatures(Class<?> clazz) {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void internalShowOAuthWarnMessage() {
|
|
||||||
if(!warnOAuthShow) {
|
|
||||||
if(!(core instanceof MySQLCoreProvider) && !(core instanceof PostgresSQLCoreProvider)) { // MySQL and PostgreSQL upgraded later
|
|
||||||
logger.warn("AuthCoreProvider {} ({}) not supported OAuth. Legacy session system may be removed in next release", name, core.getClass().getName());
|
|
||||||
}
|
|
||||||
warnOAuthShow = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void getFeatures(Class<?> clazz, Set<String> list) {
|
public static void getFeatures(Class<?> clazz, Set<String> list) {
|
||||||
Features features = clazz.getAnnotation(Features.class);
|
Features features = clazz.getAnnotation(Features.class);
|
||||||
if (features != null) {
|
if (features != null) {
|
||||||
|
@ -64,6 +55,15 @@ public static void getFeatures(Class<?> clazz, Set<String> list) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void internalShowOAuthWarnMessage() {
|
||||||
|
if (!warnOAuthShow) {
|
||||||
|
if (!(core instanceof MySQLCoreProvider) && !(core instanceof PostgresSQLCoreProvider)) { // MySQL and PostgreSQL upgraded later
|
||||||
|
logger.warn("AuthCoreProvider {} ({}) not supported OAuth. Legacy session system may be removed in next release", name, core.getClass().getName());
|
||||||
|
}
|
||||||
|
warnOAuthShow = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final <T> T isSupport(Class<T> clazz) {
|
public final <T> T isSupport(Class<T> clazz) {
|
||||||
if (core == null) return null;
|
if (core == null) return null;
|
||||||
T result = null;
|
T result = null;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public final class MySQLSourceConfig implements AutoCloseable {
|
public final class MySQLSourceConfig implements AutoCloseable, SQLSourceConfig {
|
||||||
|
|
||||||
public static final int TIMEOUT = VerifyHelper.verifyInt(
|
public static final int TIMEOUT = VerifyHelper.verifyInt(
|
||||||
Integer.parseUnsignedInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))),
|
Integer.parseUnsignedInt(System.getProperty("launcher.mysql.idleTimeout", Integer.toString(5000))),
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
public final class PostgreSQLSourceConfig implements AutoCloseable {
|
public final class PostgreSQLSourceConfig implements AutoCloseable, SQLSourceConfig {
|
||||||
public static final int TIMEOUT = VerifyHelper.verifyInt(
|
public static final int TIMEOUT = VerifyHelper.verifyInt(
|
||||||
Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.idleTimeout", Integer.toString(5000))),
|
Integer.parseUnsignedInt(System.getProperty("launcher.postgresql.idleTimeout", Integer.toString(5000))),
|
||||||
VerifyHelper.POSITIVE, "launcher.postgresql.idleTimeout can't be <= 5000");
|
VerifyHelper.POSITIVE, "launcher.postgresql.idleTimeout can't be <= 5000");
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package pro.gravit.launchserver.auth;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
public interface SQLSourceConfig {
|
||||||
|
Connection getConnection() throws SQLException;
|
||||||
|
|
||||||
|
void close();
|
||||||
|
}
|
|
@ -0,0 +1,373 @@
|
||||||
|
package pro.gravit.launchserver.auth.core;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.ExpiredJwtException;
|
||||||
|
import io.jsonwebtoken.JwtException;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
||||||
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
|
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||||
|
import pro.gravit.launchserver.auth.SQLSourceConfig;
|
||||||
|
import pro.gravit.launchserver.auth.password.PasswordVerifier;
|
||||||
|
import pro.gravit.launchserver.helper.LegacySessionHelper;
|
||||||
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.time.Clock;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public abstract class AbstractSQLCoreProvider extends AuthCoreProvider {
|
||||||
|
public transient Logger logger = LogManager.getLogger();
|
||||||
|
public int expireSeconds = 3600;
|
||||||
|
public String uuidColumn;
|
||||||
|
public String usernameColumn;
|
||||||
|
public String accessTokenColumn;
|
||||||
|
public String passwordColumn;
|
||||||
|
public String serverIDColumn;
|
||||||
|
public String table;
|
||||||
|
public String permissionsTable;
|
||||||
|
public String permissionsPermissionColumn;
|
||||||
|
public String permissionsUUIDColumn;
|
||||||
|
|
||||||
|
public String rolesTable;
|
||||||
|
public String rolesNameColumn;
|
||||||
|
public String rolesUUIDColumn;
|
||||||
|
|
||||||
|
public PasswordVerifier passwordVerifier;
|
||||||
|
public String customQueryByUUIDSQL;
|
||||||
|
public String customQueryByUsernameSQL;
|
||||||
|
public String customQueryByLoginSQL;
|
||||||
|
public String customQueryPermissionsByUUIDSQL;
|
||||||
|
public String customQueryRolesByUserUUID;
|
||||||
|
public String customUpdateAuthSQL;
|
||||||
|
public String customUpdateServerIdSQL;
|
||||||
|
// Prepared SQL queries
|
||||||
|
public transient String queryByUUIDSQL;
|
||||||
|
public transient String queryByUsernameSQL;
|
||||||
|
public transient String queryByLoginSQL;
|
||||||
|
public transient String queryPermissionsByUUIDSQL;
|
||||||
|
public transient String queryRolesByUserUUID;
|
||||||
|
|
||||||
|
public transient String updateAuthSQL;
|
||||||
|
public transient String updateServerIDSQL;
|
||||||
|
public transient LaunchServer server;
|
||||||
|
|
||||||
|
public abstract SQLSourceConfig getSQLConfig();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByUsername(String username) {
|
||||||
|
try {
|
||||||
|
return queryUser(queryByUsernameSQL, username);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("SQL error", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByUUID(UUID uuid) {
|
||||||
|
try {
|
||||||
|
return queryUser(queryByUUIDSQL, uuid.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("SQL error", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUserByLogin(String login) {
|
||||||
|
try {
|
||||||
|
return queryUser(queryByLoginSQL, login);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("SQL error", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||||
|
try {
|
||||||
|
var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey);
|
||||||
|
var user = (SQLUser) getUserByUUID(info.uuid());
|
||||||
|
if (user == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SQLUserSession(user);
|
||||||
|
} catch (ExpiredJwtException e) {
|
||||||
|
throw new OAuthAccessTokenExpired();
|
||||||
|
} catch (JwtException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
||||||
|
String[] parts = refreshToken.split("\\.");
|
||||||
|
if (parts.length != 2) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String username = parts[0];
|
||||||
|
String token = parts[1];
|
||||||
|
var user = (SQLUser) 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 SQLUserSession(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
||||||
|
SQLUser SQLUser = (SQLUser) getUserByLogin(login);
|
||||||
|
if (SQLUser == null) {
|
||||||
|
throw AuthException.wrongPassword();
|
||||||
|
}
|
||||||
|
if (context != null) {
|
||||||
|
AuthPlainPassword plainPassword = (AuthPlainPassword) password;
|
||||||
|
if (plainPassword == null) {
|
||||||
|
throw AuthException.wrongPassword();
|
||||||
|
}
|
||||||
|
if (!passwordVerifier.check(SQLUser.password, plainPassword.password)) {
|
||||||
|
throw AuthException.wrongPassword();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SQLUserSession session = new SQLUserSession(SQLUser);
|
||||||
|
var accessToken = LegacySessionHelper.makeAccessJwtTokenFromString(SQLUser, LocalDateTime.now(Clock.systemUTC()).plusSeconds(expireSeconds), server.keyAgreementManager.ecdsaPrivateKey);
|
||||||
|
var refreshToken = SQLUser.username.concat(".").concat(LegacySessionHelper.makeRefreshTokenFromPassword(SQLUser.username, SQLUser.password, server.keyAgreementManager.legacySalt));
|
||||||
|
if (minecraftAccess) {
|
||||||
|
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
||||||
|
updateAuth(SQLUser, minecraftAccessToken);
|
||||||
|
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||||
|
} else {
|
||||||
|
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(LaunchServer server) {
|
||||||
|
this.server = server;
|
||||||
|
if (getSQLConfig() == null) logger.error("SQLHolder cannot be null");
|
||||||
|
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
||||||
|
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
||||||
|
if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null");
|
||||||
|
if (serverIDColumn == null) logger.error("serverIDColumn cannot be null");
|
||||||
|
if (table == null) logger.error("table cannot be null");
|
||||||
|
// Prepare SQL queries
|
||||||
|
String userInfoCols = String.format("%s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn);
|
||||||
|
queryByUUIDSQL = customQueryByUUIDSQL != null ? customQueryByUUIDSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", userInfoCols,
|
||||||
|
table, uuidColumn);
|
||||||
|
queryByUsernameSQL = customQueryByUsernameSQL != null ? customQueryByUsernameSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1",
|
||||||
|
userInfoCols, table, usernameColumn);
|
||||||
|
queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=?",
|
||||||
|
table, accessTokenColumn, serverIDColumn, uuidColumn);
|
||||||
|
updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=?",
|
||||||
|
table, serverIDColumn, uuidColumn);
|
||||||
|
if (isEnabledPermissions()) {
|
||||||
|
if(isEnabledRoles()) {
|
||||||
|
queryPermissionsByUUIDSQL = customQueryPermissionsByUUIDSQL != null ? customQueryPermissionsByUUIDSQL :
|
||||||
|
"WITH RECURSIVE req AS (\n" +
|
||||||
|
"SELECT p."+permissionsPermissionColumn+" FROM "+permissionsTable+" p WHERE p."+permissionsUUIDColumn+" = ?\n" +
|
||||||
|
"UNION ALL\n" +
|
||||||
|
"SELECT p."+permissionsPermissionColumn+" FROM "+permissionsTable+" p\n" +
|
||||||
|
"INNER JOIN "+rolesTable+" r ON p."+permissionsUUIDColumn+" = r."+rolesUUIDColumn+"\n" +
|
||||||
|
"INNER JOIN req ON r."+rolesUUIDColumn+"=substring(req."+permissionsPermissionColumn+" from 6) or r.name=substring(req."+permissionsPermissionColumn+" from 6)\n" +
|
||||||
|
") SELECT * FROM req";
|
||||||
|
queryRolesByUserUUID = customQueryRolesByUserUUID != null ? customQueryRolesByUserUUID : "SELECT r." + rolesNameColumn + " FROM " + rolesTable + " r\n" +
|
||||||
|
"INNER JOIN " + permissionsTable + " pr ON r." + rolesUUIDColumn + "=substring(pr." + permissionsPermissionColumn + " from 6) or r." + rolesNameColumn + "=substring(pr." + permissionsPermissionColumn + " from 6)\n" +
|
||||||
|
"WHERE pr." + permissionsUUIDColumn + " = ?";
|
||||||
|
} else {
|
||||||
|
queryPermissionsByUUIDSQL = customQueryPermissionsByUUIDSQL != null ? customQueryPermissionsByUUIDSQL : String.format("SELECT (%s) FROM %s WHERE %s=?",
|
||||||
|
permissionsPermissionColumn, permissionsTable, permissionsUUIDColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
||||||
|
try (Connection c = getSQLConfig().getConnection()) {
|
||||||
|
SQLUser SQLUser = (SQLUser) user;
|
||||||
|
SQLUser.accessToken = accessToken;
|
||||||
|
PreparedStatement s = c.prepareStatement(updateAuthSQL);
|
||||||
|
s.setString(1, accessToken);
|
||||||
|
s.setString(2, user.getUUID().toString());
|
||||||
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
|
return s.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean updateServerID(User user, String serverID) throws IOException {
|
||||||
|
try (Connection c = getSQLConfig().getConnection()) {
|
||||||
|
SQLUser SQLUser = (SQLUser) user;
|
||||||
|
SQLUser.serverId = serverID;
|
||||||
|
PreparedStatement s = c.prepareStatement(updateServerIDSQL);
|
||||||
|
s.setString(1, serverID);
|
||||||
|
s.setString(2, user.getUUID().toString());
|
||||||
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
|
return s.executeUpdate() > 0;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
getSQLConfig().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SQLUser constructUser(ResultSet set) throws SQLException {
|
||||||
|
return set.next() ? new SQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
||||||
|
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), requestPermissions(set.getString(uuidColumn))) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClientPermissions requestPermissions (String uuid) throws SQLException
|
||||||
|
{
|
||||||
|
return new ClientPermissions(isEnabledRoles() ? queryRolesNames(queryRolesByUserUUID,uuid) : new ArrayList<>(),
|
||||||
|
isEnabledPermissions() ? queryPermissions(queryPermissionsByUUIDSQL,uuid) : new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private SQLUser queryUser(String sql, String value) throws SQLException {
|
||||||
|
try (Connection c = getSQLConfig().getConnection()) {
|
||||||
|
PreparedStatement s = c.prepareStatement(sql);
|
||||||
|
s.setString(1, value);
|
||||||
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
|
try (ResultSet set = s.executeQuery()) {
|
||||||
|
return constructUser(set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> queryPermissions(String sql, String value) throws SQLException {
|
||||||
|
try (Connection c = getSQLConfig().getConnection()) {
|
||||||
|
PreparedStatement s = c.prepareStatement(sql);
|
||||||
|
s.setString(1, value);
|
||||||
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
|
ResultSet set = s.executeQuery();
|
||||||
|
List<String> perms = new ArrayList<>();
|
||||||
|
while (set.next())
|
||||||
|
perms.add(set.getString(permissionsPermissionColumn));
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabledPermissions() {
|
||||||
|
return permissionsPermissionColumn != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabledRoles() {
|
||||||
|
return rolesNameColumn != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> queryRolesNames(String sql, String value) throws SQLException {
|
||||||
|
try (Connection c = getSQLConfig().getConnection()) {
|
||||||
|
PreparedStatement s = c.prepareStatement(sql);
|
||||||
|
s.setString(1, value);
|
||||||
|
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
||||||
|
ResultSet set = s.executeQuery();
|
||||||
|
List<String> perms = new ArrayList<>();
|
||||||
|
while (set.next())
|
||||||
|
perms.add(set.getString(rolesNameColumn));
|
||||||
|
return perms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SQLUser implements User {
|
||||||
|
protected UUID uuid;
|
||||||
|
protected String username;
|
||||||
|
protected String accessToken;
|
||||||
|
protected String serverId;
|
||||||
|
protected String password;
|
||||||
|
protected ClientPermissions permissions;
|
||||||
|
|
||||||
|
public SQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.username = username;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.password = password;
|
||||||
|
this.permissions = permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServerId() {
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientPermissions getPermissions() {
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "SQLUser{" +
|
||||||
|
"uuid=" + uuid +
|
||||||
|
", username='" + username + '\'' +
|
||||||
|
", permissions=" + permissions +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SQLUserSession implements UserSession {
|
||||||
|
private final SQLUser user;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public SQLUserSession(SQLUser user) {
|
||||||
|
this.user = user;
|
||||||
|
this.id = user.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getID() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getExpireIn() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -91,7 +91,7 @@ public Map<String, Command> getCommands() {
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
verifyArgs(args, 1);
|
verifyArgs(args, 1);
|
||||||
AuthRequest.AuthPasswordInterface password = null;
|
AuthRequest.AuthPasswordInterface password = null;
|
||||||
if(args.length > 1) {
|
if (args.length > 1) {
|
||||||
if (args[1].startsWith("{")) {
|
if (args[1].startsWith("{")) {
|
||||||
password = Launcher.gsonManager.gson.fromJson(args[1], AuthRequest.AuthPasswordInterface.class);
|
password = Launcher.gsonManager.gson.fromJson(args[1], AuthRequest.AuthPasswordInterface.class);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package pro.gravit.launchserver.auth.core;
|
package pro.gravit.launchserver.auth.core;
|
||||||
|
|
||||||
|
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.ClientPermissions;
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
|
@ -28,7 +29,6 @@
|
||||||
|
|
||||||
public class HttpAuthCoreProvider extends AuthCoreProvider implements AuthSupportHardware, AuthSupportRemoteClientAccess {
|
public class HttpAuthCoreProvider extends AuthCoreProvider implements AuthSupportHardware, AuthSupportRemoteClientAccess {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
private transient HttpRequester requester;
|
|
||||||
public String bearerToken;
|
public String bearerToken;
|
||||||
public String getUserByUsernameUrl;
|
public String getUserByUsernameUrl;
|
||||||
public String getUserByLoginUrl;
|
public String getUserByLoginUrl;
|
||||||
|
@ -52,6 +52,7 @@ public class HttpAuthCoreProvider extends AuthCoreProvider implements AuthSuppor
|
||||||
public String unbanHardwareUrl;
|
public String unbanHardwareUrl;
|
||||||
public String apiUrl;
|
public String apiUrl;
|
||||||
public List<String> apiFeatures;
|
public List<String> apiFeatures;
|
||||||
|
private transient HttpRequester requester;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public User getUserByUsername(String username) {
|
||||||
|
@ -65,7 +66,7 @@ public User getUserByUsername(String username) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByLogin(String login) {
|
public User getUserByLogin(String login) {
|
||||||
if(getUserByLoginUrl != null) {
|
if (getUserByLoginUrl != null) {
|
||||||
try {
|
try {
|
||||||
return requester.send(requester.get(CommonHelper.replace(getUserByLoginUrl, "login", login), null), HttpUser.class).getOrThrow();
|
return requester.send(requester.get(CommonHelper.replace(getUserByLoginUrl, "login", login), null), HttpUser.class).getOrThrow();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -88,7 +89,7 @@ public User getUserByUUID(UUID uuid) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||||
if(getAuthDetailsUrl == null) {
|
if (getAuthDetailsUrl == null) {
|
||||||
return super.getDetails(client);
|
return super.getDetails(client);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -102,14 +103,14 @@ public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||||
if(getUserByTokenUrl == null) {
|
if (getUserByTokenUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
var result = requester.send(requester.get(getUserByTokenUrl, accessToken), HttpUserSession.class);
|
var result = requester.send(requester.get(getUserByTokenUrl, accessToken), HttpUserSession.class);
|
||||||
if(!result.isSuccessful()) {
|
if (!result.isSuccessful()) {
|
||||||
var error = result.error().error;
|
var error = result.error().error;
|
||||||
if(error.equals(AuthRequestEvent.OAUTH_TOKEN_EXPIRE)) {
|
if (error.equals(AuthRequestEvent.OAUTH_TOKEN_EXPIRE)) {
|
||||||
throw new OAuthAccessTokenExpired();
|
throw new OAuthAccessTokenExpired();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -123,7 +124,7 @@ public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws O
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
||||||
if(refreshTokenUrl == null) {
|
if (refreshTokenUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -139,9 +140,9 @@ public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthRespon
|
||||||
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
||||||
var result = requester.send(requester.post(authorizeUrl, new AuthorizeRequest(login, context, password, minecraftAccess),
|
var result = requester.send(requester.post(authorizeUrl, new AuthorizeRequest(login, context, password, minecraftAccess),
|
||||||
bearerToken), HttpAuthReport.class);
|
bearerToken), HttpAuthReport.class);
|
||||||
if(!result.isSuccessful()) {
|
if (!result.isSuccessful()) {
|
||||||
var error = result.error().error;
|
var error = result.error().error;
|
||||||
if(error != null) {
|
if (error != null) {
|
||||||
throw new AuthException(error);
|
throw new AuthException(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +151,7 @@ public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext c
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
||||||
if(getHardwareInfoByPublicKeyUrl == null) {
|
if (getHardwareInfoByPublicKeyUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -164,7 +165,7 @@ public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
|
public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo info) {
|
||||||
if(getHardwareInfoByDataUrl == null) {
|
if (getHardwareInfoByDataUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -182,7 +183,7 @@ public UserHardware getHardwareInfoByData(HardwareReportRequest.HardwareInfo inf
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserHardware getHardwareInfoById(String id) {
|
public UserHardware getHardwareInfoById(String id) {
|
||||||
if(getHardwareInfoByIdUrl == null) {
|
if (getHardwareInfoByIdUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -196,7 +197,7 @@ public UserHardware getHardwareInfoById(String id) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey) {
|
public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey) {
|
||||||
if(createHardwareInfoUrl == null) {
|
if (createHardwareInfoUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -210,7 +211,7 @@ public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
|
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
|
||||||
if(connectUserAndHardwareUrl == null) {
|
if (connectUserAndHardwareUrl == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -222,24 +223,25 @@ public void connectUserAndHardware(UserSession userSession, UserHardware hardwar
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
|
public void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey) {
|
||||||
if(addPublicKeyToHardwareInfoUrl == null) {
|
if (addPublicKeyToHardwareInfoUrl == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
requester.send(requester.post(addPublicKeyToHardwareInfoUrl, new HardwareRequest((HttpUserHardware)hardware, publicKey), bearerToken), Void.class);
|
requester.send(requester.post(addPublicKeyToHardwareInfoUrl, new HardwareRequest((HttpUserHardware) hardware, publicKey), bearerToken), Void.class);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
@Override
|
@Override
|
||||||
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
|
public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
|
||||||
if(getUsersByHardwareInfoUrl == null) {
|
if (getUsersByHardwareInfoUrl == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return requester.send(requester
|
return (List<User>) (List) requester.send(requester
|
||||||
.post(getUsersByHardwareInfoUrl, new HardwareRequest((HttpUserHardware) hardware), bearerToken), List.class).getOrThrow();
|
.post(getUsersByHardwareInfoUrl, new HardwareRequest((HttpUserHardware) hardware), bearerToken), GetHardwareListResponse.class).getOrThrow().list;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
return null;
|
return null;
|
||||||
|
@ -248,7 +250,7 @@ public Iterable<User> getUsersByHardwareInfo(UserHardware hardware) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void banHardware(UserHardware hardware) {
|
public void banHardware(UserHardware hardware) {
|
||||||
if(banHardwareUrl == null) {
|
if (banHardwareUrl == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -260,7 +262,7 @@ public void banHardware(UserHardware hardware) {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unbanHardware(UserHardware hardware) {
|
public void unbanHardware(UserHardware hardware) {
|
||||||
if(unbanHardwareUrl == null) {
|
if (unbanHardwareUrl == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -280,14 +282,6 @@ public List<String> getClientApiFeatures() {
|
||||||
return apiFeatures;
|
return apiFeatures;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record HttpAuthReport(String minecraftAccessToken, String oauthAccessToken,
|
|
||||||
String oauthRefreshToken, long oauthExpire,
|
|
||||||
HttpUserSession session) {
|
|
||||||
public AuthManager.AuthReport toAuthReport() {
|
|
||||||
return new AuthManager.AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean updateServerID(User user, String serverID) throws IOException {
|
protected boolean updateServerID(User user, String serverID) throws IOException {
|
||||||
var result = requester.send(requester.post(updateServerIdUrl, new UpdateServerIdRequest(user.getUsername(), user.getUUID(), serverID),
|
var result = requester.send(requester.post(updateServerIdUrl, new UpdateServerIdRequest(user.getUsername(), user.getUUID(), serverID),
|
||||||
|
@ -306,6 +300,36 @@ public boolean joinServer(Client client, String username, String accessToken, St
|
||||||
return result.isSuccessful();
|
return result.isSuccessful();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(LaunchServer server) {
|
||||||
|
requester = new HttpRequester();
|
||||||
|
if (getUserByUsernameUrl == null) {
|
||||||
|
throw new IllegalArgumentException("'getUserByUsernameUrl' can't be null");
|
||||||
|
}
|
||||||
|
if (getUserByUUIDUrl == null) {
|
||||||
|
throw new IllegalArgumentException("'getUserByUUIDUrl' can't be null");
|
||||||
|
}
|
||||||
|
if (authorizeUrl == null) {
|
||||||
|
throw new IllegalArgumentException("'authorizeUrl' can't be null");
|
||||||
|
}
|
||||||
|
if (checkServerUrl == null && joinServerUrl == null && updateServerIdUrl == null) {
|
||||||
|
throw new IllegalArgumentException("Please set 'checkServerUrl' and 'joinServerUrl' or 'updateServerIdUrl'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public record HttpAuthReport(String minecraftAccessToken, String oauthAccessToken,
|
||||||
|
String oauthRefreshToken, long oauthExpire,
|
||||||
|
HttpUserSession session) {
|
||||||
|
public AuthManager.AuthReport toAuthReport() {
|
||||||
|
return new AuthManager.AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class UpdateServerIdRequest {
|
public static class UpdateServerIdRequest {
|
||||||
public String username;
|
public String username;
|
||||||
public UUID uuid;
|
public UUID uuid;
|
||||||
|
@ -332,6 +356,10 @@ public static class GetAuthDetailsResponse {
|
||||||
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> details;
|
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> details;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class GetHardwareListResponse {
|
||||||
|
public List<HttpUser> list;
|
||||||
|
}
|
||||||
|
|
||||||
public static class JoinServerRequest {
|
public static class JoinServerRequest {
|
||||||
public String username;
|
public String username;
|
||||||
public String accessToken;
|
public String accessToken;
|
||||||
|
@ -344,28 +372,6 @@ public JoinServerRequest(String username, String accessToken, String serverId) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(LaunchServer server) {
|
|
||||||
requester = new HttpRequester();
|
|
||||||
if(getUserByUsernameUrl == null) {
|
|
||||||
throw new IllegalArgumentException("'getUserByUsernameUrl' can't be null");
|
|
||||||
}
|
|
||||||
if(getUserByUUIDUrl == null) {
|
|
||||||
throw new IllegalArgumentException("'getUserByUUIDUrl' can't be null");
|
|
||||||
}
|
|
||||||
if(authorizeUrl == null) {
|
|
||||||
throw new IllegalArgumentException("'authorizeUrl' can't be null");
|
|
||||||
}
|
|
||||||
if(checkServerUrl == null && joinServerUrl == null && updateServerIdUrl == null) {
|
|
||||||
throw new IllegalArgumentException("Please set 'checkServerUrl' and 'joinServerUrl' or 'updateServerIdUrl'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AuthorizeRequest {
|
public static class AuthorizeRequest {
|
||||||
public String login;
|
public String login;
|
||||||
public AuthResponse.AuthContext context;
|
public AuthResponse.AuthContext context;
|
||||||
|
@ -413,153 +419,6 @@ public HardwareRequest(byte[] key) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HttpUser implements User, UserSupportTextures, UserSupportProperties, UserSupportHardware {
|
|
||||||
private String username;
|
|
||||||
private UUID uuid;
|
|
||||||
private String serverId;
|
|
||||||
private String accessToken;
|
|
||||||
private ClientPermissions permissions;
|
|
||||||
@Deprecated
|
|
||||||
private Texture skin;
|
|
||||||
@Deprecated
|
|
||||||
private Texture cloak;
|
|
||||||
private Map<String, Texture> assets;
|
|
||||||
private Map<String, String> properties;
|
|
||||||
private long hwidId;
|
|
||||||
private transient HttpUserHardware hardware;
|
|
||||||
|
|
||||||
public HttpUser() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, long hwidId) {
|
|
||||||
this.username = username;
|
|
||||||
this.uuid = uuid;
|
|
||||||
this.serverId = serverId;
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.permissions = permissions;
|
|
||||||
this.hwidId = hwidId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, long hwidId) {
|
|
||||||
this.username = username;
|
|
||||||
this.uuid = uuid;
|
|
||||||
this.serverId = serverId;
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.permissions = permissions;
|
|
||||||
this.skin = skin;
|
|
||||||
this.cloak = cloak;
|
|
||||||
this.hwidId = hwidId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, Map<String, String> properties, long hwidId) {
|
|
||||||
this.username = username;
|
|
||||||
this.uuid = uuid;
|
|
||||||
this.serverId = serverId;
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.permissions = permissions;
|
|
||||||
this.skin = skin;
|
|
||||||
this.cloak = cloak;
|
|
||||||
this.properties = properties;
|
|
||||||
this.hwidId = hwidId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Map<String, Texture> assets, Map<String, String> properties, long hwidId) {
|
|
||||||
this.username = username;
|
|
||||||
this.uuid = uuid;
|
|
||||||
this.serverId = serverId;
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.permissions = permissions;
|
|
||||||
this.assets = assets;
|
|
||||||
this.properties = properties;
|
|
||||||
this.hwidId = hwidId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getUUID() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServerId() {
|
|
||||||
return serverId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccessToken() {
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientPermissions getPermissions() {
|
|
||||||
return permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Texture getSkinTexture() {
|
|
||||||
if(assets == null) {
|
|
||||||
return skin;
|
|
||||||
}
|
|
||||||
return assets.get("SKIN");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Texture getCloakTexture() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "HttpUser{" +
|
|
||||||
"username='" + username + '\'' +
|
|
||||||
", uuid=" + uuid +
|
|
||||||
", serverId='" + serverId + '\'' +
|
|
||||||
", accessToken='" + accessToken + '\'' +
|
|
||||||
", permissions=" + permissions +
|
|
||||||
", assets=" + getAssets() +
|
|
||||||
", properties=" + properties +
|
|
||||||
", hwidId=" + hwidId +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserHardware getHardware() {
|
|
||||||
if (hardware != null) return hardware;
|
|
||||||
HttpAuthCoreProvider.HttpUserHardware result = (HttpUserHardware) getHardwareInfoById(String.valueOf(hwidId));
|
|
||||||
hardware = result;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HttpUserSession implements UserSession {
|
public static class HttpUserSession implements UserSession {
|
||||||
private String id;
|
private String id;
|
||||||
private HttpUser user;
|
private HttpUser user;
|
||||||
|
@ -659,4 +518,151 @@ public String toString() {
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class HttpUser implements User, UserSupportTextures, UserSupportProperties, UserSupportHardware {
|
||||||
|
private String username;
|
||||||
|
private UUID uuid;
|
||||||
|
private String serverId;
|
||||||
|
private String accessToken;
|
||||||
|
private ClientPermissions permissions;
|
||||||
|
@Deprecated
|
||||||
|
private Texture skin;
|
||||||
|
@Deprecated
|
||||||
|
private Texture cloak;
|
||||||
|
private Map<String, Texture> assets;
|
||||||
|
private Map<String, String> properties;
|
||||||
|
private long hwidId;
|
||||||
|
private transient HttpUserHardware hardware;
|
||||||
|
|
||||||
|
public HttpUser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, long hwidId) {
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.hwidId = hwidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, long hwidId) {
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.skin = skin;
|
||||||
|
this.cloak = cloak;
|
||||||
|
this.hwidId = hwidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak, Map<String, String> properties, long hwidId) {
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.skin = skin;
|
||||||
|
this.cloak = cloak;
|
||||||
|
this.properties = properties;
|
||||||
|
this.hwidId = hwidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Map<String, Texture> assets, Map<String, String> properties, long hwidId) {
|
||||||
|
this.username = username;
|
||||||
|
this.uuid = uuid;
|
||||||
|
this.serverId = serverId;
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.permissions = permissions;
|
||||||
|
this.assets = assets;
|
||||||
|
this.properties = properties;
|
||||||
|
this.hwidId = hwidId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UUID getUUID() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServerId() {
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClientPermissions getPermissions() {
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Texture getSkinTexture() {
|
||||||
|
if (assets == null) {
|
||||||
|
return skin;
|
||||||
|
}
|
||||||
|
return assets.get("SKIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Texture getCloakTexture() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "HttpUser{" +
|
||||||
|
"username='" + username + '\'' +
|
||||||
|
", uuid=" + uuid +
|
||||||
|
", serverId='" + serverId + '\'' +
|
||||||
|
", accessToken='" + accessToken + '\'' +
|
||||||
|
", permissions=" + permissions +
|
||||||
|
", assets=" + getAssets() +
|
||||||
|
", properties=" + properties +
|
||||||
|
", hwidId=" + hwidId +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserHardware getHardware() {
|
||||||
|
if (hardware != null) return hardware;
|
||||||
|
HttpAuthCoreProvider.HttpUserHardware result = (HttpUserHardware) getHardwareInfoById(String.valueOf(hwidId));
|
||||||
|
hardware = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,15 +13,19 @@
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class MemoryAuthCoreProvider extends AuthCoreProvider {
|
public class MemoryAuthCoreProvider extends AuthCoreProvider {
|
||||||
private transient final List<MemoryUser> memory = new ArrayList<>(16);
|
private transient final List<MemoryUser> memory = new ArrayList<>(16);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public User getUserByUsername(String username) {
|
||||||
synchronized (memory) {
|
synchronized (memory) {
|
||||||
for(MemoryUser u : memory) {
|
for (MemoryUser u : memory) {
|
||||||
if(u.username.equals(username)) {
|
if (u.username.equals(username)) {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,8 +43,8 @@ public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUUID(UUID uuid) {
|
public User getUserByUUID(UUID uuid) {
|
||||||
synchronized (memory) {
|
synchronized (memory) {
|
||||||
for(MemoryUser u : memory) {
|
for (MemoryUser u : memory) {
|
||||||
if(u.uuid.equals(uuid)) {
|
if (u.uuid.equals(uuid)) {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +55,8 @@ public User getUserByUUID(UUID uuid) {
|
||||||
@Override
|
@Override
|
||||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
||||||
synchronized (memory) {
|
synchronized (memory) {
|
||||||
for(MemoryUser u : memory) {
|
for (MemoryUser u : memory) {
|
||||||
if(u.accessToken.equals(accessToken)) {
|
if (u.accessToken.equals(accessToken)) {
|
||||||
return new MemoryUserSession(u);
|
return new MemoryUserSession(u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,23 +71,23 @@ public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthRespon
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
||||||
if(login == null) {
|
if (login == null) {
|
||||||
throw AuthException.userNotFound();
|
throw AuthException.userNotFound();
|
||||||
}
|
}
|
||||||
MemoryUser user = null;
|
MemoryUser user = null;
|
||||||
synchronized (memory) {
|
synchronized (memory) {
|
||||||
for(MemoryUser u : memory) {
|
for (MemoryUser u : memory) {
|
||||||
if(u.username.equals(login)) {
|
if (u.username.equals(login)) {
|
||||||
user = u;
|
user = u;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(user == null) {
|
if (user == null) {
|
||||||
user = new MemoryUser(login);
|
user = new MemoryUser(login);
|
||||||
memory.add(user);
|
memory.add(user);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!minecraftAccess) {
|
if (!minecraftAccess) {
|
||||||
return AuthManager.AuthReport.ofOAuth(user.accessToken, null, 0, new MemoryUserSession(user));
|
return AuthManager.AuthReport.ofOAuth(user.accessToken, null, 0, new MemoryUserSession(user));
|
||||||
} else {
|
} else {
|
||||||
return AuthManager.AuthReport.ofOAuthWithMinecraft(user.accessToken, user.accessToken, null, 0, new MemoryUserSession(user));
|
return AuthManager.AuthReport.ofOAuthWithMinecraft(user.accessToken, user.accessToken, null, 0, new MemoryUserSession(user));
|
||||||
|
@ -100,8 +104,8 @@ protected boolean updateServerID(User user, String serverID) throws IOException
|
||||||
@Override
|
@Override
|
||||||
public User checkServer(Client client, String username, String serverID) throws IOException {
|
public User checkServer(Client client, String username, String serverID) throws IOException {
|
||||||
synchronized (memory) {
|
synchronized (memory) {
|
||||||
for(MemoryUser u : memory) {
|
for (MemoryUser u : memory) {
|
||||||
if(u.username.equals(username)) {
|
if (u.username.equals(username)) {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,59 +1,30 @@
|
||||||
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.Logger;
|
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
|
||||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
|
||||||
import pro.gravit.launcher.request.secure.HardwareReportRequest;
|
import pro.gravit.launcher.request.secure.HardwareReportRequest;
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
|
||||||
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
import pro.gravit.launchserver.auth.MySQLSourceConfig;
|
||||||
|
import pro.gravit.launchserver.auth.SQLSourceConfig;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
|
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
|
||||||
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.helper.LegacySessionHelper;
|
|
||||||
import pro.gravit.launchserver.manangers.AuthManager;
|
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
|
||||||
|
|
||||||
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;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHardware {
|
public class MySQLCoreProvider extends AbstractSQLCoreProvider implements AuthSupportHardware {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
|
||||||
public MySQLSourceConfig mySQLHolder;
|
public MySQLSourceConfig mySQLHolder;
|
||||||
|
|
||||||
public int expireSeconds = 3600;
|
|
||||||
public String uuidColumn;
|
|
||||||
public String usernameColumn;
|
|
||||||
public String accessTokenColumn;
|
|
||||||
public String passwordColumn;
|
|
||||||
public String serverIDColumn;
|
|
||||||
public String hardwareIdColumn;
|
public String hardwareIdColumn;
|
||||||
public String table;
|
|
||||||
|
|
||||||
public String tableHWID = "hwids";
|
public String tableHWID = "hwids";
|
||||||
public String tableHWIDLog = "hwidLog";
|
public String tableHWIDLog = "hwidLog";
|
||||||
public PasswordVerifier passwordVerifier;
|
|
||||||
public double criticalCompareLevel = 1.0;
|
public double criticalCompareLevel = 1.0;
|
||||||
public String customQueryByUUIDSQL;
|
|
||||||
public String customQueryByUsernameSQL;
|
|
||||||
public String customQueryByLoginSQL;
|
|
||||||
public String customUpdateAuthSQL;
|
|
||||||
public String customUpdateServerIdSQL;
|
|
||||||
private transient String sqlFindHardwareByPublicKey;
|
private transient String sqlFindHardwareByPublicKey;
|
||||||
private transient String sqlFindHardwareByData;
|
private transient String sqlFindHardwareByData;
|
||||||
private transient String sqlFindHardwareById;
|
private transient String sqlFindHardwareById;
|
||||||
|
@ -63,130 +34,16 @@ public class MySQLCoreProvider extends AuthCoreProvider implements AuthSupportHa
|
||||||
private transient String sqlUpdateHardwareBanned;
|
private transient String sqlUpdateHardwareBanned;
|
||||||
private transient String sqlUpdateUsers;
|
private transient String sqlUpdateUsers;
|
||||||
private transient String sqlUsersByHwidId;
|
private transient String sqlUsersByHwidId;
|
||||||
// Prepared SQL queries
|
|
||||||
private transient String queryByUUIDSQL;
|
|
||||||
private transient String queryByUsernameSQL;
|
|
||||||
private transient String queryByLoginSQL;
|
|
||||||
private transient String updateAuthSQL;
|
|
||||||
private transient String updateServerIDSQL;
|
|
||||||
|
|
||||||
private transient LaunchServer server;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public SQLSourceConfig getSQLConfig() {
|
||||||
try {
|
return mySQLHolder;
|
||||||
return query(queryByUsernameSQL, username);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("SQL error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getUserByUUID(UUID uuid) {
|
|
||||||
try {
|
|
||||||
return query(queryByUUIDSQL, uuid.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("SQL error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getUserByLogin(String login) {
|
|
||||||
try {
|
|
||||||
return query(queryByLoginSQL, login);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("SQL error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
|
||||||
try {
|
|
||||||
var info = LegacySessionHelper.getJwtInfoFromAccessToken(accessToken, server.keyAgreementManager.ecdsaPublicKey);
|
|
||||||
var user = (MySQLUser) getUserByUUID(info.uuid());
|
|
||||||
if(user == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new MySQLUserSession(user);
|
|
||||||
} catch (ExpiredJwtException e) {
|
|
||||||
throw new OAuthAccessTokenExpired();
|
|
||||||
} catch (JwtException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
|
||||||
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
|
|
||||||
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
|
||||||
MySQLUser mySQLUser = (MySQLUser) getUserByLogin(login);
|
|
||||||
if(mySQLUser == null) {
|
|
||||||
throw AuthException.wrongPassword();
|
|
||||||
}
|
|
||||||
if(context != null) {
|
|
||||||
AuthPlainPassword plainPassword = (AuthPlainPassword) password;
|
|
||||||
if(plainPassword == null) {
|
|
||||||
throw AuthException.wrongPassword();
|
|
||||||
}
|
|
||||||
if(!passwordVerifier.check(mySQLUser.password, plainPassword.password)) {
|
|
||||||
throw AuthException.wrongPassword();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
|
||||||
updateAuth(mySQLUser, minecraftAccessToken);
|
|
||||||
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
|
||||||
} else {
|
|
||||||
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LaunchServer server) {
|
public void init(LaunchServer server) {
|
||||||
this.server = server;
|
super.init(server);
|
||||||
if (mySQLHolder == null) logger.error("mySQLHolder cannot be null");
|
|
||||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
|
||||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
|
||||||
if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null");
|
|
||||||
if (serverIDColumn == null) logger.error("serverIDColumn cannot be null");
|
|
||||||
if (hardwareIdColumn == null) logger.error("hardwareIdColumn cannot be null");
|
|
||||||
if (table == null) logger.error("table cannot be null");
|
|
||||||
// Prepare SQL queries
|
|
||||||
String userInfoCols = String.format("%s, %s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, hardwareIdColumn);
|
String userInfoCols = String.format("%s, %s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn, hardwareIdColumn);
|
||||||
queryByUUIDSQL = customQueryByUUIDSQL != null ? customQueryByUUIDSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", userInfoCols,
|
|
||||||
table, uuidColumn);
|
|
||||||
queryByUsernameSQL = customQueryByUsernameSQL != null ? customQueryByUsernameSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1",
|
|
||||||
userInfoCols, table, usernameColumn);
|
|
||||||
queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL;
|
|
||||||
|
|
||||||
updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=?",
|
|
||||||
table, accessTokenColumn, serverIDColumn, uuidColumn);
|
|
||||||
updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=?",
|
|
||||||
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)
|
||||||
sqlFindHardwareByPublicKey = String.format("SELECT %s FROM %s WHERE `publicKey` = ?", hardwareInfoCols, tableHWID);
|
sqlFindHardwareByPublicKey = String.format("SELECT %s FROM %s WHERE `publicKey` = ?", hardwareInfoCols, tableHWID);
|
||||||
|
@ -206,43 +63,9 @@ public void init(LaunchServer server) {
|
||||||
sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", table, hardwareIdColumn, uuidColumn);
|
sqlUpdateUsers = String.format("UPDATE %s SET `%s` = ? WHERE `%s` = ?", table, hardwareIdColumn, uuidColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
|
||||||
try (Connection c = mySQLHolder.getConnection()) {
|
|
||||||
MySQLUser mySQLUser = (MySQLUser) user;
|
|
||||||
mySQLUser.accessToken = accessToken;
|
|
||||||
PreparedStatement s = c.prepareStatement(updateAuthSQL);
|
|
||||||
s.setString(1, accessToken);
|
|
||||||
s.setString(2, user.getUUID().toString());
|
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
|
||||||
return s.executeUpdate() > 0;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean updateServerID(User user, String serverID) throws IOException {
|
|
||||||
try (Connection c = mySQLHolder.getConnection()) {
|
|
||||||
MySQLUser mySQLUser = (MySQLUser) user;
|
|
||||||
mySQLUser.serverId = serverID;
|
|
||||||
PreparedStatement s = c.prepareStatement(updateServerIDSQL);
|
|
||||||
s.setString(1, serverID);
|
|
||||||
s.setString(2, user.getUUID().toString());
|
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
|
||||||
return s.executeUpdate() > 0;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
mySQLHolder.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MySQLUser constructUser(ResultSet set) throws SQLException {
|
private MySQLUser constructUser(ResultSet set) throws SQLException {
|
||||||
return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
return set.next() ? new MySQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
||||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions(), set.getLong(hardwareIdColumn)) : null;
|
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), requestPermissions(set.getString(uuidColumn)), set.getLong(hardwareIdColumn)) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MySQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
|
private MySQLUserHardware fetchHardwareInfo(ResultSet set) throws SQLException, IOException {
|
||||||
|
@ -271,19 +94,6 @@ private void setUserHardwareId(Connection connection, UUID uuid, long hwidId) th
|
||||||
s.executeUpdate();
|
s.executeUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private User query(String sql, String value) throws IOException {
|
|
||||||
try (Connection c = mySQLHolder.getConnection()) {
|
|
||||||
PreparedStatement s = c.prepareStatement(sql);
|
|
||||||
s.setString(1, value);
|
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
|
||||||
try (ResultSet set = s.executeQuery()) {
|
|
||||||
return constructUser(set);
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
public UserHardware getHardwareInfoByPublicKey(byte[] publicKey) {
|
||||||
try (Connection connection = mySQLHolder.getConnection()) {
|
try (Connection connection = mySQLHolder.getConnection()) {
|
||||||
|
@ -371,8 +181,8 @@ public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
|
public void connectUserAndHardware(UserSession userSession, UserHardware hardware) {
|
||||||
MySQLUserSession mySQLUserSession = (MySQLUserSession) userSession;
|
SQLUserSession mySQLUserSession = (SQLUserSession) userSession;
|
||||||
MySQLUser mySQLUser = mySQLUserSession.user;
|
MySQLUser mySQLUser = (MySQLUser) mySQLUserSession.getUser();
|
||||||
MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware;
|
MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware;
|
||||||
if (mySQLUser.hwidId == mySQLUserHardware.id) return;
|
if (mySQLUser.hwidId == mySQLUserHardware.id) return;
|
||||||
mySQLUser.hwidId = mySQLUserHardware.id;
|
mySQLUser.hwidId = mySQLUserHardware.id;
|
||||||
|
@ -488,51 +298,15 @@ public String toString() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MySQLUser implements User, UserSupportHardware {
|
public class MySQLUser extends SQLUser implements UserSupportHardware {
|
||||||
protected UUID uuid;
|
|
||||||
protected String username;
|
|
||||||
protected String accessToken;
|
|
||||||
protected String serverId;
|
|
||||||
protected String password;
|
|
||||||
protected ClientPermissions permissions;
|
|
||||||
protected long hwidId;
|
protected long hwidId;
|
||||||
protected transient MySQLUserHardware hardware;
|
protected transient MySQLUserHardware hardware;
|
||||||
|
|
||||||
public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions, long hwidId) {
|
public MySQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions, long hwidId) {
|
||||||
this.uuid = uuid;
|
super(uuid, username, accessToken, serverId, password, permissions);
|
||||||
this.username = username;
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.serverId = serverId;
|
|
||||||
this.password = password;
|
|
||||||
this.permissions = permissions;
|
|
||||||
this.hwidId = hwidId;
|
this.hwidId = hwidId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getUUID() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServerId() {
|
|
||||||
return serverId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccessToken() {
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientPermissions getPermissions() {
|
|
||||||
return permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserHardware getHardware() {
|
public UserHardware getHardware() {
|
||||||
if (hardware != null) return hardware;
|
if (hardware != null) return hardware;
|
||||||
|
@ -551,29 +325,4 @@ public String toString() {
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MySQLUserSession implements UserSession {
|
|
||||||
private final MySQLUser user;
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
public MySQLUserSession(MySQLUser user) {
|
|
||||||
this.user = user;
|
|
||||||
this.id = user.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getID() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getExpireIn() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,297 +1,13 @@
|
||||||
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.Logger;
|
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
|
||||||
import pro.gravit.launcher.request.auth.password.AuthPlainPassword;
|
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
|
||||||
import pro.gravit.launchserver.auth.AuthException;
|
|
||||||
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.SQLSourceConfig;
|
||||||
import pro.gravit.launchserver.helper.LegacySessionHelper;
|
|
||||||
import pro.gravit.launchserver.manangers.AuthManager;
|
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
public class PostgresSQLCoreProvider extends AbstractSQLCoreProvider {
|
||||||
import java.sql.*;
|
|
||||||
import java.time.Clock;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class PostgresSQLCoreProvider extends AuthCoreProvider {
|
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
|
||||||
public PostgreSQLSourceConfig postgresSQLHolder;
|
public PostgreSQLSourceConfig postgresSQLHolder;
|
||||||
|
|
||||||
public int expireSeconds = 3600;
|
|
||||||
public String uuidColumn;
|
|
||||||
public String usernameColumn;
|
|
||||||
public String accessTokenColumn;
|
|
||||||
public String passwordColumn;
|
|
||||||
public String serverIDColumn;
|
|
||||||
public String table;
|
|
||||||
|
|
||||||
public PasswordVerifier passwordVerifier;
|
|
||||||
public String customQueryByUUIDSQL;
|
|
||||||
public String customQueryByUsernameSQL;
|
|
||||||
public String customQueryByLoginSQL;
|
|
||||||
public String customUpdateAuthSQL;
|
|
||||||
public String customUpdateServerIdSQL;
|
|
||||||
// Prepared SQL queries
|
|
||||||
private transient String queryByUUIDSQL;
|
|
||||||
private transient String queryByUsernameSQL;
|
|
||||||
private transient String queryByLoginSQL;
|
|
||||||
private transient String updateAuthSQL;
|
|
||||||
private transient String updateServerIDSQL;
|
|
||||||
|
|
||||||
private transient LaunchServer server;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User getUserByUsername(String username) {
|
public SQLSourceConfig getSQLConfig() {
|
||||||
try {
|
return postgresSQLHolder;
|
||||||
return query(queryByUsernameSQL, username);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("SQL error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getUserByUUID(UUID uuid) {
|
|
||||||
try {
|
|
||||||
return query(queryByUUIDSQL, uuid.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("SQL error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getUserByLogin(String login) {
|
|
||||||
try {
|
|
||||||
return query(queryByLoginSQL, login);
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("SQL error", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired {
|
|
||||||
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
|
|
||||||
public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) {
|
|
||||||
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
|
|
||||||
public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException {
|
|
||||||
PostgresSQLUser postgresSQLUser = (PostgresSQLUser) getUserByLogin(login);
|
|
||||||
if(postgresSQLUser == null) {
|
|
||||||
throw AuthException.wrongPassword();
|
|
||||||
}
|
|
||||||
if(context != null) {
|
|
||||||
AuthPlainPassword plainPassword = (AuthPlainPassword) password;
|
|
||||||
if(plainPassword == null) {
|
|
||||||
throw AuthException.wrongPassword();
|
|
||||||
}
|
|
||||||
if(!passwordVerifier.check(postgresSQLUser.password, plainPassword.password)) {
|
|
||||||
throw AuthException.wrongPassword();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
String minecraftAccessToken = SecurityHelper.randomStringToken();
|
|
||||||
updateAuth(postgresSQLUser, minecraftAccessToken);
|
|
||||||
return AuthManager.AuthReport.ofOAuthWithMinecraft(minecraftAccessToken, accessToken, refreshToken, expireSeconds * 1000L, session);
|
|
||||||
} else {
|
|
||||||
return AuthManager.AuthReport.ofOAuth(accessToken, refreshToken, expireSeconds * 1000L, session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init(LaunchServer server) {
|
|
||||||
this.server = server;
|
|
||||||
if (postgresSQLHolder == null) logger.error("postgresSQLHolder cannot be null");
|
|
||||||
if (uuidColumn == null) logger.error("uuidColumn cannot be null");
|
|
||||||
if (usernameColumn == null) logger.error("usernameColumn cannot be null");
|
|
||||||
if (accessTokenColumn == null) logger.error("accessTokenColumn cannot be null");
|
|
||||||
if (serverIDColumn == null) logger.error("serverIDColumn cannot be null");
|
|
||||||
if (table == null) logger.error("table cannot be null");
|
|
||||||
// Prepare SQL queries
|
|
||||||
String userInfoCols = String.format("%s, %s, %s, %s, %s", uuidColumn, usernameColumn, accessTokenColumn, serverIDColumn, passwordColumn);
|
|
||||||
queryByUUIDSQL = customQueryByUUIDSQL != null ? customQueryByUUIDSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1", userInfoCols,
|
|
||||||
table, uuidColumn);
|
|
||||||
queryByUsernameSQL = customQueryByUsernameSQL != null ? customQueryByUsernameSQL : String.format("SELECT %s FROM %s WHERE %s=? LIMIT 1",
|
|
||||||
userInfoCols, table, usernameColumn);
|
|
||||||
queryByLoginSQL = customQueryByLoginSQL != null ? customQueryByLoginSQL : queryByUsernameSQL;
|
|
||||||
|
|
||||||
updateAuthSQL = customUpdateAuthSQL != null ? customUpdateAuthSQL : String.format("UPDATE %s SET %s=?, %s=NULL WHERE %s=?",
|
|
||||||
table, accessTokenColumn, serverIDColumn, uuidColumn);
|
|
||||||
updateServerIDSQL = customUpdateServerIdSQL != null ? customUpdateServerIdSQL : String.format("UPDATE %s SET %s=? WHERE %s=?",
|
|
||||||
table, serverIDColumn, uuidColumn);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean updateAuth(User user, String accessToken) throws IOException {
|
|
||||||
try (Connection c = postgresSQLHolder.getConnection()) {
|
|
||||||
PostgresSQLUser postgresSQLUser = (PostgresSQLUser) user;
|
|
||||||
postgresSQLUser.accessToken = accessToken;
|
|
||||||
PreparedStatement s = c.prepareStatement(updateAuthSQL);
|
|
||||||
s.setString(1, accessToken);
|
|
||||||
s.setString(2, user.getUUID().toString());
|
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
|
||||||
return s.executeUpdate() > 0;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean updateServerID(User user, String serverID) throws IOException {
|
|
||||||
try (Connection c = postgresSQLHolder.getConnection()) {
|
|
||||||
PostgresSQLUser postgresSQLUser = (PostgresSQLUser) user;
|
|
||||||
postgresSQLUser.serverId = serverID;
|
|
||||||
PreparedStatement s = c.prepareStatement(updateServerIDSQL);
|
|
||||||
s.setString(1, serverID);
|
|
||||||
s.setString(2, user.getUUID().toString());
|
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
|
||||||
return s.executeUpdate() > 0;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
postgresSQLHolder.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private PostgresSQLUser constructUser(ResultSet set) throws SQLException {
|
|
||||||
return set.next() ? new PostgresSQLUser(UUID.fromString(set.getString(uuidColumn)), set.getString(usernameColumn),
|
|
||||||
set.getString(accessTokenColumn), set.getString(serverIDColumn), set.getString(passwordColumn), new ClientPermissions()) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private User query(String sql, String value) throws IOException {
|
|
||||||
try (Connection c = postgresSQLHolder.getConnection()) {
|
|
||||||
PreparedStatement s = c.prepareStatement(sql);
|
|
||||||
s.setString(1, value);
|
|
||||||
s.setQueryTimeout(MySQLSourceConfig.TIMEOUT);
|
|
||||||
try (ResultSet set = s.executeQuery()) {
|
|
||||||
return constructUser(set);
|
|
||||||
}
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PostgresSQLUser implements User {
|
|
||||||
protected UUID uuid;
|
|
||||||
protected String username;
|
|
||||||
protected String accessToken;
|
|
||||||
protected String serverId;
|
|
||||||
protected String password;
|
|
||||||
protected ClientPermissions permissions;
|
|
||||||
|
|
||||||
public PostgresSQLUser(UUID uuid, String username, String accessToken, String serverId, String password, ClientPermissions permissions) {
|
|
||||||
this.uuid = uuid;
|
|
||||||
this.username = username;
|
|
||||||
this.accessToken = accessToken;
|
|
||||||
this.serverId = serverId;
|
|
||||||
this.password = password;
|
|
||||||
this.permissions = permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getUUID() {
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServerId() {
|
|
||||||
return serverId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getAccessToken() {
|
|
||||||
return accessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClientPermissions getPermissions() {
|
|
||||||
return permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "PostgresSQLUser{" +
|
|
||||||
"uuid=" + uuid +
|
|
||||||
", username='" + username + '\'' +
|
|
||||||
", permissions=" + permissions +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class MySQLUserSession implements UserSession {
|
|
||||||
private final PostgresSQLUser user;
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
public MySQLUserSession(PostgresSQLUser user) {
|
|
||||||
this.user = user;
|
|
||||||
this.id = user.username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getID() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public User getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getExpireIn() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
|
|
||||||
public interface AuthSupportRemoteClientAccess {
|
public interface AuthSupportRemoteClientAccess {
|
||||||
String getClientApiUrl();
|
String getClientApiUrl();
|
||||||
|
|
||||||
List<String> getClientApiFeatures();
|
List<String> getClientApiFeatures();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
|
|
||||||
public interface UserSessionSupportKeys {
|
public interface UserSessionSupportKeys {
|
||||||
ClientProfileKeys getClientProfileKeys();
|
ClientProfileKeys getClientProfileKeys();
|
||||||
record ClientProfileKeys(PublicKey publicKey, PrivateKey privateKey, byte[] signature /* V2 */, long expiresAt, long refreshedAfter) {
|
|
||||||
|
record ClientProfileKeys(PublicKey publicKey, PrivateKey privateKey, byte[] signature /* V2 */, long expiresAt,
|
||||||
|
long refreshedAfter) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,10 @@ default Map<String, Texture> getUserAssets() {
|
||||||
var skin = getSkinTexture();
|
var skin = getSkinTexture();
|
||||||
var cape = getCloakTexture();
|
var cape = getCloakTexture();
|
||||||
Map<String, Texture> map = new HashMap<>();
|
Map<String, Texture> map = new HashMap<>();
|
||||||
if(skin != null) {
|
if (skin != null) {
|
||||||
map.put("SKIN", skin);
|
map.put("SKIN", skin);
|
||||||
}
|
}
|
||||||
if(cape != null) {
|
if (cape != null) {
|
||||||
map.put("CAPE", cape);
|
map.put("CAPE", cape);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
public class BCryptPasswordVerifier extends PasswordVerifier {
|
public class BCryptPasswordVerifier extends PasswordVerifier {
|
||||||
public int cost = 10;
|
public int cost = 10;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean check(String encryptedPassword, String password) {
|
public boolean check(String encryptedPassword, String password) {
|
||||||
return OpenBSDBCrypt.checkPassword(encryptedPassword, password.toCharArray());
|
return OpenBSDBCrypt.checkPassword(encryptedPassword, password.toCharArray());
|
||||||
|
|
|
@ -19,29 +19,6 @@ public class JsonPasswordVerifier extends PasswordVerifier {
|
||||||
public String url;
|
public String url;
|
||||||
public String bearerToken;
|
public String bearerToken;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean check(String encryptedPassword, String password) {
|
|
||||||
JsonPasswordResponse response = jsonRequest(new JsonPasswordRequest(encryptedPassword, password), url, bearerToken, JsonPasswordResponse.class, client);
|
|
||||||
if (response != null) {
|
|
||||||
return response.success;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class JsonPasswordRequest {
|
|
||||||
public String encryptedPassword;
|
|
||||||
public String password;
|
|
||||||
|
|
||||||
public JsonPasswordRequest(String encryptedPassword, String password) {
|
|
||||||
this.encryptedPassword = encryptedPassword;
|
|
||||||
this.password = password;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class JsonPasswordResponse {
|
|
||||||
public boolean success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T, R> R jsonRequest(T request, String url, String bearerToken, Class<R> clazz, HttpClient client) {
|
public static <T, R> R jsonRequest(T request, String url, String bearerToken, Class<R> clazz, HttpClient client) {
|
||||||
HttpRequest.BodyPublisher publisher;
|
HttpRequest.BodyPublisher publisher;
|
||||||
if (request != null) {
|
if (request != null) {
|
||||||
|
@ -78,4 +55,27 @@ public static <T, R> R jsonRequest(T request, String url, String bearerToken, Cl
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean check(String encryptedPassword, String password) {
|
||||||
|
JsonPasswordResponse response = jsonRequest(new JsonPasswordRequest(encryptedPassword, password), url, bearerToken, JsonPasswordResponse.class, client);
|
||||||
|
if (response != null) {
|
||||||
|
return response.success;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JsonPasswordRequest {
|
||||||
|
public String encryptedPassword;
|
||||||
|
public String password;
|
||||||
|
|
||||||
|
public JsonPasswordRequest(String encryptedPassword, String password) {
|
||||||
|
this.encryptedPassword = encryptedPassword;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class JsonPasswordResponse {
|
||||||
|
public boolean success;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
|
import pro.gravit.launcher.events.request.GetSecureLevelInfoRequestEvent;
|
||||||
import pro.gravit.launcher.events.request.HardwareReportRequestEvent;
|
import pro.gravit.launcher.events.request.HardwareReportRequestEvent;
|
||||||
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent;
|
import pro.gravit.launcher.events.request.VerifySecureLevelKeyRequestEvent;
|
||||||
import pro.gravit.launcher.request.secure.HardwareReportRequest;
|
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
|
import pro.gravit.launchserver.auth.core.interfaces.UserHardware;
|
||||||
|
@ -157,9 +156,9 @@ public boolean accept(Client client, AuthProviderPair pair, String extendedToken
|
||||||
var parse = parser.parseClaimsJws(extendedToken);
|
var parse = parser.parseClaimsJws(extendedToken);
|
||||||
String hardwareInfoId = parse.getBody().get("hardware", String.class);
|
String hardwareInfoId = parse.getBody().get("hardware", String.class);
|
||||||
if (hardwareInfoId == null) return false;
|
if (hardwareInfoId == null) return false;
|
||||||
if(client.auth == null) return false;
|
if (client.auth == null) return false;
|
||||||
var hardwareSupport = client.auth.core.isSupport(AuthSupportHardware.class);
|
var hardwareSupport = client.auth.core.isSupport(AuthSupportHardware.class);
|
||||||
if(hardwareSupport == null) return false;
|
if (hardwareSupport == null) return false;
|
||||||
UserHardware hardware = hardwareSupport.getHardwareInfoById(hardwareInfoId);
|
UserHardware hardware = hardwareSupport.getHardwareInfoById(hardwareInfoId);
|
||||||
if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
|
if (client.trustLevel == null) client.trustLevel = new Client.TrustLevel();
|
||||||
client.trustLevel.hardwareInfo = hardware.getHardwareInfo();
|
client.trustLevel.hardwareInfo = hardware.getHardwareInfo();
|
||||||
|
|
|
@ -27,7 +27,7 @@ public void checkLaunchServerLicense() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LaunchServer server) {
|
public void init(LaunchServer server) {
|
||||||
if(profileWhitelist != null && profileWhitelist.size() > 0) {
|
if (profileWhitelist != null && profileWhitelist.size() > 0) {
|
||||||
logger.warn("profileWhitelist deprecated. Please use permission 'launchserver.profile.PROFILE_UUID.show' and 'launchserver.profile.PROFILE_UUID.enter'");
|
logger.warn("profileWhitelist deprecated. Please use permission 'launchserver.profile.PROFILE_UUID.show' and 'launchserver.profile.PROFILE_UUID.enter'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,13 +48,13 @@ public boolean canGetUpdates(String updatesDirName, Client client) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isWhitelisted(String property, ClientProfile profile, Client client) {
|
private boolean isWhitelisted(String property, ClientProfile profile, Client client) {
|
||||||
if(client.permissions != null) {
|
if (client.permissions != null) {
|
||||||
String permByUUID = String.format(property, profile.getUUID());
|
String permByUUID = String.format(property, profile.getUUID());
|
||||||
if(client.permissions.hasPerm(permByUUID)) {
|
if (client.permissions.hasPerm(permByUUID)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
String permByTitle = String.format(property, profile.getTitle().toLowerCase(Locale.ROOT));
|
String permByTitle = String.format(property, profile.getTitle().toLowerCase(Locale.ROOT));
|
||||||
if(client.permissions.hasPerm(permByTitle)) {
|
if (client.permissions.hasPerm(permByTitle)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,10 @@
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class JsonTextureProvider extends TextureProvider {
|
public class JsonTextureProvider extends TextureProvider {
|
||||||
public String url;
|
private transient static final Type MAP_TYPE = new TypeToken<Map<String, Texture>>() {
|
||||||
|
}.getType();
|
||||||
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();
|
public String url;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
@ -42,14 +43,14 @@ public Map<String, Texture> getAssets(UUID uuid, String username, String client)
|
||||||
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)));
|
||||||
|
|
||||||
Map<String, Texture> map = Launcher.gsonManager.gson.fromJson(result, MAP_TYPE);
|
Map<String, Texture> map = Launcher.gsonManager.gson.fromJson(result, MAP_TYPE);
|
||||||
if(map == null) {
|
if (map == null) {
|
||||||
return new HashMap<>();
|
return new HashMap<>();
|
||||||
}
|
}
|
||||||
if(map.get("skin") != null) { // Legacy script
|
if (map.get("skin") != null) { // Legacy script
|
||||||
map.put("SKIN", map.get("skin"));
|
map.put("SKIN", map.get("skin"));
|
||||||
map.remove("skin");
|
map.remove("skin");
|
||||||
}
|
}
|
||||||
if(map.get("cloak") != null) {
|
if (map.get("cloak") != null) {
|
||||||
map.put("CAPE", map.get("cloak"));
|
map.put("CAPE", map.get("cloak"));
|
||||||
map.remove("cloak");
|
map.remove("cloak");
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,17 +33,6 @@ 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 final Texture skin;
|
|
||||||
public final Texture cloak;
|
|
||||||
|
|
||||||
public SkinAndCloakTextures(Texture skin, Texture cloak) {
|
|
||||||
this.skin = skin;
|
|
||||||
this.cloak = cloak;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
public SkinAndCloakTextures getTextures(UUID uuid, String username, String client) {
|
||||||
|
|
||||||
|
@ -83,13 +72,24 @@ public Map<String, Texture> getAssets(UUID uuid, String username, String client)
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Texture> map = new HashMap<>();
|
Map<String, Texture> map = new HashMap<>();
|
||||||
if(skin != null) {
|
if (skin != null) {
|
||||||
map.put("SKIN", skin);
|
map.put("SKIN", skin);
|
||||||
}
|
}
|
||||||
if(cloak != null) {
|
if (cloak != null) {
|
||||||
map.put("CAPE", cloak);
|
map.put("CAPE", cloak);
|
||||||
}
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static class SkinAndCloakTextures {
|
||||||
|
public final Texture skin;
|
||||||
|
public final Texture cloak;
|
||||||
|
|
||||||
|
public SkinAndCloakTextures(Texture skin, Texture cloak) {
|
||||||
|
this.skin = skin;
|
||||||
|
this.cloak = cloak;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -211,7 +211,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO
|
||||||
try {
|
try {
|
||||||
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec, iKeySpec);
|
cipher.init(Cipher.ENCRYPT_MODE, sKeySpec, iKeySpec);
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
|
||||||
|
InvalidAlgorithmParameterException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
try (OutputStream stream = new CipherOutputStream(new NoCloseOutputStream(output), cipher)) {
|
try (OutputStream stream = new CipherOutputStream(new NoCloseOutputStream(output), cipher)) {
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
public final class JARLauncherBinary extends LauncherBinary {
|
public final class JARLauncherBinary extends LauncherBinary {
|
||||||
|
@ -19,6 +21,8 @@ public final class JARLauncherBinary extends LauncherBinary {
|
||||||
public final List<Path> coreLibs;
|
public final List<Path> coreLibs;
|
||||||
public final List<Path> addonLibs;
|
public final List<Path> addonLibs;
|
||||||
|
|
||||||
|
public final Map<String, Path> files;
|
||||||
|
|
||||||
public JARLauncherBinary(LaunchServer server) throws IOException {
|
public JARLauncherBinary(LaunchServer server) throws IOException {
|
||||||
super(server, resolve(server, ".jar"), "Launcher-%s-%d.jar");
|
super(server, resolve(server, ".jar"), "Launcher-%s-%d.jar");
|
||||||
count = new AtomicLong(0);
|
count = new AtomicLong(0);
|
||||||
|
@ -27,6 +31,7 @@ public JARLauncherBinary(LaunchServer server) throws IOException {
|
||||||
buildDir = server.dir.resolve("build");
|
buildDir = server.dir.resolve("build");
|
||||||
coreLibs = new ArrayList<>();
|
coreLibs = new ArrayList<>();
|
||||||
addonLibs = new ArrayList<>();
|
addonLibs = new ArrayList<>();
|
||||||
|
files = new HashMap<>();
|
||||||
if (!Files.isDirectory(buildDir)) {
|
if (!Files.isDirectory(buildDir)) {
|
||||||
Files.deleteIfExists(buildDir);
|
Files.deleteIfExists(buildDir);
|
||||||
Files.createDirectory(buildDir);
|
Files.createDirectory(buildDir);
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
import java.util.zip.ZipOutputStream;
|
import java.util.zip.ZipOutputStream;
|
||||||
|
@ -48,6 +51,11 @@ public Path process(Path inputFile) throws IOException {
|
||||||
}
|
}
|
||||||
attach(output, inputFile, srv.launcherBinary.coreLibs);
|
attach(output, inputFile, srv.launcherBinary.coreLibs);
|
||||||
attach(output, inputFile, jars);
|
attach(output, inputFile, jars);
|
||||||
|
for(var entry : srv.launcherBinary.files.entrySet()) {
|
||||||
|
ZipEntry newEntry = IOHelper.newZipEntry(entry.getKey());
|
||||||
|
output.putNextEntry(newEntry);
|
||||||
|
IOHelper.transfer(entry.getValue(), output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return outputFile;
|
return outputFile;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
import java.nio.file.SimpleFileVisitor;
|
import java.nio.file.SimpleFileVisitor;
|
||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class PrepareBuildTask implements LauncherBuildTask {
|
public class PrepareBuildTask implements LauncherBuildTask {
|
||||||
private final LaunchServer server;
|
private final LaunchServer server;
|
||||||
|
@ -33,8 +35,19 @@ public String getName() {
|
||||||
public Path process(Path inputFile) throws IOException {
|
public Path process(Path inputFile) throws IOException {
|
||||||
server.launcherBinary.coreLibs.clear();
|
server.launcherBinary.coreLibs.clear();
|
||||||
server.launcherBinary.addonLibs.clear();
|
server.launcherBinary.addonLibs.clear();
|
||||||
IOHelper.walk(server.launcherLibraries, new ListFileVisitor(server.launcherBinary.coreLibs), true);
|
server.launcherBinary.files.clear();
|
||||||
IOHelper.walk(server.launcherLibrariesCompile, new ListFileVisitor(server.launcherBinary.addonLibs), true);
|
IOHelper.walk(server.launcherLibraries, new ListFileVisitor(server.launcherBinary.coreLibs), false);
|
||||||
|
IOHelper.walk(server.launcherLibrariesCompile, new ListFileVisitor(server.launcherBinary.addonLibs), false);
|
||||||
|
try(Stream<Path> stream = Files.walk(server.launcherPack).filter((e) -> {
|
||||||
|
try {
|
||||||
|
return !Files.isDirectory(e) && !Files.isHidden(e);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
})) {
|
||||||
|
var map = stream.collect(Collectors.toMap(k -> server.launcherPack.relativize(k).toString(), (v) -> v));
|
||||||
|
server.launcherBinary.files.putAll(map);
|
||||||
|
}
|
||||||
UnpackHelper.unpack(IOHelper.getResourceURL("Launcher.jar"), result);
|
UnpackHelper.unpack(IOHelper.getResourceURL("Launcher.jar"), result);
|
||||||
tryUnpack();
|
tryUnpack();
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -40,7 +40,7 @@ public static CMSSignedDataGenerator gen(LaunchServerConfig.JarSignerConf config
|
||||||
return SignHelper.createSignedDataGenerator(c,
|
return SignHelper.createSignedDataGenerator(c,
|
||||||
config.keyAlias, config.signAlgo, config.keyPass);
|
config.keyAlias, config.signAlgo, config.keyPass);
|
||||||
} catch (CertificateEncodingException | UnrecoverableKeyException | KeyStoreException
|
} catch (CertificateEncodingException | UnrecoverableKeyException | KeyStoreException
|
||||||
| OperatorCreationException | NoSuchAlgorithmException | CMSException e) {
|
| OperatorCreationException | NoSuchAlgorithmException | CMSException e) {
|
||||||
logger.error("Create signedDataGenerator failed", e);
|
logger.error("Create signedDataGenerator failed", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
public class DebugCommand extends Command {
|
public class DebugCommand extends Command {
|
||||||
private transient Logger logger = LogManager.getLogger();
|
private transient Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
public DebugCommand(LaunchServer server) {
|
public DebugCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
}
|
}
|
||||||
|
@ -34,7 +35,7 @@ public void invoke(String... args) throws Exception {
|
||||||
LoggerConfig loggerConfig = config.getLoggerConfig("pro.gravit");
|
LoggerConfig loggerConfig = config.getLoggerConfig("pro.gravit");
|
||||||
loggerConfig.setLevel(value ? Level.TRACE : Level.DEBUG);
|
loggerConfig.setLevel(value ? Level.TRACE : Level.DEBUG);
|
||||||
ctx.updateLoggers();
|
ctx.updateLoggers();
|
||||||
if(value) {
|
if (value) {
|
||||||
logger.info("Log level TRACE enabled");
|
logger.info("Log level TRACE enabled");
|
||||||
} else {
|
} else {
|
||||||
logger.info("Log level TRACE disabled");
|
logger.info("Log level TRACE disabled");
|
||||||
|
|
|
@ -10,9 +10,7 @@
|
||||||
import pro.gravit.launchserver.command.Command;
|
import pro.gravit.launchserver.command.Command;
|
||||||
import pro.gravit.utils.Downloader;
|
import pro.gravit.utils.Downloader;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import proguard.OutputWriter;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
@ -22,11 +20,9 @@
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class DownloadAssetCommand extends Command {
|
public final class DownloadAssetCommand extends Command {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
|
||||||
|
|
||||||
private static final String MINECRAFT_VERSIONS_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
private static final String MINECRAFT_VERSIONS_URL = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
||||||
|
|
||||||
private static final String RESOURCES_DOWNLOAD_URL = "https://resources.download.minecraft.net/";
|
private static final String RESOURCES_DOWNLOAD_URL = "https://resources.download.minecraft.net/";
|
||||||
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
public DownloadAssetCommand(LaunchServer server) {
|
public DownloadAssetCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
|
@ -52,23 +48,23 @@ public void invoke(String... args) throws Exception {
|
||||||
Path assetDir = server.updatesDir.resolve(dirName);
|
Path assetDir = server.updatesDir.resolve(dirName);
|
||||||
|
|
||||||
// Create asset dir
|
// Create asset dir
|
||||||
if(Files.notExists(assetDir)) {
|
if (Files.notExists(assetDir)) {
|
||||||
logger.info("Creating asset dir: '{}'", dirName);
|
logger.info("Creating asset dir: '{}'", dirName);
|
||||||
Files.createDirectory(assetDir);
|
Files.createDirectory(assetDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(type.equals("mojang")) {
|
if (type.equals("mojang")) {
|
||||||
HttpRequester requester = new HttpRequester();
|
HttpRequester requester = new HttpRequester();
|
||||||
logger.info("Fetch versions from {}", MINECRAFT_VERSIONS_URL);
|
logger.info("Fetch versions from {}", MINECRAFT_VERSIONS_URL);
|
||||||
var versions = requester.send(requester.get(MINECRAFT_VERSIONS_URL, null), MinecraftVersions.class).getOrThrow();
|
var versions = requester.send(requester.get(MINECRAFT_VERSIONS_URL, null), MinecraftVersions.class).getOrThrow();
|
||||||
String profileUrl = null;
|
String profileUrl = null;
|
||||||
for(var e : versions.versions) {
|
for (var e : versions.versions) {
|
||||||
if(e.id.equals(versionName)) {
|
if (e.id.equals(versionName)) {
|
||||||
profileUrl = e.url;
|
profileUrl = e.url;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(profileUrl == null) {
|
if (profileUrl == null) {
|
||||||
logger.error("Version {} not found", versionName);
|
logger.error("Version {} not found", versionName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -76,30 +72,30 @@ public void invoke(String... args) throws Exception {
|
||||||
var profileInfo = requester.send(requester.get(profileUrl, null), MiniVersion.class).getOrThrow();
|
var profileInfo = requester.send(requester.get(profileUrl, null), MiniVersion.class).getOrThrow();
|
||||||
String assetsIndexUrl = profileInfo.assetIndex.url;
|
String assetsIndexUrl = profileInfo.assetIndex.url;
|
||||||
String assetIndex = profileInfo.assetIndex.id;
|
String assetIndex = profileInfo.assetIndex.id;
|
||||||
Path indexPath = assetDir.resolve("indexes").resolve(assetIndex+".json");
|
Path indexPath = assetDir.resolve("indexes").resolve(assetIndex + ".json");
|
||||||
logger.info("Fetch asset index {} from {}", assetIndex, assetsIndexUrl);
|
logger.info("Fetch asset index {} from {}", assetIndex, assetsIndexUrl);
|
||||||
JsonObject assets = requester.send(requester.get(assetsIndexUrl, null), JsonObject.class).getOrThrow();
|
JsonObject assets = requester.send(requester.get(assetsIndexUrl, null), JsonObject.class).getOrThrow();
|
||||||
JsonObject objects = assets.get("objects").getAsJsonObject();
|
JsonObject objects = assets.get("objects").getAsJsonObject();
|
||||||
try(Writer writer = IOHelper.newWriter(indexPath)) {
|
try (Writer writer = IOHelper.newWriter(indexPath)) {
|
||||||
logger.info("Save {}", indexPath);
|
logger.info("Save {}", indexPath);
|
||||||
Launcher.gsonManager.configGson.toJson(assets, writer);
|
Launcher.gsonManager.configGson.toJson(assets, writer);
|
||||||
}
|
}
|
||||||
if(!assetIndex.equals(versionName)) {
|
if (!assetIndex.equals(versionName)) {
|
||||||
Path targetPath = assetDir.resolve("indexes").resolve(versionName+".json");
|
Path targetPath = assetDir.resolve("indexes").resolve(versionName + ".json");
|
||||||
logger.info("Copy {} into {}", indexPath, targetPath);
|
logger.info("Copy {} into {}", indexPath, targetPath);
|
||||||
Files.copy(indexPath, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(indexPath, targetPath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
List<AsyncDownloader.SizedFile> toDownload = new ArrayList<>(128);
|
List<AsyncDownloader.SizedFile> toDownload = new ArrayList<>(128);
|
||||||
for(var e : objects.entrySet()) {
|
for (var e : objects.entrySet()) {
|
||||||
var value = e.getValue().getAsJsonObject();
|
var value = e.getValue().getAsJsonObject();
|
||||||
var hash = value.get("hash").getAsString();
|
var hash = value.get("hash").getAsString();
|
||||||
hash = hash.substring(0, 2) + "/" + hash;
|
hash = hash.substring(0, 2) + "/" + hash;
|
||||||
var size = value.get("size").getAsLong();
|
var size = value.get("size").getAsLong();
|
||||||
var path = "objects/" + hash;
|
var path = "objects/" + hash;
|
||||||
var target = assetDir.resolve(path);
|
var target = assetDir.resolve(path);
|
||||||
if(Files.exists(target)) {
|
if (Files.exists(target)) {
|
||||||
long fileSize = Files.size(target);
|
long fileSize = Files.size(target);
|
||||||
if(fileSize != size) {
|
if (fileSize != size) {
|
||||||
logger.warn("File {} corrupted. Size {}, expected {}", target, size, fileSize);
|
logger.warn("File {} corrupted. Size {}, expected {}", target, size, fileSize);
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
public class TokenCommand extends Command {
|
public class TokenCommand extends Command {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
public TokenCommand(LaunchServer server) {
|
public TokenCommand(LaunchServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
this.childCommands.put("info", new SubCommand("[token]", "print token info") {
|
this.childCommands.put("info", new SubCommand("[token]", "print token info") {
|
||||||
|
@ -27,16 +28,16 @@ public void invoke(String... args) throws Exception {
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
AuthProviderPair pair = args.length > 1 ? server.config.getAuthProviderPair(args[1]) : server.config.getAuthProviderPair();
|
AuthProviderPair pair = args.length > 1 ? server.config.getAuthProviderPair(args[1]) : server.config.getAuthProviderPair();
|
||||||
ClientProfile profile = null;
|
ClientProfile profile = null;
|
||||||
for(ClientProfile p : server.getProfiles()) {
|
for (ClientProfile p : server.getProfiles()) {
|
||||||
if(p.getTitle().equals(args[0]) || p.getUUID().toString().equals(args[0])) {
|
if (p.getTitle().equals(args[0]) || p.getUUID().toString().equals(args[0])) {
|
||||||
profile = p;
|
profile = p;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(profile == null) {
|
if (profile == null) {
|
||||||
logger.warn("Profile {} not found", args[0]);
|
logger.warn("Profile {} not found", args[0]);
|
||||||
}
|
}
|
||||||
if(pair == null) {
|
if (pair == null) {
|
||||||
logger.error("AuthId {} not found", args[1]);
|
logger.error("AuthId {} not found", args[1]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
import pro.gravit.utils.helper.UnpackHelper;
|
import pro.gravit.utils.helper.UnpackHelper;
|
||||||
import proguard.Configuration;
|
import proguard.Configuration;
|
||||||
import proguard.ConfigurationParser;
|
import proguard.ConfigurationParser;
|
||||||
import proguard.ParseException;
|
|
||||||
import proguard.ProGuard;
|
import proguard.ProGuard;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
|
@ -166,9 +166,9 @@ public void verify() {
|
||||||
// Mirror check
|
// Mirror check
|
||||||
{
|
{
|
||||||
boolean updateMirror = Boolean.getBoolean("launchserver.config.disableUpdateMirror");
|
boolean updateMirror = Boolean.getBoolean("launchserver.config.disableUpdateMirror");
|
||||||
if(!updateMirror) {
|
if (!updateMirror) {
|
||||||
for(int i=0;i < mirrors.length;++i) {
|
for (int i = 0; i < mirrors.length; ++i) {
|
||||||
if("https://mirror.gravit.pro/5.2.x/".equals(mirrors[i])) {
|
if ("https://mirror.gravit.pro/5.2.x/".equals(mirrors[i])) {
|
||||||
logger.warn("Replace mirror 'https://mirror.gravit.pro/5.2.x/' to 'https://mirror.gravit.pro/5.3.x/'. If you really need to use original url, use '-Dlaunchserver.config.disableUpdateMirror=true'");
|
logger.warn("Replace mirror 'https://mirror.gravit.pro/5.2.x/' to 'https://mirror.gravit.pro/5.3.x/'. If you really need to use original url, use '-Dlaunchserver.config.disableUpdateMirror=true'");
|
||||||
mirrors[i] = "https://mirror.gravit.pro/5.3.x/";
|
mirrors[i] = "https://mirror.gravit.pro/5.3.x/";
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,61 @@
|
||||||
|
|
||||||
public final class HttpHelper {
|
public final class HttpHelper {
|
||||||
private static transient final Logger logger = LogManager.getLogger();
|
private static transient final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
private HttpHelper() {
|
private HttpHelper() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class HttpOptional<T,E> {
|
public static <T, E> HttpOptional<T, E> send(HttpClient client, HttpRequest request, HttpErrorHandler<T, E> handler) throws IOException {
|
||||||
|
try {
|
||||||
|
var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||||
|
return handler.apply(response);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, E> CompletableFuture<HttpOptional<T, E>> sendAsync(HttpClient client, HttpRequest request, HttpErrorHandler<T, E> handler) throws IOException {
|
||||||
|
return client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(handler::apply);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> HttpResponse.BodyHandler<T> ofJsonResult(Class<T> type) {
|
||||||
|
return ofJsonResult((Type) type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> HttpResponse.BodyHandler<T> ofJsonResult(Type type) {
|
||||||
|
return new JsonBodyHandler<>(HttpResponse.BodyHandlers.ofInputStream(), (input) -> {
|
||||||
|
try (Reader reader = new InputStreamReader(input)) {
|
||||||
|
return Launcher.gsonManager.gson.fromJson(reader, type);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> HttpRequest.BodyPublisher jsonBodyPublisher(T obj) {
|
||||||
|
return HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface HttpErrorHandler<T, E> {
|
||||||
|
HttpOptional<T, E> apply(HttpResponse<InputStream> response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface HttpJsonErrorHandler<T, E> extends HttpErrorHandler<T, E> {
|
||||||
|
HttpOptional<T, E> applyJson(JsonElement response, int statusCode);
|
||||||
|
|
||||||
|
default HttpOptional<T, E> apply(HttpResponse<InputStream> response) {
|
||||||
|
try (Reader reader = new InputStreamReader(response.body())) {
|
||||||
|
var element = Launcher.gsonManager.gson.fromJson(reader, JsonElement.class);
|
||||||
|
return applyJson(element, response.statusCode());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HttpOptional<T, E> {
|
||||||
protected final T result;
|
protected final T result;
|
||||||
protected final E error;
|
protected final E error;
|
||||||
protected final int statusCode;
|
protected final int statusCode;
|
||||||
|
@ -41,17 +91,21 @@ public HttpOptional(T result, E error, int statusCode) {
|
||||||
public T result() {
|
public T result() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public E error() {
|
public E error() {
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int statusCode() {
|
public int statusCode() {
|
||||||
return statusCode;
|
return statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSuccessful() {
|
public boolean isSuccessful() {
|
||||||
return statusCode >= 200 && statusCode < 300;
|
return statusCode >= 200 && statusCode < 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getOrThrow() throws RequestException {
|
public T getOrThrow() throws RequestException {
|
||||||
if(isSuccessful()) {
|
if (isSuccessful()) {
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
throw new RequestException(error == null ? String.format("statusCode %d", statusCode) : error.toString());
|
throw new RequestException(error == null ? String.format("statusCode %d", statusCode) : error.toString());
|
||||||
|
@ -59,22 +113,6 @@ public T getOrThrow() throws RequestException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface HttpErrorHandler<T, E> {
|
|
||||||
HttpOptional<T,E> apply(HttpResponse<InputStream> response);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface HttpJsonErrorHandler<T, E> extends HttpErrorHandler<T,E> {
|
|
||||||
HttpOptional<T,E> applyJson(JsonElement response, int statusCode);
|
|
||||||
default HttpOptional<T,E> apply(HttpResponse<InputStream> response) {
|
|
||||||
try(Reader reader = new InputStreamReader(response.body())) {
|
|
||||||
var element = Launcher.gsonManager.gson.fromJson(reader, JsonElement.class);
|
|
||||||
return applyJson(element, response.statusCode());
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class BasicJsonHttpErrorHandler<T> implements HttpJsonErrorHandler<T, Void> {
|
public static final class BasicJsonHttpErrorHandler<T> implements HttpJsonErrorHandler<T, Void> {
|
||||||
private final Class<T> type;
|
private final Class<T> type;
|
||||||
|
|
||||||
|
@ -88,38 +126,6 @@ public HttpOptional<T, Void> applyJson(JsonElement response, int statusCode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static<T,E> HttpOptional<T,E> send(HttpClient client, HttpRequest request, HttpErrorHandler<T,E> handler) throws IOException {
|
|
||||||
try {
|
|
||||||
var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
|
||||||
return handler.apply(response);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static<T,E> CompletableFuture<HttpOptional<T,E>> sendAsync(HttpClient client, HttpRequest request, HttpErrorHandler<T,E> handler) throws IOException {
|
|
||||||
return client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(handler::apply);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static<T> HttpResponse.BodyHandler<T> ofJsonResult(Class<T> type) {
|
|
||||||
return ofJsonResult((Type) type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static<T> HttpResponse.BodyHandler<T> ofJsonResult(Type type) {
|
|
||||||
return new JsonBodyHandler<>(HttpResponse.BodyHandlers.ofInputStream(), (input) -> {
|
|
||||||
try(Reader reader = new InputStreamReader(input)) {
|
|
||||||
return Launcher.gsonManager.gson.fromJson(reader, type);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static<T> HttpRequest.BodyPublisher jsonBodyPublisher(T obj) {
|
|
||||||
return HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class JsonBodyHandler<T> implements HttpResponse.BodyHandler<T> {
|
private static class JsonBodyHandler<T> implements HttpResponse.BodyHandler<T> {
|
||||||
private final HttpResponse.BodyHandler<InputStream> delegate;
|
private final HttpResponse.BodyHandler<InputStream> delegate;
|
||||||
private final Function<InputStream, T> func;
|
private final Function<InputStream, T> func;
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import java.security.interfaces.ECPrivateKey;
|
import java.security.interfaces.ECPrivateKey;
|
||||||
import java.security.interfaces.ECPublicKey;
|
import java.security.interfaces.ECPublicKey;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -20,7 +19,7 @@ public static String makeAccessJwtTokenFromString(User user, LocalDateTime expir
|
||||||
.setSubject(user.getUsername())
|
.setSubject(user.getUsername())
|
||||||
.claim("uuid", user.getUUID().toString())
|
.claim("uuid", user.getUUID().toString())
|
||||||
.setExpiration(Date.from(expirationTime
|
.setExpiration(Date.from(expirationTime
|
||||||
.toInstant(ZoneOffset.UTC)))
|
.toInstant(ZoneOffset.UTC)))
|
||||||
.signWith(privateKey)
|
.signWith(privateKey)
|
||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
@ -38,7 +37,7 @@ public static JwtTokenInfo getJwtInfoFromAccessToken(String token, ECPublicKey p
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String makeRefreshTokenFromPassword(String username, String rawPassword, String secretSalt) {
|
public static String makeRefreshTokenFromPassword(String username, String rawPassword, String secretSalt) {
|
||||||
if(rawPassword == null) {
|
if (rawPassword == null) {
|
||||||
rawPassword = "";
|
rawPassword = "";
|
||||||
}
|
}
|
||||||
return SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
|
return SecurityHelper.toHex(SecurityHelper.digest(SecurityHelper.DigestAlgorithm.SHA256,
|
||||||
|
|
|
@ -18,7 +18,7 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
||||||
ClientProfileBuilder builder = new ClientProfileBuilder();
|
ClientProfileBuilder builder = new ClientProfileBuilder();
|
||||||
builder.setVersion(version.name);
|
builder.setVersion(version.name);
|
||||||
builder.setDir(title);
|
builder.setDir(title);
|
||||||
if(findOption(options, MakeProfileOptionGlobalAssets.class).isPresent()) {
|
if (findOption(options, MakeProfileOptionGlobalAssets.class).isPresent()) {
|
||||||
builder.setAssetDir("assets");
|
builder.setAssetDir("assets");
|
||||||
} else {
|
} else {
|
||||||
builder.setAssetDir("asset" + version.name);
|
builder.setAssetDir("asset" + version.name);
|
||||||
|
@ -46,10 +46,10 @@ public static ClientProfile makeProfile(ClientProfile.Version version, String ti
|
||||||
Set<OptionalFile> optionals = new HashSet<>();
|
Set<OptionalFile> optionals = new HashSet<>();
|
||||||
jvmArgs.add("-XX:+DisableAttachMechanism");
|
jvmArgs.add("-XX:+DisableAttachMechanism");
|
||||||
// Official Mojang launcher java arguments
|
// Official Mojang launcher java arguments
|
||||||
if(version.compareTo(ClientProfile.Version.MC112) <= 0) {
|
if (version.compareTo(ClientProfile.Version.MC112) <= 0) {
|
||||||
jvmArgs.add("-XX:+UseConcMarkSweepGC");
|
jvmArgs.add("-XX:+UseConcMarkSweepGC");
|
||||||
jvmArgs.add("-XX:+CMSIncrementalMode");
|
jvmArgs.add("-XX:+CMSIncrementalMode");
|
||||||
} else if(version.compareTo(ClientProfile.Version.MC118) <= 0) { // 1.13 - 1.16.5
|
} else if (version.compareTo(ClientProfile.Version.MC118) <= 0) { // 1.13 - 1.16.5
|
||||||
jvmArgs.add("-XX:+UseG1GC");
|
jvmArgs.add("-XX:+UseG1GC");
|
||||||
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
jvmArgs.add("-XX:+UnlockExperimentalVMOptions");
|
||||||
} else { // 1.18+
|
} else { // 1.18+
|
||||||
|
@ -236,7 +236,7 @@ public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientP
|
||||||
if (Files.exists(dir.resolve("libraries/forge/launchwrapper-1.12-launcherfixed.jar.jar")) || Files.exists(dir.resolve("libraries/net/minecraft/launchwrapper"))) {
|
if (Files.exists(dir.resolve("libraries/forge/launchwrapper-1.12-launcherfixed.jar.jar")) || Files.exists(dir.resolve("libraries/net/minecraft/launchwrapper"))) {
|
||||||
options.add(new MakeProfileOptionLaunchWrapper());
|
options.add(new MakeProfileOptionLaunchWrapper());
|
||||||
}
|
}
|
||||||
if(globalAssets) {
|
if (globalAssets) {
|
||||||
options.add(new MakeProfileOptionGlobalAssets());
|
options.add(new MakeProfileOptionGlobalAssets());
|
||||||
}
|
}
|
||||||
return options.toArray(new MakeProfileOption[0]);
|
return options.toArray(new MakeProfileOption[0]);
|
||||||
|
|
|
@ -27,13 +27,7 @@
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneOffset;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class AuthManager {
|
public class AuthManager {
|
||||||
|
@ -60,9 +54,6 @@ public String newCheckServerToken(String serverName, String authId) {
|
||||||
.compact();
|
.compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
public record CheckServerTokenInfo(String serverName, String authId) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public CheckServerTokenInfo parseCheckServerToken(String token) {
|
public CheckServerTokenInfo parseCheckServerToken(String token) {
|
||||||
try {
|
try {
|
||||||
var jwt = checkServerTokenParser.parseClaimsJws(token).getBody();
|
var jwt = checkServerTokenParser.parseClaimsJws(token).getBody();
|
||||||
|
@ -72,29 +63,6 @@ public CheckServerTokenInfo parseCheckServerToken(String token) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class CheckServerVerifier implements RestoreResponse.ExtendedTokenProvider {
|
|
||||||
private final LaunchServer server;
|
|
||||||
|
|
||||||
public CheckServerVerifier(LaunchServer server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accept(Client client, AuthProviderPair pair, String extendedToken) {
|
|
||||||
var info = server.authManager.parseCheckServerToken(extendedToken);
|
|
||||||
if(info == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
client.auth_id = info.authId;
|
|
||||||
client.auth = server.config.getAuthProviderPair(info.authId);
|
|
||||||
if(client.permissions == null) client.permissions = new ClientPermissions();
|
|
||||||
client.permissions.addPerm("launchserver.checkserver");
|
|
||||||
client.permissions.addPerm(String.format("launchserver.profile.%s.show", info.serverName));
|
|
||||||
client.setProperty("launchserver.serverName", info.serverName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create AuthContext
|
* Create AuthContext
|
||||||
*
|
*
|
||||||
|
@ -154,7 +122,7 @@ public AuthReport auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswor
|
||||||
String login = context.login;
|
String login = context.login;
|
||||||
try {
|
try {
|
||||||
AuthReport result = provider.authorize(login, context, password, context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context));
|
AuthReport result = provider.authorize(login, context, password, context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context));
|
||||||
if(result == null || result.session == null || result.session.getUser() == null) {
|
if (result == null || result.session == null || result.session.getUser() == null) {
|
||||||
logger.error("AuthCoreProvider {} method 'authorize' return null", context.pair.name);
|
logger.error("AuthCoreProvider {} method 'authorize' return null", context.pair.name);
|
||||||
throw new AuthException("Internal Auth Error");
|
throw new AuthException("Internal Auth Error");
|
||||||
}
|
}
|
||||||
|
@ -175,7 +143,7 @@ public AuthReport auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswor
|
||||||
* Writing authorization information to the Client object
|
* Writing authorization information to the Client object
|
||||||
*/
|
*/
|
||||||
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) {
|
||||||
throw new UnsupportedOperationException("Unsupported legacy session system");
|
throw new UnsupportedOperationException("Unsupported legacy session system");
|
||||||
}
|
}
|
||||||
client.isAuth = true;
|
client.isAuth = true;
|
||||||
|
@ -265,7 +233,7 @@ public PlayerProfile getPlayerProfile(AuthProviderPair pair, UUID uuid, ClientPr
|
||||||
|
|
||||||
public PlayerProfile getPlayerProfile(AuthProviderPair pair, User user) {
|
public PlayerProfile getPlayerProfile(AuthProviderPair pair, User user) {
|
||||||
Map<String, String> properties;
|
Map<String, String> properties;
|
||||||
if(user instanceof UserSupportProperties userSupportProperties) {
|
if (user instanceof UserSupportProperties userSupportProperties) {
|
||||||
properties = userSupportProperties.getProperties();
|
properties = userSupportProperties.getProperties();
|
||||||
} else {
|
} else {
|
||||||
properties = new HashMap<>();
|
properties = new HashMap<>();
|
||||||
|
@ -325,6 +293,32 @@ private AuthRequest.AuthPasswordInterface tryDecryptPasswordPlain(AuthRequest.Au
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record CheckServerTokenInfo(String serverName, String authId) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CheckServerVerifier implements RestoreResponse.ExtendedTokenProvider {
|
||||||
|
private final LaunchServer server;
|
||||||
|
|
||||||
|
public CheckServerVerifier(LaunchServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accept(Client client, AuthProviderPair pair, String extendedToken) {
|
||||||
|
var info = server.authManager.parseCheckServerToken(extendedToken);
|
||||||
|
if (info == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
client.auth_id = info.authId;
|
||||||
|
client.auth = server.config.getAuthProviderPair(info.authId);
|
||||||
|
if (client.permissions == null) client.permissions = new ClientPermissions();
|
||||||
|
client.permissions.addPerm("launchserver.checkserver");
|
||||||
|
client.permissions.addPerm(String.format("launchserver.profile.%s.show", info.serverName));
|
||||||
|
client.setProperty("launchserver.serverName", info.serverName);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class CheckServerReport {
|
public static class CheckServerReport {
|
||||||
public UUID uuid;
|
public UUID uuid;
|
||||||
public User user;
|
public User user;
|
||||||
|
|
|
@ -66,7 +66,7 @@ public KeyAgreementManager(Path keyDirectory) throws IOException, InvalidKeySpec
|
||||||
IOHelper.write(rsaPrivateKeyPath, rsaPrivateKey.getEncoded());
|
IOHelper.write(rsaPrivateKeyPath, rsaPrivateKey.getEncoded());
|
||||||
}
|
}
|
||||||
Path legacySaltPath = keyDirectory.resolve("legacySalt");
|
Path legacySaltPath = keyDirectory.resolve("legacySalt");
|
||||||
if(IOHelper.isFile(legacySaltPath)) {
|
if (IOHelper.isFile(legacySaltPath)) {
|
||||||
legacySalt = new String(IOHelper.read(legacySaltPath), StandardCharsets.UTF_8);
|
legacySalt = new String(IOHelper.read(legacySaltPath), StandardCharsets.UTF_8);
|
||||||
} else {
|
} else {
|
||||||
legacySalt = SecurityHelper.randomStringToken();
|
legacySalt = SecurityHelper.randomStringToken();
|
||||||
|
|
|
@ -33,8 +33,8 @@ public LaunchServerGsonManager(LaunchServerModulesManager modulesManager) {
|
||||||
public void registerAdapters(GsonBuilder builder) {
|
public void registerAdapters(GsonBuilder builder) {
|
||||||
super.registerAdapters(builder);
|
super.registerAdapters(builder);
|
||||||
builder.registerTypeAdapterFactory(RecordTypeAdapterFactory.builder()
|
builder.registerTypeAdapterFactory(RecordTypeAdapterFactory.builder()
|
||||||
.allowMissingComponentValues()
|
.allowMissingComponentValues()
|
||||||
.create());
|
.create());
|
||||||
builder.registerTypeAdapter(TextureProvider.class, new UniversalJsonAdapter<>(TextureProvider.providers));
|
builder.registerTypeAdapter(TextureProvider.class, new UniversalJsonAdapter<>(TextureProvider.providers));
|
||||||
builder.registerTypeAdapter(AuthCoreProvider.class, new UniversalJsonAdapter<>(AuthCoreProvider.providers));
|
builder.registerTypeAdapter(AuthCoreProvider.class, new UniversalJsonAdapter<>(AuthCoreProvider.providers));
|
||||||
builder.registerTypeAdapter(PasswordVerifier.class, new UniversalJsonAdapter<>(PasswordVerifier.providers));
|
builder.registerTypeAdapter(PasswordVerifier.class, new UniversalJsonAdapter<>(PasswordVerifier.providers));
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package pro.gravit.launchserver.manangers.hook;
|
package pro.gravit.launchserver.manangers.hook;
|
||||||
|
|
||||||
import pro.gravit.launchserver.auth.core.User;
|
|
||||||
import pro.gravit.launchserver.manangers.AuthManager;
|
import pro.gravit.launchserver.manangers.AuthManager;
|
||||||
import pro.gravit.launchserver.socket.Client;
|
import pro.gravit.launchserver.socket.Client;
|
||||||
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
|
|
|
@ -65,12 +65,12 @@ public <T> void setProperty(String name, T object) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public<T> T getStaticProperty(String name) {
|
public <T> T getStaticProperty(String name) {
|
||||||
if (staticProperties == null) staticProperties = new HashMap<>();
|
if (staticProperties == null) staticProperties = new HashMap<>();
|
||||||
return (T) staticProperties.get(name);
|
return (T) staticProperties.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public<T> void setStaticProperty(String name, T value) {
|
public <T> void setStaticProperty(String name, T value) {
|
||||||
if (staticProperties == null) staticProperties = new HashMap<>();
|
if (staticProperties == null) staticProperties = new HashMap<>();
|
||||||
staticProperties.put(name, value);
|
staticProperties.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,22 @@ public static void registerResponses() {
|
||||||
providers.register("getPublicKey", GetPublicKeyResponse.class);
|
providers.register("getPublicKey", GetPublicKeyResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getIPFromContext(ChannelHandlerContext ctx) {
|
||||||
|
var handler = ctx.pipeline().get(WebSocketFrameHandler.class);
|
||||||
|
if (handler == null || handler.context == null || handler.context.ip == null) {
|
||||||
|
return IOHelper.getIP(ctx.channel().remoteAddress());
|
||||||
|
}
|
||||||
|
return handler.context.ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getIPFromChannel(Channel channel) {
|
||||||
|
var handler = channel.pipeline().get(WebSocketFrameHandler.class);
|
||||||
|
if (handler == null || handler.context == null || handler.context.ip == null) {
|
||||||
|
return IOHelper.getIP(channel.remoteAddress());
|
||||||
|
}
|
||||||
|
return handler.context.ip;
|
||||||
|
}
|
||||||
|
|
||||||
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
|
public void forEachActiveChannels(BiConsumer<Channel, WebSocketFrameHandler> callback) {
|
||||||
for (Channel channel : channels) {
|
for (Channel channel : channels) {
|
||||||
if (channel == null || channel.pipeline() == null) continue;
|
if (channel == null || channel.pipeline() == null) continue;
|
||||||
|
@ -176,25 +192,9 @@ public void registerClient(Channel channel) {
|
||||||
channels.add(channel);
|
channels.add(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getIPFromContext(ChannelHandlerContext ctx) {
|
|
||||||
var handler = ctx.pipeline().get(WebSocketFrameHandler.class);
|
|
||||||
if(handler == null || handler.context == null || handler.context.ip == null) {
|
|
||||||
return IOHelper.getIP(ctx.channel().remoteAddress());
|
|
||||||
}
|
|
||||||
return handler.context.ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getIPFromChannel(Channel channel) {
|
|
||||||
var handler = channel.pipeline().get(WebSocketFrameHandler.class);
|
|
||||||
if(handler == null || handler.context == null || handler.context.ip == null) {
|
|
||||||
return IOHelper.getIP(channel.remoteAddress());
|
|
||||||
}
|
|
||||||
return handler.context.ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendObject(ChannelHandlerContext ctx, Object obj) {
|
public void sendObject(ChannelHandlerContext ctx, Object obj) {
|
||||||
String msg = gson.toJson(obj, WebSocketEvent.class);
|
String msg = gson.toJson(obj, WebSocketEvent.class);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to {}: {}", getIPFromContext(ctx), msg);
|
logger.trace("Send to {}: {}", getIPFromContext(ctx), msg);
|
||||||
}
|
}
|
||||||
ctx.writeAndFlush(new TextWebSocketFrame(msg), ctx.voidPromise());
|
ctx.writeAndFlush(new TextWebSocketFrame(msg), ctx.voidPromise());
|
||||||
|
@ -202,7 +202,7 @@ public void sendObject(ChannelHandlerContext ctx, Object obj) {
|
||||||
|
|
||||||
public void sendObject(ChannelHandlerContext ctx, Object obj, Type type) {
|
public void sendObject(ChannelHandlerContext ctx, Object obj, Type type) {
|
||||||
String msg = gson.toJson(obj, type);
|
String msg = gson.toJson(obj, type);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to {}: {}", getIPFromContext(ctx), msg);
|
logger.trace("Send to {}: {}", getIPFromContext(ctx), msg);
|
||||||
}
|
}
|
||||||
ctx.writeAndFlush(new TextWebSocketFrame(msg), ctx.voidPromise());
|
ctx.writeAndFlush(new TextWebSocketFrame(msg), ctx.voidPromise());
|
||||||
|
@ -210,7 +210,7 @@ public void sendObject(ChannelHandlerContext ctx, Object obj, Type type) {
|
||||||
|
|
||||||
public void sendObject(Channel channel, Object obj) {
|
public void sendObject(Channel channel, Object obj) {
|
||||||
String msg = gson.toJson(obj, WebSocketEvent.class);
|
String msg = gson.toJson(obj, WebSocketEvent.class);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to channel {}: {}", getIPFromChannel(channel), msg);
|
logger.trace("Send to channel {}: {}", getIPFromChannel(channel), msg);
|
||||||
}
|
}
|
||||||
channel.writeAndFlush(new TextWebSocketFrame(msg), channel.voidPromise());
|
channel.writeAndFlush(new TextWebSocketFrame(msg), channel.voidPromise());
|
||||||
|
@ -218,7 +218,7 @@ public void sendObject(Channel channel, Object obj) {
|
||||||
|
|
||||||
public void sendObject(Channel channel, Object obj, Type type) {
|
public void sendObject(Channel channel, Object obj, Type type) {
|
||||||
String msg = gson.toJson(obj, type);
|
String msg = gson.toJson(obj, type);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to channel {}: {}", getIPFromChannel(channel), msg);
|
logger.trace("Send to channel {}: {}", getIPFromChannel(channel), msg);
|
||||||
}
|
}
|
||||||
channel.writeAndFlush(new TextWebSocketFrame(msg), channel.voidPromise());
|
channel.writeAndFlush(new TextWebSocketFrame(msg), channel.voidPromise());
|
||||||
|
@ -226,7 +226,7 @@ public void sendObject(Channel channel, Object obj, Type type) {
|
||||||
|
|
||||||
public void sendObjectAll(Object obj) {
|
public void sendObjectAll(Object obj) {
|
||||||
String msg = gson.toJson(obj, WebSocketEvent.class);
|
String msg = gson.toJson(obj, WebSocketEvent.class);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to all: {}", msg);
|
logger.trace("Send to all: {}", msg);
|
||||||
}
|
}
|
||||||
for (Channel ch : channels) {
|
for (Channel ch : channels) {
|
||||||
|
@ -236,7 +236,7 @@ public void sendObjectAll(Object obj) {
|
||||||
|
|
||||||
public void sendObjectAll(Object obj, Type type) {
|
public void sendObjectAll(Object obj, Type type) {
|
||||||
String msg = gson.toJson(obj, type);
|
String msg = gson.toJson(obj, type);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to all: {}", msg);
|
logger.trace("Send to all: {}", msg);
|
||||||
}
|
}
|
||||||
for (Channel ch : channels) {
|
for (Channel ch : channels) {
|
||||||
|
@ -252,7 +252,7 @@ public void sendObjectToUUID(UUID userUuid, Object obj, Type type) {
|
||||||
Client client = wsHandler.getClient();
|
Client client = wsHandler.getClient();
|
||||||
if (client == null || !userUuid.equals(client.uuid)) continue;
|
if (client == null || !userUuid.equals(client.uuid)) continue;
|
||||||
String msg = gson.toJson(obj, type);
|
String msg = gson.toJson(obj, type);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send to {}({}): {}", getIPFromChannel(ch), userUuid, msg);
|
logger.trace("Send to {}({}): {}", getIPFromChannel(ch), userUuid, msg);
|
||||||
}
|
}
|
||||||
ch.writeAndFlush(new TextWebSocketFrame(msg), ch.voidPromise());
|
ch.writeAndFlush(new TextWebSocketFrame(msg), ch.voidPromise());
|
||||||
|
@ -320,7 +320,7 @@ public boolean kickByIP(String ip, boolean isClose) {
|
||||||
|
|
||||||
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj) {
|
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj) {
|
||||||
String msg = gson.toJson(obj, WebSocketEvent.class);
|
String msg = gson.toJson(obj, WebSocketEvent.class);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send and close {}: {}", getIPFromContext(ctx), msg);
|
logger.trace("Send and close {}: {}", getIPFromContext(ctx), msg);
|
||||||
}
|
}
|
||||||
ctx.writeAndFlush(new TextWebSocketFrame(msg)).addListener(ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(new TextWebSocketFrame(msg)).addListener(ChannelFutureListener.CLOSE);
|
||||||
|
@ -328,7 +328,7 @@ public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj) {
|
||||||
|
|
||||||
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj, Type type) {
|
public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj, Type type) {
|
||||||
String msg = gson.toJson(obj, type);
|
String msg = gson.toJson(obj, type);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send and close {}: {}", getIPFromContext(ctx), msg);
|
logger.trace("Send and close {}: {}", getIPFromContext(ctx), msg);
|
||||||
}
|
}
|
||||||
ctx.writeAndFlush(new TextWebSocketFrame(msg)).addListener(ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(new TextWebSocketFrame(msg)).addListener(ChannelFutureListener.CLOSE);
|
||||||
|
@ -337,7 +337,7 @@ public void sendObjectAndClose(ChannelHandlerContext ctx, Object obj, Type type)
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void sendEvent(EventResult obj) {
|
public void sendEvent(EventResult obj) {
|
||||||
String msg = gson.toJson(obj, WebSocketEvent.class);
|
String msg = gson.toJson(obj, WebSocketEvent.class);
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Send event: {}", msg);
|
logger.trace("Send event: {}", msg);
|
||||||
}
|
}
|
||||||
channels.writeAndFlush(new TextWebSocketFrame(msg), ChannelMatchers.all(), true);
|
channels.writeAndFlush(new TextWebSocketFrame(msg), ChannelMatchers.all(), true);
|
||||||
|
|
|
@ -64,7 +64,7 @@ protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
|
||||||
logger.error("WebSocket frame handler hook error", ex);
|
logger.error("WebSocket frame handler hook error", ex);
|
||||||
}
|
}
|
||||||
if (frame instanceof TextWebSocketFrame) {
|
if (frame instanceof TextWebSocketFrame) {
|
||||||
if(logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.trace("Message from {}: {}", context.ip == null ? IOHelper.getIP(ctx.channel().remoteAddress()) : context.ip, ((TextWebSocketFrame) frame).text());
|
logger.trace("Message from {}: {}", context.ip == null ? IOHelper.getIP(ctx.channel().remoteAddress()) : context.ip, ((TextWebSocketFrame) frame).text());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package pro.gravit.launchserver.socket.response.auth;
|
package pro.gravit.launchserver.socket.response.auth;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import pro.gravit.launcher.ClientPermissions;
|
|
||||||
import pro.gravit.launcher.events.request.AdditionalDataRequestEvent;
|
import pro.gravit.launcher.events.request.AdditionalDataRequestEvent;
|
||||||
import pro.gravit.launchserver.auth.AuthProviderPair;
|
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||||
import pro.gravit.launchserver.auth.core.User;
|
import pro.gravit.launchserver.auth.core.User;
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
import pro.gravit.launchserver.socket.response.SimpleResponse;
|
||||||
import pro.gravit.utils.HookException;
|
import pro.gravit.utils.HookException;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class AuthResponse extends SimpleResponse {
|
public class AuthResponse extends SimpleResponse {
|
||||||
private transient final Logger logger = LogManager.getLogger();
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
public String login;
|
public String login;
|
||||||
|
@ -46,6 +44,7 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
server.authHookManager.preHook.hook(context, clientData);
|
server.authHookManager.preHook.hook(context, clientData);
|
||||||
context.report = server.authManager.auth(context, password);
|
context.report = server.authManager.auth(context, password);
|
||||||
server.authHookManager.postHook.hook(context, clientData);
|
server.authHookManager.postHook.hook(context, clientData);
|
||||||
|
result.permissions = context.report.session() != null ? (context.report.session().getUser() != null ? context.report.session().getUser().getPermissions() : null) : null;
|
||||||
if (context.report.isUsingOAuth()) {
|
if (context.report.isUsingOAuth()) {
|
||||||
result.oauth = new AuthRequestEvent.OAuthRequestEvent(context.report.oauthAccessToken(), context.report.oauthRefreshToken(), context.report.oauthExpire());
|
result.oauth = new AuthRequestEvent.OAuthRequestEvent(context.report.oauthAccessToken(), context.report.oauthRefreshToken(), context.report.oauthExpire());
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public void execute(ChannelHandlerContext ctx, Client pClient) {
|
||||||
try {
|
try {
|
||||||
server.authHookManager.checkServerHook.hook(this, pClient);
|
server.authHookManager.checkServerHook.hook(this, pClient);
|
||||||
AuthManager.CheckServerReport report = server.authManager.checkServer(pClient, username, serverID);
|
AuthManager.CheckServerReport report = server.authManager.checkServer(pClient, username, serverID);
|
||||||
if(report == null) {
|
if (report == null) {
|
||||||
sendError("User not verified");
|
sendError("User not verified");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ public static void exit(LaunchServer server, WebSocketFrameHandler wsHandler, Ch
|
||||||
Client chClient = wsHandler.getClient();
|
Client chClient = wsHandler.getClient();
|
||||||
Client newCusClient = new Client();
|
Client newCusClient = new Client();
|
||||||
newCusClient.checkSign = chClient.checkSign;
|
newCusClient.checkSign = chClient.checkSign;
|
||||||
if(chClient.staticProperties != null) {
|
if (chClient.staticProperties != null) {
|
||||||
newCusClient.staticProperties = new HashMap<>(chClient.staticProperties);
|
newCusClient.staticProperties = new HashMap<>(chClient.staticProperties);
|
||||||
}
|
}
|
||||||
wsHandler.setClient(newCusClient);
|
wsHandler.setClient(newCusClient);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import pro.gravit.launcher.events.request.FetchClientProfileKeyRequestEvent;
|
import pro.gravit.launcher.events.request.FetchClientProfileKeyRequestEvent;
|
||||||
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.session.UserSessionSupportKeys;
|
import pro.gravit.launchserver.auth.core.interfaces.session.UserSessionSupportKeys;
|
||||||
import pro.gravit.launchserver.socket.Client;
|
import pro.gravit.launchserver.socket.Client;
|
||||||
|
@ -22,7 +21,7 @@ public void execute(ChannelHandlerContext ctx, Client client) throws Exception {
|
||||||
}
|
}
|
||||||
UserSession session = client.sessionObject;
|
UserSession session = client.sessionObject;
|
||||||
UserSessionSupportKeys.ClientProfileKeys keys;
|
UserSessionSupportKeys.ClientProfileKeys keys;
|
||||||
if(session instanceof UserSessionSupportKeys support) {
|
if (session instanceof UserSessionSupportKeys support) {
|
||||||
keys = support.getClientProfileKeys();
|
keys = support.getClientProfileKeys();
|
||||||
} else {
|
} else {
|
||||||
keys = server.authManager.createClientProfileKeys(client.uuid);
|
keys = server.authManager.createClientProfileKeys(client.uuid);
|
||||||
|
|
|
@ -21,7 +21,7 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
||||||
List<GetAvailabilityAuthRequestEvent.AuthAvailability> list = new ArrayList<>();
|
List<GetAvailabilityAuthRequestEvent.AuthAvailability> list = new ArrayList<>();
|
||||||
for (AuthProviderPair pair : server.config.auth.values()) {
|
for (AuthProviderPair pair : server.config.auth.values()) {
|
||||||
var rca = pair.isSupport(AuthSupportRemoteClientAccess.class);
|
var rca = pair.isSupport(AuthSupportRemoteClientAccess.class);
|
||||||
if(rca != null) {
|
if (rca != null) {
|
||||||
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName,
|
list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName,
|
||||||
pair.core.getDetails(client), rca.getClientApiUrl(), rca.getClientApiFeatures()));
|
pair.core.getDetails(client), rca.getClientApiUrl(), rca.getClientApiFeatures()));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -13,20 +13,6 @@
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ProfilesResponse extends SimpleResponse {
|
public class ProfilesResponse extends SimpleResponse {
|
||||||
@Override
|
|
||||||
public String getType() {
|
|
||||||
return "profiles";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(ChannelHandlerContext ctx, Client client) {
|
|
||||||
if (server.config.protectHandler instanceof ProfilesProtectHandler && !((ProfilesProtectHandler) server.config.protectHandler).canGetProfiles(client)) {
|
|
||||||
sendError("Access denied");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendResult(new ProfilesRequestEvent(getListVisibleProfiles(server, client)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<ClientProfile> getListVisibleProfiles(LaunchServer server, Client client) {
|
public static List<ClientProfile> getListVisibleProfiles(LaunchServer server, Client client) {
|
||||||
List<ClientProfile> profileList;
|
List<ClientProfile> profileList;
|
||||||
Set<ClientProfile> serverProfiles = server.getProfiles();
|
Set<ClientProfile> serverProfiles = server.getProfiles();
|
||||||
|
@ -42,4 +28,18 @@ public static List<ClientProfile> getListVisibleProfiles(LaunchServer server, Cl
|
||||||
}
|
}
|
||||||
return profileList;
|
return profileList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "profiles";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(ChannelHandlerContext ctx, Client client) {
|
||||||
|
if (server.config.protectHandler instanceof ProfilesProtectHandler && !((ProfilesProtectHandler) server.config.protectHandler).canGetProfiles(client)) {
|
||||||
|
sendError("Access denied");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendResult(new ProfilesRequestEvent(getListVisibleProfiles(server, client)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
import pro.gravit.launcher.request.Request;
|
import pro.gravit.launcher.request.Request;
|
||||||
import pro.gravit.launcher.request.RequestService;
|
import pro.gravit.launcher.request.RequestService;
|
||||||
import pro.gravit.launcher.request.WebSocketEvent;
|
import pro.gravit.launcher.request.WebSocketEvent;
|
||||||
import pro.gravit.launcher.request.websockets.ClientWebSocketService;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
public class BasicLauncherEventHandler implements RequestService.EventHandler {
|
public class BasicLauncherEventHandler implements RequestService.EventHandler {
|
||||||
|
|
|
@ -140,7 +140,7 @@ 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) {
|
if (customJvmOptions != null) {
|
||||||
args.addAll(customJvmOptions);
|
args.addAll(customJvmOptions);
|
||||||
}
|
}
|
||||||
args.add("-cp");
|
args.add("-cp");
|
||||||
|
|
|
@ -246,14 +246,14 @@ public void start(String... args) throws Throwable {
|
||||||
try {
|
try {
|
||||||
service = StdWebSocketService.initWebSockets(address).get();
|
service = StdWebSocketService.initWebSockets(address).get();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if(LogHelper.isDebugEnabled()) {
|
if (LogHelper.isDebugEnabled()) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
}
|
}
|
||||||
LogHelper.warning("Launcher in offline mode");
|
LogHelper.warning("Launcher in offline mode");
|
||||||
service = initOffline();
|
service = initOffline();
|
||||||
}
|
}
|
||||||
Request.setRequestService(service);
|
Request.setRequestService(service);
|
||||||
if(service instanceof StdWebSocketService) {
|
if (service instanceof StdWebSocketService) {
|
||||||
((StdWebSocketService) service).reconnectCallback = () ->
|
((StdWebSocketService) service).reconnectCallback = () ->
|
||||||
{
|
{
|
||||||
LogHelper.debug("WebSocket connect closed. Try reconnect");
|
LogHelper.debug("WebSocket connect closed. Try reconnect");
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package pro.gravit.launcher.api;
|
package pro.gravit.launcher.api;
|
||||||
|
|
||||||
import pro.gravit.launcher.LauncherEngine;
|
import pro.gravit.launcher.LauncherEngine;
|
||||||
import pro.gravit.launcher.profiles.ClientProfile;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
|
||||||
|
|
||||||
public class SystemService {
|
public class SystemService {
|
||||||
private SystemService() {
|
private SystemService() {
|
||||||
|
|
|
@ -121,7 +121,7 @@ public static void main(String[] args) throws Throwable {
|
||||||
List<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList());
|
List<URL> classpathURLs = classpath.stream().map(IOHelper::toURL).collect(Collectors.toList());
|
||||||
// Start client with WatchService monitoring
|
// Start client with WatchService monitoring
|
||||||
RequestService service;
|
RequestService service;
|
||||||
if(params.offlineMode) {
|
if (params.offlineMode) {
|
||||||
service = initOffline(LauncherEngine.modulesManager, params);
|
service = initOffline(LauncherEngine.modulesManager, params);
|
||||||
Request.setRequestService(service);
|
Request.setRequestService(service);
|
||||||
} else {
|
} else {
|
||||||
|
@ -231,13 +231,13 @@ public static RequestService initOffline(LauncherModulesManager modulesManager,
|
||||||
|
|
||||||
public static void applyClientOfflineProcessors(OfflineRequestService service, ClientLauncherProcess.ClientParams params) {
|
public static void applyClientOfflineProcessors(OfflineRequestService service, ClientLauncherProcess.ClientParams params) {
|
||||||
service.registerRequestProcessor(ProfileByUsernameRequest.class, (r) -> {
|
service.registerRequestProcessor(ProfileByUsernameRequest.class, (r) -> {
|
||||||
if(params.playerProfile.username.equals(r.username)) {
|
if (params.playerProfile.username.equals(r.username)) {
|
||||||
return new ProfileByUsernameRequestEvent(params.playerProfile);
|
return new ProfileByUsernameRequestEvent(params.playerProfile);
|
||||||
}
|
}
|
||||||
throw new RequestException("User not found");
|
throw new RequestException("User not found");
|
||||||
});
|
});
|
||||||
service.registerRequestProcessor(ProfileByUUIDRequest.class, (r) -> {
|
service.registerRequestProcessor(ProfileByUUIDRequest.class, (r) -> {
|
||||||
if(params.playerProfile.uuid.equals(r.uuid)) {
|
if (params.playerProfile.uuid.equals(r.uuid)) {
|
||||||
return new ProfileByUUIDRequestEvent(params.playerProfile);
|
return new ProfileByUUIDRequestEvent(params.playerProfile);
|
||||||
}
|
}
|
||||||
throw new RequestException("User not found");
|
throw new RequestException("User not found");
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import pro.gravit.launcher.Launcher;
|
import pro.gravit.launcher.Launcher;
|
||||||
import pro.gravit.launcher.LauncherConfig;
|
import pro.gravit.launcher.LauncherConfig;
|
||||||
import pro.gravit.launcher.LauncherEngine;
|
import pro.gravit.launcher.LauncherEngine;
|
||||||
import pro.gravit.launcher.LauncherNetworkAPI;
|
|
||||||
import pro.gravit.launcher.client.events.client.ClientProcessBuilderCreateEvent;
|
import pro.gravit.launcher.client.events.client.ClientProcessBuilderCreateEvent;
|
||||||
import pro.gravit.launcher.client.events.client.ClientProcessBuilderLaunchedEvent;
|
import pro.gravit.launcher.client.events.client.ClientProcessBuilderLaunchedEvent;
|
||||||
import pro.gravit.launcher.client.events.client.ClientProcessBuilderParamsWrittedEvent;
|
import pro.gravit.launcher.client.events.client.ClientProcessBuilderParamsWrittedEvent;
|
||||||
|
@ -28,7 +27,6 @@
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -79,7 +77,7 @@ public ClientLauncherProcess(Path clientDir, Path assetDir, JavaHelper.JavaVersi
|
||||||
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
this.params.resourcePackDir = resourcePackDir.toAbsolutePath().toString();
|
||||||
this.params.assetDir = assetDir.toAbsolutePath().toString();
|
this.params.assetDir = assetDir.toAbsolutePath().toString();
|
||||||
Path nativesPath = workDir.resolve("natives").resolve(JVMHelper.OS_TYPE.name).resolve(javaVersion.arch.name);
|
Path nativesPath = workDir.resolve("natives").resolve(JVMHelper.OS_TYPE.name).resolve(javaVersion.arch.name);
|
||||||
if(!Files.isDirectory(nativesPath)) {
|
if (!Files.isDirectory(nativesPath)) {
|
||||||
nativesPath = workDir.resolve("natives");
|
nativesPath = workDir.resolve("natives");
|
||||||
}
|
}
|
||||||
this.params.nativesDir = nativesPath.toString();
|
this.params.nativesDir = nativesPath.toString();
|
||||||
|
@ -158,7 +156,7 @@ public void start(boolean pipeOutput) throws IOException, InterruptedException {
|
||||||
.map(Path::toString)
|
.map(Path::toString)
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
if(Launcher.getConfig().environment != LauncherConfig.LauncherEnvironment.PROD) {
|
if (Launcher.getConfig().environment != LauncherConfig.LauncherEnvironment.PROD) {
|
||||||
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEV_PROPERTY, String.valueOf(LogHelper.isDevEnabled())));
|
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEV_PROPERTY, String.valueOf(LogHelper.isDevEnabled())));
|
||||||
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, String.valueOf(LogHelper.isDebugEnabled())));
|
processArgs.add(JVMHelper.jvmProperty(LogHelper.DEBUG_PROPERTY, String.valueOf(LogHelper.isDebugEnabled())));
|
||||||
processArgs.add(JVMHelper.jvmProperty(LogHelper.STACKTRACE_PROPERTY, String.valueOf(LogHelper.isStacktraceEnabled())));
|
processArgs.add(JVMHelper.jvmProperty(LogHelper.STACKTRACE_PROPERTY, String.valueOf(LogHelper.isStacktraceEnabled())));
|
||||||
|
@ -211,7 +209,7 @@ private void applyJava9Params(List<String> processArgs) {
|
||||||
if (modulesAdd.length() > 0) modulesAdd.append(",");
|
if (modulesAdd.length() > 0) modulesAdd.append(",");
|
||||||
modulesAdd.append(moduleName);
|
modulesAdd.append(moduleName);
|
||||||
}
|
}
|
||||||
for(String modulePath : jvmModulesPaths) {
|
for (String modulePath : jvmModulesPaths) {
|
||||||
if (modulesPath.length() > 0) modulesPath.append(File.pathSeparator);
|
if (modulesPath.length() > 0) modulesPath.append(File.pathSeparator);
|
||||||
modulesPath.append(modulePath);
|
modulesPath.append(modulePath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,7 +163,7 @@ private Result modernPing(HInput input, HOutput output) throws IOException {
|
||||||
|
|
||||||
// Parse JSON response
|
// Parse JSON response
|
||||||
JsonObject object = JsonParser.parseString(response).getAsJsonObject();
|
JsonObject object = JsonParser.parseString(response).getAsJsonObject();
|
||||||
if(object.has("error")) {
|
if (object.has("error")) {
|
||||||
throw new IOException(object.get("error").getAsString());
|
throw new IOException(object.get("error").getAsString());
|
||||||
}
|
}
|
||||||
JsonObject playersObject = object.get("players").getAsJsonObject();
|
JsonObject playersObject = object.get("players").getAsJsonObject();
|
||||||
|
|
|
@ -24,10 +24,10 @@ public String getUsageDescription() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invoke(String... args) throws Exception {
|
public void invoke(String... args) throws Exception {
|
||||||
for(LauncherModule module : LauncherEngine.modulesManager.getModules()) {
|
for (LauncherModule module : LauncherEngine.modulesManager.getModules()) {
|
||||||
LauncherModuleInfo info = module.getModuleInfo();
|
LauncherModuleInfo info = module.getModuleInfo();
|
||||||
LauncherTrustManager.CheckClassResult checkStatus = module.getCheckResult();
|
LauncherTrustManager.CheckClassResult checkStatus = module.getCheckResult();
|
||||||
if(!ConsoleManager.isConsoleUnlock) {
|
if (!ConsoleManager.isConsoleUnlock) {
|
||||||
LogHelper.info("[MODULE] %s v: %s", info.name, info.version.getVersionString());
|
LogHelper.info("[MODULE] %s v: %s", info.name, info.version.getVersionString());
|
||||||
} else {
|
} else {
|
||||||
LogHelper.info("[MODULE] %s v: %s p: %d deps: %s sig: %s", info.name, info.version.getVersionString(), info.priority, Arrays.toString(info.dependencies), checkStatus == null ? "null" : checkStatus.type);
|
LogHelper.info("[MODULE] %s v: %s p: %d deps: %s sig: %s", info.name, info.version.getVersionString(), info.priority, Arrays.toString(info.dependencies), checkStatus == null ? "null" : checkStatus.type);
|
||||||
|
|
|
@ -57,7 +57,7 @@ public static void main(String[] args) throws Throwable {
|
||||||
ConsoleManager.initConsole();
|
ConsoleManager.initConsole();
|
||||||
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
|
LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase());
|
||||||
RequestService service;
|
RequestService service;
|
||||||
if(offlineMode) {
|
if (offlineMode) {
|
||||||
OfflineRequestService offlineRequestService = new OfflineRequestService();
|
OfflineRequestService offlineRequestService = new OfflineRequestService();
|
||||||
LauncherEngine.applyBasicOfflineProcessors(offlineRequestService);
|
LauncherEngine.applyBasicOfflineProcessors(offlineRequestService);
|
||||||
OfflineModeEvent event = new OfflineModeEvent(offlineRequestService);
|
OfflineModeEvent event = new OfflineModeEvent(offlineRequestService);
|
||||||
|
|
|
@ -18,7 +18,7 @@ public static void checkCertificatesSuccess(X509Certificate[] certs) throws Exce
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String findLibrary(ClassLoader classLoader, String library) {
|
public static String findLibrary(ClassLoader classLoader, String library) {
|
||||||
if(classLoader instanceof ClientClassLoader) {
|
if (classLoader instanceof ClientClassLoader) {
|
||||||
ClientClassLoader clientClassLoader = (ClientClassLoader) classLoader;
|
ClientClassLoader clientClassLoader = (ClientClassLoader) classLoader;
|
||||||
return clientClassLoader.findLibrary(library);
|
return clientClassLoader.findLibrary(library);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package pro.gravit.launcher.utils;
|
package pro.gravit.launcher.utils;
|
||||||
|
|
||||||
import pro.gravit.launcher.AsyncDownloader;
|
import pro.gravit.launcher.AsyncDownloader;
|
||||||
import pro.gravit.launcher.Launcher;
|
|
||||||
import pro.gravit.launcher.LauncherEngine;
|
import pro.gravit.launcher.LauncherEngine;
|
||||||
import pro.gravit.launcher.LauncherInject;
|
import pro.gravit.launcher.LauncherInject;
|
||||||
import pro.gravit.launcher.events.request.LauncherRequestEvent;
|
|
||||||
import pro.gravit.launcher.request.update.LauncherRequest;
|
import pro.gravit.launcher.request.update.LauncherRequest;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
@ -13,12 +11,10 @@
|
||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.security.KeyManagementException;
|
import java.security.KeyManagementException;
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
@ -30,14 +26,16 @@
|
||||||
public class LauncherUpdater {
|
public class LauncherUpdater {
|
||||||
@LauncherInject("launcher.certificatePinning")
|
@LauncherInject("launcher.certificatePinning")
|
||||||
private static boolean isCertificatePinning;
|
private static boolean isCertificatePinning;
|
||||||
|
|
||||||
public static void nothing() {
|
public static void nothing() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Path getLauncherPath() {
|
private static Path getLauncherPath() {
|
||||||
Path pathToCore = IOHelper.getCodeSource(IOHelper.class);
|
Path pathToCore = IOHelper.getCodeSource(IOHelper.class);
|
||||||
Path pathToApi = IOHelper.getCodeSource(LauncherRequest.class);
|
Path pathToApi = IOHelper.getCodeSource(LauncherRequest.class);
|
||||||
Path pathToSelf = IOHelper.getCodeSource(LauncherUpdater.class);
|
Path pathToSelf = IOHelper.getCodeSource(LauncherUpdater.class);
|
||||||
if(pathToCore.equals(pathToApi) && pathToCore.equals(pathToSelf)) {
|
if (pathToCore.equals(pathToApi) && pathToCore.equals(pathToSelf)) {
|
||||||
return pathToCore;
|
return pathToCore;
|
||||||
} else {
|
} else {
|
||||||
throw new SecurityException("Found split-jar launcher");
|
throw new SecurityException("Found split-jar launcher");
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package pro.gravit.launcher.utils;
|
package pro.gravit.launcher.utils;
|
||||||
|
|
||||||
import pro.gravit.utils.helper.JVMHelper;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.security.AccessController;
|
|
||||||
import java.security.PrivilegedAction;
|
|
||||||
|
|
||||||
public final class NativeJVMHalt {
|
public final class NativeJVMHalt {
|
||||||
public final int haltCode;
|
public final int haltCode;
|
||||||
|
@ -30,7 +27,7 @@ public static void haltA(int code) {
|
||||||
exitMethod.invoke(null, code);
|
exitMethod.invoke(null, code);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
th[1] = e;
|
th[1] = e;
|
||||||
if(LogHelper.isDevEnabled()) {
|
if (LogHelper.isDevEnabled()) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
package pro.gravit.launcher;
|
package pro.gravit.launcher;
|
||||||
|
|
||||||
import pro.gravit.launcher.serialize.HInput;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ClientPermissions {
|
public class ClientPermissions {
|
||||||
|
@ -71,7 +68,7 @@ public void addPerm(String perm) {
|
||||||
perms = new ArrayList<>(1);
|
perms = new ArrayList<>(1);
|
||||||
}
|
}
|
||||||
perms.add(perm);
|
perms.add(perm);
|
||||||
if(available == null) {
|
if (available == null) {
|
||||||
available = new ArrayList<>(1);
|
available = new ArrayList<>(1);
|
||||||
}
|
}
|
||||||
available.add(new PermissionPattern(perm));
|
available.add(new PermissionPattern(perm));
|
||||||
|
@ -81,7 +78,7 @@ public void removePerm(String action) {
|
||||||
if (perms == null) {
|
if (perms == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(available == null) {
|
if (available == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
perms.remove(action);
|
perms.remove(action);
|
||||||
|
@ -110,11 +107,11 @@ public static class PermissionPattern {
|
||||||
|
|
||||||
public PermissionPattern(String pattern) {
|
public PermissionPattern(String pattern) {
|
||||||
List<String> prepare = new ArrayList<>();
|
List<String> prepare = new ArrayList<>();
|
||||||
for(int i=0;true;) {
|
for (int i = 0; true; ) {
|
||||||
int pos = pattern.indexOf("*", i);
|
int pos = pattern.indexOf("*", i);
|
||||||
if(pos >= 0) {
|
if (pos >= 0) {
|
||||||
prepare.add(pattern.substring(i, pos));
|
prepare.add(pattern.substring(i, pos));
|
||||||
i = pos+1;
|
i = pos + 1;
|
||||||
} else {
|
} else {
|
||||||
prepare.add(pattern.substring(i));
|
prepare.add(pattern.substring(i));
|
||||||
break;
|
break;
|
||||||
|
@ -129,23 +126,23 @@ public int getPriority() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean match(String str) {
|
public boolean match(String str) {
|
||||||
if(parts.length == 0) {
|
if (parts.length == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(parts.length == 1) {
|
if (parts.length == 1) {
|
||||||
return parts[0].equals(str);
|
return parts[0].equals(str);
|
||||||
}
|
}
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
if(!str.startsWith(parts[0])) {
|
if (!str.startsWith(parts[0])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(!str.endsWith(parts[parts.length-1])) {
|
if (!str.endsWith(parts[parts.length - 1])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for(int i=1;i<parts.length-1;++i) {
|
for (int i = 1; i < parts.length - 1; ++i) {
|
||||||
int pos = str.indexOf(parts[i], offset);
|
int pos = str.indexOf(parts[i], offset);
|
||||||
if(pos >= 0) {
|
if (pos >= 0) {
|
||||||
offset = pos+1;
|
offset = pos + 1;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -484,7 +484,8 @@ public enum Version {
|
||||||
MC1182("1.18.2", 758),
|
MC1182("1.18.2", 758),
|
||||||
MC119("1.19", 759),
|
MC119("1.19", 759),
|
||||||
MC1191("1.19.1", 760),
|
MC1191("1.19.1", 760),
|
||||||
MC1192("1.19.2", 760);
|
MC1192("1.19.2", 760),
|
||||||
|
MC1193("1.19.3", 761);
|
||||||
private static final Map<String, Version> VERSIONS;
|
private static final Map<String, Version> VERSIONS;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package pro.gravit.launcher.profiles;
|
package pro.gravit.launcher.profiles;
|
||||||
|
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.VerifyHelper;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -30,10 +29,10 @@ public PlayerProfile(UUID uuid, String username, Texture skin, Texture cloak, Ma
|
||||||
this.skin = skin;
|
this.skin = skin;
|
||||||
this.cloak = cloak;
|
this.cloak = cloak;
|
||||||
this.assets = new HashMap<>();
|
this.assets = new HashMap<>();
|
||||||
if(skin != null) {
|
if (skin != null) {
|
||||||
this.assets.put("SKIN", skin);
|
this.assets.put("SKIN", skin);
|
||||||
}
|
}
|
||||||
if(cloak != null) {
|
if (cloak != null) {
|
||||||
this.assets.put("CAPE", cloak);
|
this.assets.put("CAPE", cloak);
|
||||||
}
|
}
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
public class ArchTrigger extends OptionalTrigger {
|
public class ArchTrigger extends OptionalTrigger {
|
||||||
public JVMHelper.ARCH arch;
|
public JVMHelper.ARCH arch;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isTriggered(OptionalFile optional, OptionalTriggerContext context) {
|
protected boolean isTriggered(OptionalFile optional, OptionalTriggerContext context) {
|
||||||
return context.getJavaVersion().arch == arch;
|
return context.getJavaVersion().arch == arch;
|
||||||
|
|
|
@ -28,17 +28,17 @@ public abstract class Request<R extends WebSocketEvent> implements WebSocketRequ
|
||||||
public final UUID requestUUID = UUID.randomUUID();
|
public final UUID requestUUID = UUID.randomUUID();
|
||||||
private transient final AtomicBoolean started = new AtomicBoolean(false);
|
private transient final AtomicBoolean started = new AtomicBoolean(false);
|
||||||
|
|
||||||
public static void setRequestService(RequestService service) {
|
|
||||||
requestService = service;
|
|
||||||
if(service instanceof StdWebSocketService) {
|
|
||||||
Request.service = (StdWebSocketService) service;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RequestService getRequestService() {
|
public static RequestService getRequestService() {
|
||||||
return requestService;
|
return requestService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setRequestService(RequestService service) {
|
||||||
|
requestService = service;
|
||||||
|
if (service instanceof StdWebSocketService) {
|
||||||
|
Request.service = (StdWebSocketService) service;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isAvailable() {
|
public static boolean isAvailable() {
|
||||||
return requestService != null;
|
return requestService != null;
|
||||||
}
|
}
|
||||||
|
@ -121,22 +121,10 @@ public static RequestRestoreReport reconnect() throws Exception {
|
||||||
return restore();
|
return restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RequestRestoreReport {
|
|
||||||
public final boolean legacySession;
|
|
||||||
public final boolean refreshed;
|
|
||||||
public final List<String> invalidExtendedTokens;
|
|
||||||
|
|
||||||
public RequestRestoreReport(boolean legacySession, boolean refreshed, List<String> invalidExtendedTokens) {
|
|
||||||
this.legacySession = legacySession;
|
|
||||||
this.refreshed = refreshed;
|
|
||||||
this.invalidExtendedTokens = invalidExtendedTokens;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RequestRestoreReport restore() throws Exception {
|
public static RequestRestoreReport restore() throws Exception {
|
||||||
boolean refreshed = false;
|
boolean refreshed = false;
|
||||||
RestoreRequest request;
|
RestoreRequest request;
|
||||||
if(oauth != null) {
|
if (oauth != null) {
|
||||||
if (isTokenExpired() || oauth.accessToken == null) {
|
if (isTokenExpired() || oauth.accessToken == null) {
|
||||||
RefreshTokenRequest refreshRequest = new RefreshTokenRequest(authId, oauth.refreshToken);
|
RefreshTokenRequest refreshRequest = new RefreshTokenRequest(authId, oauth.refreshToken);
|
||||||
RefreshTokenRequestEvent event = refreshRequest.request();
|
RefreshTokenRequestEvent event = refreshRequest.request();
|
||||||
|
@ -197,7 +185,7 @@ public void removeOAuthChangeHandler(BiConsumer<String, AuthRequestEvent.OAuthRe
|
||||||
public R request() throws Exception {
|
public R request() throws Exception {
|
||||||
if (!started.compareAndSet(false, true))
|
if (!started.compareAndSet(false, true))
|
||||||
throw new IllegalStateException("Request already started");
|
throw new IllegalStateException("Request already started");
|
||||||
if(!isAvailable()) {
|
if (!isAvailable()) {
|
||||||
throw new RequestException("RequestService not initialized");
|
throw new RequestException("RequestService not initialized");
|
||||||
}
|
}
|
||||||
return requestDo(requestService);
|
return requestDo(requestService);
|
||||||
|
@ -224,4 +212,16 @@ public interface ExtendedTokenCallback {
|
||||||
String tryGetNewToken(String name);
|
String tryGetNewToken(String name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class RequestRestoreReport {
|
||||||
|
public final boolean legacySession;
|
||||||
|
public final boolean refreshed;
|
||||||
|
public final List<String> invalidExtendedTokens;
|
||||||
|
|
||||||
|
public RequestRestoreReport(boolean legacySession, boolean refreshed, List<String> invalidExtendedTokens) {
|
||||||
|
this.legacySession = legacySession;
|
||||||
|
this.refreshed = refreshed;
|
||||||
|
this.invalidExtendedTokens = invalidExtendedTokens;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,12 @@
|
||||||
|
|
||||||
public interface RequestService {
|
public interface RequestService {
|
||||||
<T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException;
|
<T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException;
|
||||||
|
|
||||||
void registerEventHandler(EventHandler handler);
|
void registerEventHandler(EventHandler handler);
|
||||||
|
|
||||||
void unregisterEventHandler(EventHandler handler);
|
void unregisterEventHandler(EventHandler handler);
|
||||||
default <T extends WebSocketEvent> T requestSync(Request<T> request) throws IOException {
|
|
||||||
|
default <T extends WebSocketEvent> T requestSync(Request<T> request) throws IOException {
|
||||||
try {
|
try {
|
||||||
return request(request).get();
|
return request(request).get();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
|
|
|
@ -5,21 +5,13 @@
|
||||||
import pro.gravit.launcher.events.request.LauncherRequestEvent;
|
import pro.gravit.launcher.events.request.LauncherRequestEvent;
|
||||||
import pro.gravit.launcher.request.Request;
|
import pro.gravit.launcher.request.Request;
|
||||||
import pro.gravit.launcher.request.RequestService;
|
import pro.gravit.launcher.request.RequestService;
|
||||||
import pro.gravit.launcher.request.websockets.StdWebSocketService;
|
|
||||||
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.JVMHelper;
|
|
||||||
import pro.gravit.utils.helper.LogHelper;
|
import pro.gravit.utils.helper.LogHelper;
|
||||||
import pro.gravit.utils.helper.SecurityHelper;
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public final class LauncherRequest extends Request<LauncherRequestEvent> implements WebSocketRequest {
|
public final class LauncherRequest extends Request<LauncherRequestEvent> implements WebSocketRequest {
|
||||||
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
public static final Path BINARY_PATH = IOHelper.getCodeSource(Launcher.class);
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
import pro.gravit.launcher.request.Request;
|
import pro.gravit.launcher.request.Request;
|
||||||
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
||||||
import pro.gravit.utils.helper.IOHelper;
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
import pro.gravit.utils.helper.VerifyHelper;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
|
import pro.gravit.launcher.events.request.ProfileByUsernameRequestEvent;
|
||||||
import pro.gravit.launcher.request.Request;
|
import pro.gravit.launcher.request.Request;
|
||||||
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
import pro.gravit.launcher.request.websockets.WebSocketRequest;
|
||||||
import pro.gravit.utils.helper.VerifyHelper;
|
|
||||||
|
|
||||||
public final class ProfileByUsernameRequest extends Request<ProfileByUsernameRequestEvent> implements WebSocketRequest {
|
public final class ProfileByUsernameRequest extends Request<ProfileByUsernameRequestEvent> implements WebSocketRequest {
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
import java.security.KeyStoreException;
|
import java.security.KeyStoreException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.ThreadFactory;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -112,10 +111,10 @@ public void openAsync(Runnable onConnect, Consumer<Throwable> onFail) {
|
||||||
uri, WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, 12800000), this);
|
uri, WebSocketVersion.V13, null, false, EmptyHttpHeaders.INSTANCE, 12800000), this);
|
||||||
ChannelFuture future = bootstrap.connect(uri.getHost(), port);
|
ChannelFuture future = bootstrap.connect(uri.getHost(), port);
|
||||||
future.addListener((l) -> {
|
future.addListener((l) -> {
|
||||||
if(l.isSuccess()) {
|
if (l.isSuccess()) {
|
||||||
ch = future.channel();
|
ch = future.channel();
|
||||||
webSocketClientHandler.handshakeFuture().addListener((e) -> {
|
webSocketClientHandler.handshakeFuture().addListener((e) -> {
|
||||||
if(e.isSuccess()) {
|
if (e.isSuccess()) {
|
||||||
onConnect.run();
|
onConnect.run();
|
||||||
} else {
|
} else {
|
||||||
onFail.accept(webSocketClientHandler.handshakeFuture().cause());
|
onFail.accept(webSocketClientHandler.handshakeFuture().cause());
|
||||||
|
|
|
@ -82,7 +82,7 @@ public void registerRequests() {
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public void registerResults() {
|
public void registerResults() {
|
||||||
if(!resultsRegistered) {
|
if (!resultsRegistered) {
|
||||||
results.register("auth", AuthRequestEvent.class);
|
results.register("auth", AuthRequestEvent.class);
|
||||||
results.register("checkServer", CheckServerRequestEvent.class);
|
results.register("checkServer", CheckServerRequestEvent.class);
|
||||||
results.register("joinServer", JoinServerRequestEvent.class);
|
results.register("joinServer", JoinServerRequestEvent.class);
|
||||||
|
|
|
@ -16,26 +16,27 @@
|
||||||
public class OfflineRequestService implements RequestService {
|
public class OfflineRequestService implements RequestService {
|
||||||
private final HashSet<EventHandler> eventHandlers = new HashSet<>();
|
private final HashSet<EventHandler> eventHandlers = new HashSet<>();
|
||||||
private final Map<Class<?>, RequestProcessor<?, ?>> processors = new ConcurrentHashMap<>();
|
private final Map<Class<?>, RequestProcessor<?, ?>> processors = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException {
|
public <T extends WebSocketEvent> CompletableFuture<T> request(Request<T> request) throws IOException {
|
||||||
RequestProcessor<T, Request<T>> processor = (RequestProcessor<T, Request<T>>) processors.get(request.getClass());
|
RequestProcessor<T, Request<T>> processor = (RequestProcessor<T, Request<T>>) processors.get(request.getClass());
|
||||||
CompletableFuture<T> future = new CompletableFuture<>();
|
CompletableFuture<T> future = new CompletableFuture<>();
|
||||||
if(processor == null) {
|
if (processor == null) {
|
||||||
future.completeExceptionally(new RequestException(String.format("Offline mode not support '%s'", request.getType())));
|
future.completeExceptionally(new RequestException(String.format("Offline mode not support '%s'", request.getType())));
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
if(LogHelper.isDevEnabled()) {
|
if (LogHelper.isDevEnabled()) {
|
||||||
LogHelper.dev("Request %s: %s", request.getType(), Launcher.gsonManager.gson.toJson(request));
|
LogHelper.dev("Request %s: %s", request.getType(), Launcher.gsonManager.gson.toJson(request));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
T event = processor.process(request);
|
T event = processor.process(request);
|
||||||
if(LogHelper.isDevEnabled()) {
|
if (LogHelper.isDevEnabled()) {
|
||||||
LogHelper.dev("Response %s: %s", event.getType(), Launcher.gsonManager.gson.toJson(event));
|
LogHelper.dev("Response %s: %s", event.getType(), Launcher.gsonManager.gson.toJson(event));
|
||||||
}
|
}
|
||||||
future.complete(event);
|
future.complete(event);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if(e instanceof RequestException) {
|
if (e instanceof RequestException) {
|
||||||
future.completeExceptionally(e);
|
future.completeExceptionally(e);
|
||||||
} else {
|
} else {
|
||||||
future.completeExceptionally(new RequestException(e));
|
future.completeExceptionally(new RequestException(e));
|
||||||
|
@ -59,11 +60,11 @@ public boolean isClosed() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public<T extends WebSocketEvent, V extends WebSocketRequest> void registerRequestProcessor(Class<V> requestClazz, RequestProcessor<T, V> function) {
|
public <T extends WebSocketEvent, V extends WebSocketRequest> void registerRequestProcessor(Class<V> requestClazz, RequestProcessor<T, V> function) {
|
||||||
processors.put(requestClazz, function);
|
processors.put(requestClazz, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
public<T extends WebSocketEvent> void unregisterRequestProcessor(Class<Request<T>> requestClazz) {
|
public <T extends WebSocketEvent> void unregisterRequestProcessor(Class<Request<T>> requestClazz) {
|
||||||
processors.remove(requestClazz);
|
processors.remove(requestClazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,6 @@ public static CompletableFuture<StdWebSocketService> initWebSockets(String addre
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void registerEventHandler(ClientWebSocketService.EventHandler handler) {
|
public void registerEventHandler(ClientWebSocketService.EventHandler handler) {
|
||||||
legacyEventHandlers.add(handler);
|
legacyEventHandlers.add(handler);
|
||||||
|
|
|
@ -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 = 3;
|
public static final int MINOR = 3;
|
||||||
public static final int PATCH = 1;
|
public static final int PATCH = 2;
|
||||||
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;
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
import pro.gravit.utils.command.CommandException;
|
import pro.gravit.utils.command.CommandException;
|
||||||
|
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptEngineFactory;
|
|
||||||
import javax.script.ScriptEngineManager;
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
|
@ -58,9 +58,9 @@ public static String ansiFormatVersion(String product) {
|
||||||
fgBright(Ansi.Color.MAGENTA).a("GravitLauncher "). // sashok724's
|
fgBright(Ansi.Color.MAGENTA).a("GravitLauncher "). // sashok724's
|
||||||
fgBright(Ansi.Color.BLUE).a("(fork sashok724's Launcher) ").
|
fgBright(Ansi.Color.BLUE).a("(fork sashok724's Launcher) ").
|
||||||
fgBright(Ansi.Color.CYAN).a(product). // Product
|
fgBright(Ansi.Color.CYAN).a(product). // Product
|
||||||
fgBright(Ansi.Color.WHITE).a(" v").fgBright(Ansi.Color.BLUE).a(Version.getVersion().toString()). // Version
|
fgBright(Ansi.Color.WHITE).a(" v").fgBright(Ansi.Color.BLUE).a(Version.getVersion().toString()). // Version
|
||||||
fgBright(Ansi.Color.WHITE).a(" (build #").fgBright(Ansi.Color.RED).a(Version.getVersion().build).fgBright(Ansi.Color.WHITE).a(')'). // Build#
|
fgBright(Ansi.Color.WHITE).a(" (build #").fgBright(Ansi.Color.RED).a(Version.getVersion().build).fgBright(Ansi.Color.WHITE).a(')'). // Build#
|
||||||
reset().toString(); // To file
|
reset().toString(); // To file
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String ansiFormatLicense(String product) {
|
public static String ansiFormatLicense(String product) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ public final class IOHelper {
|
||||||
public static final FileSystem FS = FileSystems.getDefault();
|
public static final FileSystem FS = FileSystems.getDefault();
|
||||||
// Platform-dependent
|
// Platform-dependent
|
||||||
public static final String PLATFORM_SEPARATOR = FS.getSeparator();
|
public static final String PLATFORM_SEPARATOR = FS.getSeparator();
|
||||||
|
private static final Pattern PLATFORM_SEPARATOR_PATTERN = Pattern.compile(PLATFORM_SEPARATOR, Pattern.LITERAL);
|
||||||
public static final boolean POSIX = FS.supportedFileAttributeViews().contains("posix") || FS.supportedFileAttributeViews().contains("Posix");
|
public static final boolean POSIX = FS.supportedFileAttributeViews().contains("posix") || FS.supportedFileAttributeViews().contains("Posix");
|
||||||
public static final Path JVM_DIR = Paths.get(System.getProperty("java.home"));
|
public static final Path JVM_DIR = Paths.get(System.getProperty("java.home"));
|
||||||
public static final Path HOME_DIR = Paths.get(System.getProperty("user.home"));
|
public static final Path HOME_DIR = Paths.get(System.getProperty("user.home"));
|
||||||
|
@ -54,7 +55,6 @@ public final class IOHelper {
|
||||||
private static final Set<FileVisitOption> WALK_OPTIONS = Collections.singleton(FileVisitOption.FOLLOW_LINKS);
|
private static final Set<FileVisitOption> WALK_OPTIONS = Collections.singleton(FileVisitOption.FOLLOW_LINKS);
|
||||||
// Other constants
|
// Other constants
|
||||||
private static final Pattern CROSS_SEPARATOR_PATTERN = Pattern.compile(CROSS_SEPARATOR, Pattern.LITERAL);
|
private static final Pattern CROSS_SEPARATOR_PATTERN = Pattern.compile(CROSS_SEPARATOR, Pattern.LITERAL);
|
||||||
private static final Pattern PLATFORM_SEPARATOR_PATTERN = Pattern.compile(PLATFORM_SEPARATOR, Pattern.LITERAL);
|
|
||||||
|
|
||||||
private IOHelper() {
|
private IOHelper() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,10 @@ public final class JVMHelper {
|
||||||
public static final OperatingSystemMXBean OPERATING_SYSTEM_MXBEAN =
|
public static final OperatingSystemMXBean OPERATING_SYSTEM_MXBEAN =
|
||||||
ManagementFactory.getOperatingSystemMXBean();
|
ManagementFactory.getOperatingSystemMXBean();
|
||||||
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
public static final OS OS_TYPE = OS.byName(OPERATING_SYSTEM_MXBEAN.getName());
|
||||||
// System properties
|
|
||||||
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final int OS_BITS = getCorrectOSArch();
|
public static final int OS_BITS = getCorrectOSArch();
|
||||||
|
// System properties
|
||||||
|
public static final String OS_VERSION = OPERATING_SYSTEM_MXBEAN.getVersion();
|
||||||
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
public static final ARCH ARCH_TYPE = getArch(System.getProperty("os.arch"));
|
||||||
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
public static final int JVM_BITS = Integer.parseInt(System.getProperty("sun.arch.data.model"));
|
||||||
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
|
public static final SecurityManager SECURITY_MANAGER = System.getSecurityManager();
|
||||||
|
@ -46,21 +44,11 @@ public final class JVMHelper {
|
||||||
private JVMHelper() {
|
private JVMHelper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ARCH {
|
|
||||||
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
|
||||||
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
ARCH(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ARCH getArch(String arch) {
|
public static ARCH getArch(String arch) {
|
||||||
if(arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
if (arch.equals("amd64") || arch.equals("x86-64") || arch.equals("x86_64")) return ARCH.X86_64;
|
||||||
if(arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
if (arch.equals("i386") || arch.equals("i686") || arch.equals("x86")) return ARCH.X86;
|
||||||
if(arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
if (arch.startsWith("armv8") || arch.startsWith("aarch64")) return ARCH.ARM64;
|
||||||
if(arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
if (arch.startsWith("arm") || arch.startsWith("aarch32")) return ARCH.ARM32;
|
||||||
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
throw new InternalError(String.format("Unsupported arch '%s'", arch));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,19 +98,16 @@ public static Class<?> firstClass(String... names) throws ClassNotFoundException
|
||||||
throw new ClassNotFoundException(Arrays.toString(names));
|
throw new ClassNotFoundException(Arrays.toString(names));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void fullGC() {
|
public static void fullGC() {
|
||||||
RUNTIME.gc();
|
RUNTIME.gc();
|
||||||
RUNTIME.runFinalization();
|
RUNTIME.runFinalization();
|
||||||
LogHelper.debug("Used heap: %d MiB", RUNTIME.totalMemory() - RUNTIME.freeMemory() >> 20);
|
LogHelper.debug("Used heap: %d MiB", RUNTIME.totalMemory() - RUNTIME.freeMemory() >> 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String[] getClassPath() {
|
public static String[] getClassPath() {
|
||||||
return System.getProperty("java.class.path").split(File.pathSeparator);
|
return System.getProperty("java.class.path").split(File.pathSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static URL[] getClassPathURL() {
|
public static URL[] getClassPathURL() {
|
||||||
String[] cp = System.getProperty("java.class.path").split(File.pathSeparator);
|
String[] cp = System.getProperty("java.class.path").split(File.pathSeparator);
|
||||||
URL[] list = new URL[cp.length];
|
URL[] list = new URL[cp.length];
|
||||||
|
@ -164,35 +149,29 @@ private static int getCorrectOSArch() {
|
||||||
return System.getProperty("os.arch").contains("64") ? 64 : 32;
|
return System.getProperty("os.arch").contains("64") ? 64 : 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String getEnvPropertyCaseSensitive(String name) {
|
public static String getEnvPropertyCaseSensitive(String name) {
|
||||||
return System.getenv().get(name);
|
return System.getenv().get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static boolean isJVMMatchesSystemArch() {
|
public static boolean isJVMMatchesSystemArch() {
|
||||||
return JVM_BITS == OS_BITS;
|
return JVM_BITS == OS_BITS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String jvmProperty(String name, String value) {
|
public static String jvmProperty(String name, String value) {
|
||||||
return String.format("-D%s=%s", name, value);
|
return String.format("-D%s=%s", name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static String systemToJvmProperty(String name) {
|
public static String systemToJvmProperty(String name) {
|
||||||
return String.format("-D%s=%s", name, System.getProperties().getProperty(name));
|
return String.format("-D%s=%s", name, System.getProperties().getProperty(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void addSystemPropertyToArgs(Collection<String> args, String name) {
|
public static void addSystemPropertyToArgs(Collection<String> args, String name) {
|
||||||
String property = System.getProperty(name);
|
String property = System.getProperty(name);
|
||||||
if (property != null)
|
if (property != null)
|
||||||
args.add(String.format("-D%s=%s", name, property));
|
args.add(String.format("-D%s=%s", name, property));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void verifySystemProperties(Class<?> mainClass, boolean requireSystem) {
|
public static void verifySystemProperties(Class<?> mainClass, boolean requireSystem) {
|
||||||
Locale.setDefault(Locale.US);
|
Locale.setDefault(Locale.US);
|
||||||
// Verify class loader
|
// Verify class loader
|
||||||
|
@ -204,6 +183,17 @@ public static void verifySystemProperties(Class<?> mainClass, boolean requireSys
|
||||||
LogHelper.debug("Verifying JVM architecture");
|
LogHelper.debug("Verifying JVM architecture");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public enum ARCH {
|
||||||
|
X86("x86"), X86_64("x86-64"), ARM64("arm64"), ARM32("arm32");
|
||||||
|
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
ARCH(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum OS {
|
public enum OS {
|
||||||
MUSTDIE("mustdie"), LINUX("linux"), MACOSX("macosx");
|
MUSTDIE("mustdie"), LINUX("linux"), MACOSX("macosx");
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
public class JavaHelper {
|
public class JavaHelper {
|
||||||
private static List<JavaVersion> javaVersionsCache;
|
private static List<JavaVersion> javaVersionsCache;
|
||||||
|
|
||||||
public static Path tryGetOpenJFXPath(Path jvmDir) {
|
public static Path tryGetOpenJFXPath(Path jvmDir) {
|
||||||
String dirName = jvmDir.getFileName().toString();
|
String dirName = jvmDir.getFileName().toString();
|
||||||
Path parent = jvmDir.getParent();
|
Path parent = jvmDir.getParent();
|
||||||
|
@ -57,7 +58,7 @@ public static boolean tryAddModule(List<Path> paths, String moduleName, StringBu
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized static List<JavaVersion> findJava() {
|
public synchronized static List<JavaVersion> findJava() {
|
||||||
if(javaVersionsCache != null) {
|
if (javaVersionsCache != null) {
|
||||||
return javaVersionsCache;
|
return javaVersionsCache;
|
||||||
}
|
}
|
||||||
List<String> javaPaths = new ArrayList<>(4);
|
List<String> javaPaths = new ArrayList<>(4);
|
||||||
|
@ -106,11 +107,11 @@ public synchronized static List<JavaVersion> findJava() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JavaVersion tryFindJavaByPath(Path path) {
|
private static JavaVersion tryFindJavaByPath(Path path) {
|
||||||
if(javaVersionsCache == null) {
|
if (javaVersionsCache == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
for(JavaVersion version : javaVersionsCache) {
|
for (JavaVersion version : javaVersionsCache) {
|
||||||
if(version.jvmDir.equals(path)) {
|
if (version.jvmDir.equals(path)) {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,8 +157,8 @@ public static JavaVersionAndBuild getJavaVersion(String version) {
|
||||||
result.build = Integer.parseInt(version.substring(dot + 1));
|
result.build = Integer.parseInt(version.substring(dot + 1));
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
if(version.endsWith("-ea")) {
|
if (version.endsWith("-ea")) {
|
||||||
version = version.substring(0, version.length()-3);
|
version = version.substring(0, version.length() - 3);
|
||||||
}
|
}
|
||||||
result.version = Integer.parseInt(version);
|
result.version = Integer.parseInt(version);
|
||||||
result.build = 0;
|
result.build = 0;
|
||||||
|
@ -232,7 +233,7 @@ private static boolean isCurrentJavaSupportJavaFX() {
|
||||||
public static JavaVersion getByPath(Path jvmDir) throws IOException {
|
public static JavaVersion getByPath(Path jvmDir) throws IOException {
|
||||||
{
|
{
|
||||||
JavaVersion version = JavaHelper.tryFindJavaByPath(jvmDir);
|
JavaVersion version = JavaHelper.tryFindJavaByPath(jvmDir);
|
||||||
if(version != null) {
|
if (version != null) {
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,9 +180,9 @@ public void run(String... args) throws Throwable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
LogHelper.info("Start Minecraft Server");
|
LogHelper.info("Start Minecraft Server");
|
||||||
LogHelper.debug("Invoke main method %s with %s", config.mainclass, launch.getClass().getName());
|
LogHelper.debug("Invoke main method %s with %s", classname, launch.getClass().getName());
|
||||||
try {
|
try {
|
||||||
launch.run(config, real_args);
|
launch.run(classname, config, real_args);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
LogHelper.error(e);
|
LogHelper.error(e);
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
public class ClasspathLaunch implements Launch {
|
public class ClasspathLaunch implements Launch {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
||||||
public void run(ServerWrapper.Config config, String[] args) throws Throwable {
|
public void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable {
|
||||||
URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new);
|
URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new);
|
||||||
ClassLoader ucl = new PublicURLClassLoader(urls);
|
ClassLoader ucl = new PublicURLClassLoader(urls);
|
||||||
Class<?> mainClass = Class.forName(config.mainclass, true, ucl);
|
Class<?> mainClass = Class.forName(mainclass, true, ucl);
|
||||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
||||||
mainMethod.invoke(args);
|
mainMethod.invoke(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
import pro.gravit.launcher.server.ServerWrapper;
|
import pro.gravit.launcher.server.ServerWrapper;
|
||||||
|
|
||||||
public interface Launch {
|
public interface Launch {
|
||||||
void run(ServerWrapper.Config config, String[] args) throws Throwable;
|
void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
public class ModuleLaunch implements Launch {
|
public class ModuleLaunch implements Launch {
|
||||||
@Override
|
@Override
|
||||||
public void run(ServerWrapper.Config config, String[] args) throws Throwable {
|
public void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable {
|
||||||
throw new UnsupportedOperationException("Module system not supported");
|
throw new UnsupportedOperationException("Module system not supported");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
public class SimpleLaunch implements Launch {
|
public class SimpleLaunch implements Launch {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
||||||
public void run(ServerWrapper.Config config, String[] args) throws Throwable {
|
public void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable {
|
||||||
Class<?> mainClass = Class.forName(config.mainclass);
|
Class<?> mainClass = Class.forName(mainclass);
|
||||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
||||||
mainMethod.invoke(args);
|
mainMethod.invoke(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,11 @@
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class ModuleLaunch implements Launch {
|
public class ModuleLaunch implements Launch {
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
@SuppressWarnings("ConfusingArgumentToVarargsMethod")
|
||||||
public void run(ServerWrapper.Config config, String[] args) throws Throwable {
|
public void run(String mainclass, ServerWrapper.Config config, String[] args) throws Throwable {
|
||||||
URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new);
|
URL[] urls = config.classpath.stream().map(Paths::get).map(IOHelper::toURL).toArray(URL[]::new);
|
||||||
ClassLoader ucl = new PublicURLClassLoader(urls);
|
ClassLoader ucl = new PublicURLClassLoader(urls);
|
||||||
// Create Module Layer
|
// Create Module Layer
|
||||||
|
@ -55,7 +54,7 @@ public void run(ServerWrapper.Config config, String[] args) throws Throwable {
|
||||||
}
|
}
|
||||||
// Start main class
|
// Start main class
|
||||||
ClassLoader loader = mainModule.getClassLoader();
|
ClassLoader loader = mainModule.getClassLoader();
|
||||||
Class<?> mainClass = Class.forName(config.mainclass, true, loader);
|
Class<?> mainClass = Class.forName(mainclass, true, loader);
|
||||||
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
MethodHandle mainMethod = MethodHandles.lookup().findStatic(mainClass, "main", MethodType.methodType(void.class, String[].class));
|
||||||
mainMethod.invoke(args);
|
mainMethod.invoke(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.3.1'
|
version = '5.3.2'
|
||||||
|
|
||||||
apply from: 'props.gradle'
|
apply from: 'props.gradle'
|
||||||
|
|
||||||
|
|
2
modules
2
modules
|
@ -1 +1 @@
|
||||||
Subproject commit 8677bc30c7e6c5f168131dbee63a1c10f2ea0394
|
Subproject commit aba8a880bd644c211f6f6d6fdceedd21adf66ca6
|
Loading…
Reference in a new issue