From ce95268c52ee6d4c36bf5f10cfed9546665e8805 Mon Sep 17 00:00:00 2001 From: Roba Date: Wed, 24 Apr 2024 07:07:56 +0200 Subject: [PATCH 01/12] jwtService --- .../java/com/roba/security/JwtService.java | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/security/security/src/main/java/com/roba/security/JwtService.java b/security/security/src/main/java/com/roba/security/JwtService.java index 4ea89c9..13f259c 100644 --- a/security/security/src/main/java/com/roba/security/JwtService.java +++ b/security/security/src/main/java/com/roba/security/JwtService.java @@ -1,12 +1,58 @@ package com.roba.security; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; +import java.security.Key; +import java.util.Base64; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + @Service public class JwtService { +public static final String SECRET_KEY="Hqj4ZSrMAhMvjEpAQMQb2FiHqeMNmJi7mvT5xArPVzdhVfYHvqL2ZnwWzjJ+mbb2"; + + public String extractUsername(String token) {///the email + return extractClaim(token,Claims::getSubject); + } + private Claims extractClaims(String token) { + return Jwts.parserBuilder(). + setSigningKey(getSigningKey()). + build(). + parseClaimsJws(token).getBody(); + } + public T extractClaim(String token, FunctionclaimsResolver) { + final Claims claims = extractClaims(token); + return claimsResolver.apply(claims); + } + public String generateToken(UserDetails userDetails) { + return generateToken(new HashMap<>(),userDetails); + } + public String generateToken(Map extraClaims, - public String extractEmail(String token) { - return null; + UserDetails userDetails) { + return Jwts + .builder() + .setClaims(extraClaims) + .setSubject(userDetails.getUsername()) + .setIssuedAt(new Date(System.currentTimeMillis())) + .setExpiration(new Date(System.currentTimeMillis()+1000*60*24)) + .signWith(getSigningKey(), SignatureAlgorithm.HS256).compact(); + } +public boolean isTokenValid(String token,UserDetails userDetails){ + final String email = extractUsername(token); + return (email.equals(userDetails.getUsername())); +} + private Key getSigningKey() { + byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); + return Keys.hmacShaKeyFor(keyBytes); } } From b136ec34e89c640d5e40d35f3fd4b7ecbe3caf06 Mon Sep 17 00:00:00 2001 From: Roba Date: Fri, 26 Apr 2024 09:54:05 +0200 Subject: [PATCH 02/12] authentication & authorization --- security/security/pom.xml | 6 ++ .../auth/AuthenticationController.java | 27 ++++++++ .../security/auth/AuthenticationRequest.java | 16 +++++ .../security/auth/AuthenticationResponse.java | 14 ++++ .../security/auth/AuthenticationService.java | 52 +++++++++++++++ .../roba/security/auth/RegisterRequest.java | 17 +++++ .../security/config/ApplicationConfig.java | 49 ++++++++++++++ .../config/JwtAuthenticationFilter.java | 64 +++++++++++++++++++ .../security/{ => config}/JwtService.java | 17 ++++- .../config/SecurityConfiguration.java | 49 ++++++++++++++ .../config_JwtAuthenticationFilter.java | 41 ------------ .../roba/security/demo/DemoController.java | 15 +++++ .../java/com/roba/security/user/Role.java | 14 ++++ .../com/roba/security/{ => user}/User.java | 6 +- .../security/{ => user}/UserRespository.java | 4 +- 15 files changed, 342 insertions(+), 49 deletions(-) create mode 100644 security/security/src/main/java/com/roba/security/auth/AuthenticationController.java create mode 100644 security/security/src/main/java/com/roba/security/auth/AuthenticationRequest.java create mode 100644 security/security/src/main/java/com/roba/security/auth/AuthenticationResponse.java create mode 100644 security/security/src/main/java/com/roba/security/auth/AuthenticationService.java create mode 100644 security/security/src/main/java/com/roba/security/auth/RegisterRequest.java create mode 100644 security/security/src/main/java/com/roba/security/config/ApplicationConfig.java create mode 100644 security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java rename security/security/src/main/java/com/roba/security/{ => config}/JwtService.java (84%) create mode 100644 security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java delete mode 100644 security/security/src/main/java/com/roba/security/config_JwtAuthenticationFilter.java create mode 100644 security/security/src/main/java/com/roba/security/demo/DemoController.java create mode 100644 security/security/src/main/java/com/roba/security/user/Role.java rename security/security/src/main/java/com/roba/security/{ => user}/User.java (91%) rename security/security/src/main/java/com/roba/security/{ => user}/UserRespository.java (64%) diff --git a/security/security/pom.xml b/security/security/pom.xml index 883c7c5..39441b2 100644 --- a/security/security/pom.xml +++ b/security/security/pom.xml @@ -50,6 +50,12 @@ jjwt-impl 0.11.5 + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + + org.springframework.boot spring-boot-starter-test diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java new file mode 100644 index 0000000..4d64db7 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java @@ -0,0 +1,27 @@ +package com.roba.security.auth; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.web.bind.annotation.*; + +@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)); + } + + + +} diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationRequest.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationRequest.java new file mode 100644 index 0000000..824e757 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationRequest.java @@ -0,0 +1,16 @@ +package com.roba.security.auth; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthenticationRequest { + private String workEmail; + private String password; + +} diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationResponse.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationResponse.java new file mode 100644 index 0000000..022f994 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationResponse.java @@ -0,0 +1,14 @@ +package com.roba.security.auth; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AuthenticationResponse { + private String token; +} diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java new file mode 100644 index 0000000..6593253 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -0,0 +1,52 @@ +package com.roba.security.auth; + +import com.roba.security.config.JwtService; + + +import com.roba.security.user.Role; +import com.roba.security.user.User; +import com.roba.security.user.UserRespository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor + +public class AuthenticationService { + private final UserRespository repository; + private final PasswordEncoder passwordEncoder; + private final JwtService jwtService; + private final AuthenticationManager authenticationManager; + + + + public AuthenticationResponse register(RegisterRequest request) { + var user= User.builder() + .fullName(request.getFullName()) + .workEmail(request.getWorkEmail()) + .password(passwordEncoder.encode(request.getPassword())) + .role(Role.USER) + .build(); + repository.save(user); + var jwtToken=jwtService.generateToken(user); + + return AuthenticationResponse.builder().token(jwtToken).build(); + } + + public AuthenticationResponse authenticate(AuthenticationRequest request) { + authenticationManager.authenticate( + new UsernamePasswordAuthenticationToken( + request.getWorkEmail(), + request.getPassword() + ) + ); + var user = repository.findByworkEmail(request.getWorkEmail()) + .orElseThrow(); + var jwtToken=jwtService.generateToken(user); + + return AuthenticationResponse.builder().token(jwtToken).build(); + } +} diff --git a/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java b/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java new file mode 100644 index 0000000..c3dd18f --- /dev/null +++ b/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java @@ -0,0 +1,17 @@ +package com.roba.security.auth; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class RegisterRequest { + private String fullName; + private String password; + private String workEmail; + +} diff --git a/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java b/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java new file mode 100644 index 0000000..519f078 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java @@ -0,0 +1,49 @@ +package com.roba.security.config; + +import com.roba.security.user.UserRespository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +@RequiredArgsConstructor +public class ApplicationConfig { + private final UserRespository repository; + @Bean + public UserDetailsService userDetailsService() { + return username -> repository.findByworkEmail(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 PasswordEncoder passwordEncoder(){ + return new BCryptPasswordEncoder(); + + } + + @Bean + public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { + return config.getAuthenticationManager(); + + } +} diff --git a/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java b/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java new file mode 100644 index 0000000..a9a54cf --- /dev/null +++ b/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java @@ -0,0 +1,64 @@ +package com.roba.security.config; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtService jwtService; + private final UserDetailsService userDetailsService; + + + + + + + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain) throws ServletException, IOException { + final String authHeader = request.getHeader("Authorization"); + final String jwt; + final String workEmail; + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + + } + jwt = authHeader.substring(7); + workEmail = jwtService.extractUsername(jwt); + if (workEmail != null && SecurityContextHolder.getContext().getAuthentication()==null) { + UserDetails userDetails = this.userDetailsService.loadUserByUsername(workEmail); + if (jwtService.isTokenValid( jwt, userDetails)) { + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + authToken.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request) + ); + SecurityContextHolder.getContext().setAuthentication(authToken); + } + + } + filterChain.doFilter(request, response); + } +} diff --git a/security/security/src/main/java/com/roba/security/JwtService.java b/security/security/src/main/java/com/roba/security/config/JwtService.java similarity index 84% rename from security/security/src/main/java/com/roba/security/JwtService.java rename to security/security/src/main/java/com/roba/security/config/JwtService.java index 13f259c..2aa0217 100644 --- a/security/security/src/main/java/com/roba/security/JwtService.java +++ b/security/security/src/main/java/com/roba/security/config/JwtService.java @@ -1,4 +1,4 @@ -package com.roba.security; +package com.roba.security.config; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; @@ -9,7 +9,6 @@ import org.springframework.stereotype.Service; import java.security.Key; -import java.util.Base64; import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -49,8 +48,20 @@ public String generateToken(Map extraClaims, } public boolean isTokenValid(String token,UserDetails userDetails){ final String email = extractUsername(token); - return (email.equals(userDetails.getUsername())); + return (email.equals(userDetails.getUsername())) && !isTokenExpired(token); } + + +public boolean isTokenExpired(String token) { + + return extractExpiration(token).before(new Date()); +} + + private Date extractExpiration(String token) { + return extractClaim(token,Claims::getExpiration); + + } + private Key getSigningKey() { byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY); return Keys.hmacShaKeyFor(keyBytes); diff --git a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java new file mode 100644 index 0000000..5a88227 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java @@ -0,0 +1,49 @@ +package com.roba.security.config; + + +import jakarta.servlet.Filter; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +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.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor + +public class SecurityConfiguration { + private final JwtAuthenticationFilter jwtAuthFilter; + private final AuthenticationProvider authenticationProvider; + + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + + .csrf(csrf -> csrf.disable()) // Disable CSRF protection + .authorizeRequests(authorizeRequests -> + authorizeRequests + .requestMatchers("/api/v1/auth/**").permitAll() // Allow access to public endpoints + .anyRequest().authenticated() // Require authentication for other requests + ) + .sessionManagement(sessionManagement -> + sessionManagement + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + ) + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + return http.build(); + } + + + + + + + +} diff --git a/security/security/src/main/java/com/roba/security/config_JwtAuthenticationFilter.java b/security/security/src/main/java/com/roba/security/config_JwtAuthenticationFilter.java deleted file mode 100644 index fade8e9..0000000 --- a/security/security/src/main/java/com/roba/security/config_JwtAuthenticationFilter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.roba.security; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -@Component -@RequiredArgsConstructor -public class config_JwtAuthenticationFilter extends OncePerRequestFilter { - - private final JwtService jwtService; - - - - - - - @Override - protected void doFilterInternal( - @NonNull HttpServletRequest request, - @NonNull HttpServletResponse response, - @NonNull FilterChain filterChain) throws ServletException, IOException { - final String authHeader = request.getHeader("Authorization"); - final String jwt; - final String workEmail; - if (authHeader == null || !authHeader.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - return; - - } - jwt = authHeader.substring(7); - workEmail = jwtService.extractEmail(jwt); - } -} diff --git a/security/security/src/main/java/com/roba/security/demo/DemoController.java b/security/security/src/main/java/com/roba/security/demo/DemoController.java new file mode 100644 index 0000000..9b15770 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/demo/DemoController.java @@ -0,0 +1,15 @@ +package com.roba.security.demo; + +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") +public class DemoController { + @GetMapping + public ResponseEntity sayHello() { + return ResponseEntity.ok("Hello from secured endpoint"); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/Role.java b/security/security/src/main/java/com/roba/security/user/Role.java new file mode 100644 index 0000000..45ddab5 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/Role.java @@ -0,0 +1,14 @@ +package com.roba.security.user; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor + +public enum Role { + USER, + ADMIN; + // MANAGER(); + + + +} diff --git a/security/security/src/main/java/com/roba/security/User.java b/security/security/src/main/java/com/roba/security/user/User.java similarity index 91% rename from security/security/src/main/java/com/roba/security/User.java rename to security/security/src/main/java/com/roba/security/user/User.java index 42a8933..6265c3a 100644 --- a/security/security/src/main/java/com/roba/security/User.java +++ b/security/security/src/main/java/com/roba/security/user/User.java @@ -1,4 +1,4 @@ -package com.roba.security; +package com.roba.security.user; import jakarta.persistence.*; @@ -7,7 +7,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; -import javax.management.relation.Role; + import java.time.LocalDate; import java.util.Collection; import java.util.List; @@ -42,7 +42,7 @@ public class User implements UserDetails { @Override public Collection getAuthorities() { - return List.of(new SimpleGrantedAuthority(role.getRoleName())); + return List.of(new SimpleGrantedAuthority(role.name())); } @Override diff --git a/security/security/src/main/java/com/roba/security/UserRespository.java b/security/security/src/main/java/com/roba/security/user/UserRespository.java similarity index 64% rename from security/security/src/main/java/com/roba/security/UserRespository.java rename to security/security/src/main/java/com/roba/security/user/UserRespository.java index cdbbee5..8d342b4 100644 --- a/security/security/src/main/java/com/roba/security/UserRespository.java +++ b/security/security/src/main/java/com/roba/security/user/UserRespository.java @@ -1,9 +1,9 @@ -package com.roba.security; +package com.roba.security.user; import org.springframework.data.jpa.repository.JpaRepository; import java.util.Optional; public interface UserRespository extends JpaRepository { - Optional findByEmail(String workEmail); + Optional findByworkEmail(String workEmail); } From c88d2481b0edd0b005eca2edd82e0ed6cd513763 Mon Sep 17 00:00:00 2001 From: Roba Date: Fri, 26 Apr 2024 13:31:11 +0200 Subject: [PATCH 03/12] logout service-jwt --- security/security/pom.xml | 2 + .../security/auth/AuthenticationService.java | 26 ++++++++++++- .../config/JwtAuthenticationFilter.java | 7 +++- .../roba/security/config/LogoutService.java | 39 +++++++++++++++++++ .../config/SecurityConfiguration.java | 20 +++++++++- .../java/com/roba/security/token/Token.java | 28 +++++++++++++ .../roba/security/token/TokenRepository.java | 18 +++++++++ .../com/roba/security/token/TokenType.java | 5 +++ .../java/com/roba/security/user/User.java | 4 ++ 9 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 security/security/src/main/java/com/roba/security/config/LogoutService.java create mode 100644 security/security/src/main/java/com/roba/security/token/Token.java create mode 100644 security/security/src/main/java/com/roba/security/token/TokenRepository.java create mode 100644 security/security/src/main/java/com/roba/security/token/TokenType.java diff --git a/security/security/pom.xml b/security/security/pom.xml index 39441b2..e6c9c35 100644 --- a/security/security/pom.xml +++ b/security/security/pom.xml @@ -66,6 +66,8 @@ spring-security-test test + + diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index 6593253..0706253 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -3,6 +3,9 @@ import com.roba.security.config.JwtService; +import com.roba.security.token.Token; +import com.roba.security.token.TokenRepository; +import com.roba.security.token.TokenType; import com.roba.security.user.Role; import com.roba.security.user.User; import com.roba.security.user.UserRespository; @@ -20,7 +23,7 @@ public class AuthenticationService { private final PasswordEncoder passwordEncoder; private final JwtService jwtService; private final AuthenticationManager authenticationManager; - + private final TokenRepository tokenRepository; public AuthenticationResponse register(RegisterRequest request) { @@ -30,11 +33,23 @@ public AuthenticationResponse register(RegisterRequest request) { .password(passwordEncoder.encode(request.getPassword())) .role(Role.USER) .build(); - repository.save(user); + var savedUser =repository.save(user); var jwtToken=jwtService.generateToken(user); + saveUserToken(savedUser, jwtToken); return AuthenticationResponse.builder().token(jwtToken).build(); } + 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 AuthenticationResponse authenticate(AuthenticationRequest request) { authenticationManager.authenticate( @@ -46,7 +61,14 @@ public AuthenticationResponse authenticate(AuthenticationRequest request) { var user = repository.findByworkEmail(request.getWorkEmail()) .orElseThrow(); var jwtToken=jwtService.generateToken(user); + revokeAllUserTokens(user); + saveUserToken(user,jwtToken); return AuthenticationResponse.builder().token(jwtToken).build(); + + } + private void saveUserToken(User user, String jwtToken) { + var token = Token.builder().user(user).token(jwtToken).tokenType(TokenType.BEARER).revoked(false).expired(false).build(); + tokenRepository.save(token); } } diff --git a/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java b/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java index a9a54cf..e8eae2c 100644 --- a/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java +++ b/security/security/src/main/java/com/roba/security/config/JwtAuthenticationFilter.java @@ -1,5 +1,6 @@ package com.roba.security.config; +import com.roba.security.token.TokenRepository; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -22,6 +23,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { private final JwtService jwtService; private final UserDetailsService userDetailsService; + private final TokenRepository tokenRepository; @@ -46,7 +48,10 @@ protected void doFilterInternal( workEmail = jwtService.extractUsername(jwt); if (workEmail != null && SecurityContextHolder.getContext().getAuthentication()==null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(workEmail); - if (jwtService.isTokenValid( jwt, userDetails)) { + 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, diff --git a/security/security/src/main/java/com/roba/security/config/LogoutService.java b/security/security/src/main/java/com/roba/security/config/LogoutService.java new file mode 100644 index 0000000..7f18258 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/config/LogoutService.java @@ -0,0 +1,39 @@ +package com.roba.security.config; + +import com.roba.security.token.TokenRepository; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutHandler; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class LogoutService implements LogoutHandler { + 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; + } + jwt = authHeader.substring(7); + var storedToken =tokenRepository.findByToken(jwt) + .orElse(null); + if (storedToken != null) { + storedToken.setRevoked(true); + storedToken.setExpired(true); + tokenRepository.save(storedToken); + } + + } + + +} diff --git a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java index 5a88227..9a3af94 100644 --- a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java +++ b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java @@ -9,8 +9,10 @@ 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.core.context.SecurityContextHolder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutHandler; @Configuration @EnableWebSecurity @@ -19,7 +21,7 @@ public class SecurityConfiguration { private final JwtAuthenticationFilter jwtAuthFilter; private final AuthenticationProvider authenticationProvider; - + private final LogoutHandler logoutHandler; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { @@ -36,7 +38,21 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .authenticationProvider(authenticationProvider) - .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) + .logout(logout -> // Customizing logout configuration + logout + .logoutUrl("/api/v1/auth/logout") + .addLogoutHandler(logoutHandler) + .logoutSuccessHandler( (request, response, authentication) -> SecurityContextHolder.clearContext())); + + + +// .logout() +// .logoutUrl("/api/v1/auth/logout") +// .addLogoutHandler(logoutHandler) +// .logoutSuccessHandler( +// (request, response, authentication) -> SecurityContextHolder.clearContext() +// ); return http.build(); } diff --git a/security/security/src/main/java/com/roba/security/token/Token.java b/security/security/src/main/java/com/roba/security/token/Token.java new file mode 100644 index 0000000..55de36d --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/Token.java @@ -0,0 +1,28 @@ +package com.roba.security.token; + +import com.roba.security.user.User; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class Token { + + @Id + @GeneratedValue + private Integer id; + private String token; + @Enumerated(EnumType.STRING) + private TokenType tokenType; + private boolean expired; + private boolean revoked; + + + @ManyToOne + @JoinColumn(name="user_id") + private User user; + +} diff --git a/security/security/src/main/java/com/roba/security/token/TokenRepository.java b/security/security/src/main/java/com/roba/security/token/TokenRepository.java new file mode 100644 index 0000000..aeb4ee7 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/TokenRepository.java @@ -0,0 +1,18 @@ +package com.roba.security.token; + + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import java.util.Optional; +import java.util.List; + +public interface TokenRepository extends JpaRepository { + + @Query(""" + select t from Token t inner join User u on t.user.id=u.id + where u.id=:userId and(t.expired=false or t.revoked=false) + """) + List findAllValidTokenByUser(Integer userId); + + Optional findByToken(String token); +} diff --git a/security/security/src/main/java/com/roba/security/token/TokenType.java b/security/security/src/main/java/com/roba/security/token/TokenType.java new file mode 100644 index 0000000..59f32a6 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/TokenType.java @@ -0,0 +1,5 @@ +package com.roba.security.token; + +public enum TokenType { + BEARER +} diff --git a/security/security/src/main/java/com/roba/security/user/User.java b/security/security/src/main/java/com/roba/security/user/User.java index 6265c3a..b122230 100644 --- a/security/security/src/main/java/com/roba/security/user/User.java +++ b/security/security/src/main/java/com/roba/security/user/User.java @@ -1,6 +1,7 @@ package com.roba.security.user; +import com.roba.security.token.Token; import jakarta.persistence.*; import lombok.*; import org.springframework.security.core.GrantedAuthority; @@ -36,6 +37,9 @@ public class User implements UserDetails { private String password; @Enumerated(EnumType.STRING) private Role role; + @OneToMany(mappedBy = "user") + private List tokens; + private LocalDate joinDate; //foreign key to organization table private Integer organizationId; From 9091f7aacbcd82ccf16f7e2a3fa25389d873da27 Mon Sep 17 00:00:00 2001 From: Roba Date: Tue, 7 May 2024 03:00:23 +0200 Subject: [PATCH 04/12] user&organization table relations,updated roles,registeras(owner,team_member) --- .../roba/security/Project/BudgetBasedOn.java | 4 + .../com/roba/security/Project/BudgetType.java | 4 + .../com/roba/security/Project/Project.java | 39 ++++++++++ .../security/auth/AuthenticationService.java | 77 ++++++++++++++++--- .../roba/security/auth/Emailvalidator.java | 13 ++++ .../roba/security/auth/RegisterRequest.java | 7 ++ .../config/SecurityConfiguration.java | 8 +- .../demo/BusinessOwnerController.java | 17 ++++ .../security/organization/Organization.java | 40 ++++++++++ .../organization/OrganizationRepository.java | 11 +++ .../organization/OrganizationService.java | 20 +++++ .../com/roba/security/user/Permission.java | 18 +++++ .../java/com/roba/security/user/Role.java | 31 +++++++- .../java/com/roba/security/user/User.java | 35 ++++++++- .../roba/security/user/UserRespository.java | 2 + 15 files changed, 305 insertions(+), 21 deletions(-) create mode 100644 security/security/src/main/java/com/roba/security/Project/BudgetBasedOn.java create mode 100644 security/security/src/main/java/com/roba/security/Project/BudgetType.java create mode 100644 security/security/src/main/java/com/roba/security/Project/Project.java create mode 100644 security/security/src/main/java/com/roba/security/auth/Emailvalidator.java create mode 100644 security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java create mode 100644 security/security/src/main/java/com/roba/security/organization/Organization.java create mode 100644 security/security/src/main/java/com/roba/security/organization/OrganizationRepository.java create mode 100644 security/security/src/main/java/com/roba/security/organization/OrganizationService.java create mode 100644 security/security/src/main/java/com/roba/security/user/Permission.java diff --git a/security/security/src/main/java/com/roba/security/Project/BudgetBasedOn.java b/security/security/src/main/java/com/roba/security/Project/BudgetBasedOn.java new file mode 100644 index 0000000..4e018e6 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/BudgetBasedOn.java @@ -0,0 +1,4 @@ +package com.roba.security.Project; + +public enum BudgetBasedOn { +} diff --git a/security/security/src/main/java/com/roba/security/Project/BudgetType.java b/security/security/src/main/java/com/roba/security/Project/BudgetType.java new file mode 100644 index 0000000..cdd2421 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/BudgetType.java @@ -0,0 +1,4 @@ +package com.roba.security.Project; + +public enum BudgetType { +} diff --git a/security/security/src/main/java/com/roba/security/Project/Project.java b/security/security/src/main/java/com/roba/security/Project/Project.java new file mode 100644 index 0000000..7d34214 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/Project.java @@ -0,0 +1,39 @@ +package com.roba.security.Project; + + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity +public class Project { + @Id + @SequenceGenerator( + name = "project_id_sequence", + sequenceName = "project_id_sequence" + ) + @GeneratedValue( + strategy = GenerationType.SEQUENCE, + generator = "project_id_sequence" + ) + private Integer id; + private String projectName; + private boolean billable; + private boolean disableActivity; + private boolean disableIdleTime; + private Integer clientId; + private BudgetType budgetType; + private BudgetBasedOn budgetBasedOn; + private Double budgetCost; + private Double budgetNotifyAt; + private Date budgetStartDate; + private boolean budgetIncludeNonBillabeTime; +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index 0706253..02b5d44 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -3,6 +3,8 @@ import com.roba.security.config.JwtService; +import com.roba.security.organization.Organization; +import com.roba.security.organization.OrganizationRepository; import com.roba.security.token.Token; import com.roba.security.token.TokenRepository; import com.roba.security.token.TokenType; @@ -10,11 +12,15 @@ import com.roba.security.user.User; import com.roba.security.user.UserRespository; import lombok.RequiredArgsConstructor; + 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.util.Optional; + @Service @RequiredArgsConstructor @@ -24,20 +30,69 @@ public class AuthenticationService { private final JwtService jwtService; private final AuthenticationManager authenticationManager; private final TokenRepository tokenRepository; - + private final Emailvalidator emailvalidator; + private final OrganizationRepository organizationRepository; public AuthenticationResponse register(RegisterRequest request) { - var user= User.builder() - .fullName(request.getFullName()) - .workEmail(request.getWorkEmail()) - .password(passwordEncoder.encode(request.getPassword())) - .role(Role.USER) - .build(); - var savedUser =repository.save(user); - var jwtToken=jwtService.generateToken(user); + //boolean isValidEmail =emailvalidator.test(request.getWorkEmail()); + //if(!isValidEmail) {throw new IllegalStateException("Invalid email");} + Optional existingUser = repository.findByworkEmail(request.getWorkEmail()); + if (existingUser.isPresent()) { + throw new IllegalStateException("Email is already in use"); + } + Role role; + if(request.getRole().equals("team_member")) { + role = Role.USER; + }else { + role = request.getRole(); + } + if (role == Role.OWNER) { + // Create organization + Organization organization = Organization.builder() + .name(request.getOrganizationName()) + .teamSize(request.getTeamSize()) + .websiteURL(request.getWebsite())// Assuming organization name is provided in the request + .build(); + Organization savedOrganization = organizationRepository.save(organization); + + var user = User.builder() + .fullName(request.getFullName()) + .workEmail(request.getWorkEmail()) + .password(passwordEncoder.encode(request.getPassword())) + .role(request.getRole()) + .organization(savedOrganization) + .build(); + var savedUser = repository.save(user); + var jwtToken = jwtService.generateToken(user); + + saveUserToken(savedUser, jwtToken); + return AuthenticationResponse.builder().token(jwtToken).build(); + } + else if(role == Role.USER){ + // Fetch organization by name + Optional organizationOptional = organizationRepository.findByName(request.getOrganizationName()); + if (organizationOptional.isEmpty()) { + throw new IllegalStateException("Organization not found"); + } + Organization organization = organizationOptional.get(); + + var user = User.builder() + .fullName(request.getFullName()) + .workEmail(request.getWorkEmail()) + .password(passwordEncoder.encode(request.getPassword())) + .role(Role.USER) + .organization(organization) + .build(); + var savedUser = repository.save(user); + var jwtToken = jwtService.generateToken(user); + + saveUserToken(savedUser, jwtToken); + return AuthenticationResponse.builder().token(jwtToken).build(); + }else{ + throw new IllegalStateException("Role not supported"); + + } - saveUserToken(savedUser, jwtToken); - return AuthenticationResponse.builder().token(jwtToken).build(); } private void revokeAllUserTokens(User user) { var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId()); diff --git a/security/security/src/main/java/com/roba/security/auth/Emailvalidator.java b/security/security/src/main/java/com/roba/security/auth/Emailvalidator.java new file mode 100644 index 0000000..aaa8bfc --- /dev/null +++ b/security/security/src/main/java/com/roba/security/auth/Emailvalidator.java @@ -0,0 +1,13 @@ +package com.roba.security.auth; + +import org.springframework.stereotype.Service; + +import java.util.function.Predicate; +@Service +public class Emailvalidator implements Predicate { + + @Override + public boolean test(String s) { + return false; + } +} diff --git a/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java b/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java index c3dd18f..fd4eb33 100644 --- a/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java +++ b/security/security/src/main/java/com/roba/security/auth/RegisterRequest.java @@ -1,5 +1,7 @@ package com.roba.security.auth; +import com.roba.security.organization.Organization; +import com.roba.security.user.Role; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -13,5 +15,10 @@ public class RegisterRequest { private String fullName; private String password; private String workEmail; + private Role role; + private String organizationName; + private Integer teamSize; + private String website; + } diff --git a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java index 9a3af94..34a557c 100644 --- a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java +++ b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java @@ -1,7 +1,7 @@ package com.roba.security.config; -import jakarta.servlet.Filter; +import com.roba.security.demo.BusinessOwnerController; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -14,6 +14,9 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutHandler; + +import static com.roba.security.user.Role.OWNER; + @Configuration @EnableWebSecurity @RequiredArgsConstructor @@ -30,7 +33,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .csrf(csrf -> csrf.disable()) // Disable CSRF protection .authorizeRequests(authorizeRequests -> authorizeRequests - .requestMatchers("/api/v1/auth/**").permitAll() // Allow access to public endpoints + .requestMatchers("/api/v1/auth/**").permitAll()// Allow access to public endpoints + .requestMatchers("/api/v1/admin/**").hasAnyRole(OWNER.name())//allow access by biz owner to admin endpoint .anyRequest().authenticated() // Require authentication for other requests ) .sessionManagement(sessionManagement -> diff --git a/security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java b/security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java new file mode 100644 index 0000000..1b072d9 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java @@ -0,0 +1,17 @@ +package com.roba.security.demo; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/admin") +public class BusinessOwnerController { + @GetMapping + public String get() { + return "get in admin"; + } + @PostMapping + public String post(){ return "POST:IN ADMIN";} +} diff --git a/security/security/src/main/java/com/roba/security/organization/Organization.java b/security/security/src/main/java/com/roba/security/organization/Organization.java new file mode 100644 index 0000000..7dd5e36 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/organization/Organization.java @@ -0,0 +1,40 @@ +package com.roba.security.organization; + +import com.roba.security.user.User; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Data +public class Organization { + @Id + @SequenceGenerator( + name="organization_id_sequence", + sequenceName="organization_id_sequence" + ) + @GeneratedValue( + strategy = GenerationType.SEQUENCE, + generator = "organization_id_seqeunce" + ) + private Integer id; + private String name; + private String websiteURL; + private Integer teamSize; + private String industry; + @OneToMany(mappedBy = "organization") // mappedBy refers to the field in User entity that owns the relationship + private List users ; + + +// @OneToOne(mappedBy = "organization", cascade = CascadeType.ALL) +// private User manager; + + +} diff --git a/security/security/src/main/java/com/roba/security/organization/OrganizationRepository.java b/security/security/src/main/java/com/roba/security/organization/OrganizationRepository.java new file mode 100644 index 0000000..73d45ec --- /dev/null +++ b/security/security/src/main/java/com/roba/security/organization/OrganizationRepository.java @@ -0,0 +1,11 @@ +package com.roba.security.organization; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface OrganizationRepository extends JpaRepository { + Optional findByName(String organizationName); +} diff --git a/security/security/src/main/java/com/roba/security/organization/OrganizationService.java b/security/security/src/main/java/com/roba/security/organization/OrganizationService.java new file mode 100644 index 0000000..60301ef --- /dev/null +++ b/security/security/src/main/java/com/roba/security/organization/OrganizationService.java @@ -0,0 +1,20 @@ +package com.roba.security.organization; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class OrganizationService { + + private final OrganizationRepository organizationRepository; + @Autowired + public OrganizationService(OrganizationRepository organizationRepository) { + this.organizationRepository = organizationRepository; + } + + public List getAllOrganizations() { + return organizationRepository.findAll(); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/Permission.java b/security/security/src/main/java/com/roba/security/user/Permission.java new file mode 100644 index 0000000..39149fb --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/Permission.java @@ -0,0 +1,18 @@ +package com.roba.security.user; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public enum Permission { + + OWNER_READ("owner:read"), + OWNER_UPDATE("owner:update"), + OWNER_CREATE("owner:create"), + OWNER_DELETE("owner:delete"); + + + + @Getter + private final String permission; +} diff --git a/security/security/src/main/java/com/roba/security/user/Role.java b/security/security/src/main/java/com/roba/security/user/Role.java index 45ddab5..a80fb99 100644 --- a/security/security/src/main/java/com/roba/security/user/Role.java +++ b/security/security/src/main/java/com/roba/security/user/Role.java @@ -1,14 +1,37 @@ package com.roba.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.roba.security.user.Permission.*; + @RequiredArgsConstructor public enum Role { - USER, - ADMIN; - // MANAGER(); - + USER(Collections.emptySet()), + OWNER(Set.of(OWNER_READ, + OWNER_UPDATE, + OWNER_DELETE, + OWNER_CREATE)), + BUSINESS_MANAGER(Collections.emptySet()), + PROJECT_MANAGER(Collections.emptySet()); +@Getter +private final Set permissions; +public List getAuthorities(){ + var authorities=getPermissions() + .stream() + .map(permission -> new SimpleGrantedAuthority(permission.name())) + .collect(Collectors.toList()); + authorities.add(new SimpleGrantedAuthority("ROLE"+this.name())); + return authorities; + } } diff --git a/security/security/src/main/java/com/roba/security/user/User.java b/security/security/src/main/java/com/roba/security/user/User.java index b122230..c87c4aa 100644 --- a/security/security/src/main/java/com/roba/security/user/User.java +++ b/security/security/src/main/java/com/roba/security/user/User.java @@ -1,6 +1,8 @@ package com.roba.security.user; +import com.roba.security.Project.Project; +import com.roba.security.organization.Organization; import com.roba.security.token.Token; import jakarta.persistence.*; import lombok.*; @@ -21,6 +23,7 @@ @Builder @Data @Table(name="_user") +@EqualsAndHashCode public class User implements UserDetails { @Id @SequenceGenerator( @@ -40,13 +43,37 @@ public class User implements UserDetails { @OneToMany(mappedBy = "user") private List tokens; - private LocalDate joinDate; - //foreign key to organization table - private Integer organizationId; + + + private String Department; + private String Position;//Title + + + @ManyToOne + @JoinColumn(name="organization_id") + // This specifies the foreign key column name in the User table + private Organization organization; // Reference to organization) + + + @OneToMany + private List projects; + private String TimeZone; + private LocalDate DateAdded; + private LocalDate DateRemoved; + private String PayType;//hourly/fixed + private Integer BillRate; + private Integer WeeklyLimit; + private Integer DailyLimit; + private boolean TrackingEnabled; + private boolean TimesheetsEnabled; + private String Status; + // private List teams; + + @Override public Collection getAuthorities() { - return List.of(new SimpleGrantedAuthority(role.name())); + return role.getAuthorities(); } @Override diff --git a/security/security/src/main/java/com/roba/security/user/UserRespository.java b/security/security/src/main/java/com/roba/security/user/UserRespository.java index 8d342b4..58a395b 100644 --- a/security/security/src/main/java/com/roba/security/user/UserRespository.java +++ b/security/security/src/main/java/com/roba/security/user/UserRespository.java @@ -6,4 +6,6 @@ public interface UserRespository extends JpaRepository { Optional findByworkEmail(String workEmail); + + // Optional findByWorkEmail(String workEmail); } From afdd915f4f98fb6e5fb8aaa9f871913830cac5c7 Mon Sep 17 00:00:00 2001 From: Roba Date: Thu, 9 May 2024 20:46:13 +0200 Subject: [PATCH 05/12] user profile edits, new permissions --- .../java/com/roba/security/Client/Client.java | 54 ++++++++++++++ .../com/roba/security/Project/Project.java | 22 +++++- .../security/Project/ProjectController.java | 42 +++++++++++ .../Project/ProjectCreationRequest.java | 25 +++++++ .../security/Project/ProjectEditRequest.java | 25 +++++++ .../security/Project/ProjectRepository.java | 9 +++ .../roba/security/Project/ProjectService.java | 72 +++++++++++++++++++ .../security/auth/AuthenticationService.java | 4 +- .../security/config/ApplicationConfig.java | 6 +- .../config/SecurityConfiguration.java | 13 +++- ...ontroller.java => ProjectController1.java} | 15 ++-- .../security/user/ChangePasswordRequest.java | 10 +++ .../com/roba/security/user/Permission.java | 50 +++++++++++-- .../user/ResourceNotFoundException.java | 12 ++++ .../java/com/roba/security/user/Role.java | 24 +++++-- .../security/user/UnauthorizedException.java | 11 +++ .../roba/security/user/UserController.java | 25 +++++++ .../user/UserProfileUpdateRequest.java | 11 +++ ...erRespository.java => UserRepository.java} | 2 +- .../java/com/roba/user/UserRepository.java | 12 ++-- 20 files changed, 411 insertions(+), 33 deletions(-) create mode 100644 security/security/src/main/java/com/roba/security/Client/Client.java create mode 100644 security/security/src/main/java/com/roba/security/Project/ProjectController.java create mode 100644 security/security/src/main/java/com/roba/security/Project/ProjectCreationRequest.java create mode 100644 security/security/src/main/java/com/roba/security/Project/ProjectEditRequest.java create mode 100644 security/security/src/main/java/com/roba/security/Project/ProjectRepository.java create mode 100644 security/security/src/main/java/com/roba/security/Project/ProjectService.java rename security/security/src/main/java/com/roba/security/demo/{BusinessOwnerController.java => ProjectController1.java} (67%) create mode 100644 security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java create mode 100644 security/security/src/main/java/com/roba/security/user/ResourceNotFoundException.java create mode 100644 security/security/src/main/java/com/roba/security/user/UnauthorizedException.java create mode 100644 security/security/src/main/java/com/roba/security/user/UserController.java create mode 100644 security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java rename security/security/src/main/java/com/roba/security/user/{UserRespository.java => UserRepository.java} (76%) diff --git a/security/security/src/main/java/com/roba/security/Client/Client.java b/security/security/src/main/java/com/roba/security/Client/Client.java new file mode 100644 index 0000000..25eca0a --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Client/Client.java @@ -0,0 +1,54 @@ +package com.roba.security.Client; + +import com.roba.security.Project.BudgetBasedOn; +import com.roba.security.Project.BudgetType; +import com.roba.security.Project.Project; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.antlr.v4.runtime.misc.NotNull; + +import java.util.Date; +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "Client") +public class Client { + @Id + @SequenceGenerator( + name = "client_id_sequence", + sequenceName = "client_id_sequence" + ) + @GeneratedValue( + strategy = GenerationType.SEQUENCE, + generator = "client_id_sequence" + ) + private Integer id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String email; + + @NotNull + @OneToMany(mappedBy = "client", cascade = CascadeType.ALL) + private List projects; + private String phoneNumber; + private String emailAddress; + //private Budget budget; + private boolean billable; + private BudgetType budgetType; + private BudgetBasedOn budgetBasedOn; + private Double budgetCost; + private Double budgetNotifyAt; + private Date budgetStartDate; + private boolean budgetIncludeNonBillabeTime; + private Integer organizationId; +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/Project/Project.java b/security/security/src/main/java/com/roba/security/Project/Project.java index 7d34214..1f58074 100644 --- a/security/security/src/main/java/com/roba/security/Project/Project.java +++ b/security/security/src/main/java/com/roba/security/Project/Project.java @@ -1,19 +1,25 @@ package com.roba.security.Project; + +import com.roba.security.Client.Client; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import org.antlr.v4.runtime.misc.NotNull; import java.util.Date; +//import javax.*; + @Data @Builder @AllArgsConstructor @NoArgsConstructor @Entity +@Table(name = "Project") public class Project { @Id @SequenceGenerator( @@ -25,15 +31,27 @@ public class Project { generator = "project_id_sequence" ) private Integer id; + // @NotNull private String projectName; - private boolean billable; private boolean disableActivity; private boolean disableIdleTime; - private Integer clientId; + //private Integer clientId; + @ManyToOne + @JoinColumn(name = "client_id", nullable = false) + private Client client; + // @NotNull + //private Budget budget; + private boolean billable; private BudgetType budgetType; private BudgetBasedOn budgetBasedOn; private Double budgetCost; private Double budgetNotifyAt; private Date budgetStartDate; private boolean budgetIncludeNonBillabeTime; + // @NotNull + private Integer organizationId; + //members + //users + //managers + //CONSTRAINTS } \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/Project/ProjectController.java b/security/security/src/main/java/com/roba/security/Project/ProjectController.java new file mode 100644 index 0000000..8c8a43e --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/ProjectController.java @@ -0,0 +1,42 @@ +package com.roba.security.Project; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Optional; + +@Slf4j +@RestController +@RequestMapping("api/v1/project") +public record ProjectController(ProjectService projectService) { + + @PostMapping + public void createProject(@RequestBody ProjectCreationRequest projectRequest){ + System.out.printf("new project added {}", projectRequest); + projectService.createProject(projectRequest); + } + @PutMapping + public void editProject(@RequestBody ProjectEditRequest projectRequest){ + //log.info("project edited {}", projectRequest); + projectService.editProject(projectRequest); + } + @GetMapping + public Optional findById(@RequestParam Integer id){ + //log.info("find project {}", id); + return projectService.findById(id); + } + + +// @GetMapping("/{managerId}") +// public List findByManagerId(@PathVariable Integer managerId){ +// //log.info("find project {}", id); +// return projectService.findAllByManager(managerId); +// } + @DeleteMapping + public void deleteProject(@RequestParam Integer id){ + //log.info("project deleted {}", id); + projectService.deleteProject(id); + } + +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/Project/ProjectCreationRequest.java b/security/security/src/main/java/com/roba/security/Project/ProjectCreationRequest.java new file mode 100644 index 0000000..5b613f3 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/ProjectCreationRequest.java @@ -0,0 +1,25 @@ +package com.roba.security.Project; + + + +import com.roba.security.Client.Client; + +import java.util.Date; + +public record ProjectCreationRequest( + String projectName, + boolean disableActivity, + boolean disableIdleTime, + //Integer clientId, + Client client, + boolean billable, + BudgetType budgetType, + BudgetBasedOn budgetBasedOn, + Double budgetCost, + Double budgetNotifyAt, + Date budgetStartDate, + boolean budgetIncludeNonBillabeTime, + //Budget budget, + Integer organizationId +) { +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/Project/ProjectEditRequest.java b/security/security/src/main/java/com/roba/security/Project/ProjectEditRequest.java new file mode 100644 index 0000000..959cdf6 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/ProjectEditRequest.java @@ -0,0 +1,25 @@ +package com.roba.security.Project; + + +import com.roba.security.Client.Client; + +import java.util.Date; + +public record ProjectEditRequest( + Integer id, + String projectName, + //Budget budget, + boolean disableActivity, + boolean disableIdleTime, + //Integer clientId, + Client client, + boolean billable, + BudgetType budgetType, + BudgetBasedOn budgetBasedOn, + Double budgetCost, + Double budgetNotifyAt, + Date budgetStartDate, + boolean budgetIncludeNonBillabeTime, + Integer organizationId +) { +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/Project/ProjectRepository.java b/security/security/src/main/java/com/roba/security/Project/ProjectRepository.java new file mode 100644 index 0000000..f415ac4 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/ProjectRepository.java @@ -0,0 +1,9 @@ +package com.roba.security.Project; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ProjectRepository extends JpaRepository { + // List findAllByManagerId(Integer managerId); +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/Project/ProjectService.java b/security/security/src/main/java/com/roba/security/Project/ProjectService.java new file mode 100644 index 0000000..4d4acd3 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/Project/ProjectService.java @@ -0,0 +1,72 @@ +package com.roba.security.Project; + + + +import org.springframework.stereotype.Service; +import java.util.List; +import java.util.Optional; + +@Service +public record ProjectService(ProjectRepository projectRepository) { + public void createProject(ProjectCreationRequest projectRequest) { + if (projectRequest.projectName() == null || projectRequest.projectName().isEmpty()) { + throw new IllegalArgumentException("Project name cannot be empty"); + } + Project project = Project.builder() + .projectName(projectRequest.projectName()) + .billable(projectRequest.billable()) + .budgetBasedOn(projectRequest.budgetBasedOn()) + .budgetCost(projectRequest.budgetCost()) + .budgetNotifyAt(projectRequest.budgetNotifyAt()) + .budgetStartDate(projectRequest.budgetStartDate()) + .budgetType(projectRequest.budgetType()) + .client(projectRequest.client()) + //.clientId(projectRequest.clientId()) + .budgetIncludeNonBillabeTime(projectRequest.budgetIncludeNonBillabeTime()) + .disableActivity(projectRequest.disableActivity()) + .disableIdleTime(projectRequest.disableIdleTime()) + .organizationId(projectRequest.organizationId()) + //.budget(projectRequest.budget()) + .build(); + //todo: check constraints + //todo: fix pgadmin bug + projectRepository.save(project); + } + public void editProject(ProjectEditRequest projectRequest) { + //Project project = projectRepository.getById(projectRequest.id()); + Project project = Project.builder() + .id(projectRequest.id()) + .projectName(projectRequest.projectName()) + .billable(projectRequest.billable()) + .budgetBasedOn(projectRequest.budgetBasedOn()) + .budgetCost(projectRequest.budgetCost()) + .budgetNotifyAt(projectRequest.budgetNotifyAt()) + .budgetStartDate(projectRequest.budgetStartDate()) + .budgetType(projectRequest.budgetType()) + //.clientId(projectRequest.clientId()) + .client(projectRequest.client()) + .budgetIncludeNonBillabeTime(projectRequest.budgetIncludeNonBillabeTime()) + .disableActivity(projectRequest.disableActivity()) + .disableIdleTime(projectRequest.disableIdleTime()) + .organizationId(projectRequest.organizationId()) +// .budget(projectRequest.budget()) + .build(); + //todo: check constraints + //todo: fix pgadmin bug + projectRepository.save(project); + } + public void deleteProject(Integer id){ + try { + projectRepository.deleteById(id); + } catch (Exception e) { + // Handle exception + throw new RuntimeException("Failed to delete project with id: " + id, e); + } + } + public Optional findById(Integer id){ + return projectRepository.findById(id); + } +// public List findAllByManager(Integer managerId){ +// return projectRepository.findAllByManagerId(managerId); +// } +} \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index 02b5d44..45435c1 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -10,7 +10,7 @@ import com.roba.security.token.TokenType; import com.roba.security.user.Role; import com.roba.security.user.User; -import com.roba.security.user.UserRespository; +import com.roba.security.user.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; @@ -25,7 +25,7 @@ @RequiredArgsConstructor public class AuthenticationService { - private final UserRespository repository; + private final UserRepository repository; private final PasswordEncoder passwordEncoder; private final JwtService jwtService; private final AuthenticationManager authenticationManager; diff --git a/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java b/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java index 519f078..a5a119d 100644 --- a/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java +++ b/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java @@ -1,6 +1,6 @@ package com.roba.security.config; -import com.roba.security.user.UserRespository; +import com.roba.security.user.UserRepository; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -8,8 +8,6 @@ import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -18,7 +16,7 @@ @Configuration @RequiredArgsConstructor public class ApplicationConfig { - private final UserRespository repository; + private final UserRepository repository; @Bean public UserDetailsService userDetailsService() { return username -> repository.findByworkEmail(username) diff --git a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java index 34a557c..c1f7c24 100644 --- a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java +++ b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java @@ -1,10 +1,10 @@ package com.roba.security.config; -import com.roba.security.demo.BusinessOwnerController; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -15,6 +15,8 @@ import org.springframework.security.web.authentication.logout.LogoutHandler; +import static com.roba.security.user.Permission.*; +import static com.roba.security.user.Role.ORGANIZATION_MANAGER; import static com.roba.security.user.Role.OWNER; @Configuration @@ -31,10 +33,15 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti http .csrf(csrf -> csrf.disable()) // Disable CSRF protection - .authorizeRequests(authorizeRequests -> + .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers("/api/v1/auth/**").permitAll()// Allow access to public endpoints - .requestMatchers("/api/v1/admin/**").hasAnyRole(OWNER.name())//allow access by biz owner to admin endpoint + .requestMatchers("api/v1/users/**").permitAll() + .requestMatchers("/api/v1/project/**").hasAnyRole(OWNER.name(),ORGANIZATION_MANAGER.name())//allow access by biz owner to admin endpoint + .requestMatchers(HttpMethod.GET,"/api/v1/project/**").hasAnyAuthority(PROJECT_READ.name()) + .requestMatchers(HttpMethod.PUT,"/api/v1/project/**").hasAnyAuthority(PROJECT_UPDATE.name()) + .requestMatchers(HttpMethod.POST,"/api/v1/project/**").hasAnyAuthority(PROJECT_CREATE.name()) + .requestMatchers(HttpMethod.DELETE,"/api/v1/project/**").hasAnyAuthority(PROJECT_DELETE.name()) .anyRequest().authenticated() // Require authentication for other requests ) .sessionManagement(sessionManagement -> diff --git a/security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java b/security/security/src/main/java/com/roba/security/demo/ProjectController1.java similarity index 67% rename from security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java rename to security/security/src/main/java/com/roba/security/demo/ProjectController1.java index 1b072d9..a528f11 100644 --- a/security/security/src/main/java/com/roba/security/demo/BusinessOwnerController.java +++ b/security/security/src/main/java/com/roba/security/demo/ProjectController1.java @@ -1,17 +1,24 @@ package com.roba.security.demo; + import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/v1/admin") -public class BusinessOwnerController { +@RequestMapping("/api/v1/projects") + +public class ProjectController1 { + + @GetMapping public String get() { - return "get in admin"; + return "get controller"; } + @PostMapping - public String post(){ return "POST:IN ADMIN";} + public String post() { + return "post controller"; + } } diff --git a/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java b/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java new file mode 100644 index 0000000..26fb6d6 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java @@ -0,0 +1,10 @@ +package com.roba.security.user; + + +import lombok.Data; + +@Data +public class ChangePasswordRequest { + private String currentPassword; + private String newPassword; +} diff --git a/security/security/src/main/java/com/roba/security/user/Permission.java b/security/security/src/main/java/com/roba/security/user/Permission.java index 39149fb..be24339 100644 --- a/security/security/src/main/java/com/roba/security/user/Permission.java +++ b/security/security/src/main/java/com/roba/security/user/Permission.java @@ -6,10 +6,52 @@ @RequiredArgsConstructor public enum Permission { - OWNER_READ("owner:read"), - OWNER_UPDATE("owner:update"), - OWNER_CREATE("owner:create"), - OWNER_DELETE("owner:delete"); + PROJECT_READ("project:read"), + PROJECT_UPDATE("project:update"), + PROJECT_CREATE("project:create"), + PROJECT_DELETE("project:delete"), + ORGANIZATION_CREATE("organization:create"), + ORGANIZATION_DELETE("organization:delete"), + ORGANIZATION_UPDATE("organization:update"),//INVITE USERS=ADD MEMBERS + ORGANIZATION_READ("organization:read"),//VIEW MEMBERS + + PAYMENT_CREATE("payment:create"), + PAYMEMT_DELETE("payment:delete"), + PAYMENT_UPDATE("payment:update"), + PAYMENT_READ("payment:read"), + + PAYROLL_CREATE("payment:create"), + PAYROLL_DELETE("payment:delete"), + PAYROLL_UPDATE("payment:update"), + PAYROLL_READ("payment:read"), + + TEAMINVOICE_CREATE("invoice:create"), + TEAMINVOICE_DELETE("invoice:delete"), + TEAMINVOICE_UPDATE("invoice:update"), + TEAMINVOICE_READ("invoice:read"), + + + CLIENTINVOICE_CREATE("invoice:create"), + CLIENTINVOICE_DELETE("invoice:delete"), + CLIENTINVOICE_UPDATE("invoice:update"), + CLIENTINVOICE_READ("invoice:read"), + + + TIMESHEET_CREATE("timesheets:create"), + TIMESHEET_DELETE("timesheets:delete"), + TIMESHEET_UPDATE("timesheets:update"), + TIMESHEET_READ("timesheets:read"), + REPORTS_CREATE("reports:create"), + REPORTS_DELETE("reports:delete"), + REPORTS_UPDATE("reports:update"), + REPORTS_READ("reports:read"), + + + + + + + ; diff --git a/security/security/src/main/java/com/roba/security/user/ResourceNotFoundException.java b/security/security/src/main/java/com/roba/security/user/ResourceNotFoundException.java new file mode 100644 index 0000000..2f669eb --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/ResourceNotFoundException.java @@ -0,0 +1,12 @@ +package com.roba.security.user; + + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.NOT_FOUND) +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/Role.java b/security/security/src/main/java/com/roba/security/user/Role.java index a80fb99..e5ee41d 100644 --- a/security/security/src/main/java/com/roba/security/user/Role.java +++ b/security/security/src/main/java/com/roba/security/user/Role.java @@ -17,11 +17,21 @@ public enum Role { USER(Collections.emptySet()), - OWNER(Set.of(OWNER_READ, - OWNER_UPDATE, - OWNER_DELETE, - OWNER_CREATE)), - BUSINESS_MANAGER(Collections.emptySet()), + OWNER(Set.of(PROJECT_READ, + PROJECT_UPDATE, + PROJECT_DELETE, + PROJECT_CREATE, + PAYMENT_READ, + PAYMENT_UPDATE, + PAYMENT_CREATE + + + )), + + ORGANIZATION_MANAGER(Set.of(PROJECT_READ, + PROJECT_UPDATE, + PROJECT_DELETE, + PROJECT_CREATE)), PROJECT_MANAGER(Collections.emptySet()); @Getter @@ -29,9 +39,9 @@ public enum Role { public List getAuthorities(){ var authorities=getPermissions() .stream() - .map(permission -> new SimpleGrantedAuthority(permission.name())) + .map(permission -> new SimpleGrantedAuthority(permission.getPermission())) .collect(Collectors.toList()); - authorities.add(new SimpleGrantedAuthority("ROLE"+this.name())); + authorities.add(new SimpleGrantedAuthority("ROLE_"+this.name())); return authorities; } } diff --git a/security/security/src/main/java/com/roba/security/user/UnauthorizedException.java b/security/security/src/main/java/com/roba/security/user/UnauthorizedException.java new file mode 100644 index 0000000..0dfa3d2 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/UnauthorizedException.java @@ -0,0 +1,11 @@ +package com.roba.security.user; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.UNAUTHORIZED) +public class UnauthorizedException extends RuntimeException { + public UnauthorizedException(String message) { + super(message); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/UserController.java b/security/security/src/main/java/com/roba/security/user/UserController.java new file mode 100644 index 0000000..9c6f47a --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/UserController.java @@ -0,0 +1,25 @@ +package com.roba.security.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/users") +public class UserController { + + @Autowired + private UserService userService; + + @PutMapping("/{userId}/profile") + public ResponseEntity updateProfile(@PathVariable("userId") Integer userId, @RequestBody UserProfileUpdateRequest request) { + userService.updateUserProfile(userId, request); + return ResponseEntity.ok().build(); + } + + @PutMapping("/{userId}/password") + public ResponseEntity changePassword(@PathVariable("userId") Integer userId, @RequestBody ChangePasswordRequest request) { + userService.changeUserPassword(userId, request); + return ResponseEntity.ok().build(); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java b/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java new file mode 100644 index 0000000..0854890 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java @@ -0,0 +1,11 @@ +package com.roba.security.user; + +import lombok.Data; + +@Data +public class UserProfileUpdateRequest { + private String fullName; + private String WorkEmail; + + // Add other fields as needed for profile update +} diff --git a/security/security/src/main/java/com/roba/security/user/UserRespository.java b/security/security/src/main/java/com/roba/security/user/UserRepository.java similarity index 76% rename from security/security/src/main/java/com/roba/security/user/UserRespository.java rename to security/security/src/main/java/com/roba/security/user/UserRepository.java index 58a395b..88d702e 100644 --- a/security/security/src/main/java/com/roba/security/user/UserRespository.java +++ b/security/security/src/main/java/com/roba/security/user/UserRepository.java @@ -4,7 +4,7 @@ import java.util.Optional; -public interface UserRespository extends JpaRepository { +public interface UserRepository extends JpaRepository { Optional findByworkEmail(String workEmail); // Optional findByWorkEmail(String workEmail); diff --git a/user/src/main/java/com/roba/user/UserRepository.java b/user/src/main/java/com/roba/user/UserRepository.java index f18c563..e6fc952 100644 --- a/user/src/main/java/com/roba/user/UserRepository.java +++ b/user/src/main/java/com/roba/user/UserRepository.java @@ -1,6 +1,6 @@ -package com.roba.user; - -import org.springframework.data.jpa.repository.JpaRepository; - -public interface UserRepository extends JpaRepository { -} +//package com.roba.user; +// +//import org.springframework.data.jpa.repository.JpaRepository; +// +//public interface UserRepository extends JpaRepository { +//} From dfdf7fb2246c6d795079774493213b5c2a8ebca5 Mon Sep 17 00:00:00 2001 From: Roba Date: Fri, 10 May 2024 22:22:43 +0200 Subject: [PATCH 06/12] change password ,find all users,find user by id --- .../security/auth/AuthenticationService.java | 78 ++++++++++++++++++- .../config/SecurityConfiguration.java | 5 +- .../security/user/ChangePasswordRequest.java | 1 + .../roba/security/user/UserController.java | 25 +++++- .../user/UserProfileUpdateRequest.java | 1 + .../roba/security/user/UserRepository.java | 5 +- 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index 45435c1..73af82c 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -8,9 +8,7 @@ import com.roba.security.token.Token; import com.roba.security.token.TokenRepository; import com.roba.security.token.TokenType; -import com.roba.security.user.Role; -import com.roba.security.user.User; -import com.roba.security.user.UserRepository; +import com.roba.security.user.*; import lombok.RequiredArgsConstructor; import org.springframework.security.authentication.AuthenticationManager; @@ -19,6 +17,7 @@ import org.springframework.stereotype.Service; +import java.util.List; import java.util.Optional; @Service @@ -126,4 +125,77 @@ private void saveUserToken(User user, String jwtToken) { var token = Token.builder().user(user).token(jwtToken).tokenType(TokenType.BEARER).revoked(false).expired(false).build(); tokenRepository.save(token); } + + + + + + + + + +/////////profile edits + + public String updateUserProfile(Integer userId, UserProfileUpdateRequest request) { + User user = repository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + if (request.getFullName() != null) { + user.setFullName(request.getFullName()); + } + if (request.getWorkEmail() != null) { + user.setWorkEmail(request.getWorkEmail()); + } + + repository.save(user); + + + + return ""; + + + + + + + } + + public void changeUserPassword(Integer userId, ChangePasswordRequest request) { + User user = repository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + + + + + // Check if the current password matches + if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) { + throw new UnauthorizedException("Current password is incorrect"); + } + // Update password + user.setPassword(passwordEncoder.encode(request.getNewPassword())); + + repository.save(user); + } + + + + + + + + + + //get all users + public List getAllUsers() { + return repository.findAll(); + } + + public User getUserById(Integer userId) { + return repository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + } + public List getUsersByRole(Role role) { + return repository.findByRole(role); + } + + } diff --git a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java index c1f7c24..16a752a 100644 --- a/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java +++ b/security/security/src/main/java/com/roba/security/config/SecurityConfiguration.java @@ -36,12 +36,15 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .authorizeHttpRequests(authorizeRequests -> authorizeRequests .requestMatchers("/api/v1/auth/**").permitAll()// Allow access to public endpoints - .requestMatchers("api/v1/users/**").permitAll() + .requestMatchers("api/users/**").permitAll() .requestMatchers("/api/v1/project/**").hasAnyRole(OWNER.name(),ORGANIZATION_MANAGER.name())//allow access by biz owner to admin endpoint .requestMatchers(HttpMethod.GET,"/api/v1/project/**").hasAnyAuthority(PROJECT_READ.name()) .requestMatchers(HttpMethod.PUT,"/api/v1/project/**").hasAnyAuthority(PROJECT_UPDATE.name()) .requestMatchers(HttpMethod.POST,"/api/v1/project/**").hasAnyAuthority(PROJECT_CREATE.name()) .requestMatchers(HttpMethod.DELETE,"/api/v1/project/**").hasAnyAuthority(PROJECT_DELETE.name()) + + .requestMatchers("/api/v1/payment/**").hasAnyRole(OWNER.name()) + .requestMatchers("/payroll/**").hasAnyRole(OWNER.name()) .anyRequest().authenticated() // Require authentication for other requests ) .sessionManagement(sessionManagement -> diff --git a/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java b/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java index 26fb6d6..ae9b85e 100644 --- a/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java +++ b/security/security/src/main/java/com/roba/security/user/ChangePasswordRequest.java @@ -7,4 +7,5 @@ public class ChangePasswordRequest { private String currentPassword; private String newPassword; + // private String workEmail; } diff --git a/security/security/src/main/java/com/roba/security/user/UserController.java b/security/security/src/main/java/com/roba/security/user/UserController.java index 9c6f47a..694dfd6 100644 --- a/security/security/src/main/java/com/roba/security/user/UserController.java +++ b/security/security/src/main/java/com/roba/security/user/UserController.java @@ -1,15 +1,20 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationService; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.List; + @RestController @RequestMapping("/api/users") +@RequiredArgsConstructor public class UserController { - @Autowired - private UserService userService; + + private final AuthenticationService userService; @PutMapping("/{userId}/profile") public ResponseEntity updateProfile(@PathVariable("userId") Integer userId, @RequestBody UserProfileUpdateRequest request) { @@ -22,4 +27,20 @@ public ResponseEntity changePassword(@PathVariable("userId") Integer userId, userService.changeUserPassword(userId, request); return ResponseEntity.ok().build(); } + + + @GetMapping("/all") + public List getAllUsers() { + return userService.getAllUsers(); + } + + @GetMapping("/{userId}") + public User getUserById(@PathVariable Integer userId) { + return userService.getUserById(userId); + } + + @GetMapping("/role/{role}") + public List getUsersByRole(@PathVariable Role role) { + return userService.getUsersByRole(role); + } } diff --git a/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java b/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java index 0854890..955376e 100644 --- a/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java +++ b/security/security/src/main/java/com/roba/security/user/UserProfileUpdateRequest.java @@ -7,5 +7,6 @@ public class UserProfileUpdateRequest { private String fullName; private String WorkEmail; + // Add other fields as needed for profile update } diff --git a/security/security/src/main/java/com/roba/security/user/UserRepository.java b/security/security/src/main/java/com/roba/security/user/UserRepository.java index 88d702e..ce88755 100644 --- a/security/security/src/main/java/com/roba/security/user/UserRepository.java +++ b/security/security/src/main/java/com/roba/security/user/UserRepository.java @@ -2,10 +2,13 @@ import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface UserRepository extends JpaRepository { Optional findByworkEmail(String workEmail); - // Optional findByWorkEmail(String workEmail); + List findByRole(Role role); + + // Optional findByWorkEmail(String workEmail); } From 60ac952631fa58234aa776df7bb900048c479fb3 Mon Sep 17 00:00:00 2001 From: Roba Date: Sun, 12 May 2024 09:47:08 +0200 Subject: [PATCH 07/12] command design pattern --- .../security/auth/AuthenticationService.java | 68 ++++++++++++------ .../security/config/ApplicationConfig.java | 2 + .../roba/security/token/TokenRepository.java | 3 + .../security/user/ChangePasswordCommand.java | 22 ++++++ .../java/com/roba/security/user/Command.java | 5 ++ .../roba/security/user/CommandFactory.java | 38 ++++++++++ .../security/user/GetAllUsersCommand.java | 18 +++++ .../security/user/GetUserByIdCommand.java | 20 ++++++ .../security/user/GetUsersByRoleCommand.java | 22 ++++++ .../security/user/UpdateProfileCommand.java | 21 ++++++ .../roba/security/user/UserController.java | 38 ++++++++-- user/src/main/java/com/roba/user/User.java | 68 +++++++++--------- .../java/com/roba/user/UserController.java | 68 +++++++++--------- .../main/java/com/roba/user/UserService.java | 70 +++++++++---------- 14 files changed, 332 insertions(+), 131 deletions(-) create mode 100644 security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java create mode 100644 security/security/src/main/java/com/roba/security/user/Command.java create mode 100644 security/security/src/main/java/com/roba/security/user/CommandFactory.java create mode 100644 security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java create mode 100644 security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java create mode 100644 security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java create mode 100644 security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index 73af82c..f84e88d 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -1,8 +1,9 @@ package com.roba.security.auth; +import com.roba.security.Project.Project; import com.roba.security.config.JwtService; - +import java.util.Optional; import com.roba.security.organization.Organization; import com.roba.security.organization.OrganizationRepository; import com.roba.security.token.Token; @@ -27,7 +28,7 @@ public class AuthenticationService { private final UserRepository repository; private final PasswordEncoder passwordEncoder; private final JwtService jwtService; - private final AuthenticationManager authenticationManager; + private final AuthenticationManager authenticationManager; private final TokenRepository tokenRepository; private final Emailvalidator emailvalidator; private final OrganizationRepository organizationRepository; @@ -40,10 +41,10 @@ public AuthenticationResponse register(RegisterRequest request) { throw new IllegalStateException("Email is already in use"); } Role role; - if(request.getRole().equals("team_member")) { + if (request.getRole().equals("team_member")) { role = Role.USER; - }else { - role = request.getRole(); + } else { + role = request.getRole(); } if (role == Role.OWNER) { // Create organization @@ -66,8 +67,7 @@ public AuthenticationResponse register(RegisterRequest request) { saveUserToken(savedUser, jwtToken); return AuthenticationResponse.builder().token(jwtToken).build(); - } - else if(role == Role.USER){ + } else if (role == Role.USER) { // Fetch organization by name Optional organizationOptional = organizationRepository.findByName(request.getOrganizationName()); if (organizationOptional.isEmpty()) { @@ -87,12 +87,13 @@ else if(role == Role.USER){ saveUserToken(savedUser, jwtToken); return AuthenticationResponse.builder().token(jwtToken).build(); - }else{ + } else { throw new IllegalStateException("Role not supported"); } } + private void revokeAllUserTokens(User user) { var validUserTokens = tokenRepository.findAllValidTokenByUser(user.getId()); if (validUserTokens.isEmpty()) @@ -114,31 +115,42 @@ public AuthenticationResponse authenticate(AuthenticationRequest request) { ); var user = repository.findByworkEmail(request.getWorkEmail()) .orElseThrow(); - var jwtToken=jwtService.generateToken(user); + var jwtToken = jwtService.generateToken(user); revokeAllUserTokens(user); - saveUserToken(user,jwtToken); + saveUserToken(user, jwtToken); return AuthenticationResponse.builder().token(jwtToken).build(); } + private void saveUserToken(User user, String jwtToken) { var token = Token.builder().user(user).token(jwtToken).tokenType(TokenType.BEARER).revoked(false).expired(false).build(); tokenRepository.save(token); } +/////////profile edits +// public String updateUserProfile(Integer userId, UserProfileUpdateRequest request) { +// User user = repository.findById(userId) +// .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); +// if (request.getFullName() != null) { +// user.setFullName(request.getFullName()); +// } +// if (request.getWorkEmail() != null) { +// user.setWorkEmail(request.getWorkEmail()); +// } +// +// repository.save(user); +// return ""; +// - - - - -/////////profile edits - public String updateUserProfile(Integer userId, UserProfileUpdateRequest request) { User user = repository.findById(userId) .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + + // Update user profile fields if provided in the request if (request.getFullName() != null) { user.setFullName(request.getFullName()); } @@ -146,18 +158,30 @@ public String updateUserProfile(Integer userId, UserProfileUpdateRequest request user.setWorkEmail(request.getWorkEmail()); } - repository.save(user); + // Save the updated user record + User savedUser = repository.save(user); + + // Refresh the associated token(s) to prevent expiration + refreshUserTokens(savedUser); + + return "Profile updated successfully"; + } + private void refreshUserTokens(User user) { + List userTokens = tokenRepository.findByUser(user); + userTokens.forEach(token -> { + token.setExpired(false); // Set the token as not expired + tokenRepository.save(token); // Save the refreshed token + }); - return ""; - } +} public void changeUserPassword(Integer userId, ChangePasswordRequest request) { User user = repository.findById(userId) @@ -189,9 +213,9 @@ public List getAllUsers() { return repository.findAll(); } - public User getUserById(Integer userId) { - return repository.findById(userId) - .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + public OptionalgetUserById(Integer userId) { + return repository.findById(userId); + // .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); } public List getUsersByRole(Role role) { return repository.findByRole(role); diff --git a/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java b/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java index a5a119d..86ce4ad 100644 --- a/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java +++ b/security/security/src/main/java/com/roba/security/config/ApplicationConfig.java @@ -13,6 +13,8 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; @Configuration @RequiredArgsConstructor public class ApplicationConfig { diff --git a/security/security/src/main/java/com/roba/security/token/TokenRepository.java b/security/security/src/main/java/com/roba/security/token/TokenRepository.java index aeb4ee7..e3b294e 100644 --- a/security/security/src/main/java/com/roba/security/token/TokenRepository.java +++ b/security/security/src/main/java/com/roba/security/token/TokenRepository.java @@ -1,6 +1,7 @@ package com.roba.security.token; +import com.roba.security.user.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import java.util.Optional; @@ -15,4 +16,6 @@ public interface TokenRepository extends JpaRepository { List findAllValidTokenByUser(Integer userId); Optional findByToken(String token); + + List findByUser(User user); } diff --git a/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java b/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java new file mode 100644 index 0000000..29dc061 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java @@ -0,0 +1,22 @@ +package com.roba.security.user; + + +import com.roba.security.auth.AuthenticationService; + +public class ChangePasswordCommand implements Command { + private final AuthenticationService userService; + private final Integer userId; + private final ChangePasswordRequest request; + + public ChangePasswordCommand(AuthenticationService userService, Integer userId, ChangePasswordRequest request) { + this.userService = userService; + this.userId = userId; + this.request = request; + } + + @Override + public void execute() { + userService.changeUserPassword(userId, request); + } +} + diff --git a/security/security/src/main/java/com/roba/security/user/Command.java b/security/security/src/main/java/com/roba/security/user/Command.java new file mode 100644 index 0000000..dca777c --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/Command.java @@ -0,0 +1,5 @@ +package com.roba.security.user; + +public interface Command { + void execute(); +} diff --git a/security/security/src/main/java/com/roba/security/user/CommandFactory.java b/security/security/src/main/java/com/roba/security/user/CommandFactory.java new file mode 100644 index 0000000..a27954a --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/CommandFactory.java @@ -0,0 +1,38 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationService; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Optional; + +@Component +public class CommandFactory { + private final AuthenticationService userService; + + public CommandFactory(AuthenticationService userService) { + this.userService = userService; + } + + public Command createUpdateProfileCommand(Integer userId, UserProfileUpdateRequest request) { + return new UpdateProfileCommand(userService, userId, request); + } + + public Command createChangePasswordCommand(Integer userId, ChangePasswordRequest request) { + return new ChangePasswordCommand(userService, userId, request); + } + public Command createGetAllUsersCommand() { + return new GetAllUsersCommand(userService); + } + public Command createGetUserByIdCommand(Integer userId) { + return new GetUserByIdCommand(userId, userService);} + + public Command createGetUsersByRoleCommand(Role role) { + return new GetUsersByRoleCommand(role, userService); + } + + + + + +} diff --git a/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java b/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java new file mode 100644 index 0000000..db3bab0 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java @@ -0,0 +1,18 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationService; + +import java.util.List; + +public class GetAllUsersCommand implements Command { + private final AuthenticationService userService; + + public GetAllUsersCommand(AuthenticationService userService) { + this.userService = userService; + } + + @Override + public void execute() { + userService.getAllUsers(); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java b/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java new file mode 100644 index 0000000..91ff8af --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java @@ -0,0 +1,20 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationService; + +import java.util.Optional; + +public class GetUserByIdCommand implements Command { + private final Integer userId; + private final AuthenticationService userService; + + public GetUserByIdCommand(Integer userId, AuthenticationService userService) { + this.userId = userId; + this.userService = userService; + } + + @Override + public void execute() { + userService.getUserById(userId); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java b/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java new file mode 100644 index 0000000..3b5547c --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java @@ -0,0 +1,22 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationService; + +import java.util.List; + +public class GetUsersByRoleCommand implements Command { + + + private final Role role; + private final AuthenticationService userService; + + public GetUsersByRoleCommand(Role role, AuthenticationService userService) { + this.role = role; + this.userService = userService; + } + + @Override + public void execute() { + userService.getUsersByRole(role); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java b/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java new file mode 100644 index 0000000..f37f12c --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java @@ -0,0 +1,21 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationService; + +public class UpdateProfileCommand implements Command{ + private final AuthenticationService userService; + private final Integer userId; + private final UserProfileUpdateRequest request; + + + public UpdateProfileCommand(AuthenticationService userService, Integer userId, UserProfileUpdateRequest request) { + this.userService = userService; + this.userId = userId; + this.request = request; + } + + @Override + public void execute() { + userService.updateUserProfile(userId, request); + } +} diff --git a/security/security/src/main/java/com/roba/security/user/UserController.java b/security/security/src/main/java/com/roba/security/user/UserController.java index 694dfd6..64a0eae 100644 --- a/security/security/src/main/java/com/roba/security/user/UserController.java +++ b/security/security/src/main/java/com/roba/security/user/UserController.java @@ -5,42 +5,68 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; - +import java.util.Optional; import java.util.List; - + import com.roba.security.user.CommandFactory; @RestController @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { - + private final CommandFactory commandFactory; + // @Autowired private final AuthenticationService userService; @PutMapping("/{userId}/profile") +// public ResponseEntity updateProfile(@PathVariable("userId") Integer userId, @RequestBody UserProfileUpdateRequest request) { +// System.out.println("gg"); +// userService.updateUserProfile(userId, request); +// return ResponseEntity.ok().build(); +// } + public ResponseEntity updateProfile(@PathVariable("userId") Integer userId, @RequestBody UserProfileUpdateRequest request) { - userService.updateUserProfile(userId, request); + Command command = commandFactory.createUpdateProfileCommand(userId, request); + command.execute(); return ResponseEntity.ok().build(); } + @PutMapping("/{userId}/password") +// public ResponseEntity changePassword(@PathVariable("userId") Integer userId, @RequestBody ChangePasswordRequest request) { +// userService.changeUserPassword(userId, request); +// return ResponseEntity.ok().build(); +// } public ResponseEntity changePassword(@PathVariable("userId") Integer userId, @RequestBody ChangePasswordRequest request) { - userService.changeUserPassword(userId, request); + Command command = commandFactory.createChangePasswordCommand(userId, request); + command.execute(); return ResponseEntity.ok().build(); } @GetMapping("/all") public List getAllUsers() { + Command command = commandFactory.createGetAllUsersCommand(); + command.execute(); return userService.getAllUsers(); } + @GetMapping("/{userId}") - public User getUserById(@PathVariable Integer userId) { + public Optional getUserById(@PathVariable Integer userId) { + Command command = commandFactory.createGetUserByIdCommand(userId); + command.execute(); return userService.getUserById(userId); } @GetMapping("/role/{role}") public List getUsersByRole(@PathVariable Role role) { + Command command = commandFactory.createGetUsersByRoleCommand(role); + command.execute(); return userService.getUsersByRole(role); } + + + + + } diff --git a/user/src/main/java/com/roba/user/User.java b/user/src/main/java/com/roba/user/User.java index 2006641..946f6b2 100644 --- a/user/src/main/java/com/roba/user/User.java +++ b/user/src/main/java/com/roba/user/User.java @@ -1,34 +1,34 @@ -package com.roba.user; - -import jakarta.persistence.*; -import lombok.*; - -import java.time.LocalDate; - -@Entity -@Getter -@Setter -@AllArgsConstructor -@NoArgsConstructor -@Builder -@Data -@Table(name="_user") -public class User { - @Id - @SequenceGenerator( - name="user_id_sequence", - sequenceName="user_id_sequence" - ) - @GeneratedValue( - strategy = GenerationType.SEQUENCE, - generator = "user_id_seqeunce" - ) - private Integer id; - private String fullName; - private String workEmail; - private String password; - private String role; - private LocalDate joinDate; - private Integer organizationId; - -} +//package com.roba.user; +// +//import jakarta.persistence.*; +//import lombok.*; +// +//import java.time.LocalDate; +// +//@Entity +//@Getter +//@Setter +//@AllArgsConstructor +//@NoArgsConstructor +//@Builder +//@Data +//@Table(name="_user") +//public class User { +// @Id +// @SequenceGenerator( +// name="user_id_sequence", +// sequenceName="user_id_sequence" +// ) +// @GeneratedValue( +// strategy = GenerationType.SEQUENCE, +// generator = "user_id_seqeunce" +// ) +// private Integer id; +// private String fullName; +// private String workEmail; +// private String password; +// private String role; +// private LocalDate joinDate; +// private Integer organizationId; +// +//} diff --git a/user/src/main/java/com/roba/user/UserController.java b/user/src/main/java/com/roba/user/UserController.java index eb2e723..f4cfac6 100644 --- a/user/src/main/java/com/roba/user/UserController.java +++ b/user/src/main/java/com/roba/user/UserController.java @@ -1,36 +1,36 @@ -package com.roba.user; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - - -@RestController -@RequestMapping("api/v1/users") -@RequiredArgsConstructor -public class UserController { -private final UserService service; - private final UserService userService; - - @PostMapping - @ResponseStatus(HttpStatus.CREATED) -// public void save(@RequestBody User user){ -// service.saveUser(user); +//package com.roba.user; // +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +// +//import org.springframework.http.HttpStatus; +//import org.springframework.http.ResponseEntity; +//import org.springframework.web.bind.annotation.*; +// +//import java.util.List; +// +// +//@RestController +//@RequestMapping("api/v1/users") +//@RequiredArgsConstructor +//public class UserController { +//private final UserService service; +// private final UserService userService; +// +// @PostMapping +// @ResponseStatus(HttpStatus.CREATED) +//// public void save(@RequestBody User user){ +//// service.saveUser(user); +//// +//// } +// public void createUser(@RequestBody UserRequest userRequest){ +// userService.createUser(userRequest); +// +// } +// +// @GetMapping +// @ResponseStatus(HttpStatus.OK) +// public List getAllUsers(){ +// return userService.getAllUsers(); // } - public void createUser(@RequestBody UserRequest userRequest){ - userService.createUser(userRequest); - - } - - @GetMapping - @ResponseStatus(HttpStatus.OK) - public List getAllUsers(){ - return userService.getAllUsers(); - } -} +//} diff --git a/user/src/main/java/com/roba/user/UserService.java b/user/src/main/java/com/roba/user/UserService.java index ae3ad91..ecbac7a 100644 --- a/user/src/main/java/com/roba/user/UserService.java +++ b/user/src/main/java/com/roba/user/UserService.java @@ -1,39 +1,39 @@ -package com.roba.user; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; - -@Service -@RequiredArgsConstructor -@Slf4j -public class UserService { - - - private final UserRepository userRepository; -// public void saveUser(User user) { +//package com.roba.user; // +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Service; // +//import java.util.List; +// +//@Service +//@RequiredArgsConstructor +//@Slf4j +//public class UserService { +// +// +// private final UserRepository userRepository; +//// public void saveUser(User user) { +//// +//// +//// userRepository.save(user); +//// } +// +// public void createUser(UserRequest userRequest) { +// User user = User.builder().fullName(userRequest.getFullName()) +// .workEmail(userRequest.getWorkEmail()).password(userRequest.getPassword()).role(userRequest.getRole()).build(); // userRepository.save(user); +// log.info("is saved user: {}", user); // } - - public void createUser(UserRequest userRequest) { - User user = User.builder().fullName(userRequest.getFullName()) - .workEmail(userRequest.getWorkEmail()).password(userRequest.getPassword()).role(userRequest.getRole()).build(); - userRepository.save(user); - log.info("is saved user: {}", user); - } - - public List getAllUsers(){ - List users= userRepository.findAll(); - return users.stream().map(this::mapToUserResponse).toList(); - } - private UserResponse mapToUserResponse (User user ){ - return UserResponse.builder(). - id(user.getId()) - .fullName(user.getFullName()).workEmail(user.getWorkEmail()).password(user.getPassword()).role(user.getRole()).build(); - } - -} +// +// public List getAllUsers(){ +// List users= userRepository.findAll(); +// return users.stream().map(this::mapToUserResponse).toList(); +// } +// private UserResponse mapToUserResponse (User user ){ +// return UserResponse.builder(). +// id(user.getId()) +// .fullName(user.getFullName()).workEmail(user.getWorkEmail()).password(user.getPassword()).role(user.getRole()).build(); +// } +// +//} From d856a4e91bed4dc39454e4273695d3c227f7697f Mon Sep 17 00:00:00 2001 From: Roba Date: Sun, 12 May 2024 22:27:56 +0200 Subject: [PATCH 08/12] command design pattern on register,login,added attributes in user table --- security/security/pom.xml | 4 +++ .../roba/security/SecurityApplication.java | 2 ++ .../auth/AuthenticationController.java | 33 +++++++++++++++++-- .../security/auth/AuthenticationService.java | 3 ++ .../security/user/AuthenticateCommand.java | 30 +++++++++++++++++ .../security/user/ChangePasswordCommand.java | 6 ++++ .../java/com/roba/security/user/Command.java | 3 ++ .../roba/security/user/CommandFactory.java | 9 +++++ .../security/user/GetAllUsersCommand.java | 6 ++++ .../security/user/GetUserByIdCommand.java | 6 ++++ .../security/user/GetUsersByRoleCommand.java | 6 ++++ .../roba/security/user/RegisterCommand.java | 27 +++++++++++++++ .../security/user/UpdateProfileCommand.java | 6 ++++ .../java/com/roba/security/user/User.java | 22 +++++++++++-- .../roba/security/user/UserController.java | 11 +++++++ 15 files changed, 168 insertions(+), 6 deletions(-) create mode 100644 security/security/src/main/java/com/roba/security/user/AuthenticateCommand.java create mode 100644 security/security/src/main/java/com/roba/security/user/RegisterCommand.java diff --git a/security/security/pom.xml b/security/security/pom.xml index e6c9c35..b7b12d6 100644 --- a/security/security/pom.xml +++ b/security/security/pom.xml @@ -66,6 +66,10 @@ spring-security-test test + + org.springframework.boot + spring-boot-starter-data-redis + diff --git a/security/security/src/main/java/com/roba/security/SecurityApplication.java b/security/security/src/main/java/com/roba/security/SecurityApplication.java index 4fac3e2..245fa5a 100644 --- a/security/security/src/main/java/com/roba/security/SecurityApplication.java +++ b/security/security/src/main/java/com/roba/security/SecurityApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +@EnableCaching @SpringBootApplication public class SecurityApplication { diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java index 4d64db7..e95bc9e 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationController.java @@ -1,6 +1,9 @@ package com.roba.security.auth; +import com.roba.security.user.Command; +import com.roba.security.user.CommandFactory; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.web.bind.annotation.*; @@ -10,16 +13,40 @@ @RequiredArgsConstructor public class AuthenticationController { private final AuthenticationService service; + private final CommandFactory commandFactory; + +// @PostMapping("/register") +// public ResponseEntity register(@RequestBody RegisterRequest request) { +// Command command = commandFactory.createRegisterCommand(request); +// command.execute(); +// //return ResponseEntity.ok().build(); +// return ResponseEntity.ok(service.register(request)); +// } +// +// +// @PostMapping("/authenticate") +// public ResponseEntity authenticate(@RequestBody AuthenticationRequest request) { +// Command command = commandFactory.createAuthenticateCommand(request); +// command.execute(); +// //return ResponseEntity.ok().build(); +// return ResponseEntity.ok(service.authenticate(request)); +// } + @PostMapping("/register") public ResponseEntity register(@RequestBody RegisterRequest request) { - return ResponseEntity.ok(service.register(request)); + Command command = commandFactory.createRegisterCommand(request); + command.execute(); + AuthenticationResponse response = command.getResult(); + return ResponseEntity.ok(response); } - @PostMapping("/authenticate") public ResponseEntity authenticate(@RequestBody AuthenticationRequest request) { - return ResponseEntity.ok(service.authenticate(request)); + Command command = commandFactory.createAuthenticateCommand(request); + command.execute(); + AuthenticationResponse response = command.getResult(); + return ResponseEntity.ok(response); } diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index f84e88d..b86d006 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -12,6 +12,7 @@ import com.roba.security.user.*; import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.crypto.password.PasswordEncoder; @@ -33,6 +34,7 @@ public class AuthenticationService { private final Emailvalidator emailvalidator; private final OrganizationRepository organizationRepository; + // @Cacheable(key="#id",value="User") public AuthenticationResponse register(RegisterRequest request) { //boolean isValidEmail =emailvalidator.test(request.getWorkEmail()); //if(!isValidEmail) {throw new IllegalStateException("Invalid email");} @@ -86,6 +88,7 @@ public AuthenticationResponse register(RegisterRequest request) { var jwtToken = jwtService.generateToken(user); saveUserToken(savedUser, jwtToken); + return AuthenticationResponse.builder().token(jwtToken).build(); } else { throw new IllegalStateException("Role not supported"); diff --git a/security/security/src/main/java/com/roba/security/user/AuthenticateCommand.java b/security/security/src/main/java/com/roba/security/user/AuthenticateCommand.java new file mode 100644 index 0000000..0d388e9 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/AuthenticateCommand.java @@ -0,0 +1,30 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationRequest; +import com.roba.security.auth.AuthenticationResponse; +import com.roba.security.auth.AuthenticationService; + +public class AuthenticateCommand implements Command { + private final AuthenticationService service; + private final AuthenticationRequest request; + private AuthenticationResponse result; + public AuthenticateCommand(AuthenticationService service, AuthenticationRequest request) { + this.service = service; + this.request = request; + } + + @Override + public void execute() { + result = service.authenticate(request); + } + @Override + public AuthenticationResponse getResult() { + return result; + } +} + + + + + + diff --git a/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java b/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java index 29dc061..0774998 100644 --- a/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java +++ b/security/security/src/main/java/com/roba/security/user/ChangePasswordCommand.java @@ -1,6 +1,7 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationResponse; import com.roba.security.auth.AuthenticationService; public class ChangePasswordCommand implements Command { @@ -18,5 +19,10 @@ public ChangePasswordCommand(AuthenticationService userService, Integer userId, public void execute() { userService.changeUserPassword(userId, request); } + + @Override + public AuthenticationResponse getResult() { + return null; + } } diff --git a/security/security/src/main/java/com/roba/security/user/Command.java b/security/security/src/main/java/com/roba/security/user/Command.java index dca777c..6362e4d 100644 --- a/security/security/src/main/java/com/roba/security/user/Command.java +++ b/security/security/src/main/java/com/roba/security/user/Command.java @@ -1,5 +1,8 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationResponse; + public interface Command { void execute(); + AuthenticationResponse getResult(); } diff --git a/security/security/src/main/java/com/roba/security/user/CommandFactory.java b/security/security/src/main/java/com/roba/security/user/CommandFactory.java index a27954a..38d5d9d 100644 --- a/security/security/src/main/java/com/roba/security/user/CommandFactory.java +++ b/security/security/src/main/java/com/roba/security/user/CommandFactory.java @@ -1,6 +1,8 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationRequest; import com.roba.security.auth.AuthenticationService; +import com.roba.security.auth.RegisterRequest; import org.springframework.stereotype.Component; import java.util.List; @@ -30,6 +32,13 @@ public Command createGetUserByIdCommand(Integer userId) { public Command createGetUsersByRoleCommand(Role role) { return new GetUsersByRoleCommand(role, userService); } + public Command createRegisterCommand(RegisterRequest request) { + return new RegisterCommand(userService, request); + } + + public Command createAuthenticateCommand(AuthenticationRequest request) { + return new AuthenticateCommand(userService, request); + } diff --git a/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java b/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java index db3bab0..5f7ea63 100644 --- a/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java +++ b/security/security/src/main/java/com/roba/security/user/GetAllUsersCommand.java @@ -1,5 +1,6 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationResponse; import com.roba.security.auth.AuthenticationService; import java.util.List; @@ -15,4 +16,9 @@ public GetAllUsersCommand(AuthenticationService userService) { public void execute() { userService.getAllUsers(); } + + @Override + public AuthenticationResponse getResult() { + return null; + } } diff --git a/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java b/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java index 91ff8af..6f601ad 100644 --- a/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java +++ b/security/security/src/main/java/com/roba/security/user/GetUserByIdCommand.java @@ -1,5 +1,6 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationResponse; import com.roba.security.auth.AuthenticationService; import java.util.Optional; @@ -17,4 +18,9 @@ public GetUserByIdCommand(Integer userId, AuthenticationService userService) { public void execute() { userService.getUserById(userId); } + + @Override + public AuthenticationResponse getResult() { + return null; + } } diff --git a/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java b/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java index 3b5547c..5aef022 100644 --- a/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java +++ b/security/security/src/main/java/com/roba/security/user/GetUsersByRoleCommand.java @@ -1,5 +1,6 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationResponse; import com.roba.security.auth.AuthenticationService; import java.util.List; @@ -19,4 +20,9 @@ public GetUsersByRoleCommand(Role role, AuthenticationService userService) { public void execute() { userService.getUsersByRole(role); } + + @Override + public AuthenticationResponse getResult() { + return null; + } } diff --git a/security/security/src/main/java/com/roba/security/user/RegisterCommand.java b/security/security/src/main/java/com/roba/security/user/RegisterCommand.java new file mode 100644 index 0000000..b4c7e4c --- /dev/null +++ b/security/security/src/main/java/com/roba/security/user/RegisterCommand.java @@ -0,0 +1,27 @@ +package com.roba.security.user; + +import com.roba.security.auth.AuthenticationResponse; +import com.roba.security.auth.AuthenticationService; +import com.roba.security.auth.RegisterRequest; + +public class RegisterCommand implements Command { + private final AuthenticationService service; + private final RegisterRequest request; + private AuthenticationResponse result; + + public RegisterCommand(AuthenticationService service, RegisterRequest request) { + this.service = service; + this.request = request; + } + + @Override + public void execute() { + result = service.register(request); + } + @Override + public AuthenticationResponse getResult() { + return result; + } + + +} diff --git a/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java b/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java index f37f12c..10a83e1 100644 --- a/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java +++ b/security/security/src/main/java/com/roba/security/user/UpdateProfileCommand.java @@ -1,5 +1,6 @@ package com.roba.security.user; +import com.roba.security.auth.AuthenticationResponse; import com.roba.security.auth.AuthenticationService; public class UpdateProfileCommand implements Command{ @@ -18,4 +19,9 @@ public UpdateProfileCommand(AuthenticationService userService, Integer userId, U public void execute() { userService.updateUserProfile(userId, request); } + + @Override + public AuthenticationResponse getResult() { + return null; + } } diff --git a/security/security/src/main/java/com/roba/security/user/User.java b/security/security/src/main/java/com/roba/security/user/User.java index c87c4aa..b749f62 100644 --- a/security/security/src/main/java/com/roba/security/user/User.java +++ b/security/security/src/main/java/com/roba/security/user/User.java @@ -1,6 +1,7 @@ package com.roba.security.user; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.roba.security.Project.Project; import com.roba.security.organization.Organization; import com.roba.security.token.Token; @@ -11,6 +12,7 @@ import org.springframework.security.core.userdetails.UserDetails; +import java.io.Serializable; import java.time.LocalDate; import java.util.Collection; import java.util.List; @@ -24,7 +26,7 @@ @Data @Table(name="_user") @EqualsAndHashCode -public class User implements UserDetails { +public class User implements UserDetails, Serializable { @Id @SequenceGenerator( name="user_id_sequence", @@ -41,6 +43,7 @@ public class User implements UserDetails { @Enumerated(EnumType.STRING) private Role role; @OneToMany(mappedBy = "user") + @JsonIgnore private List tokens; @@ -52,17 +55,29 @@ public class User implements UserDetails { @ManyToOne @JoinColumn(name="organization_id") // This specifies the foreign key column name in the User table + + @JsonIgnore private Organization organization; // Reference to organization) @OneToMany private List projects; private String TimeZone; - private LocalDate DateAdded; + private LocalDate hireDate; + private double hourlyRate; + private double salary; + private String taxInfo; + private Integer usedTimeOff; + private Integer pendingTimeOff; + private Integer balanceTimeOff; + private double totalHoursWorked; + private double WeeklyLimit; + + private LocalDate DateRemoved; private String PayType;//hourly/fixed private Integer BillRate; - private Integer WeeklyLimit; + private Integer DailyLimit; private boolean TrackingEnabled; private boolean TimesheetsEnabled; @@ -71,6 +86,7 @@ public class User implements UserDetails { + @Override public Collection getAuthorities() { return role.getAuthorities(); diff --git a/security/security/src/main/java/com/roba/security/user/UserController.java b/security/security/src/main/java/com/roba/security/user/UserController.java index 64a0eae..f1dd4cd 100644 --- a/security/security/src/main/java/com/roba/security/user/UserController.java +++ b/security/security/src/main/java/com/roba/security/user/UserController.java @@ -3,6 +3,7 @@ import com.roba.security.auth.AuthenticationService; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.Optional; @@ -52,6 +53,7 @@ public List getAllUsers() { @GetMapping("/{userId}") + @Cacheable(key="#id",value="User") public Optional getUserById(@PathVariable Integer userId) { Command command = commandFactory.createGetUserByIdCommand(userId); command.execute(); @@ -65,6 +67,15 @@ public List getUsersByRole(@PathVariable Role role) { return userService.getUsersByRole(role); } +// +// @DeleteMapping("/{userId}") +// public ResponseEntity deleteUserById(@PathVariable("userId") Integer userId) { +// Command command = commandFactory.createDeleteUserCommand(userId); +// command.execute(); +// userService.deleteUserById(userId); +// return ResponseEntity.ok().build(); +// } + From ada8f761284f2630c848ed041f3c47395129c87d Mon Sep 17 00:00:00 2001 From: doaa-shafi Date: Tue, 14 May 2024 14:42:12 +0300 Subject: [PATCH 09/12] redis start --- security/security/pom.xml | 13 +++++++++++++ .../java/com/roba/security/token/Token.java | 18 ++++++++++++------ .../src/main/resources/application.yml | 14 ++++++++++++++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/security/security/pom.xml b/security/security/pom.xml index e6c9c35..20abe9c 100644 --- a/security/security/pom.xml +++ b/security/security/pom.xml @@ -66,7 +66,20 @@ spring-security-test test + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-data-redis + + + + redis.clients + jedis + diff --git a/security/security/src/main/java/com/roba/security/token/Token.java b/security/security/src/main/java/com/roba/security/token/Token.java index 55de36d..98d44d4 100644 --- a/security/security/src/main/java/com/roba/security/token/Token.java +++ b/security/security/src/main/java/com/roba/security/token/Token.java @@ -3,26 +3,32 @@ import com.roba.security.user.User; import jakarta.persistence.*; import lombok.*; +import java.io.Serializable; @Entity +@Data @AllArgsConstructor @NoArgsConstructor @Builder -@Data -public class Token { +public class Token implements Serializable { + + private static final long serialVersionUID = 6529685098267757690L; // Add a serial version UID for serialization @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) // Make sure to specify the strategy explicitly if not default private Integer id; + private String token; + @Enumerated(EnumType.STRING) private TokenType tokenType; + private boolean expired; - private boolean revoked; + private boolean revoked; @ManyToOne - @JoinColumn(name="user_id") - private User user; + @JoinColumn(name = "user_id") + private User user; // Ensure that the User class is also Serializable if it's being cached or stored } diff --git a/security/security/src/main/resources/application.yml b/security/security/src/main/resources/application.yml index aa957fb..9a20088 100644 --- a/security/security/src/main/resources/application.yml +++ b/security/security/src/main/resources/application.yml @@ -22,6 +22,20 @@ spring: show_sql: true database: postgresql database-platform: org.hibernate.dialect.PostgreSQLDialect + cache: + type: redis + redis: + host: 'redis-11797.c274.us-east-1-3.ec2.redns.redis-cloud.com' + port: 11797 + password: 'QrFTxCnfnHJfXqsm9CAwUHWLJj8puIp5' + database: 0 + timeout: 60000 # Optional: Timeout in milliseconds for connection (60 seconds) + jedis: + pool: + max-active: 8 + max-wait: -1ms + max-idle: 8 + min-idle: 0 From e2ff3a43bcff343f96470b344cff53228a3ab610 Mon Sep 17 00:00:00 2001 From: doaa-shafi Date: Tue, 14 May 2024 14:44:21 +0300 Subject: [PATCH 10/12] redis start --- .idea/dataSources.local.xml | 9 ++ .idea/workspace.xml | 139 ++++++++++++++++++ .../target/classes/application.properties | 1 + .../ConfigServerApplication.class | Bin 0 -> 767 bytes .../com/roba/security/token/RedisConfig.java | 26 ++++ .../roba/security/token/TokenController.java | 28 ++++ .../com/roba/security/token/TokenService.java | 40 +++++ 7 files changed, 243 insertions(+) create mode 100644 .idea/dataSources.local.xml create mode 100644 .idea/workspace.xml create mode 100644 config-server/target/classes/application.properties create mode 100644 config-server/target/classes/com/roba/configserver/ConfigServerApplication.class create mode 100644 security/security/src/main/java/com/roba/security/token/RedisConfig.java create mode 100644 security/security/src/main/java/com/roba/security/token/TokenController.java create mode 100644 security/security/src/main/java/com/roba/security/token/TokenService.java diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml new file mode 100644 index 0000000..a37bc83 --- /dev/null +++ b/.idea/dataSources.local.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..c552114 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1715368237803 + + + + + + \ No newline at end of file diff --git a/config-server/target/classes/application.properties b/config-server/target/classes/application.properties new file mode 100644 index 0000000..d2c7449 --- /dev/null +++ b/config-server/target/classes/application.properties @@ -0,0 +1 @@ +spring.application.name=config-server diff --git a/config-server/target/classes/com/roba/configserver/ConfigServerApplication.class b/config-server/target/classes/com/roba/configserver/ConfigServerApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..c63e94275ea8fbb83e8b7a0f19cf8881ae8fcfa3 GIT binary patch literal 767 zcmb7CO;6iE5PcgG96~4%zDj{ZZy{2qf$d^iWHl(R|OoH?M!5UjQ6ouZ0@s0@T}RV4h*+THFer3Z3xt=vu~(Vg68Q z<&GKVy8EL5O@=SA$+$I<;IYwHD#@k2m6i`mV^|vBCX-af!YQL$XkjrxyNxA$VCWf} z@O)yGPOhxT*o}u&2`upLm&a|PE_inme zVMUb6$_U!ohx-imfw`0n%c0WptjHp1fB9U7PH1A0j)Ya-o;vGpta1h({udN}OePdP zX|p@)1tW?Jn^5Dm8DV1QRJyUb{2@GWPFhNA4NYNV`Ca){cmF}bR}UY<_Rm5)mC2FH zm4`uV<4TIC&+$z3N?t_a48cf57gkPLIw8U;F!u2o0bP>+YGD4BNG_AbNi))o-XqvU z8D1gj!=RE?lI_0<=wOYk1*~I(n)u>_vKi3q1idFL{C=AO|{2%FW S5Xhuz*e>NK?2z_jcY!xx`pW_U literal 0 HcmV?d00001 diff --git a/security/security/src/main/java/com/roba/security/token/RedisConfig.java b/security/security/src/main/java/com/roba/security/token/RedisConfig.java new file mode 100644 index 0000000..ca65628 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/RedisConfig.java @@ -0,0 +1,26 @@ +package com.roba.security.token; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; + +@Configuration +public class RedisConfig { + + @Bean + public JedisConnectionFactory jedisConnectionFactory() { + return new JedisConnectionFactory(); + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(jedisConnectionFactory()); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + return template; + } +} diff --git a/security/security/src/main/java/com/roba/security/token/TokenController.java b/security/security/src/main/java/com/roba/security/token/TokenController.java new file mode 100644 index 0000000..47ccc67 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/TokenController.java @@ -0,0 +1,28 @@ +package com.roba.security.token; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +public class TokenController { + + @Autowired + private TokenService tokenService; + + @PostMapping("/tokens") + public ResponseEntity createToken(@RequestBody Token token) { + tokenService.saveToken(token); + return ResponseEntity.ok(token); + } + + @GetMapping("/tokens/{id}") + public ResponseEntity getToken(@PathVariable Integer id) { + Token token = tokenService.getToken(id); + if (token != null) { + return ResponseEntity.ok(token); + } + return ResponseEntity.notFound().build(); + } +} + diff --git a/security/security/src/main/java/com/roba/security/token/TokenService.java b/security/security/src/main/java/com/roba/security/token/TokenService.java new file mode 100644 index 0000000..18cb613 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/TokenService.java @@ -0,0 +1,40 @@ +package com.roba.security.token; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import java.util.concurrent.TimeUnit; + +@Service +public class TokenService { + + @Autowired + private RedisTemplate redisTemplate; + + // Save or update token in Redis and cache + @CachePut(value = "tokens", key = "#token.id") + public Token saveToken(Token token) { + redisTemplate.opsForValue().set(buildRedisKey(token.getId()), token, 30, TimeUnit.MINUTES); + return token; + } + + // Retrieve token from cache or Redis + @Cacheable(value = "tokens", key = "#id") + public Token getToken(Integer id) { + // This will only execute if the token is not found in the cache + return redisTemplate.opsForValue().get(buildRedisKey(id)); + } + + // Remove token from Redis and cache + @CacheEvict(value = "tokens", key = "#id") + public void deleteToken(Integer id) { + redisTemplate.delete(buildRedisKey(id)); + } + + private String buildRedisKey(Integer id) { + return "TOKEN_" + id; + } +} From ad26ac389774a69a6b8f4cb25419fafd24b4e19c Mon Sep 17 00:00:00 2001 From: doaa-shafi <116211998+doaa-shafi@users.noreply.github.com> Date: Sat, 18 May 2024 00:42:58 +0300 Subject: [PATCH 11/12] correct redis --- .idea/dataSources.local.xml | 9 ++ .idea/workspace.xml | 138 ++++++++++++++++++ security/security/pom.xml | 17 +++ .../security/auth/AuthenticationService.java | 4 + .../com/roba/security/token/RedisConfig.java | 30 ++++ .../java/com/roba/security/token/Token.java | 18 ++- .../com/roba/security/token/TokenService.java | 41 ++++++ .../src/main/resources/application.yml | 15 +- 8 files changed, 265 insertions(+), 7 deletions(-) create mode 100644 .idea/dataSources.local.xml create mode 100644 .idea/workspace.xml create mode 100644 security/security/src/main/java/com/roba/security/token/RedisConfig.java create mode 100644 security/security/src/main/java/com/roba/security/token/TokenService.java diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml new file mode 100644 index 0000000..a37bc83 --- /dev/null +++ b/.idea/dataSources.local.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..05616df --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1715981610166 + + + + + + \ No newline at end of file diff --git a/security/security/pom.xml b/security/security/pom.xml index b7b12d6..392963d 100644 --- a/security/security/pom.xml +++ b/security/security/pom.xml @@ -70,7 +70,24 @@ org.springframework.boot spring-boot-starter-data-redis + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.springframework.boot + spring-boot-starter-data-redis + + + + redis.clients + jedis + diff --git a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java index b86d006..c69616a 100644 --- a/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java +++ b/security/security/src/main/java/com/roba/security/auth/AuthenticationService.java @@ -8,6 +8,7 @@ import com.roba.security.organization.OrganizationRepository; import com.roba.security.token.Token; import com.roba.security.token.TokenRepository; +import com.roba.security.token.TokenService; import com.roba.security.token.TokenType; import com.roba.security.user.*; import lombok.RequiredArgsConstructor; @@ -31,6 +32,7 @@ public class AuthenticationService { private final JwtService jwtService; private final AuthenticationManager authenticationManager; private final TokenRepository tokenRepository; + private final TokenService tokenService; private final Emailvalidator emailvalidator; private final OrganizationRepository organizationRepository; @@ -129,6 +131,7 @@ public AuthenticationResponse authenticate(AuthenticationRequest request) { private void saveUserToken(User user, String jwtToken) { var token = Token.builder().user(user).token(jwtToken).tokenType(TokenType.BEARER).revoked(false).expired(false).build(); tokenRepository.save(token); + tokenService.saveToken(token); } @@ -175,6 +178,7 @@ private void refreshUserTokens(User user) { userTokens.forEach(token -> { token.setExpired(false); // Set the token as not expired tokenRepository.save(token); // Save the refreshed token + tokenService.saveToken(token); }); diff --git a/security/security/src/main/java/com/roba/security/token/RedisConfig.java b/security/security/src/main/java/com/roba/security/token/RedisConfig.java new file mode 100644 index 0000000..c319724 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/RedisConfig.java @@ -0,0 +1,30 @@ +package com.roba.security.token; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +@Configuration +public class RedisConfig { + + @Bean + public JedisConnectionFactory jedisConnectionFactory() { + JedisConnectionFactory factory = new JedisConnectionFactory(); + factory.setHostName("redis-12006.c62.us-east-1-4.ec2.redns.redis-cloud.com"); + factory.setPort(12006); + factory.setPassword("oSrE2KvyJdxFJ8vmpDlIBDtQ9ZriadjR"); + return factory; + } + + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(jedisConnectionFactory()); + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + return template; + } +} diff --git a/security/security/src/main/java/com/roba/security/token/Token.java b/security/security/src/main/java/com/roba/security/token/Token.java index 55de36d..98d44d4 100644 --- a/security/security/src/main/java/com/roba/security/token/Token.java +++ b/security/security/src/main/java/com/roba/security/token/Token.java @@ -3,26 +3,32 @@ import com.roba.security.user.User; import jakarta.persistence.*; import lombok.*; +import java.io.Serializable; @Entity +@Data @AllArgsConstructor @NoArgsConstructor @Builder -@Data -public class Token { +public class Token implements Serializable { + + private static final long serialVersionUID = 6529685098267757690L; // Add a serial version UID for serialization @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) // Make sure to specify the strategy explicitly if not default private Integer id; + private String token; + @Enumerated(EnumType.STRING) private TokenType tokenType; + private boolean expired; - private boolean revoked; + private boolean revoked; @ManyToOne - @JoinColumn(name="user_id") - private User user; + @JoinColumn(name = "user_id") + private User user; // Ensure that the User class is also Serializable if it's being cached or stored } diff --git a/security/security/src/main/java/com/roba/security/token/TokenService.java b/security/security/src/main/java/com/roba/security/token/TokenService.java new file mode 100644 index 0000000..1cae9f5 --- /dev/null +++ b/security/security/src/main/java/com/roba/security/token/TokenService.java @@ -0,0 +1,41 @@ +package com.roba.security.token; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; + +import java.util.concurrent.TimeUnit; + +@Service +public class TokenService { + + @Autowired + private RedisTemplate redisTemplate; + + // Save or update token in Redis and cache + @CachePut(value = "tokens", key = "#token.id") + public Token saveToken(Token token) { + redisTemplate.opsForValue().set(buildRedisKey(token.getId()), token, 30, TimeUnit.MINUTES); + return token; + } + + // Retrieve token from cache or Redis + @Cacheable(value = "tokens", key = "#id") + public Token getToken(Integer id) { + // This will only execute if the token is not found in the cache + return redisTemplate.opsForValue().get(buildRedisKey(id)); + } + + // Remove token from Redis and cache + @CacheEvict(value = "tokens", key = "#id") + public void deleteToken(Integer id) { + redisTemplate.delete(buildRedisKey(id)); + } + + private String buildRedisKey(Integer id) { + return "TOKEN_" + id; + } +} diff --git a/security/security/src/main/resources/application.yml b/security/security/src/main/resources/application.yml index aa957fb..e6d1afc 100644 --- a/security/security/src/main/resources/application.yml +++ b/security/security/src/main/resources/application.yml @@ -22,6 +22,19 @@ spring: show_sql: true database: postgresql database-platform: org.hibernate.dialect.PostgreSQLDialect - + cache: + type: redis + redis: + host: redis-12006.c62.us-east-1-4.ec2.redns.redis-cloud.com + port: 12006 + password: oSrE2KvyJdxFJ8vmpDlIBDtQ9ZriadjR + database: 0 + timeout: 60000 # Optional: Timeout in milliseconds for connection (60 seconds) + jedis: + pool: + max-active: 8 + max-wait: -1ms + max-idle: 8 + min-idle: 0 From fb8a6ef42284660649c4d98b88dafa9c15ba29b3 Mon Sep 17 00:00:00 2001 From: doaa-shafi <116211998+doaa-shafi@users.noreply.github.com> Date: Sat, 18 May 2024 00:49:55 +0300 Subject: [PATCH 12/12] correct redis --- .idea/workspace.xml | 68 ++++++++++--------- .../com/roba/security/token/RedisConfig.java | 6 ++ .../roba/security/token/TokenController.java | 28 -------- 3 files changed, 42 insertions(+), 60 deletions(-) delete mode 100644 security/security/src/main/java/com/roba/security/token/TokenController.java diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 05616df..eff9a4d 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,13 +4,9 @@ \ No newline at end of file diff --git a/security/security/src/main/java/com/roba/security/token/RedisConfig.java b/security/security/src/main/java/com/roba/security/token/RedisConfig.java index cfd78b8..c319724 100644 --- a/security/security/src/main/java/com/roba/security/token/RedisConfig.java +++ b/security/security/src/main/java/com/roba/security/token/RedisConfig.java @@ -4,6 +4,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @@ -11,6 +12,11 @@ public class RedisConfig { @Bean public JedisConnectionFactory jedisConnectionFactory() { + JedisConnectionFactory factory = new JedisConnectionFactory(); + factory.setHostName("redis-12006.c62.us-east-1-4.ec2.redns.redis-cloud.com"); + factory.setPort(12006); + factory.setPassword("oSrE2KvyJdxFJ8vmpDlIBDtQ9ZriadjR"); + return factory; } @Bean diff --git a/security/security/src/main/java/com/roba/security/token/TokenController.java b/security/security/src/main/java/com/roba/security/token/TokenController.java deleted file mode 100644 index 47ccc67..0000000 --- a/security/security/src/main/java/com/roba/security/token/TokenController.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.roba.security.token; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -@RestController -public class TokenController { - - @Autowired - private TokenService tokenService; - - @PostMapping("/tokens") - public ResponseEntity createToken(@RequestBody Token token) { - tokenService.saveToken(token); - return ResponseEntity.ok(token); - } - - @GetMapping("/tokens/{id}") - public ResponseEntity getToken(@PathVariable Integer id) { - Token token = tokenService.getToken(id); - if (token != null) { - return ResponseEntity.ok(token); - } - return ResponseEntity.notFound().build(); - } -} -