From d7a6eb760ee47a6d3df2925b78a9b26a25b1be97 Mon Sep 17 00:00:00 2001 From: Sashegdev Date: Thu, 9 Apr 2026 18:03:00 +0000 Subject: [PATCH] fixes --- .../zernmc/launcher/auth/AuthManager.java | 70 +++++++++++-------- .../zernmc/launcher/menu/LaunchMenu.java | 11 ++- .../launcher/minecraft/PackDownloader.java | 27 +++---- server/main.py | 2 +- 4 files changed, 58 insertions(+), 52 deletions(-) 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 5e732ae..508ea7d 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 @@ -28,13 +28,18 @@ public class AuthManager { 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; try { @@ -43,12 +48,11 @@ public class AuthManager { if (loaded == null || loaded.accessToken == null) return false; session = loaded; - - // Получаем информацию о пользователе + if (session.username != null) { userInfo = fetchUserInfo(); } - + if (isAccessTokenExpired()) { return tryRefresh(); } @@ -71,9 +75,9 @@ public class AuthManager { JsonObject body = new JsonObject(); body.addProperty("username", username); body.addProperty("password", password); - + String jsonBody = GSON.toJson(body); - + HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(ZHttpClient.getBaseUrl() + endpoint)) .timeout(Duration.ofSeconds(15)) @@ -84,15 +88,14 @@ public class AuthManager { .build(); HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); - + if (response.statusCode() == 200) { session = GSON.fromJson(response.body(), AuthSession.class); session.expiresAt = System.currentTimeMillis() / 1000L + session.expiresIn; saveSession(); - - // Получаем информацию о пользователе + userInfo = fetchUserInfo(); - + return AuthResult.ok(); } else { String error = extractError(response.body()); @@ -108,14 +111,14 @@ public class AuthManager { 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()); } catch (Exception ignored) {} } @@ -144,20 +147,29 @@ public class AuthManager { return session != null ? session.roleName : "Игрок"; } + // === Основные проверки === public static boolean hasPass() { + if (userInfo != null) return userInfo.has_pass; return getRole() >= ROLE_PASS_HOLDER; } - public static boolean isModerator() { - return getRole() >= ROLE_MODERATOR; + 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 isElder() { - return getRole() >= ROLE_ELDER; + public static boolean canViewPacks() { + return hasPermission(PERM_VIEW_PACKS); } - public static boolean isCreator() { - return getRole() == ROLE_CREATOR; + public static boolean canDownloadPacks() { + return hasPermission(PERM_DOWNLOAD_PACK); } public static String getAccessToken() { @@ -170,7 +182,7 @@ public class AuthManager { private static UserInfo fetchUserInfo() { if (!isLoggedIn()) return null; - + try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(ZHttpClient.getBaseUrl() + "/admin/me")) @@ -181,7 +193,7 @@ public class AuthManager { .build(); HttpResponse response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()); - + if (response.statusCode() == 200) { return GSON.fromJson(response.body(), UserInfo.class); } @@ -198,11 +210,11 @@ 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); - + HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(ZHttpClient.getBaseUrl() + "/auth/refresh")) .timeout(Duration.ofSeconds(15)) @@ -211,19 +223,20 @@ public class AuthManager { .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; saveSession(); + userInfo = fetchUserInfo(); // обновляем информацию после рефреша return true; } } catch (Exception ignored) {} - + session = null; userInfo = null; try { Files.deleteIfExists(AUTH_FILE); } catch (Exception ignored) {} @@ -242,7 +255,6 @@ public class AuthManager { 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() @@ -254,12 +266,10 @@ public class AuthManager { return json.get("error").getAsString(); } } catch (Exception ignored) {} - return body.length() > 200 ? body.substring(0, 200) + "..." : body; } // ====================== ВНУТРЕННИЕ КЛАССЫ ====================== - public static class AuthSession { @SerializedName("access_token") public String accessToken; @SerializedName("refresh_token") public String refreshToken; @@ -281,6 +291,10 @@ public class AuthManager { public Long last_login; public boolean has_pass; public List permissions; + + public boolean hasPermission(String permission) { + return permissions != null && permissions.contains(permission); + } } public static class AuthResult { diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LaunchMenu.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LaunchMenu.java index 3c03dad..b6c1a1b 100644 --- a/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LaunchMenu.java +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LaunchMenu.java @@ -81,13 +81,10 @@ public class LaunchMenu { } private void installServerPack() throws Exception { - // Проверяем наличие проходки - if (!AuthManager.hasPass()) { + if (!AuthManager.canDownloadPacks()) { ConsoleUtils.clearScreen(); - System.out.println(ZAnsi.brightRed("У вас нет активной проходки!")); - System.out.println(ZAnsi.white("Чтобы скачивать сборки с сервера ZernMC, необходимо активировать проходку.")); - System.out.println(); - System.out.println(ZAnsi.white("Обратитесь к администратору для получения проходки.")); + System.out.println(ZAnsi.brightRed("У вас нет права на скачивание сборок!")); + System.out.println(ZAnsi.white("Для доступа к серверным сборкам необходима активная проходка.")); ConsoleUtils.pause(); return; } @@ -514,7 +511,7 @@ public class LaunchMenu { } private void launchExistingInstance(Instance instance) { - if (instance.isServerPack() && !AuthManager.hasPass()) { + if (instance.isServerPack() && !AuthManager.canDownloadPacks()) { ConsoleUtils.clearScreen(); System.out.println(ZAnsi.brightRed("Для запуска серверной сборки требуется активная проходка!")); ConsoleUtils.pause(); diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/minecraft/PackDownloader.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/minecraft/PackDownloader.java index 4118207..9483000 100644 --- a/launcher/src/main/java/me/sashegdev/zernmc/launcher/minecraft/PackDownloader.java +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/minecraft/PackDownloader.java @@ -43,6 +43,9 @@ public class PackDownloader { if (accessToken == null) { throw new IOException("Не авторизован. Требуется проходка для просмотра сборок."); } + if (!AuthManager.canViewPacks()) { + throw new IOException("Для просмотра сборок требуется активная проходка"); + } // Используем HttpURLConnection для GET с авторизацией java.net.HttpURLConnection connection = null; @@ -90,11 +93,11 @@ public class PackDownloader { for (JsonElement elem : packsArray) { JsonObject pack = elem.getAsJsonObject(); - + if (pack.has("error") || (pack.has("status") && "not_scanned".equals(pack.get("status").getAsString()))) { continue; } - + try { String name = pack.get("name").getAsString(); int version = pack.has("version") ? pack.get("version").getAsInt() : 0; @@ -103,7 +106,7 @@ public class PackDownloader { String loaderVersion = pack.has("loader_version") && !pack.get("loader_version").isJsonNull() ? pack.get("loader_version").getAsString() : ""; int filesCount = pack.has("files_count") ? pack.get("files_count").getAsInt() : 0; - + LocalDateTime updatedAt = null; if (pack.has("updated_at") && !pack.get("updated_at").isJsonNull()) { try { @@ -111,14 +114,14 @@ public class PackDownloader { DateTimeFormatter.ISO_DATE_TIME); } catch (Exception ignored) {} } - + result.add(new ServerPack(name, version, minecraftVersion, loaderType, loaderVersion, updatedAt, filesCount)); } catch (Exception e) { System.err.println("Ошибка парсинга пака: " + e.getMessage()); } } - + return result; } @@ -313,6 +316,9 @@ public class PackDownloader { if (accessToken == null) { throw new IOException("Не авторизован. Требуется проходка для скачивания сборок."); } + if (!AuthManager.canDownloadPacks()) { + throw new IOException("Для скачивания сборок требуется активная проходка"); + } String url = ZHttpClient.getBaseUrl() + "/pack/" + packName + "/diff"; @@ -508,17 +514,6 @@ public class PackDownloader { return sb.toString(); } - /** - * Парсинг даты из строки - */ - private LocalDateTime parseDateTime(String dateTimeStr) { - try { - return LocalDateTime.parse(dateTimeStr, DATE_FORMATTER); - } catch (Exception e) { - return null; - } - } - // ====================== Вложенные классы ====================== public static class PackManifest { diff --git a/server/main.py b/server/main.py index 1c98253..6bc4a10 100644 --- a/server/main.py +++ b/server/main.py @@ -25,7 +25,7 @@ import base64 from fastapi.responses import StreamingResponse from auth import get_current_user, router as auth_router, init_db, verify_jwt -from server.roles import Permissions, has_permission +from roles import Permissions, has_permission logger = structlog.get_logger(__name__)