From 89aa262830dd1db6c3888ba5a05de6c0928b1f63 Mon Sep 17 00:00:00 2001
From: Snoweuph <snow+git@euph.email>
Date: Tue, 11 Feb 2025 13:32:36 +0100
Subject: [PATCH 1/2] TD-3: Player Test Data and CSRF Fix

---
 .../towerdefence/server/auth/AuthConfig.java  |  4 +--
 .../de/towerdefence/server/player/Player.java |  1 +
 .../server/player/PlayerTestData.java         | 32 +++++++++++++++++++
 src/main/resources/application.properties     |  1 +
 4 files changed, 36 insertions(+), 2 deletions(-)
 create mode 100644 src/main/java/de/towerdefence/server/player/PlayerTestData.java

diff --git a/src/main/java/de/towerdefence/server/auth/AuthConfig.java b/src/main/java/de/towerdefence/server/auth/AuthConfig.java
index 8bf7f7f..a3c920f 100644
--- a/src/main/java/de/towerdefence/server/auth/AuthConfig.java
+++ b/src/main/java/de/towerdefence/server/auth/AuthConfig.java
@@ -2,10 +2,10 @@ package de.towerdefence.server.auth;
 
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.Customizer;
 import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
 import org.springframework.security.core.session.SessionRegistry;
 import org.springframework.security.core.session.SessionRegistryImpl;
 import org.springframework.security.web.SecurityFilterChain;
@@ -45,7 +45,7 @@ public class AuthConfig {
     @Bean
     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
         return http
-                .csrf(Customizer.withDefaults())
+                .csrf(AbstractHttpConfigurer::disable)
                 .authorizeHttpRequests(auth -> auth
                         .requestMatchers("/api/" + API_VERSION + "/admin/**")
                         .authenticated()
diff --git a/src/main/java/de/towerdefence/server/player/Player.java b/src/main/java/de/towerdefence/server/player/Player.java
index 79a7fd3..1d84ec9 100644
--- a/src/main/java/de/towerdefence/server/player/Player.java
+++ b/src/main/java/de/towerdefence/server/player/Player.java
@@ -19,6 +19,7 @@ public class Player {
     public static final int PASSWORD_HASH_BYTE_LENGTH = 64;
 
     @Id
+    @GeneratedValue(strategy=GenerationType.IDENTITY)
     private Long id;
 
     @NotNull
diff --git a/src/main/java/de/towerdefence/server/player/PlayerTestData.java b/src/main/java/de/towerdefence/server/player/PlayerTestData.java
new file mode 100644
index 0000000..b7db6ac
--- /dev/null
+++ b/src/main/java/de/towerdefence/server/player/PlayerTestData.java
@@ -0,0 +1,32 @@
+package de.towerdefence.server.player;
+
+import jakarta.annotation.PostConstruct;
+import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+
+import java.security.NoSuchAlgorithmException;
+
+@AllArgsConstructor
+@Configuration
+@Profile("dev")
+public class PlayerTestData {
+    @Autowired
+    private PlayerRepository playerRepository;
+    @Autowired
+    private PlayerService playerService;
+
+    @PostConstruct
+    public void loadFixtures() throws NoSuchAlgorithmException {
+        Player player1 = new Player();
+        player1.setUsername("Player1");
+        playerService.setPassword(player1, "1234");
+        this.playerRepository.save(player1);
+
+        Player player2 = new Player();
+        player2.setUsername("Player2");
+        playerService.setPassword(player2, "1234");
+        this.playerRepository.save(player2);
+    }
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 88e9b20..eb3a310 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,4 +1,5 @@
 # General
+spring.profiles.active=dev
 spring.application.name=Tower Defence Server
 server.port=8080
 
-- 
2.45.3


From 721ad39cb97e5dc090dce6a401e2081072c14091 Mon Sep 17 00:00:00 2001
From: Snoweuph <snow+git@euph.email>
Date: Tue, 11 Feb 2025 13:34:36 +0100
Subject: [PATCH 2/2] TD-3: Add Player Registration Endpoint

---
 api/api.yml                                   | 40 +++++++++++++++++++
 .../server/player/PlayerRepository.java       |  1 +
 .../server/server/ServerApiController.java    | 29 ++++++++++++++
 3 files changed, 70 insertions(+)

diff --git a/api/api.yml b/api/api.yml
index b0cc553..17a79e2 100644
--- a/api/api.yml
+++ b/api/api.yml
@@ -25,6 +25,20 @@ components:
       format: uuid
       example: f0981749-f550-46cd-b9ce-b6ca7cd0251f
     #############################################
+    #  PlayerRegistrationData                   #
+    #############################################
+    PlayerRegistrationData:
+      description: Data needed to create a new player
+      type: object
+      properties:
+        username:
+          type: string
+        password:
+          type: string
+      required:
+        - username
+        - password
+    #############################################
     #  AdminAuthInfo                            #
     #############################################
     ServerHealth:
@@ -45,6 +59,8 @@ components:
       required:
         - username
   responses:
+    201PlayerCreated:
+      description: "201 - Player Created"
     401Unauthorized:
       description: "401 - Unauthorized"
     404NotFound:
@@ -53,6 +69,12 @@ components:
         text/plain:
           schema:
             type: string
+    409UsernameTaken:
+      description: "409 - Username Taken"
+      content:
+        text/plain:
+          schema:
+            type: string
     409Conflict:
       description: "409 - Conflict"
       content:
@@ -72,6 +94,24 @@ components:
           schema:
             type: string
 paths:
+  /player/register:
+    post:
+      operationId: "PlayerRegister"
+      tags:
+        - server
+      description: "Endpoint for registering a new Player"
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/PlayerRegistrationData"
+      responses:
+        201:
+          $ref: "#/components/responses/201PlayerCreated"
+        409:
+          $ref: "#/components/responses/409UsernameTaken"
+        500:
+          $ref: "#/components/responses/500InternalError"
   /server/health:
     get:
       operationId: "ServerGetHealthcheck"
diff --git a/src/main/java/de/towerdefence/server/player/PlayerRepository.java b/src/main/java/de/towerdefence/server/player/PlayerRepository.java
index 39b7d04..5ca4c19 100644
--- a/src/main/java/de/towerdefence/server/player/PlayerRepository.java
+++ b/src/main/java/de/towerdefence/server/player/PlayerRepository.java
@@ -4,4 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
 
 public  interface PlayerRepository extends JpaRepository<Player, Long> {
     Player findByUsername(String username);
+    boolean existsByUsername(String username);
 }
diff --git a/src/main/java/de/towerdefence/server/server/ServerApiController.java b/src/main/java/de/towerdefence/server/server/ServerApiController.java
index 01f966e..447660f 100644
--- a/src/main/java/de/towerdefence/server/server/ServerApiController.java
+++ b/src/main/java/de/towerdefence/server/server/ServerApiController.java
@@ -2,17 +2,30 @@ package de.towerdefence.server.server;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import de.towerdefence.server.oas.ServerApi;
+import de.towerdefence.server.oas.models.PlayerRegistrationData;
 import de.towerdefence.server.oas.models.ServerHealth;
+import de.towerdefence.server.player.Player;
+import de.towerdefence.server.player.PlayerRepository;
+import de.towerdefence.server.player.PlayerService;
 import jakarta.servlet.http.HttpServletRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 
+import java.security.NoSuchAlgorithmException;
 import java.util.Optional;
 
 @Controller
 @RequestMapping("${openapi.api.base-path:/api/v1}")
 public class ServerApiController implements ServerApi {
+
+    @Autowired
+    private PlayerRepository playerRepository;
+    @Autowired
+    private PlayerService playerService;
+
     @Override
     public Optional<ObjectMapper> getObjectMapper() {
         return Optional.empty();
@@ -23,6 +36,22 @@ public class ServerApiController implements ServerApi {
         return Optional.empty();
     }
 
+    @Override
+    public ResponseEntity<Void> playerRegister(PlayerRegistrationData body) {
+        if(playerRepository.existsByUsername(body.getUsername())){
+            return new ResponseEntity<>(HttpStatus.CONFLICT);
+        }
+        Player newPlayer = new Player();
+        newPlayer.setUsername(body.getUsername());
+        try{
+            playerService.setPassword(newPlayer, body.getPassword());
+        } catch (NoSuchAlgorithmException exception){
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+        playerRepository.save(newPlayer);
+        return new ResponseEntity<>(HttpStatus.CREATED);
+    }
+
     @Override
     public ResponseEntity<ServerHealth> serverGetHealthcheck() {
         ServerHealth health = new ServerHealth();
-- 
2.45.3