diff --git a/src/main/java/com/displaynone/acss/components/acs/ACSComponent.java b/src/main/java/com/displaynone/acss/components/acs/ACSComponent.java new file mode 100644 index 0000000..1f1d2e2 --- /dev/null +++ b/src/main/java/com/displaynone/acss/components/acs/ACSComponent.java @@ -0,0 +1,26 @@ +package com.displaynone.acss.components.acs; + +import com.displaynone.acss.components.acs.code.CodeModel; +import com.displaynone.acss.components.acs.code.service.CodeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +@RequiredArgsConstructor +public class ACSComponent { + private final CodeService codeService; + + public Optional getCodeByValue(Long value) { + return codeService.getCodeByValue(value); + } + + public CodeModel getCodeByValueStrict(Long value) { + return codeService.getCodeByValueStrict(value); + } + + public boolean isValid(Long code) { + return codeService.isValid(code); + } +} diff --git a/src/main/java/com/displaynone/acss/components/acs/code/CodeDTO.java b/src/main/java/com/displaynone/acss/components/acs/code/CodeDTO.java new file mode 100644 index 0000000..fbf8769 --- /dev/null +++ b/src/main/java/com/displaynone/acss/components/acs/code/CodeDTO.java @@ -0,0 +1,10 @@ +package com.displaynone.acss.components.acs.code; + +import lombok.Data; + + +@Data +public class CodeDTO { + private Long id; + private Long codeValue; +} diff --git a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeMapper.java b/src/main/java/com/displaynone/acss/components/acs/code/CodeMapper.java similarity index 87% rename from src/main/java/com/displaynone/acss/components/auth/models/code/CodeMapper.java rename to src/main/java/com/displaynone/acss/components/acs/code/CodeMapper.java index 243d166..cfd0a31 100644 --- a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeMapper.java +++ b/src/main/java/com/displaynone/acss/components/acs/code/CodeMapper.java @@ -1,4 +1,4 @@ -package com.displaynone.acss.components.auth.models.code; +package com.displaynone.acss.components.acs.code; import com.displaynone.acss.components.auth.models.role.RoleDTO; import com.displaynone.acss.components.auth.models.role.RoleMapper; @@ -15,7 +15,7 @@ public class CodeMapper { CodeDTO dto = new CodeDTO(); dto.setId(model.getId()); - dto.setValue(model.getValue()); + dto.setCodeValue(model.getCodeValue()); return dto; } diff --git a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeModel.java b/src/main/java/com/displaynone/acss/components/acs/code/CodeModel.java similarity index 54% rename from src/main/java/com/displaynone/acss/components/auth/models/code/CodeModel.java rename to src/main/java/com/displaynone/acss/components/acs/code/CodeModel.java index dcfa0f1..831106b 100644 --- a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeModel.java +++ b/src/main/java/com/displaynone/acss/components/acs/code/CodeModel.java @@ -1,9 +1,6 @@ -package com.displaynone.acss.components.auth.models.code; +package com.displaynone.acss.components.acs.code; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -12,11 +9,12 @@ import lombok.NoArgsConstructor; @Entity @Data @Builder +@Table(name = "codes") @NoArgsConstructor @AllArgsConstructor public class CodeModel { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; - private long value; + private long codeValue; } \ No newline at end of file diff --git a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeRepository.java b/src/main/java/com/displaynone/acss/components/acs/code/CodeRepository.java similarity index 50% rename from src/main/java/com/displaynone/acss/components/auth/models/code/CodeRepository.java rename to src/main/java/com/displaynone/acss/components/acs/code/CodeRepository.java index f131a1d..b3df7f3 100644 --- a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeRepository.java +++ b/src/main/java/com/displaynone/acss/components/acs/code/CodeRepository.java @@ -1,6 +1,9 @@ -package com.displaynone.acss.components.auth.models.code; +package com.displaynone.acss.components.acs.code; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface CodeRepository extends JpaRepository { + Optional findByCodeValue(Long codeValue); } diff --git a/src/main/java/com/displaynone/acss/components/acs/code/service/CodeService.java b/src/main/java/com/displaynone/acss/components/acs/code/service/CodeService.java new file mode 100644 index 0000000..3ffc6df --- /dev/null +++ b/src/main/java/com/displaynone/acss/components/acs/code/service/CodeService.java @@ -0,0 +1,13 @@ +package com.displaynone.acss.components.acs.code.service; + +import com.displaynone.acss.components.acs.code.CodeModel; + +import java.util.Optional; + +public interface CodeService { + Optional getCodeByValue(Long value); + + CodeModel getCodeByValueStrict(Long value); + + boolean isValid(Long code); +} diff --git a/src/main/java/com/displaynone/acss/components/acs/code/service/CodeServiceImpl.java b/src/main/java/com/displaynone/acss/components/acs/code/service/CodeServiceImpl.java new file mode 100644 index 0000000..66126b7 --- /dev/null +++ b/src/main/java/com/displaynone/acss/components/acs/code/service/CodeServiceImpl.java @@ -0,0 +1,33 @@ +package com.displaynone.acss.components.acs.code.service; + +import com.displaynone.acss.components.acs.code.CodeModel; +import com.displaynone.acss.components.acs.code.CodeRepository; +import com.displaynone.acss.exception.generics.NotFoundHTTPException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.Optional; + + +@Component +@RequiredArgsConstructor +public class CodeServiceImpl implements CodeService { + private final CodeRepository codeRepository; + + @Override + public Optional getCodeByValue(Long value) { + return codeRepository.findByCodeValue(value); + } + + @Override + public CodeModel getCodeByValueStrict(Long value) { + Optional model = getCodeByValue(value); + if (model.isEmpty()) throw new NotFoundHTTPException("Code not found"); + return model.get(); + } + + @Override + public boolean isValid(Long code) { + return getCodeByValue(code).isPresent(); + } +} diff --git a/src/main/java/com/displaynone/acss/components/auth/AuthComponent.java b/src/main/java/com/displaynone/acss/components/auth/AuthComponent.java index 1a3eeaa..ccb20a3 100644 --- a/src/main/java/com/displaynone/acss/components/auth/AuthComponent.java +++ b/src/main/java/com/displaynone/acss/components/auth/AuthComponent.java @@ -2,8 +2,6 @@ package com.displaynone.acss.components.auth; import com.displaynone.acss.components.auth.internal_utils.JWTUtils; import com.displaynone.acss.components.auth.models.AuthTokenPair; -import com.displaynone.acss.components.auth.models.user.UserDTO; -import com.displaynone.acss.components.auth.models.user.UserMapper; import com.displaynone.acss.components.auth.models.user.UserModel; import com.displaynone.acss.components.auth.models.user.service.UserService; import com.displaynone.acss.exception.generics.UnauthorizedHTTPException; @@ -31,18 +29,18 @@ public class AuthComponent implements UserDetailsService { this.userService = userService; } - private AuthTokenPair _generateTokenPair(@NonNull UserDetails user) { + public AuthTokenPair _generateTokenPair(@NonNull UserDetails user) { String accessToken = jwtUtils.generateAccessToken(user); String refreshToken = jwtUtils.generateRefreshToken(user); return new AuthTokenPair(accessToken, refreshToken); } - private Optional _findModelByLogin(@NonNull String login) { + public Optional getUserByLogin(@NonNull String login) { return userService.findByLogin(login); } - private UserModel _findModelByLoginStrict(@NonNull String login) { + public UserModel getUserByLoginStrict(@NonNull String login) { return userService.findByLoginStrict(login); } @@ -50,7 +48,7 @@ public class AuthComponent implements UserDetailsService { if (!jwtUtils.validateToken(accessToken)) throw new UnauthorizedHTTPException("Invalid access token"); String username = jwtUtils.getLogin(accessToken); - UserModel userModel = _findModelByLoginStrict(username); + UserModel userModel = getUserByLoginStrict(username); return new UsernamePasswordAuthenticationToken(userModel, null, userModel.getAuthorities()); } @@ -60,22 +58,13 @@ public class AuthComponent implements UserDetailsService { String login = jwtUtils.getLogin(refreshToken); System.out.println(login); - UserModel userModel = _findModelByLoginStrict(login); + UserModel userModel = getUserByLoginStrict(login); return _generateTokenPair(userModel); } - public Optional findByLogin(@NonNull String login) { - Optional userModel = _findModelByLogin(login); - return userModel.map(UserMapper::convertToDTO); - } - - public UserDTO findByLoginStrict(@NonNull String login) { - return UserMapper.convertToDTO(userService.findByLoginStrict(login)); - } - public Pair authenticate(@NonNull String login) { - UserModel userModel = _findModelByLoginStrict(login); + UserModel userModel = getUserByLoginStrict(login); return Pair.of(userModel, authenticate(userModel)); } @@ -85,6 +74,6 @@ public class AuthComponent implements UserDetailsService { @Override public UserDetails loadUserByUsername(@NonNull String username) throws UsernameNotFoundException { - return _findModelByLoginStrict(username); + return getUserByLoginStrict(username); } } diff --git a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeDTO.java b/src/main/java/com/displaynone/acss/components/auth/models/code/CodeDTO.java deleted file mode 100644 index 50253bf..0000000 --- a/src/main/java/com/displaynone/acss/components/auth/models/code/CodeDTO.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.displaynone.acss.components.auth.models.code; - -import lombok.Data; - - -@Data -public class CodeDTO { - private Long id; - private Long value; -} diff --git a/src/main/java/com/displaynone/acss/components/auth/models/code/service/CodeService.java b/src/main/java/com/displaynone/acss/components/auth/models/code/service/CodeService.java deleted file mode 100644 index 71f35bb..0000000 --- a/src/main/java/com/displaynone/acss/components/auth/models/code/service/CodeService.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.displaynone.acss.components.auth.models.code.service; - -import com.displaynone.acss.components.auth.models.code.CodeModel; - -public interface CodeService { - CodeModel getCodeByValue(Long value); - - boolean isValid(Long code); -} diff --git a/src/main/java/com/displaynone/acss/components/auth/models/code/service/CodeServiceImpl.java b/src/main/java/com/displaynone/acss/components/auth/models/code/service/CodeServiceImpl.java deleted file mode 100644 index a6945e5..0000000 --- a/src/main/java/com/displaynone/acss/components/auth/models/code/service/CodeServiceImpl.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.displaynone.acss.components.auth.models.code.service; - -import com.displaynone.acss.components.auth.models.code.CodeModel; -import com.displaynone.acss.components.auth.models.code.CodeRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - - -@Component -@RequiredArgsConstructor -public class CodeServiceImpl implements CodeService { - private final CodeRepository codeRepository; - - @Override - public CodeModel getCodeByValue(Long value) { - return null; - } - - @Override - public boolean isValid(Long code) { - return false; - } -} diff --git a/src/main/java/com/displaynone/acss/controllers/acs/ACSController.java b/src/main/java/com/displaynone/acss/controllers/acs/ACSController.java index 8210759..4cc206d 100644 --- a/src/main/java/com/displaynone/acss/controllers/acs/ACSController.java +++ b/src/main/java/com/displaynone/acss/controllers/acs/ACSController.java @@ -1,13 +1,24 @@ package com.displaynone.acss.controllers.acs; +import com.displaynone.acss.components.acs.ACSComponent; +import com.displaynone.acss.exception.generics.ForbiddenHTTPException; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/acs") +@RequiredArgsConstructor public class ACSController { + private final ACSComponent acsComponent; + @PostMapping("/open") - public ResponseEntity open() {} + public ResponseEntity open(@RequestBody OpenRB body) { + Long code = body.getCode(); + if (!acsComponent.isValid(code)) throw new ForbiddenHTTPException("Invalid code"); + return ResponseEntity.ok(null); + } } diff --git a/src/main/java/com/displaynone/acss/controllers/acs/OpenRB.java b/src/main/java/com/displaynone/acss/controllers/acs/OpenRB.java new file mode 100644 index 0000000..42f170e --- /dev/null +++ b/src/main/java/com/displaynone/acss/controllers/acs/OpenRB.java @@ -0,0 +1,8 @@ +package com.displaynone.acss.controllers.acs; + +import lombok.Data; + +@Data +public class OpenRB { + private Long code; +} diff --git a/src/main/java/com/displaynone/acss/controllers/auth/AuthController.java b/src/main/java/com/displaynone/acss/controllers/auth/AuthController.java index aadf3d9..6a66786 100644 --- a/src/main/java/com/displaynone/acss/controllers/auth/AuthController.java +++ b/src/main/java/com/displaynone/acss/controllers/auth/AuthController.java @@ -2,13 +2,13 @@ package com.displaynone.acss.controllers.auth; import com.displaynone.acss.components.auth.AuthComponent; import com.displaynone.acss.components.auth.models.AuthTokenPair; -import com.displaynone.acss.components.auth.models.user.UserDTO; import com.displaynone.acss.components.auth.models.user.UserModel; import com.displaynone.acss.exception.generics.BadRequestHTTPException; import com.displaynone.acss.exception.generics.NotFoundHTTPException; import com.displaynone.acss.exception.generics.UnauthorizedHTTPException; import com.displaynone.acss.utils.Pair; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -17,17 +17,13 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/auth") +@RequiredArgsConstructor public class AuthController { private final AuthComponent authComponent; - @Autowired - public AuthController(AuthComponent authComponent) { - this.authComponent = authComponent; - } - @PostMapping("/login") - public ResponseEntity login(@RequestBody LoginRequestBody loginRequest) { - String login = loginRequest.getLogin(); + public ResponseEntity login(@RequestBody LoginRB body) { + String login = body.getLogin(); if (login == null) throw new BadRequestHTTPException("Login not specified"); Pair authInfo; @@ -42,8 +38,8 @@ public class AuthController { } @PostMapping("/refresh") - public ResponseEntity refreshToken(@RequestBody TokenRefreshRequestBody request) { - String requestRefreshToken = request.getRefreshToken(); + public ResponseEntity refreshToken(@RequestBody TokenRefreshRB body) { + String requestRefreshToken = body.getRefreshToken(); if (requestRefreshToken == null) throw new BadRequestHTTPException("Refresh token not specified"); AuthTokenPair authTokenPair; diff --git a/src/main/java/com/displaynone/acss/controllers/auth/LoginRequestBody.java b/src/main/java/com/displaynone/acss/controllers/auth/LoginRB.java similarity index 76% rename from src/main/java/com/displaynone/acss/controllers/auth/LoginRequestBody.java rename to src/main/java/com/displaynone/acss/controllers/auth/LoginRB.java index 852a5d1..03132d8 100644 --- a/src/main/java/com/displaynone/acss/controllers/auth/LoginRequestBody.java +++ b/src/main/java/com/displaynone/acss/controllers/auth/LoginRB.java @@ -3,6 +3,6 @@ package com.displaynone.acss.controllers.auth; import lombok.Data; @Data -public class LoginRequestBody { +public class LoginRB { private String login; } diff --git a/src/main/java/com/displaynone/acss/controllers/auth/TokenRefreshRequestBody.java b/src/main/java/com/displaynone/acss/controllers/auth/TokenRefreshRB.java similarity index 73% rename from src/main/java/com/displaynone/acss/controllers/auth/TokenRefreshRequestBody.java rename to src/main/java/com/displaynone/acss/controllers/auth/TokenRefreshRB.java index bc35509..316bff0 100644 --- a/src/main/java/com/displaynone/acss/controllers/auth/TokenRefreshRequestBody.java +++ b/src/main/java/com/displaynone/acss/controllers/auth/TokenRefreshRB.java @@ -3,6 +3,6 @@ package com.displaynone.acss.controllers.auth; import lombok.Data; @Data -public class TokenRefreshRequestBody { +public class TokenRefreshRB { public String refreshToken; } diff --git a/src/main/java/com/displaynone/acss/controllers/user/UserController.java b/src/main/java/com/displaynone/acss/controllers/user/UserController.java index 071442a..571641d 100644 --- a/src/main/java/com/displaynone/acss/controllers/user/UserController.java +++ b/src/main/java/com/displaynone/acss/controllers/user/UserController.java @@ -1,17 +1,23 @@ package com.displaynone.acss.controllers.user; +import com.displaynone.acss.components.auth.AuthComponent; import com.displaynone.acss.components.auth.models.user.UserDTO; import com.displaynone.acss.components.auth.models.user.UserMapper; import com.displaynone.acss.components.auth.models.user.UserModel; +import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/users") +@RequiredArgsConstructor public class UserController { + private final AuthComponent authComponent; + @GetMapping("/me") public ResponseEntity getMe() { UserModel user = (UserModel) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); @@ -19,7 +25,8 @@ public class UserController { } @GetMapping("/login/{login}") - public ResponseEntity getUserByLogin() { - // TODO + public ResponseEntity getUserByLogin(@PathVariable String login) { + UserModel user = authComponent.getUserByLoginStrict(login); + return ResponseEntity.ok(UserMapper.convertToDTO(user)); } } diff --git a/src/main/java/com/displaynone/acss/exception/generics/ForbiddenHTTPException.java b/src/main/java/com/displaynone/acss/exception/generics/ForbiddenHTTPException.java new file mode 100644 index 0000000..82934f4 --- /dev/null +++ b/src/main/java/com/displaynone/acss/exception/generics/ForbiddenHTTPException.java @@ -0,0 +1,12 @@ +package com.displaynone.acss.exception.generics; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + + +@ResponseStatus(HttpStatus.FORBIDDEN) +public class ForbiddenHTTPException extends RuntimeException { + public ForbiddenHTTPException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/displaynone/acss/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/displaynone/acss/exception/handler/GlobalExceptionHandler.java index 6b70ed8..844f3fe 100644 --- a/src/main/java/com/displaynone/acss/exception/handler/GlobalExceptionHandler.java +++ b/src/main/java/com/displaynone/acss/exception/handler/GlobalExceptionHandler.java @@ -1,10 +1,7 @@ package com.displaynone.acss.exception.handler; import com.displaynone.acss.exception.*; -import com.displaynone.acss.exception.generics.AlreadyExistsHTTPException; -import com.displaynone.acss.exception.generics.BadRequestHTTPException; -import com.displaynone.acss.exception.generics.NotFoundHTTPException; -import com.displaynone.acss.exception.generics.UnauthorizedHTTPException; +import com.displaynone.acss.exception.generics.*; import com.fasterxml.jackson.databind.exc.InvalidFormatException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,6 +14,7 @@ import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.resource.NoResourceFoundException; @RestControllerAdvice public class GlobalExceptionHandler { @@ -73,4 +71,14 @@ public class GlobalExceptionHandler { return buildErrorResponse(e, HttpStatus.UNAUTHORIZED, "Unauthorized: unexpected error occurred"); } + + @ExceptionHandler(NoResourceFoundException.class) + public ResponseEntity handleNoResourceFoundException(NoResourceFoundException e, WebRequest request) { + return buildErrorResponse(e, HttpStatus.NOT_FOUND, null); + } + + @ExceptionHandler(ForbiddenHTTPException.class) + public ResponseEntity handleForbiddenHTTPException(ForbiddenHTTPException e, WebRequest request) { + return buildErrorResponse(e, HttpStatus.FORBIDDEN, null); + } } diff --git a/src/main/java/com/displaynone/acss/security/WebSecurityConfig.java b/src/main/java/com/displaynone/acss/security/WebSecurityConfig.java index 79f9081..e8facae 100644 --- a/src/main/java/com/displaynone/acss/security/WebSecurityConfig.java +++ b/src/main/java/com/displaynone/acss/security/WebSecurityConfig.java @@ -46,6 +46,7 @@ public class WebSecurityConfig { .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers("/api/auth/**").permitAll() + .requestMatchers("/api/users/login/**").hasAuthority("ROLE_ADMIN") .anyRequest().authenticated() ) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) diff --git a/src/main/resources/db/changelog/01/0004-codes.xml b/src/main/resources/db/changelog/01/0004-codes.xml index c678ca2..c811087 100644 --- a/src/main/resources/db/changelog/01/0004-codes.xml +++ b/src/main/resources/db/changelog/01/0004-codes.xml @@ -9,7 +9,7 @@ - + diff --git a/src/main/resources/db/changelog/data/csv/0003-user_roles-data.csv b/src/main/resources/db/changelog/data/csv/0003-user_roles-data.csv index b947668..ec912ce 100644 --- a/src/main/resources/db/changelog/data/csv/0003-user_roles-data.csv +++ b/src/main/resources/db/changelog/data/csv/0003-user_roles-data.csv @@ -1,5 +1,5 @@ user_id;role_id -1;1 +1;2 2;2 3;2 4;1 diff --git a/src/main/resources/db/changelog/data/csv/0004-codes-data.csv b/src/main/resources/db/changelog/data/csv/0004-codes-data.csv index c2aca49..2d3e19e 100644 --- a/src/main/resources/db/changelog/data/csv/0004-codes-data.csv +++ b/src/main/resources/db/changelog/data/csv/0004-codes-data.csv @@ -1,3 +1,4 @@ +id;code_value 1;1234567890123456789 2;9223372036854775807 3;1122334455667788990 diff --git a/src/main/resources/db/changelog/db.changelog-master.xml b/src/main/resources/db/changelog/db.changelog-master.xml index 07b4c8e..244ef3b 100644 --- a/src/main/resources/db/changelog/db.changelog-master.xml +++ b/src/main/resources/db/changelog/db.changelog-master.xml @@ -8,8 +8,10 @@ + +