Improved token verification

~1h work
This commit is contained in:
Benedikt Galbavy 2024-01-05 22:14:52 +01:00
parent 86d43f04ce
commit 5a0b03a75f
4 changed files with 91 additions and 55 deletions

View File

@ -12,63 +12,94 @@ import java.util.Map;
import java.util.UUID;
public final class SessionHandler {
private static SessionHandler INSTANCE;
private final Map<UUID, UserInfo> Sessions = new HashMap<>();
private static SessionHandler INSTANCE;
private final Map<UUID, UserInfo> Sessions = new HashMap<>();
private SessionHandler() {
private SessionHandler() {
}
}
public static SessionHandler getInstance() {
public static SessionHandler getInstance() {
if (INSTANCE == null) {
INSTANCE = new SessionHandler();
}
return INSTANCE;
}
}
public UUID login(UserCredentials userCredentials) throws SQLException {
val result = DbQuery.builder()
.command(SqlCommand.SELECT)
.table(Table.USERS)
.column("uuid")
.column("password")
.column("admin")
.condition("username", userCredentials.username())
.executeQuery();
if (result.isEmpty()) {
// user not found
return null;
}
public UUID login(UserCredentials userCredentials) throws SQLException {
val result = DbQuery.builder()
.command(SqlCommand.SELECT)
.table(Table.USERS)
.column("uuid")
.column("password")
.column("admin")
.condition("username", userCredentials.username())
.executeQuery();
if (result.isEmpty()) {
// user not found
return null;
}
val row1 = result.get(0);
if (!row1.get("password").equals(userCredentials.password())) {
// wrong password
return null;
}
val row1 = result.get(0);
if (!row1.get("password").equals(userCredentials.password())) {
// wrong password
return null;
}
UUID uuid = UUID.randomUUID();
this.Sessions.put(uuid, new UserInfo((UUID) row1.get("uuid"), userCredentials.username(), (boolean) row1.get("admin")));
return uuid;
}
UUID uuid = UUID.randomUUID();
this.Sessions.put(uuid, new UserInfo((UUID) row1.get("uuid"), userCredentials.username(), (boolean) row1.get("admin")));
return uuid;
}
public static UUID uuidFromHttpHeader(String headerValue) {
return headerValue == null ? null : UUID.fromString(headerValue.replaceFirst("^Bearer ", ""));
}
public static UUID uuidFromHttpHeader(String headerValue) {
return headerValue == null ? null : UUID.fromString(headerValue.replaceFirst("^Bearer ", ""));
}
public boolean verifyUUID(UUID uuid) {
public TokenValidity verifyUUID(UUID uuid) {
return verifyUUID(uuid, false);
}
public boolean verifyUUID(UUID uuid, boolean requireAdmin) {
return uuid != null && Sessions.containsKey(uuid) && (!requireAdmin || Sessions.get(uuid).admin());
}
public TokenValidity verifyUUID(UUID uuid, boolean requireAdmin) {
if (uuid == null) return TokenValidity.MISSING;
if (!Sessions.containsKey(uuid)) return TokenValidity.INVALID;
if (Sessions.get(uuid).admin() || !requireAdmin) return TokenValidity.VALID;
return TokenValidity.FORBIDDEN;
}
public boolean verifyUUID(UUID uuid, String username) {
public TokenValidity verifyUUID(UUID uuid, String username) {
return verifyUUID(uuid, username, false);
}
}
public TokenValidity verifyUUID(UUID uuid, String username, boolean allowAdmin) {
if (uuid == null) return TokenValidity.MISSING;
if (!Sessions.containsKey(uuid)) return TokenValidity.INVALID;
if (username.equals(Sessions.get(uuid).username())) return TokenValidity.VALID;
if (allowAdmin && Sessions.get(uuid).admin()) return TokenValidity.VALID;
return TokenValidity.FORBIDDEN;
public boolean verifyUUID(UUID uuid, String username, boolean allowAdmin) {
return uuid != null && Sessions.containsKey(uuid) && (username.equals(Sessions.get(uuid).username()) || (allowAdmin && Sessions.get(uuid).admin()));
}
}
/*
*
* join() {
* lock()
* checks if someone waiting
* no -> {
* var waiting = true // sets self as waiting idk
* unlock()
* wait()
* response = battle log
* // process stuff
* unlock()
* }
* yes -> {
* start battle
* var battle log = battle log // no na ned
* // process stuff
* notifyAll()
* }
* }
*
* */

View File

@ -0,0 +1,8 @@
package at.nanopenguin.mtcg.application;
public enum TokenValidity {
MISSING,
INVALID,
FORBIDDEN,
VALID
}

View File

@ -2,6 +2,7 @@ package at.nanopenguin.mtcg.application.service;
import at.nanopenguin.mtcg.application.Package;
import at.nanopenguin.mtcg.application.SessionHandler;
import at.nanopenguin.mtcg.application.TokenValidity;
import at.nanopenguin.mtcg.application.service.schemas.Card;
import at.nanopenguin.mtcg.http.HttpMethod;
import at.nanopenguin.mtcg.http.HttpRequest;
@ -10,10 +11,8 @@ import at.nanopenguin.mtcg.http.Response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.val;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
@ -25,18 +24,18 @@ public class PackagesService implements Service {
try {
if (request.getPath().split("/")[1].equals("packages") && request.getMethod() == HttpMethod.POST) {
UUID uuid;
if ((uuid = SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) == null)
return new Response(HttpStatus.UNAUTHORIZED); // TODO: unauthorized for invalid token
if (!SessionHandler.getInstance().verifyUUID(uuid, true))
return new Response(HttpStatus.FORBIDDEN);
return new Response(Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference<List<Card>>() {})) ?
HttpStatus.CREATED :
HttpStatus.CONFLICT);
return switch (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), true)) {
case MISSING, INVALID -> new Response(HttpStatus.UNAUTHORIZED);
case FORBIDDEN -> new Response(HttpStatus.FORBIDDEN);
case VALID -> new Response(
Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference<List<Card>>() {})) ?
HttpStatus.CREATED :
HttpStatus.CONFLICT);
};
}
if (String.join("/", Arrays.copyOfRange(request.getPath().split("/"), 1, 2)).equals("transactions/packages") && request.getMethod() == HttpMethod.POST) {
return new Response(!SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) ?
return new Response(SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) != TokenValidity.VALID ?
HttpStatus.UNAUTHORIZED :
HttpStatus.NOT_IMPLEMENTED);
}

View File

@ -1,12 +1,10 @@
package at.nanopenguin.mtcg.application.service;
import at.nanopenguin.mtcg.application.SessionHandler;
import at.nanopenguin.mtcg.application.TokenValidity;
import at.nanopenguin.mtcg.application.User;
import at.nanopenguin.mtcg.application.service.schemas.UserCredentials;
import at.nanopenguin.mtcg.application.service.schemas.UserData;
import at.nanopenguin.mtcg.db.DbQuery;
import at.nanopenguin.mtcg.db.SqlCommand;
import at.nanopenguin.mtcg.db.Table;
import at.nanopenguin.mtcg.http.HttpMethod;
import at.nanopenguin.mtcg.http.HttpRequest;
import at.nanopenguin.mtcg.http.HttpStatus;
@ -35,7 +33,7 @@ public class UserService implements Service {
return switch (request.getMethod()) {
case GET -> {
String username = request.getPath().split("/")[2];
if (!SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true))
if (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.VALID)
yield new Response(HttpStatus.UNAUTHORIZED);
val userData = User.retrieve(username);
yield userData != null ?
@ -49,7 +47,7 @@ public class UserService implements Service {
case PUT -> {
String username = request.getPath().split("/")[2];
UserData userData = new ObjectMapper().readValue(request.getBody(), UserData.class);
if (!SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true))
if (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), username, true) != TokenValidity.FORBIDDEN)
yield new Response(HttpStatus.UNAUTHORIZED);
yield User.update(username, userData) ? new Response(HttpStatus.OK) : new Response(HttpStatus.NOT_FOUND);
}