diff --git a/launcher/pom.xml b/launcher/pom.xml index a08d5b7..1e2bee9 100644 --- a/launcher/pom.xml +++ b/launcher/pom.xml @@ -13,6 +13,9 @@ 21 21 UTF-8 + ZernMC + 2026 + ZernMC Launcher - just a minimalistic launcher by SashegDev me.sashegdev.zernmc.launcher.Main diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/auth/AuthManager.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/auth/AuthManager.java index 508ea7d..b88c960 100644 --- a/launcher/src/main/java/me/sashegdev/zernmc/launcher/auth/AuthManager.java +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/auth/AuthManager.java @@ -1,44 +1,40 @@ package me.sashegdev.zernmc.launcher.auth; -import com.google.gson.*; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.google.gson.annotations.SerializedName; import me.sashegdev.zernmc.launcher.utils.Config; import me.sashegdev.zernmc.launcher.utils.ZAnsi; import me.sashegdev.zernmc.launcher.utils.ZHttpClient; import java.io.IOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; +import java.net.HttpURLConnection; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.time.Duration; import java.util.List; public class AuthManager { private static final Path AUTH_FILE = Config.getConfigDir().resolve("auth.json"); private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(15)) - .build(); private static volatile AuthSession session = null; private static volatile UserInfo userInfo = null; - // === Роли (для совместимости) === + // === Роли === public static final int ROLE_USER = 0; public static final int ROLE_PASS_HOLDER = 1; public static final int ROLE_MODERATOR = 2; public static final int ROLE_ELDER = 3; public static final int ROLE_CREATOR = 4; - // === Права доступа (синхронизировано с сервером) === + // === Права доступа === public static final String PERM_VIEW_PACKS = "view_packs"; public static final String PERM_DOWNLOAD_PACK = "download_pack"; - public static final String PERM_REQUEST_PASS = "request_pass"; public static boolean loadSavedSession() { if (!Files.exists(AUTH_FILE)) return false; @@ -48,10 +44,7 @@ public class AuthManager { if (loaded == null || loaded.accessToken == null) return false; session = loaded; - - if (session.username != null) { - userInfo = fetchUserInfo(); - } + userInfo = fetchUserInfo(); if (isAccessTokenExpired()) { return tryRefresh(); @@ -62,6 +55,7 @@ public class AuthManager { } } + // ====================== АВТОРИЗАЦИЯ ====================== public static AuthResult login(String username, String password) { return authRequest("/auth/login", username, password); } @@ -72,36 +66,22 @@ public class AuthManager { private static AuthResult authRequest(String endpoint, String username, String password) { try { - JsonObject body = new JsonObject(); - body.addProperty("username", username); - body.addProperty("password", password); + String body = GSON.toJson(new LoginRequest(username, password)); + SimpleHttpResponse resp = post(endpoint, body); - String jsonBody = GSON.toJson(body); - - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(ZHttpClient.getBaseUrl() + endpoint)) - .timeout(Duration.ofSeconds(15)) - .header("Content-Type", "application/json") - .header("Accept", "application/json") - .header("User-Agent", "ZernMC-Launcher/1.0") - .POST(HttpRequest.BodyPublishers.ofString(jsonBody, StandardCharsets.UTF_8)) - .build(); - - HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); - - if (response.statusCode() == 200) { - session = GSON.fromJson(response.body(), AuthSession.class); + if (resp.statusCode() == 200) { + session = GSON.fromJson(resp.body(), AuthSession.class); session.expiresAt = System.currentTimeMillis() / 1000L + session.expiresIn; saveSession(); - userInfo = fetchUserInfo(); - return AuthResult.ok(); + } else if (resp.statusCode() == 422) { + return AuthResult.fail("Ошибка валидации: " + extractError(resp.body())); } else { - String error = extractError(response.body()); - return AuthResult.fail(error); + return AuthResult.fail(extractError(resp.body())); } } catch (Exception e) { + e.printStackTrace(); return AuthResult.fail("Ошибка соединения: " + e.getMessage()); } } @@ -109,17 +89,7 @@ public class AuthManager { public static void logout() { if (session != null && session.refreshToken != null) { try { - JsonObject body = new JsonObject(); - body.addProperty("refresh_token", session.refreshToken); - - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(ZHttpClient.getBaseUrl() + "/auth/logout")) - .timeout(Duration.ofSeconds(10)) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(GSON.toJson(body))) - .build(); - - HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); + post("/auth/logout", "{\"refresh_token\":\"" + session.refreshToken + "\"}"); } catch (Exception ignored) {} } session = null; @@ -139,68 +109,12 @@ public class AuthManager { return session != null ? session.uuid : "00000000-0000-0000-0000-000000000000"; } - public static int getRole() { - return session != null ? session.role : ROLE_USER; - } - - public static String getRoleName() { - return session != null ? session.roleName : "Игрок"; - } - - // === Основные проверки === - public static boolean hasPass() { - if (userInfo != null) return userInfo.has_pass; - return getRole() >= ROLE_PASS_HOLDER; - } - - public static boolean hasPermission(String permission) { - if (userInfo != null && userInfo.permissions != null) { - return userInfo.permissions.contains(permission); - } - // Fallback на старую систему - if (PERM_VIEW_PACKS.equals(permission) || PERM_DOWNLOAD_PACK.equals(permission)) { - return hasPass(); - } - return false; - } - - public static boolean canViewPacks() { - return hasPermission(PERM_VIEW_PACKS); - } - - public static boolean canDownloadPacks() { - return hasPermission(PERM_DOWNLOAD_PACK); - } - public static String getAccessToken() { - if (session == null) return null; + if (session == null) return "0"; if (isAccessTokenExpired()) { tryRefresh(); } - return session != null ? session.accessToken : null; - } - - private static UserInfo fetchUserInfo() { - if (!isLoggedIn()) return null; - - try { - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(ZHttpClient.getBaseUrl() + "/admin/me")) - .timeout(Duration.ofSeconds(10)) - .header("Authorization", "Bearer " + session.accessToken) - .header("Accept", "application/json") - .GET() - .build(); - - HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); - - if (response.statusCode() == 200) { - return GSON.fromJson(response.body(), UserInfo.class); - } - } catch (Exception e) { - System.err.println("Не удалось получить информацию о пользователе: " + e.getMessage()); - } - return null; + return session != null && session.accessToken != null ? session.accessToken : "0"; } private static boolean isAccessTokenExpired() { @@ -210,33 +124,19 @@ public class AuthManager { private static boolean tryRefresh() { if (session == null || session.refreshToken == null) return false; - try { - JsonObject body = new JsonObject(); - body.addProperty("refresh_token", session.refreshToken); + String body = "{\"refresh_token\":\"" + session.refreshToken + "\"}"; + SimpleHttpResponse resp = post("/auth/refresh", body); - HttpRequest request = HttpRequest.newBuilder() - .uri(URI.create(ZHttpClient.getBaseUrl() + "/auth/refresh")) - .timeout(Duration.ofSeconds(15)) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(GSON.toJson(body))) - .build(); - - HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); - - if (response.statusCode() == 200) { - JsonObject json = JsonParser.parseString(response.body()).getAsJsonObject(); - String newAccessToken = json.get("access_token").getAsString(); - int expiresIn = json.get("expires_in").getAsInt(); - - session.accessToken = newAccessToken; - session.expiresAt = System.currentTimeMillis() / 1000L + expiresIn; + if (resp.statusCode() == 200) { + AuthSession newSession = GSON.fromJson(resp.body(), AuthSession.class); + newSession.expiresAt = System.currentTimeMillis() / 1000L + newSession.expiresIn; + session = newSession; + userInfo = fetchUserInfo(); saveSession(); - userInfo = fetchUserInfo(); // обновляем информацию после рефреша return true; } } catch (Exception ignored) {} - session = null; userInfo = null; try { Files.deleteIfExists(AUTH_FILE); } catch (Exception ignored) {} @@ -252,19 +152,122 @@ public class AuthManager { } } + // ==================== ПОЛУЧЕНИЕ ИНФОРМАЦИИ О ПОЛЬЗОВАТЕЛЕ ==================== + private static UserInfo fetchUserInfo() { + if (!isLoggedIn() || session.accessToken == null) return null; + + try { + // Используем существующий метод ZHttpClient.get() + вручную добавляем токен + java.net.HttpURLConnection conn = null; + try { + URL url = new URL(ZHttpClient.getBaseUrl() + "/admin/me"); + conn = (java.net.HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("Authorization", "Bearer " + session.accessToken); + conn.setConnectTimeout(10000); + conn.setReadTimeout(10000); + + int responseCode = conn.getResponseCode(); + if (responseCode != 200) return null; + + StringBuilder response = new StringBuilder(); + try (var reader = new java.io.BufferedReader( + new java.io.InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { + String line; + while ((line = reader.readLine()) != null) { + response.append(line); + } + } + return GSON.fromJson(response.toString(), UserInfo.class); + } finally { + if (conn != null) conn.disconnect(); + } + } catch (Exception e) { + System.err.println("Не удалось получить UserInfo: " + e.getMessage()); + return null; + } + } + + // ==================== ПРОВЕРКИ ПРАВ ==================== + public static boolean hasPass() { + if (userInfo != null) return userInfo.has_pass; + return getRole() >= ROLE_PASS_HOLDER; + } + + public static boolean canViewPacks() { + if (userInfo != null && userInfo.permissions != null) { + return userInfo.permissions.contains(PERM_VIEW_PACKS); + } + return hasPass(); // fallback для старых аккаунтов + } + + public static boolean canDownloadPacks() { + if (userInfo != null && userInfo.permissions != null) { + return userInfo.permissions.contains(PERM_DOWNLOAD_PACK); + } + return hasPass(); // fallback + } + + public static int getRole() { + return session != null ? session.role : ROLE_USER; + } + + // ====================== POST ====================== + private static SimpleHttpResponse post(String endpoint, String jsonBody) throws Exception { + String fullUrl = ZHttpClient.getBaseUrl() + endpoint; + HttpURLConnection conn = null; + + try { + URL url = new URL(fullUrl); + conn = (HttpURLConnection) url.openConnection(); + + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json; charset=utf-8"); + conn.setRequestProperty("Accept", "application/json"); + conn.setRequestProperty("User-Agent", "ZernMC-Launcher/1.0"); + conn.setRequestProperty("Connection", "close"); + + if (session != null && session.accessToken != null) { + conn.setRequestProperty("Authorization", "Bearer " + session.accessToken); + } + + conn.setDoOutput(true); + conn.setConnectTimeout(15000); + conn.setReadTimeout(15000); + + byte[] input = jsonBody.getBytes(StandardCharsets.UTF_8); + conn.setFixedLengthStreamingMode(input.length); + + try (var os = conn.getOutputStream()) { + os.write(input); + os.flush(); + } + + int statusCode = conn.getResponseCode(); + var is = (statusCode >= 200 && statusCode < 300) ? conn.getInputStream() : conn.getErrorStream(); + + String responseBody; + try (var scanner = new java.util.Scanner(is, StandardCharsets.UTF_8.name())) { + responseBody = scanner.useDelimiter("\\A").hasNext() ? scanner.next() : ""; + } + + return new SimpleHttpResponse(statusCode, responseBody); + + } finally { + if (conn != null) conn.disconnect(); + } + } + private static String extractError(String body) { try { JsonObject json = JsonParser.parseString(body).getAsJsonObject(); if (json.has("detail")) { if (json.get("detail").isJsonArray()) { - return json.getAsJsonArray("detail").get(0).getAsJsonObject() - .get("msg").getAsString(); + return json.getAsJsonArray("detail").get(0).getAsJsonObject().get("msg").getAsString(); } return json.get("detail").getAsString(); } - if (json.has("error")) { - return json.get("error").getAsString(); - } } catch (Exception ignored) {} return body.length() > 200 ? body.substring(0, 200) + "..." : body; } @@ -278,7 +281,6 @@ public class AuthManager { public String username; public String uuid; public int role; - @SerializedName("role_name") public String roleName; } public static class UserInfo { @@ -287,13 +289,20 @@ public class AuthManager { public String uuid; public int role; public String role_name; - public long created_at; - public Long last_login; public boolean has_pass; public List permissions; - public boolean hasPermission(String permission) { - return permissions != null && permissions.contains(permission); + public boolean hasPermission(String perm) { + return permissions != null && permissions.contains(perm); + } + } + + private static class LoginRequest { + final String username; + final String password; + LoginRequest(String u, String p) { + this.username = u; + this.password = p; } } @@ -304,4 +313,18 @@ public class AuthManager { public static AuthResult ok() { return new AuthResult(true, null); } public static AuthResult fail(String msg) { return new AuthResult(false, msg); } } +} + +// ====================== ВСПОМОГАТЕЛЬНЫЙ КЛАСС ====================== +class SimpleHttpResponse { + final int statusCode; + final String body; + + SimpleHttpResponse(int statusCode, String body) { + this.statusCode = statusCode; + this.body = body; + } + + int statusCode() { return statusCode; } + String body() { return body; } } \ No newline at end of file