From a34ded26ddb63d74260f8d612a5fda0b705e22e3 Mon Sep 17 00:00:00 2001 From: DashDeipayan Date: Sat, 23 Sep 2023 02:46:40 +0530 Subject: [PATCH 01/10] added authorisation for skill tree --- skill-tree /pom.xml | 42 ++++++++++ .../Authentication/AuthEntryPoint.java | 31 +++++++ .../UserAuthenticationToken.java | 31 +++++++ .../RDS/skilltree/Config/SecurityConfig.java | 42 ++++++++++ .../Exceptions/NoEntityException.java | 16 ++++ .../Filters/JWTAuthenticationFilter.java | 80 +++++++++++++++++++ .../RDS/skilltree/SkillTreeApplication.java | 21 ++++- .../java/com/RDS/skilltree/User/UserDRO.java | 61 ++++++++++++++ .../java/com/RDS/skilltree/User/UserDTO.java | 41 ++++++++++ .../com/RDS/skilltree/User/UserModel.java | 1 - .../com/RDS/skilltree/utils/FetchAPI.java | 33 ++++++++ .../com/RDS/skilltree/utils/JWTUtils.java | 79 ++++++++++++++++++ .../RDS/skilltree/utils/RDSUser/Picture.java | 9 +++ .../RDS/skilltree/utils/RDSUser/Response.java | 9 +++ .../RDS/skilltree/utils/RDSUser/Roles.java | 9 +++ .../com/RDS/skilltree/utils/RDSUser/User.java | 26 ++++++ .../src/main/resources/application.properties | 10 ++- 17 files changed, 533 insertions(+), 8 deletions(-) create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/Authentication/AuthEntryPoint.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/Authentication/UserAuthenticationToken.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/Exceptions/NoEntityException.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Picture.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Response.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Roles.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/User.java diff --git a/skill-tree /pom.xml b/skill-tree /pom.xml index dd5a1079..829ad84e 100644 --- a/skill-tree /pom.xml +++ b/skill-tree /pom.xml @@ -14,6 +14,7 @@ skill-tree skill tree project + com.RDS.skilltree.SkillTreeApplication 17 @@ -52,11 +53,52 @@ + junit junit test + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.1.0 + + + org.springframework.security + spring-security-web + + + org.jetbrains + annotations + 17.0.0 + compile + + + io.jsonwebtoken + jjwt-api + 0.11.2 + + + io.jsonwebtoken + jjwt-impl + 0.11.2 + runtime + + + io.jsonwebtoken + jjwt-jackson + 0.11.2 + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Authentication/AuthEntryPoint.java b/skill-tree /src/main/java/com/RDS/skilltree/Authentication/AuthEntryPoint.java new file mode 100644 index 00000000..37fb5170 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/Authentication/AuthEntryPoint.java @@ -0,0 +1,31 @@ +package com.RDS.skilltree.Authentication; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.PrintWriter; + +@Component +public class AuthEntryPoint implements AuthenticationEntryPoint { + + /** + * A description of the entire Java function. + * + * @param request the HttpServletRequest object representing the client's request + * @param response the HttpServletResponse object representing the server's response + * @param authException the AuthenticationException object representing the exception that occurred during authentication + * @throws IOException if an I/O error occurs while writing the response + * @throws ServletException if the request could not be handled + */ + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + PrintWriter writer = response.getWriter(); + writer.println("Access Denied !! " + authException.getMessage()); + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Authentication/UserAuthenticationToken.java b/skill-tree /src/main/java/com/RDS/skilltree/Authentication/UserAuthenticationToken.java new file mode 100644 index 00000000..b5464d78 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/Authentication/UserAuthenticationToken.java @@ -0,0 +1,31 @@ +package com.RDS.skilltree.Authentication; + +import com.RDS.skilltree.User.UserModel; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import javax.security.auth.Subject; + +public class UserAuthenticationToken extends AbstractAuthenticationToken { + + private final UserModel user; + + public UserAuthenticationToken(UserModel user) { + super(null); + this.user = user; + setAuthenticated(true); + } + + @Override + public Object getCredentials() { + return null; + } + + @Override + public Object getPrincipal() { + return user; + } + + @Override + public boolean implies(Subject subject) { + return super.implies(subject); + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java b/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java new file mode 100644 index 00000000..9ebda00e --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java @@ -0,0 +1,42 @@ +package com.RDS.skilltree.Config; + +import com.RDS.skilltree.Authentication.AuthEntryPoint; +import com.RDS.skilltree.Filters.JWTAuthenticationFilter; +import com.RDS.skilltree.User.UserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@EnableWebSecurity +@Configuration +public class SecurityConfig { + private final UserService userService; + private final AuthEntryPoint authEntryPoint; + + @Autowired + public SecurityConfig(UserService userService, AuthEntryPoint authEntryPoint) { + this.userService = userService; + this.authEntryPoint = authEntryPoint; + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth->auth.requestMatchers("/**").authenticated()) + .exceptionHandling(ex->ex.authenticationEntryPoint(authEntryPoint)) + .sessionManagement(session->session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); + http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); + return http.build(); + } + + @Bean + public JWTAuthenticationFilter jwtAuthenticationFilter(){ + return new JWTAuthenticationFilter(); + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Exceptions/NoEntityException.java b/skill-tree /src/main/java/com/RDS/skilltree/Exceptions/NoEntityException.java new file mode 100644 index 00000000..7804baf0 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/Exceptions/NoEntityException.java @@ -0,0 +1,16 @@ +package com.RDS.skilltree.Exceptions; + +public class NoEntityException extends RuntimeException{ + + public NoEntityException(String message) { + super(message); + } + + public NoEntityException(String message, Throwable cause) { + super(message, cause); + } + + public NoEntityException(Throwable cause) { + super(cause); + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java new file mode 100644 index 00000000..fe36e2ae --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java @@ -0,0 +1,80 @@ +package com.RDS.skilltree.Filters; + +import com.RDS.skilltree.Authentication.UserAuthenticationToken; +import com.RDS.skilltree.User.*; +import com.RDS.skilltree.utils.FetchAPI; +import com.RDS.skilltree.utils.JWTUtils; +import com.RDS.skilltree.utils.RDSUser.Response; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.net.URL; +import java.util.concurrent.CompletableFuture; + +public class JWTAuthenticationFilter extends OncePerRequestFilter { + + @Autowired + private JWTUtils jwtUtils; + + @Autowired + private FetchAPI fetchAPI; + + @Autowired + private UserService userService; + + @Autowired + private UserRepository userRepository; + + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + String token = getJWTFromRequest(request); + try { + if (StringUtils.hasText(token) && jwtUtils.validateToken(token)) { + String rdsUserId = jwtUtils.getRDSUserId(token); + + + UserModel userModel; + + if (userRepository.existsByRdsUserId(rdsUserId)) { + userModel = userRepository.findByRdsUserId(rdsUserId).get(); + } else { + CompletableFuture userResponse = fetchAPI.getRDSUserData(rdsUserId); + CompletableFuture.allOf(userResponse).join(); + Response rdsUserResponse = userResponse.get(); + UserDRO userDro = new UserDRO(rdsUserId, rdsUserResponse.getUser().getFirst_name(), + rdsUserResponse.getUser().getLast_name(), + new URL(rdsUserResponse.getUser().getPicture().getUrl()), + UserType.MEMBER, UserRole.USER); + userModel = UserDRO.toModel(userDro); + userService.createUser(userDro); + } + + UserAuthenticationToken authentication = new UserAuthenticationToken(userModel); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + filterChain.doFilter(request, response); + } + + private String getJWTFromRequest(HttpServletRequest request) { + String bearerToken = request.getHeader("Authorization"); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java b/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java index 1393b0f6..18803054 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java @@ -1,12 +1,27 @@ package com.RDS.skilltree; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; + @SpringBootApplication +@OpenAPIDefinition( + info = @Info( + title = "Skill Tree", + version = "1.0.0", + description = "Skill Tree" + ), + tags = @Tag( + name = "Skill Tree", + description = "Skill Tree" + ) +) public class SkillTreeApplication { - public static void main(String[] args) { - SpringApplication.run(SkillTreeApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(SkillTreeApplication.class, args); + } } diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java new file mode 100644 index 00000000..eec508be --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java @@ -0,0 +1,61 @@ +package com.RDS.skilltree.User; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.net.URL; +import java.time.Instant; + + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class UserDRO { + private String rdsUserId; + + private String firstName; + + private String lastName; + + private URL imageUrl; + + private UserType type; + + private UserRole role; + + public static UserModel toModel(UserDRO user) { + return new UserModel( + user.getRdsUserId(), + user.getFirstName(), + user.getLastName(), + user.getImageUrl(), + user.getType(), + user.getRole() + ); + } + + public static UserModel compareAndUpdateModel(UserModel user, UserDRO userDRO) { + if (userDRO.getRdsUserId() != null) { + user.setRdsUserId(user.getRdsUserId()); + } + if (userDRO.getFirstName() != null) { + user.setFirstName(user.getFirstName()); + } + if (userDRO.getLastName() != null) { + user.setLastName(user.getLastName()); + } + if (userDRO.getImageUrl() != null) { + user.setImageUrl(user.getImageUrl()); + } + if (userDRO.getType() != null) { + user.setType(user.getType()); + } + if (userDRO.getRole() != null) { + user.setRole(user.getRole()); + } + user.setUpdatedAt(Instant.now()); + user.setUpdatedBy(user); + return user; + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java new file mode 100644 index 00000000..6c9f805b --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java @@ -0,0 +1,41 @@ +package com.RDS.skilltree.User; + +import com.RDS.skilltree.Skill.SkillModel; +import lombok.*; + +import java.net.URL; +import java.util.Set; +import java.util.UUID; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class UserDTO { + + private UUID id; + + private String rdsUserId; + + private String firstName; + + private String lastName; + + private URL imageUrl; + + private UserType type; + + private UserRole role; + + private Set skills; + + public static UserDTO toDTO(UserModel user) { + return new UserDTO(user.getId(), + user.getRdsUserId(), + user.getFirstName(), + user.getLastName(), + user.getImageUrl(), + user.getType(), + user.getRole(), + user.getSkills()); + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserModel.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserModel.java index 2e723a1e..6a7000fd 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/User/UserModel.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserModel.java @@ -9,7 +9,6 @@ import lombok.NoArgsConstructor; import java.net.URL; -import java.time.Instant; import java.util.Set; import java.util.UUID; diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java new file mode 100644 index 00000000..80f18ef1 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java @@ -0,0 +1,33 @@ +package com.RDS.skilltree.utils; + +import com.RDS.skilltree.utils.RDSUser.Response; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.CompletableFuture; + +@Component +public class FetchAPI { + private final RestTemplate restTemplate; + + public FetchAPI(RestTemplateBuilder restTemplateBuilder){ + restTemplate = restTemplateBuilder.build(); + } + + @Async + public CompletableFuture getRDSUserData(String userId) { + String url = String.format("https://api.realdevsquad.com/users/userId/%s", userId); + try{ + ResponseEntity response = restTemplate.getForEntity(url, Response.class); + Response result = response.getBody(); + return CompletableFuture.completedFuture(result); + }catch (Exception e){ + throw new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + } + } +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java new file mode 100644 index 00000000..6fbc0527 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java @@ -0,0 +1,79 @@ +package com.RDS.skilltree.utils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.stereotype.Component; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +@Component +public class JWTUtils { + + @Value("${jwt.rds.public.key}") + private String publicRDSKeyString; + + /** + * Converts the given private key string to an RSAPrivateKey object. + * + * @param privateKeyString The private key string to be converted. + * @return The corresponding RSAPrivateKey object. + * @throws Exception If an error occurs during the conversion process. + */ + private RSAPrivateKey convertToRSAPrivateKey(String privateKeyString) throws Exception { + try { + privateKeyString = privateKeyString.replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s", ""); + + byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException e) { + throw new Exception("Error converting private key: " + e.getMessage(), e); + } + } + + /** + * Converts the given public key string to an RSAPublicKey object. + * + * @param publicKeyString the public key string to be converted + * @return the RSAPublicKey object converted from the public key string + * @throws Exception if there is an error during the conversion process + */ + private RSAPublicKey convertToRSAPublicKey(String publicKeyString) throws Exception { + publicKeyString = publicKeyString.replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", "") + .replaceAll("\\s", ""); + + byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } + + public String getRDSUserId(String token) throws Exception { + Claims claims = Jwts.parser().setSigningKey(convertToRSAPublicKey(publicRDSKeyString)).parseClaimsJws(token).getBody(); + String temp = claims.get("userId", String.class); + return temp; + } + + public boolean validateToken(String token) throws Exception { + try { + Jwts.parser().setSigningKey(convertToRSAPublicKey(publicRDSKeyString)).parseClaimsJws(token); + return true; + } catch (Exception e) { + throw new AuthenticationCredentialsNotFoundException("Invalid JWT"); + } + } + +} diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Picture.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Picture.java new file mode 100644 index 00000000..cf9a78e3 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Picture.java @@ -0,0 +1,9 @@ +package com.RDS.skilltree.utils.RDSUser; + +import lombok.Data; + +@Data +public class Picture{ + private String publicId; + private String url; +} \ No newline at end of file diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Response.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Response.java new file mode 100644 index 00000000..1a363186 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Response.java @@ -0,0 +1,9 @@ +package com.RDS.skilltree.utils.RDSUser; + +import lombok.Data; + +@Data +public class Response{ + private String message; + private User user; +} \ No newline at end of file diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Roles.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Roles.java new file mode 100644 index 00000000..155df3a3 --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/Roles.java @@ -0,0 +1,9 @@ +package com.RDS.skilltree.utils.RDSUser; + +import lombok.Data; + +@Data +public class Roles{ + private boolean archived; + private boolean inDiscord; +} \ No newline at end of file diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/User.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/User.java new file mode 100644 index 00000000..e4d0c27e --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/RDSUser/User.java @@ -0,0 +1,26 @@ +package com.RDS.skilltree.utils.RDSUser; + +import lombok.Data; + +@Data +public class User{ + private boolean incompleteUserDetails; + private String discordJoinedAt; + private String discordId; + private Roles roles; + private String last_name; + private long createdAt; + private String linkedinId; + private boolean nicknameSynced; + private Picture picture; + private long githubCreatedAt; + private String githubDisplayName; + private long updatedAt; + private String githubId; + private String company; + private String id; + private String designation; + private String twitterId; + private String first_name; + private String username; +} \ No newline at end of file diff --git a/skill-tree /src/main/resources/application.properties b/skill-tree /src/main/resources/application.properties index defe3403..f799cbfc 100644 --- a/skill-tree /src/main/resources/application.properties +++ b/skill-tree /src/main/resources/application.properties @@ -1,7 +1,9 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:3306/skilltree +spring.datasource.url=jdbc:mysql://localhost:3306/${DB_NAME} spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect -spring.datasource.username=${MYSQL_DB_USERNAME} -spring.datasource.password=${MYSQL_DB_PASSWORD} -spring.jpa.hibernate.ddl-auto=update +spring.datasource.username=${MYSQL_USERNAME} +spring.datasource.password=${MYSQL_PASSWORD} +spring.jpa.hibernate.ddl-auto=${DB_DDL_POLICY} + +jwt.rds.public.key = ${RDS_PUBLIC_KEY} From d5b44fcb8e39e32a19b584b1711c158535c947ee Mon Sep 17 00:00:00 2001 From: DashDeipayan Date: Sat, 23 Sep 2023 02:47:36 +0530 Subject: [PATCH 02/10] added services --- skill-tree /.gitignore | 1 + .../RDS/skilltree/User/UserRepository.java | 4 + .../com/RDS/skilltree/User/UserService.java | 17 +++ .../RDS/skilltree/User/UserServiceImpl.java | 114 ++++++++++++++++++ .../skilltree/User/UserServiceImplTest.java | 56 +++++++++ 5 files changed, 192 insertions(+) create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java create mode 100644 skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java create mode 100644 skill-tree /src/test/java/com/RDS/skilltree/User/UserServiceImplTest.java diff --git a/skill-tree /.gitignore b/skill-tree /.gitignore index 549e00a2..66853d59 100644 --- a/skill-tree /.gitignore +++ b/skill-tree /.gitignore @@ -31,3 +31,4 @@ build/ ### VS Code ### .vscode/ +dev.env \ No newline at end of file diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserRepository.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserRepository.java index 9bba0027..518931b9 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/User/UserRepository.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserRepository.java @@ -3,8 +3,12 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.Optional; import java.util.UUID; @Repository public interface UserRepository extends JpaRepository { + Optional findByRdsUserId(String rdsUserId); + + Boolean existsByRdsUserId(String rdsUserId); } diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java new file mode 100644 index 00000000..d3078ccf --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java @@ -0,0 +1,17 @@ +package com.RDS.skilltree.User; + + +import java.util.List; +import java.util.UUID; + +public interface UserService { + void createUser(UserDRO user); + + void updateUser(UUID id,UserDRO user); + + UserDTO getUserById(UUID id); + + List getAllUsers(); + + void addSkill(UUID skill, UUID id); +} \ No newline at end of file diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java new file mode 100644 index 00000000..59f0dcdd --- /dev/null +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java @@ -0,0 +1,114 @@ +package com.RDS.skilltree.User; + +import com.RDS.skilltree.Exceptions.NoEntityException; +import com.RDS.skilltree.Skill.SkillModel; +import com.RDS.skilltree.Skill.SkillRepository; +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Service +public class UserServiceImpl implements UserService { + private final UserRepository userRepository; + private final SkillRepository skillRepository; + + @Autowired + public UserServiceImpl(UserRepository userRepository, SkillRepository skillRepository) { + this.userRepository = userRepository; + this.skillRepository = skillRepository; + } + + /** + * Creates a new user. + * + * @param user the UserDRO object containing the user information + */ + @Override + public void createUser(UserDRO user) { + try { + UserModel userModel = UserDRO.toModel(user); + userRepository.save(userModel); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Updates a user with the specified ID using the provided user details. + * + * @param id the ID of the user to update + * @param updatedUserDetails the updated details of the user + */ + @Override + @Transactional + public void updateUser(UUID id, UserDRO updatedUserDetails) throws NoEntityException { + Optional userOptional = userRepository.findById(id); + if (userOptional.isEmpty()) + throw new NoEntityException("User with id:" + id + " not found"); + UserModel user = userOptional.get(); + UserModel updatedUser = UserDRO.compareAndUpdateModel(user, updatedUserDetails); + + userRepository.save(updatedUser); + } + + /** + * Retrieves a user by their unique identifier. + * + * @param id the unique identifier of the user + * @return the UserDTO representing the user + */ + @Override + public UserDTO getUserById(UUID id) throws NoEntityException { + Optional user = userRepository.findById(id); + if (user.isEmpty()) { + throw new NoEntityException("User with id:" + id + " not found"); + } + return UserDTO.toDTO(user.get()); + } + + /** + * Retrieves a list of all users. + * + * @return a list of UserDTO objects representing the users + */ + @Override + public List getAllUsers() throws NoEntityException { + List usersData = userRepository.findAll(); + if (usersData.isEmpty()) + throw new NoEntityException("No users found"); + return usersData.stream().map(UserDTO::toDTO).toList(); + } + + /** + * Adds a skill to a user. + * + * @param skillId the ID of the skill to be added + * @param id the ID of the user + * @return none + */ + @Override + @Transactional + public void addSkill(UUID skillId, UUID id) throws NoEntityException { + Optional skillOptional = skillRepository.findById(skillId); + Optional userOptional = userRepository.findById(id); + + if (skillOptional.isPresent() && userOptional.isPresent()) { + SkillModel skillModel = skillOptional.get(); + UserModel user = userOptional.get(); + + skillModel.getUsers().add(user); + skillRepository.save(skillModel); + + user.getSkills().add(skillModel); + userRepository.save(user); + } else { + if (skillOptional.isEmpty()) + throw new NoEntityException("Skill with id:" + skillId + " not found"); + throw new NoEntityException("User with id:" + id + " not found"); + } + } +} diff --git a/skill-tree /src/test/java/com/RDS/skilltree/User/UserServiceImplTest.java b/skill-tree /src/test/java/com/RDS/skilltree/User/UserServiceImplTest.java new file mode 100644 index 00000000..e9a28df2 --- /dev/null +++ b/skill-tree /src/test/java/com/RDS/skilltree/User/UserServiceImplTest.java @@ -0,0 +1,56 @@ +package com.RDS.skilltree.User; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.net.MalformedURLException; +import java.net.URL; + +import static org.junit.jupiter.api.Assertions.*; + + +@SpringBootTest +class UserServiceImplTest { + + private final UserService userService; + + @Autowired + public UserServiceImplTest(UserService userService) { + this.userService = userService; + } + + @BeforeEach + void setUp() { + + } + + @AfterEach + void tearDown() { + + } + + @Test + public void testCreateUser() throws MalformedURLException { + UserDRO user1 = new UserDRO("12345abcd","John", "Doe",new URL("https://example.com"), UserType.MEMBER,UserRole.USER); + userService.createUser(user1); + } + + @Test + void updateUser() { + } + + @Test + void getUserById() { + } + + @Test + void getAllUsers() { + } + + @Test + void addSkill() { + } +} \ No newline at end of file From b14610836ee733f697e16b74b3e892257e1f9b83 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Sat, 11 Nov 2023 18:05:15 +0530 Subject: [PATCH 03/10] Resolving PR comments --- .../Filters/JWTAuthenticationFilter.java | 2 +- .../java/com/RDS/skilltree/User/UserDTO.java | 18 +++--- .../com/RDS/skilltree/utils/JWTUtils.java | 9 ++- .../src/main/resources/application.properties | 4 +- .../EndorsementsIntegrationTests.java | 64 +++++++++---------- 5 files changed, 53 insertions(+), 44 deletions(-) diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java index fe36e2ae..d20a96f1 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java @@ -49,7 +49,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse userModel = userRepository.findByRdsUserId(rdsUserId).get(); } else { CompletableFuture userResponse = fetchAPI.getRDSUserData(rdsUserId); - CompletableFuture.allOf(userResponse).join(); + CompletableFuture.anyOf(userResponse).join(); Response rdsUserResponse = userResponse.get(); UserDRO userDro = new UserDRO(rdsUserId, rdsUserResponse.getUser().getFirst_name(), rdsUserResponse.getUser().getLast_name(), diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java index 6c9f805b..96ff25bc 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDTO.java @@ -10,6 +10,7 @@ @NoArgsConstructor @AllArgsConstructor @Data +@Builder public class UserDTO { private UUID id; @@ -29,13 +30,14 @@ public class UserDTO { private Set skills; public static UserDTO toDTO(UserModel user) { - return new UserDTO(user.getId(), - user.getRdsUserId(), - user.getFirstName(), - user.getLastName(), - user.getImageUrl(), - user.getType(), - user.getRole(), - user.getSkills()); + return UserDTO.builder() + .rdsUserId(user.getRdsUserId()) + .firstName(user.getFirstName()) + .lastName(user.getLastName()) + .imageUrl(user.getImageUrl()) + .type(user.getType()) + .role(user.getRole()) + .skills(user.getSkills()) + .build(); } } diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java index 6fbc0527..8fa07fbf 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java @@ -2,6 +2,7 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.stereotype.Component; @@ -14,12 +15,16 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; @Component +@Slf4j public class JWTUtils { @Value("${jwt.rds.public.key}") private String publicRDSKeyString; + private static ConcurrentHashMap publicKeyCache = new ConcurrentHashMap<>(); /** * Converts the given private key string to an RSAPrivateKey object. @@ -67,10 +72,12 @@ public String getRDSUserId(String token) throws Exception { return temp; } - public boolean validateToken(String token) throws Exception { + public boolean validateToken(String token) throws Exception { //TODO check for the case where token is expired try { Jwts.parser().setSigningKey(convertToRSAPublicKey(publicRDSKeyString)).parseClaimsJws(token); return true; + } catch (InvalidKeySpecException e) { + throw new RuntimeException(e); } catch (Exception e) { throw new AuthenticationCredentialsNotFoundException("Invalid JWT"); } diff --git a/skill-tree /src/main/resources/application.properties b/skill-tree /src/main/resources/application.properties index f799cbfc..0833ad45 100644 --- a/skill-tree /src/main/resources/application.properties +++ b/skill-tree /src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:3306/${DB_NAME} -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect +spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/${DB_NAME} +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect spring.datasource.username=${MYSQL_USERNAME} spring.datasource.password=${MYSQL_PASSWORD} spring.jpa.hibernate.ddl-auto=${DB_DDL_POLICY} diff --git a/skill-tree /src/test/java/com/RDS/skilltree/EndorsementsIntegrationTests.java b/skill-tree /src/test/java/com/RDS/skilltree/EndorsementsIntegrationTests.java index 352c5a86..6aca9e20 100644 --- a/skill-tree /src/test/java/com/RDS/skilltree/EndorsementsIntegrationTests.java +++ b/skill-tree /src/test/java/com/RDS/skilltree/EndorsementsIntegrationTests.java @@ -1,7 +1,7 @@ package com.RDS.skilltree; -import com.RDS.skilltree.enums.Type; -import com.RDS.skilltree.request.EndorsementCreationRequest; +//import com.RDS.skilltree.enums.Type; +//import com.RDS.skilltree.request.EndorsementCreationRequest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import io.restassured.RestAssured; @@ -103,36 +103,36 @@ public void testAPIReturns400_OnInvalidStatusPassed(){ .body("message", equalTo("Invalid status passed")); } - @Test - @Disabled - @DisplayName("Return 200 on endorsement creation") - public void testAPIReturns200_OnEndorsementCreation() throws JsonProcessingException { - - String requestBody = objectMapper.writeValueAsString(EndorsementCreationRequest.builder() - .endorserUserId("u-1") - .endorseeUserId("u-2") - .skillName("Java") - .description("This is endorsed for the skill showcased in Skill tree") - .endorsementType(Type.POSITIVE) - .build()); - - Response response = given() - .contentType("application/json") - .body(requestBody) - .post("/endorsements"); - response.then() - .statusCode(200) - .contentType("application/json") - .body("code", equalTo(200)) - .body("data.endorseeId", equalTo("user-1")) - .body("data.skillName", equalTo("Java")) - .body("data.status",anyOf(equalTo("APPROVED"),equalTo("PENDING"),equalTo("REJECTED"))) - .body("data.endorsementType",anyOf(equalTo("POSITIVE"),equalTo("NEGATIVE"))) - .body("data.endorsersList",hasSize(1)) - .body("data.endorsersList[0].endorserId",equalTo("user-2")) - .body("data.endorsersList[0].description",isA(String.class)) - .body("data.endorsersList[0].userType",anyOf(equalTo("SUPER_USER"),equalTo("MAVEN"),equalTo("USER"))); - } +// @Test +// @Disabled +// @DisplayName("Return 200 on endorsement creation") +// public void testAPIReturns200_OnEndorsementCreation() throws JsonProcessingException { +// +// String requestBody = objectMapper.writeValueAsString(EndorsementCreationRequest.builder() +// .endorserUserId("u-1") +// .endorseeUserId("u-2") +// .skillName("Java") +// .description("This is endorsed for the skill showcased in Skill tree") +// .endorsementType(Type.POSITIVE) +// .build()); +// +// Response response = given() +// .contentType("application/json") +// .body(requestBody) +// .post("/endorsements"); +// response.then() +// .statusCode(200) +// .contentType("application/json") +// .body("code", equalTo(200)) +// .body("data.endorseeId", equalTo("user-1")) +// .body("data.skillName", equalTo("Java")) +// .body("data.status",anyOf(equalTo("APPROVED"),equalTo("PENDING"),equalTo("REJECTED"))) +// .body("data.endorsementType",anyOf(equalTo("POSITIVE"),equalTo("NEGATIVE"))) +// .body("data.endorsersList",hasSize(1)) +// .body("data.endorsersList[0].endorserId",equalTo("user-2")) +// .body("data.endorsersList[0].description",isA(String.class)) +// .body("data.endorsersList[0].userType",anyOf(equalTo("SUPER_USER"),equalTo("MAVEN"),equalTo("USER"))); +// } @Test @Disabled From af963552caeebb506d91210f417dd28e362f97c6 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Tue, 14 Nov 2023 12:17:30 +0530 Subject: [PATCH 04/10] Making the RSA key generation a static variable --- skill-tree /pom.xml | 7 ++++--- .../java/com/RDS/skilltree/utils/JWTUtils.java | 17 +++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/skill-tree /pom.xml b/skill-tree /pom.xml index 829ad84e..48de64ee 100644 --- a/skill-tree /pom.xml +++ b/skill-tree /pom.xml @@ -16,6 +16,7 @@ com.RDS.skilltree.SkillTreeApplication 17 + 0.11.2 @@ -77,18 +78,18 @@ io.jsonwebtoken jjwt-api - 0.11.2 + ${io.jsonwebtoken.version} io.jsonwebtoken jjwt-impl - 0.11.2 + ${io.jsonwebtoken.version} runtime io.jsonwebtoken jjwt-jackson - 0.11.2 + ${io.jsonwebtoken.version} org.springframework.boot diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java index 8fa07fbf..3523c0c6 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java @@ -15,8 +15,6 @@ import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; -import java.util.HashMap; -import java.util.concurrent.ConcurrentHashMap; @Component @Slf4j @@ -24,7 +22,16 @@ public class JWTUtils { @Value("${jwt.rds.public.key}") private String publicRDSKeyString; - private static ConcurrentHashMap publicKeyCache = new ConcurrentHashMap<>(); + private static KeyFactory keyFactory = null; + + static { + try { + keyFactory = KeyFactory.getInstance("RSA"); + } catch (NoSuchAlgorithmException e) { + // Handle the exception + e.printStackTrace(); + } + } /** * Converts the given private key string to an RSAPrivateKey object. @@ -41,9 +48,8 @@ private RSAPrivateKey convertToRSAPrivateKey(String privateKeyString) throws Exc byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); - } catch (NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException e) { + } catch (InvalidKeySpecException | IllegalArgumentException e) { throw new Exception("Error converting private key: " + e.getMessage(), e); } } @@ -62,7 +68,6 @@ private RSAPublicKey convertToRSAPublicKey(String publicKeyString) throws Except byte[] publicKeyBytes = Base64.getDecoder().decode(publicKeyString); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return (RSAPublicKey) keyFactory.generatePublic(keySpec); } From a6953be91671e055eaa9665a11b344c51de03cb5 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Tue, 28 Nov 2023 18:46:29 +0530 Subject: [PATCH 05/10] Making the dependencies injected by constructor, to make class testable --- .../RDS/skilltree/Config/SecurityConfig.java | 2 +- .../Filters/JWTAuthenticationFilter.java | 6 ++-- .../com/RDS/skilltree/utils/JWTUtils.java | 35 +++---------------- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java b/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java index 9ebda00e..5550027f 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/Config/SecurityConfig.java @@ -37,6 +37,6 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { @Bean public JWTAuthenticationFilter jwtAuthenticationFilter(){ - return new JWTAuthenticationFilter(); + return new JWTAuthenticationFilter(userService); } } diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java index 5d919c9f..7f3cee9c 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java @@ -27,12 +27,14 @@ public class JWTAuthenticationFilter extends OncePerRequestFilter { @Autowired private FetchAPI fetchAPI; - @Autowired - private UserService userService; + private final UserService userService; @Autowired private UserRepository userRepository; + public JWTAuthenticationFilter(UserService userService){ + this.userService = userService; + } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java index 3523c0c6..5945a439 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/JWTUtils.java @@ -2,6 +2,7 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; +import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; @@ -22,38 +23,12 @@ public class JWTUtils { @Value("${jwt.rds.public.key}") private String publicRDSKeyString; - private static KeyFactory keyFactory = null; + private KeyFactory keyFactory; - static { - try { - keyFactory = KeyFactory.getInstance("RSA"); - } catch (NoSuchAlgorithmException e) { - // Handle the exception - e.printStackTrace(); - } + @PostConstruct + public void init() throws NoSuchAlgorithmException { + keyFactory = KeyFactory.getInstance("RSA"); } - - /** - * Converts the given private key string to an RSAPrivateKey object. - * - * @param privateKeyString The private key string to be converted. - * @return The corresponding RSAPrivateKey object. - * @throws Exception If an error occurs during the conversion process. - */ - private RSAPrivateKey convertToRSAPrivateKey(String privateKeyString) throws Exception { - try { - privateKeyString = privateKeyString.replace("-----BEGIN PRIVATE KEY-----", "") - .replace("-----END PRIVATE KEY-----", "") - .replaceAll("\\s", ""); - - byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString); - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); - return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); - } catch (InvalidKeySpecException | IllegalArgumentException e) { - throw new Exception("Error converting private key: " + e.getMessage(), e); - } - } - /** * Converts the given public key string to an RSAPublicKey object. * From eab8fb9e03bf7a697cd4779645ad0cf6bb864ec2 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Sat, 2 Dec 2023 13:32:55 +0530 Subject: [PATCH 06/10] Updated logging and added null check --- .../com/RDS/skilltree/Filters/JWTAuthenticationFilter.java | 4 +++- .../src/main/java/com/RDS/skilltree/User/UserDRO.java | 5 +++++ .../src/main/java/com/RDS/skilltree/utils/FetchAPI.java | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java index 7f3cee9c..3c2914ea 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/Filters/JWTAuthenticationFilter.java @@ -9,6 +9,7 @@ import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; @@ -19,6 +20,7 @@ import java.net.URL; import java.util.concurrent.CompletableFuture; +@Slf4j public class JWTAuthenticationFilter extends OncePerRequestFilter { @Autowired @@ -69,7 +71,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { - e.printStackTrace(); + log.error("Error in fetching the user details, error : {}", e.getMessage(), e); throw new RuntimeException(e); } filterChain.doFilter(request, response); diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java index 2fdaf086..285a7e64 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserDRO.java @@ -1,7 +1,9 @@ package com.RDS.skilltree.User; +import com.RDS.skilltree.Exceptions.NoEntityException; import lombok.*; import org.hibernate.usertype.UserType; +import org.springframework.util.ObjectUtils; import java.net.URL; import java.time.Instant; @@ -31,6 +33,9 @@ public static UserModel toModel(UserDRO user) { } public static UserModel compareAndUpdateModel(UserModel user, UserDRO userDRO) { + if (ObjectUtils.isEmpty(user)){ + throw new NoEntityException("No User found with the given Id"); + } if (userDRO.getRdsUserId() != null) { user.setRdsUserId(user.getRdsUserId()); } diff --git a/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java b/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java index 80f18ef1..503d0256 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/utils/FetchAPI.java @@ -1,6 +1,7 @@ package com.RDS.skilltree.utils; import com.RDS.skilltree.utils.RDSUser.Response; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -12,6 +13,7 @@ import java.util.concurrent.CompletableFuture; @Component +@Slf4j public class FetchAPI { private final RestTemplate restTemplate; @@ -27,6 +29,7 @@ public CompletableFuture getRDSUserData(String userId) { Response result = response.getBody(); return CompletableFuture.completedFuture(result); }catch (Exception e){ + log.error("Error in calling the RDS backend, error : {}", e.getMessage(), e); throw new HttpClientErrorException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); } } From 5dea02e6310e2ebfffe451850c5455245e1cfa3e Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Wed, 13 Dec 2023 19:58:54 +0530 Subject: [PATCH 07/10] Uncommenting files which got commented due to mistake --- .../com/RDS/skilltree/User/UserService.java | 36 ++--- .../RDS/skilltree/User/UserServiceImpl.java | 145 +++++++++--------- 2 files changed, 89 insertions(+), 92 deletions(-) diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java index 082c240c..b7f90149 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserService.java @@ -1,19 +1,17 @@ -//package com.RDS.skilltree.User; -// -// -//import org.springframework.stereotype.Service; -// -//import java.util.List; -//import java.util.UUID; -// -//public interface UserService { -// UserDTO createUser(UserDRO user); -// -// void updateUser(UUID id,UserDRO user); -// -// UserDTO getUserById(UUID id); -// -// List getAllUsers(); -// -// void addSkill(UUID skill, UUID userId); -//} \ No newline at end of file +package com.RDS.skilltree.User; + + +import java.util.List; +import java.util.UUID; + +public interface UserService { + UserDTO createUser(UserDRO user); + + void updateUser(UUID id,UserDRO user); + + UserDTO getUserById(UUID id); + + List getAllUsers(); + + void addSkill(UUID skill, UUID userId); +} \ No newline at end of file diff --git a/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java b/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java index b61aa9a0..bcfd8f54 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/User/UserServiceImpl.java @@ -1,73 +1,72 @@ -//package com.RDS.skilltree.User; -// -//import com.RDS.skilltree.Exceptions.NoEntityException; -//import com.RDS.skilltree.Skill.SkillModel; -//import com.RDS.skilltree.Skill.SkillRepository; -//import jakarta.transaction.Transactional; -//import org.springframework.stereotype.Service; -// -//import java.util.Collections; -//import java.util.List; -//import java.util.Optional; -//import java.util.UUID; -// -//@Service -//public class UserServiceImpl implements UserService{ -// private final UserRepository userRepository; -// private final SkillRepository skillRepository; -// public UserServiceImpl(UserRepository userRepository, SkillRepository skillRepository){ -// this.userRepository = userRepository; -// this.skillRepository = skillRepository; -// } -// -// @Override -// public UserDTO createUser(UserDRO user) { -// UserModel userModel = UserDRO.toModel(user); -// userRepository.save(userModel); -// return UserDTO.toDTO(userModel); -// } -// -// @Override -// public void updateUser(UUID id, UserDRO user) { -// -// } -// -// @Override -// public UserDTO getUserById(UUID id) { -// Optional userModel = userRepository.findById(id); -// return userModel.map(UserDTO::getUsersWithSkills).orElse(null); -// } -// -// @Override -// public List getAllUsers() { -// return null; -// } -// -// /** -// * updates the user and skill both -// * @param skillId -// * @param userId -// */ -// @Override -// @Transactional -// public void addSkill(UUID skillId, UUID userId) { -// Optional userOptional = userRepository.findById(userId); -// Optional skillOptional = skillRepository.findById(skillId); -// -// if(userOptional.isPresent() && skillOptional.isPresent()){ -// UserModel userModel = userOptional.get(); -// SkillModel skillModel = skillOptional.get(); -// -// userModel.getSkills().add(skillModel); -// skillModel.getUsers().add(userModel); -// -// userRepository.save(userModel); -// skillRepository.save(skillModel); -// } else { -// if(skillOptional.isEmpty()){ -// throw new NoEntityException("Skill Id is not passed in the input"); -// } -// throw new NoEntityException("User with Id doesn't exists"); -// } -// } -//} +package com.RDS.skilltree.User; + +import com.RDS.skilltree.Exceptions.NoEntityException; +import com.RDS.skilltree.Skill.SkillModel; +import com.RDS.skilltree.Skill.SkillRepository; +import jakarta.transaction.Transactional; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Service +public class UserServiceImpl implements UserService{ + private final UserRepository userRepository; + private final SkillRepository skillRepository; + public UserServiceImpl(UserRepository userRepository, SkillRepository skillRepository){ + this.userRepository = userRepository; + this.skillRepository = skillRepository; + } + + @Override + public UserDTO createUser(UserDRO user) { + UserModel userModel = UserDRO.toModel(user); + userRepository.save(userModel); + return UserDTO.toDTO(userModel); + } + + @Override + public void updateUser(UUID id, UserDRO user) { + + } + + @Override + public UserDTO getUserById(UUID id) { + Optional userModel = userRepository.findById(id); + return userModel.map(UserDTO::getUsersWithSkills).orElse(null); + } + + @Override + public List getAllUsers() { + return null; + } + + /** + * updates the user and skill both + * @param skillId + * @param userId + */ + @Override + @Transactional + public void addSkill(UUID skillId, UUID userId) { + Optional userOptional = userRepository.findById(userId); + Optional skillOptional = skillRepository.findById(skillId); + + if(userOptional.isPresent() && skillOptional.isPresent()){ + UserModel userModel = userOptional.get(); + SkillModel skillModel = skillOptional.get(); + + userModel.getSkills().add(skillModel); + skillModel.getUsers().add(userModel); + + userRepository.save(userModel); + skillRepository.save(skillModel); + } else { + if(skillOptional.isEmpty()){ + throw new NoEntityException("Skill Id is not passed in the input"); + } + throw new NoEntityException("User with Id doesn't exists"); + } + } +} From 80f99886d6b5655fd533d730bfe4efd21b336432 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Thu, 21 Dec 2023 21:53:09 +0530 Subject: [PATCH 08/10] Added removed dependencies --- skill-tree /pom.xml | 26 +++++++++++++++++++ .../RDS/skilltree/SkillTreeApplication.java | 14 ---------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/skill-tree /pom.xml b/skill-tree /pom.xml index 76cb6a90..11e8c325 100644 --- a/skill-tree /pom.xml +++ b/skill-tree /pom.xml @@ -15,6 +15,7 @@ skill tree project 17 + 0.11.2 1.17.6 @@ -85,6 +86,31 @@ junit-jupiter test + + org.springframework.boot + spring-boot-starter-security + + + io.jsonwebtoken + jjwt-api + 0.11.2 + + + io.jsonwebtoken + jjwt-api + ${io.jsonwebtoken.version} + + + io.jsonwebtoken + jjwt-impl + ${io.jsonwebtoken.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${io.jsonwebtoken.version} + diff --git a/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java b/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java index 18803054..d0e7f01b 100644 --- a/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java +++ b/skill-tree /src/main/java/com/RDS/skilltree/SkillTreeApplication.java @@ -1,24 +1,10 @@ package com.RDS.skilltree; -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.info.Info; -import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -@OpenAPIDefinition( - info = @Info( - title = "Skill Tree", - version = "1.0.0", - description = "Skill Tree" - ), - tags = @Tag( - name = "Skill Tree", - description = "Skill Tree" - ) -) public class SkillTreeApplication { public static void main(String[] args) { From e42abdfef743abfcc63903cd1f46b74b838d4575 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Sat, 23 Dec 2023 20:17:19 +0530 Subject: [PATCH 09/10] resolving merge conflicts --- skill-tree/pom.xml | 148 ++++----------------------------------------- 1 file changed, 11 insertions(+), 137 deletions(-) diff --git a/skill-tree/pom.xml b/skill-tree/pom.xml index 11e8c325..b0fbb1ce 100644 --- a/skill-tree/pom.xml +++ b/skill-tree/pom.xml @@ -1,143 +1,17 @@ - + 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.1.2 - - - com.RDS + + org.example skill-tree - 0.0.1-SNAPSHOT - skill-tree - skill tree project + 1.0-SNAPSHOT + - 17 - 0.11.2 - 1.17.6 + 17 + 17 + UTF-8 - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-web - - - org.projectlombok - lombok - true - - - com.mysql - mysql-connector-j - runtime - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-validation - - - io.rest-assured - rest-assured - 4.5.1 - test - - - org.apache.groovy - groovy-xml - - - - - org.springframework.boot - spring-boot-testcontainers - test - 3.2.0 - - - junit - junit - - - - - org.junit.vintage - junit-vintage-engine - 5.7.0 - test - - - org.testcontainers - mysql - test - - - org.testcontainers - junit-jupiter - test - - - org.springframework.boot - spring-boot-starter-security - - - io.jsonwebtoken - jjwt-api - 0.11.2 - - - io.jsonwebtoken - jjwt-api - ${io.jsonwebtoken.version} - - - io.jsonwebtoken - jjwt-impl - ${io.jsonwebtoken.version} - runtime - - - io.jsonwebtoken - jjwt-jackson - ${io.jsonwebtoken.version} - - - - - - org.testcontainers - testcontainers-bom - ${testcontainers.version} - pom - import - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - - - + \ No newline at end of file From d20539a218c8d0a34bfeca18324c3c096f023328 Mon Sep 17 00:00:00 2001 From: vikhyat187 Date: Sat, 23 Dec 2023 20:18:45 +0530 Subject: [PATCH 10/10] Merge conflicts --- skill-tree/pom.xml | 148 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 137 insertions(+), 11 deletions(-) diff --git a/skill-tree/pom.xml b/skill-tree/pom.xml index b0fbb1ce..11e8c325 100644 --- a/skill-tree/pom.xml +++ b/skill-tree/pom.xml @@ -1,17 +1,143 @@ - + 4.0.0 - - org.example + + org.springframework.boot + spring-boot-starter-parent + 3.1.2 + + + com.RDS skill-tree - 1.0-SNAPSHOT - + 0.0.1-SNAPSHOT + skill-tree + skill tree project - 17 - 17 - UTF-8 + 17 + 0.11.2 + 1.17.6 - + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + true + + + com.mysql + mysql-connector-j + runtime + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-validation + + + io.rest-assured + rest-assured + 4.5.1 + test + + + org.apache.groovy + groovy-xml + + + + + org.springframework.boot + spring-boot-testcontainers + test + 3.2.0 + + + junit + junit + + + + + org.junit.vintage + junit-vintage-engine + 5.7.0 + test + + + org.testcontainers + mysql + test + + + org.testcontainers + junit-jupiter + test + + + org.springframework.boot + spring-boot-starter-security + + + io.jsonwebtoken + jjwt-api + 0.11.2 + + + io.jsonwebtoken + jjwt-api + ${io.jsonwebtoken.version} + + + io.jsonwebtoken + jjwt-impl + ${io.jsonwebtoken.version} + runtime + + + io.jsonwebtoken + jjwt-jackson + ${io.jsonwebtoken.version} + + + + + + org.testcontainers + testcontainers-bom + ${testcontainers.version} + pom + import + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + \ No newline at end of file