From 210930c57b667726fe89acf26c8a284d18a8f360 Mon Sep 17 00:00:00 2001 From: Yusuf ATALAR Date: Sat, 12 Oct 2024 22:44:08 +0300 Subject: [PATCH 1/6] base regulations --- pom.xml | 9 +- .../java/com/alibou/security/Application.java | 13 ++ .../alibou/security/SecurityApplication.java | 48 ------- .../security/auth/AuthenticationService.java | 120 ---------------- .../AuthenticationController.java | 11 +- .../{ => model}/AuthenticationRequest.java | 6 +- .../{ => model}/AuthenticationResponse.java | 2 +- .../auth/{ => model}/RegisterRequest.java | 14 +- .../security/{token => auth/model}/Token.java | 20 +-- .../alibou/security/auth/model/TokenType.java | 5 + .../repository}/TokenRepository.java | 4 +- .../service}/ApplicationAuditAware.java | 7 +- .../auth/service/AuthenticationService.java | 133 ++++++++++++++++++ .../java/com/alibou/security/book/Book.java | 56 -------- .../alibou/security/book/BookController.java | 32 ----- .../alibou/security/book/BookRepository.java | 6 - .../com/alibou/security/book/BookRequest.java | 15 -- .../com/alibou/security/book/BookService.java | 26 ---- .../security/config/ApplicationConfig.java | 5 +- .../config/JwtAuthenticationFilter.java | 6 +- .../alibou/security/config/LogoutService.java | 2 +- .../config/SecurityConfiguration.java | 26 ++-- .../alibou/security/demo/AdminController.java | 40 ------ .../alibou/security/demo/DemoController.java | 19 --- .../security/demo/ManagementController.java | 50 ------- .../com/alibou/security/token/TokenType.java | 5 - .../java/com/alibou/security/user/Role.java | 59 -------- .../user/{ => controller}/UserController.java | 5 +- .../{ => model}/ChangePasswordRequest.java | 2 +- .../security/user/{ => model}/Permission.java | 11 +- .../com/alibou/security/user/model/Role.java | 41 ++++++ .../security/user/{ => model}/User.java | 18 +-- .../user/{ => repository}/UserRepository.java | 4 +- .../security/user/service/UserService.java | 10 ++ .../impl/UserServiceImpl.java} | 13 +- src/main/resources/application.yml | 17 +-- 36 files changed, 296 insertions(+), 564 deletions(-) create mode 100644 src/main/java/com/alibou/security/Application.java delete mode 100644 src/main/java/com/alibou/security/SecurityApplication.java delete mode 100644 src/main/java/com/alibou/security/auth/AuthenticationService.java rename src/main/java/com/alibou/security/auth/{ => controller}/AuthenticationController.java (74%) rename src/main/java/com/alibou/security/auth/{ => model}/AuthenticationRequest.java (68%) rename src/main/java/com/alibou/security/auth/{ => model}/AuthenticationResponse.java (90%) rename src/main/java/com/alibou/security/auth/{ => model}/RegisterRequest.java (63%) rename src/main/java/com/alibou/security/{token => auth/model}/Token.java (53%) create mode 100644 src/main/java/com/alibou/security/auth/model/TokenType.java rename src/main/java/com/alibou/security/{token => auth/repository}/TokenRepository.java (85%) rename src/main/java/com/alibou/security/{auditing => auth/service}/ApplicationAuditAware.java (86%) create mode 100644 src/main/java/com/alibou/security/auth/service/AuthenticationService.java delete mode 100644 src/main/java/com/alibou/security/book/Book.java delete mode 100644 src/main/java/com/alibou/security/book/BookController.java delete mode 100644 src/main/java/com/alibou/security/book/BookRepository.java delete mode 100644 src/main/java/com/alibou/security/book/BookRequest.java delete mode 100644 src/main/java/com/alibou/security/book/BookService.java delete mode 100644 src/main/java/com/alibou/security/demo/AdminController.java delete mode 100644 src/main/java/com/alibou/security/demo/DemoController.java delete mode 100644 src/main/java/com/alibou/security/demo/ManagementController.java delete mode 100644 src/main/java/com/alibou/security/token/TokenType.java delete mode 100644 src/main/java/com/alibou/security/user/Role.java rename src/main/java/com/alibou/security/user/{ => controller}/UserController.java (77%) rename src/main/java/com/alibou/security/user/{ => model}/ChangePasswordRequest.java (85%) rename src/main/java/com/alibou/security/user/{ => model}/Permission.java (52%) create mode 100644 src/main/java/com/alibou/security/user/model/Role.java rename src/main/java/com/alibou/security/user/{ => model}/User.java (74%) rename src/main/java/com/alibou/security/user/{ => repository}/UserRepository.java (70%) create mode 100644 src/main/java/com/alibou/security/user/service/UserService.java rename src/main/java/com/alibou/security/user/{UserService.java => service/impl/UserServiceImpl.java} (70%) diff --git a/pom.xml b/pom.xml index 9c14b26..98ff86a 100644 --- a/pom.xml +++ b/pom.xml @@ -31,8 +31,13 @@ - org.postgresql - postgresql + org.jetbrains + annotations + 24.0.1 + + + com.mysql + mysql-connector-j runtime diff --git a/src/main/java/com/alibou/security/Application.java b/src/main/java/com/alibou/security/Application.java new file mode 100644 index 0000000..b2c93d9 --- /dev/null +++ b/src/main/java/com/alibou/security/Application.java @@ -0,0 +1,13 @@ +package com.alibou.security; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@SpringBootApplication +@EnableJpaAuditing(auditorAwareRef = "auditorAware") +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/src/main/java/com/alibou/security/SecurityApplication.java b/src/main/java/com/alibou/security/SecurityApplication.java deleted file mode 100644 index 1448cff..0000000 --- a/src/main/java/com/alibou/security/SecurityApplication.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.alibou.security; - -import com.alibou.security.auth.AuthenticationService; -import com.alibou.security.auth.RegisterRequest; -import com.alibou.security.user.Role; -import org.springframework.boot.CommandLineRunner; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; - -import static com.alibou.security.user.Role.ADMIN; -import static com.alibou.security.user.Role.MANAGER; - -@SpringBootApplication -@EnableJpaAuditing(auditorAwareRef = "auditorAware") -public class SecurityApplication { - - public static void main(String[] args) { - SpringApplication.run(SecurityApplication.class, args); - } - - @Bean - public CommandLineRunner commandLineRunner( - AuthenticationService service - ) { - return args -> { - var admin = RegisterRequest.builder() - .firstname("Admin") - .lastname("Admin") - .email("admin@mail.com") - .password("password") - .role(ADMIN) - .build(); - System.out.println("Admin token: " + service.register(admin).getAccessToken()); - - var manager = RegisterRequest.builder() - .firstname("Admin") - .lastname("Admin") - .email("manager@mail.com") - .password("password") - .role(MANAGER) - .build(); - System.out.println("Manager token: " + service.register(manager).getAccessToken()); - - }; - } -} diff --git a/src/main/java/com/alibou/security/auth/AuthenticationService.java b/src/main/java/com/alibou/security/auth/AuthenticationService.java deleted file mode 100644 index 53193a7..0000000 --- a/src/main/java/com/alibou/security/auth/AuthenticationService.java +++ /dev/null @@ -1,120 +0,0 @@ -package com.alibou.security.auth; - -import com.alibou.security.config.JwtService; -import com.alibou.security.token.Token; -import com.alibou.security.token.TokenRepository; -import com.alibou.security.token.TokenType; -import com.alibou.security.user.Role; -import com.alibou.security.user.User; -import com.alibou.security.user.UserRepository; -import com.fasterxml.jackson.databind.ObjectMapper; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpHeaders; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.stereotype.Service; - -import java.io.IOException; - -@Service -@RequiredArgsConstructor -public class AuthenticationService { - private final UserRepository repository; - private final TokenRepository tokenRepository; - private final PasswordEncoder passwordEncoder; - private final JwtService jwtService; - private final AuthenticationManager authenticationManager; - - public AuthenticationResponse register(RegisterRequest request) { - var user = User.builder() - .firstname(request.getFirstname()) - .lastname(request.getLastname()) - .email(request.getEmail()) - .password(passwordEncoder.encode(request.getPassword())) - .role(request.getRole()) - .build(); - var savedUser = repository.save(user); - var jwtToken = jwtService.generateToken(user); - var refreshToken = jwtService.generateRefreshToken(user); - saveUserToken(savedUser, jwtToken); - return AuthenticationResponse.builder() - .accessToken(jwtToken) - .refreshToken(refreshToken) - .build(); - } - - public AuthenticationResponse authenticate(AuthenticationRequest request) { - authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken( - request.getEmail(), - request.getPassword() - ) - ); - var user = repository.findByEmail(request.getEmail()) - .orElseThrow(); - var jwtToken = jwtService.generateToken(user); - var refreshToken = jwtService.generateRefreshToken(user); - revokeAllUserTokens(user); - saveUserToken(user, jwtToken); - return AuthenticationResponse.builder() - .accessToken(jwtToken) - .refreshToken(refreshToken) - .build(); - } - - private void saveUserToken(User user, String jwtToken) { - var token = Token.builder() - .user(user) - .token(jwtToken) - .tokenType(TokenType.BEARER) - .expired(false) - .revoked(false) - .build(); - tokenRepository.save(token); - } - - private void revokeAllUserTokens(User user) { - var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId()); - if (validUserTokens.isEmpty()) - return; - validUserTokens.forEach(token -> { - token.setExpired(true); - token.setRevoked(true); - }); - tokenRepository.saveAll(validUserTokens); - } - - public void refreshToken( - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { - final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION); - final String refreshToken; - final String userEmail; - if (authHeader == null ||!authHeader.startsWith("Bearer ")) { - return; - } - refreshToken = authHeader.substring(7); - userEmail = jwtService.extractUsername(refreshToken); - if (userEmail != null) { - var user = this.repository.findByEmail(userEmail) - .orElseThrow(); - if (jwtService.isTokenValid(refreshToken, user)) { - var accessToken = jwtService.generateToken(user); - revokeAllUserTokens(user); - saveUserToken(user, accessToken); - var authResponse = AuthenticationResponse.builder() - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); - new ObjectMapper().writeValue(response.getOutputStream(), authResponse); - } - } - } -} diff --git a/src/main/java/com/alibou/security/auth/AuthenticationController.java b/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java similarity index 74% rename from src/main/java/com/alibou/security/auth/AuthenticationController.java rename to src/main/java/com/alibou/security/auth/controller/AuthenticationController.java index e1d5107..294f1a0 100644 --- a/src/main/java/com/alibou/security/auth/AuthenticationController.java +++ b/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java @@ -1,5 +1,9 @@ -package com.alibou.security.auth; +package com.alibou.security.auth.controller; +import com.alibou.security.auth.model.AuthenticationRequest; +import com.alibou.security.auth.model.AuthenticationResponse; +import com.alibou.security.auth.service.AuthenticationService; +import com.alibou.security.auth.model.RegisterRequest; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @@ -32,10 +36,7 @@ public ResponseEntity authenticate( } @PostMapping("/refresh-token") - public void refreshToken( - HttpServletRequest request, - HttpServletResponse response - ) throws IOException { + public void refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException { service.refreshToken(request, response); } diff --git a/src/main/java/com/alibou/security/auth/AuthenticationRequest.java b/src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java similarity index 68% rename from src/main/java/com/alibou/security/auth/AuthenticationRequest.java rename to src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java index 6d72722..78b2509 100644 --- a/src/main/java/com/alibou/security/auth/AuthenticationRequest.java +++ b/src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java @@ -1,4 +1,4 @@ -package com.alibou.security.auth; +package com.alibou.security.auth.model; import lombok.AllArgsConstructor; import lombok.Builder; @@ -11,6 +11,6 @@ @NoArgsConstructor public class AuthenticationRequest { - private String email; - String password; + private String email; + private String password; } diff --git a/src/main/java/com/alibou/security/auth/AuthenticationResponse.java b/src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java similarity index 90% rename from src/main/java/com/alibou/security/auth/AuthenticationResponse.java rename to src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java index c10bbb6..d425189 100644 --- a/src/main/java/com/alibou/security/auth/AuthenticationResponse.java +++ b/src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java @@ -1,4 +1,4 @@ -package com.alibou.security.auth; +package com.alibou.security.auth.model; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/alibou/security/auth/RegisterRequest.java b/src/main/java/com/alibou/security/auth/model/RegisterRequest.java similarity index 63% rename from src/main/java/com/alibou/security/auth/RegisterRequest.java rename to src/main/java/com/alibou/security/auth/model/RegisterRequest.java index 4f51665..d9b48ef 100644 --- a/src/main/java/com/alibou/security/auth/RegisterRequest.java +++ b/src/main/java/com/alibou/security/auth/model/RegisterRequest.java @@ -1,10 +1,11 @@ -package com.alibou.security.auth; +package com.alibou.security.auth.model; -import com.alibou.security.user.Role; +import com.alibou.security.user.model.Role; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.jetbrains.annotations.NotNull; @Data @Builder @@ -12,9 +13,18 @@ @NoArgsConstructor public class RegisterRequest { + @NotNull private String firstname; + + @NotNull private String lastname; + + @NotNull private String email; + + @NotNull private String password; + + @NotNull private Role role; } diff --git a/src/main/java/com/alibou/security/token/Token.java b/src/main/java/com/alibou/security/auth/model/Token.java similarity index 53% rename from src/main/java/com/alibou/security/token/Token.java rename to src/main/java/com/alibou/security/auth/model/Token.java index 71f3571..3127647 100644 --- a/src/main/java/com/alibou/security/token/Token.java +++ b/src/main/java/com/alibou/security/auth/model/Token.java @@ -1,19 +1,12 @@ -package com.alibou.security.token; - -import com.alibou.security.user.User; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +package com.alibou.security.auth.model; + +import com.alibou.security.user.model.User; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; @Data @Builder @@ -23,7 +16,8 @@ public class Token { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") + @GenericGenerator(name = "native") public Integer id; @Column(unique = true) diff --git a/src/main/java/com/alibou/security/auth/model/TokenType.java b/src/main/java/com/alibou/security/auth/model/TokenType.java new file mode 100644 index 0000000..b685f59 --- /dev/null +++ b/src/main/java/com/alibou/security/auth/model/TokenType.java @@ -0,0 +1,5 @@ +package com.alibou.security.auth.model; + +public enum TokenType { + BEARER +} diff --git a/src/main/java/com/alibou/security/token/TokenRepository.java b/src/main/java/com/alibou/security/auth/repository/TokenRepository.java similarity index 85% rename from src/main/java/com/alibou/security/token/TokenRepository.java rename to src/main/java/com/alibou/security/auth/repository/TokenRepository.java index 48235d8..353de33 100644 --- a/src/main/java/com/alibou/security/token/TokenRepository.java +++ b/src/main/java/com/alibou/security/auth/repository/TokenRepository.java @@ -1,7 +1,9 @@ -package com.alibou.security.token; +package com.alibou.security.auth.repository; import java.util.List; import java.util.Optional; + +import com.alibou.security.auth.model.Token; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java b/src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java similarity index 86% rename from src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java rename to src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java index 3f8172f..e499f4c 100644 --- a/src/main/java/com/alibou/security/auditing/ApplicationAuditAware.java +++ b/src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java @@ -1,6 +1,6 @@ -package com.alibou.security.auditing; +package com.alibou.security.auth.service; -import com.alibou.security.user.User; +import com.alibou.security.user.model.User; import org.springframework.data.domain.AuditorAware; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; @@ -9,6 +9,7 @@ import java.util.Optional; public class ApplicationAuditAware implements AuditorAware { + @Override public Optional getCurrentAuditor() { Authentication authentication = @@ -16,7 +17,7 @@ public Optional getCurrentAuditor() { .getContext() .getAuthentication(); if (authentication == null || - !authentication.isAuthenticated() || + !authentication.isAuthenticated() || authentication instanceof AnonymousAuthenticationToken ) { return Optional.empty(); diff --git a/src/main/java/com/alibou/security/auth/service/AuthenticationService.java b/src/main/java/com/alibou/security/auth/service/AuthenticationService.java new file mode 100644 index 0000000..9e0d508 --- /dev/null +++ b/src/main/java/com/alibou/security/auth/service/AuthenticationService.java @@ -0,0 +1,133 @@ +package com.alibou.security.auth.service; + +import com.alibou.security.auth.model.RegisterRequest; +import com.alibou.security.auth.model.AuthenticationRequest; +import com.alibou.security.auth.model.AuthenticationResponse; +import com.alibou.security.config.JwtService; +import com.alibou.security.auth.model.Token; +import com.alibou.security.auth.repository.TokenRepository; +import com.alibou.security.auth.model.TokenType; +import com.alibou.security.user.model.User; +import com.alibou.security.user.repository.UserRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.persistence.EntityExistsException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class AuthenticationService { + private final Logger logger = LoggerFactory.getLogger(AuthenticationService.class); + private final UserRepository repository; + private final TokenRepository tokenRepository; + private final PasswordEncoder passwordEncoder; + private final JwtService jwtService; + private final AuthenticationManager authenticationManager; + private final UserRepository userRepository; + + @NotNull + public AuthenticationResponse register(@NotNull RegisterRequest request) { + final Optional existUser = userRepository.findByEmail(request.getEmail()); + if (existUser.isPresent()) { + final String errorMessage = String.format("User with given email = %s already exists",request.getEmail()); + throw new EntityExistsException(errorMessage); + } + var user = User.builder() + .firstname(request.getFirstname()) + .lastname(request.getLastname()) + .email(request.getEmail()) + .password(passwordEncoder.encode(request.getPassword())) + .role(request.getRole()) + .build(); + var savedUser = repository.save(user); + var jwtToken = jwtService.generateToken(user); + var refreshToken = jwtService.generateRefreshToken(user); + saveUserToken(savedUser, jwtToken); + return AuthenticationResponse.builder() + .accessToken(jwtToken) + .refreshToken(refreshToken) + .build(); + } + + @NotNull + public AuthenticationResponse authenticate(@NotNull AuthenticationRequest request) { + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + request.getEmail(), + request.getPassword() + ) + ); + var user = repository.findByEmail(request.getEmail()) + .orElseThrow(); + var jwtToken = jwtService.generateToken(user); + var refreshToken = jwtService.generateRefreshToken(user); + revokeAllUserTokens(user); + saveUserToken(user, jwtToken); + return AuthenticationResponse.builder() + .accessToken(jwtToken) + .refreshToken(refreshToken) + .build(); + } + + private void saveUserToken(@NotNull User user, @NotNull String jwtToken) { + var token = Token.builder() + .user(user) + .token(jwtToken) + .tokenType(TokenType.BEARER) + .expired(false) + .revoked(false) + .build(); + tokenRepository.save(token); + } + + private void revokeAllUserTokens(@NotNull User user) { + var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId()); + if (validUserTokens.isEmpty()) + return; + validUserTokens.forEach(token -> { + token.setExpired(true); + token.setRevoked(true); + }); + tokenRepository.saveAll(validUserTokens); + } + + public void refreshToken( + HttpServletRequest request, + HttpServletResponse response + ) throws IOException { + final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION); + final String refreshToken; + final String userEmail; + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + return; + } + refreshToken = authHeader.substring(7); + userEmail = jwtService.extractUsername(refreshToken); + if (userEmail != null) { + var user = this.repository.findByEmail(userEmail) + .orElseThrow(); + if (jwtService.isTokenValid(refreshToken, user)) { + var accessToken = jwtService.generateToken(user); + revokeAllUserTokens(user); + saveUserToken(user, accessToken); + var authResponse = AuthenticationResponse.builder() + .accessToken(accessToken) + .refreshToken(refreshToken) + .build(); + new ObjectMapper().writeValue(response.getOutputStream(), authResponse); + } + } + } +} diff --git a/src/main/java/com/alibou/security/book/Book.java b/src/main/java/com/alibou/security/book/Book.java deleted file mode 100644 index 3f041af..0000000 --- a/src/main/java/com/alibou/security/book/Book.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.alibou.security.book; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.springframework.data.annotation.CreatedBy; -import org.springframework.data.annotation.CreatedDate; -import org.springframework.data.annotation.LastModifiedBy; -import org.springframework.data.annotation.LastModifiedDate; -import org.springframework.data.jpa.domain.support.AuditingEntityListener; - -import java.time.LocalDateTime; - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -@Entity -@EntityListeners(AuditingEntityListener.class) -public class Book { - - @Id - @GeneratedValue - private Integer id; - private String author; - private String isbn; - - @CreatedDate - @Column( - nullable = false, - updatable = false - ) - private LocalDateTime createDate; - - @LastModifiedDate - @Column(insertable = false) - private LocalDateTime lastModified; - - - @CreatedBy - @Column( - nullable = false, - updatable = false - ) - private Integer createdBy; - - @LastModifiedBy - @Column(insertable = false) - private Integer lastModifiedBy; -} diff --git a/src/main/java/com/alibou/security/book/BookController.java b/src/main/java/com/alibou/security/book/BookController.java deleted file mode 100644 index 4c45728..0000000 --- a/src/main/java/com/alibou/security/book/BookController.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.alibou.security.book; - -import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -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; - -import java.util.List; - -@RestController -@RequestMapping("/api/v1/books") -@RequiredArgsConstructor -public class BookController { - - private final BookService service; - - @PostMapping - public ResponseEntity save( - @RequestBody BookRequest request - ) { - service.save(request); - return ResponseEntity.accepted().build(); - } - - @GetMapping - public ResponseEntity> findAllBooks() { - return ResponseEntity.ok(service.findAll()); - } -} diff --git a/src/main/java/com/alibou/security/book/BookRepository.java b/src/main/java/com/alibou/security/book/BookRepository.java deleted file mode 100644 index 21ca467..0000000 --- a/src/main/java/com/alibou/security/book/BookRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.alibou.security.book; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface BookRepository extends JpaRepository { -} diff --git a/src/main/java/com/alibou/security/book/BookRequest.java b/src/main/java/com/alibou/security/book/BookRequest.java deleted file mode 100644 index dcf6765..0000000 --- a/src/main/java/com/alibou/security/book/BookRequest.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.alibou.security.book; - -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@Builder -public class BookRequest { - - private Integer id; - private String author; - private String isbn; -} diff --git a/src/main/java/com/alibou/security/book/BookService.java b/src/main/java/com/alibou/security/book/BookService.java deleted file mode 100644 index c09ded8..0000000 --- a/src/main/java/com/alibou/security/book/BookService.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.alibou.security.book; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -@RequiredArgsConstructor -public class BookService { - - private final BookRepository repository; - - public void save(BookRequest request) { - var book = Book.builder() - .id(request.getId()) - .author(request.getAuthor()) - .isbn(request.getIsbn()) - .build(); - repository.save(book); - } - - public List findAll() { - return repository.findAll(); - } -} diff --git a/src/main/java/com/alibou/security/config/ApplicationConfig.java b/src/main/java/com/alibou/security/config/ApplicationConfig.java index ae71abf..76c3f4d 100644 --- a/src/main/java/com/alibou/security/config/ApplicationConfig.java +++ b/src/main/java/com/alibou/security/config/ApplicationConfig.java @@ -1,8 +1,7 @@ package com.alibou.security.config; -import com.alibou.security.auditing.ApplicationAuditAware; -import com.alibou.security.user.UserRepository; -import jakarta.persistence.criteria.CriteriaBuilder; +import com.alibou.security.auth.service.ApplicationAuditAware; +import com.alibou.security.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java index d6e55d1..0b5d20c 100644 --- a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java @@ -1,17 +1,13 @@ package com.alibou.security.config; -import com.alibou.security.token.TokenRepository; +import com.alibou.security.auth.repository.TokenRepository; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import java.beans.Transient; import java.io.IOException; -import java.security.Security; -import jakarta.transaction.TransactionScoped; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; diff --git a/src/main/java/com/alibou/security/config/LogoutService.java b/src/main/java/com/alibou/security/config/LogoutService.java index 0784565..3bca509 100644 --- a/src/main/java/com/alibou/security/config/LogoutService.java +++ b/src/main/java/com/alibou/security/config/LogoutService.java @@ -1,6 +1,6 @@ package com.alibou.security.config; -import com.alibou.security.token.TokenRepository; +import com.alibou.security.auth.repository.TokenRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/alibou/security/config/SecurityConfiguration.java b/src/main/java/com/alibou/security/config/SecurityConfiguration.java index e4aefe6..22235ae 100644 --- a/src/main/java/com/alibou/security/config/SecurityConfiguration.java +++ b/src/main/java/com/alibou/security/config/SecurityConfiguration.java @@ -13,16 +13,12 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; -import static com.alibou.security.user.Permission.ADMIN_CREATE; -import static com.alibou.security.user.Permission.ADMIN_DELETE; -import static com.alibou.security.user.Permission.ADMIN_READ; -import static com.alibou.security.user.Permission.ADMIN_UPDATE; -import static com.alibou.security.user.Permission.MANAGER_CREATE; -import static com.alibou.security.user.Permission.MANAGER_DELETE; -import static com.alibou.security.user.Permission.MANAGER_READ; -import static com.alibou.security.user.Permission.MANAGER_UPDATE; -import static com.alibou.security.user.Role.ADMIN; -import static com.alibou.security.user.Role.MANAGER; +import static com.alibou.security.user.model.Permission.ADMIN_CREATE; +import static com.alibou.security.user.model.Permission.ADMIN_DELETE; +import static com.alibou.security.user.model.Permission.ADMIN_READ; +import static com.alibou.security.user.model.Permission.ADMIN_UPDATE; + +import static com.alibou.security.user.model.Role.ADMIN; import static org.springframework.http.HttpMethod.DELETE; import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; @@ -57,11 +53,11 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests(req -> req.requestMatchers(WHITE_LIST_URL) .permitAll() - .requestMatchers("/api/v1/management/**").hasAnyRole(ADMIN.name(), MANAGER.name()) - .requestMatchers(GET, "/api/v1/management/**").hasAnyAuthority(ADMIN_READ.name(), MANAGER_READ.name()) - .requestMatchers(POST, "/api/v1/management/**").hasAnyAuthority(ADMIN_CREATE.name(), MANAGER_CREATE.name()) - .requestMatchers(PUT, "/api/v1/management/**").hasAnyAuthority(ADMIN_UPDATE.name(), MANAGER_UPDATE.name()) - .requestMatchers(DELETE, "/api/v1/management/**").hasAnyAuthority(ADMIN_DELETE.name(), MANAGER_DELETE.name()) + .requestMatchers("/api/v1/management/**").hasAnyRole(ADMIN.name()) + .requestMatchers(GET, "/api/v1/management/**").hasAnyAuthority(ADMIN_READ.name()) + .requestMatchers(POST, "/api/v1/management/**").hasAnyAuthority(ADMIN_CREATE.name()) + .requestMatchers(PUT, "/api/v1/management/**").hasAnyAuthority(ADMIN_UPDATE.name()) + .requestMatchers(DELETE, "/api/v1/management/**").hasAnyAuthority(ADMIN_DELETE.name()) .anyRequest() .authenticated() ) diff --git a/src/main/java/com/alibou/security/demo/AdminController.java b/src/main/java/com/alibou/security/demo/AdminController.java deleted file mode 100644 index 18ede65..0000000 --- a/src/main/java/com/alibou/security/demo/AdminController.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.alibou.security.demo; - -import io.swagger.v3.oas.annotations.Hidden; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/v1/admin") -@PreAuthorize("hasRole('ADMIN')") -public class AdminController { - - @GetMapping - @PreAuthorize("hasAuthority('admin:read')") - public String get() { - return "GET:: admin controller"; - } - @PostMapping - @PreAuthorize("hasAuthority('admin:create')") - @Hidden - public String post() { - return "POST:: admin controller"; - } - @PutMapping - @PreAuthorize("hasAuthority('admin:update')") - @Hidden - public String put() { - return "PUT:: admin controller"; - } - @DeleteMapping - @PreAuthorize("hasAuthority('admin:delete')") - @Hidden - public String delete() { - return "DELETE:: admin controller"; - } -} diff --git a/src/main/java/com/alibou/security/demo/DemoController.java b/src/main/java/com/alibou/security/demo/DemoController.java deleted file mode 100644 index ee2c380..0000000 --- a/src/main/java/com/alibou/security/demo/DemoController.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.alibou.security.demo; - -import io.swagger.v3.oas.annotations.Hidden; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/v1/demo-controller") -@Hidden -public class DemoController { - - @GetMapping - public ResponseEntity sayHello() { - return ResponseEntity.ok("Hello from secured endpoint"); - } - -} diff --git a/src/main/java/com/alibou/security/demo/ManagementController.java b/src/main/java/com/alibou/security/demo/ManagementController.java deleted file mode 100644 index a214a9b..0000000 --- a/src/main/java/com/alibou/security/demo/ManagementController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.alibou.security.demo; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/api/v1/management") -@Tag(name = "Management") -public class ManagementController { - - - @Operation( - description = "Get endpoint for manager", - summary = "This is a summary for management get endpoint", - responses = { - @ApiResponse( - description = "Success", - responseCode = "200" - ), - @ApiResponse( - description = "Unauthorized / Invalid Token", - responseCode = "403" - ) - } - - ) - @GetMapping - public String get() { - return "GET:: management controller"; - } - @PostMapping - public String post() { - return "POST:: management controller"; - } - @PutMapping - public String put() { - return "PUT:: management controller"; - } - @DeleteMapping - public String delete() { - return "DELETE:: management controller"; - } -} diff --git a/src/main/java/com/alibou/security/token/TokenType.java b/src/main/java/com/alibou/security/token/TokenType.java deleted file mode 100644 index 82a8cff..0000000 --- a/src/main/java/com/alibou/security/token/TokenType.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.alibou.security.token; - -public enum TokenType { - BEARER -} diff --git a/src/main/java/com/alibou/security/user/Role.java b/src/main/java/com/alibou/security/user/Role.java deleted file mode 100644 index 0ff9bd1..0000000 --- a/src/main/java/com/alibou/security/user/Role.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.alibou.security.user; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.springframework.security.core.authority.SimpleGrantedAuthority; - -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import static com.alibou.security.user.Permission.ADMIN_CREATE; -import static com.alibou.security.user.Permission.ADMIN_DELETE; -import static com.alibou.security.user.Permission.ADMIN_READ; -import static com.alibou.security.user.Permission.ADMIN_UPDATE; -import static com.alibou.security.user.Permission.MANAGER_CREATE; -import static com.alibou.security.user.Permission.MANAGER_DELETE; -import static com.alibou.security.user.Permission.MANAGER_READ; -import static com.alibou.security.user.Permission.MANAGER_UPDATE; - -@RequiredArgsConstructor -public enum Role { - - USER(Collections.emptySet()), - ADMIN( - Set.of( - ADMIN_READ, - ADMIN_UPDATE, - ADMIN_DELETE, - ADMIN_CREATE, - MANAGER_READ, - MANAGER_UPDATE, - MANAGER_DELETE, - MANAGER_CREATE - ) - ), - MANAGER( - Set.of( - MANAGER_READ, - MANAGER_UPDATE, - MANAGER_DELETE, - MANAGER_CREATE - ) - ) - - ; - - @Getter - private final Set permissions; - - public List getAuthorities() { - var authorities = getPermissions() - .stream() - .map(permission -> new SimpleGrantedAuthority(permission.getPermission())) - .collect(Collectors.toList()); - authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name())); - return authorities; - } -} diff --git a/src/main/java/com/alibou/security/user/UserController.java b/src/main/java/com/alibou/security/user/controller/UserController.java similarity index 77% rename from src/main/java/com/alibou/security/user/UserController.java rename to src/main/java/com/alibou/security/user/controller/UserController.java index 415be48..613e64f 100644 --- a/src/main/java/com/alibou/security/user/UserController.java +++ b/src/main/java/com/alibou/security/user/controller/UserController.java @@ -1,5 +1,8 @@ -package com.alibou.security.user; +package com.alibou.security.user.controller; +import com.alibou.security.user.service.UserService; +import com.alibou.security.user.service.impl.UserServiceImpl; +import com.alibou.security.user.model.ChangePasswordRequest; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PatchMapping; diff --git a/src/main/java/com/alibou/security/user/ChangePasswordRequest.java b/src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java similarity index 85% rename from src/main/java/com/alibou/security/user/ChangePasswordRequest.java rename to src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java index 70bca36..7dbced3 100644 --- a/src/main/java/com/alibou/security/user/ChangePasswordRequest.java +++ b/src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java @@ -1,4 +1,4 @@ -package com.alibou.security.user; +package com.alibou.security.user.model; import lombok.Builder; import lombok.Getter; diff --git a/src/main/java/com/alibou/security/user/Permission.java b/src/main/java/com/alibou/security/user/model/Permission.java similarity index 52% rename from src/main/java/com/alibou/security/user/Permission.java rename to src/main/java/com/alibou/security/user/model/Permission.java index 16ae8b4..f7c8e4b 100644 --- a/src/main/java/com/alibou/security/user/Permission.java +++ b/src/main/java/com/alibou/security/user/model/Permission.java @@ -1,4 +1,4 @@ -package com.alibou.security.user; +package com.alibou.security.user.model; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -9,14 +9,7 @@ public enum Permission { ADMIN_READ("admin:read"), ADMIN_UPDATE("admin:update"), ADMIN_CREATE("admin:create"), - ADMIN_DELETE("admin:delete"), - MANAGER_READ("management:read"), - MANAGER_UPDATE("management:update"), - MANAGER_CREATE("management:create"), - MANAGER_DELETE("management:delete") - - ; - + ADMIN_DELETE("admin:delete"); @Getter private final String permission; } diff --git a/src/main/java/com/alibou/security/user/model/Role.java b/src/main/java/com/alibou/security/user/model/Role.java new file mode 100644 index 0000000..ef183a8 --- /dev/null +++ b/src/main/java/com/alibou/security/user/model/Role.java @@ -0,0 +1,41 @@ +package com.alibou.security.user.model; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.alibou.security.user.model.Permission.ADMIN_CREATE; +import static com.alibou.security.user.model.Permission.ADMIN_DELETE; +import static com.alibou.security.user.model.Permission.ADMIN_READ; +import static com.alibou.security.user.model.Permission.ADMIN_UPDATE; + +@RequiredArgsConstructor +public enum Role { + + USER(Collections.emptySet()), + ADMIN( + Set.of( + ADMIN_READ, + ADMIN_UPDATE, + ADMIN_DELETE, + ADMIN_CREATE + ) + ); + + @Getter + private final Set permissions; + + public List getAuthorities() { + var authorities = getPermissions() + .stream() + .map(permission -> new SimpleGrantedAuthority(permission.getPermission())) + .collect(Collectors.toList()); + authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name())); + return authorities; + } +} diff --git a/src/main/java/com/alibou/security/user/User.java b/src/main/java/com/alibou/security/user/model/User.java similarity index 74% rename from src/main/java/com/alibou/security/user/User.java rename to src/main/java/com/alibou/security/user/model/User.java index bc4e086..f178f46 100644 --- a/src/main/java/com/alibou/security/user/User.java +++ b/src/main/java/com/alibou/security/user/model/User.java @@ -1,21 +1,16 @@ -package com.alibou.security.user; +package com.alibou.security.user.model; + +import com.alibou.security.auth.model.Token; +import jakarta.persistence.*; -import com.alibou.security.token.Token; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; import java.util.Collection; import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @Data @@ -27,7 +22,8 @@ public class User implements UserDetails { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") + @GenericGenerator(name = "native") private Integer id; private String firstname; private String lastname; diff --git a/src/main/java/com/alibou/security/user/UserRepository.java b/src/main/java/com/alibou/security/user/repository/UserRepository.java similarity index 70% rename from src/main/java/com/alibou/security/user/UserRepository.java rename to src/main/java/com/alibou/security/user/repository/UserRepository.java index a979ad6..403ccbd 100644 --- a/src/main/java/com/alibou/security/user/UserRepository.java +++ b/src/main/java/com/alibou/security/user/repository/UserRepository.java @@ -1,6 +1,8 @@ -package com.alibou.security.user; +package com.alibou.security.user.repository; import java.util.Optional; + +import com.alibou.security.user.model.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository { diff --git a/src/main/java/com/alibou/security/user/service/UserService.java b/src/main/java/com/alibou/security/user/service/UserService.java new file mode 100644 index 0000000..d3f02ca --- /dev/null +++ b/src/main/java/com/alibou/security/user/service/UserService.java @@ -0,0 +1,10 @@ +package com.alibou.security.user.service; + +import com.alibou.security.user.model.ChangePasswordRequest; +import org.jetbrains.annotations.NotNull; + +import java.security.Principal; + +public interface UserService { + void changePassword(@NotNull ChangePasswordRequest request, @NotNull Principal connectedUser); +} diff --git a/src/main/java/com/alibou/security/user/UserService.java b/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java similarity index 70% rename from src/main/java/com/alibou/security/user/UserService.java rename to src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java index a17181d..4b00710 100644 --- a/src/main/java/com/alibou/security/user/UserService.java +++ b/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java @@ -1,6 +1,11 @@ -package com.alibou.security.user; +package com.alibou.security.user.service.impl; +import com.alibou.security.user.model.ChangePasswordRequest; +import com.alibou.security.user.model.User; +import com.alibou.security.user.repository.UserRepository; +import com.alibou.security.user.service.UserService; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -9,11 +14,13 @@ @Service @RequiredArgsConstructor -public class UserService { +public class UserServiceImpl implements UserService { private final PasswordEncoder passwordEncoder; private final UserRepository repository; - public void changePassword(ChangePasswordRequest request, Principal connectedUser) { + + @Override + public void changePassword(@NotNull ChangePasswordRequest request, @NotNull Principal connectedUser) { var user = (User) ((UsernamePasswordAuthenticationToken) connectedUser).getPrincipal(); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 71b71d1..e060b4d 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,18 +1,19 @@ spring: datasource: - url: jdbc:postgresql://localhost:5432/jwt_security - username: username - password: password - driver-class-name: org.postgresql.Driver + url: jdbc:mysql://localhost:3306/warehouse + username: root + password: Mert121217 + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: hibernate: - ddl-auto: create-drop - show-sql: false + ddl-auto: none + show-sql: true properties: hibernate: format_sql: true - database: postgresql - database-platform: org.hibernate.dialect.PostgreSQLDialect + database: mysql + database-platform: org.hibernate.dialect.MySQLDialect application: security: From 7cb517f050a914db28e610f771b538b4fc84fedf Mon Sep 17 00:00:00 2001 From: Yusuf ATALAR Date: Sun, 13 Oct 2024 12:22:52 +0300 Subject: [PATCH 2/6] regs fot lombok --- .../controller/AuthenticationController.java | 44 +++++----- .../auth/model/AuthenticationRequest.java | 13 +-- .../auth/model/AuthenticationResponse.java | 15 +--- .../security/auth/model/RegisterRequest.java | 27 ++---- .../com/alibou/security/auth/model/Token.java | 83 +++++++++++++----- .../auth/service/AuthenticationService.java | 65 +++++++------- .../security/config/ApplicationConfig.java | 65 +++++++------- .../config/JwtAuthenticationFilter.java | 87 ++++++++++--------- .../alibou/security/config/LogoutService.java | 46 +++++----- .../config/SecurityConfiguration.java | 9 +- .../user/controller/UserController.java | 5 +- .../user/model/ChangePasswordRequest.java | 14 +-- .../security/user/model/Permission.java | 14 +-- .../com/alibou/security/user/model/Role.java | 24 ++--- .../com/alibou/security/user/model/User.java | 65 ++++++++++++-- .../user/service/impl/UserServiceImpl.java | 13 ++- 16 files changed, 334 insertions(+), 255 deletions(-) diff --git a/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java b/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java index 294f1a0..3031e0d 100644 --- a/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java +++ b/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java @@ -17,28 +17,32 @@ @RestController @RequestMapping("/api/v1/auth") -@RequiredArgsConstructor public class AuthenticationController { - private final AuthenticationService service; - - @PostMapping("/register") - public ResponseEntity register( - @RequestBody RegisterRequest request - ) { - return ResponseEntity.ok(service.register(request)); - } - @PostMapping("/authenticate") - public ResponseEntity authenticate( - @RequestBody AuthenticationRequest request - ) { - return ResponseEntity.ok(service.authenticate(request)); - } - - @PostMapping("/refresh-token") - public void refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException { - service.refreshToken(request, response); - } + private final AuthenticationService service; + + public AuthenticationController(AuthenticationService service) { + this.service = service; + } + + @PostMapping("/register") + public ResponseEntity register( + @RequestBody RegisterRequest request + ) { + return ResponseEntity.ok(service.register(request)); + } + + @PostMapping("/authenticate") + public ResponseEntity authenticate( + @RequestBody AuthenticationRequest request + ) { + return ResponseEntity.ok(service.authenticate(request)); + } + + @PostMapping("/refresh-token") + public void refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException { + service.refreshToken(request, response); + } } diff --git a/src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java b/src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java index 78b2509..cab7c01 100644 --- a/src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java +++ b/src/main/java/com/alibou/security/auth/model/AuthenticationRequest.java @@ -1,16 +1,7 @@ package com.alibou.security.auth.model; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class AuthenticationRequest { +import org.jetbrains.annotations.NotNull; - private String email; - private String password; +public record AuthenticationRequest(@NotNull String email, @NotNull String password) { } diff --git a/src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java b/src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java index d425189..be9213e 100644 --- a/src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java +++ b/src/main/java/com/alibou/security/auth/model/AuthenticationResponse.java @@ -1,19 +1,8 @@ package com.alibou.security.auth.model; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class AuthenticationResponse { - @JsonProperty("access_token") - private String accessToken; - @JsonProperty("refresh_token") - private String refreshToken; +public record AuthenticationResponse(@JsonProperty("access_token") String accessToken, + @JsonProperty("refresh_token") String refreshToken) { } diff --git a/src/main/java/com/alibou/security/auth/model/RegisterRequest.java b/src/main/java/com/alibou/security/auth/model/RegisterRequest.java index d9b48ef..d95ccd6 100644 --- a/src/main/java/com/alibou/security/auth/model/RegisterRequest.java +++ b/src/main/java/com/alibou/security/auth/model/RegisterRequest.java @@ -1,30 +1,13 @@ package com.alibou.security.auth.model; import com.alibou.security.user.model.Role; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; import org.jetbrains.annotations.NotNull; -@Data -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class RegisterRequest { - @NotNull - private String firstname; +public record RegisterRequest(@NotNull String firstname, + @NotNull String lastname, + @NotNull String email, + @NotNull String password, + @NotNull Role role) { - @NotNull - private String lastname; - - @NotNull - private String email; - - @NotNull - private String password; - - @NotNull - private Role role; } diff --git a/src/main/java/com/alibou/security/auth/model/Token.java b/src/main/java/com/alibou/security/auth/model/Token.java index 3127647..bd32fa3 100644 --- a/src/main/java/com/alibou/security/auth/model/Token.java +++ b/src/main/java/com/alibou/security/auth/model/Token.java @@ -2,35 +2,76 @@ import com.alibou.security.user.model.User; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; import org.hibernate.annotations.GenericGenerator; -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor @Entity +@Table(name = "token") public class Token { - @Id - @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") - @GenericGenerator(name = "native") - public Integer id; + @Id + @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") + @GenericGenerator(name = "native") + public Integer id; - @Column(unique = true) - public String token; + @Column(unique = true) + public String token; - @Enumerated(EnumType.STRING) - public TokenType tokenType = TokenType.BEARER; + @Enumerated(EnumType.STRING) + public TokenType tokenType = TokenType.BEARER; - public boolean revoked; + public boolean revoked; - public boolean expired; + public boolean expired; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - public User user; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + public User user; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getToken() { + return token; + } + + public void setToken(String token) { + this.token = token; + } + + public TokenType getTokenType() { + return tokenType; + } + + public void setTokenType(TokenType tokenType) { + this.tokenType = tokenType; + } + + public boolean isRevoked() { + return revoked; + } + + public void setRevoked(boolean revoked) { + this.revoked = revoked; + } + + public boolean isExpired() { + return expired; + } + + public void setExpired(boolean expired) { + this.expired = expired; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } } diff --git a/src/main/java/com/alibou/security/auth/service/AuthenticationService.java b/src/main/java/com/alibou/security/auth/service/AuthenticationService.java index 9e0d508..ac7ef3a 100644 --- a/src/main/java/com/alibou/security/auth/service/AuthenticationService.java +++ b/src/main/java/com/alibou/security/auth/service/AuthenticationService.java @@ -27,7 +27,6 @@ import java.util.Optional; @Service -@RequiredArgsConstructor public class AuthenticationService { private final Logger logger = LoggerFactory.getLogger(AuthenticationService.class); private final UserRepository repository; @@ -37,58 +36,59 @@ public class AuthenticationService { private final AuthenticationManager authenticationManager; private final UserRepository userRepository; + public AuthenticationService(UserRepository repository, TokenRepository tokenRepository, PasswordEncoder passwordEncoder, JwtService jwtService, AuthenticationManager authenticationManager, UserRepository userRepository) { + this.repository = repository; + this.tokenRepository = tokenRepository; + this.passwordEncoder = passwordEncoder; + this.jwtService = jwtService; + this.authenticationManager = authenticationManager; + this.userRepository = userRepository; + } + @NotNull public AuthenticationResponse register(@NotNull RegisterRequest request) { - final Optional existUser = userRepository.findByEmail(request.getEmail()); + final Optional existUser = userRepository.findByEmail(request.email()); if (existUser.isPresent()) { - final String errorMessage = String.format("User with given email = %s already exists",request.getEmail()); + final String errorMessage = String.format("User with given email = %s already exists", request.email()); throw new EntityExistsException(errorMessage); } - var user = User.builder() - .firstname(request.getFirstname()) - .lastname(request.getLastname()) - .email(request.getEmail()) - .password(passwordEncoder.encode(request.getPassword())) - .role(request.getRole()) - .build(); + final User user = new User(); + user.setFirstname(request.firstname()); + user.setLastname(request.lastname()); + user.setEmail(request.email()); + user.setPassword(passwordEncoder.encode(request.password())); + user.setRole(request.role()); var savedUser = repository.save(user); var jwtToken = jwtService.generateToken(user); var refreshToken = jwtService.generateRefreshToken(user); saveUserToken(savedUser, jwtToken); - return AuthenticationResponse.builder() - .accessToken(jwtToken) - .refreshToken(refreshToken) - .build(); + return new AuthenticationResponse(jwtToken, refreshToken); } @NotNull public AuthenticationResponse authenticate(@NotNull AuthenticationRequest request) { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( - request.getEmail(), - request.getPassword() + request.email(), + request.password() ) ); - var user = repository.findByEmail(request.getEmail()) + var user = repository.findByEmail(request.email()) .orElseThrow(); var jwtToken = jwtService.generateToken(user); var refreshToken = jwtService.generateRefreshToken(user); revokeAllUserTokens(user); saveUserToken(user, jwtToken); - return AuthenticationResponse.builder() - .accessToken(jwtToken) - .refreshToken(refreshToken) - .build(); + return new AuthenticationResponse(jwtToken, refreshToken); } private void saveUserToken(@NotNull User user, @NotNull String jwtToken) { - var token = Token.builder() - .user(user) - .token(jwtToken) - .tokenType(TokenType.BEARER) - .expired(false) - .revoked(false) - .build(); + final Token token = new Token(); + token.setUser(user); + token.setToken(jwtToken); + token.setTokenType(TokenType.BEARER); + token.setExpired(false); + token.setRevoked(false); tokenRepository.save(token); } @@ -103,9 +103,7 @@ private void revokeAllUserTokens(@NotNull User user) { tokenRepository.saveAll(validUserTokens); } - public void refreshToken( - HttpServletRequest request, - HttpServletResponse response + public void refreshToken(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response ) throws IOException { final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION); final String refreshToken; @@ -122,10 +120,7 @@ public void refreshToken( var accessToken = jwtService.generateToken(user); revokeAllUserTokens(user); saveUserToken(user, accessToken); - var authResponse = AuthenticationResponse.builder() - .accessToken(accessToken) - .refreshToken(refreshToken) - .build(); + var authResponse = new AuthenticationResponse(accessToken, refreshToken); new ObjectMapper().writeValue(response.getOutputStream(), authResponse); } } diff --git a/src/main/java/com/alibou/security/config/ApplicationConfig.java b/src/main/java/com/alibou/security/config/ApplicationConfig.java index 76c3f4d..7d50e95 100644 --- a/src/main/java/com/alibou/security/config/ApplicationConfig.java +++ b/src/main/java/com/alibou/security/config/ApplicationConfig.java @@ -16,38 +16,41 @@ import org.springframework.security.crypto.password.PasswordEncoder; @Configuration -@RequiredArgsConstructor public class ApplicationConfig { - private final UserRepository repository; - - @Bean - public UserDetailsService userDetailsService() { - return username -> repository.findByEmail(username) - .orElseThrow(() -> new UsernameNotFoundException("User not found")); - } - - @Bean - public AuthenticationProvider authenticationProvider() { - DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); - authProvider.setUserDetailsService(userDetailsService()); - authProvider.setPasswordEncoder(passwordEncoder()); - return authProvider; - } - - @Bean - public AuditorAware auditorAware() { - return new ApplicationAuditAware(); - } - - @Bean - public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { - return config.getAuthenticationManager(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } + private final UserRepository repository; + + public ApplicationConfig(UserRepository repository) { + this.repository = repository; + } + + @Bean + public UserDetailsService userDetailsService() { + return username -> repository.findByEmail(username) + .orElseThrow(() -> new UsernameNotFoundException("User not found")); + } + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userDetailsService()); + authProvider.setPasswordEncoder(passwordEncoder()); + return authProvider; + } + + @Bean + public AuditorAware auditorAware() { + return new ApplicationAuditAware(); + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } } diff --git a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java index 0b5d20c..71b519d 100644 --- a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java @@ -19,49 +19,56 @@ import org.springframework.web.filter.OncePerRequestFilter; @Component -@RequiredArgsConstructor public class JwtAuthenticationFilter extends OncePerRequestFilter { - private final JwtService jwtService; - private final UserDetailsService userDetailsService; - private final TokenRepository tokenRepository; + private final JwtService jwtService; + private final UserDetailsService userDetailsService; + private final TokenRepository tokenRepository; - @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain - ) throws ServletException, IOException { - if (request.getServletPath().contains("/api/v1/auth")) { - filterChain.doFilter(request, response); - return; + public JwtAuthenticationFilter(JwtService jwtService, + UserDetailsService userDetailsService, + TokenRepository tokenRepository) { + this.jwtService = jwtService; + this.userDetailsService = userDetailsService; + this.tokenRepository = tokenRepository; } - final String authHeader = request.getHeader("Authorization"); - final String jwt; - final String userEmail; - if (authHeader == null ||!authHeader.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - return; - } - jwt = authHeader.substring(7); - userEmail = jwtService.extractUsername(jwt); - if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); - var isTokenValid = tokenRepository.findByToken(jwt) - .map(t -> !t.isExpired() && !t.isRevoked()) - .orElse(false); - if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) { - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); - authToken.setDetails( - new WebAuthenticationDetailsSource().buildDetails(request) - ); - SecurityContextHolder.getContext().setAuthentication(authToken); - } + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) throws ServletException, IOException { + if (request.getServletPath().contains("/api/v1/auth")) { + filterChain.doFilter(request, response); + return; + } + final String authHeader = request.getHeader("Authorization"); + final String jwt; + final String userEmail; + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + jwt = authHeader.substring(7); + userEmail = jwtService.extractUsername(jwt); + if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = this.userDetailsService.loadUserByUsername(userEmail); + var isTokenValid = tokenRepository.findByToken(jwt) + .map(t -> !t.isExpired() && !t.isRevoked()) + .orElse(false); + if (jwtService.isTokenValid(jwt, userDetails) && isTokenValid) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + authToken.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request) + ); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + } + filterChain.doFilter(request, response); } - filterChain.doFilter(request, response); - } } diff --git a/src/main/java/com/alibou/security/config/LogoutService.java b/src/main/java/com/alibou/security/config/LogoutService.java index 3bca509..932cc3c 100644 --- a/src/main/java/com/alibou/security/config/LogoutService.java +++ b/src/main/java/com/alibou/security/config/LogoutService.java @@ -3,37 +3,39 @@ import com.alibou.security.auth.repository.TokenRepository; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.logout.LogoutHandler; import org.springframework.stereotype.Service; @Service -@RequiredArgsConstructor public class LogoutService implements LogoutHandler { - private final TokenRepository tokenRepository; + private final TokenRepository tokenRepository; - @Override - public void logout( - HttpServletRequest request, - HttpServletResponse response, - Authentication authentication - ) { - final String authHeader = request.getHeader("Authorization"); - final String jwt; - if (authHeader == null ||!authHeader.startsWith("Bearer ")) { - return; + public LogoutService(TokenRepository tokenRepository) { + this.tokenRepository = tokenRepository; } - jwt = authHeader.substring(7); - var storedToken = tokenRepository.findByToken(jwt) - .orElse(null); - if (storedToken != null) { - storedToken.setExpired(true); - storedToken.setRevoked(true); - tokenRepository.save(storedToken); - SecurityContextHolder.clearContext(); + + @Override + public void logout( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication + ) { + final String authHeader = request.getHeader("Authorization"); + final String jwt; + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + return; + } + jwt = authHeader.substring(7); + var storedToken = tokenRepository.findByToken(jwt) + .orElse(null); + if (storedToken != null) { + storedToken.setExpired(true); + storedToken.setRevoked(true); + tokenRepository.save(storedToken); + SecurityContextHolder.clearContext(); + } } - } } diff --git a/src/main/java/com/alibou/security/config/SecurityConfiguration.java b/src/main/java/com/alibou/security/config/SecurityConfiguration.java index 22235ae..d8bbf87 100644 --- a/src/main/java/com/alibou/security/config/SecurityConfiguration.java +++ b/src/main/java/com/alibou/security/config/SecurityConfiguration.java @@ -27,7 +27,6 @@ @Configuration @EnableWebSecurity -@RequiredArgsConstructor @EnableMethodSecurity public class SecurityConfiguration { @@ -46,6 +45,14 @@ public class SecurityConfiguration { private final AuthenticationProvider authenticationProvider; private final LogoutHandler logoutHandler; + public SecurityConfiguration(JwtAuthenticationFilter jwtAuthFilter, + AuthenticationProvider authenticationProvider, + LogoutHandler logoutHandler) { + this.jwtAuthFilter = jwtAuthFilter; + this.authenticationProvider = authenticationProvider; + this.logoutHandler = logoutHandler; + } + @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http diff --git a/src/main/java/com/alibou/security/user/controller/UserController.java b/src/main/java/com/alibou/security/user/controller/UserController.java index 613e64f..0bbf0de 100644 --- a/src/main/java/com/alibou/security/user/controller/UserController.java +++ b/src/main/java/com/alibou/security/user/controller/UserController.java @@ -14,11 +14,14 @@ @RestController @RequestMapping("/api/v1/users") -@RequiredArgsConstructor public class UserController { private final UserService service; + public UserController(UserService service) { + this.service = service; + } + @PatchMapping public ResponseEntity changePassword( @RequestBody ChangePasswordRequest request, diff --git a/src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java b/src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java index 7dbced3..0097f66 100644 --- a/src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java +++ b/src/main/java/com/alibou/security/user/model/ChangePasswordRequest.java @@ -1,15 +1,9 @@ package com.alibou.security.user.model; -import lombok.Builder; -import lombok.Getter; -import lombok.Setter; -@Getter -@Setter -@Builder -public class ChangePasswordRequest { +import org.jetbrains.annotations.NotNull; - private String currentPassword; - private String newPassword; - private String confirmationPassword; +public record ChangePasswordRequest(@NotNull String currentPassword, + @NotNull String newPassword, + @NotNull String confirmationPassword) { } diff --git a/src/main/java/com/alibou/security/user/model/Permission.java b/src/main/java/com/alibou/security/user/model/Permission.java index f7c8e4b..3602a7f 100644 --- a/src/main/java/com/alibou/security/user/model/Permission.java +++ b/src/main/java/com/alibou/security/user/model/Permission.java @@ -1,15 +1,19 @@ package com.alibou.security.user.model; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor public enum Permission { ADMIN_READ("admin:read"), ADMIN_UPDATE("admin:update"), ADMIN_CREATE("admin:create"), ADMIN_DELETE("admin:delete"); - @Getter + private final String permission; + + Permission(String permission) { + this.permission = permission; + } + + public String getPermission() { + return permission; + } } diff --git a/src/main/java/com/alibou/security/user/model/Role.java b/src/main/java/com/alibou/security/user/model/Role.java index ef183a8..ac15bfb 100644 --- a/src/main/java/com/alibou/security/user/model/Role.java +++ b/src/main/java/com/alibou/security/user/model/Role.java @@ -14,22 +14,20 @@ import static com.alibou.security.user.model.Permission.ADMIN_READ; import static com.alibou.security.user.model.Permission.ADMIN_UPDATE; -@RequiredArgsConstructor public enum Role { USER(Collections.emptySet()), - ADMIN( - Set.of( - ADMIN_READ, - ADMIN_UPDATE, - ADMIN_DELETE, - ADMIN_CREATE - ) - ); - - @Getter + ADMIN(Set.of(ADMIN_READ, + ADMIN_UPDATE, + ADMIN_DELETE, + ADMIN_CREATE)); + private final Set permissions; + Role(Set permissions) { + this.permissions = permissions; + } + public List getAuthorities() { var authorities = getPermissions() .stream() @@ -38,4 +36,8 @@ public List getAuthorities() { authorities.add(new SimpleGrantedAuthority("ROLE_" + this.name())); return authorities; } + + public Set getPermissions() { + return permissions; + } } diff --git a/src/main/java/com/alibou/security/user/model/User.java b/src/main/java/com/alibou/security/user/model/User.java index f178f46..7280f8c 100644 --- a/src/main/java/com/alibou/security/user/model/User.java +++ b/src/main/java/com/alibou/security/user/model/User.java @@ -5,18 +5,11 @@ import java.util.Collection; import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; + import org.hibernate.annotations.GenericGenerator; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor @Entity @Table(name = "_user") public class User implements UserDetails { @@ -25,9 +18,13 @@ public class User implements UserDetails { @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native") private Integer id; + private String firstname; + private String lastname; + private String email; + private String password; @Enumerated(EnumType.STRING) @@ -70,4 +67,56 @@ public boolean isCredentialsNonExpired() { public boolean isEnabled() { return true; } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setPassword(String password) { + this.password = password; + } + + public Role getRole() { + return role; + } + + public void setRole(Role role) { + this.role = role; + } + + public List getTokens() { + return tokens; + } + + public void setTokens(List tokens) { + this.tokens = tokens; + } } diff --git a/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java b/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java index 4b00710..3983f31 100644 --- a/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java +++ b/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java @@ -13,28 +13,33 @@ import java.security.Principal; @Service -@RequiredArgsConstructor public class UserServiceImpl implements UserService { private final PasswordEncoder passwordEncoder; private final UserRepository repository; + public UserServiceImpl(PasswordEncoder passwordEncoder, + UserRepository repository) { + this.passwordEncoder = passwordEncoder; + this.repository = repository; + } + @Override public void changePassword(@NotNull ChangePasswordRequest request, @NotNull Principal connectedUser) { var user = (User) ((UsernamePasswordAuthenticationToken) connectedUser).getPrincipal(); // check if the current password is correct - if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) { + if (!passwordEncoder.matches(request.currentPassword(), user.getPassword())) { throw new IllegalStateException("Wrong password"); } // check if the two new passwords are the same - if (!request.getNewPassword().equals(request.getConfirmationPassword())) { + if (!request.newPassword().equals(request.confirmationPassword())) { throw new IllegalStateException("Password are not the same"); } // update the password - user.setPassword(passwordEncoder.encode(request.getNewPassword())); + user.setPassword(passwordEncoder.encode(request.newPassword())); // save the new password repository.save(user); From 65a1a1ee10b9d690ed81424c19fea45e3eceb062 Mon Sep 17 00:00:00 2001 From: Yusuf ATALAR Date: Sun, 13 Oct 2024 13:22:21 +0300 Subject: [PATCH 3/6] exception handler --- .../config/GlobalExceptionHandler.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/main/java/com/alibou/security/config/GlobalExceptionHandler.java diff --git a/src/main/java/com/alibou/security/config/GlobalExceptionHandler.java b/src/main/java/com/alibou/security/config/GlobalExceptionHandler.java new file mode 100644 index 0000000..3f3bb20 --- /dev/null +++ b/src/main/java/com/alibou/security/config/GlobalExceptionHandler.java @@ -0,0 +1,19 @@ +package com.alibou.security.config; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.ErrorResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.WebRequest; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGlobalException(Exception ex, WebRequest request) { + final ErrorResponse errorDetails = ErrorResponse.create(ex, HttpStatus.INTERNAL_SERVER_ERROR, ex.getMessage()); + return new ResponseEntity<>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR); + } + +} From fbe7dd679af0fcaefe4074772042cc555bf568c9 Mon Sep 17 00:00:00 2001 From: Yusuf ATALAR Date: Sun, 13 Oct 2024 14:42:05 +0300 Subject: [PATCH 4/6] id change to big int --- .../com/alibou/security/auth/model/Token.java | 21 ++++++++++++++++--- .../auth/repository/TokenRepository.java | 2 +- .../auth/service/ApplicationAuditAware.java | 4 ++-- .../security/config/ApplicationConfig.java | 3 +-- .../com/alibou/security/user/model/User.java | 20 +++++++++++++++--- 5 files changed, 39 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/alibou/security/auth/model/Token.java b/src/main/java/com/alibou/security/auth/model/Token.java index bd32fa3..062b748 100644 --- a/src/main/java/com/alibou/security/auth/model/Token.java +++ b/src/main/java/com/alibou/security/auth/model/Token.java @@ -4,6 +4,8 @@ import jakarta.persistence.*; import org.hibernate.annotations.GenericGenerator; +import java.util.Objects; + @Entity @Table(name = "token") public class Token { @@ -11,7 +13,7 @@ public class Token { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native") - public Integer id; + public Long id; @Column(unique = true) public String token; @@ -27,11 +29,11 @@ public class Token { @JoinColumn(name = "user_id") public User user; - public Integer getId() { + public Long getId() { return id; } - public void setId(Integer id) { + public void setId(Long id) { this.id = id; } @@ -74,4 +76,17 @@ public User getUser() { public void setUser(User user) { this.user = user; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Token token1 = (Token) o; + return revoked == token1.revoked && expired == token1.expired && Objects.equals(id, token1.id) && Objects.equals(token, token1.token) && tokenType == token1.tokenType && Objects.equals(user, token1.user); + } + + @Override + public int hashCode() { + return Objects.hash(id, token, tokenType, revoked, expired, user); + } } diff --git a/src/main/java/com/alibou/security/auth/repository/TokenRepository.java b/src/main/java/com/alibou/security/auth/repository/TokenRepository.java index 353de33..a3710be 100644 --- a/src/main/java/com/alibou/security/auth/repository/TokenRepository.java +++ b/src/main/java/com/alibou/security/auth/repository/TokenRepository.java @@ -14,7 +14,7 @@ public interface TokenRepository extends JpaRepository { on t.user.id = u.id\s where u.id = :id and (t.expired = false or t.revoked = false)\s """) - List findAllValidTokenByUser(Integer id); + List findAllValidTokenByUser(Long id); Optional findByToken(String token); } diff --git a/src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java b/src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java index e499f4c..68339d0 100644 --- a/src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java +++ b/src/main/java/com/alibou/security/auth/service/ApplicationAuditAware.java @@ -8,10 +8,10 @@ import java.util.Optional; -public class ApplicationAuditAware implements AuditorAware { +public class ApplicationAuditAware implements AuditorAware { @Override - public Optional getCurrentAuditor() { + public Optional getCurrentAuditor() { Authentication authentication = SecurityContextHolder .getContext() diff --git a/src/main/java/com/alibou/security/config/ApplicationConfig.java b/src/main/java/com/alibou/security/config/ApplicationConfig.java index 7d50e95..b911888 100644 --- a/src/main/java/com/alibou/security/config/ApplicationConfig.java +++ b/src/main/java/com/alibou/security/config/ApplicationConfig.java @@ -2,7 +2,6 @@ import com.alibou.security.auth.service.ApplicationAuditAware; import com.alibou.security.user.repository.UserRepository; -import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.domain.AuditorAware; @@ -39,7 +38,7 @@ public AuthenticationProvider authenticationProvider() { } @Bean - public AuditorAware auditorAware() { + public AuditorAware auditorAware() { return new ApplicationAuditAware(); } diff --git a/src/main/java/com/alibou/security/user/model/User.java b/src/main/java/com/alibou/security/user/model/User.java index 7280f8c..1c64404 100644 --- a/src/main/java/com/alibou/security/user/model/User.java +++ b/src/main/java/com/alibou/security/user/model/User.java @@ -5,6 +5,7 @@ import java.util.Collection; import java.util.List; +import java.util.Objects; import org.hibernate.annotations.GenericGenerator; import org.springframework.security.core.GrantedAuthority; @@ -17,7 +18,7 @@ public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "native") @GenericGenerator(name = "native") - private Integer id; + private Long id; private String firstname; @@ -68,11 +69,11 @@ public boolean isEnabled() { return true; } - public Integer getId() { + public Long getId() { return id; } - public void setId(Integer id) { + public void setId(Long id) { this.id = id; } @@ -119,4 +120,17 @@ public List getTokens() { public void setTokens(List tokens) { this.tokens = tokens; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && Objects.equals(firstname, user.firstname) && Objects.equals(lastname, user.lastname) && Objects.equals(email, user.email) && Objects.equals(password, user.password) && role == user.role && Objects.equals(tokens, user.tokens); + } + + @Override + public int hashCode() { + return Objects.hash(id, firstname, lastname, email, password, role, tokens); + } } From 48682fb7eee8cdd5f694a761487f0ba3679c46ab Mon Sep 17 00:00:00 2001 From: Yusuf ATALAR Date: Sun, 13 Oct 2024 19:25:09 +0300 Subject: [PATCH 5/6] Stock process --- .../inventory/controller/StockController.java | 33 +++++++ .../security/inventory/model/Stock.java | 44 +++++++++ .../inventory/model/StockChangeRecord.java | 95 +++++++++++++++++++ .../model/StockChangeRecordRequest.java | 6 ++ .../inventory/model/StockChangeType.java | 7 ++ .../security/inventory/model/StockId.java | 48 ++++++++++ .../inventory/model/UploadStockRequest.java | 4 + .../StockChangeRecordRepository.java | 9 ++ .../inventory/repository/StockRepository.java | 10 ++ .../service/SnowFlakeIdGenerator.java | 9 ++ .../service/StockChangeRecordService.java | 12 +++ .../inventory/service/StockService.java | 14 +++ .../impl/StockChangeRecordServiceImpl.java | 44 +++++++++ ...StockChangeRecordSnowFlakeIdGenerator.java | 63 ++++++++++++ .../service/impl/StockServiceImpl.java | 73 ++++++++++++++ .../security/product/model/Product.java | 39 ++++++++ .../product/repository/ProductRepository.java | 9 ++ .../product/service/ProductService.java | 11 +++ .../product/service/ProductServiceImpl.java | 29 ++++++ 19 files changed, 559 insertions(+) create mode 100644 src/main/java/com/alibou/security/inventory/controller/StockController.java create mode 100644 src/main/java/com/alibou/security/inventory/model/Stock.java create mode 100644 src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java create mode 100644 src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java create mode 100644 src/main/java/com/alibou/security/inventory/model/StockChangeType.java create mode 100644 src/main/java/com/alibou/security/inventory/model/StockId.java create mode 100644 src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java create mode 100644 src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java create mode 100644 src/main/java/com/alibou/security/inventory/repository/StockRepository.java create mode 100644 src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java create mode 100644 src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java create mode 100644 src/main/java/com/alibou/security/inventory/service/StockService.java create mode 100644 src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java create mode 100644 src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java create mode 100644 src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java create mode 100644 src/main/java/com/alibou/security/product/model/Product.java create mode 100644 src/main/java/com/alibou/security/product/repository/ProductRepository.java create mode 100644 src/main/java/com/alibou/security/product/service/ProductService.java create mode 100644 src/main/java/com/alibou/security/product/service/ProductServiceImpl.java diff --git a/src/main/java/com/alibou/security/inventory/controller/StockController.java b/src/main/java/com/alibou/security/inventory/controller/StockController.java new file mode 100644 index 0000000..8582015 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/controller/StockController.java @@ -0,0 +1,33 @@ +package com.alibou.security.inventory.controller; + +import com.alibou.security.inventory.model.Stock; +import com.alibou.security.inventory.model.UploadStockRequest; +import com.alibou.security.inventory.service.StockService; +import com.alibou.security.user.model.User; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/stock") +public class StockController { + + private final StockService service; + + public StockController(StockService service) { + this.service = service; + } + + @GetMapping("/{barcode}") + public Stock get(@AuthenticationPrincipal User user, + @PathVariable String barcode) { + return service.get(user.getId(), barcode); + } + + @PutMapping("/{barcode}") + public Stock upload(@AuthenticationPrincipal User user, + @PathVariable String barcode, + @RequestBody UploadStockRequest request) { + return service.upload(user.getId(), barcode, request); + } + +} diff --git a/src/main/java/com/alibou/security/inventory/model/Stock.java b/src/main/java/com/alibou/security/inventory/model/Stock.java new file mode 100644 index 0000000..8698f2f --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/model/Stock.java @@ -0,0 +1,44 @@ +package com.alibou.security.inventory.model; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; + +@Entity +@Table(name = "stocks") +@IdClass(StockId.class) +public class Stock { + + @Id + private long userId; + + @Id + private String barcode; + + private long quantity; + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public String getBarcode() { + return barcode; + } + + public void setBarcode(String barcode) { + this.barcode = barcode; + } + + public long getQuantity() { + return quantity; + } + + public void setQuantity(long quantity) { + this.quantity = quantity; + } +} diff --git a/src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java b/src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java new file mode 100644 index 0000000..3fc34b5 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java @@ -0,0 +1,95 @@ +package com.alibou.security.inventory.model; + +import jakarta.persistence.*; + +import java.util.Objects; + +@Entity +@Table(name = "stocks_change_records") +public class StockChangeRecord { + + @Id + private Long id; + + private long userId; + + private String barcode; + + private int qty; + + private long oldQuantity; + + private long currentQuantity; + + @Enumerated + private StockChangeType changeType; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public String getBarcode() { + return barcode; + } + + public void setBarcode(String barcode) { + this.barcode = barcode; + } + + public int getQty() { + return qty; + } + + public void setQty(int qty) { + this.qty = qty; + } + + public long getOldQuantity() { + return oldQuantity; + } + + public void setOldQuantity(long oldQuantity) { + this.oldQuantity = oldQuantity; + } + + public long getCurrentQuantity() { + return currentQuantity; + } + + public void setCurrentQuantity(long currentQuantity) { + this.currentQuantity = currentQuantity; + } + + public StockChangeType getChangeType() { + return changeType; + } + + public void setChangeType(StockChangeType changeType) { + this.changeType = changeType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StockChangeRecord that = (StockChangeRecord) o; + return userId == that.userId && qty == that.qty && oldQuantity == that.oldQuantity && currentQuantity == that.currentQuantity && Objects.equals(id, that.id) && Objects.equals(barcode, that.barcode) && changeType == that.changeType; + } + + @Override + public int hashCode() { + return Objects.hash(id, userId, barcode, qty, oldQuantity, currentQuantity, changeType); + } +} diff --git a/src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java b/src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java new file mode 100644 index 0000000..ee77d11 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java @@ -0,0 +1,6 @@ +package com.alibou.security.inventory.model; + +import org.jetbrains.annotations.NotNull; + +public record StockChangeRecordRequest(long oldQty, long currentQty, int qty, @NotNull String barcode) { +} diff --git a/src/main/java/com/alibou/security/inventory/model/StockChangeType.java b/src/main/java/com/alibou/security/inventory/model/StockChangeType.java new file mode 100644 index 0000000..8442724 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/model/StockChangeType.java @@ -0,0 +1,7 @@ +package com.alibou.security.inventory.model; + +public enum StockChangeType { + + UPLOAD, + +} diff --git a/src/main/java/com/alibou/security/inventory/model/StockId.java b/src/main/java/com/alibou/security/inventory/model/StockId.java new file mode 100644 index 0000000..26bf7e4 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/model/StockId.java @@ -0,0 +1,48 @@ +package com.alibou.security.inventory.model; + +import java.io.Serializable; +import java.util.Objects; + +public class StockId implements Serializable { + + private long userId; + + private String barcode; + + public StockId(long userId, String barcode) { + this.userId = userId; + this.barcode = barcode; + } + + public StockId() { + } + + public long getUserId() { + return userId; + } + + public void setUserId(long userId) { + this.userId = userId; + } + + public String getBarcode() { + return barcode; + } + + public void setBarcode(String barcode) { + this.barcode = barcode; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StockId stockId = (StockId) o; + return userId == stockId.userId && Objects.equals(barcode, stockId.barcode); + } + + @Override + public int hashCode() { + return Objects.hash(userId, barcode); + } +} diff --git a/src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java b/src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java new file mode 100644 index 0000000..d51fe58 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java @@ -0,0 +1,4 @@ +package com.alibou.security.inventory.model; + +public record UploadStockRequest(int qty) { +} diff --git a/src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java b/src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java new file mode 100644 index 0000000..598e6e0 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java @@ -0,0 +1,9 @@ +package com.alibou.security.inventory.repository; + +import com.alibou.security.inventory.model.StockChangeRecord; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface StockChangeRecordRepository extends JpaRepository { +} diff --git a/src/main/java/com/alibou/security/inventory/repository/StockRepository.java b/src/main/java/com/alibou/security/inventory/repository/StockRepository.java new file mode 100644 index 0000000..170eb81 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/repository/StockRepository.java @@ -0,0 +1,10 @@ +package com.alibou.security.inventory.repository; + +import com.alibou.security.inventory.model.Stock; +import com.alibou.security.inventory.model.StockId; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface StockRepository extends JpaRepository { +} diff --git a/src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java b/src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java new file mode 100644 index 0000000..57bec46 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java @@ -0,0 +1,9 @@ +package com.alibou.security.inventory.service; + +public interface SnowFlakeIdGenerator { + long generateId(); + + long extractEpochSecondsFromSnowflakeId(long snowflakeId); + + long getThresholdIdForSecondsAgo(long seconds); +} diff --git a/src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java b/src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java new file mode 100644 index 0000000..6e8ed6a --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java @@ -0,0 +1,12 @@ +package com.alibou.security.inventory.service; + +import com.alibou.security.inventory.model.StockChangeRecord; +import com.alibou.security.inventory.model.StockChangeRecordRequest; +import com.alibou.security.inventory.model.StockChangeType; +import org.jetbrains.annotations.NotNull; + +public interface StockChangeRecordService { + + @NotNull + StockChangeRecord save(long userId, @NotNull StockChangeType type, @NotNull StockChangeRecordRequest request); +} diff --git a/src/main/java/com/alibou/security/inventory/service/StockService.java b/src/main/java/com/alibou/security/inventory/service/StockService.java new file mode 100644 index 0000000..6905102 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/service/StockService.java @@ -0,0 +1,14 @@ +package com.alibou.security.inventory.service; + +import com.alibou.security.inventory.model.Stock; +import com.alibou.security.inventory.model.UploadStockRequest; +import org.jetbrains.annotations.NotNull; + +public interface StockService { + + @NotNull + Stock upload(long userId, @NotNull String barcode, @NotNull UploadStockRequest uploadRequest); + + @NotNull + Stock get(long userId, @NotNull String barcode); +} diff --git a/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java b/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java new file mode 100644 index 0000000..d8bc791 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java @@ -0,0 +1,44 @@ +package com.alibou.security.inventory.service.impl; + +import com.alibou.security.inventory.model.StockChangeRecord; +import com.alibou.security.inventory.model.StockChangeRecordRequest; +import com.alibou.security.inventory.model.StockChangeType; +import com.alibou.security.inventory.repository.StockChangeRecordRepository; +import com.alibou.security.inventory.service.SnowFlakeIdGenerator; +import com.alibou.security.inventory.service.StockChangeRecordService; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; + +@Service +public class StockChangeRecordServiceImpl implements StockChangeRecordService { + + private final Logger logger = LoggerFactory.getLogger(StockChangeRecordServiceImpl.class); + + private final StockChangeRecordRepository repository; + private final SnowFlakeIdGenerator snowFlakeIdGenerator; + + public StockChangeRecordServiceImpl(StockChangeRecordRepository repository, + @Qualifier("stockChangeRecordSnowFlakeIdGenerator") SnowFlakeIdGenerator snowFlakeIdGenerator) { + this.repository = repository; + this.snowFlakeIdGenerator = snowFlakeIdGenerator; + } + + @NotNull + @Override + public StockChangeRecord save(long userId, @NotNull StockChangeType type, @NotNull StockChangeRecordRequest request) { + logger.info("User = {} saving stock change record with for type = {} and request = {}", userId, type, request); + final StockChangeRecord record = new StockChangeRecord(); + final long id = snowFlakeIdGenerator.generateId(); + record.setId(id); + record.setUserId(userId); + record.setBarcode(request.barcode()); + record.setChangeType(type); + record.setOldQuantity(request.oldQty()); + record.setCurrentQuantity(request.currentQty()); + record.setQty(request.qty()); + return repository.save(record); + } +} diff --git a/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java b/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java new file mode 100644 index 0000000..71f41f7 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java @@ -0,0 +1,63 @@ +package com.alibou.security.inventory.service.impl; + +import com.alibou.security.inventory.service.SnowFlakeIdGenerator; +import org.springframework.stereotype.Component; + +@Component(value = "stockChangeRecordSnowFlakeIdGenerator") +public class StockChangeRecordSnowFlakeIdGenerator implements SnowFlakeIdGenerator { + + private static final long OFFSET_EPOCH = 1728834116000L; + private static final long SEQUENCE_BITS = 19L; + private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS); + private long sequence = 0L; + private long lastTimestamp = -1L; + + @Override + public synchronized long generateId() { + long timestamp = System.currentTimeMillis(); + if (timestamp < lastTimestamp) { + throw new RuntimeException("Clock moved backwards. Refusing to generate id"); + } + if (timestamp == lastTimestamp) { + sequence++; + long seq = sequence & SEQUENCE_MASK; + if (seq == 0) { + timestamp = waitForNextMillis(lastTimestamp); + sequence = 0; + } + } else { + sequence = 0; + } + lastTimestamp = timestamp; + return ((timestamp - OFFSET_EPOCH) << SEQUENCE_BITS) + | sequence; + } + + @Override + public long extractEpochSecondsFromSnowflakeId(long snowflakeId) { + long timestampPart = snowflakeId >> SEQUENCE_BITS; + long actualTimestamp = timestampPart + OFFSET_EPOCH; + return actualTimestamp / 1000; + } + + @Override + public long getThresholdIdForSecondsAgo(long seconds) { + long sixMonthsAgoTimestamp = System.currentTimeMillis() - (seconds * 1000); + long adjustedTimestamp = sixMonthsAgoTimestamp - OFFSET_EPOCH; + return adjustedTimestamp << SEQUENCE_BITS; + } + + private long waitForNextMillis(long lastTimestamp) { + long timestamp = System.currentTimeMillis(); + while (timestamp <= lastTimestamp) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for next millisecond", e); + } + timestamp = System.currentTimeMillis(); + } + return timestamp; + } +} diff --git a/src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java b/src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java new file mode 100644 index 0000000..7413c76 --- /dev/null +++ b/src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java @@ -0,0 +1,73 @@ +package com.alibou.security.inventory.service.impl; + +import com.alibou.security.inventory.model.*; +import com.alibou.security.inventory.repository.StockRepository; +import com.alibou.security.inventory.service.StockChangeRecordService; +import com.alibou.security.inventory.service.StockService; +import com.alibou.security.product.service.ProductService; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.concurrent.atomic.AtomicLong; + +@Service +public class StockServiceImpl implements StockService { + + private final Logger logger = LoggerFactory.getLogger(StockServiceImpl.class); + private final StockRepository repository; + private final ProductService productService; + private final StockChangeRecordService stockChangeRecordService; + + public StockServiceImpl(StockRepository repository, + ProductService productService, + StockChangeRecordService stockChangeRecordService) { + this.repository = repository; + this.productService = productService; + this.stockChangeRecordService = stockChangeRecordService; + } + + @Transactional + @NotNull + @Override + public Stock upload(long userId, @NotNull String barcode, @NotNull UploadStockRequest uploadRequest) { + logger.info("User = {} uploading stock with for product = {} request = {}", userId, barcode, uploadRequest); + final int qty = uploadRequest.qty(); + final StockId stockId = new StockId(userId, barcode); + productService.getProduct(userId, barcode); + final AtomicLong oldQty = new AtomicLong(0); + final Stock stock = repository.findById(stockId).map(existStock -> { + final long existQty = existStock.getQuantity(); + oldQty.set(existQty); + existStock.setQuantity(existQty + qty); + return existStock; + }).orElseGet(() -> { + logger.info("Stock for product = {} does not exist. New stock created", barcode); + final Stock newStock = new Stock(); + newStock.setUserId(userId); + newStock.setBarcode(barcode); + newStock.setQuantity(qty); + return newStock; + }); + final StockChangeRecordRequest stockChangeRecordRequest = new StockChangeRecordRequest(oldQty.get(), stock.getQuantity(), uploadRequest.qty(), barcode); + stockChangeRecordService.save(userId, StockChangeType.UPLOAD, stockChangeRecordRequest); + return repository.save(stock); + } + + @NotNull + @Override + public Stock get(long userId, @NotNull String barcode) { + logger.info("User = {} getting stock = {}", userId, barcode); + productService.getProduct(userId, barcode); + return repository.findById(new StockId(userId, barcode)) + .orElseGet(() -> { + logger.info("Stock for product = {} does not exist. Empty stock created", barcode); + final Stock newStock = new Stock(); + newStock.setUserId(userId); + newStock.setBarcode(barcode); + return repository.save(newStock); + }); + } +} diff --git a/src/main/java/com/alibou/security/product/model/Product.java b/src/main/java/com/alibou/security/product/model/Product.java new file mode 100644 index 0000000..7482618 --- /dev/null +++ b/src/main/java/com/alibou/security/product/model/Product.java @@ -0,0 +1,39 @@ +package com.alibou.security.product.model; + +import jakarta.persistence.*; + +@Entity +@Table(name = "products") +public class Product { + + @Id + private String barcode; + + private String name; + + private String description; + + public String getBarcode() { + return barcode; + } + + public void setBarcode(String barcode) { + this.barcode = barcode; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/src/main/java/com/alibou/security/product/repository/ProductRepository.java b/src/main/java/com/alibou/security/product/repository/ProductRepository.java new file mode 100644 index 0000000..885aa83 --- /dev/null +++ b/src/main/java/com/alibou/security/product/repository/ProductRepository.java @@ -0,0 +1,9 @@ +package com.alibou.security.product.repository; + +import com.alibou.security.product.model.Product; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ProductRepository extends JpaRepository { +} diff --git a/src/main/java/com/alibou/security/product/service/ProductService.java b/src/main/java/com/alibou/security/product/service/ProductService.java new file mode 100644 index 0000000..14d728a --- /dev/null +++ b/src/main/java/com/alibou/security/product/service/ProductService.java @@ -0,0 +1,11 @@ +package com.alibou.security.product.service; + +import com.alibou.security.product.model.Product; +import org.jetbrains.annotations.NotNull; + +public interface ProductService { + + @NotNull + Product getProduct(long userId, @NotNull String barcode); + +} diff --git a/src/main/java/com/alibou/security/product/service/ProductServiceImpl.java b/src/main/java/com/alibou/security/product/service/ProductServiceImpl.java new file mode 100644 index 0000000..5888112 --- /dev/null +++ b/src/main/java/com/alibou/security/product/service/ProductServiceImpl.java @@ -0,0 +1,29 @@ +package com.alibou.security.product.service; + +import com.alibou.security.product.model.Product; +import com.alibou.security.product.repository.ProductRepository; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +@Service +public class ProductServiceImpl implements ProductService { + + private final Logger logger = LoggerFactory.getLogger(ProductServiceImpl.class); + private final ProductRepository repository; + + public ProductServiceImpl(ProductRepository repository) { + this.repository = repository; + } + + @NotNull + @Override + public Product getProduct(long userId, @NotNull String barcode) { + logger.info("User = {} request product for barcode = {}", userId, barcode); + return repository.findById(barcode).orElseThrow(() -> { + final String errorMessage = String.format("Product not found for barcode %s", barcode); + return new RuntimeException(errorMessage); + }); + } +} From 5187262fb7a79740a61ad3df487b50be386c5a81 Mon Sep 17 00:00:00 2001 From: Yusuf ATALAR Date: Sun, 10 Nov 2024 13:17:17 +0300 Subject: [PATCH 6/6] clear redundant files --- pom.xml | 15 --- .../controller/AuthenticationController.java | 1 - .../auth/service/AuthenticationService.java | 2 - .../config/JwtAuthenticationFilter.java | 1 - .../config/SecurityConfiguration.java | 1 - .../inventory/controller/StockController.java | 33 ------- .../security/inventory/model/Stock.java | 44 --------- .../inventory/model/StockChangeRecord.java | 95 ------------------- .../model/StockChangeRecordRequest.java | 6 -- .../inventory/model/StockChangeType.java | 7 -- .../security/inventory/model/StockId.java | 48 ---------- .../inventory/model/UploadStockRequest.java | 4 - .../StockChangeRecordRepository.java | 9 -- .../inventory/repository/StockRepository.java | 10 -- .../service/SnowFlakeIdGenerator.java | 9 -- .../service/StockChangeRecordService.java | 12 --- .../inventory/service/StockService.java | 14 --- .../impl/StockChangeRecordServiceImpl.java | 44 --------- ...StockChangeRecordSnowFlakeIdGenerator.java | 63 ------------ .../service/impl/StockServiceImpl.java | 73 -------------- .../security/product/model/Product.java | 39 -------- .../product/repository/ProductRepository.java | 9 -- .../product/service/ProductService.java | 11 --- .../product/service/ProductServiceImpl.java | 29 ------ .../user/controller/UserController.java | 2 - .../com/alibou/security/user/model/Role.java | 2 - .../user/service/impl/UserServiceImpl.java | 1 - 27 files changed, 584 deletions(-) delete mode 100644 src/main/java/com/alibou/security/inventory/controller/StockController.java delete mode 100644 src/main/java/com/alibou/security/inventory/model/Stock.java delete mode 100644 src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java delete mode 100644 src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java delete mode 100644 src/main/java/com/alibou/security/inventory/model/StockChangeType.java delete mode 100644 src/main/java/com/alibou/security/inventory/model/StockId.java delete mode 100644 src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java delete mode 100644 src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java delete mode 100644 src/main/java/com/alibou/security/inventory/repository/StockRepository.java delete mode 100644 src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java delete mode 100644 src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java delete mode 100644 src/main/java/com/alibou/security/inventory/service/StockService.java delete mode 100644 src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java delete mode 100644 src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java delete mode 100644 src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java delete mode 100644 src/main/java/com/alibou/security/product/model/Product.java delete mode 100644 src/main/java/com/alibou/security/product/repository/ProductRepository.java delete mode 100644 src/main/java/com/alibou/security/product/service/ProductService.java delete mode 100644 src/main/java/com/alibou/security/product/service/ProductServiceImpl.java diff --git a/pom.xml b/pom.xml index 98ff86a..a90be1a 100644 --- a/pom.xml +++ b/pom.xml @@ -40,11 +40,6 @@ mysql-connector-j runtime - - org.projectlombok - lombok - true - io.jsonwebtoken jjwt-api @@ -70,16 +65,6 @@ spring-boot-starter-validation - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.security - spring-security-test - test - diff --git a/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java b/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java index 3031e0d..4c3a7fd 100644 --- a/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java +++ b/src/main/java/com/alibou/security/auth/controller/AuthenticationController.java @@ -6,7 +6,6 @@ import com.alibou.security.auth.model.RegisterRequest; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; diff --git a/src/main/java/com/alibou/security/auth/service/AuthenticationService.java b/src/main/java/com/alibou/security/auth/service/AuthenticationService.java index ac7ef3a..423da9c 100644 --- a/src/main/java/com/alibou/security/auth/service/AuthenticationService.java +++ b/src/main/java/com/alibou/security/auth/service/AuthenticationService.java @@ -13,7 +13,6 @@ import jakarta.persistence.EntityExistsException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,7 +27,6 @@ @Service public class AuthenticationService { - private final Logger logger = LoggerFactory.getLogger(AuthenticationService.class); private final UserRepository repository; private final TokenRepository tokenRepository; private final PasswordEncoder passwordEncoder; diff --git a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java index 71b519d..143c4a2 100644 --- a/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java +++ b/src/main/java/com/alibou/security/config/JwtAuthenticationFilter.java @@ -8,7 +8,6 @@ import java.io.IOException; -import lombok.RequiredArgsConstructor; import org.springframework.lang.NonNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; diff --git a/src/main/java/com/alibou/security/config/SecurityConfiguration.java b/src/main/java/com/alibou/security/config/SecurityConfiguration.java index d8bbf87..1ca2cd7 100644 --- a/src/main/java/com/alibou/security/config/SecurityConfiguration.java +++ b/src/main/java/com/alibou/security/config/SecurityConfiguration.java @@ -1,6 +1,5 @@ package com.alibou.security.config; -import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationProvider; diff --git a/src/main/java/com/alibou/security/inventory/controller/StockController.java b/src/main/java/com/alibou/security/inventory/controller/StockController.java deleted file mode 100644 index 8582015..0000000 --- a/src/main/java/com/alibou/security/inventory/controller/StockController.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.alibou.security.inventory.controller; - -import com.alibou.security.inventory.model.Stock; -import com.alibou.security.inventory.model.UploadStockRequest; -import com.alibou.security.inventory.service.StockService; -import com.alibou.security.user.model.User; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequestMapping("/api/v1/stock") -public class StockController { - - private final StockService service; - - public StockController(StockService service) { - this.service = service; - } - - @GetMapping("/{barcode}") - public Stock get(@AuthenticationPrincipal User user, - @PathVariable String barcode) { - return service.get(user.getId(), barcode); - } - - @PutMapping("/{barcode}") - public Stock upload(@AuthenticationPrincipal User user, - @PathVariable String barcode, - @RequestBody UploadStockRequest request) { - return service.upload(user.getId(), barcode, request); - } - -} diff --git a/src/main/java/com/alibou/security/inventory/model/Stock.java b/src/main/java/com/alibou/security/inventory/model/Stock.java deleted file mode 100644 index 8698f2f..0000000 --- a/src/main/java/com/alibou/security/inventory/model/Stock.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.alibou.security.inventory.model; - -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.IdClass; -import jakarta.persistence.Table; - -@Entity -@Table(name = "stocks") -@IdClass(StockId.class) -public class Stock { - - @Id - private long userId; - - @Id - private String barcode; - - private long quantity; - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } - - public String getBarcode() { - return barcode; - } - - public void setBarcode(String barcode) { - this.barcode = barcode; - } - - public long getQuantity() { - return quantity; - } - - public void setQuantity(long quantity) { - this.quantity = quantity; - } -} diff --git a/src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java b/src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java deleted file mode 100644 index 3fc34b5..0000000 --- a/src/main/java/com/alibou/security/inventory/model/StockChangeRecord.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.alibou.security.inventory.model; - -import jakarta.persistence.*; - -import java.util.Objects; - -@Entity -@Table(name = "stocks_change_records") -public class StockChangeRecord { - - @Id - private Long id; - - private long userId; - - private String barcode; - - private int qty; - - private long oldQuantity; - - private long currentQuantity; - - @Enumerated - private StockChangeType changeType; - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } - - public String getBarcode() { - return barcode; - } - - public void setBarcode(String barcode) { - this.barcode = barcode; - } - - public int getQty() { - return qty; - } - - public void setQty(int qty) { - this.qty = qty; - } - - public long getOldQuantity() { - return oldQuantity; - } - - public void setOldQuantity(long oldQuantity) { - this.oldQuantity = oldQuantity; - } - - public long getCurrentQuantity() { - return currentQuantity; - } - - public void setCurrentQuantity(long currentQuantity) { - this.currentQuantity = currentQuantity; - } - - public StockChangeType getChangeType() { - return changeType; - } - - public void setChangeType(StockChangeType changeType) { - this.changeType = changeType; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StockChangeRecord that = (StockChangeRecord) o; - return userId == that.userId && qty == that.qty && oldQuantity == that.oldQuantity && currentQuantity == that.currentQuantity && Objects.equals(id, that.id) && Objects.equals(barcode, that.barcode) && changeType == that.changeType; - } - - @Override - public int hashCode() { - return Objects.hash(id, userId, barcode, qty, oldQuantity, currentQuantity, changeType); - } -} diff --git a/src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java b/src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java deleted file mode 100644 index ee77d11..0000000 --- a/src/main/java/com/alibou/security/inventory/model/StockChangeRecordRequest.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.alibou.security.inventory.model; - -import org.jetbrains.annotations.NotNull; - -public record StockChangeRecordRequest(long oldQty, long currentQty, int qty, @NotNull String barcode) { -} diff --git a/src/main/java/com/alibou/security/inventory/model/StockChangeType.java b/src/main/java/com/alibou/security/inventory/model/StockChangeType.java deleted file mode 100644 index 8442724..0000000 --- a/src/main/java/com/alibou/security/inventory/model/StockChangeType.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.alibou.security.inventory.model; - -public enum StockChangeType { - - UPLOAD, - -} diff --git a/src/main/java/com/alibou/security/inventory/model/StockId.java b/src/main/java/com/alibou/security/inventory/model/StockId.java deleted file mode 100644 index 26bf7e4..0000000 --- a/src/main/java/com/alibou/security/inventory/model/StockId.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.alibou.security.inventory.model; - -import java.io.Serializable; -import java.util.Objects; - -public class StockId implements Serializable { - - private long userId; - - private String barcode; - - public StockId(long userId, String barcode) { - this.userId = userId; - this.barcode = barcode; - } - - public StockId() { - } - - public long getUserId() { - return userId; - } - - public void setUserId(long userId) { - this.userId = userId; - } - - public String getBarcode() { - return barcode; - } - - public void setBarcode(String barcode) { - this.barcode = barcode; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - StockId stockId = (StockId) o; - return userId == stockId.userId && Objects.equals(barcode, stockId.barcode); - } - - @Override - public int hashCode() { - return Objects.hash(userId, barcode); - } -} diff --git a/src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java b/src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java deleted file mode 100644 index d51fe58..0000000 --- a/src/main/java/com/alibou/security/inventory/model/UploadStockRequest.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.alibou.security.inventory.model; - -public record UploadStockRequest(int qty) { -} diff --git a/src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java b/src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java deleted file mode 100644 index 598e6e0..0000000 --- a/src/main/java/com/alibou/security/inventory/repository/StockChangeRecordRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.alibou.security.inventory.repository; - -import com.alibou.security.inventory.model.StockChangeRecord; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface StockChangeRecordRepository extends JpaRepository { -} diff --git a/src/main/java/com/alibou/security/inventory/repository/StockRepository.java b/src/main/java/com/alibou/security/inventory/repository/StockRepository.java deleted file mode 100644 index 170eb81..0000000 --- a/src/main/java/com/alibou/security/inventory/repository/StockRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.alibou.security.inventory.repository; - -import com.alibou.security.inventory.model.Stock; -import com.alibou.security.inventory.model.StockId; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface StockRepository extends JpaRepository { -} diff --git a/src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java b/src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java deleted file mode 100644 index 57bec46..0000000 --- a/src/main/java/com/alibou/security/inventory/service/SnowFlakeIdGenerator.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.alibou.security.inventory.service; - -public interface SnowFlakeIdGenerator { - long generateId(); - - long extractEpochSecondsFromSnowflakeId(long snowflakeId); - - long getThresholdIdForSecondsAgo(long seconds); -} diff --git a/src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java b/src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java deleted file mode 100644 index 6e8ed6a..0000000 --- a/src/main/java/com/alibou/security/inventory/service/StockChangeRecordService.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.alibou.security.inventory.service; - -import com.alibou.security.inventory.model.StockChangeRecord; -import com.alibou.security.inventory.model.StockChangeRecordRequest; -import com.alibou.security.inventory.model.StockChangeType; -import org.jetbrains.annotations.NotNull; - -public interface StockChangeRecordService { - - @NotNull - StockChangeRecord save(long userId, @NotNull StockChangeType type, @NotNull StockChangeRecordRequest request); -} diff --git a/src/main/java/com/alibou/security/inventory/service/StockService.java b/src/main/java/com/alibou/security/inventory/service/StockService.java deleted file mode 100644 index 6905102..0000000 --- a/src/main/java/com/alibou/security/inventory/service/StockService.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.alibou.security.inventory.service; - -import com.alibou.security.inventory.model.Stock; -import com.alibou.security.inventory.model.UploadStockRequest; -import org.jetbrains.annotations.NotNull; - -public interface StockService { - - @NotNull - Stock upload(long userId, @NotNull String barcode, @NotNull UploadStockRequest uploadRequest); - - @NotNull - Stock get(long userId, @NotNull String barcode); -} diff --git a/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java b/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java deleted file mode 100644 index d8bc791..0000000 --- a/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordServiceImpl.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.alibou.security.inventory.service.impl; - -import com.alibou.security.inventory.model.StockChangeRecord; -import com.alibou.security.inventory.model.StockChangeRecordRequest; -import com.alibou.security.inventory.model.StockChangeType; -import com.alibou.security.inventory.repository.StockChangeRecordRepository; -import com.alibou.security.inventory.service.SnowFlakeIdGenerator; -import com.alibou.security.inventory.service.StockChangeRecordService; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -@Service -public class StockChangeRecordServiceImpl implements StockChangeRecordService { - - private final Logger logger = LoggerFactory.getLogger(StockChangeRecordServiceImpl.class); - - private final StockChangeRecordRepository repository; - private final SnowFlakeIdGenerator snowFlakeIdGenerator; - - public StockChangeRecordServiceImpl(StockChangeRecordRepository repository, - @Qualifier("stockChangeRecordSnowFlakeIdGenerator") SnowFlakeIdGenerator snowFlakeIdGenerator) { - this.repository = repository; - this.snowFlakeIdGenerator = snowFlakeIdGenerator; - } - - @NotNull - @Override - public StockChangeRecord save(long userId, @NotNull StockChangeType type, @NotNull StockChangeRecordRequest request) { - logger.info("User = {} saving stock change record with for type = {} and request = {}", userId, type, request); - final StockChangeRecord record = new StockChangeRecord(); - final long id = snowFlakeIdGenerator.generateId(); - record.setId(id); - record.setUserId(userId); - record.setBarcode(request.barcode()); - record.setChangeType(type); - record.setOldQuantity(request.oldQty()); - record.setCurrentQuantity(request.currentQty()); - record.setQty(request.qty()); - return repository.save(record); - } -} diff --git a/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java b/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java deleted file mode 100644 index 71f41f7..0000000 --- a/src/main/java/com/alibou/security/inventory/service/impl/StockChangeRecordSnowFlakeIdGenerator.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.alibou.security.inventory.service.impl; - -import com.alibou.security.inventory.service.SnowFlakeIdGenerator; -import org.springframework.stereotype.Component; - -@Component(value = "stockChangeRecordSnowFlakeIdGenerator") -public class StockChangeRecordSnowFlakeIdGenerator implements SnowFlakeIdGenerator { - - private static final long OFFSET_EPOCH = 1728834116000L; - private static final long SEQUENCE_BITS = 19L; - private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS); - private long sequence = 0L; - private long lastTimestamp = -1L; - - @Override - public synchronized long generateId() { - long timestamp = System.currentTimeMillis(); - if (timestamp < lastTimestamp) { - throw new RuntimeException("Clock moved backwards. Refusing to generate id"); - } - if (timestamp == lastTimestamp) { - sequence++; - long seq = sequence & SEQUENCE_MASK; - if (seq == 0) { - timestamp = waitForNextMillis(lastTimestamp); - sequence = 0; - } - } else { - sequence = 0; - } - lastTimestamp = timestamp; - return ((timestamp - OFFSET_EPOCH) << SEQUENCE_BITS) - | sequence; - } - - @Override - public long extractEpochSecondsFromSnowflakeId(long snowflakeId) { - long timestampPart = snowflakeId >> SEQUENCE_BITS; - long actualTimestamp = timestampPart + OFFSET_EPOCH; - return actualTimestamp / 1000; - } - - @Override - public long getThresholdIdForSecondsAgo(long seconds) { - long sixMonthsAgoTimestamp = System.currentTimeMillis() - (seconds * 1000); - long adjustedTimestamp = sixMonthsAgoTimestamp - OFFSET_EPOCH; - return adjustedTimestamp << SEQUENCE_BITS; - } - - private long waitForNextMillis(long lastTimestamp) { - long timestamp = System.currentTimeMillis(); - while (timestamp <= lastTimestamp) { - try { - Thread.sleep(1); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException("Interrupted while waiting for next millisecond", e); - } - timestamp = System.currentTimeMillis(); - } - return timestamp; - } -} diff --git a/src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java b/src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java deleted file mode 100644 index 7413c76..0000000 --- a/src/main/java/com/alibou/security/inventory/service/impl/StockServiceImpl.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.alibou.security.inventory.service.impl; - -import com.alibou.security.inventory.model.*; -import com.alibou.security.inventory.repository.StockRepository; -import com.alibou.security.inventory.service.StockChangeRecordService; -import com.alibou.security.inventory.service.StockService; -import com.alibou.security.product.service.ProductService; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.concurrent.atomic.AtomicLong; - -@Service -public class StockServiceImpl implements StockService { - - private final Logger logger = LoggerFactory.getLogger(StockServiceImpl.class); - private final StockRepository repository; - private final ProductService productService; - private final StockChangeRecordService stockChangeRecordService; - - public StockServiceImpl(StockRepository repository, - ProductService productService, - StockChangeRecordService stockChangeRecordService) { - this.repository = repository; - this.productService = productService; - this.stockChangeRecordService = stockChangeRecordService; - } - - @Transactional - @NotNull - @Override - public Stock upload(long userId, @NotNull String barcode, @NotNull UploadStockRequest uploadRequest) { - logger.info("User = {} uploading stock with for product = {} request = {}", userId, barcode, uploadRequest); - final int qty = uploadRequest.qty(); - final StockId stockId = new StockId(userId, barcode); - productService.getProduct(userId, barcode); - final AtomicLong oldQty = new AtomicLong(0); - final Stock stock = repository.findById(stockId).map(existStock -> { - final long existQty = existStock.getQuantity(); - oldQty.set(existQty); - existStock.setQuantity(existQty + qty); - return existStock; - }).orElseGet(() -> { - logger.info("Stock for product = {} does not exist. New stock created", barcode); - final Stock newStock = new Stock(); - newStock.setUserId(userId); - newStock.setBarcode(barcode); - newStock.setQuantity(qty); - return newStock; - }); - final StockChangeRecordRequest stockChangeRecordRequest = new StockChangeRecordRequest(oldQty.get(), stock.getQuantity(), uploadRequest.qty(), barcode); - stockChangeRecordService.save(userId, StockChangeType.UPLOAD, stockChangeRecordRequest); - return repository.save(stock); - } - - @NotNull - @Override - public Stock get(long userId, @NotNull String barcode) { - logger.info("User = {} getting stock = {}", userId, barcode); - productService.getProduct(userId, barcode); - return repository.findById(new StockId(userId, barcode)) - .orElseGet(() -> { - logger.info("Stock for product = {} does not exist. Empty stock created", barcode); - final Stock newStock = new Stock(); - newStock.setUserId(userId); - newStock.setBarcode(barcode); - return repository.save(newStock); - }); - } -} diff --git a/src/main/java/com/alibou/security/product/model/Product.java b/src/main/java/com/alibou/security/product/model/Product.java deleted file mode 100644 index 7482618..0000000 --- a/src/main/java/com/alibou/security/product/model/Product.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.alibou.security.product.model; - -import jakarta.persistence.*; - -@Entity -@Table(name = "products") -public class Product { - - @Id - private String barcode; - - private String name; - - private String description; - - public String getBarcode() { - return barcode; - } - - public void setBarcode(String barcode) { - this.barcode = barcode; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } -} diff --git a/src/main/java/com/alibou/security/product/repository/ProductRepository.java b/src/main/java/com/alibou/security/product/repository/ProductRepository.java deleted file mode 100644 index 885aa83..0000000 --- a/src/main/java/com/alibou/security/product/repository/ProductRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.alibou.security.product.repository; - -import com.alibou.security.product.model.Product; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ProductRepository extends JpaRepository { -} diff --git a/src/main/java/com/alibou/security/product/service/ProductService.java b/src/main/java/com/alibou/security/product/service/ProductService.java deleted file mode 100644 index 14d728a..0000000 --- a/src/main/java/com/alibou/security/product/service/ProductService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.alibou.security.product.service; - -import com.alibou.security.product.model.Product; -import org.jetbrains.annotations.NotNull; - -public interface ProductService { - - @NotNull - Product getProduct(long userId, @NotNull String barcode); - -} diff --git a/src/main/java/com/alibou/security/product/service/ProductServiceImpl.java b/src/main/java/com/alibou/security/product/service/ProductServiceImpl.java deleted file mode 100644 index 5888112..0000000 --- a/src/main/java/com/alibou/security/product/service/ProductServiceImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.alibou.security.product.service; - -import com.alibou.security.product.model.Product; -import com.alibou.security.product.repository.ProductRepository; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Service; - -@Service -public class ProductServiceImpl implements ProductService { - - private final Logger logger = LoggerFactory.getLogger(ProductServiceImpl.class); - private final ProductRepository repository; - - public ProductServiceImpl(ProductRepository repository) { - this.repository = repository; - } - - @NotNull - @Override - public Product getProduct(long userId, @NotNull String barcode) { - logger.info("User = {} request product for barcode = {}", userId, barcode); - return repository.findById(barcode).orElseThrow(() -> { - final String errorMessage = String.format("Product not found for barcode %s", barcode); - return new RuntimeException(errorMessage); - }); - } -} diff --git a/src/main/java/com/alibou/security/user/controller/UserController.java b/src/main/java/com/alibou/security/user/controller/UserController.java index 0bbf0de..49bc961 100644 --- a/src/main/java/com/alibou/security/user/controller/UserController.java +++ b/src/main/java/com/alibou/security/user/controller/UserController.java @@ -1,9 +1,7 @@ package com.alibou.security.user.controller; import com.alibou.security.user.service.UserService; -import com.alibou.security.user.service.impl.UserServiceImpl; import com.alibou.security.user.model.ChangePasswordRequest; -import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestBody; diff --git a/src/main/java/com/alibou/security/user/model/Role.java b/src/main/java/com/alibou/security/user/model/Role.java index ac15bfb..86b3443 100644 --- a/src/main/java/com/alibou/security/user/model/Role.java +++ b/src/main/java/com/alibou/security/user/model/Role.java @@ -1,7 +1,5 @@ package com.alibou.security.user.model; -import lombok.Getter; -import lombok.RequiredArgsConstructor; import org.springframework.security.core.authority.SimpleGrantedAuthority; import java.util.Collections; diff --git a/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java b/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java index 3983f31..7f3b8ad 100644 --- a/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java +++ b/src/main/java/com/alibou/security/user/service/impl/UserServiceImpl.java @@ -4,7 +4,6 @@ import com.alibou.security.user.model.User; import com.alibou.security.user.repository.UserRepository; import com.alibou.security.user.service.UserService; -import lombok.RequiredArgsConstructor; import org.jetbrains.annotations.NotNull; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.crypto.password.PasswordEncoder;