Merge pull request '[Feature]: Player Registration' (!2) from story/td-3-player-registration-api into trunk
All checks were successful
Quality Check / Validate OAS (push) Successful in 30s
Quality Check / Linting (push) Successful in 1m0s
Quality Check / Static Analysis (push) Successful in 1m2s
Quality Check / Testing (push) Successful in 1m8s

Reviewed-on: #2
This commit is contained in:
Snoweuph 2025-02-11 12:38:15 +00:00 committed by Euph Forge
commit a400f3391d
Signed by: Euph Forge
GPG key ID: 85A06461FB6BDBB7
7 changed files with 106 additions and 2 deletions

View file

@ -25,6 +25,20 @@ components:
format: uuid format: uuid
example: f0981749-f550-46cd-b9ce-b6ca7cd0251f 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 # # AdminAuthInfo #
############################################# #############################################
ServerHealth: ServerHealth:
@ -45,6 +59,8 @@ components:
required: required:
- username - username
responses: responses:
201PlayerCreated:
description: "201 - Player Created"
401Unauthorized: 401Unauthorized:
description: "401 - Unauthorized" description: "401 - Unauthorized"
404NotFound: 404NotFound:
@ -53,6 +69,12 @@ components:
text/plain: text/plain:
schema: schema:
type: string type: string
409UsernameTaken:
description: "409 - Username Taken"
content:
text/plain:
schema:
type: string
409Conflict: 409Conflict:
description: "409 - Conflict" description: "409 - Conflict"
content: content:
@ -72,6 +94,24 @@ components:
schema: schema:
type: string type: string
paths: 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: /server/health:
get: get:
operationId: "ServerGetHealthcheck" operationId: "ServerGetHealthcheck"

View file

@ -2,10 +2,10 @@ package de.towerdefence.server.auth;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; 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.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.session.SessionRegistry; import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
@ -45,7 +45,7 @@ public class AuthConfig {
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http return http
.csrf(Customizer.withDefaults()) .csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth .authorizeHttpRequests(auth -> auth
.requestMatchers("/api/" + API_VERSION + "/admin/**") .requestMatchers("/api/" + API_VERSION + "/admin/**")
.authenticated() .authenticated()

View file

@ -19,6 +19,7 @@ public class Player {
public static final int PASSWORD_HASH_BYTE_LENGTH = 64; public static final int PASSWORD_HASH_BYTE_LENGTH = 64;
@Id @Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id; private Long id;
@NotNull @NotNull

View file

@ -4,4 +4,5 @@ import org.springframework.data.jpa.repository.JpaRepository;
public interface PlayerRepository extends JpaRepository<Player, Long> { public interface PlayerRepository extends JpaRepository<Player, Long> {
Player findByUsername(String username); Player findByUsername(String username);
boolean existsByUsername(String username);
} }

View file

@ -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);
}
}

View file

@ -2,17 +2,30 @@ 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.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.PlayerRepository;
import de.towerdefence.server.player.PlayerService;
import jakarta.servlet.http.HttpServletRequest; 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.http.ResponseEntity;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import java.security.NoSuchAlgorithmException;
import java.util.Optional; import java.util.Optional;
@Controller @Controller
@RequestMapping("${openapi.api.base-path:/api/v1}") @RequestMapping("${openapi.api.base-path:/api/v1}")
public class ServerApiController implements ServerApi { public class ServerApiController implements ServerApi {
@Autowired
private PlayerRepository playerRepository;
@Autowired
private PlayerService playerService;
@Override @Override
public Optional<ObjectMapper> getObjectMapper() { public Optional<ObjectMapper> getObjectMapper() {
return Optional.empty(); return Optional.empty();
@ -23,6 +36,22 @@ public class ServerApiController implements ServerApi {
return Optional.empty(); 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 @Override
public ResponseEntity<ServerHealth> serverGetHealthcheck() { public ResponseEntity<ServerHealth> serverGetHealthcheck() {
ServerHealth health = new ServerHealth(); ServerHealth health = new ServerHealth();

View file

@ -1,4 +1,5 @@
# General # General
spring.profiles.active=dev
spring.application.name=Tower Defence Server spring.application.name=Tower Defence Server
server.port=8080 server.port=8080