TD-69: Create Login Endpoint and Store Login Session
All checks were successful
Quality Check / Validate OAS (push) Successful in 31s
Quality Check / Linting (push) Successful in 58s
Quality Check / Testing (push) Successful in 57s
Quality Check / Static Analysis (push) Successful in 1m0s
Quality Check / Validate OAS (pull_request) Successful in 30s
Quality Check / Linting (pull_request) Successful in 59s
Quality Check / Testing (pull_request) Successful in 59s
Quality Check / Static Analysis (pull_request) Successful in 1m17s
All checks were successful
Quality Check / Validate OAS (push) Successful in 31s
Quality Check / Linting (push) Successful in 58s
Quality Check / Testing (push) Successful in 57s
Quality Check / Static Analysis (push) Successful in 1m0s
Quality Check / Validate OAS (pull_request) Successful in 30s
Quality Check / Linting (pull_request) Successful in 59s
Quality Check / Testing (pull_request) Successful in 59s
Quality Check / Static Analysis (pull_request) Successful in 1m17s
This commit is contained in:
parent
601d814b97
commit
11d3b8721f
3 changed files with 144 additions and 0 deletions
52
api/api.yml
52
api/api.yml
|
@ -39,6 +39,34 @@ components:
|
||||||
- username
|
- username
|
||||||
- password
|
- password
|
||||||
#############################################
|
#############################################
|
||||||
|
# PlayerLoginData #
|
||||||
|
#############################################
|
||||||
|
PlayerLoginData:
|
||||||
|
description: Data needed to log a Player in
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- username
|
||||||
|
- password
|
||||||
|
#############################################
|
||||||
|
# PlayerLoginSession #
|
||||||
|
#############################################
|
||||||
|
PlayerLoginSession:
|
||||||
|
description: Data needed to log a Player in
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- username
|
||||||
|
- token
|
||||||
|
#############################################
|
||||||
# AdminAuthInfo #
|
# AdminAuthInfo #
|
||||||
#############################################
|
#############################################
|
||||||
ServerHealth:
|
ServerHealth:
|
||||||
|
@ -61,6 +89,8 @@ components:
|
||||||
responses:
|
responses:
|
||||||
201PlayerCreated:
|
201PlayerCreated:
|
||||||
description: "201 - Player Created"
|
description: "201 - Player Created"
|
||||||
|
401PlayerNameOrPasswordWrong:
|
||||||
|
description: "401 - Player Name or Password is Wrong"
|
||||||
401Unauthorized:
|
401Unauthorized:
|
||||||
description: "401 - Unauthorized"
|
description: "401 - Unauthorized"
|
||||||
404NotFound:
|
404NotFound:
|
||||||
|
@ -112,6 +142,28 @@ paths:
|
||||||
$ref: "#/components/responses/409UsernameTaken"
|
$ref: "#/components/responses/409UsernameTaken"
|
||||||
500:
|
500:
|
||||||
$ref: "#/components/responses/500InternalError"
|
$ref: "#/components/responses/500InternalError"
|
||||||
|
/player/login:
|
||||||
|
post:
|
||||||
|
operationId: "PlayerLogin"
|
||||||
|
tags:
|
||||||
|
- server
|
||||||
|
description: "Endpoint for logging a Player in"
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/PlayerLoginData"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: "A Login Session, which can be used in the Webhook"
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: "#/components/schemas/PlayerLoginSession"
|
||||||
|
401:
|
||||||
|
$ref: "#/components/responses/401PlayerNameOrPasswordWrong"
|
||||||
|
500:
|
||||||
|
$ref: "#/components/responses/500InternalError"
|
||||||
/server/health:
|
/server/health:
|
||||||
get:
|
get:
|
||||||
operationId: "ServerGetHealthcheck"
|
operationId: "ServerGetHealthcheck"
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
package de.towerdefence.server.player.session;
|
||||||
|
|
||||||
|
import de.towerdefence.server.player.Player;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class PlayerLoginSessions {
|
||||||
|
public static final int PLAYER_LOGIN_SESSION_TOKEN_BYTE_LENGTH = 64;
|
||||||
|
private final SecureRandom random;
|
||||||
|
private final Map<String, String> playerLoginSessionTokens = new HashMap<>();
|
||||||
|
private final Map<String, Player> playerLoginSessionPlayers = new HashMap<>();
|
||||||
|
private final Map<String, ScheduledFuture<?>> playerLoginSessionSchedule = new HashMap<>();
|
||||||
|
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||||
|
|
||||||
|
public PlayerLoginSessions() {
|
||||||
|
random = new SecureRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createPlayerLoginSession(Player player) {
|
||||||
|
byte[] token_data = new byte[PLAYER_LOGIN_SESSION_TOKEN_BYTE_LENGTH];
|
||||||
|
this.random.nextBytes(token_data);
|
||||||
|
String token = new String(token_data, StandardCharsets.UTF_8);
|
||||||
|
String playerName = player.getUsername();
|
||||||
|
this.playerLoginSessionTokens.put(playerName, token);
|
||||||
|
this.playerLoginSessionPlayers.put(playerName, player);
|
||||||
|
this.playerLoginSessionSchedule.put(playerName, scheduler.schedule(
|
||||||
|
() -> {
|
||||||
|
this.playerLoginSessionTokens.remove(playerName);
|
||||||
|
this.playerLoginSessionPlayers.remove(playerName);
|
||||||
|
this.playerLoginSessionSchedule.remove(playerName);
|
||||||
|
},
|
||||||
|
30,
|
||||||
|
TimeUnit.SECONDS
|
||||||
|
));
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an Optional Player. If it is empty, that Player has no valid Login Session
|
||||||
|
*/
|
||||||
|
public Optional<Player> getPlayerFromLoginSession(String username, String token) {
|
||||||
|
if (!this.playerLoginSessionTokens.containsKey(username)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
if (!this.playerLoginSessionTokens.get(username).equals(token)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
this.playerLoginSessionTokens.remove(username);
|
||||||
|
Player player = this.playerLoginSessionPlayers.get(username);
|
||||||
|
ScheduledFuture<?> task = this.playerLoginSessionSchedule.get(username);
|
||||||
|
if (task != null) {
|
||||||
|
task.cancel(true);
|
||||||
|
}
|
||||||
|
this.playerLoginSessionSchedule.remove(username);
|
||||||
|
return Optional.of(player);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,14 @@ package de.towerdefence.server.server;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import de.towerdefence.server.oas.ServerApi;
|
import de.towerdefence.server.oas.ServerApi;
|
||||||
|
import de.towerdefence.server.oas.models.PlayerLoginData;
|
||||||
|
import de.towerdefence.server.oas.models.PlayerLoginSession;
|
||||||
import de.towerdefence.server.oas.models.PlayerRegistrationData;
|
import de.towerdefence.server.oas.models.PlayerRegistrationData;
|
||||||
import de.towerdefence.server.oas.models.ServerHealth;
|
import de.towerdefence.server.oas.models.ServerHealth;
|
||||||
import de.towerdefence.server.player.Player;
|
import de.towerdefence.server.player.Player;
|
||||||
import de.towerdefence.server.player.PlayerRepository;
|
import de.towerdefence.server.player.PlayerRepository;
|
||||||
import de.towerdefence.server.player.PlayerService;
|
import de.towerdefence.server.player.PlayerService;
|
||||||
|
import de.towerdefence.server.player.session.PlayerLoginSessions;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
|
@ -25,6 +28,8 @@ public class ServerApiController implements ServerApi {
|
||||||
private PlayerRepository playerRepository;
|
private PlayerRepository playerRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
private PlayerService playerService;
|
private PlayerService playerService;
|
||||||
|
@Autowired
|
||||||
|
private PlayerLoginSessions playerLoginSessions;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<ObjectMapper> getObjectMapper() {
|
public Optional<ObjectMapper> getObjectMapper() {
|
||||||
|
@ -52,6 +57,26 @@ public class ServerApiController implements ServerApi {
|
||||||
return new ResponseEntity<>(HttpStatus.CREATED);
|
return new ResponseEntity<>(HttpStatus.CREATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<PlayerLoginSession> playerLogin(PlayerLoginData body) {
|
||||||
|
Player player = playerRepository.findByUsername(body.getUsername());
|
||||||
|
if(player == null){
|
||||||
|
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if(!playerService.checkPassword(player, body.getPassword())) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
String token = playerLoginSessions.createPlayerLoginSession(player);
|
||||||
|
PlayerLoginSession session = new PlayerLoginSession();
|
||||||
|
session.setUsername(player.getUsername());
|
||||||
|
session.setToken(token);
|
||||||
|
return new ResponseEntity<>(session, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ResponseEntity<ServerHealth> serverGetHealthcheck() {
|
public ResponseEntity<ServerHealth> serverGetHealthcheck() {
|
||||||
ServerHealth health = new ServerHealth();
|
ServerHealth health = new ServerHealth();
|
||||||
|
|
Loading…
Add table
Reference in a new issue