Skip to content

Commit

Permalink
refactor auth service by introducing jwt helper class. return user id…
Browse files Browse the repository at this point in the history
… for isauthorisedUserAccess function
  • Loading branch information
linxiaoxin committed Jul 23, 2024
1 parent 080149c commit 3337b5e
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 42 deletions.
53 changes: 53 additions & 0 deletions src/main/java/com/quemistry/auth_ms/Util/JwtHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.quemistry.auth_ms.Util;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.ArrayList;
import java.util.Base64;
import java.util.Map;

public class JwtHelper {
private static final String EMAIL = "email";
private static final String USERID = "sub";
private static final String USER_GROUP ="cognito:groups";

private final Map<String, Object> KeyValue;
public JwtHelper(String jwtString){
this.KeyValue = JwtKeyValue(jwtString);
}

public Boolean getValid(){
return this.KeyValue != null;
}

public String getEmail(){
return (String)this.KeyValue.get(JwtHelper.EMAIL);
}

public String getUserId(){
return (String)this.KeyValue.get(JwtHelper.USERID);
}

public String[] getUserGroup(){
var useRoles = (ArrayList<Object>)this.KeyValue.get(JwtHelper.USER_GROUP);
if(useRoles != null) {
return useRoles.toArray(new String[useRoles.size()]);
}
return new String[0];
}
private static Map<String, Object> JwtKeyValue(String jwtString) {
String[] chunks = (jwtString == null) ? null : jwtString.split("\\.");
if (chunks == null || chunks.length < 3) {
return null;
}
try {
Base64.Decoder decoder = Base64.getUrlDecoder();
String payload = new String(decoder.decode(chunks[1]));
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(payload, new TypeReference<>() {});
} catch (Exception ex) {
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public ResponseEntity<Boolean> isAuthorised(@RequestBody IsAuthorisedRequest req
return ResponseEntity.status(HttpStatus.OK).body(result);
}
@PostMapping("isauthoriseduser")
public ResponseEntity<Boolean> isAuthorisedUser(@RequestBody IsAuthorisedRequest request){
public ResponseEntity<String> isAuthorisedUser(@RequestBody IsAuthorisedRequest request){
var result =authenticationService.checkUserSessionAccess(request.getSessionId(), request.getPath(), request.getMethod());
return ResponseEntity.status(HttpStatus.OK).body(result);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ public interface AuthenticationService {

Boolean checkAccess(String roleName, String path, String method);

Boolean checkUserSessionAccess(String sessionId, String path, String method);
String checkUserSessionAccess(String sessionId, String path, String method);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.quemistry.auth_ms.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.quemistry.auth_ms.Util.JwtHelper;
import com.quemistry.auth_ms.model.TokenRequest;
import com.quemistry.auth_ms.model.TokenResponse;
import com.quemistry.auth_ms.model.UserProfile;
Expand All @@ -16,9 +15,6 @@
import org.springframework.web.client.RestTemplate;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Map;
import java.util.UUID;

@Service
Expand Down Expand Up @@ -64,25 +60,20 @@ public UserProfile getAccessToken(TokenRequest request) {

if(response.getStatusCode() == HttpStatus.OK){
var idToken = response.getBody().getIdToken();
String[] chunks = (idToken == null) ? null : idToken.split("\\.");
if(chunks == null || chunks.length < 3) {
log.error("Invalid response from Idp Cognito.");
}
Base64.Decoder decoder = Base64.getUrlDecoder();
String payload = new String(decoder.decode(chunks[1]));
ObjectMapper mapper = new ObjectMapper();
try {
Map<String, Object> map = mapper.readValue(payload, new TypeReference<>() {});
if(idToken == null)
return null;

try {
JwtHelper jwtIdToken = new JwtHelper(idToken);
if(!jwtIdToken.getValid()){
log.error("Invalid Id token");
return null;
}
//creates session id as key and store user profile and access tokens info in redis
user = new UserProfile();
user.setSessionId(UUID.randomUUID().toString());
user.setEmail((String)map.get("email"));
var useRoles = (ArrayList<Object>)map.get("cognito:groups");
if(useRoles != null) {
var roles = useRoles.toArray(new String[useRoles.size()]);
user.setRoles(roles);
}
user.setEmail(jwtIdToken.getEmail());
user.setRoles(jwtIdToken.getUserGroup());

redisTemplate.opsForValue().set(user.getSessionId()+"_profile", user, Duration.ofSeconds(SESSION_TIMEOUT));
redisTemplate.opsForValue().set(user.getSessionId()+"_tokens", response.getBody(), Duration.ofSeconds(SESSION_TIMEOUT));
Expand Down Expand Up @@ -133,16 +124,21 @@ public Boolean checkAccess(String roleName, String path, String method) {
}

@Override
public Boolean checkUserSessionAccess(String sessionId, String path, String method) {
public String checkUserSessionAccess(String sessionId, String path, String method) {
//get user profile role
log.info("checkUserSessionAccess invoked");
String userId = "";
var profile = ((UserProfile) redisTemplate.opsForValue().get(sessionId + "_profile"));
if(profile == null)
var tokens = ((TokenResponse) redisTemplate.opsForValue().get(sessionId + "_tokens"));
if(tokens == null || profile == null)
{
log.info("checkUserSessionAccess: session not found");
return false;
return "";
}else{
log.info("checkUserSessionAccess: found. With roles:"+ String.join(";",profile.getRoles()));
//check if user token has expired. If yes to refresh, asynchronously.
JwtHelper jwtAccessToken = new JwtHelper(tokens.getAccessToken());
userId = jwtAccessToken.getUserId();
}
//get role
var roles = roleRepository.findByNames(profile.getRoles());
Expand All @@ -154,11 +150,11 @@ public Boolean checkUserSessionAccess(String sessionId, String path, String meth
roles.forEach(role -> rolesFound.append(role.getName()+";"));
log.info("checkUserSessionAccess: "+rolesFound);
}
if(roles.stream().anyMatch(role ->
if(roles.stream().anyMatch(role ->
role.getGrantedWith().stream().anyMatch(granted -> granted.getPath().compareToIgnoreCase(path) == 0
&& granted.getMethod().compareToIgnoreCase(method) == 0))){
return true;
return userId;
}
return false;
return "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,14 @@ void givenisAuthorisedUser_Success() throws Exception{
ObjectMapper mapper = new ObjectMapper();

given(authenticationService.checkUserSessionAccess(request.getSessionId(), request.getPath(), request.getMethod()))
.willReturn(true);
.willReturn("userid-test");

var result = mockMvc.perform(post("/v1/auth/isauthoriseduser")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(request)))
.andExpect(status().isOk())
.andReturn();

Assertions.assertEquals("true", result.getResponse().getContentAsString());
Assertions.assertEquals("userid-test", result.getResponse().getContentAsString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ public class AuthenticationServiceImplTest {
@InjectMocks
private AuthenticationServiceImpl authenticationService;


private TokenResponse tokenResponse;
private UserProfile user;

private String idToken;
private final String UserId = "c9aad54c-60e1-7045-e712-9ad1da73f87a";
@BeforeEach
void init() throws NoSuchFieldException, IllegalAccessException {

Expand All @@ -65,11 +64,12 @@ void init() throws NoSuchFieldException, IllegalAccessException {
user.setSessionId(UUID.randomUUID().toString());

//idtoken with email set as [email protected]
idToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoidThRdnNKdFY1TXRETFdodF9xQmFpZyIsInN1YiI6ImM5YWFkNTRjLTYwZTEtNzA0NS1lNzEyLTlhZDFkYTczZjg3YSIsImNvZ25pdG86Z3JvdXBzIjpbInR1dG9yIiwiYWRtaW4iXSwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRwLmFwLXNvdXRoZWFzdC0xLmFtYXpvbmF3cy5jb20vYXAtc291dGhlYXN0LTFfWmIwSmwwN1dzIiwiY29nbml0bzp1c2VybmFtZSI6Imdvb2dsZV8xMDQwMjk0OTE2Njc5NjE1ODg4NDAiLCJvcmlnaW5fanRpIjoiYTY1M2ExMDEtNTUyMi00ZDIyLTk1NzctZGZkZjA4ZDM5NDc4IiwiYXVkIjoiMXEzMHZtZDB2Y2VlNmsxbHJwMmluMTA2MjMiLCJpZGVudGl0aWVzIjpbeyJkYXRlQ3JlYXRlZCI6IjE3MTcyNTI0Mzk3MDMiLCJ1c2VySWQiOiIxMDQwMjk0OTE2Njc5NjE1ODg4NDAiLCJwcm92aWRlck5hbWUiOiJHb29nbGUiLCJwcm92aWRlclR5cGUiOiJHb29nbGUiLCJpc3N1ZXIiOm51bGwsInByaW1hcnkiOiJ0cnVlIn1dLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcyMTE0MDE3NywiZXhwIjoxNzIxMTQxMDc3LCJpYXQiOjE3MjExNDAxNzcsImp0aSI6IjU5YjlkZmZlLWFkMjItNDMyZC05ZWIxLTRiZmVhYjFhOGY4MyIsImVtYWlsIjoidGVzdFVzZXJAZW1haWwuY29tIn0.HabZEsulPCsu-IYRE_G42RUWo0k5jMJqYSxJx_QgtuY";
String idToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoidThRdnNKdFY1TXRETFdodF9xQmFpZyIsInN1YiI6ImM5YWFkNTRjLTYwZTEtNzA0NS1lNzEyLTlhZDFkYTczZjg3YSIsImNvZ25pdG86Z3JvdXBzIjpbInR1dG9yIiwiYWRtaW4iXSwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRwLmFwLXNvdXRoZWFzdC0xLmFtYXpvbmF3cy5jb20vYXAtc291dGhlYXN0LTFfWmIwSmwwN1dzIiwiY29nbml0bzp1c2VybmFtZSI6Imdvb2dsZV8xMDQwMjk0OTE2Njc5NjE1ODg4NDAiLCJvcmlnaW5fanRpIjoiYTY1M2ExMDEtNTUyMi00ZDIyLTk1NzctZGZkZjA4ZDM5NDc4IiwiYXVkIjoiMXEzMHZtZDB2Y2VlNmsxbHJwMmluMTA2MjMiLCJpZGVudGl0aWVzIjpbeyJkYXRlQ3JlYXRlZCI6IjE3MTcyNTI0Mzk3MDMiLCJ1c2VySWQiOiIxMDQwMjk0OTE2Njc5NjE1ODg4NDAiLCJwcm92aWRlck5hbWUiOiJHb29nbGUiLCJwcm92aWRlclR5cGUiOiJHb29nbGUiLCJpc3N1ZXIiOm51bGwsInByaW1hcnkiOiJ0cnVlIn1dLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcyMTE0MDE3NywiZXhwIjoxNzIxMTQxMDc3LCJpYXQiOjE3MjExNDAxNzcsImp0aSI6IjU5YjlkZmZlLWFkMjItNDMyZC05ZWIxLTRiZmVhYjFhOGY4MyIsImVtYWlsIjoidGVzdFVzZXJAZW1haWwuY29tIn0.HabZEsulPCsu-IYRE_G42RUWo0k5jMJqYSxJx_QgtuY";

tokenResponse = new TokenResponse();
tokenResponse.setIdToken(idToken);
tokenResponse.setAccessToken("testAccessToken");
tokenResponse.setAccessToken("testRefreshToken");
tokenResponse.setAccessToken(idToken);
tokenResponse.setRefreshToken("testRefreshToken");
tokenResponse.setExpiresIn(120);
}

Expand Down Expand Up @@ -101,10 +101,9 @@ void givenGetAccessToken_Success(){

Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);

var result = authenticationService.getAccessToken(tokenRequest);
tokenResponse.setEmail("[email protected]");
var result = authenticationService.getAccessToken(tokenRequest);

Assertions.assertEquals(user.getEmail(), result.getEmail() );
Assertions.assertEquals(user.getEmail(), result.getEmail() );
Assertions.assertEquals(user.getRoles().length, result.getRoles().length );

}
Expand Down Expand Up @@ -182,11 +181,13 @@ void givencheckUserSessionAccess_Success(){
Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_profile"))
.thenReturn(user);
Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_tokens"))
.thenReturn(tokenResponse);

Mockito.when(roleRepository.findByNames(user.getRoles())).thenReturn(roles);
var result = authenticationService.checkUserSessionAccess(user.getSessionId(), "/questions", "GET");

Assertions.assertEquals(result, true);
Assertions.assertEquals(result, UserId);
}

@Test
Expand All @@ -207,11 +208,13 @@ void givencheckUserSessionAccess_InValidSession(){
Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_profile"))
.thenReturn(null);
Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_tokens"))
.thenReturn(null);

Mockito.when(roleRepository.findByNames(user.getRoles())).thenReturn(roles);
var result = authenticationService.checkUserSessionAccess(user.getSessionId(), "/questions", "GET");

Assertions.assertEquals(result, false);
Assertions.assertEquals(result, "");
}

@Test
Expand All @@ -231,11 +234,13 @@ void givencheckUserSessionAccess_NoAccess(){

Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations);
Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_profile"))
.thenReturn(null);
.thenReturn(user);
Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_tokens"))
.thenReturn(tokenResponse);

Mockito.when(roleRepository.findByNames(user.getRoles())).thenReturn(roles);
var result = authenticationService.checkUserSessionAccess(user.getSessionId(), "/questions", "POST");

Assertions.assertEquals(result, false);
Assertions.assertEquals(result, "");
}
}

0 comments on commit 3337b5e

Please sign in to comment.