From 2a2c2f6b936f19e05620510e7f8e67aacff87dc2 Mon Sep 17 00:00:00 2001 From: Gravita Date: Fri, 5 Nov 2021 22:19:00 +0700 Subject: [PATCH] [FEATURE] AuthCoreProvider rework --- .../gravit/launchserver/HttpRequester.java | 94 ++++ .../launchserver/auth/AuthException.java | 22 + .../launchserver/auth/AuthProviderPair.java | 25 -- .../auth/core/AuthCoreProvider.java | 91 ++-- .../auth/core/AuthSocialProvider.java | 61 --- .../auth/core/HttpAuthCoreProvider.java | 333 ++++++++++++++ .../auth/core/JsonCoreProvider.java | 410 ------------------ .../auth/core/MemoryAuthCoreProvider.java | 212 +++++++++ .../auth/core/MySQLCoreProvider.java | 66 ++- .../auth/core/RejectAuthCoreProvider.java | 9 +- .../provider/AuthSupportHardware.java | 3 +- .../auth/password/JsonPasswordVerifier.java | 51 ++- .../auth/protect/AdvancedProtectHandler.java | 4 +- .../config/LaunchServerConfig.java | 2 - .../launchserver/manangers/AuthManager.java | 76 +--- .../socket/response/auth/AuthResponse.java | 6 +- .../auth/GetAvailabilityAuthResponse.java | 2 +- build.gradle | 2 +- modules | 2 +- 19 files changed, 822 insertions(+), 649 deletions(-) create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/HttpRequester.java delete mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthSocialProvider.java create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/HttpAuthCoreProvider.java delete mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MemoryAuthCoreProvider.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/HttpRequester.java b/LaunchServer/src/main/java/pro/gravit/launchserver/HttpRequester.java new file mode 100644 index 00000000..fae7b1c4 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/HttpRequester.java @@ -0,0 +1,94 @@ +package pro.gravit.launchserver; + +import com.google.gson.JsonElement; +import pro.gravit.launcher.Launcher; +import pro.gravit.launchserver.helper.HttpHelper; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.time.Duration; + +public class HttpRequester { + private transient final HttpClient httpClient = HttpClient.newBuilder().build(); + + public HttpRequester() { + } + + public static class SimpleErrorHandler implements HttpHelper.HttpJsonErrorHandler { + private final Type type; + + private SimpleErrorHandler(Type type) { + this.type = type; + } + + @Override + public HttpHelper.HttpOptional applyJson(JsonElement response, int statusCode) { + if(statusCode < 200 || statusCode >= 300) { + return new HttpHelper.HttpOptional<>(null, Launcher.gsonManager.gson.fromJson(response, SimpleError.class), statusCode); + } + return new HttpHelper.HttpOptional<>(Launcher.gsonManager.gson.fromJson(response, type), null, statusCode); + } + } + + public SimpleErrorHandler makeEH(Class clazz) { + return new SimpleErrorHandler<>(clazz); + } + + public HttpRequest get(String url, String token) { + try { + var requestBuilder = HttpRequest.newBuilder() + .method("GET", HttpRequest.BodyPublishers.noBody()) + .uri(new URI(url)) + .header("Content-Type", "application/json; charset=UTF-8") + .header("Accept", "application/json") + .timeout(Duration.ofMillis(10000)); + if(token != null) { + requestBuilder.header("Authorization", "Bearer ".concat(token)); + } + return requestBuilder.build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public HttpRequest post(String url, T request, String token) { + try { + var requestBuilder = HttpRequest.newBuilder() + .method("POST", HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(request))) + .uri(new URI(url)) + .header("Content-Type", "application/json; charset=UTF-8") + .header("Accept", "application/json") + .timeout(Duration.ofMillis(10000)); + if(token != null) { + requestBuilder.header("Authorization", "Bearer ".concat(token)); + } + return requestBuilder.build(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public HttpHelper.HttpOptional send(HttpRequest request, Class clazz) throws IOException { + return HttpHelper.send(httpClient, request, makeEH(clazz)); + } + + public static class SimpleError { + public String error; + public int code; + + public SimpleError(String error) { + this.error = error; + } + + @Override + public String toString() { + return "SimpleError{" + + "error='" + error + '\'' + + ", code=" + code + + '}'; + } + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthException.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthException.java index 13a6aae5..83e81014 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthException.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthException.java @@ -1,6 +1,10 @@ package pro.gravit.launchserver.auth; +import pro.gravit.launcher.events.request.AuthRequestEvent; + import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; public final class AuthException extends IOException { private static final long serialVersionUID = -2586107832847245863L; @@ -10,6 +14,24 @@ public AuthException(String message) { super(message); } + public static AuthException need2FA() { + return new AuthException(AuthRequestEvent.TWO_FACTOR_NEED_ERROR_MESSAGE); + } + + public static AuthException needMFA(List factors) { + String message = AuthRequestEvent.ONE_FACTOR_NEED_ERROR_MESSAGE_PREFIX + .concat(factors.stream().map(String::valueOf).collect(Collectors.joining("."))); + return new AuthException(message); + } + + public static AuthException wrongPassword() { + return new AuthException(AuthRequestEvent.WRONG_PASSWORD_ERROR_MESSAGE); + } + + public static AuthException userNotFound() { + return new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE); + } + @Override public String toString() { return getMessage(); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java index ab71c7e7..ff58cc50 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/AuthProviderPair.java @@ -4,7 +4,6 @@ import org.apache.logging.log4j.Logger; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.auth.core.AuthCoreProvider; -import pro.gravit.launchserver.auth.core.AuthSocialProvider; import pro.gravit.launchserver.auth.core.MySQLCoreProvider; import pro.gravit.launchserver.auth.texture.TextureProvider; @@ -17,7 +16,6 @@ public final class AuthProviderPair { private transient final Logger logger = LogManager.getLogger(); public boolean isDefault = true; public AuthCoreProvider core; - public AuthSocialProvider social; public TextureProvider textureProvider; public Map links; public transient String name; @@ -33,17 +31,6 @@ public AuthProviderPair(AuthCoreProvider core, TextureProvider textureProvider) this.textureProvider = textureProvider; } - public AuthProviderPair(AuthCoreProvider core, AuthSocialProvider social) { - this.core = core; - this.social = social; - } - - public AuthProviderPair(AuthCoreProvider core, AuthSocialProvider social, TextureProvider textureProvider) { - this.core = core; - this.social = social; - this.textureProvider = textureProvider; - } - public static Set getFeatures(Class clazz) { Set list = new HashSet<>(); getFeatures(clazz, list); @@ -79,7 +66,6 @@ public static void getFeatures(Class clazz, Set list) { public final T isSupport(Class clazz) { if (core == null) return null; T result = null; - if (social != null) result = social.isSupport(clazz); if (result == null) result = core.isSupport(clazz); return result; } @@ -90,10 +76,6 @@ public final void init(LaunchServer srv, String name) { core.init(srv); features = new HashSet<>(); getFeatures(core.getClass(), features); - if (social != null) { - social.init(srv, core); - getFeatures(social.getClass(), features); - } } public final void link(LaunchServer srv) { @@ -111,16 +93,9 @@ public final void link(LaunchServer srv) { } public final void close() throws IOException { - if (social != null) { - social.close(); - } core.close(); if (textureProvider != null) { textureProvider.close(); } } - - public final boolean isUseSocial() { - return core != null && social != null; - } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java index 58585117..bffceb7e 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/AuthCoreProvider.java @@ -45,7 +45,8 @@ public static void registerProviders() { if (!registredProviders) { providers.register("reject", RejectAuthCoreProvider.class); providers.register("mysql", MySQLCoreProvider.class); - providers.register("json", JsonCoreProvider.class); + providers.register("memory", MemoryAuthCoreProvider.class); + providers.register("http", HttpAuthCoreProvider.class); registredProviders = true; } } @@ -62,16 +63,22 @@ public User getUserByLogin(String login) { public abstract AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context /* may be null */); - public abstract void verifyAuth(AuthResponse.AuthContext context) throws AuthException; + public void verifyAuth(AuthResponse.AuthContext context) throws AuthException { + // None + } - public abstract PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password); + public abstract AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context /* may be null */, AuthRequest.AuthPasswordInterface password /* may be null */, boolean minecraftAccess) throws IOException; - public abstract AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context /* may be null */, PasswordVerifyReport report /* may be null */, boolean minecraftAccess) throws IOException; + public AuthManager.AuthReport authorize(User user, AuthResponse.AuthContext context /* may be null */, AuthRequest.AuthPasswordInterface password /* may be null */, boolean minecraftAccess) throws IOException { + return authorize(user.getUsername(), context, password, minecraftAccess); + } public abstract void init(LaunchServer server); // Auth Handler methods - protected abstract boolean updateServerID(User user, String serverID) throws IOException; + protected boolean updateServerID(User user, String serverID) throws IOException { + throw new UnsupportedOperationException(); + } public List getDetails(Client client) { return List.of(new AuthPasswordDetails()); @@ -80,32 +87,28 @@ public List getDetails( @Override public Map getCommands() { Map map = defaultCommandsMap(); - map.put("checkpassword", new SubCommand("[username] [json/plain password data]", "check password") { + map.put("auth", new SubCommand("[login] (json/plain password data)", "Test auth") { @Override public void invoke(String... args) throws Exception { - verifyArgs(args, 2); - User user = getUserByUsername(args[0]); - if (user == null) throw new CommandException("User not found"); - AuthRequest.AuthPasswordInterface password; - if (args[1].startsWith("{")) { - password = Launcher.gsonManager.gson.fromJson(args[1], AuthRequest.AuthPasswordInterface.class); - } else { - password = new AuthPlainPassword(args[1]); - } - PasswordVerifyReport report = verifyPassword(user, password); - if (report.success) { - logger.info("Password correct"); - } else { - if (report.needMoreFactors) { - if (report.factors.size() == 1 && report.factors.get(0) == -1) { - logger.info("Password not correct: Required 2FA"); - } else { - logger.info("Password not correct: Required more factors: {}", report.factors.toString()); - } + verifyArgs(args, 1); + AuthRequest.AuthPasswordInterface password = null; + if(args.length > 1) { + if (args[1].startsWith("{")) { + password = Launcher.gsonManager.gson.fromJson(args[1], AuthRequest.AuthPasswordInterface.class); } else { - logger.info("Password incorrect"); + password = new AuthPlainPassword(args[1]); } } + var report = authorize(args[0], null, password, false); + if (report.isUsingOAuth()) { + logger.info("OAuth: AccessToken: {} RefreshToken: {} MinecraftAccessToken: {}", report.oauthAccessToken(), report.oauthRefreshToken(), report.minecraftAccessToken()); + if (report.session() != null) { + logger.info("UserSession: id {} expire {} user {}", report.session().getID(), report.session().getExpireIn(), report.session().getUser() == null ? "null" : "found"); + logger.info(report.session().toString()); + } + } else { + logger.info("Basic: MinecraftAccessToken: {}", report.minecraftAccessToken()); + } } }); map.put("getuserbyusername", new SubCommand("[username]", "get user by username") { @@ -132,32 +135,6 @@ public void invoke(String... args) throws Exception { } } }); - map.put("createsession", new SubCommand("[username] (true/false)", "create user session with/without minecraft access") { - @Override - public void invoke(String... args) throws Exception { - verifyArgs(args, 1); - User user = getUserByUsername(args[0]); - if (user == null) { - logger.info("User {} not found", args[0]); - return; - } - boolean minecraftAccess = args.length > 1 && Boolean.parseBoolean(args[1]); - AuthManager.AuthReport report = createOAuthSession(user, null, null, minecraftAccess); - if (report == null) { - logger.error("Method createOAuthSession return null"); - return; - } - if (report.isUsingOAuth()) { - logger.info("OAuth: AccessToken: {} RefreshToken: {} MinecraftAccessToken: {}", report.oauthAccessToken(), report.oauthRefreshToken(), report.minecraftAccessToken()); - if (report.session() != null) { - logger.info("UserSession: id {} expire {} user {}", report.session().getID(), report.session().getExpireIn(), report.session().getUser() == null ? "null" : "found"); - logger.info(report.session().toString()); - } - } else { - logger.info("Basic: MinecraftAccessToken: {}", report.minecraftAccessToken()); - } - } - }); { var instance = isSupport(AuthSupportGetAllUsers.class); if (instance != null) { @@ -355,6 +332,12 @@ public PasswordVerifyReport(boolean success) { this.factors = List.of(); } + public PasswordVerifyReport(AuthManager.AuthReport report) { + this.success = true; + this.needMoreFactors = false; + this.factors = List.of(); + } + public PasswordVerifyReport(int nextFactor) { this.success = false; this.needMoreFactors = true; @@ -372,6 +355,10 @@ private PasswordVerifyReport(boolean success, boolean needMoreFactors, List providers = new ProviderMap<>("AuthSocialProvider"); - private static final Logger logger = LogManager.getLogger(); - private static boolean registredProviders = false; - - public static void registerProviders() { - if (!registredProviders) { - registredProviders = true; - } - } - - public abstract void init(LaunchServer server, AuthCoreProvider provider); - - public abstract List getDetails(Client client); - - public abstract SocialResult preAuth(AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password) throws AuthException; - - @SuppressWarnings("unchecked") - public T isSupport(Class clazz) { - if (clazz.isAssignableFrom(getClass())) return (T) this; - return null; - } - - @Override - public abstract void close() throws IOException; - - public static class SocialResult { - public String login; - public AuthRequest.AuthPasswordInterface password; - public User user; - - public SocialResult(String login, AuthRequest.AuthPasswordInterface password, User user) { - this.login = login; - this.password = password; - this.user = user; - } - - public static SocialResult ofLoginAndPassword(String login, AuthRequest.AuthPasswordInterface password) { - return new SocialResult(login, password, null); - } - - public static SocialResult ofUser(User user) { - return new SocialResult(null, null, user); - } - } -} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/HttpAuthCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/HttpAuthCoreProvider.java new file mode 100644 index 00000000..732f8b95 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/HttpAuthCoreProvider.java @@ -0,0 +1,333 @@ +package pro.gravit.launchserver.auth.core; + +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.events.request.GetAvailabilityAuthRequestEvent; +import pro.gravit.launcher.profiles.Texture; +import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launchserver.HttpRequester; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.AuthException; +import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures; +import pro.gravit.launchserver.helper.HttpHelper; +import pro.gravit.launchserver.manangers.AuthManager; +import pro.gravit.launchserver.socket.Client; +import pro.gravit.launchserver.socket.response.auth.AuthResponse; +import pro.gravit.utils.helper.CommonHelper; + +import java.io.IOException; +import java.util.List; +import java.util.UUID; + +public class HttpAuthCoreProvider extends AuthCoreProvider { + private transient final Logger logger = LogManager.getLogger(); + private transient HttpRequester requester; + public String bearerToken; + public String getUserByUsernameUrl; + public String getUserByLoginUrl; + public String getUserByUUIDUrl; + public String getUserByTokenUrl; + public String getAuthDetails; + public String refreshTokenUrl; + public String authorizeUrl; + public String joinServerUrl; + public String checkServerUrl; + public String updateServerIdUrl; + @Override + public User getUserByUsername(String username) { + try { + return requester.send(requester.get(CommonHelper.replace(getUserByUsernameUrl, "username", username), null), HttpUser.class).getOrThrow(); + } catch (IOException e) { + logger.error(e); + return null; + } + } + + @Override + public User getUserByLogin(String login) { + if(getUserByLoginUrl != null) { + try { + return requester.send(requester.get(CommonHelper.replace(getUserByLoginUrl, "login", login), null), HttpUser.class).getOrThrow(); + } catch (IOException e) { + logger.error(e); + return null; + } + } + return super.getUserByLogin(login); + } + + @Override + public User getUserByUUID(UUID uuid) { + try { + return requester.send(requester.get(CommonHelper.replace(getUserByUUIDUrl, "uuid", uuid.toString()), null), HttpUser.class).getOrThrow(); + } catch (IOException e) { + logger.error(e); + return null; + } + } + + @Override + public List getDetails(Client client) { + if(getAuthDetails == null) { + return super.getDetails(client); + } + try { + var result = requester.send(requester.get(getAuthDetails, bearerToken), GetAuthDetailsResponse.class).getOrThrow(); + return result.details; + } catch (IOException e) { + logger.error(e); + return super.getDetails(client); + } + } + + @Override + public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired { + if(getUserByTokenUrl == null) { + return null; + } + try { + var result = requester.send(requester.get(getUserByTokenUrl, accessToken), HttpUserSession.class); + if(!result.isSuccessful()) { + var error = result.error().error; + if(error.equals(AuthRequestEvent.OAUTH_TOKEN_EXPIRE)) { + throw new OAuthAccessTokenExpired(); + } + return null; + } + return result.getOrThrow(); + } catch (IOException e) { + logger.error(e); + return null; + } + } + + @Override + public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { + if(refreshTokenUrl == null) { + return null; + } + try { + return requester.send(requester.post(refreshTokenUrl, new RefreshTokenRequest(refreshToken, context), + null), AuthManager.AuthReport.class).getOrThrow(); + } catch (IOException e) { + logger.error(e); + return null; + } + } + + @Override + 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), + bearerToken), AuthManager.AuthReport.class); + if(!result.isSuccessful()) { + var error = result.error().error; + if(error != null) { + throw new AuthException(error); + } + } + return result.getOrThrow(); + } + + @Override + protected boolean updateServerID(User user, String serverID) throws IOException { + var result = requester.send(requester.post(updateServerIdUrl, new UpdateServerIdRequest(user.getUsername(), user.getUUID(), serverID), + null), Void.class); + return result.isSuccessful(); + } + + @Override + public User checkServer(Client client, String username, String serverID) throws IOException { + return requester.send(requester.post(checkServerUrl, new CheckServerRequest(username, serverID), null), HttpUser.class).getOrThrow(); + } + + @Override + public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException { + var result = requester.send(requester.post(joinServerUrl, new JoinServerRequest(username, accessToken, serverID), null), Void.class); + return result.isSuccessful(); + } + + public static class UpdateServerIdRequest { + public String username; + public UUID uuid; + public String serverId; + + public UpdateServerIdRequest(String username, UUID uuid, String serverId) { + this.username = username; + this.uuid = uuid; + this.serverId = serverId; + } + } + + public static class CheckServerRequest { + public String username; + public String serverId; + + public CheckServerRequest(String username, String serverId) { + this.username = username; + this.serverId = serverId; + } + } + + public static class GetAuthDetailsResponse { + public List details; + } + + public static class JoinServerRequest { + public String username; + public String accessToken; + public String serverId; + + public JoinServerRequest(String username, String accessToken, String serverId) { + this.username = username; + this.accessToken = accessToken; + this.serverId = 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 String login; + public AuthResponse.AuthContext context; + public AuthRequest.AuthPasswordInterface password; + public boolean minecraftAccess; + + public AuthorizeRequest() { + } + + public AuthorizeRequest(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) { + this.login = login; + this.context = context; + this.password = password; + this.minecraftAccess = minecraftAccess; + } + } + + public static class RefreshTokenRequest { + public String refreshToken; + public AuthResponse.AuthContext context; + + public RefreshTokenRequest(String refreshToken, AuthResponse.AuthContext context) { + this.refreshToken = refreshToken; + this.context = context; + } + } + + public static class HttpUser implements User, UserSupportTextures { + private String username; + private UUID uuid; + private String serverId; + private String accessToken; + private ClientPermissions permissions; + private Texture skin; + private Texture cloak; + + public HttpUser() { + } + + public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions) { + this.username = username; + this.uuid = uuid; + this.serverId = serverId; + this.accessToken = accessToken; + this.permissions = permissions; + } + + public HttpUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, Texture skin, Texture cloak) { + this.username = username; + this.uuid = uuid; + this.serverId = serverId; + this.accessToken = accessToken; + this.permissions = permissions; + this.skin = skin; + this.cloak = cloak; + } + + @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() { + return skin; + } + + @Override + public Texture getCloakTexture() { + return cloak; + } + } + + public static class HttpUserSession implements UserSession { + private String id; + private User user; + private long expireIn; + + public HttpUserSession() { + } + + public HttpUserSession(String id, User user, long expireIn) { + this.id = id; + this.user = user; + this.expireIn = expireIn; + } + + @Override + public String getID() { + return id; + } + + @Override + public User getUser() { + return user; + } + + @Override + public long getExpireIn() { + return expireIn; + } + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java deleted file mode 100644 index 5cb8e255..00000000 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/JsonCoreProvider.java +++ /dev/null @@ -1,410 +0,0 @@ -package pro.gravit.launchserver.auth.core; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import pro.gravit.launcher.ClientPermissions; -import pro.gravit.launcher.Launcher; -import pro.gravit.launcher.events.request.GetAvailabilityAuthRequestEvent; -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.password.PasswordVerifier; -import pro.gravit.launchserver.manangers.AuthManager; -import pro.gravit.launchserver.socket.Client; -import pro.gravit.launchserver.socket.response.auth.AuthResponse; - -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.time.Duration; -import java.util.List; -import java.util.UUID; - -public class JsonCoreProvider extends AuthCoreProvider { - private static transient final Logger logger = LogManager.getLogger(); - public String getUserByUsernameUrl; - public String getUserByLoginUrl; - public String getUserByUUIDUrl; - public String getUserSessionByOAuthAccessTokenUrl; - public String getAuthDetailsUrl; - public String refreshAccessTokenUrl; - public String verifyPasswordUrl; - public String createOAuthSessionUrl; - public String updateServerIdUrl; - public String joinServerUrl; - public String checkServerUrl; - public String bearerToken; - public PasswordVerifier passwordVerifier; - private transient HttpClient client; - - public static R jsonRequest(T request, String url, String bearerToken, Class clazz, HttpClient client) { - HttpRequest.BodyPublisher publisher; - if (request != null) { - publisher = HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(request)); - } else { - publisher = HttpRequest.BodyPublishers.noBody(); - } - try { - HttpRequest.Builder request1 = HttpRequest.newBuilder() - .method("POST", publisher) - .uri(new URI(url)) - .header("Content-Type", "application/json; charset=UTF-8") - .header("Accept", "application/json") - .timeout(Duration.ofMillis(10000)); - if (bearerToken != null) { - request1.header("Authorization", "Bearer ".concat(bearerToken)); - } - HttpResponse response = client.send(request1.build(), HttpResponse.BodyHandlers.ofInputStream()); - int statusCode = response.statusCode(); - if (200 > statusCode || statusCode > 300) { - if (statusCode >= 500) { - logger.error("JsonCoreProvider: {} return {}", url, statusCode); - } else if (statusCode >= 300 && statusCode <= 400) { - logger.error("JsonCoreProvider: {} return {}, try redirect to {}. Redirects not supported!", url, statusCode, response.headers().firstValue("Location").orElse("Unknown")); - } else if (statusCode == 403 || statusCode == 401) { - logger.error("JsonCoreProvider: {} return {}. Please set 'bearerToken'!", url, statusCode); - } - return null; - } - try (Reader reader = new InputStreamReader(response.body())) { - return Launcher.gsonManager.gson.fromJson(reader, clazz); - } - } catch (Exception e) { - return null; - } - } - - @Override - public User getUserByUsername(String username) { - return jsonRequest(new JsonGetUserByUsername(username), getUserByUsernameUrl, JsonUser.class); - } - - @Override - public User getUserByLogin(String login) { - if (getUserByLoginUrl != null) { - return jsonRequest(new JsonGetUserByUsername(login), getUserByLoginUrl, JsonUser.class); - } - return super.getUserByLogin(login); - } - - @Override - public User getUserByUUID(UUID uuid) { - return jsonRequest(new JsonGetUserByUUID(uuid), getUserByUUIDUrl, JsonUser.class); - } - - @Override - public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired { - if (getUserSessionByOAuthAccessTokenUrl == null) { - return null; - } - JsonGetUserSessionByOAuthTokenResponse response = jsonRequest(new JsonGetUserSessionByAccessToken(accessToken), getUserSessionByOAuthAccessTokenUrl, JsonGetUserSessionByOAuthTokenResponse.class); - if (response == null) return null; - if (!response.expired) throw new OAuthAccessTokenExpired(); - return response.session; - } - - @Override - public List getDetails(Client client) { - if (getAuthDetailsUrl != null) { - JsonGetDetailsResponse response = jsonRequest(new JsonGetDetails(), getAuthDetailsUrl, JsonGetDetailsResponse.class); - if (response == null) return super.getDetails(client); - return response.details; - } - return super.getDetails(client); - } - - @Override - public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { - JsonAuthReportResponse response = jsonRequest(new JsonRefreshToken(refreshToken, context.ip), this.refreshAccessTokenUrl, JsonAuthReportResponse.class); - return response == null ? null : response.toAuthReport(); - } - - @Override - public void verifyAuth(AuthResponse.AuthContext context) throws AuthException { - - } - - @Override - public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password) { - JsonUser jsonUser = (JsonUser) user; - if (password instanceof AuthPlainPassword && jsonUser.password != null && passwordVerifier != null) { - if (passwordVerifier.check(jsonUser.password, ((AuthPlainPassword) password).password)) { - return PasswordVerifyReport.OK; - } else { - return PasswordVerifyReport.FAILED; - } - } - if (user == null) { - return jsonRequest(new JsonPasswordVerify(null, null, password), verifyPasswordUrl, PasswordVerifyReport.class); - } - return jsonRequest(new JsonPasswordVerify(user.getUsername(), user.getUUID(), password), verifyPasswordUrl, PasswordVerifyReport.class); - } - - @Override - public AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context, PasswordVerifyReport report, boolean minecraftAccess) throws IOException { - JsonAuthReportResponse response = jsonRequest(new JsonCreateOAuthSession(user == null ? null : user.getUsername(), user == null ? null : user.getUUID(), minecraftAccess), createOAuthSessionUrl, JsonAuthReportResponse.class); - if (response == null) return null; - if (response.error != null) throw new AuthException(response.error); - JsonUser user1 = (JsonUser) user; - user1.accessToken = response.minecraftAccessToken; - return response.toAuthReport(); - } - - @Override - public void init(LaunchServer server) { - client = HttpClient.newBuilder().build(); - } - - @Override - public User checkServer(Client client, String username, String serverID) throws IOException { - if (checkServerUrl == null) { - return super.checkServer(client, username, serverID); - } - return jsonRequest(new JsonCheckServer(username, serverID), checkServerUrl, JsonUser.class); - } - - @Override - public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException { - if (joinServerUrl == null) { - return super.joinServer(client, username, accessToken, serverID); - } - return jsonRequest(new JsonJoinServer(username, accessToken, serverID), joinServerUrl, JsonSuccessResponse.class).success; - } - - @Override - protected boolean updateServerID(User user, String serverID) throws IOException { - JsonUser jsonUser = (JsonUser) user; - if (updateServerIdUrl == null) { - return false; - } - jsonUser.serverId = serverID; - JsonSuccessResponse successResponse = jsonRequest(new JsonUpdateServerId(user.getUsername(), user.getUUID(), serverID), updateServerIdUrl, JsonSuccessResponse.class); - if (successResponse == null) return false; - return successResponse.success; - } - - @Override - public void close() throws IOException { - - } - - public R jsonRequest(T request, String url, Class clazz) { - return jsonRequest(request, url, bearerToken, clazz, client); - } - - public static class JsonGetUserByUsername { - public String username; - - public JsonGetUserByUsername(String username) { - this.username = username; - } - } - - public static class JsonCheckServer { - public String username; - public String serverId; - - public JsonCheckServer(String username, String serverId) { - this.username = username; - this.serverId = serverId; - } - } - - public static class JsonJoinServer { - public String username; - public String accessToken; - public String serverId; - - public JsonJoinServer(String username, String accessToken, String serverId) { - this.username = username; - this.accessToken = accessToken; - this.serverId = serverId; - } - } - - public static class JsonGetUserByUUID { - public UUID uuid; - - public JsonGetUserByUUID(UUID uuid) { - this.uuid = uuid; - } - } - - public static class JsonGetUserSessionByAccessToken { - public String accessToken; - - public JsonGetUserSessionByAccessToken(String accessToken) { - this.accessToken = accessToken; - } - } - - public static class JsonRefreshToken { - public String refreshToken; - public String ip; - - public JsonRefreshToken(String refreshToken, String ip) { - this.refreshToken = refreshToken; - this.ip = ip; - } - } - - public static class JsonAuthReportResponse { - public String minecraftAccessToken; - public String oauthAccessToken; - public String oauthRefreshToken; - public long oauthExpire; - public JsonUserSession session; - public String error; - - public AuthManager.AuthReport toAuthReport() { - return new AuthManager.AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session); - } - } - - public static class JsonPasswordVerify { - public String username; - public UUID uuid; - public AuthRequest.AuthPasswordInterface password; - - public JsonPasswordVerify(String username, UUID uuid, AuthRequest.AuthPasswordInterface password) { - this.username = username; - this.uuid = uuid; - this.password = password; - } - } - - public static class JsonCreateOAuthSession { - public String username; - public UUID uuid; - public boolean minecraftAccess; - - public JsonCreateOAuthSession(String username, UUID uuid, boolean minecraftAccess) { - this.username = username; - this.uuid = uuid; - this.minecraftAccess = minecraftAccess; - } - } - - public static class JsonUpdateServerId { - public String username; - public UUID uuid; - public String serverId; - - public JsonUpdateServerId(String username, UUID uuid, String serverId) { - this.username = username; - this.uuid = uuid; - this.serverId = serverId; - } - } - - public static class JsonSuccessResponse { - public boolean success; - } - - public static class JsonGetUserSessionByOAuthTokenResponse { - public boolean expired; - public JsonUserSession session; - - public JsonGetUserSessionByOAuthTokenResponse() { - } - } - - public static class JsonGetDetails { - - } - - public static class JsonGetDetailsResponse { - public List details; - } - - public static class JsonUser implements User { - private String username; - private UUID uuid; - private String serverId; - private String accessToken; - private ClientPermissions permissions; - private String password; - - public JsonUser() { - } - - public JsonUser(String username, UUID uuid, String serverId, String accessToken, ClientPermissions permissions, String password) { - this.username = username; - this.uuid = uuid; - this.serverId = serverId; - this.accessToken = accessToken; - this.permissions = permissions; - this.password = password; - } - - @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 "JsonUser{" + - "username='" + username + '\'' + - ", uuid=" + uuid + - ", permissions=" + permissions + - '}'; - } - } - - public static class JsonUserSession implements UserSession { - public String id; - public JsonUser user; - public long expireIn; - - @Override - public String getID() { - return id; - } - - @Override - public User getUser() { - return user; - } - - @Override - public long getExpireIn() { - return expireIn; - } - - @Override - public String toString() { - return "JsonUserSession{" + - "id='" + id + '\'' + - "user='" + (user == null ? null : user.getUsername()) + '\'' + - ", expireIn=" + expireIn + - '}'; - } - } -} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MemoryAuthCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MemoryAuthCoreProvider.java new file mode 100644 index 00000000..c3c83db6 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MemoryAuthCoreProvider.java @@ -0,0 +1,212 @@ +package pro.gravit.launchserver.auth.core; + +import pro.gravit.launcher.ClientPermissions; +import pro.gravit.launcher.events.request.GetAvailabilityAuthRequestEvent; +import pro.gravit.launcher.request.auth.AuthRequest; +import pro.gravit.launcher.request.auth.details.AuthLoginOnlyDetails; +import pro.gravit.launchserver.LaunchServer; +import pro.gravit.launchserver.auth.AuthException; +import pro.gravit.launchserver.manangers.AuthManager; +import pro.gravit.launchserver.socket.Client; +import pro.gravit.launchserver.socket.response.auth.AuthResponse; +import pro.gravit.utils.helper.SecurityHelper; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.*; + +public class MemoryAuthCoreProvider extends AuthCoreProvider { + private transient final List memory = new ArrayList<>(16); + @Override + public User getUserByUsername(String username) { + synchronized (memory) { + for(MemoryUser u : memory) { + if(u.username.equals(username)) { + return u; + } + } + var result = new MemoryUser(username); + memory.add(result); + return result; + } + } + + @Override + public List getDetails(Client client) { + return List.of(new AuthLoginOnlyDetails()); + } + + @Override + public User getUserByUUID(UUID uuid) { + synchronized (memory) { + for(MemoryUser u : memory) { + if(u.uuid.equals(uuid)) { + return u; + } + } + } + return null; + } + + @Override + public UserSession getUserSessionByOAuthAccessToken(String accessToken) throws OAuthAccessTokenExpired { + synchronized (memory) { + for(MemoryUser u : memory) { + if(u.accessToken.equals(accessToken)) { + return new MemoryUserSession(u); + } + } + } + return null; + } + + @Override + public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthResponse.AuthContext context) { + return null; + } + + @Override + public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException { + if(login == null) { + throw AuthException.userNotFound(); + } + MemoryUser user = null; + synchronized (memory) { + for(MemoryUser u : memory) { + if(u.username.equals(login)) { + user = u; + break; + } + } + if(user == null) { + user = new MemoryUser(login); + memory.add(user); + } + } + if(minecraftAccess) { + return AuthManager.AuthReport.ofOAuth(user.accessToken, null, 0, new MemoryUserSession(user)); + } else { + return AuthManager.AuthReport.ofOAuthWithMinecraft(user.accessToken, user.accessToken, null, 0, new MemoryUserSession(user)); + } + } + + @Override + protected boolean updateServerID(User user, String serverID) throws IOException { + MemoryUser memoryUser = (MemoryUser) user; + memoryUser.serverId = serverID; + return true; + } + + @Override + public User checkServer(Client client, String username, String serverID) throws IOException { + synchronized (memory) { + for(MemoryUser u : memory) { + if(u.username.equals(username)) { + return u; + } + } + var result = new MemoryUser(username); + memory.add(result); + return result; + } + } + + @Override + public boolean joinServer(Client client, String username, String accessToken, String serverID) throws IOException { + return true; + } + + @Override + public void init(LaunchServer server) { + + } + + @Override + public void close() throws IOException { + + } + + public static class MemoryUser implements User { + private String username; + private UUID uuid; + private String serverId; + private String accessToken; + private ClientPermissions permissions; + + public MemoryUser(String username) { + this.username = username; + this.uuid = makeUuidFromUsername(username); + this.accessToken = SecurityHelper.randomStringToken(); + this.permissions = new ClientPermissions(); + } + + private static UUID makeUuidFromUsername(String username) { + return UUID.nameUUIDFromBytes(username.getBytes(StandardCharsets.UTF_8)); + } + + @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 boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MemoryUser that = (MemoryUser) o; + return uuid.equals(that.uuid); + } + + @Override + public int hashCode() { + return Objects.hash(uuid); + } + } + + public static class MemoryUserSession implements UserSession { + private String id; + private MemoryUser user; + private long expireIn; + + public MemoryUserSession(MemoryUser user) { + this.id = SecurityHelper.randomStringToken(); + this.user = user; + this.expireIn = 0; + } + + @Override + public String getID() { + return id; + } + + @Override + public User getUser() { + return user; + } + + @Override + public long getExpireIn() { + return expireIn; + } + } +} diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java index 2046769e..7842058c 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/MySQLCoreProvider.java @@ -105,27 +105,27 @@ public AuthManager.AuthReport refreshAccessToken(String refreshToken, AuthRespon } @Override - public void verifyAuth(AuthResponse.AuthContext context) throws AuthException { - - } - - @Override - public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password) { - if (passwordVerifier.check(((MySQLUser) user).password, ((AuthPlainPassword) password).password)) { - return PasswordVerifyReport.OK; - } else { - return PasswordVerifyReport.FAILED; + 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(); } - } - - @Override - public AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context, PasswordVerifyReport report, boolean minecraftAccess) throws IOException { + 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); if (minecraftAccess) { String minecraftAccessToken = SecurityHelper.randomStringToken(); - updateAuth(user, minecraftAccessToken); - return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken); + updateAuth(mySQLUser, minecraftAccessToken); + return AuthManager.AuthReport.ofMinecraftAccessToken(minecraftAccessToken, session); } else { - return AuthManager.AuthReport.ofMinecraftAccessToken(null); + return AuthManager.AuthReport.ofMinecraftAccessToken(null, session); } } @@ -333,13 +333,14 @@ public UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo hardwa } @Override - public void connectUserAndHardware(User user, UserHardware hardware) { - MySQLUser mySQLUser = (MySQLUser) user; + public void connectUserAndHardware(UserSession userSession, UserHardware hardware) { + MySQLUserSession mySQLUserSession = (MySQLUserSession) userSession; + MySQLUser mySQLUser = mySQLUserSession.user; MySQLUserHardware mySQLUserHardware = (MySQLUserHardware) hardware; if (mySQLUser.hwidId == mySQLUserHardware.id) return; mySQLUser.hwidId = mySQLUserHardware.id; try (Connection connection = mySQLHolder.getConnection()) { - setUserHardwareId(connection, user.getUUID(), mySQLUserHardware.id); + setUserHardwareId(connection, mySQLUser.getUUID(), mySQLUserHardware.id); } catch (SQLException throwables) { logger.error("SQL Error", throwables); } @@ -513,4 +514,29 @@ 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; + } + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/RejectAuthCoreProvider.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/RejectAuthCoreProvider.java index 6f211353..6db40500 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/RejectAuthCoreProvider.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/RejectAuthCoreProvider.java @@ -36,13 +36,8 @@ public void verifyAuth(AuthResponse.AuthContext context) throws AuthException { } @Override - public PasswordVerifyReport verifyPassword(User user, AuthRequest.AuthPasswordInterface password) { - return PasswordVerifyReport.FAILED; - } - - @Override - public AuthManager.AuthReport createOAuthSession(User user, AuthResponse.AuthContext context, PasswordVerifyReport report, boolean minecraftAccess) throws IOException { - return null; + public AuthManager.AuthReport authorize(String login, AuthResponse.AuthContext context, AuthRequest.AuthPasswordInterface password, boolean minecraftAccess) throws IOException { + throw new AuthException("Please configure AuthCoreProvider"); } @Override diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java index e8daa0e8..60ba8353 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportHardware.java @@ -2,6 +2,7 @@ import pro.gravit.launcher.request.secure.HardwareReportRequest; import pro.gravit.launchserver.auth.core.User; +import pro.gravit.launchserver.auth.core.UserSession; import pro.gravit.launchserver.auth.core.interfaces.UserHardware; import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportHardware; import pro.gravit.launchserver.auth.protect.hwid.HWIDProvider; @@ -18,7 +19,7 @@ public interface AuthSupportHardware extends AuthSupport { UserHardware createHardwareInfo(HardwareReportRequest.HardwareInfo info, byte[] publicKey); - void connectUserAndHardware(User user, UserHardware hardware); + void connectUserAndHardware(UserSession userSession, UserHardware hardware); void addPublicKeyToHardwareInfo(UserHardware hardware, byte[] publicKey); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/password/JsonPasswordVerifier.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/password/JsonPasswordVerifier.java index d712fd10..171a5769 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/password/JsonPasswordVerifier.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/password/JsonPasswordVerifier.java @@ -1,17 +1,27 @@ package pro.gravit.launchserver.auth.password; -import pro.gravit.launchserver.auth.core.JsonCoreProvider; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import pro.gravit.launcher.Launcher; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URI; import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; public class JsonPasswordVerifier extends PasswordVerifier { + private static transient final Logger logger = LogManager.getLogger(); private transient final HttpClient client = HttpClient.newBuilder().build(); public String url; public String bearerToken; @Override public boolean check(String encryptedPassword, String password) { - JsonPasswordResponse response = JsonCoreProvider.jsonRequest(new JsonPasswordRequest(encryptedPassword, password), url, bearerToken, JsonPasswordResponse.class, client); + JsonPasswordResponse response = jsonRequest(new JsonPasswordRequest(encryptedPassword, password), url, bearerToken, JsonPasswordResponse.class, client); if (response != null) { return response.success; } @@ -31,4 +41,41 @@ public JsonPasswordRequest(String encryptedPassword, String password) { public static class JsonPasswordResponse { public boolean success; } + + public static R jsonRequest(T request, String url, String bearerToken, Class clazz, HttpClient client) { + HttpRequest.BodyPublisher publisher; + if (request != null) { + publisher = HttpRequest.BodyPublishers.ofString(Launcher.gsonManager.gson.toJson(request)); + } else { + publisher = HttpRequest.BodyPublishers.noBody(); + } + try { + HttpRequest.Builder request1 = HttpRequest.newBuilder() + .method("POST", publisher) + .uri(new URI(url)) + .header("Content-Type", "application/json; charset=UTF-8") + .header("Accept", "application/json") + .timeout(Duration.ofMillis(10000)); + if (bearerToken != null) { + request1.header("Authorization", "Bearer ".concat(bearerToken)); + } + HttpResponse response = client.send(request1.build(), HttpResponse.BodyHandlers.ofInputStream()); + int statusCode = response.statusCode(); + if (200 > statusCode || statusCode > 300) { + if (statusCode >= 500) { + logger.error("JsonCoreProvider: {} return {}", url, statusCode); + } else if (statusCode >= 300 && statusCode <= 400) { + logger.error("JsonCoreProvider: {} return {}, try redirect to {}. Redirects not supported!", url, statusCode, response.headers().firstValue("Location").orElse("Unknown")); + } else if (statusCode == 403 || statusCode == 401) { + logger.error("JsonCoreProvider: {} return {}. Please set 'bearerToken'!", url, statusCode); + } + return null; + } + try (Reader reader = new InputStreamReader(response.body())) { + return Launcher.gsonManager.gson.fromJson(reader, clazz); + } + } catch (Exception e) { + return null; + } + } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java index a440e330..520cc9ce 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/protect/AdvancedProtectHandler.java @@ -76,7 +76,7 @@ public void onHardwareReport(HardwareReportResponse response, Client client) { } else { authSupportHardware.addPublicKeyToHardwareInfo(hardware, client.trustLevel.publicKey); } - authSupportHardware.connectUserAndHardware(client.getUser(), hardware); + authSupportHardware.connectUserAndHardware(client.sessionObject, hardware); if (hardware.isBanned()) { throw new SecurityException("Your hardware banned"); } @@ -108,7 +108,7 @@ public VerifySecureLevelKeyRequestEvent onSuccessVerify(Client client) { throw new SecurityException("Your hardware banned"); } client.trustLevel.hardwareInfo = hardware.getHardwareInfo(); - authSupportHardware.connectUserAndHardware(client.getUser(), hardware); + authSupportHardware.connectUserAndHardware(client.sessionObject, hardware); } else if (provider == null) { logger.warn("HWIDProvider null. HardwareInfo not checked!"); } else { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java index a44831dd..3ba63457 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/config/LaunchServerConfig.java @@ -192,7 +192,6 @@ public void init(LaunchServer.ReloadType type) { if (!type.equals(LaunchServer.ReloadType.NO_AUTH)) { for (AuthProviderPair pair : auth.values()) { server.registerObject("auth.".concat(pair.name).concat(".core"), pair.core); - server.registerObject("auth.".concat(pair.name).concat(".social"), pair.social); server.registerObject("auth.".concat(pair.name).concat(".texture"), pair.textureProvider); } } @@ -203,7 +202,6 @@ public void close(LaunchServer.ReloadType type) { try { if (!type.equals(LaunchServer.ReloadType.NO_AUTH)) { for (AuthProviderPair pair : auth.values()) { - server.unregisterObject("auth.".concat(pair.name).concat(".social"), pair.social); server.unregisterObject("auth.".concat(pair.name).concat(".core"), pair.core); server.unregisterObject("auth.".concat(pair.name).concat(".texture"), pair.textureProvider); pair.close(); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java index ce23b57d..1da9a340 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/manangers/AuthManager.java @@ -14,7 +14,6 @@ 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.AuthSocialProvider; import pro.gravit.launchserver.auth.core.User; import pro.gravit.launchserver.auth.core.UserSession; import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportTextures; @@ -31,7 +30,6 @@ 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; @@ -143,62 +141,27 @@ public AuthReport auth(AuthResponse.AuthContext context, AuthRequest.AuthPasswor context.client.sessionObject = session; internalAuth(context.client, context.authType, context.pair, user.getUsername(), user.getUUID(), user.getPermissions(), true); if (context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)) { - return AuthReport.ofMinecraftAccessToken(user.getAccessToken()); + return AuthReport.ofMinecraftAccessToken(user.getAccessToken(), session); } - return AuthReport.ofMinecraftAccessToken(null); + return AuthReport.ofMinecraftAccessToken(null, session); } - User user = null; - boolean skipPasswordCheck = false; String login = context.login; - if (context.pair.social != null) { - AuthSocialProvider.SocialResult result = context.pair.social.preAuth(context, password); - if (result != null) { - if (result.user != null) user = result.user; - if (result.login != null) login = result.login; - if (result.password != null) password = result.password; - if (result.user != null && result.password == null) skipPasswordCheck = true; - } - } - if (user == null && login != null) { - user = provider.getUserByLogin(context.login); - if (user == null) { - throw new AuthException(AuthRequestEvent.USER_NOT_FOUND_ERROR_MESSAGE); - } - } - AuthCoreProvider.PasswordVerifyReport report = null; - if (!skipPasswordCheck) { - report = provider.verifyPassword(user, password); - } - if (skipPasswordCheck || report.success) { - AuthReport result; - try { - result = provider.createOAuthSession(user, context, report, context.authType == AuthResponse.ConnectTypes.CLIENT && server.config.protectHandler.allowGetAccessToken(context)); - } catch (IOException e) { - if (e instanceof AuthException) throw (AuthException) e; - logger.error(e); + try { + 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) { + logger.error("AuthCoreProvider {} method 'authorize' return null", context.pair.name); throw new AuthException("Internal Auth Error"); } - if (user == null) { - if (result.session != null) { - user = result.session.getUser(); - } else { - logger.error("AuthCoreProvider {} method createOAuthSession returns null session with login null", context.pair.name); - throw new AuthException("Internal Auth Error"); - } - } + var session = result.session; + var user = session.getUser(); context.client.coreObject = user; + context.client.sessionObject = session; internalAuth(context.client, context.authType, context.pair, user.getUsername(), user.getUUID(), user.getPermissions(), result.isUsingOAuth()); return result; - } else { - if (report.needMoreFactors) { - 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); + } catch (IOException e) { + if (e instanceof AuthException) throw (AuthException) e; + logger.error(e); + throw new AuthException("Internal Auth Error"); } } @@ -217,7 +180,6 @@ public void internalAuth(Client client, AuthResponse.ConnectTypes authType, Auth client.type = authType; client.uuid = uuid; client.useOAuth = oauth; - client.coreObject = pair.core.getUserByUUID(uuid); } public CheckServerReport checkServer(Client client, String username, String serverID) throws IOException { @@ -374,24 +336,16 @@ public record AuthReport(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire, UserSession session) { - public static AuthReport ofOAuth(String oauthAccessToken, String oauthRefreshToken, long oauthExpire) { - return new AuthReport(null, oauthAccessToken, oauthRefreshToken, oauthExpire, null); - } - public static AuthReport ofOAuth(String oauthAccessToken, String oauthRefreshToken, long oauthExpire, UserSession session) { return new AuthReport(null, oauthAccessToken, oauthRefreshToken, oauthExpire, session); } - public static AuthReport ofOAuthWithMinecraft(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire) { - return new AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, null); - } - public static AuthReport ofOAuthWithMinecraft(String minecraftAccessToken, String oauthAccessToken, String oauthRefreshToken, long oauthExpire, UserSession session) { return new AuthReport(minecraftAccessToken, oauthAccessToken, oauthRefreshToken, oauthExpire, session); } - public static AuthReport ofMinecraftAccessToken(String minecraftAccessToken) { - return new AuthReport(minecraftAccessToken, null, null, 0, null); + public static AuthReport ofMinecraftAccessToken(String minecraftAccessToken, UserSession session) { + return new AuthReport(minecraftAccessToken, null, null, 0, session); } public boolean isUsingOAuth() { diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java index ffc9ab88..4e66c5da 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/AuthResponse.java @@ -76,9 +76,9 @@ public static class AuthContext { public final String profileName; public final String ip; public final ConnectTypes authType; - public final Client client; - public final AuthProviderPair pair; - public AuthManager.AuthReport report; + public transient final Client client; + public transient final AuthProviderPair pair; + public transient AuthManager.AuthReport report; public AuthContext(Client client, String login, String profileName, String ip, ConnectTypes authType, AuthProviderPair pair) { this.client = client; diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/GetAvailabilityAuthResponse.java b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/GetAvailabilityAuthResponse.java index 8df28870..25f0007d 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/GetAvailabilityAuthResponse.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/socket/response/auth/GetAvailabilityAuthResponse.java @@ -20,7 +20,7 @@ public void execute(ChannelHandlerContext ctx, Client client) { List list = new ArrayList<>(); for (AuthProviderPair pair : server.config.auth.values()) { list.add(new GetAvailabilityAuthRequestEvent.AuthAvailability(pair.name, pair.displayName, - pair.isUseSocial() ? pair.social.getDetails(client) : pair.core.getDetails(client))); + pair.core.getDetails(client))); } sendResult(new GetAvailabilityAuthRequestEvent(list)); } diff --git a/build.gradle b/build.gradle index 43827ff9..01b09609 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.0.10' apply false } group = 'pro.gravit.launcher' -version = '5.2.4' +version = '5.2.5-SNAPSHOT' apply from: 'props.gradle' diff --git a/modules b/modules index fd75e8df..dced1548 160000 --- a/modules +++ b/modules @@ -1 +1 @@ -Subproject commit fd75e8dfa26a3103f03e4e6e6e3eb1c6e6a4ba1a +Subproject commit dced154854c59d8b1e816ebfb1f51baaaeafabfb