Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Authorisation and integrate sign up for users #22

Merged
merged 24 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a34ded2
added authorisation for skill tree
DashDeipayan Sep 22, 2023
d5b44fc
added services
DashDeipayan Sep 22, 2023
20a8924
Merge branch 'develop' into jpa-entity-rel
DashDeipayan Sep 22, 2023
b146108
Resolving PR comments
vikhyat187 Nov 11, 2023
af96355
Making the RSA key generation a static variable
vikhyat187 Nov 14, 2023
4bdce6a
Merge branch 'develop' into jpa-entity-rel
vikhyat187 Nov 27, 2023
5b6889d
Resolving comments
vikhyat187 Nov 27, 2023
3b15a20
Merge upstream changes
vikhyat187 Nov 27, 2023
a6953be
Making the dependencies injected by constructor, to make class testable
vikhyat187 Nov 28, 2023
eab8fb9
Updated logging and added null check
vikhyat187 Dec 2, 2023
afce972
Merge branch 'develop' into jpa-entity-rel
Ajeyakrishna-k Dec 8, 2023
e7b7d80
Merge branch 'develop' into jpa-entity-rel
vikhyat187 Dec 9, 2023
1a77019
resolving merge conflicts
vikhyat187 Dec 13, 2023
5dea02e
Uncommenting files which got commented due to mistake
vikhyat187 Dec 13, 2023
35516cc
Merge branch 'develop' into jpa-entity-rel
vikhyat187 Dec 16, 2023
56a1da9
Resolving merge conflicts
vikhyat187 Dec 19, 2023
9fe423f
Merge branch 'jpa-entity-rel' of https://github.com/Real-Dev-Squad/sk…
vikhyat187 Dec 19, 2023
0386e58
Merge branch 'develop' into jpa-entity-rel
vikhyat187 Dec 20, 2023
80f9988
Added removed dependencies
vikhyat187 Dec 21, 2023
b3e394a
Removing the extra space from the directory name
vikhyat187 Dec 22, 2023
ecfdf46
Merge branch 'develop' into jpa-entity-rel
vikhyat187 Dec 23, 2023
e42abdf
resolving merge conflicts
vikhyat187 Dec 23, 2023
d20539a
Merge conflicts
vikhyat187 Dec 23, 2023
a7ec2c7
Merge branch 'jpa-entity-rel' of https://github.com/Real-Dev-Squad/sk…
vikhyat187 Dec 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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(userService);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
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 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;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.net.URL;
import java.util.concurrent.CompletableFuture;

@Slf4j
public class JWTAuthenticationFilter extends OncePerRequestFilter {

@Autowired
private JWTUtils jwtUtils;
vikhyat187 marked this conversation as resolved.
Show resolved Hide resolved

@Autowired
private FetchAPI fetchAPI;

private final UserService userService;
vikhyat187 marked this conversation as resolved.
Show resolved Hide resolved

@Autowired
private UserRepository userRepository;

public JWTAuthenticationFilter(UserService userService){
this.userService = userService;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
vikhyat187 marked this conversation as resolved.
Show resolved Hide resolved
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<Response> userResponse = fetchAPI.getRDSUserData(rdsUserId);
CompletableFuture.anyOf(userResponse).join();
Response rdsUserResponse = userResponse.get();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get() method throws an exception if the CompletableFuture does not complete successfully. This means that if the getRDSUserData() method fails, the entire request will fail.

Copy link
Contributor

@vikhyat187 vikhyat187 Oct 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we handle this using timeouts? @iamitprakash, so that we don't perform the operation for too long and return TimeoutException in case of service taking higher time?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is any exception in making the call to the RDS backend we return an exception.

UserDRO userDRO = UserDRO.builder()
.rdsUserId(rdsUserId)
.firstName(rdsUserResponse.getUser().getFirst_name())
.lastName(rdsUserResponse.getUser().getLast_name())
.imageUrl(new URL(rdsUserResponse.getUser().getPicture().getUrl()))
.role(UserRole.USER)
.build();
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) {
log.error("Error in fetching the user details, error : {}", e.getMessage(), e);
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;
}
}
Original file line number Diff line number Diff line change
@@ -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() {
}
}
1 change: 1 addition & 0 deletions skill-tree/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ build/

### VS Code ###
.vscode/
dev.env
26 changes: 26 additions & 0 deletions skill-tree/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<description>skill tree project</description>
<properties>
<java.version>17</java.version>
<io.jsonwebtoken.version>0.11.2</io.jsonwebtoken.version>
<testcontainers.version>1.17.6</testcontainers.version>
</properties>
<dependencies>
Expand Down Expand Up @@ -85,6 +86,31 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${io.jsonwebtoken.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${io.jsonwebtoken.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${io.jsonwebtoken.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<UserModel, UUID> {
Optional<UserModel> findByRdsUserId(String rdsUserId);

Boolean existsByRdsUserId(String rdsUserId);
}
36 changes: 36 additions & 0 deletions skill-tree/src/main/java/com/RDS/skilltree/utils/FetchAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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;
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
@Slf4j
public class FetchAPI {
private final RestTemplate restTemplate;

public FetchAPI(RestTemplateBuilder restTemplateBuilder){
restTemplate = restTemplateBuilder.build();
}

@Async
public CompletableFuture<Response> getRDSUserData(String userId) {
String url = String.format("https://api.realdevsquad.com/users/userId/%s", userId);
try{
ResponseEntity<Response> response = restTemplate.getForEntity(url, Response.class);
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());
}
}
}
Loading
Loading