From 9f4c55e80d451a0c81ac6b7d729636e98ce95fc2 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Sun, 5 Apr 2026 15:42:01 -0300 Subject: [PATCH] Add UserHandler with promote/demote capabilities. Fix bug in ConfigLoad preventing salt and hash fields from being populated when loading users. Convert User to a class. Fix UserService.getUser bug where it would compare the input user string instead of the resulting user against null. --- README.md | 4 +- .../adrianvictor/livingroom/auth/User.java | 33 +++++++++++- .../livingroom/config/AppConfig.java | 1 + .../livingroom/config/ConfigLoader.java | 2 + .../livingroom/http/Handlers.java | 2 + .../adrianvictor/livingroom/http/Server.java | 3 +- .../livingroom/http/handlers/UserHandler.java | 52 +++++++++++++++++++ .../livingroom/services/UserService.java | 2 +- 8 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/main/java/org/adrianvictor/livingroom/http/handlers/UserHandler.java diff --git a/README.md b/README.md index 83a15c7..5797a2e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,9 @@ The server includes a (working but very WIP) HTTP API for third-party clients an - [x] Search - [x] Downloads - [ ] Resumable downloads -- [ ] User Management +- [x] User Management + - [x] Promote/demote users + - [ ] Create/delete users ### Web interface - [x] Authentication diff --git a/src/main/java/org/adrianvictor/livingroom/auth/User.java b/src/main/java/org/adrianvictor/livingroom/auth/User.java index 1c30a28..b63a7d7 100644 --- a/src/main/java/org/adrianvictor/livingroom/auth/User.java +++ b/src/main/java/org/adrianvictor/livingroom/auth/User.java @@ -1,9 +1,40 @@ package org.adrianvictor.livingroom.auth; +import org.adrianvictor.livingroom.config.AppConfig; import org.adrianvictor.livingroom.utils.PasswordUtils; -public record User(String username, String hashedPassword, String salt, Role role) { +public class User { + private final String username; + private final String hashedPassword; + private final String salt; + private Role role; + + public User(String username, String hashedPassword, String salt, Role role) { + this.username = username; + this.hashedPassword = hashedPassword; + this.salt = salt; + this.role = role; + } + public boolean auth(String password) { return PasswordUtils.verifyPassword(password, salt, hashedPassword); } + + public AppConfig.UserConfig toConfig() { + AppConfig.UserConfig config = new AppConfig.UserConfig(); + config.setRole(role.toString().toLowerCase()); + config.setHash(this.hashedPassword); + config.setSalt(this.salt); + return config; + } + + public void setRole(Role role) { + this.role = role; + } + + public String getName() { + return username; + } + + public Role role() { return role; } } diff --git a/src/main/java/org/adrianvictor/livingroom/config/AppConfig.java b/src/main/java/org/adrianvictor/livingroom/config/AppConfig.java index 172f39b..b2686b7 100644 --- a/src/main/java/org/adrianvictor/livingroom/config/AppConfig.java +++ b/src/main/java/org/adrianvictor/livingroom/config/AppConfig.java @@ -1,6 +1,7 @@ package org.adrianvictor.livingroom.config; import org.adrianvictor.livingroom.Logger; +import org.adrianvictor.livingroom.auth.User; import org.adrianvictor.livingroom.utils.PasswordUtils; import org.json.simple.JSONObject; diff --git a/src/main/java/org/adrianvictor/livingroom/config/ConfigLoader.java b/src/main/java/org/adrianvictor/livingroom/config/ConfigLoader.java index e028bf9..faef190 100644 --- a/src/main/java/org/adrianvictor/livingroom/config/ConfigLoader.java +++ b/src/main/java/org/adrianvictor/livingroom/config/ConfigLoader.java @@ -50,6 +50,8 @@ public class ConfigLoader { AppConfig.UserConfig user = new AppConfig.UserConfig(); user.setRole((String) userJson.get("role")); user.setPassword((String) userJson.get("password")); + user.setSalt((String) userJson.get("salt")); + user.setHash((String) userJson.get("hash")); users.put(username, user); } diff --git a/src/main/java/org/adrianvictor/livingroom/http/Handlers.java b/src/main/java/org/adrianvictor/livingroom/http/Handlers.java index ec4a194..67e8a3f 100644 --- a/src/main/java/org/adrianvictor/livingroom/http/Handlers.java +++ b/src/main/java/org/adrianvictor/livingroom/http/Handlers.java @@ -1,6 +1,7 @@ package org.adrianvictor.livingroom.http; import org.adrianvictor.livingroom.http.handlers.*; +import org.adrianvictor.livingroom.http.handlers.UserHandler; import java.util.HashMap; @@ -16,6 +17,7 @@ public class Handlers { map.put(new WebRedirectHandler(), "/"); map.put(new LoginHandler(), "/login"); map.put(new SearchHandler(), "/search"); + map.put(new UserHandler(), "/user"); } public static HashMap getAll() { diff --git a/src/main/java/org/adrianvictor/livingroom/http/Server.java b/src/main/java/org/adrianvictor/livingroom/http/Server.java index 6613d48..815527b 100644 --- a/src/main/java/org/adrianvictor/livingroom/http/Server.java +++ b/src/main/java/org/adrianvictor/livingroom/http/Server.java @@ -73,6 +73,7 @@ public class Server { if (session == null) { try { AuthenticationHelper.sendUnauthorized(exchange, null); + return; } catch (IOException ignored) {} } } @@ -98,7 +99,7 @@ public class Server { } catch (IOException ignored) { } catch (Exception e) { Logger.error("Handler error: " + e.getMessage()); - //e.printStackTrace(); + e.printStackTrace(); try { exchange.sendResponseHeaders(500, 0); exchange.close(); diff --git a/src/main/java/org/adrianvictor/livingroom/http/handlers/UserHandler.java b/src/main/java/org/adrianvictor/livingroom/http/handlers/UserHandler.java new file mode 100644 index 0000000..89005a3 --- /dev/null +++ b/src/main/java/org/adrianvictor/livingroom/http/handlers/UserHandler.java @@ -0,0 +1,52 @@ +package org.adrianvictor.livingroom.http.handlers; + +import com.sun.net.httpserver.HttpExchange; +import org.adrianvictor.livingroom.Main; +import org.adrianvictor.livingroom.auth.Role; +import org.adrianvictor.livingroom.auth.Session; +import org.adrianvictor.livingroom.auth.User; +import org.adrianvictor.livingroom.http.Handler; +import org.adrianvictor.livingroom.http.HttpResponse; +import org.adrianvictor.livingroom.http.utils.AuthenticationHelper; +import org.adrianvictor.livingroom.services.UserService; + +public class UserHandler implements Handler { + @Override + public HttpResponse result(String baseAddress, String path, HttpExchange exchange) { + String[] parts = path.split("/"); + Session session = AuthenticationHelper.getAuthenticatedSession(exchange); + String username = session.getUsername(); + + if (parts.length < 2) { + return HttpResponse.json(400, "Usage: '.../user//'"); + } + if (UserService.getInstance().getUser(username).role() != Role.ADMIN) { + return HttpResponse.json(401, "You are not an administrator."); + } + + User user = UserService.getInstance().getUser(parts[1]); + if (user == null) { + return HttpResponse.json(401, "This user does not exist."); + } + + try { + switch (parts[0]) { + case "promote": + user.setRole(Role.ADMIN); + break; + case "demote": + user.setRole(Role.USER); + break; + default: + return HttpResponse.json(400, "Usage: '.../user//'"); + } + + Main.getConfig().addUser(user.getName(), user.toConfig()); + Main.getConfig().saveConfig(); + + return HttpResponse.json(200, "Success."); + } catch (Exception e) { + return HttpResponse.json(500, ""); + } + } +} diff --git a/src/main/java/org/adrianvictor/livingroom/services/UserService.java b/src/main/java/org/adrianvictor/livingroom/services/UserService.java index f616635..7f92296 100644 --- a/src/main/java/org/adrianvictor/livingroom/services/UserService.java +++ b/src/main/java/org/adrianvictor/livingroom/services/UserService.java @@ -25,7 +25,7 @@ public class UserService { public User getUser(String user) { AppConfig.UserConfig raw = users.get(user); - if (user == null) { + if (raw == null) { return null; }