Improved token verification
~1h work
This commit is contained in:
parent
86d43f04ce
commit
5a0b03a75f
@ -12,63 +12,94 @@ import java.util.Map;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public final class SessionHandler {
|
public final class SessionHandler {
|
||||||
private static SessionHandler INSTANCE;
|
private static SessionHandler INSTANCE;
|
||||||
private final Map<UUID, UserInfo> Sessions = new HashMap<>();
|
private final Map<UUID, UserInfo> Sessions = new HashMap<>();
|
||||||
|
|
||||||
private SessionHandler() {
|
private SessionHandler() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SessionHandler getInstance() {
|
public static SessionHandler getInstance() {
|
||||||
if (INSTANCE == null) {
|
if (INSTANCE == null) {
|
||||||
INSTANCE = new SessionHandler();
|
INSTANCE = new SessionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID login(UserCredentials userCredentials) throws SQLException {
|
public UUID login(UserCredentials userCredentials) throws SQLException {
|
||||||
val result = DbQuery.builder()
|
val result = DbQuery.builder()
|
||||||
.command(SqlCommand.SELECT)
|
.command(SqlCommand.SELECT)
|
||||||
.table(Table.USERS)
|
.table(Table.USERS)
|
||||||
.column("uuid")
|
.column("uuid")
|
||||||
.column("password")
|
.column("password")
|
||||||
.column("admin")
|
.column("admin")
|
||||||
.condition("username", userCredentials.username())
|
.condition("username", userCredentials.username())
|
||||||
.executeQuery();
|
.executeQuery();
|
||||||
if (result.isEmpty()) {
|
if (result.isEmpty()) {
|
||||||
// user not found
|
// user not found
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
val row1 = result.get(0);
|
val row1 = result.get(0);
|
||||||
if (!row1.get("password").equals(userCredentials.password())) {
|
if (!row1.get("password").equals(userCredentials.password())) {
|
||||||
// wrong password
|
// wrong password
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID uuid = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
this.Sessions.put(uuid, new UserInfo((UUID) row1.get("uuid"), userCredentials.username(), (boolean) row1.get("admin")));
|
this.Sessions.put(uuid, new UserInfo((UUID) row1.get("uuid"), userCredentials.username(), (boolean) row1.get("admin")));
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static UUID uuidFromHttpHeader(String headerValue) {
|
public static UUID uuidFromHttpHeader(String headerValue) {
|
||||||
return headerValue == null ? null : UUID.fromString(headerValue.replaceFirst("^Bearer ", ""));
|
return headerValue == null ? null : UUID.fromString(headerValue.replaceFirst("^Bearer ", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verifyUUID(UUID uuid) {
|
public TokenValidity verifyUUID(UUID uuid) {
|
||||||
return verifyUUID(uuid, false);
|
return verifyUUID(uuid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean verifyUUID(UUID uuid, boolean requireAdmin) {
|
public TokenValidity verifyUUID(UUID uuid, boolean requireAdmin) {
|
||||||
return uuid != null && Sessions.containsKey(uuid) && (!requireAdmin || Sessions.get(uuid).admin());
|
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);
|
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()
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* */
|
||||||
8
src/at/nanopenguin/mtcg/application/TokenValidity.java
Normal file
8
src/at/nanopenguin/mtcg/application/TokenValidity.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package at.nanopenguin.mtcg.application;
|
||||||
|
|
||||||
|
public enum TokenValidity {
|
||||||
|
MISSING,
|
||||||
|
INVALID,
|
||||||
|
FORBIDDEN,
|
||||||
|
VALID
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ package at.nanopenguin.mtcg.application.service;
|
|||||||
|
|
||||||
import at.nanopenguin.mtcg.application.Package;
|
import at.nanopenguin.mtcg.application.Package;
|
||||||
import at.nanopenguin.mtcg.application.SessionHandler;
|
import at.nanopenguin.mtcg.application.SessionHandler;
|
||||||
|
import at.nanopenguin.mtcg.application.TokenValidity;
|
||||||
import at.nanopenguin.mtcg.application.service.schemas.Card;
|
import at.nanopenguin.mtcg.application.service.schemas.Card;
|
||||||
import at.nanopenguin.mtcg.http.HttpMethod;
|
import at.nanopenguin.mtcg.http.HttpMethod;
|
||||||
import at.nanopenguin.mtcg.http.HttpRequest;
|
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.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.val;
|
|
||||||
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -25,18 +24,18 @@ public class PackagesService implements Service {
|
|||||||
try {
|
try {
|
||||||
if (request.getPath().split("/")[1].equals("packages") && request.getMethod() == HttpMethod.POST) {
|
if (request.getPath().split("/")[1].equals("packages") && request.getMethod() == HttpMethod.POST) {
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
if ((uuid = SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization"))) == null)
|
return switch (SessionHandler.getInstance().verifyUUID(SessionHandler.uuidFromHttpHeader(request.getHttpHeader("Authorization")), true)) {
|
||||||
return new Response(HttpStatus.UNAUTHORIZED); // TODO: unauthorized for invalid token
|
case MISSING, INVALID -> new Response(HttpStatus.UNAUTHORIZED);
|
||||||
if (!SessionHandler.getInstance().verifyUUID(uuid, true))
|
case FORBIDDEN -> new Response(HttpStatus.FORBIDDEN);
|
||||||
return new Response(HttpStatus.FORBIDDEN);
|
case VALID -> new Response(
|
||||||
|
Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference<List<Card>>() {})) ?
|
||||||
return new Response(Package.create(new ObjectMapper().readValue(request.getBody(), new TypeReference<List<Card>>() {})) ?
|
HttpStatus.CREATED :
|
||||||
HttpStatus.CREATED :
|
HttpStatus.CONFLICT);
|
||||||
HttpStatus.CONFLICT);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (String.join("/", Arrays.copyOfRange(request.getPath().split("/"), 1, 2)).equals("transactions/packages") && request.getMethod() == HttpMethod.POST) {
|
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.UNAUTHORIZED :
|
||||||
HttpStatus.NOT_IMPLEMENTED);
|
HttpStatus.NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
package at.nanopenguin.mtcg.application.service;
|
package at.nanopenguin.mtcg.application.service;
|
||||||
|
|
||||||
import at.nanopenguin.mtcg.application.SessionHandler;
|
import at.nanopenguin.mtcg.application.SessionHandler;
|
||||||
|
import at.nanopenguin.mtcg.application.TokenValidity;
|
||||||
import at.nanopenguin.mtcg.application.User;
|
import at.nanopenguin.mtcg.application.User;
|
||||||
import at.nanopenguin.mtcg.application.service.schemas.UserCredentials;
|
import at.nanopenguin.mtcg.application.service.schemas.UserCredentials;
|
||||||
import at.nanopenguin.mtcg.application.service.schemas.UserData;
|
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.HttpMethod;
|
||||||
import at.nanopenguin.mtcg.http.HttpRequest;
|
import at.nanopenguin.mtcg.http.HttpRequest;
|
||||||
import at.nanopenguin.mtcg.http.HttpStatus;
|
import at.nanopenguin.mtcg.http.HttpStatus;
|
||||||
@ -35,7 +33,7 @@ public class UserService implements Service {
|
|||||||
return switch (request.getMethod()) {
|
return switch (request.getMethod()) {
|
||||||
case GET -> {
|
case GET -> {
|
||||||
String username = request.getPath().split("/")[2];
|
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);
|
yield new Response(HttpStatus.UNAUTHORIZED);
|
||||||
val userData = User.retrieve(username);
|
val userData = User.retrieve(username);
|
||||||
yield userData != null ?
|
yield userData != null ?
|
||||||
@ -49,7 +47,7 @@ public class UserService implements Service {
|
|||||||
case PUT -> {
|
case PUT -> {
|
||||||
String username = request.getPath().split("/")[2];
|
String username = request.getPath().split("/")[2];
|
||||||
UserData userData = new ObjectMapper().readValue(request.getBody(), UserData.class);
|
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 new Response(HttpStatus.UNAUTHORIZED);
|
||||||
yield User.update(username, userData) ? new Response(HttpStatus.OK) : new Response(HttpStatus.NOT_FOUND);
|
yield User.update(username, userData) ? new Response(HttpStatus.OK) : new Response(HttpStatus.NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user