Skip to content

Commit

Permalink
P4ADEV-445 userInfo controller2service (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
antonioT90 authored Jun 5, 2024
1 parent 6bc7761 commit 122b7c5
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 31 deletions.
4 changes: 1 addition & 3 deletions openapi/p4pa-auth.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/UserInfo'
'400':
description: Invalid request
'401':
description: Authentication failed
description: Unauthorized
'500':
description: Server ERROR
content:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package it.gov.pagopa.payhub.auth.controller;

import it.gov.pagopa.payhub.auth.exception.custom.InvalidAccessTokenException;
import it.gov.pagopa.payhub.auth.service.AuthService;
import it.gov.pagopa.payhub.controller.generated.AuthApi;
import it.gov.pagopa.payhub.model.generated.AccessToken;
import it.gov.pagopa.payhub.model.generated.UserInfo;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@RestController
public class AuthControllerImpl implements AuthApi {
Expand All @@ -21,4 +27,18 @@ public ResponseEntity<AccessToken> postToken(String clientId, String grantType,
AccessToken accessToken = authService.postToken(clientId, grantType, subjectToken, subjectIssuer, subjectTokenType, scope);
return new ResponseEntity<>(accessToken, HttpStatus.OK);
}

@Override
public ResponseEntity<UserInfo> getUserInfo() {
String authorization = getAuthorizationHeader();
if(StringUtils.hasText(authorization)){
return ResponseEntity.ok(authService.getUserInfo(authorization.replace("Bearer ", "")));
} else {
throw new InvalidAccessTokenException("Missing authorization header");
}
}

private static String getAuthorizationHeader() {
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getHeader(HttpHeaders.AUTHORIZATION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ public ResponseEntity<AuthErrorDTO> handleUnsupportedGrantType(RuntimeException
return handleAuthErrorException(ex, request, HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.UNSUPPORTED_GRANT_TYPE);
}

@ExceptionHandler({InvalidAccessTokenException.class})
public ResponseEntity<Void> handleInvalidAccessTokenException(InvalidAccessTokenException ex, HttpServletRequest request){
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

@ExceptionHandler(MissingServletRequestParameterException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public AuthErrorDTO handleMissingServletRequestParameterException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package it.gov.pagopa.payhub.auth.exception.custom;

public class InvalidAccessTokenException extends RuntimeException {
public InvalidAccessTokenException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package it.gov.pagopa.payhub.auth.service;

import it.gov.pagopa.payhub.model.generated.AccessToken;
import it.gov.pagopa.payhub.model.generated.UserInfo;

public interface AuthService {

AccessToken postToken(String clientId, String grantType, String subjectToken, String subjectIssuer, String subjectTokenType, String scope);
UserInfo getUserInfo(String accessToken);
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
package it.gov.pagopa.payhub.auth.service;

import it.gov.pagopa.payhub.auth.service.exchange.ExchangeTokenService;
import it.gov.pagopa.payhub.auth.service.user.UserService;
import it.gov.pagopa.payhub.model.generated.AccessToken;
import it.gov.pagopa.payhub.model.generated.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class AuthServiceImpl implements AuthService{
private final ExchangeTokenService exchangeTokenService;
private final UserService userService;

public AuthServiceImpl(ExchangeTokenService exchangeTokenService) {
public AuthServiceImpl(ExchangeTokenService exchangeTokenService, UserService userService) {
this.exchangeTokenService = exchangeTokenService;
this.userService = userService;
}

@Override
public AccessToken postToken(String clientId, String grantType, String subjectToken, String subjectIssuer, String subjectTokenType, String scope) {
return exchangeTokenService.postToken(clientId, grantType, subjectToken, subjectIssuer, subjectTokenType, scope);
}

@Override
public UserInfo getUserInfo(String accessToken) {
return userService.getUserInfo(accessToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package it.gov.pagopa.payhub.auth.service.user;

import it.gov.pagopa.payhub.model.generated.UserInfo;

public interface UserService {
UserInfo getUserInfo(String accessToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package it.gov.pagopa.payhub.auth.service.user;

import it.gov.pagopa.payhub.model.generated.UserInfo;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{
@Override
public UserInfo getUserInfo(String accessToken) {
return null; //TODO
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@
import it.gov.pagopa.payhub.auth.service.AuthService;
import it.gov.pagopa.payhub.model.generated.AccessToken;
import it.gov.pagopa.payhub.model.generated.AuthErrorDTO;
import it.gov.pagopa.payhub.model.generated.UserInfo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(AuthControllerImpl.class)
Expand All @@ -34,16 +40,16 @@ class AuthControllerTest {
private AuthService authServiceMock;

@Test
void givenExpectedAuthTokenThenOk() throws Exception {
void givenExpectedAuthTokenWhenPostTokenThenOk() throws Exception {
MvcResult result =
invokeAndVerify(null, HttpStatus.OK, null);
invokePostTokenAndVerify(null, HttpStatus.OK, null);

Assertions.assertNotNull(result);
Assertions.assertEquals("{\"accessToken\":\"token\",\"tokenType\":\"bearer\",\"expiresIn\":0}", result.getResponse().getContentAsString());
}

@Test
void givenRequestWithoutAuthTokenThenBadRequest() throws Exception {
void givenRequestWithoutAuthTokenWhenPostTokenThenBadRequest() throws Exception {
MvcResult result = mockMvc.perform(
post("/payhub/auth/token")
).andExpect(status().isBadRequest()).andReturn();
Expand All @@ -54,47 +60,47 @@ void givenRequestWithoutAuthTokenThenBadRequest() throws Exception {
}

@Test
void givenInvalidExchangeClientExceptionThenInvalidClientError() throws Exception {
invokeAndVerify(new InvalidExchangeClientException("description"), HttpStatus.UNAUTHORIZED, AuthErrorDTO.ErrorEnum.INVALID_CLIENT);
void givenInvalidExchangeClientExceptionWhenPostTokenThenInvalidClientError() throws Exception {
invokePostTokenAndVerify(new InvalidExchangeClientException("description"), HttpStatus.UNAUTHORIZED, AuthErrorDTO.ErrorEnum.INVALID_CLIENT);
}

@Test
void givenInvalidExchangeRequestExceptionThenInvalidClientError() throws Exception {
invokeAndVerify(new InvalidExchangeRequestException("description"), HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.INVALID_REQUEST);
void givenInvalidExchangeRequestExceptionWhenPostTokenThenInvalidClientError() throws Exception {
invokePostTokenAndVerify(new InvalidExchangeRequestException("description"), HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.INVALID_REQUEST);
}

@Test
void givenInvalidGrantTypeExceptionThenInvalidClientError() throws Exception {
invokeAndVerify(new InvalidGrantTypeException("description"), HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.UNSUPPORTED_GRANT_TYPE);
void givenInvalidGrantTypeExceptionWhenPostTokenThenInvalidClientError() throws Exception {
invokePostTokenAndVerify(new InvalidGrantTypeException("description"), HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.UNSUPPORTED_GRANT_TYPE);
}

@Test
void givenInvalidTokenExceptionThenInvalidClientError() throws Exception {
invokeAndVerify(new InvalidTokenException("description"), HttpStatus.UNAUTHORIZED, AuthErrorDTO.ErrorEnum.INVALID_GRANT);
void givenInvalidTokenExceptionWhenPostTokenThenInvalidClientError() throws Exception {
invokePostTokenAndVerify(new InvalidTokenException("description"), HttpStatus.UNAUTHORIZED, AuthErrorDTO.ErrorEnum.INVALID_GRANT);
}

@Test
void givenInvalidTokenIssuerExceptionThenInvalidClientError() throws Exception {
invokeAndVerify(new InvalidTokenIssuerException("description"), HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.INVALID_REQUEST);
void givenInvalidTokenIssuerExceptionWhenPostTokenThenInvalidClientError() throws Exception {
invokePostTokenAndVerify(new InvalidTokenIssuerException("description"), HttpStatus.BAD_REQUEST, AuthErrorDTO.ErrorEnum.INVALID_REQUEST);
}

@Test
void givenTokenExpiredExceptionThenInvalidClientError() throws Exception {
invokeAndVerify(new TokenExpiredException("description"), HttpStatus.UNAUTHORIZED, AuthErrorDTO.ErrorEnum.INVALID_GRANT);
void givenTokenExpiredExceptionWhenPostTokenThenInvalidClientError() throws Exception {
invokePostTokenAndVerify(new TokenExpiredException("description"), HttpStatus.UNAUTHORIZED, AuthErrorDTO.ErrorEnum.INVALID_GRANT);
}

MvcResult invokeAndVerify(RuntimeException exception, HttpStatus expectedStatus, AuthErrorDTO.ErrorEnum expectedError) throws Exception {
String clientId="CLIENT_ID";
String grantType="GRANT_TYPE";
String subjectToken="SUBJECT_TOKEN";
String subjectIssuer="SUBJECT_ISSUER";
String subjectTokenType="SUBJECT_TOKEN_TYPE";
String scope="SCOPE";
MvcResult invokePostTokenAndVerify(RuntimeException exception, HttpStatus expectedStatus, AuthErrorDTO.ErrorEnum expectedError) throws Exception {
String clientId = "CLIENT_ID";
String grantType = "GRANT_TYPE";
String subjectToken = "SUBJECT_TOKEN";
String subjectIssuer = "SUBJECT_ISSUER";
String subjectTokenType = "SUBJECT_TOKEN_TYPE";
String scope = "SCOPE";

(exception != null
? doThrow(exception)
: doReturn(new AccessToken("token", "bearer", 0)))
.when(authServiceMock).postToken(clientId,grantType,subjectToken,subjectIssuer,subjectTokenType,scope);
.when(authServiceMock).postToken(clientId, grantType, subjectToken, subjectIssuer, subjectTokenType, scope);

MvcResult result = mockMvc.perform(
post("/payhub/auth/token")
Expand All @@ -106,7 +112,7 @@ MvcResult invokeAndVerify(RuntimeException exception, HttpStatus expectedStatus,
.param("scope", scope)
).andExpect(status().is(expectedStatus.value())).andReturn();

if(exception != null && expectedError != null) {
if (exception != null && expectedError != null) {
AuthErrorDTO actual = objectMapper.readValue(result.getResponse().getContentAsString(),
AuthErrorDTO.class);
assertEquals(expectedError, actual.getError());
Expand All @@ -117,4 +123,36 @@ MvcResult invokeAndVerify(RuntimeException exception, HttpStatus expectedStatus,

return result;
}

@Test
void givenRequestWithoutAuthorizationWhenGetUserInfoThenUnauthorized() throws Exception {
mockMvc.perform(
get("/payhub/auth/userinfo")
).andExpect(status().isUnauthorized());
}

@Test
void givenRequestWitAuthorizationWhenGetUserInfoThenOk() throws Exception {
UserInfo expectedUser = UserInfo.builder().userId("USERID").build();

Mockito.when(authServiceMock.getUserInfo("accessToken"))
.thenReturn(expectedUser);

mockMvc.perform(
get("/payhub/auth/userinfo")
.header(HttpHeaders.AUTHORIZATION, "Bearer accessToken")
).andExpect(status().isOk())
.andExpect(content().json("{\"userId\":\"USERID\"}"));
}

@Test
void givenRequestWitInvalidAuthorizationWhenGetUserInfoThenUnauthorized() throws Exception {
Mockito.when(authServiceMock.getUserInfo("accessToken"))
.thenThrow(new InvalidAccessTokenException(""));

mockMvc.perform(
get("/payhub/auth/userinfo")
.header(HttpHeaders.AUTHORIZATION, "Bearer accessToken")
).andExpect(status().isUnauthorized());
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package it.gov.pagopa.payhub.auth.service;

import it.gov.pagopa.payhub.auth.service.exchange.ExchangeTokenService;
import it.gov.pagopa.payhub.auth.service.user.UserService;
import it.gov.pagopa.payhub.model.generated.AccessToken;
import it.gov.pagopa.payhub.model.generated.UserInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
Expand All @@ -16,18 +18,21 @@ class AuthServiceTest {

@Mock
private ExchangeTokenService exchangeTokenServiceMock;
@Mock
private UserService userServiceMock;

private AuthService service;

@BeforeEach
void init(){
service = new AuthServiceImpl(exchangeTokenServiceMock);
service = new AuthServiceImpl(exchangeTokenServiceMock, userServiceMock);
}

@AfterEach
void verifyNotMoreInteractions(){
Mockito.verifyNoMoreInteractions(
exchangeTokenServiceMock
exchangeTokenServiceMock,
userServiceMock
);
}

Expand All @@ -51,4 +56,19 @@ void whenPostTokenThenCallExchangeService(){
// Then
Assertions.assertSame(expectedResult, result);
}

@Test
void whenGetUserInfoThenCallUserService(){
// Given
String accessToken = "accessToken";
UserInfo expectedResult = new UserInfo();
Mockito.when(userServiceMock.getUserInfo(accessToken))
.thenReturn(expectedResult);

// When
UserInfo result = service.getUserInfo(accessToken);

// Then
Assertions.assertSame(expectedResult, result);
}
}

0 comments on commit 122b7c5

Please sign in to comment.