mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-12-23 00:51:01 +03:00
[FEATURE] New Core Auth System
This commit is contained in:
parent
97faf5ef79
commit
b647f49390
19 changed files with 429 additions and 109 deletions
|
@ -108,6 +108,7 @@ public final class LaunchServer implements Runnable, AutoCloseable, Reconfigurab
|
||||||
public final LaunchServerModulesManager modulesManager;
|
public final LaunchServerModulesManager modulesManager;
|
||||||
// Launcher binary
|
// Launcher binary
|
||||||
public final MirrorManager mirrorManager;
|
public final MirrorManager mirrorManager;
|
||||||
|
public final AuthManager authManager;
|
||||||
public final ReconfigurableManager reconfigurableManager;
|
public final ReconfigurableManager reconfigurableManager;
|
||||||
public final ConfigManager configManager;
|
public final ConfigManager configManager;
|
||||||
public final PingServerManager pingServerManager;
|
public final PingServerManager pingServerManager;
|
||||||
|
@ -169,6 +170,7 @@ public LaunchServer(LaunchServerDirectories directories, LaunchServerEnv env, La
|
||||||
configManager = new ConfigManager();
|
configManager = new ConfigManager();
|
||||||
pingServerManager = new PingServerManager(this);
|
pingServerManager = new PingServerManager(this);
|
||||||
featuresManager = new FeaturesManager(this);
|
featuresManager = new FeaturesManager(this);
|
||||||
|
authManager = new AuthManager(this);
|
||||||
//Generate or set new Certificate API
|
//Generate or set new Certificate API
|
||||||
certificateManager.orgName = config.projectName;
|
certificateManager.orgName = config.projectName;
|
||||||
config.init(ReloadType.FULL);
|
config.init(ReloadType.FULL);
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
import pro.gravit.launcher.profiles.optional.actions.OptionalAction;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
||||||
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||||
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
|
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
|
||||||
|
@ -199,8 +200,9 @@ public static void initGson(LaunchServerModulesManager modulesManager) {
|
||||||
Launcher.gsonManager.initGson();
|
Launcher.gsonManager.initGson();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public static void registerAll() {
|
public static void registerAll() {
|
||||||
|
AuthCoreProvider.registerProviders();
|
||||||
AuthHandler.registerHandlers();
|
AuthHandler.registerHandlers();
|
||||||
AuthProvider.registerProviders();
|
AuthProvider.registerProviders();
|
||||||
TextureProvider.registerProviders();
|
TextureProvider.registerProviders();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package pro.gravit.launchserver.auth;
|
package pro.gravit.launchserver.auth;
|
||||||
|
|
||||||
import pro.gravit.launchserver.LaunchServer;
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||||
import pro.gravit.launchserver.auth.provider.AuthProvider;
|
import pro.gravit.launchserver.auth.provider.AuthProvider;
|
||||||
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
import pro.gravit.launchserver.auth.texture.TextureProvider;
|
||||||
|
@ -13,6 +14,7 @@ public class AuthProviderPair {
|
||||||
public AuthProvider provider;
|
public AuthProvider provider;
|
||||||
public AuthHandler handler;
|
public AuthHandler handler;
|
||||||
public TextureProvider textureProvider;
|
public TextureProvider textureProvider;
|
||||||
|
public AuthCoreProvider core;
|
||||||
public Map<String, String> links;
|
public Map<String, String> links;
|
||||||
public transient String name;
|
public transient String name;
|
||||||
public String displayName;
|
public String displayName;
|
||||||
|
@ -26,12 +28,18 @@ public AuthProviderPair(AuthProvider provider, AuthHandler handler, TextureProvi
|
||||||
public void init(LaunchServer srv, String name) {
|
public void init(LaunchServer srv, String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
if (links != null) link(srv);
|
if (links != null) link(srv);
|
||||||
|
if(core == null) {
|
||||||
if (provider == null) throw new NullPointerException(String.format("Auth %s provider null", name));
|
if (provider == null) throw new NullPointerException(String.format("Auth %s provider null", name));
|
||||||
if (handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
|
if (handler == null) throw new NullPointerException(String.format("Auth %s handler null", name));
|
||||||
if (textureProvider == null)
|
|
||||||
throw new NullPointerException(String.format("Auth %s textureProvider null", name));
|
|
||||||
provider.init(srv);
|
provider.init(srv);
|
||||||
handler.init(srv);
|
handler.init(srv);
|
||||||
|
} else {
|
||||||
|
if (provider != null) throw new IllegalArgumentException(String.format("Auth %s provider not null", name));
|
||||||
|
if (handler != null) throw new IllegalArgumentException(String.format("Auth %s handler not null", name));
|
||||||
|
core.init(srv);
|
||||||
|
}
|
||||||
|
if (textureProvider == null)
|
||||||
|
throw new NullPointerException(String.format("Auth %s textureProvider null", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void link(LaunchServer srv) {
|
public void link(LaunchServer srv) {
|
||||||
|
@ -57,8 +65,20 @@ public void link(LaunchServer srv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
if(core == null) {
|
||||||
provider.close();
|
provider.close();
|
||||||
handler.close();
|
handler.close();
|
||||||
|
} else {
|
||||||
|
core.close();
|
||||||
|
}
|
||||||
textureProvider.close();
|
textureProvider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUseCore() {
|
||||||
|
return core != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isUseProviderAndHandler() {
|
||||||
|
return !isUseCore();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package pro.gravit.launchserver.auth.core;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.events.request.GetAvailabilityAuthRequestEvent;
|
||||||
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launcher.request.auth.details.AuthPasswordDetails;
|
||||||
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
|
import pro.gravit.launchserver.socket.Client;
|
||||||
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
|
import pro.gravit.utils.ProviderMap;
|
||||||
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/*
|
||||||
|
All-In-One provider
|
||||||
|
*/
|
||||||
|
public abstract class AuthCoreProvider implements AutoCloseable {
|
||||||
|
public static final ProviderMap<AuthCoreProvider> providers = new ProviderMap<>("AuthCoreProvider");
|
||||||
|
private static boolean registredProviders = false;
|
||||||
|
public static void registerProviders() {
|
||||||
|
if (!registredProviders) {
|
||||||
|
registredProviders = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public abstract User getUserByUsername(String username);
|
||||||
|
public abstract User getUserByUUID(UUID uuid);
|
||||||
|
public abstract void verifyAuth(AuthResponse.AuthContext context) throws AuthException;
|
||||||
|
public abstract PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password);
|
||||||
|
public abstract void init(LaunchServer server);
|
||||||
|
// Auth Handler methods
|
||||||
|
protected abstract boolean updateAuth(User user) throws IOException;
|
||||||
|
protected abstract boolean updateServerID(User user, String serverID) throws IOException;
|
||||||
|
|
||||||
|
public List<GetAvailabilityAuthRequestEvent.AuthAvailabilityDetails> getDetails(Client client) {
|
||||||
|
return List.of(new AuthPasswordDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID checkServer(Client client, String username, String serverID) throws IOException {
|
||||||
|
User user = getUserByUsername(username);
|
||||||
|
if(user.getUsername().equals(username) && user.getServerId().equals(serverID)) {
|
||||||
|
return user.getUUID();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException {
|
||||||
|
User user = client.getUser();
|
||||||
|
if(user == null) return false;
|
||||||
|
return user.getUsername().equals(username) && user.getAccessToken().equals(accessToken) && updateServerID(user, serverID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract void close() throws IOException;
|
||||||
|
|
||||||
|
public static class PasswordVerifyReport {
|
||||||
|
public final boolean success;
|
||||||
|
public final boolean needMoreFactor;
|
||||||
|
public final List<Integer> factors;
|
||||||
|
public final String accessToken;
|
||||||
|
public static final PasswordVerifyReport REQUIRED_2FA = new PasswordVerifyReport(-1);
|
||||||
|
public static final PasswordVerifyReport FAILED = new PasswordVerifyReport(false);
|
||||||
|
|
||||||
|
public PasswordVerifyReport(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
this.needMoreFactor = false;
|
||||||
|
this.factors = List.of();
|
||||||
|
this.accessToken = SecurityHelper.randomStringToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordVerifyReport(String accessToken) {
|
||||||
|
this.success = true;
|
||||||
|
this.needMoreFactor = false;
|
||||||
|
this.factors = List.of();
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordVerifyReport(int nextFactor) {
|
||||||
|
this.success = false;
|
||||||
|
this.needMoreFactor = true;
|
||||||
|
this.factors = List.of(nextFactor);
|
||||||
|
this.accessToken = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PasswordVerifyReport(List<Integer> factors) {
|
||||||
|
this.success = false;
|
||||||
|
this.needMoreFactor = false;
|
||||||
|
this.factors = Collections.unmodifiableList(factors);
|
||||||
|
this.accessToken = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package pro.gravit.launchserver.auth.core;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface User {
|
||||||
|
String getUsername();
|
||||||
|
UUID getUUID();
|
||||||
|
String getServerId();
|
||||||
|
String getAccessToken();
|
||||||
|
ClientPermissions getPermissions();
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
package pro.gravit.launchserver.auth.core.interfaces.provider;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launchserver.auth.core.User;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface AuthSupportRegistration {
|
||||||
|
User registration(String login, AuthRequest.AuthPasswordInterface password, Map<String, String> properties);
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package pro.gravit.launchserver.auth.core.interfaces.user;
|
||||||
|
|
||||||
|
public interface UserSupportMoney {
|
||||||
|
long getMoney();
|
||||||
|
long getDonateMoney();
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public interface User {
|
public interface User {
|
||||||
String getUsername();
|
String getUsername();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public interface UserDAO {
|
public interface UserDAO {
|
||||||
User findById(int id);
|
User findById(int id);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import pro.gravit.launchserver.dao.UserDAO;
|
import pro.gravit.launchserver.dao.UserDAO;
|
||||||
import pro.gravit.utils.ProviderMap;
|
import pro.gravit.utils.ProviderMap;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public abstract class DaoProvider {
|
public abstract class DaoProvider {
|
||||||
public static final ProviderMap<DaoProvider> providers = new ProviderMap<>("DaoProvider");
|
public static final ProviderMap<DaoProvider> providers = new ProviderMap<>("DaoProvider");
|
||||||
public transient UserDAO userDAO;
|
public transient UserDAO userDAO;
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
package pro.gravit.launchserver.manangers;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import pro.gravit.launcher.ClientPermissions;
|
||||||
|
import pro.gravit.launcher.events.request.AuthRequestEvent;
|
||||||
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
|
import pro.gravit.launcher.request.auth.password.*;
|
||||||
|
import pro.gravit.launchserver.LaunchServer;
|
||||||
|
import pro.gravit.launchserver.auth.AuthException;
|
||||||
|
import pro.gravit.launchserver.auth.AuthProviderPair;
|
||||||
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||||
|
import pro.gravit.launchserver.auth.core.User;
|
||||||
|
import pro.gravit.launchserver.auth.provider.AuthProvider;
|
||||||
|
import pro.gravit.launchserver.auth.provider.AuthProviderDAOResult;
|
||||||
|
import pro.gravit.launchserver.auth.provider.AuthProviderResult;
|
||||||
|
import pro.gravit.launchserver.socket.Client;
|
||||||
|
import pro.gravit.launchserver.socket.response.auth.AuthResponse;
|
||||||
|
import pro.gravit.launchserver.socket.response.profile.ProfileByUUIDResponse;
|
||||||
|
import pro.gravit.utils.helper.IOHelper;
|
||||||
|
import pro.gravit.utils.helper.SecurityHelper;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class AuthManager {
|
||||||
|
private transient final LaunchServer server;
|
||||||
|
private transient final Logger logger = LogManager.getLogger();
|
||||||
|
|
||||||
|
public AuthManager(LaunchServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create AuthContext
|
||||||
|
* @return AuthContext instance
|
||||||
|
*/
|
||||||
|
public AuthResponse.AuthContext makeAuthContext(Client client, AuthResponse.ConnectTypes authType, AuthProviderPair pair, String login, String profileName, String ip) {
|
||||||
|
Objects.requireNonNull(client, "Client must be not null");
|
||||||
|
Objects.requireNonNull(authType, "authType must be not null");
|
||||||
|
Objects.requireNonNull(pair, "AuthProviderPair must be not null");
|
||||||
|
return new AuthResponse.AuthContext(client, login, profileName, ip, authType, pair);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate auth params ans state
|
||||||
|
* @param context Auth context
|
||||||
|
* @throws AuthException auth not possible
|
||||||
|
*/
|
||||||
|
public void check(AuthResponse.AuthContext context) throws AuthException {
|
||||||
|
if (context.authType == AuthResponse.ConnectTypes.CLIENT && !context.client.checkSign) {
|
||||||
|
AuthProvider.authError("Don't skip Launcher Update");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (context.client.isAuth) {
|
||||||
|
AuthProvider.authError("You are already logged in");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Full client authorization with password verification
|
||||||
|
* @param context AuthContext
|
||||||
|
* @param password User password
|
||||||
|
* @return Access token
|
||||||
|
*/
|
||||||
|
public String auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
|
AuthProviderPair pair = context.pair;
|
||||||
|
String accessToken;
|
||||||
|
if(pair.core == null) {
|
||||||
|
try {
|
||||||
|
accessToken = authWithProviderAndHandler(context, password);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if(e instanceof AuthException) throw (AuthException) e;
|
||||||
|
throw new AuthException("Internal Auth Error. Please contact administrator");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
accessToken = authWithCore(context, password);
|
||||||
|
}
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private String authWithProviderAndHandler(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws Exception {
|
||||||
|
String accessToken;
|
||||||
|
context.pair.provider.preAuth(context.login, password, context.ip);
|
||||||
|
AuthProviderResult aresult = context.pair.provider.auth(context.login, password, context.ip);
|
||||||
|
UUID uuid;
|
||||||
|
String username = aresult.username != null ? aresult.username : context.login;
|
||||||
|
if (aresult instanceof AuthProviderDAOResult) {
|
||||||
|
context.client.daoObject = ((AuthProviderDAOResult) aresult).daoObject;
|
||||||
|
}
|
||||||
|
if(context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
|
||||||
|
uuid = context.pair.handler.auth(aresult);
|
||||||
|
accessToken = aresult.accessToken;
|
||||||
|
} else {
|
||||||
|
uuid = context.pair.handler.usernameToUUID(aresult.username);
|
||||||
|
accessToken = null;
|
||||||
|
}
|
||||||
|
internalAuth(context.client, context.authType, context.pair, username, uuid, aresult.permissions);
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String authWithCore(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
|
AuthCoreProvider provider = context.pair.core;
|
||||||
|
provider.verifyAuth(context);
|
||||||
|
User user = provider.getUserByUsername(context.login);
|
||||||
|
if(user == null) {
|
||||||
|
throw new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
AuthCoreProvider.PasswordVerifyReport report = provider.verifyPassword(user, password);
|
||||||
|
if(report.success) {
|
||||||
|
String accessToken;
|
||||||
|
UUID uuid = user.getUUID();
|
||||||
|
if(context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
|
||||||
|
provider.verifyAuth(context);
|
||||||
|
accessToken = report.accessToken;
|
||||||
|
} else {
|
||||||
|
accessToken = null;
|
||||||
|
}
|
||||||
|
context.client.coreObject = user;
|
||||||
|
internalAuth(context.client, context.authType, context.pair, user.getUsername(), uuid, user.getPermissions());
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(report.needMoreFactor) {
|
||||||
|
if(report.factors.size() == 1 && report.factors.get(0) == -1) {
|
||||||
|
throw new AuthException(AuthRequestEvent.TWO_FACTOR_NEED_ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
String message = AuthRequestEvent.ONE_FACTOR_NEED_ERROR_MESSAGE_PREFIX
|
||||||
|
.concat(report.factors.stream().map(String::valueOf).collect(Collectors.joining(".")));
|
||||||
|
throw new AuthException(message);
|
||||||
|
}
|
||||||
|
throw new AuthException(AuthRequestEvent.WRONG_PASSWORD_ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writing authorization information to the Client object
|
||||||
|
*/
|
||||||
|
public void internalAuth(Client client, AuthResponse.ConnectTypes authType, AuthProviderPair pair, String username, UUID uuid, ClientPermissions permissions) {
|
||||||
|
client.isAuth = true;
|
||||||
|
client.permissions = permissions;
|
||||||
|
client.auth_id = pair.name;
|
||||||
|
client.auth = pair;
|
||||||
|
client.username = username;
|
||||||
|
client.type = authType;
|
||||||
|
client.uuid = uuid;
|
||||||
|
if(pair.isUseCore() && client.coreObject == null) {
|
||||||
|
client.coreObject = pair.core.getUserByUUID(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID checkServer(Client client, String username, String serverID) throws IOException {
|
||||||
|
if(client.auth == null) return null;
|
||||||
|
if(client.auth.isUseCore()) {
|
||||||
|
return client.auth.core.checkServer(client, username, serverID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return client.auth.handler.checkServer(username, serverID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException {
|
||||||
|
if(client.auth == null) return false;
|
||||||
|
if(client.auth.isUseCore()) {
|
||||||
|
return client.auth.core.joinServer(client, username, accessToken, serverID);
|
||||||
|
} else {
|
||||||
|
return client.auth.handler.joinServer(username, accessToken, serverID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthRequest.AuthPasswordInterface decryptPassword(AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
|
if(password instanceof Auth2FAPassword) {
|
||||||
|
Auth2FAPassword auth2FAPassword = (Auth2FAPassword) password;
|
||||||
|
auth2FAPassword.firstPassword = tryDecryptPasswordPlain(auth2FAPassword.firstPassword);
|
||||||
|
auth2FAPassword.secondPassword = tryDecryptPasswordPlain(auth2FAPassword.secondPassword);
|
||||||
|
}
|
||||||
|
else if(password instanceof AuthMultiPassword) {
|
||||||
|
AuthMultiPassword multiPassword = (AuthMultiPassword) password;
|
||||||
|
List<AuthRequest.AuthPasswordInterface> list = new ArrayList<>(multiPassword.list.size());
|
||||||
|
for(AuthRequest.AuthPasswordInterface p : multiPassword.list) {
|
||||||
|
list.add(tryDecryptPasswordPlain(p));
|
||||||
|
}
|
||||||
|
multiPassword.list = list;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
password = tryDecryptPasswordPlain(password);
|
||||||
|
}
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private AuthRequest.AuthPasswordInterface tryDecryptPasswordPlain(AuthRequest.AuthPasswordInterface password) throws AuthException {
|
||||||
|
if (password instanceof AuthECPassword) {
|
||||||
|
try {
|
||||||
|
return new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
|
||||||
|
, ((AuthECPassword) password).password)));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
throw new AuthException("Password decryption error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (password instanceof AuthAESPassword) {
|
||||||
|
try {
|
||||||
|
return new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
|
||||||
|
, ((AuthAESPassword) password).password)));
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
throw new AuthException("Password decryption error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(password instanceof AuthRSAPassword) {
|
||||||
|
try {
|
||||||
|
Cipher cipher = SecurityHelper.newRSADecryptCipher(server.keyAgreementManager.rsaPrivateKey);
|
||||||
|
return new AuthPlainPassword(
|
||||||
|
IOHelper.decode(cipher.doFinal(((AuthRSAPassword) password).password))
|
||||||
|
);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
throw new AuthException("Password decryption error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@
|
||||||
import pro.gravit.launcher.request.WebSocketEvent;
|
import pro.gravit.launcher.request.WebSocketEvent;
|
||||||
import pro.gravit.launcher.request.auth.AuthRequest;
|
import pro.gravit.launcher.request.auth.AuthRequest;
|
||||||
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
import pro.gravit.launcher.request.auth.GetAvailabilityAuthRequest;
|
||||||
|
import pro.gravit.launchserver.auth.core.AuthCoreProvider;
|
||||||
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
import pro.gravit.launchserver.auth.handler.AuthHandler;
|
||||||
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
import pro.gravit.launchserver.auth.protect.ProtectHandler;
|
||||||
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
|
import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider;
|
||||||
|
@ -31,11 +32,13 @@ public LaunchServerGsonManager(LaunchServerModulesManager modulesManager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public void registerAdapters(GsonBuilder builder) {
|
public void registerAdapters(GsonBuilder builder) {
|
||||||
super.registerAdapters(builder);
|
super.registerAdapters(builder);
|
||||||
builder.registerTypeAdapter(AuthProvider.class, new UniversalJsonAdapter<>(AuthProvider.providers));
|
builder.registerTypeAdapter(AuthProvider.class, new UniversalJsonAdapter<>(AuthProvider.providers));
|
||||||
builder.registerTypeAdapter(TextureProvider.class, new UniversalJsonAdapter<>(TextureProvider.providers));
|
builder.registerTypeAdapter(TextureProvider.class, new UniversalJsonAdapter<>(TextureProvider.providers));
|
||||||
builder.registerTypeAdapter(AuthHandler.class, new UniversalJsonAdapter<>(AuthHandler.providers));
|
builder.registerTypeAdapter(AuthHandler.class, new UniversalJsonAdapter<>(AuthHandler.providers));
|
||||||
|
builder.registerTypeAdapter(AuthCoreProvider.class, new UniversalJsonAdapter<>(AuthCoreProvider.providers));
|
||||||
builder.registerTypeAdapter(Component.class, new UniversalJsonAdapter<>(Component.providers));
|
builder.registerTypeAdapter(Component.class, new UniversalJsonAdapter<>(Component.providers));
|
||||||
builder.registerTypeAdapter(ProtectHandler.class, new UniversalJsonAdapter<>(ProtectHandler.providers));
|
builder.registerTypeAdapter(ProtectHandler.class, new UniversalJsonAdapter<>(ProtectHandler.providers));
|
||||||
builder.registerTypeAdapter(DaoProvider.class, new UniversalJsonAdapter<>(DaoProvider.providers));
|
builder.registerTypeAdapter(DaoProvider.class, new UniversalJsonAdapter<>(DaoProvider.providers));
|
||||||
|
|
|
@ -53,14 +53,19 @@ private Client decompressClient(byte[] client) {
|
||||||
return Launcher.gsonManager.gson.fromJson(new String(client, StandardCharsets.UTF_8), Client.class); //Compress using later
|
return Launcher.gsonManager.gson.fromJson(new String(client, StandardCharsets.UTF_8), Client.class); //Compress using later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private Client restoreFromString(byte[] data) {
|
private Client restoreFromString(byte[] data) {
|
||||||
Client result = decompressClient(data);
|
Client result = decompressClient(data);
|
||||||
result.updateAuth(server);
|
result.updateAuth(server);
|
||||||
if (result.auth != null && (result.username != null)) {
|
if (result.auth != null && (result.username != null)) {
|
||||||
|
if(result.auth.isUseCore()) {
|
||||||
|
result.coreObject = result.auth.core.getUserByUUID(result.uuid);
|
||||||
|
} else {
|
||||||
if (result.auth.handler instanceof RequiredDAO || result.auth.provider instanceof RequiredDAO || result.auth.textureProvider instanceof RequiredDAO) {
|
if (result.auth.handler instanceof RequiredDAO || result.auth.provider instanceof RequiredDAO || result.auth.textureProvider instanceof RequiredDAO) {
|
||||||
result.daoObject = server.config.dao.userDAO.findByUsername(result.username);
|
result.daoObject = server.config.dao.userDAO.findByUsername(result.username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (result.refCount == null) result.refCount = new AtomicInteger(1);
|
if (result.refCount == null) result.refCount = new AtomicInteger(1);
|
||||||
clientRestoreHook.hook(result);
|
clientRestoreHook.hook(result);
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -28,8 +28,11 @@ public class Client {
|
||||||
|
|
||||||
public transient AuthProviderPair auth;
|
public transient AuthProviderPair auth;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public transient User daoObject;
|
public transient User daoObject;
|
||||||
|
|
||||||
|
public transient pro.gravit.launchserver.auth.core.User coreObject;
|
||||||
|
|
||||||
public transient Map<String, Object> properties;
|
public transient Map<String, Object> properties;
|
||||||
|
|
||||||
public Map<String, String> serializableProperties;
|
public Map<String, String> serializableProperties;
|
||||||
|
@ -78,6 +81,14 @@ public void setSerializableProperty(String name, String value) {
|
||||||
properties.put(name, value);
|
properties.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public pro.gravit.launchserver.auth.core.User getUser() {
|
||||||
|
if(coreObject != null) return coreObject;
|
||||||
|
if(auth != null && uuid != null && auth.isUseCore()) {
|
||||||
|
coreObject = auth.core.getUserByUUID(uuid);
|
||||||
|
}
|
||||||
|
return coreObject;
|
||||||
|
}
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public enum Type {
|
public enum Type {
|
||||||
SERVER,
|
SERVER,
|
||||||
|
|
|
@ -221,6 +221,7 @@ public void sendObjectToUUID(UUID userUuid, Object obj, Type type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public void updateDaoObject(UUID userUuid, User daoObject, Consumer<Channel> callback) {
|
public void updateDaoObject(UUID userUuid, User daoObject, Consumer<Channel> callback) {
|
||||||
for (Channel ch : channels) {
|
for (Channel ch : channels) {
|
||||||
if (ch == null || ch.pipeline() == null) continue;
|
if (ch == null || ch.pipeline() == null) continue;
|
||||||
|
@ -252,7 +253,7 @@ public boolean kickByUserUUID(UUID userUuid, boolean isClose) {
|
||||||
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
|
WebSocketFrameHandler wsHandler = ch.pipeline().get(WebSocketFrameHandler.class);
|
||||||
if (wsHandler == null) continue;
|
if (wsHandler == null) continue;
|
||||||
Client client = wsHandler.getClient();
|
Client client = wsHandler.getClient();
|
||||||
if (client == null || client.daoObject == null || !userUuid.equals(client.uuid)) continue;
|
if (client == null || !userUuid.equals(client.uuid)) continue;
|
||||||
ExitResponse.exit(server, wsHandler, ch, ExitRequestEvent.ExitReason.SERVER);
|
ExitResponse.exit(server, wsHandler, ch, ExitRequestEvent.ExitReason.SERVER);
|
||||||
if (isClose) ch.close();
|
if (isClose) ch.close();
|
||||||
result = true;
|
result = true;
|
||||||
|
|
|
@ -48,15 +48,6 @@ public String getType() {
|
||||||
public void execute(ChannelHandlerContext ctx, Client clientData) throws Exception {
|
public void execute(ChannelHandlerContext ctx, Client clientData) throws Exception {
|
||||||
try {
|
try {
|
||||||
AuthRequestEvent result = new AuthRequestEvent();
|
AuthRequestEvent result = new AuthRequestEvent();
|
||||||
if ((authType == null || authType == ConnectTypes.CLIENT) && (clientData == null || !clientData.checkSign)) {
|
|
||||||
AuthProvider.authError("Don't skip Launcher Update");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clientData.isAuth) {
|
|
||||||
sendError("You are already logged in");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
AuthProviderPair pair;
|
AuthProviderPair pair;
|
||||||
if (auth_id == null || auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
|
if (auth_id == null || auth_id.isEmpty()) pair = server.config.getAuthProviderPair();
|
||||||
else pair = server.config.getAuthProviderPair(auth_id);
|
else pair = server.config.getAuthProviderPair(auth_id);
|
||||||
|
@ -64,45 +55,12 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
sendError("auth_id incorrect");
|
sendError("auth_id incorrect");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AuthContext context = new AuthContext(clientData, login, client, ip, authType);
|
AuthContext context = server.authManager.makeAuthContext(clientData, authType, pair, login, client, ip);
|
||||||
AuthProvider provider = pair.provider;
|
server.authManager.check(context);
|
||||||
|
password = server.authManager.decryptPassword(password);
|
||||||
server.authHookManager.preHook.hook(context, clientData);
|
server.authHookManager.preHook.hook(context, clientData);
|
||||||
provider.preAuth(login, password, ip);
|
server.authManager.auth(context, password);
|
||||||
if(password instanceof Auth2FAPassword) {
|
|
||||||
AuthPlainPassword first = decryptPassword(server, ((Auth2FAPassword) password).firstPassword);
|
|
||||||
AuthPlainPassword second = decryptPassword(server, ((Auth2FAPassword) password).secondPassword);
|
|
||||||
if(first != null) {
|
|
||||||
((Auth2FAPassword) password).firstPassword = first;
|
|
||||||
}
|
|
||||||
if(second != null) {
|
|
||||||
((Auth2FAPassword) password).secondPassword = second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
AuthPlainPassword passwd = decryptPassword(server, password);
|
|
||||||
if(passwd != null) {
|
|
||||||
password = passwd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AuthProviderResult aresult = provider.auth(login, password, ip);
|
|
||||||
if (!VerifyHelper.isValidUsername(aresult.username)) {
|
|
||||||
AuthProvider.authError(String.format("Illegal result: '%s'", aresult.username));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
server.authHookManager.postHook.hook(context, clientData);
|
server.authHookManager.postHook.hook(context, clientData);
|
||||||
clientData.isAuth = true;
|
|
||||||
clientData.permissions = aresult.permissions;
|
|
||||||
clientData.auth_id = auth_id;
|
|
||||||
clientData.updateAuth(server);
|
|
||||||
if (aresult.username != null)
|
|
||||||
clientData.username = aresult.username;
|
|
||||||
else
|
|
||||||
clientData.username = login;
|
|
||||||
if (aresult instanceof AuthProviderDAOResult) {
|
|
||||||
clientData.daoObject = ((AuthProviderDAOResult) aresult).daoObject;
|
|
||||||
}
|
|
||||||
result.accessToken = aresult.accessToken;
|
|
||||||
result.permissions = clientData.permissions;
|
|
||||||
if (getSession) {
|
if (getSession) {
|
||||||
if (clientData.session == null) {
|
if (clientData.session == null) {
|
||||||
clientData.session = UUID.randomUUID();
|
clientData.session = UUID.randomUUID();
|
||||||
|
@ -110,56 +68,13 @@ public void execute(ChannelHandlerContext ctx, Client clientData) throws Excepti
|
||||||
}
|
}
|
||||||
result.session = clientData.session;
|
result.session = clientData.session;
|
||||||
}
|
}
|
||||||
if (authType == ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) {
|
result.playerProfile = ProfileByUUIDResponse.getProfile(clientData.uuid, clientData.username, client, clientData.auth.textureProvider);
|
||||||
clientData.uuid = pair.handler.auth(aresult);
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Auth: {} accessToken {} uuid: {}", login, result.accessToken, clientData.uuid.toString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clientData.uuid = pair.handler.usernameToUUID(aresult.username);
|
|
||||||
result.accessToken = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.playerProfile = ProfileByUUIDResponse.getProfile(clientData.uuid, aresult.username, client, clientData.auth.textureProvider);
|
|
||||||
|
|
||||||
clientData.type = authType;
|
|
||||||
sendResult(result);
|
sendResult(result);
|
||||||
} catch (AuthException | HookException e) {
|
} catch (AuthException | HookException e) {
|
||||||
sendError(e.getMessage());
|
sendError(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public static AuthPlainPassword decryptPassword(LaunchServer server, AuthRequest.AuthPasswordInterface password) throws Exception {
|
|
||||||
if (password instanceof AuthECPassword) {
|
|
||||||
try {
|
|
||||||
return new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
|
|
||||||
, ((AuthECPassword) password).password)));
|
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException ignored) {
|
|
||||||
throw new AuthException("Password decryption error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (password instanceof AuthAESPassword) {
|
|
||||||
try {
|
|
||||||
return new AuthPlainPassword(IOHelper.decode(SecurityHelper.decrypt(server.runtime.passwordEncryptKey
|
|
||||||
, ((AuthAESPassword) password).password)));
|
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException ignored) {
|
|
||||||
throw new AuthException("Password decryption error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(password instanceof AuthRSAPassword) {
|
|
||||||
try {
|
|
||||||
Cipher cipher = SecurityHelper.newRSADecryptCipher(server.keyAgreementManager.rsaPrivateKey);
|
|
||||||
return new AuthPlainPassword(
|
|
||||||
IOHelper.decode(cipher.doFinal(((AuthRSAPassword) password).password))
|
|
||||||
);
|
|
||||||
} catch (IllegalBlockSizeException | BadPaddingException ignored) {
|
|
||||||
throw new AuthException("Password decryption error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ConnectTypes {
|
public enum ConnectTypes {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
SERVER,
|
SERVER,
|
||||||
|
@ -173,15 +88,17 @@ public static class AuthContext {
|
||||||
public final String ip;
|
public final String ip;
|
||||||
public final ConnectTypes authType;
|
public final ConnectTypes authType;
|
||||||
public final Client client;
|
public final Client client;
|
||||||
|
public final AuthProviderPair pair;
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public int password_length; //Use AuthProvider for get password
|
public int password_length; //Use AuthProvider for get password
|
||||||
|
|
||||||
public AuthContext(Client client, String login, String profileName, String ip, ConnectTypes authType) {
|
public AuthContext(Client client, String login, String profileName, String ip, ConnectTypes authType, AuthProviderPair pair) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.login = login;
|
this.login = login;
|
||||||
this.profileName = profileName;
|
this.profileName = profileName;
|
||||||
this.ip = ip;
|
this.ip = ip;
|
||||||
this.authType = authType;
|
this.authType = authType;
|
||||||
|
this.pair = pair;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,11 @@ public void execute(ChannelHandlerContext ctx, Client pClient) {
|
||||||
CheckServerRequestEvent result = new CheckServerRequestEvent();
|
CheckServerRequestEvent result = new CheckServerRequestEvent();
|
||||||
try {
|
try {
|
||||||
server.authHookManager.checkServerHook.hook(this, pClient);
|
server.authHookManager.checkServerHook.hook(this, pClient);
|
||||||
result.uuid = pClient.auth.handler.checkServer(username, serverID);
|
result.uuid = server.authManager.checkServer(pClient, username, serverID);
|
||||||
if (result.uuid != null)
|
if (result.uuid != null) {
|
||||||
result.playerProfile = ProfileByUUIDResponse.getProfile(result.uuid, username, client, pClient.auth.textureProvider);
|
result.playerProfile = ProfileByUUIDResponse.getProfile(result.uuid, username, client, pClient.auth.textureProvider);
|
||||||
logger.debug("checkServer: {} uuid: {} serverID: {}", result.playerProfile.username, result.uuid, serverID);
|
logger.debug("checkServer: {} uuid: {} serverID: {}", result.playerProfile.username, result.uuid, serverID);
|
||||||
|
}
|
||||||
} catch (AuthException | HookException e) {
|
} catch (AuthException | HookException e) {
|
||||||
sendError(e.getMessage());
|
sendError(e.getMessage());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -42,11 +42,10 @@ public void execute(ChannelHandlerContext ctx, Client client) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (client.auth == null) {
|
success = server.authManager.joinServer(client, username, accessToken, serverID);
|
||||||
logger.warn("Client auth is null. Using default.");
|
if(success) {
|
||||||
success = server.config.getAuthProviderPair().handler.joinServer(username, accessToken, serverID);
|
|
||||||
} else success = client.auth.handler.joinServer(username, accessToken, serverID);
|
|
||||||
logger.debug("joinServer: {} accessToken: {} serverID: {}", username, accessToken, serverID);
|
logger.debug("joinServer: {} accessToken: {} serverID: {}", username, accessToken, serverID);
|
||||||
|
}
|
||||||
} catch (AuthException | HookException | SecurityException e) {
|
} catch (AuthException | HookException | SecurityException e) {
|
||||||
sendError(e.getMessage());
|
sendError(e.getMessage());
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -9,6 +9,10 @@
|
||||||
|
|
||||||
public class AuthRequestEvent extends RequestEvent {
|
public class AuthRequestEvent extends RequestEvent {
|
||||||
public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa";
|
public static final String TWO_FACTOR_NEED_ERROR_MESSAGE = "auth.require2fa";
|
||||||
|
public static final String ONE_FACTOR_NEED_ERROR_MESSAGE_PREFIX = "auth.require.factor.";
|
||||||
|
public static final String USER_NOT_FOUND_ERROR_MESSAGE = "auth.message.usernotfound";
|
||||||
|
public static final String WRONG_PASSWORD_ERROR_MESSAGE = "auth.message.wrongpassword";
|
||||||
|
public static final String ACCOUNT_BLOCKED_ERROR_MESSAGE = "auth.message.blocked";
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public ClientPermissions permissions;
|
public ClientPermissions permissions;
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
|
|
Loading…
Reference in a new issue