diff --git a/pom.xml b/pom.xml index eb4b8f0..0fd76f0 100644 --- a/pom.xml +++ b/pom.xml @@ -37,10 +37,6 @@ org.springframework.boot spring-boot-starter-web - - org.springframework.boot - spring-boot-starter-security - org.springframework.boot spring-boot-starter-test @@ -80,11 +76,6 @@ 3.25.3 test - - io.jsonwebtoken - jjwt - 0.9.1 - @@ -98,11 +89,6 @@ org.projectlombok lombok - diff --git a/src/main/java/sg/com/smartinventory/entities/ErrorResponse.java b/src/main/java/sg/com/smartinventory/entities/ErrorResponse.java deleted file mode 100644 index 42ae4d9..0000000 --- a/src/main/java/sg/com/smartinventory/entities/ErrorResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package sg.com.smartinventory.entities; - -import java.time.LocalDateTime; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.Setter; - -@Getter -@Setter -@AllArgsConstructor -public class ErrorResponse { - private String message; - private LocalDateTime timestamp; -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/entities/User.java b/src/main/java/sg/com/smartinventory/entities/User.java deleted file mode 100644 index af96d06..0000000 --- a/src/main/java/sg/com/smartinventory/entities/User.java +++ /dev/null @@ -1,69 +0,0 @@ -package sg.com.smartinventory.entities; - -import java.util.List; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.JoinTable; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.Table; -import jakarta.validation.constraints.Digits; -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Entity -@Table(name = "my_user") -public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") - private Long id; - - @NotBlank(message = "Username cannot be blank") - @Column(name = "username") - private String username; - - @NotBlank(message = "Password cannot be blank") - @Column(name = "password") - private String password; - - @NotBlank(message = "First name cannot be blank") - @Column(name = "first_name") - private String firstName; - - @Column(name = "last_name") - private String lastName; - - @Digits(fraction = 0, integer = 8, message = "ContactNo must be 8 digits") - @Column(name = "contact_no") - private String contactNo; - - @Email(message = "Email format must be valid") - @Column(name = "email") - private String email; - - @ManyToMany(cascade = CascadeType.ALL) - @JoinTable(name = "user_user_role", joinColumns = { @JoinColumn(name = "user_id") }, inverseJoinColumns = { - @JoinColumn(name = "user_role_id") }) - private List roles; - - public User(String email) { - this.email = email; - } -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/entities/UserRole.java b/src/main/java/sg/com/smartinventory/entities/UserRole.java deleted file mode 100644 index 660d6ac..0000000 --- a/src/main/java/sg/com/smartinventory/entities/UserRole.java +++ /dev/null @@ -1,43 +0,0 @@ -package sg.com.smartinventory.entities; - -import java.util.List; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.ManyToMany; -import jakarta.persistence.Table; - -import com.fasterxml.jackson.annotation.JsonBackReference; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -@Getter -@Setter -@Builder -@AllArgsConstructor -@NoArgsConstructor -@Entity -@Table(name = "user_role") -public class UserRole { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name = "id") - private Long id; - - @Column(name = "role_name") - private String roleName; - - @Column(name = "description") - private String description; - - @JsonBackReference - @ManyToMany(mappedBy = "roles") - private List users; -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/exceptions/GlobalExceptionHandler.java b/src/main/java/sg/com/smartinventory/exceptions/GlobalExceptionHandler.java deleted file mode 100644 index ca6e759..0000000 --- a/src/main/java/sg/com/smartinventory/exceptions/GlobalExceptionHandler.java +++ /dev/null @@ -1,70 +0,0 @@ -package sg.com.smartinventory.exceptions; - -import java.time.LocalDateTime; -import java.util.List; - -import sg.com.smartinventory.entities.ErrorResponse; - -import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; - -@ControllerAdvice -public class GlobalExceptionHandler { - // This is handler for CustomerNotFoundException - @ExceptionHandler({ CustomerNotFoundException.class, ProductNotFoundException.class, - ReviewNotFoundException.class }) - public ResponseEntity handleResourceNotFoundException(CustomerNotFoundException ex) { - ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), LocalDateTime.now()); - - return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); - } - - // @ExceptionHandler(ProductNotFoundException.class) - // public ResponseEntity - // handleProductNotFoundException(CustomerNotFoundException ex){ - // ErrorResponse errorResponse = new ErrorResponse(ex.getMessage(), - // LocalDateTime.now()); - // - // return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); - // } - - @ExceptionHandler(EmptyResultDataAccessException.class) - public ResponseEntity handleEmptyResultDataAccessException(EmptyResultDataAccessException ex) { - ErrorResponse errorResponse = new ErrorResponse("Entry does not exist. ", LocalDateTime.now()); - - return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND); - } - - @ExceptionHandler(MethodArgumentNotValidException.class) - public ResponseEntity handleValidationExceptions(MethodArgumentNotValidException ex) { - // Get a list of all validation errors from the exception object. - List validationErrors = ex.getBindingResult().getAllErrors(); - - // Create a StringBuilder to store all error messages. - StringBuilder sb = new StringBuilder(); - - for (ObjectError error : validationErrors) { - sb.append(error.getDefaultMessage() + "."); - } - - ErrorResponse errorResponse = new ErrorResponse(sb.toString(), LocalDateTime.now()); - - return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); - } - - @ExceptionHandler(Exception.class) - public ResponseEntity handleException(Exception ex) { - // We can log the exception here. - // logger.error(ex.getMessage(), ex); - - // Return generic error message. - ErrorResponse errorResponse = new ErrorResponse("Something went wrong", LocalDateTime.now()); - - return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); - } -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/repositories/UserRepository.java b/src/main/java/sg/com/smartinventory/repositories/UserRepository.java deleted file mode 100644 index 6f20afc..0000000 --- a/src/main/java/sg/com/smartinventory/repositories/UserRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package sg.com.smartinventory.repositories; - -import org.springframework.data.jpa.repository.JpaRepository; - -import sg.com.smartinventory.entities.User; - -public interface UserRepository extends JpaRepository { - User findByUsername(String username); -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/security/JwtTokenFilter.java b/src/main/java/sg/com/smartinventory/security/JwtTokenFilter.java deleted file mode 100644 index 941b23c..0000000 --- a/src/main/java/sg/com/smartinventory/security/JwtTokenFilter.java +++ /dev/null @@ -1,61 +0,0 @@ -package sg.com.smartinventory.security; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.ArrayList; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import io.jsonwebtoken.Claims; -import sg.com.smartinventory.entities.ErrorResponse; - -@Component -public class JwtTokenFilter extends OncePerRequestFilter { - - private JwtTokenUtil jwtTokenUtil; - - public JwtTokenFilter(JwtTokenUtil jwtTokenUtil) { - this.jwtTokenUtil = jwtTokenUtil; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - - try { - String accessToken = jwtTokenUtil.resolveToken(request); - if (accessToken == null) { - filterChain.doFilter(request, response); - return; - } - - Claims claims = jwtTokenUtil.resolveClaims(request); - - if (claims != null & jwtTokenUtil.validateClaims(claims)) { - String email = claims.getSubject(); - - Authentication authentication = new UsernamePasswordAuthenticationToken(email, "", new ArrayList<>()); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - } catch (Exception ex) { - response.setStatus(HttpStatus.FORBIDDEN.value()); - response.getWriter().write(new ErrorResponse("Authentication failure", LocalDateTime.now()).toString()); - response.setContentType(MediaType.APPLICATION_JSON_VALUE); - - } - filterChain.doFilter(request, response); - - } -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/security/JwtTokenUtil.java b/src/main/java/sg/com/smartinventory/security/JwtTokenUtil.java deleted file mode 100644 index 3092157..0000000 --- a/src/main/java/sg/com/smartinventory/security/JwtTokenUtil.java +++ /dev/null @@ -1,92 +0,0 @@ -package sg.com.smartinventory.security; - -import java.util.Date; -import java.util.concurrent.TimeUnit; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.AuthenticationException; -import org.springframework.stereotype.Component; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.ExpiredJwtException; -import io.jsonwebtoken.JwtParser; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import sg.com.smartinventory.entities.User; - -@Component -public class JwtTokenUtil { - - private final String JWT_SECRET_KEY = "mysecretkey"; - - @Value("${jwt.session.period:3600000}") - private long jwtSessionPeriod; - - private final JwtParser jwtParser; - - private final String TOKEN_HEADER = "Authorization"; - private final String TOKEN_PREFIX = "Bearer "; - - public JwtTokenUtil() { - this.jwtParser = Jwts.parser().setSigningKey(JWT_SECRET_KEY); - } - - public String createToken(User user) { - Claims claims = Jwts.claims().setSubject(user.getEmail()); - claims.put("userName", user.getUsername()); - claims.put("firstName", user.getFirstName()); - claims.put("lastName", user.getLastName()); - - Date tokenCreateTime = new Date(); - Date tokenValidity = new Date(tokenCreateTime.getTime() + TimeUnit.MINUTES.toMillis(jwtSessionPeriod)); - - return Jwts.builder() - .setClaims(claims) - .setExpiration(tokenValidity) - .signWith(SignatureAlgorithm.HS256, JWT_SECRET_KEY) - .compact(); - } - - private Claims parseJwtClaims(String token) { - return jwtParser.parseClaimsJws(token).getBody(); - } - - public String resolveToken(HttpServletRequest request) { - - String bearerToken = request.getHeader(TOKEN_HEADER); - if (bearerToken != null && bearerToken.startsWith(TOKEN_PREFIX)) { - return bearerToken.substring(TOKEN_PREFIX.length()); - } - return null; - } - - public Claims resolveClaims(HttpServletRequest req) { - try { - String token = resolveToken(req); - if (token != null) { - return parseJwtClaims(token); - } - return null; - } catch (ExpiredJwtException ex) { - req.setAttribute("expired", ex.getMessage()); - throw ex; - } catch (Exception ex) { - req.setAttribute("invalid", ex.getMessage()); - throw ex; - } - } - - public boolean validateClaims(Claims claims) throws AuthenticationException { - try { - return claims.getExpiration().after(new Date()); - } catch (Exception e) { - throw e; - } - } - - public String getEmail(Claims claims) { - return claims.getSubject(); - } -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/security/SecurityConfiguration.java b/src/main/java/sg/com/smartinventory/security/SecurityConfiguration.java deleted file mode 100644 index 87a03d5..0000000 --- a/src/main/java/sg/com/smartinventory/security/SecurityConfiguration.java +++ /dev/null @@ -1,65 +0,0 @@ -package sg.com.smartinventory.security; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -import sg.com.smartinventory.serviceImpls.CustomUserDetailsService; - -import jakarta.servlet.http.HttpServletResponse; - -@Configuration -@EnableWebSecurity -public class SecurityConfiguration { - - private final JwtTokenFilter jwtTokenFilter; - private final CustomUserDetailsService customUserDetailsService; - - public SecurityConfiguration(JwtTokenFilter jwtTokenFilter, CustomUserDetailsService customUserDetailsService) { - this.jwtTokenFilter = jwtTokenFilter; - this.customUserDetailsService = customUserDetailsService; - } - - @Bean - PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - AuthenticationManager authenticationManager(HttpSecurity http, PasswordEncoder passwordEncoder) - throws Exception { - AuthenticationManagerBuilder authenticationManagerBuilder = http - .getSharedObject(AuthenticationManagerBuilder.class); - authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder); - return authenticationManagerBuilder.build(); - } - - @Bean - SecurityFilterChain securityFitlerChain(HttpSecurity http) throws Exception { - http.csrf(csrf -> csrf.disable()); - - http = http.sessionManagement(management -> management - .sessionCreationPolicy(SessionCreationPolicy.STATELESS)); - - http = http.exceptionHandling(handling -> handling.authenticationEntryPoint((request, response, ex) -> { - response.sendError( - HttpServletResponse.SC_UNAUTHORIZED, - ex.getMessage()); - })); - - http.authorizeRequests( - (authorize) -> authorize.antMatchers("/auth/**").permitAll().anyRequest().authenticated()); - - http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } -} \ No newline at end of file diff --git a/src/main/java/sg/com/smartinventory/serviceImpls/CustomUserDetailsService.java b/src/main/java/sg/com/smartinventory/serviceImpls/CustomUserDetailsService.java deleted file mode 100644 index b7411e6..0000000 --- a/src/main/java/sg/com/smartinventory/serviceImpls/CustomUserDetailsService.java +++ /dev/null @@ -1,35 +0,0 @@ -package sg.com.smartinventory.serviceImpls; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -import sg.com.smartinventory.entities.User; -import sg.com.smartinventory.repositories.UserRepository; - -@Service -public class CustomUserDetailsService implements UserDetailsService { - - private final UserRepository userRepository; - - public CustomUserDetailsService(UserRepository userRepository) { - this.userRepository = userRepository; - } - - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - User user = userRepository.findByUsername(username); - List roles = new ArrayList<>(); - roles.add("USER"); - UserDetails userDetails = org.springframework.security.core.userdetails.User.builder() - .username(user.getUsername()) - .password(user.getPassword()) - .roles(roles.toArray(new String[0])) - .build(); - return userDetails; - } -} \ No newline at end of file diff --git a/src/test/java/sg/com/smartinventory/entities/ErrorResponseTest.java b/src/test/java/sg/com/smartinventory/entities/ErrorResponseTest.java deleted file mode 100644 index ae73c98..0000000 --- a/src/test/java/sg/com/smartinventory/entities/ErrorResponseTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package sg.com.smartinventory.entities; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import org.mockito.InjectMocks; -import org.mockito.Mock; - -public class ErrorResponseTest { - @DisplayName("Error Response Test") - @Test - public void errorResponseTest() throws Exception { - - } -} \ No newline at end of file