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

feat: P4ADEV-1611 API postToken #24

Draft
wants to merge 4 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions helm/values-dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ microservice-chart:
envConfig:
ENV: "DEV"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
TOKEN_EXCHANGE_ISSUER: "https://dev.selfcare.pagopa.it"

keyvault:
name: "p4pa-d-payhub-kv"
Expand Down
1 change: 1 addition & 0 deletions helm/values-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ microservice-chart:
envConfig:
ENV: "PROD"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
TOKEN_EXCHANGE_ISSUER: "https://selfcare.pagopa.it"

keyvault:
name: "p4pa-p-payhub-kv"
Expand Down
1 change: 1 addition & 0 deletions helm/values-uat.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ microservice-chart:
envConfig:
ENV: "UAT"
JAVA_TOOL_OPTIONS: "-Xms128m -Xmx4g -Djava.util.concurrent.ForkJoinPool.common.parallelism=7 -javaagent:/app/applicationinsights-agent.jar -Dapplicationinsights.configuration.file=/mnt/file-config-external/appinsights-config/applicationinsights.json -agentlib:jdwp=transport=dt_socket,server=y,address=8001,suspend=n -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=3002 -Dcom.sun.management.jmxremote.rmi.port=3003 -Djava.rmi.server.hostname=127.0.0.1 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
TOKEN_EXCHANGE_ISSUER: "https://uat.selfcare.pagopa.it"

keyvault:
name: "p4pa-u-payhub-kv"
Expand Down
42 changes: 42 additions & 0 deletions openapi/p4pa-pu-bff.openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,35 @@ paths:
description: Forbidden - Access denied
'500':
description: Internal Server Error
/token:
post:
tags:
- Authentication
summary: "Generate an access token"
operationId: postToken
security: []
parameters:
- name: idToken
in: query
required: true
schema:
type: string
description: ID token from the client
responses:
'200':
description: Access Token generated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/AccessTokenDTO'
'400':
description: Invalid request
'401':
description: Authentication failed
'429':
description: Too many requests
'500':
description: Internal Server Error
/core-health:
get:
tags:
Expand Down Expand Up @@ -172,3 +201,16 @@ components:
description:
type: string
description: Description of the error
AccessTokenDTO:
type: object
properties:
accessToken:
type: string
description: The access token
tokenType:
type: string
example: Bearer
description: The type of the token
expiresIn:
type: integer
description: Time in seconds until the token expires
Original file line number Diff line number Diff line change
@@ -1,21 +1,36 @@
package it.gov.pagopa.pu.bff.connector.auth.client;

import it.gov.pagopa.pu.bff.connector.auth.config.AuthApisHolder;
import it.gov.pagopa.pu.p4paauth.dto.generated.AccessToken;
import it.gov.pagopa.pu.p4paauth.dto.generated.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;

@Service
@Slf4j
public class AuthnClient {

private final AuthApisHolder authApisHolder;
private final AuthApisHolder authApisHolder;

public AuthnClient(AuthApisHolder authApisHolder) {
this.authApisHolder = authApisHolder;
}
public AuthnClient(AuthApisHolder authApisHolder) {
this.authApisHolder = authApisHolder;
}

public UserInfo getUserInfo(String accessToken) {
return authApisHolder.getAuthnApi(accessToken)
.getUserInfo();
}

public AccessToken postToken(String clientId, String grantType, String scope, String subjectToken, String subjectIssuer, String subjectTokenType, String clientSecret) {
try {
return authApisHolder.getAuthnApi(null)
.postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret);

public UserInfo getUserInfo(String accessToken) {
return authApisHolder.getAuthnApi(accessToken)
.getUserInfo();
} catch (HttpClientErrorException e) {
log.error("Error during token exchange: {}", e.getStatusCode(), e);
throw e;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package it.gov.pagopa.pu.bff.controller;

import it.gov.pagopa.pu.bff.controller.generated.AuthenticationApi;
import it.gov.pagopa.pu.bff.dto.generated.AccessTokenDTO;
import it.gov.pagopa.pu.bff.service.AuthorizationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class AuthenticationController implements AuthenticationApi {

private final AuthorizationService authorizationService;

public AuthenticationController(AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}

@Override
public ResponseEntity<AccessTokenDTO> postToken(String idToken) {
log.info("User requested postToken()");

return new ResponseEntity<>(authorizationService.postToken(idToken), HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package it.gov.pagopa.pu.bff.mapper;

import it.gov.pagopa.pu.bff.dto.generated.AccessTokenDTO;
import it.gov.pagopa.pu.p4paauth.dto.generated.AccessToken;
import org.springframework.stereotype.Component;

@Component
public class AccessTokenDTOMapper {

public AccessTokenDTO toDTO(AccessToken accessToken) {
AccessTokenDTO dto = new AccessTokenDTO();
dto.setAccessToken(accessToken.getAccessToken());
dto.setExpiresIn(accessToken.getExpiresIn());
dto.setTokenType(accessToken.getTokenType());
return dto;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public OrganizationDTO mapToOrganizationDTO(Organization organization, List<Stri
.ipaCode(organization.getIpaCode())
.orgName(organization.getOrgName())
.operatorRole(operatorRole)
.orgLogo(organization.getOrgLogoDesc())
.orgLogo(organization.getOrgLogo())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
package it.gov.pagopa.pu.bff.service;

import it.gov.pagopa.pu.bff.connector.auth.client.AuthnClient;
import it.gov.pagopa.pu.bff.dto.generated.AccessTokenDTO;
import it.gov.pagopa.pu.bff.mapper.AccessTokenDTOMapper;
import it.gov.pagopa.pu.p4paauth.dto.generated.UserInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class AuthorizationService {

private final AccessTokenDTOMapper accessTokenDTOMapper;
private final AuthnClient authClientImpl;
private final String subjectIssuer;
public static final String CLIENT_ID = "piattaforma-unitaria";
public static final String GRANT_TYPE = "urn:ietf:params:oauth:grant-type:token-exchange";
public static final String SCOPE = "openid";
public static final String SUBJECT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt";

public AuthorizationService(AuthnClient authClientImpl) {
public AuthorizationService(@Value("${TOKEN_EXCHANGE_ISSUER}") String subjectIssuer,
AccessTokenDTOMapper accessTokenDTOMapper,
AuthnClient authClientImpl) {
this.subjectIssuer = subjectIssuer;
this.accessTokenDTOMapper = accessTokenDTOMapper;
this.authClientImpl = authClientImpl;
}

public UserInfo validateToken(String accessToken){
public UserInfo validateToken(String accessToken) {
log.info("Requesting validate token");
return authClientImpl.getUserInfo(accessToken);
}

public AccessTokenDTO postToken(String idToken) {
log.info("Posting token for validation");

return accessTokenDTOMapper.toDTO(
authClientImpl.postToken(
CLIENT_ID,
GRANT_TYPE,
SCOPE,
idToken,
subjectIssuer,
SUBJECT_TOKEN_TYPE,
null));
}

}
1 change: 1 addition & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,6 @@ rest:
read-millis: "\${DEFAULT_REST_READ_TIMEOUT_MILLIS:120000}"
auth:
base-url: "\${AUTH_SERVER_BASE_URL:}/payhub"
tokenExchange_issuer: "\${TOKEN_EXCHANGE_ISSUER:}"
organization:
base-url: "\${ORGANIZATION_BASE_URL:}"
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@

import it.gov.pagopa.pu.bff.connector.auth.config.AuthApisHolder;
import it.gov.pagopa.pu.p4paauth.controller.generated.AuthnApi;
import it.gov.pagopa.pu.p4paauth.dto.generated.AccessToken;
import it.gov.pagopa.pu.p4paauth.dto.generated.UserInfo;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.web.client.HttpClientErrorException;

import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class AuthnClientTest {
Expand All @@ -35,19 +40,69 @@ void verifyNoMoreInteractions() {

@Test
void whenGetUserInfoThenInvokeWithAccessToken() {
// Given
String accessToken = "ACCESSTOKEN";
UserInfo expectedResult = new UserInfo();

Mockito.when(authApisHolderMock.getAuthnApi(accessToken))
when(authApisHolderMock.getAuthnApi(accessToken))
.thenReturn(authnApiMock);
Mockito.when(authnApiMock.getUserInfo())
when(authnApiMock.getUserInfo())
.thenReturn(expectedResult);

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

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


@Test
void whenPostTokenThenInvokeAuthnApi() {
String clientId = "clientId";
String grantType = "grantType";
String scope = "scope";
String subjectToken = "subjectToken";
String subjectIssuer = "subjectIssuer";
String subjectTokenType = "subjectTokenType";
String clientSecret = "clientSecret";

AccessToken expectedToken = new AccessToken();
expectedToken.setAccessToken("mockAccessToken");
expectedToken.setTokenType("Bearer");
expectedToken.setExpiresIn(3600);

when(authApisHolderMock.getAuthnApi(null)).thenReturn(authnApiMock);
when(authnApiMock.postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret))
.thenReturn(expectedToken);

AccessToken result = authnClient.postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret);

assertSame(expectedToken, result);
verify(authApisHolderMock).getAuthnApi(null);
verify(authnApiMock).postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret);
}

@Test
void whenPostTokenThrowsHttpClientErrorExceptionThenLogAndRethrow() {
String clientId = "clientId";
String grantType = "grantType";
String scope = "scope";
String subjectToken = "subjectToken";
String subjectIssuer = "subjectIssuer";
String subjectTokenType = "subjectTokenType";
String clientSecret = "clientSecret";

HttpClientErrorException exception = mock(HttpClientErrorException.class);
when(authApisHolderMock.getAuthnApi(null)).thenReturn(authnApiMock);
when(authnApiMock.postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret))
.thenThrow(exception);

HttpClientErrorException thrown = assertThrows(
HttpClientErrorException.class,
() -> authnClient.postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret)
);

assertSame(exception, thrown);
verify(authApisHolderMock).getAuthnApi(null);
verify(authnApiMock).postToken(clientId, grantType, scope, subjectToken, subjectIssuer, subjectTokenType, clientSecret);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package it.gov.pagopa.pu.bff.controller;

import it.gov.pagopa.pu.bff.dto.generated.AccessTokenDTO;
import it.gov.pagopa.pu.bff.service.AuthorizationService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class AuthenticationControllerTest {

@Mock
private AuthorizationService authorizationService;

@InjectMocks
private AuthenticationController authenticationController;

private AccessTokenDTO accessTokenDTO;

@BeforeEach
void setUp() {
accessTokenDTO = new AccessTokenDTO();
accessTokenDTO.setAccessToken("fake-access-token");
accessTokenDTO.setExpiresIn(3600);
accessTokenDTO.setTokenType("bearer");
}

@Test
void testPostToken() {
String idToken = "validIdToken";

when(authorizationService.postToken(idToken)).thenReturn(accessTokenDTO);

ResponseEntity<AccessTokenDTO> response = authenticationController.postToken(idToken);

assertEquals(HttpStatus.OK, response.getStatusCode());
assertEquals("fake-access-token", response.getBody().getAccessToken());
assertEquals(3600, response.getBody().getExpiresIn());
assertEquals("bearer", response.getBody().getTokenType());

verify(authorizationService, times(1)).postToken(idToken);
}

}
Loading
Loading