From 9b22c7909887151af6c02424b48a2f13638b5b3f Mon Sep 17 00:00:00 2001 From: MathNerd28 Date: Sun, 10 Mar 2024 15:03:55 -0400 Subject: [PATCH] Trim usernames globally; improve 'duplicate' error message --- .../controller/SessionController.java | 37 +++++++++++-------- .../controller/UserController.java | 30 +++++++++++---- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/victorrobotics/devilscoutserver/controller/SessionController.java b/src/main/java/org/victorrobotics/devilscoutserver/controller/SessionController.java index 0460129..0840eec 100644 --- a/src/main/java/org/victorrobotics/devilscoutserver/controller/SessionController.java +++ b/src/main/java/org/victorrobotics/devilscoutserver/controller/SessionController.java @@ -43,19 +43,16 @@ private SessionController() {} public static void login(Context ctx) throws SQLException { LoginRequest request = jsonDecode(ctx, LoginRequest.class); int teamNum = request.team(); - String username = request.username() - .trim(); - - byte[] salt = userDB().getSalt(teamNum, username); + byte[] salt = userDB().getSalt(teamNum, request.username()); if (salt == null) { - throw userNotFound(teamNum, username); + throw userNotFound(teamNum, request.username()); } byte[] nonce = new byte[16]; SECURE_RANDOM.nextBytes(nonce); System.arraycopy(request.clientNonce(), 0, nonce, 0, 8); - String nonceId = username + "@" + teamNum + ":" + base64Encode(nonce); + String nonceId = request.username() + "@" + teamNum + ":" + base64Encode(nonce); NONCES.add(nonceId); ctx.json(new LoginChallenge(salt, nonce)); @@ -78,13 +75,11 @@ public static void login(Context ctx) throws SQLException { public static void auth(Context ctx) throws NoSuchAlgorithmException, InvalidKeyException, SQLException { AuthRequest request = jsonDecode(ctx, AuthRequest.class); - String username = request.username() - .trim(); int teamNum = request.team(); - User user = userDB().getUser(teamNum, username); + User user = userDB().getUser(teamNum, request.username()); if (user == null) { - throw userNotFound(teamNum, username); + throw userNotFound(teamNum, request.username()); } Team team = teamDB().getTeam(teamNum); @@ -92,21 +87,21 @@ public static void auth(Context ctx) throw teamNotFound(teamNum); } - String nonceId = username + "@" + teamNum + ":" + base64Encode(request.nonce()); + String nonceId = request.username() + "@" + teamNum + ":" + base64Encode(request.nonce()); if (!NONCES.contains(nonceId)) { - throw incorrectCredentials(teamNum, username); + throw incorrectCredentials(teamNum, request.username()); } MessageDigest hashFunction = MessageDigest.getInstance(HASH_ALGORITHM); Mac hmacFunction = Mac.getInstance(MAC_ALGORITHM); hmacFunction.init(new SecretKeySpec(user.storedKey(), MAC_ALGORITHM)); - byte[] userAndNonce = combine(teamNum + username, request.nonce()); + byte[] userAndNonce = combine(teamNum + request.username(), request.nonce()); byte[] clientSignature = hmacFunction.doFinal(userAndNonce); byte[] clientKey = xor(request.clientProof(), clientSignature); byte[] storedKey = hashFunction.digest(clientKey); if (!MessageDigest.isEqual(user.storedKey(), storedKey)) { - throw incorrectCredentials(teamNum, username); + throw incorrectCredentials(teamNum, request.username()); } hmacFunction.init(new SecretKeySpec(user.serverKey(), MAC_ALGORITHM)); @@ -166,7 +161,12 @@ private static byte[] combine(String username, byte[] nonce) { public record LoginRequest(@JsonProperty(required = true) int team, @JsonProperty(required = true) String username, - @JsonProperty(required = true) byte[] clientNonce) {} + @JsonProperty(required = true) byte[] clientNonce) { + @Override + public String username() { + return username.trim(); + } + } public record LoginChallenge(byte[] salt, byte[] nonce) {} @@ -174,7 +174,12 @@ public record LoginChallenge(byte[] salt, public record AuthRequest(@JsonProperty(required = true) String username, @JsonProperty(required = true) int team, @JsonProperty(required = true) byte[] nonce, - @JsonProperty(required = true) byte[] clientProof) {} + @JsonProperty(required = true) byte[] clientProof) { + @Override + public String username() { + return username.trim(); + } + } public record AuthResponse(User user, Team team, diff --git a/src/main/java/org/victorrobotics/devilscoutserver/controller/UserController.java b/src/main/java/org/victorrobotics/devilscoutserver/controller/UserController.java index 83e3748..515340c 100644 --- a/src/main/java/org/victorrobotics/devilscoutserver/controller/UserController.java +++ b/src/main/java/org/victorrobotics/devilscoutserver/controller/UserController.java @@ -57,9 +57,9 @@ public static void registerUser(Context ctx) throws SQLException { } UserRegistration registration = jsonDecode(ctx, UserRegistration.class); - String username = registration.username().trim(); - if (userDB().getUser(teamNum, username) != null) { - throw userConflict(teamNum, username); + User duplicate = userDB().getUser(teamNum, registration.username()); + if (duplicate != null) { + throw userConflict(teamNum, duplicate.username()); } byte[][] auth = computeAuthentication(registration.password()); @@ -67,7 +67,7 @@ public static void registerUser(Context ctx) throws SQLException { byte[] storedKey = auth[1]; byte[] serverKey = auth[2]; - User user = userDB().registerUser(teamNum, username, registration.fullName(), + User user = userDB().registerUser(teamNum, registration.username(), registration.fullName(), registration.admin(), salt, storedKey, serverKey); ctx.json(user); ctx.status(HttpStatus.CREATED); @@ -176,8 +176,12 @@ public static void editUser(Context ctx) throws SQLException { session.verifyAdmin(); } - if (edits.username() != null && userDB().getUser(user.team(), edits.username()) != null) { - throw userConflict(user.team(), edits.username()); + if (edits.username() != null && !edits.username() + .equals(user.username())) { + User duplicate = userDB().getUser(user.team(), edits.username()); + if (duplicate != null) { + throw userConflict(user.team(), duplicate.username()); + } } byte[][] authInfo = null; @@ -258,10 +262,20 @@ private static byte[][] computeAuthentication(String password) { record UserRegistration(@JsonProperty(required = true) String username, @JsonProperty(required = true) String fullName, @JsonProperty(required = true) boolean admin, - @JsonProperty(required = true) String password) {} + @JsonProperty(required = true) String password) { + @Override + public String username() { + return username.trim(); + } + } record UserEdits(String username, String fullName, Boolean admin, - String password) {} + String password) { + @Override + public String username() { + return username.trim(); + } + } }