From 8340e657a640aef8e741c88a30dd0d41d590622c Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Fri, 22 Nov 2024 15:25:00 +0100 Subject: [PATCH 1/7] Password grant type improvements. - User data has been moved to DB from blob storage. - Password is protected with Argon instead of sha-256. - First administrative operations for user management. --- .github/workflows/post-merge.yml | 2 + .../swclient/mil/auth/AuthErrorCode.java | 9 +- .../mil/auth/admin/AdminErrorCode.java | 9 +- .../admin/bean/AdminJsonPropertyName.java | 14 + .../auth/admin/bean/AdminQueryParamName.java | 7 + .../bean/CreateOrUpdateClientRequest.java | 2 +- .../auth/admin/bean/CreateUserRequest.java | 74 ++++ .../auth/admin/bean/CreateUserResponse.java | 38 ++ .../auth/admin/resource/ClientResource.java | 2 +- .../auth/admin/resource/RolesResource.java | 2 +- .../mil/auth/admin/resource/UserResource.java | 221 ++++++++++ .../swclient/mil/auth/dao/UserEntity.java | 71 +++- .../swclient/mil/auth/dao/UserRepository.java | 61 +-- .../auth/service/TokenByPasswordService.java | 104 ++--- .../mil/auth/util/PasswordVerifier.java | 54 --- .../swclient/mil/auth/util/SecretTriplet.java | 12 +- .../validation/constraints/Validator.java | 30 ++ src/main/resources/META-INF/openapi.yaml | 166 +++++++- .../resources/META-INF/openapi_not_admin.yaml | 11 +- .../env/cstar-d-mcshared/terraform.tfvars | 2 +- .../env/cstar-p-mcshared/terraform.tfvars | 2 +- .../env/cstar-u-mcshared/terraform.tfvars | 2 +- .../auth/admin/resource/UserResourceTest.java | 248 +++++++++++ .../mil/auth/dao/RolesRepositoryTest.java | 2 +- .../mil/auth/dao/UserRepositoryTest.java | 131 ++---- .../service/TokenByPasswordServiceTest.java | 276 ++---------- .../validation/constraints/ValidatorTest.java | 395 ++++++++++++++++++ 27 files changed, 1411 insertions(+), 536 deletions(-) create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserRequest.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserResponse.java create mode 100644 src/main/java/it/pagopa/swclient/mil/auth/admin/resource/UserResource.java delete mode 100644 src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/admin/resource/UserResourceTest.java diff --git a/.github/workflows/post-merge.yml b/.github/workflows/post-merge.yml index 0a4270c9..7eb9e257 100644 --- a/.github/workflows/post-merge.yml +++ b/.github/workflows/post-merge.yml @@ -75,6 +75,7 @@ jobs: if: steps.semantic.outputs.new_release_published == 'true' run: | yq -i ".info.version = \"${{ steps.semantic.outputs.new_release_version }}-RC\"" "src/main/resources/META-INF/openapi.yaml" + yq -i ".info.version = \"${{ steps.semantic.outputs.new_release_version }}-RC\"" "src/main/resources/META-INF/openapi_not_admin.yaml" ${{ runner.temp }}/maven/bin/mvn versions:set -DnewVersion=${{ steps.semantic.outputs.new_release_version }}-RC -s ${{ runner.temp }}/settings.xml --no-transfer-progress # @@ -142,6 +143,7 @@ jobs: run: | ${{ runner.temp }}/maven/bin/mvn versions:set -DnewVersion=${{ steps.semantic.outputs.new_release_version }} -s ${{ runner.temp }}/settings.xml --no-transfer-progress yq -i ".info.version = \"${{ steps.semantic.outputs.new_release_version }}\"" "src/main/resources/META-INF/openapi.yaml" + yq -i ".info.version = \"${{ steps.semantic.outputs.new_release_version }}\"" "src/main/resources/META-INF/openapi_not_admin.yaml" git config user.name "GitHub Workflow" git config user.email "<>" git add pom.xml diff --git a/src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java b/src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java index d9b98b53..81fc3f02 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/AuthErrorCode.java @@ -24,7 +24,7 @@ public final class AuthErrorCode { public static final String REFRESH_TOKEN_MUST_MATCH_REGEXP = MODULE_ID + "000008"; public static final String ERROR_SEARCHING_FOR_CLIENT = MODULE_ID + "000009"; public static final String CLIENT_NOT_FOUND = MODULE_ID + "00000A"; - public static final String ERROR_SEARCHING_FOR_CREDENTIALS = MODULE_ID + "00000B"; + public static final String ERROR_SEARCHING_FOR_USER = MODULE_ID + "00000B"; public static final String WRONG_CREDENTIALS = MODULE_ID + "00000C"; public static final String ERROR_VERIFING_CREDENTIALS = MODULE_ID + "00000D"; public static final String INCONSISTENT_CREDENTIALS = MODULE_ID + "00000E"; @@ -55,7 +55,7 @@ public final class AuthErrorCode { public static final String ERROR_ENCRYPTING_CLAIM = MODULE_ID + "000027"; public static final String ERROR_DECRYPTING_CLAIM = MODULE_ID + "000028"; public static final String ERROR_SIGNING_TOKEN = MODULE_ID + "000029"; - public static final String ERROR_SEARCHING_FOR_USER = MODULE_ID + "00002A"; + public static final String USER_NOT_FOUND = MODULE_ID + "00002A"; // @formatter:on public static final String MUST_NOT_BE_NULL_MSG = " must not be null"; @@ -64,7 +64,7 @@ public final class AuthErrorCode { // @formatter:off public static final String GRANT_TYPE_MUST_NOT_BE_NULL_MSG = "[" + GRANT_TYPE_MUST_NOT_BE_NULL + "] Grant type" + MUST_NOT_BE_NULL_MSG; public static final String GRANT_TYPE_MUST_MATCH_REGEXP_MSG = "[" + GRANT_TYPE_MUST_MATCH_REGEXP + "] Grant type" + MUST_MATCH_REGEXP_MSG; - public static final String USERNAME_MUST_MATCH_REGEXP_MSG = "[" + USERNAME_MUST_MATCH_REGEXP + "] User name" + MUST_MATCH_REGEXP_MSG; + public static final String USERNAME_MUST_MATCH_REGEXP_MSG = "[" + USERNAME_MUST_MATCH_REGEXP + "] Username" + MUST_MATCH_REGEXP_MSG; public static final String PASSWORD_MUST_MATCH_REGEXP_MSG = "[" + PASSWORD_MUST_MATCH_REGEXP + "] Password" + MUST_MATCH_REGEXP_MSG; public static final String REFRESH_TOKEN_MUST_MATCH_REGEXP_MSG = "[" + REFRESH_TOKEN_MUST_MATCH_REGEXP + "] Refresh token" + MUST_MATCH_REGEXP_MSG; public static final String EXT_TOKEN_MUST_MATCH_REGEXP_MSG = "[" + EXT_TOKEN_MUST_MATCH_REGEXP + "] Ext token" + MUST_MATCH_REGEXP_MSG; @@ -74,9 +74,10 @@ public final class AuthErrorCode { public static final String SCOPE_MUST_MATCH_REGEXP_MSG = "[" + SCOPE_MUST_MATCH_REGEXP + "] Scope" + MUST_MATCH_REGEXP_MSG; public static final String CLIENT_SECRET_MUST_MATCH_REGEXP_MSG = "[" + CLIENT_SECRET_MUST_MATCH_REGEXP + "] Client secret" + MUST_MATCH_REGEXP_MSG; public static final String FISCAL_CODE_MUST_MATCH_REGEXP_MSG = "[" + FISCAL_CODE_MUST_MATCH_REGEXP + "] Fiscal code" + MUST_MATCH_REGEXP_MSG; - public static final String INCONSISTENT_REQUEST_MSG = "[" + INCONSISTENT_REQUEST + "] Inconsistent request."; + public static final String INCONSISTENT_REQUEST_MSG = "[" + INCONSISTENT_REQUEST + "] Inconsistent request"; public static final String TOKEN_MUST_NOT_BE_NULL_MSG = "[" + TOKEN_MUST_NOT_BE_NULL + "] Token" + MUST_NOT_BE_NULL_MSG; public static final String TOKEN_MUST_MATCH_REGEXP_MSG = "[" + TOKEN_MUST_MATCH_REGEXP + "] Token" + MUST_MATCH_REGEXP_MSG; + public static final String USER_NOT_FOUND_MSG = "[" + USER_NOT_FOUND + "] User not found"; // @formatter:on /** diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/AdminErrorCode.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/AdminErrorCode.java index b1ea9eb1..06786657 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/admin/AdminErrorCode.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/AdminErrorCode.java @@ -42,6 +42,10 @@ public final class AdminErrorCode { public static final String ROLE_MUST_NOT_BE_NULL = AuthErrorCode.MODULE_ID + "100018"; public static final String ROLES_MUST_NOT_BE_NULL = AuthErrorCode.MODULE_ID + "100019"; public static final String SET_OF_ROLES_ID_MUST_MATCH_REGEXP = AuthErrorCode.MODULE_ID + "10001A"; + public static final String USERNAME_MUST_NOT_BE_NULL = AuthErrorCode.MODULE_ID + "10001B"; + public static final String DUPLICATE_USER = AuthErrorCode.MODULE_ID + "10001C"; + public static final String ERROR_STORING_USER = AuthErrorCode.MODULE_ID + "10001D"; + public static final String ERROR_DELETING_USER = AuthErrorCode.MODULE_ID + "10001E"; public static final String MUST_BE_BETWEEN_MIN_AND_MAX_MSG = " must be between {min} and {max}"; @@ -71,7 +75,10 @@ public final class AdminErrorCode { public static final String ROLE_MUST_MATCH_REGEXP_MSG = "[" + ROLE_MUST_MATCH_REGEXP + "] Role" + AuthErrorCode.MUST_MATCH_REGEXP_MSG; public static final String ROLES_MUST_NOT_BE_NULL_MSG = "[" + ROLES_MUST_NOT_BE_NULL + "] Roles" + AuthErrorCode.MUST_NOT_BE_NULL_MSG; public static final String SET_OF_ROLES_ID_MUST_MATCH_REGEXP_MSG = "[" + SET_OF_ROLES_ID_MUST_MATCH_REGEXP + "] Set of roles" + AuthErrorCode.MUST_MATCH_REGEXP_MSG; - + public static final String USERNAME_MUST_NOT_BE_NULL_MSG = "[" + USERNAME_MUST_NOT_BE_NULL + "] Username" + AuthErrorCode.MUST_NOT_BE_NULL_MSG; + public static final String DUPLICATE_USER_MSG = "[" + DUPLICATE_USER + "] Duplicate user"; + public static final String ERROR_STORING_USER_MSG = "[" + ERROR_STORING_USER + "] Error storing user"; + public static final String ERROR_DELETING_USER_MSG = "[" + ERROR_DELETING_USER + "] Error deleting user"; // @formatter:on /** diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminJsonPropertyName.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminJsonPropertyName.java index 830c31cb..86dea763 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminJsonPropertyName.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminJsonPropertyName.java @@ -146,6 +146,20 @@ public class AdminJsonPropertyName { */ public static final String SET_OF_ROLES_ID = "id"; + /** + *

+ * Username. + *

+ */ + public static final String USERNAME = "username"; + + /** + *

+ * Password. + *

+ */ + public static final String PASSWORD = "password"; + /** *

* This class contains constants only. diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminQueryParamName.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminQueryParamName.java index 76a6a4b1..3ec0d361 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminQueryParamName.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/AdminQueryParamName.java @@ -62,6 +62,13 @@ public class AdminQueryParamName { */ public static final String CLIENT_ID = "clientId"; + /** + *

+ * User name. + *

+ */ + public static final String USERNAME = "username"; + /** *

* This class contains only constants. diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateOrUpdateClientRequest.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateOrUpdateClientRequest.java index 7e1d7a24..e374199b 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateOrUpdateClientRequest.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateOrUpdateClientRequest.java @@ -46,7 +46,7 @@ public class CreateOrUpdateClientRequest { *

*/ @JsonProperty(value = AdminJsonPropertyName.DESCRIPTION) - @NotNull(message = AdminErrorCode.DESCRIPTION_MUST_MATCH_REGEXP_MSG) + @NotNull(message = AdminErrorCode.DESCRIPTION_MUST_NOT_BE_NULL_MSG) @Pattern(regexp = AdminValidationPattern.DESCRIPTION, message = AdminErrorCode.DESCRIPTION_MUST_MATCH_REGEXP_MSG) private String description; diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserRequest.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserRequest.java new file mode 100644 index 00000000..bb61d0dc --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserRequest.java @@ -0,0 +1,74 @@ +/* + * CreateUserRequest.java + * + * 20 nov 2024 + */ +package it.pagopa.swclient.mil.auth.admin.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import it.pagopa.swclient.mil.ErrorCode; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.admin.AdminErrorCode; +import it.pagopa.swclient.mil.auth.bean.AuthValidationPattern; +import it.pagopa.swclient.mil.bean.ValidationPattern; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * + * @author Antonio Tarricone + */ +@RegisterForReflection +@JsonInclude(Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +@Data +@Accessors(chain = true) +public class CreateUserRequest { + /** + *

+ * User name. + *

+ */ + @JsonProperty(value = AdminJsonPropertyName.USERNAME) + @NotNull(message = AdminErrorCode.USERNAME_MUST_NOT_BE_NULL_MSG) + @Pattern(regexp = AuthValidationPattern.USERNAME, message = AuthErrorCode.USERNAME_MUST_MATCH_REGEXP_MSG) + @ToString.Exclude + private String username; + + /** + *

+ * Channel. + *

+ */ + @JsonProperty(value = AdminJsonPropertyName.CHANNEL) + @Pattern(regexp = ValidationPattern.CHANNEL, message = ErrorCode.CHANNEL_MUST_MATCH_REGEXP_MSG) + private String channel; + + /** + *

+ * Acquirer ID. + *

+ */ + @JsonProperty(value = AdminJsonPropertyName.ACQUIRER_ID) + @Pattern(regexp = ValidationPattern.ACQUIRER_ID, message = ErrorCode.ACQUIRER_ID_MUST_MATCH_REGEXP_MSG) + private String acquirerId; + + /** + *

+ * Merchant ID. + *

+ */ + @JsonProperty(value = AdminJsonPropertyName.MERCHANT_ID) + @Pattern(regexp = ValidationPattern.MERCHANT_ID, message = ErrorCode.MERCHANT_ID_MUST_MATCH_REGEXP_MSG) + private String merchantId; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserResponse.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserResponse.java new file mode 100644 index 00000000..36ca909f --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/bean/CreateUserResponse.java @@ -0,0 +1,38 @@ +/* + * CreateUserResponse.java + * + * 21 nov 2024 + */ +package it.pagopa.swclient.mil.auth.admin.bean; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import com.fasterxml.jackson.annotation.JsonProperty; + +import io.quarkus.runtime.annotations.RegisterForReflection; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * + * @author Antonio Tarricone + */ +@RegisterForReflection +@JsonInclude(Include.NON_NULL) +@NoArgsConstructor +@AllArgsConstructor +@Data +@Accessors(chain = true) +public class CreateUserResponse { + /** + *

+ * Password. + *

+ */ + @JsonProperty(AdminJsonPropertyName.PASSWORD) + @ToString.Exclude + private String password; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/ClientResource.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/ClientResource.java index fc28dfdf..b8a1346b 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/ClientResource.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/ClientResource.java @@ -65,7 +65,7 @@ public class ClientResource { */ @ConfigProperty(name = "base-url", defaultValue = "") String baseUrl; - + /* * */ diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/RolesResource.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/RolesResource.java index 2676005f..826714fb 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/RolesResource.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/RolesResource.java @@ -66,7 +66,7 @@ public class RolesResource { */ @ConfigProperty(name = "base-url", defaultValue = "") String baseUrl; - + /** *

* Repository of roles entities. diff --git a/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/UserResource.java b/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/UserResource.java new file mode 100644 index 00000000..39d3de10 --- /dev/null +++ b/src/main/java/it/pagopa/swclient/mil/auth/admin/resource/UserResource.java @@ -0,0 +1,221 @@ +/* + * UserResource.java + * + * 20 nov 2024 + */ +package it.pagopa.swclient.mil.auth.admin.resource; + +import java.net.URI; +import java.util.UUID; + +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import com.mongodb.MongoWriteException; + +import io.quarkus.logging.Log; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.AuthErrorCode; +import it.pagopa.swclient.mil.auth.admin.AdminErrorCode; +import it.pagopa.swclient.mil.auth.admin.bean.AdminQueryParamName; +import it.pagopa.swclient.mil.auth.admin.bean.CreateUserRequest; +import it.pagopa.swclient.mil.auth.admin.bean.CreateUserResponse; +import it.pagopa.swclient.mil.auth.bean.AuthValidationPattern; +import it.pagopa.swclient.mil.auth.dao.UserEntity; +import it.pagopa.swclient.mil.auth.dao.UserRepository; +import it.pagopa.swclient.mil.auth.util.SecretTriplet; +import it.pagopa.swclient.mil.bean.Errors; +import jakarta.annotation.security.RolesAllowed; +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Pattern; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DELETE; +import jakarta.ws.rs.InternalServerErrorException; +import jakarta.ws.rs.NotFoundException; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; + +/** + * + * @author antonio.tarricone + */ +@Path("/admin/users") +@Produces(MediaType.APPLICATION_JSON) +@RolesAllowed({ + "mil-auth-admin" +}) +public class UserResource { + /* + * Lenght of generated password. + */ + private static final int PASSWORD_LEN = 12; + + /* + * mil-auth base URL. + */ + @ConfigProperty(name = "base-url", defaultValue = "") + String baseUrl; + + /* + * + */ + private UserRepository repository; + + /** + * + * @param repository + */ + @Inject + UserResource(UserRepository repository) { + this.repository = repository; + } + + /* + * >>>>>>>>>>>>>>>> CREATE <<<<<<<<<<<<<<<< + */ + + /** + * + * @param t + * @return + */ + private WebApplicationException onPersistError(Throwable t) { + if (t instanceof MongoWriteException m) { + if (m.getCode() == 11000) { + /* + * Duplicate key + */ + Log.warnf(m, AdminErrorCode.DUPLICATE_USER_MSG); + return new WebApplicationException( + Response + .status(Status.CONFLICT) + .entity(new Errors(AdminErrorCode.DUPLICATE_USER, AdminErrorCode.DUPLICATE_USER_MSG)) + .build()); + } else { + /* + * Other error + */ + Log.errorf(m, AdminErrorCode.ERROR_STORING_USER_MSG); + return new InternalServerErrorException( + Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(new Errors(AdminErrorCode.ERROR_STORING_USER, AdminErrorCode.ERROR_STORING_USER_MSG)) + .build()); + } + } else { + /* + * Unexpected error + */ + Log.errorf(t, AdminErrorCode.ERROR_CREATING_CLIENT_MSG); + return new InternalServerErrorException( + Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(new Errors(AdminErrorCode.ERROR_CREATING_CLIENT, AdminErrorCode.ERROR_CREATING_CLIENT_MSG)) + .build()); + } + } + + /** + * + * @param req + * @return + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + public Uni create(@Valid CreateUserRequest req) { + Log.tracef("Create a new user: %s", req.toString()); + + /* + * Generate random user id. + */ + Log.tracef("Generate random user id"); + String userId = UUID.randomUUID().toString(); + + /* + * Generate secret triplet (password, salt and hash). + */ + Log.tracef("Generate password, salt and hash"); + SecretTriplet triplet = SecretTriplet.generate(PASSWORD_LEN); + + /* + * Store client in the DB. + */ + Log.tracef("Store new user in the DB"); + UserEntity entity = new UserEntity( + userId, + req.getUsername(), + req.getChannel(), + triplet.getSalt(), + triplet.getHash(), + req.getAcquirerId(), + req.getMerchantId()); + + return repository + .persist(entity) + .map(e -> { + CreateUserResponse res = new CreateUserResponse(triplet.getSecret()); + Log.debugf("User created successfully: %s", res.toString()); + return Response.created(URI.create(baseUrl + "/admin/users/" + userId)) + .entity(res) + .build(); + }) + .onFailure().transform(this::onPersistError); + } + + /* + * >>>>>>>>>>>>>>>> DELETE <<<<<<<<<<<<<<<< + */ + + /** + * + * @param t + * @return + */ + private WebApplicationException onDeleteError(Throwable t) { + Log.errorf(t, AdminErrorCode.ERROR_DELETING_USER_MSG); + return new InternalServerErrorException(Response + .status(Status.INTERNAL_SERVER_ERROR) + .entity(new Errors(AdminErrorCode.ERROR_DELETING_USER, AdminErrorCode.ERROR_DELETING_USER_MSG)) + .build()); + } + + /** + * + * @return + */ + private WebApplicationException onNotFoundError() { + Log.error(AuthErrorCode.USER_NOT_FOUND_MSG); + return new NotFoundException(Response + .status(Status.NOT_FOUND) + .entity(new Errors(AuthErrorCode.USER_NOT_FOUND, AuthErrorCode.USER_NOT_FOUND_MSG)) + .build()); + } + + /** + * + * @param username + * @return + */ + @DELETE + public Uni delete( + @QueryParam(AdminQueryParamName.USERNAME) + @Pattern(regexp = AuthValidationPattern.USERNAME, message = AuthErrorCode.USERNAME_MUST_MATCH_REGEXP_MSG) String username) { + Log.trace("Delete user"); + return repository.deleteByUsername(username) + .onFailure().transform(this::onDeleteError) + .map(n -> { + if (n > 0) { + Log.debug("User deleted successfully"); + return Response.noContent().build(); + } else { + throw onNotFoundError(); + } + }); + } +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/dao/UserEntity.java b/src/main/java/it/pagopa/swclient/mil/auth/dao/UserEntity.java index 2ad4f7f1..1e040e70 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/dao/UserEntity.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/dao/UserEntity.java @@ -5,7 +5,11 @@ */ package it.pagopa.swclient.mil.auth.dao; -import io.quarkus.runtime.annotations.RegisterForReflection; +import org.bson.codecs.pojo.annotations.BsonId; +import org.bson.codecs.pojo.annotations.BsonProperty; +import org.bson.types.ObjectId; + +import io.quarkus.mongodb.panache.common.MongoEntity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -15,39 +19,88 @@ * * @author Antonio Tarricone */ -@RegisterForReflection @NoArgsConstructor @AllArgsConstructor @Data @Accessors(chain = true) +@MongoEntity(database = "mil", collection = "users") public class UserEntity { /* - * + * Properties name. */ - private String username; + public static final String USER_ID_PRP = "userId"; + public static final String USERNAME_PRP = "username"; + public static final String CHANNEL_PRP = "channel"; + public static final String SALT_PRP = "salt"; + public static final String PASSWORD_HASH_PRP = "passwordHash"; + public static final String ACQUIRER_ID_PRP = "acquirerId"; + public static final String MERCHANT_ID_PRP = "merchantId"; + + /* + * Used by MongoDB for the _id field. + */ + @BsonId + public ObjectId id; /* * */ - private String salt; + @BsonProperty(value = USER_ID_PRP) + public String userId; /* * */ - private String passwordHash; + @BsonProperty(value = USERNAME_PRP) + public String username; /* * */ - private String acquirerId; + @BsonProperty(value = CHANNEL_PRP) + public String channel; + + /* + * Base64 string. + */ + @BsonProperty(value = SALT_PRP) + public String salt; + + /* + * Base64 string. + */ + @BsonProperty(value = PASSWORD_HASH_PRP) + public String passwordHash; /* * */ - private String channel; + @BsonProperty(value = ACQUIRER_ID_PRP) + public String acquirerId; /* * */ - private String merchantId; + @BsonProperty(value = MERCHANT_ID_PRP) + public String merchantId; + + /** + * + * @param userId + * @param username + * @param channel + * @param salt + * @param passwordHash + * @param acquirerId + * @param merchantId + */ + public UserEntity(String userId, String username, String channel, String salt, String passwordHash, String acquirerId, String merchantId) { + this.userId = userId; + this.username = username; + this.channel = channel; + this.salt = salt; + this.passwordHash = passwordHash; + this.acquirerId = acquirerId; + this.merchantId = merchantId; + } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/dao/UserRepository.java b/src/main/java/it/pagopa/swclient/mil/auth/dao/UserRepository.java index 8b1ff018..908ff73e 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/dao/UserRepository.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/dao/UserRepository.java @@ -1,76 +1,39 @@ /* - * AuthDataRepository.java + * UserRepository.java * * 23 ott 2023 */ package it.pagopa.swclient.mil.auth.dao; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.Optional; -import io.quarkus.cache.CacheResult; -import io.quarkus.logging.Log; +import io.quarkus.mongodb.panache.reactive.ReactivePanacheMongoRepository; import io.smallrye.mutiny.Uni; -import it.pagopa.swclient.mil.auth.AuthErrorCode; -import it.pagopa.swclient.mil.auth.util.AuthError; -import it.pagopa.swclient.mil.azureservices.storageblob.service.AzureStorageBlobReactiveService; +import it.pagopa.swclient.mil.observability.TraceReactivePanacheMongoRepository; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.ws.rs.core.Response; /** * * @author Antonio Tarricone */ +@TraceReactivePanacheMongoRepository @ApplicationScoped -public class UserRepository { - /* - * - */ - private AzureStorageBlobReactiveService blobService; - - /* - * - */ - private ObjectMapper json2obj; - - /** - * - * @param blobService - */ - UserRepository(AzureStorageBlobReactiveService blobService) { - this.blobService = blobService; - json2obj = new ObjectMapper(); - } - +public class UserRepository implements ReactivePanacheMongoRepository { /** * - * @param r + * @param username * @return */ - private UserEntity response2User(Response r) { - if (r.getStatus() == 200) { - try { - return json2obj.readValue(r.readEntity(String.class), UserEntity.class); - } catch (JsonProcessingException e) { - String message = String.format("[%s] Error deserializing user data", AuthErrorCode.ERROR_SEARCHING_FOR_USER); - Log.error(message); - throw new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_USER, message); - } - } else { - String message = String.format("[%s] Error searching for user: %d", AuthErrorCode.ERROR_SEARCHING_FOR_USER, r.getStatus()); - Log.error(message); - throw new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_USER, message); - } + public Uni> findByUsername(String username) { + return find(UserEntity.USERNAME_PRP, username).firstResultOptional(); } /** * - * @param userHash + * @param username * @return */ - @CacheResult(cacheName = "client-role") - public Uni getUser(String userHash) { - return blobService.getBlob("users", userHash + ".json") - .map(this::response2User); + public Uni deleteByUsername(String username) { + return delete(UserEntity.USERNAME_PRP, username); } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java index a2f86441..5d1e027d 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordService.java @@ -5,12 +5,9 @@ */ package it.pagopa.swclient.mil.auth.service; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; import java.util.Objects; +import io.quarkus.cache.CacheResult; import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; import it.pagopa.swclient.mil.auth.AuthErrorCode; @@ -21,12 +18,9 @@ import it.pagopa.swclient.mil.auth.qualifier.Password; import it.pagopa.swclient.mil.auth.util.AuthError; import it.pagopa.swclient.mil.auth.util.AuthException; -import it.pagopa.swclient.mil.auth.util.PasswordVerifier; -import it.pagopa.swclient.mil.auth.util.UniGenerator; +import it.pagopa.swclient.mil.auth.util.SecretTriplet; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import jakarta.ws.rs.WebApplicationException; -import jakarta.ws.rs.core.Response; /** * @author Antonio Tarricone @@ -34,11 +28,6 @@ @ApplicationScoped @Password public class TokenByPasswordService extends TokenService { - /* - * - */ - private static final String ERROR_SEARCHING_FOR_CREDENTIALS_MSG = "[%s] Error searching for the credentials"; - /* * */ @@ -71,39 +60,22 @@ public class TokenByPasswordService extends TokenService { * @param getAccessToken * @return */ - private Uni findCredentials(GetAccessTokenRequest getAccessToken) { - Log.trace("Search for the credentials"); - - String userHash; - try { - userHash = Base64.getUrlEncoder().encodeToString( - MessageDigest.getInstance("SHA256").digest( - getAccessToken.getUsername().getBytes(StandardCharsets.UTF_8))); - } catch (NoSuchAlgorithmException e) { - String message = String.format(ERROR_SEARCHING_FOR_CREDENTIALS_MSG, AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS); - Log.errorf(e, message); - return UniGenerator.error(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS, message); - } - - return repository.getUser(userHash) + @CacheResult(cacheName = "client-role") + public Uni findUser(GetAccessTokenRequest getAccessToken) { + String username = getAccessToken.getUsername(); + Log.tracef("Search for the user %s", username); + return repository.findByUsername(username) .onFailure().transform(t -> { - if (t instanceof WebApplicationException e) { - Response r = e.getResponse(); - // r cannot be null - if (r.getStatus() == 404) { - Log.warnf("[%s] Credentials not found", AuthErrorCode.WRONG_CREDENTIALS); - return new AuthException(AuthErrorCode.WRONG_CREDENTIALS, String.format("[%s] Wrong credentials", AuthErrorCode.WRONG_CREDENTIALS)); // It's better not to give details... - } else { - String message = String.format(ERROR_SEARCHING_FOR_CREDENTIALS_MSG, AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS); - Log.errorf(t, message); - return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS, message); - } - } else { - String message = String.format(ERROR_SEARCHING_FOR_CREDENTIALS_MSG, AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS); - Log.errorf(t, message); - return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_CREDENTIALS, message); - } - }); + String message = String.format("[%s] Error searching for user %s", AuthErrorCode.ERROR_SEARCHING_FOR_USER, username); + Log.errorf(t, message); + return new AuthError(AuthErrorCode.ERROR_SEARCHING_FOR_USER, message); + }) + .map(opt -> opt.orElseThrow(() -> { + String message = String.format("[%s] User %s not found", AuthErrorCode.USER_NOT_FOUND, username); + Log.warn(message); + throw new AuthException(AuthErrorCode.USER_NOT_FOUND, message); + })) + .invoke(entity -> Log.debugf("User found: %s", entity)); } /** @@ -112,28 +84,28 @@ private Uni findCredentials(GetAccessTokenRequest getAccessToken) { * If the verification succeeds, the method returns ResourceOwnerCredentialsEntity, otherwise it * returns a failure with specific error code. * - * @param credentialsEntity + * @param userEntity * @param getAccessToken * @return */ - private UserEntity verifyConsistency(UserEntity credentialsEntity, GetAccessTokenRequest getAccessToken) { + private UserEntity verifyConsistency(UserEntity userEntity, GetAccessTokenRequest getAccessToken) { Log.trace("Acquirer/channel/merchant consistency verification"); - String foundAcquirerId = credentialsEntity.getAcquirerId(); - String foundChannel = credentialsEntity.getChannel(); - String foundMerchantId = credentialsEntity.getMerchantId(); + String foundAcquirerId = userEntity.getAcquirerId(); + String foundChannel = userEntity.getChannel(); + String foundMerchantId = userEntity.getMerchantId(); String expectedAcquirerId = getAccessToken.getAcquirerId(); String expectedChannel = getAccessToken.getChannel(); String expectedMerchantId = getAccessToken.getMerchantId(); - boolean consistency = foundAcquirerId.equals(expectedAcquirerId) - && foundChannel.equals(expectedChannel) + boolean consistency = Objects.equals(foundAcquirerId, expectedAcquirerId) + && Objects.equals(foundChannel, expectedChannel) && Objects.equals(foundMerchantId, expectedMerchantId); if (consistency) { Log.debug("Acquirer/channel/merchant consistency has been successufully verified"); - return credentialsEntity; + return userEntity; } else { Log.warnf("[%s] Acquirer/channel/merchant isn't consistent. Expected %s/%s/%s, found %s/%s/%s", AuthErrorCode.INCONSISTENT_CREDENTIALS, expectedAcquirerId, expectedChannel, expectedMerchantId, foundAcquirerId, foundChannel, foundMerchantId); throw new AuthException(AuthErrorCode.INCONSISTENT_CREDENTIALS, String.format("[%s] Inconsistent credentials", AuthErrorCode.INCONSISTENT_CREDENTIALS)); // It's better not to give details... @@ -146,25 +118,19 @@ private UserEntity verifyConsistency(UserEntity credentialsEntity, GetAccessToke * If the verification succeeds, the method returns void, otherwise it returns a failure with * specific error code. * - * @param credentialsEntity + * @param userEntity * @param getAccessToken * @return */ - private Void verifyPassword(UserEntity credentialsEntity, GetAccessTokenRequest getAccessToken) { + private Void verifyPassword(UserEntity userEntity, GetAccessTokenRequest getAccessToken) { Log.trace("Password verification"); - try { - if (PasswordVerifier.verify(getAccessToken.getPassword(), credentialsEntity.getSalt(), credentialsEntity.getPasswordHash())) { - Log.debug("Password has been successfully verified"); - return null; - } else { - String message = String.format("[%s] Wrong credentials", AuthErrorCode.WRONG_CREDENTIALS); - Log.warn(message); - throw new AuthException(AuthErrorCode.WRONG_CREDENTIALS, message); - } - } catch (NoSuchAlgorithmException e) { - String message = String.format("[%s] Error verifing credentials", AuthErrorCode.ERROR_VERIFING_CREDENTIALS); - Log.errorf(e, message); - throw new AuthError(AuthErrorCode.ERROR_VERIFING_CREDENTIALS, message); + if (new SecretTriplet(getAccessToken.getPassword(), userEntity.getSalt(), userEntity.getPasswordHash()).verify()) { + Log.debug("Password has been successfully verified"); + return null; + } else { + String message = String.format("[%s] Wrong credentials", AuthErrorCode.WRONG_CREDENTIALS); + Log.warn(message); + throw new AuthException(AuthErrorCode.WRONG_CREDENTIALS, message); } } @@ -176,7 +142,7 @@ private Void verifyPassword(UserEntity credentialsEntity, GetAccessTokenRequest * @param getAccessToken */ private Uni verifyCredentials(GetAccessTokenRequest getAccessToken) { - return findCredentials(getAccessToken) + return findUser(getAccessToken) .map(c -> verifyConsistency(c, getAccessToken)) .map(c -> verifyPassword(c, getAccessToken)); } diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java b/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java deleted file mode 100644 index 264fd891..00000000 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/PasswordVerifier.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * PasswordVerifier.java - * - * 20 mar 2023 - */ -package it.pagopa.swclient.mil.auth.util; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.Base64; - -/** - * @author Antonio Tarricone - */ -public class PasswordVerifier { - /** - * - */ - private PasswordVerifier() { - } - - /** - * @param password - * @param salt - * @param hash - * @return - * @throws NoSuchAlgorithmException - */ - public static boolean verify(String password, String salt, String hash) throws NoSuchAlgorithmException { - byte[] hashBytes = Base64.getDecoder().decode(hash); - byte[] calcHashBytes = hashBytes(password, salt); - return Arrays.equals(calcHashBytes, hashBytes); - } - - /** - * @param password - * @param salt - * @return - * @throws NoSuchAlgorithmException - */ - public static byte[] hashBytes(String password, String salt) throws NoSuchAlgorithmException { - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); - - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); - - MessageDigest digest = MessageDigest.getInstance("SHA256"); - return digest.digest(data); - } -} \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/util/SecretTriplet.java b/src/main/java/it/pagopa/swclient/mil/auth/util/SecretTriplet.java index a0acddde..581eeb98 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/util/SecretTriplet.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/util/SecretTriplet.java @@ -104,11 +104,11 @@ private static SecureRandom getSecureRandom() { * * @return */ - public static SecretTriplet generate() { + public static SecretTriplet generate(int secretLen) { /* * Generate random client secret. */ - String secretStr = getSecureRandom().ints(SECRET_LEN, 0, SYMBOLS.length()) // 36 random integers included between [0, 63[ + String secretStr = getSecureRandom().ints(secretLen, 0, SYMBOLS.length()) // 36 random integers included between [0, 63[ .map(SYMBOLS::charAt) .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) .toString(); @@ -148,4 +148,12 @@ public static SecretTriplet generate() { Base64.getEncoder().encodeToString(saltBytes), Base64.getEncoder().encodeToString(hash)); } + + /** + * + * @return + */ + public static SecretTriplet generate() { + return generate(SECRET_LEN); + } } \ No newline at end of file diff --git a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java index bc191e4f..7a27e7f9 100644 --- a/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java +++ b/src/main/java/it/pagopa/swclient/mil/auth/validation/constraints/Validator.java @@ -40,6 +40,21 @@ && usernameMustNotBeNull(getAccessToken) } }); + VALIDATORS.put(GrantType.PASSWORD + "/null", new Verifier() { + @Override + public boolean test(GetAccessTokenRequest getAccessToken) { + return acquirerIdMustBeNull(getAccessToken) + && merchantIdMustBeNull(getAccessToken) + && terminalIdMustBeNull(getAccessToken) + && clientSecretMustBeNull(getAccessToken) + && extTokenMustBeNull(getAccessToken) + && addDataMustBeNull(getAccessToken) + && refreshTokenMustBeNull(getAccessToken) + && usernameMustNotBeNull(getAccessToken) + && passwordMustNotBeNull(getAccessToken); + } + }); + VALIDATORS.put(GrantType.REFRESH_TOKEN + "/" + Channel.POS, new Verifier() { @Override public boolean test(GetAccessTokenRequest getAccessToken) { @@ -54,7 +69,22 @@ && usernameMustBeNull(getAccessToken) && passwordMustBeNull(getAccessToken) && scopeMustBeNull(getAccessToken); } + }); + VALIDATORS.put(GrantType.REFRESH_TOKEN + "/null", new Verifier() { + @Override + public boolean test(GetAccessTokenRequest getAccessToken) { + return acquirerIdMustBeNull(getAccessToken) + && merchantIdMustBeNull(getAccessToken) + && terminalIdMustBeNull(getAccessToken) + && clientSecretMustBeNull(getAccessToken) + && extTokenMustBeNull(getAccessToken) + && addDataMustBeNull(getAccessToken) + && refreshTokenMustNotBeNull(getAccessToken) + && usernameMustBeNull(getAccessToken) + && passwordMustBeNull(getAccessToken) + && scopeMustBeNull(getAccessToken); + } }); VALIDATORS.put(GrantType.POYNT_TOKEN + "/" + Channel.POS, new Verifier() { diff --git a/src/main/resources/META-INF/openapi.yaml b/src/main/resources/META-INF/openapi.yaml index ef5de314..3b7b5312 100644 --- a/src/main/resources/META-INF/openapi.yaml +++ b/src/main/resources/META-INF/openapi.yaml @@ -4,15 +4,18 @@ info: version: 2.14.0 description: Authorization Microservice for Multi-channel Integration Layer of SW Client Project contact: - name: Antonio Tarricone - email: antonio.tarricone@pagopa.it + name: CSTAR + email: cstar@pagopa.it servers: - description: Development/Test - url: https://mil-d-apim.azure-api.net/mil-auth + url: https://api-mcshared.dev.cstar.pagopa.it/auth x-internal: true - description: User Acceptance Test - url: https://mil-u-apim.azure-api.net/mil-auth + url: https://api-mcshared.uat.cstar.pagopa.it/auth x-internal: true + - description: Production + url: https://api-mcshared.cstar.pagopa.it/auth + x-internal: false tags: - name: wellknown description: Well-known operation @@ -26,6 +29,8 @@ tags: description: Administrative operation for set of roles - name: maintenance description: Maintenance operation + - name: user + description: Administrative operation for user paths: /token: post: @@ -614,6 +619,85 @@ paths: default: #description: Unexpected error $ref: '#/components/responses/Error' + /admin/users: + post: + operationId: createUser + description: Creates a new user + tags: [user] + security: + - oAuth2: [admin] + parameters: + - $ref: '#/components/parameters/RequestId' + - $ref: '#/components/parameters/Version' + requestBody: + $ref: '#/components/requestBodies/CreateUser' + responses: + "201": + #description: Created + $ref: '#/components/responses/CreateUser' + "400": + #description: Bad request + $ref: '#/components/responses/Error' + "401": + #description: Wrong credentials + $ref: '#/components/responses/Error' + "403": + #description: Forbidden + $ref: '#/components/responses/Error' + "406": + #description: Not acceptable. Did you require application/json? + $ref: '#/components/responses/Error' + "415": + #description: Unsupported media type. Did you provide application/json? + $ref: '#/components/responses/Error' + "429": + #description: Too many request + $ref: '#/components/responses/Error' + "500": + #description: Server error + $ref: '#/components/responses/Error' + default: + #description: Unexpected error + $ref: '#/components/responses/Error' + delete: + operationId: deleteUserByUserName + description: Deletes a user by user name + tags: [user] + security: + - oAuth2: [admin] + parameters: + - $ref: '#/components/parameters/RequestId' + - $ref: '#/components/parameters/Version' + - $ref: '#/components/parameters/Username' + responses: + "204": + #description: No content + $ref: '#/components/responses/NoContent' + "400": + #description: Bad request + $ref: '#/components/responses/Error' + "401": + #description: Access token is missing or invalid + $ref: '#/components/responses/Error' + "403": + #description: Forbidden + $ref: '#/components/responses/Error' + "404": + #description: Not found + $ref: '#/components/responses/Error' + "406": + #description: Not acceptable. Did you require application/json? + $ref: '#/components/responses/Error' + "429": + #description: Too many request + $ref: '#/components/responses/Error' + "500": + #description: Server error + $ref: '#/components/responses/Error' + default: + #description: Unexpected error + $ref: '#/components/responses/Error' + components: # ======================================================== # Schemas @@ -945,6 +1029,14 @@ components: minimum: 0 maximum: 9223372036854775807 example: 20 + UserLocation: + description: URL of the user + type: string + format: uri + pattern: "^[ -~]{1,2048}$" + minLength: 1 + maxLength: 2048 + example: "https://mil-d-apim.azure-api.net/mil-auth/admin/users/9c2ef3bb-bca5-4d27-b9a0-0b055cf0540e" Username: description: User name type: string @@ -1085,6 +1177,32 @@ components: terminalId: "NA" roles: - "mil-auth-admin" + CreateUserReq: + description: Request to create an user + additionalProperties: false + properties: + username: + $ref: '#/components/schemas/Username' + channel: + $ref: '#/components/schemas/Channel' + acquirerId: + $ref: '#/components/schemas/AcquirerId' + merchantId: + $ref: '#/components/schemas/MerchantId' + required: + - username + example: + username: "mario.rossi@pagopa.it" + CreateUserRes: + description: Response to a request to create a new user + additionalProperties: false + properties: + password: + $ref: '#/components/schemas/Password' + required: + - password + example: + password: "Ar57f-7G-gz8T" DeletedKeys: description: List of IDs of deleted keys additionalProperties: false @@ -1456,6 +1574,12 @@ components: application/json: schema: $ref: '#/components/schemas/CreateOrUpdateSetOfRolesReq' + CreateUser: + description: Request to create an user + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserReq' GetAccessToken: description: Request to get an access token by means of username and password or by refresh token or by external token content: @@ -1581,6 +1705,13 @@ components: required: false schema: $ref: '#/components/schemas/TerminalIdOrNa' + Username: + name: username + in: query + description: Username + required: true + schema: + $ref: '#/components/schemas/Username' Version: name: Version in: header @@ -1690,6 +1821,33 @@ components: required: true schema: $ref: '#/components/schemas/SetOfRolesLocation' + CreateUser: + description: Response returned when the creation of a new user is requested + headers: + Access-Control-Allow-Origin: + description: Indicates whether the response can be shared with requesting code from the given origin + required: false + schema: + $ref: '#/components/schemas/AccessControlAllowOrigin' + RateLimit-Limit: + description: The number of allowed requests in the current period + required: false + schema: + $ref: '#/components/schemas/RateLimitLimit' + RateLimit-Reset: + description: The number of seconds left in the current period + required: false + schema: + $ref: '#/components/schemas/RateLimitReset' + Location: + description: URL of the user + required: true + schema: + $ref: '#/components/schemas/UserLocation' + content: + application/json: + schema: + $ref: '#/components/schemas/CreateUserRes' DeletedKeys: description: Response returned when cleaning of expired keys is requested headers: diff --git a/src/main/resources/META-INF/openapi_not_admin.yaml b/src/main/resources/META-INF/openapi_not_admin.yaml index 40c21b46..f4e04c7a 100644 --- a/src/main/resources/META-INF/openapi_not_admin.yaml +++ b/src/main/resources/META-INF/openapi_not_admin.yaml @@ -4,15 +4,18 @@ info: version: 2.13.0 description: Authorization Microservice for Multi-channel Integration Layer of SW Client Project contact: - name: Antonio Tarricone - email: antonio.tarricone@pagopa.it + name: CSTAR + email: cstar@pagopa.it servers: - description: Development/Test - url: https://mil-d-apim.azure-api.net/mil-auth + url: https://api-mcshared.dev.cstar.pagopa.it/auth x-internal: true - description: User Acceptance Test - url: https://mil-u-apim.azure-api.net/mil-auth + url: https://api-mcshared.uat.cstar.pagopa.it/auth x-internal: true + - description: Production + url: https://api-mcshared.cstar.pagopa.it/auth + x-internal: false tags: - name: wellknown description: Well-known operation diff --git a/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars b/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars index a527ba02..fb604f93 100644 --- a/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars +++ b/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars @@ -46,7 +46,7 @@ mil_auth_image = "ghcr.io/pagopa/mil-auth:latest" mil_auth_cpu = 1 mil_auth_memory = "2Gi" mil_auth_max_replicas = 5 -mil_auth_min_replicas = 1 +mil_auth_min_replicas = 0 mil_auth_keyvault_maxresults = 20 mil_auth_keyvault_backoff_num_of_attempts = 5 mil_auth_mongodb_connect_timeout = "5s" diff --git a/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars b/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars index cc53ed88..ca83f155 100644 --- a/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars +++ b/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars @@ -46,7 +46,7 @@ mil_auth_image = "ghcr.io/pagopa/mil-auth:latest" mil_auth_cpu = 1 mil_auth_memory = "2Gi" mil_auth_max_replicas = 5 -mil_auth_min_replicas = 1 +mil_auth_min_replicas = 0 mil_auth_keyvault_maxresults = 20 mil_auth_keyvault_backoff_num_of_attempts = 5 mil_auth_mongodb_connect_timeout = "5s" diff --git a/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars b/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars index 673c014e..00127750 100644 --- a/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars +++ b/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars @@ -46,7 +46,7 @@ mil_auth_image = "ghcr.io/pagopa/mil-auth:latest" mil_auth_cpu = 1 mil_auth_memory = "2Gi" mil_auth_max_replicas = 5 -mil_auth_min_replicas = 1 +mil_auth_min_replicas = 0 mil_auth_keyvault_maxresults = 20 mil_auth_keyvault_backoff_num_of_attempts = 5 mil_auth_mongodb_connect_timeout = "5s" diff --git a/src/test/java/it/pagopa/swclient/mil/auth/admin/resource/UserResourceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/admin/resource/UserResourceTest.java new file mode 100644 index 00000000..ae08ab23 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/admin/resource/UserResourceTest.java @@ -0,0 +1,248 @@ +/* + * UserResourceTest.java + * + * 22 nov 2024 + */ +package it.pagopa.swclient.mil.auth.admin.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.CALLS_REAL_METHODS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; + +import org.assertj.core.util.Files; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import com.atlassian.oai.validator.restassured.OpenApiValidationFilter; +import com.mongodb.MongoWriteException; +import com.nimbusds.jose.util.StandardCharset; + +import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.security.TestSecurity; +import io.smallrye.mutiny.Uni; +import it.pagopa.swclient.mil.auth.admin.bean.AdminJsonPropertyName; +import it.pagopa.swclient.mil.auth.admin.bean.AdminQueryParamName; +import it.pagopa.swclient.mil.auth.admin.bean.CreateUserRequest; +import it.pagopa.swclient.mil.auth.dao.UserEntity; +import it.pagopa.swclient.mil.auth.dao.UserRepository; +import it.pagopa.swclient.mil.auth.util.UniGenerator; +import jakarta.ws.rs.core.MediaType; + +/** + * + * @author Antonio Tarricone + */ +@QuarkusTest +@TestHTTPEndpoint(UserResource.class) +@TestSecurity(user = "test_user", roles = { + "mil-auth-admin" +}) +class UserResourceTest { + /* + * + */ + @InjectMock + UserRepository repository; + + /* + * + */ + private static OpenApiValidationFilter validationFilter; + + /** + * + */ + @BeforeAll + static void loadOpenApiDescriptor() { + validationFilter = new OpenApiValidationFilter( + Files.contentOf( + new File("src/main/resources/META-INF/openapi.yaml"), + StandardCharset.UTF_8)); + } + + /** + * + * @param testInfo + */ + @BeforeEach + void init(TestInfo testInfo) { + String frame = "*".repeat(testInfo.getDisplayName().length() + 11); + System.out.println(frame); + System.out.printf("* %s: START *%n", testInfo.getDisplayName()); + System.out.println(frame); + } + + /** + * + */ + @Test + void given_requestToCreateNewUser_when_allGoesOk_then_getPassword() { + when(repository.persist(any(UserEntity.class))) + .thenReturn(UniGenerator.item(new UserEntity())); + + given() + .log().all() + .filter(validationFilter) + .contentType(MediaType.APPLICATION_JSON) + .body(new CreateUserRequest().setUsername("username")) + .when() + .post() + .then() + .log().all() + .statusCode(201) + .contentType(MediaType.APPLICATION_JSON) + .body(AdminJsonPropertyName.PASSWORD, notNullValue()); + } + + /** + * + */ + @Test + void given_requestToCreateNewUser_when_duplicateKeyOccurs_then_getFailure() { + MongoWriteException exc = mock(MongoWriteException.class, CALLS_REAL_METHODS); + when(exc.getCode()).thenReturn(11000); + + when(repository.persist(any(UserEntity.class))) + .thenReturn(Uni.createFrom().failure(exc)); + + given() + .log().all() + .filter(validationFilter) + .contentType(MediaType.APPLICATION_JSON) + .body(new CreateUserRequest().setUsername("username")) + .when() + .post() + .then() + .log().all() + .statusCode(409); + } + + /** + * + */ + @Test + void given_requestToCreateNewClient_when_mongoErrorOccursOnPersist_then_getFailure() { + MongoWriteException exc = mock(MongoWriteException.class, CALLS_REAL_METHODS); + when(exc.getCode()).thenReturn(12000); + + when(repository.persist(any(UserEntity.class))) + .thenReturn(Uni.createFrom().failure(exc)); + + given() + .log().all() + .filter(validationFilter) + .contentType(MediaType.APPLICATION_JSON) + .body(new CreateUserRequest().setUsername("username")) + .when() + .post() + .then() + .log().all() + .statusCode(500); + } + + /** + * + */ + @Test + void given_requestToCreateNewClient_when_otherErrorOccursOnPersist_then_getFailure() { + when(repository.persist(any(UserEntity.class))) + .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + + given() + .log().all() + .filter(validationFilter) + .contentType(MediaType.APPLICATION_JSON) + .body(new CreateUserRequest().setUsername("username")) + .when() + .post() + .then() + .log().all() + .statusCode(500); + } + + /** + * + */ + @Test + void given_requestToDeleteUser_when_allGoesOk_then_getNoContent() { + /* + * Mocking + */ + when(repository.deleteByUsername("83c0b10f-b398-4cc8-b356-a3e0f0291679")) + .thenReturn(UniGenerator.item((Long.valueOf(1)))); + + /* + * Tests + */ + given() + .log().all() + .filter(validationFilter) + .queryParam(AdminQueryParamName.USERNAME, "83c0b10f-b398-4cc8-b356-a3e0f0291679") + .when() + .delete() + .then() + .log().all() + .statusCode(204); + } + + /** + * + */ + @Test + void given_requestToDeleteUser_when_clientDoesntExist_then_getNotFound() { + /* + * Mocking + */ + when(repository.deleteByUsername("83c0b10f-b398-4cc8-b356-a3e0f0291679")) + .thenReturn(UniGenerator.item((Long.valueOf(0)))); + + /* + * Tests + */ + given() + .log().all() + .filter(validationFilter) + .queryParam(AdminQueryParamName.USERNAME, "83c0b10f-b398-4cc8-b356-a3e0f0291679") + .when() + .delete() + .then() + .log().all() + .statusCode(404) + .contentType(MediaType.APPLICATION_JSON); + } + + /** + * + */ + @Test + void given_requestToDeleteUser_when_databaseErrorOccurs_then_getServerError() { + /* + * Mocking + */ + when(repository.deleteByUsername("83c0b10f-b398-4cc8-b356-a3e0f0291679")) + .thenReturn(Uni.createFrom().failure(new Exception("synthetic exception"))); + + /* + * Tests + */ + given() + .log().all() + .filter(validationFilter) + .queryParam(AdminQueryParamName.USERNAME, "83c0b10f-b398-4cc8-b356-a3e0f0291679") + .when() + .delete() + .then() + .log().all() + .statusCode(500) + .contentType(MediaType.APPLICATION_JSON); + } +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/dao/RolesRepositoryTest.java b/src/test/java/it/pagopa/swclient/mil/auth/dao/RolesRepositoryTest.java index 89dc737e..f48c3cf0 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/dao/RolesRepositoryTest.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/dao/RolesRepositoryTest.java @@ -186,7 +186,7 @@ void testFindByParameters() { .assertCompleted() .assertItem(Tuple2.of(Long.valueOf(2), List.of(entity1, entity2))); } - + /** * */ diff --git a/src/test/java/it/pagopa/swclient/mil/auth/dao/UserRepositoryTest.java b/src/test/java/it/pagopa/swclient/mil/auth/dao/UserRepositoryTest.java index e8841e2c..7df36cca 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/dao/UserRepositoryTest.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/dao/UserRepositoryTest.java @@ -1,46 +1,34 @@ /* * UserRepositoryTest.java * - * 6 giu 2024 + * 2 nov 2024 */ package it.pagopa.swclient.mil.auth.dao; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import java.util.Optional; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; +import org.mockito.Mockito; +import io.quarkus.mongodb.panache.reactive.ReactivePanacheQuery; import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; -import it.pagopa.swclient.mil.auth.util.AuthError; import it.pagopa.swclient.mil.auth.util.UniGenerator; -import it.pagopa.swclient.mil.azureservices.storageblob.service.AzureStorageBlobReactiveService; -import jakarta.inject.Inject; -import jakarta.ws.rs.core.Response; -import jakarta.ws.rs.core.Response.Status; -/** - * - * @author Antonio Tarricone - */ @QuarkusTest class UserRepositoryTest { - /* - * - */ - @Inject - UserRepository repository; - /* * */ @InjectMock - AzureStorageBlobReactiveService blobService; + UserRepository repository; /** * @@ -52,106 +40,49 @@ void init(TestInfo testInfo) { System.out.println(frame); System.out.printf("* %s: START *%n", testInfo.getDisplayName()); System.out.println(frame); + Mockito.reset(repository); } /** * */ @Test - void given_userData_when_invokeGetUser_then_getIt() { - /* - * Setup - */ - when(blobService.getBlob("users", "user_hash.json")) - .thenReturn(UniGenerator.item(Response.ok(""" - { - "username": "username", - "salt": "salt", - "passwordHash": "passwordHash", - "acquirerId": "acquirerId", - "channel": "channel", - "merchantId": "merchantId" - } - """) - .build())); + void testFindByUsername() { + UserEntity entity = new UserEntity("user_id", "username", "channel", "salt", "secret_hash", "acquirer_id", "merchant_id"); - /* - * Test - */ - repository.getUser("user_hash") - .subscribe() - .with( - actual -> { - assertThat(actual) - .usingRecursiveComparison() - .isEqualTo(new UserEntity("username", "salt", "passwordHash", "acquirerId", "channel", "merchantId")); - }, - f -> fail(f)); - } + @SuppressWarnings("unchecked") + ReactivePanacheQuery query = mock(ReactivePanacheQuery.class); + when(query.firstResultOptional()) + .thenReturn(Uni.createFrom().item(Optional.of(entity))); - /** - * - */ - @Test - void given_inexistentUserHash_when_invokeGetUser_then_getFailure() { - /* - * Setup - */ - when(blobService.getBlob("users", "user_hash.json")) - .thenReturn(UniGenerator.item(Response.status(Status.NOT_FOUND) - .build())); + when(repository.find(UserEntity.USERNAME_PRP, "username")) + .thenReturn(query); - /* - * Test - */ - repository.getUser("user_hash") - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailedWith(AuthError.class); - } + when(repository.findByUsername("username")) + .thenCallRealMethod(); - /** - * - */ - @Test - void given_badUserData_when_invokeGetUser_then_getFailure() { - /* - * Setup - */ - when(blobService.getBlob("users", "user_hash.json")) - .thenReturn(UniGenerator.item(Response.ok(""" - { - "imnotauser": "imnotauser" - } - """) - .build())); - - /* - * Test - */ - repository.getUser("user_hash") + repository.findByUsername("username") .subscribe() .withSubscriber(UniAssertSubscriber.create()) - .assertFailedWith(AuthError.class); + .assertCompleted() + .assertItem(Optional.of(entity)); } /** * */ @Test - void given_errorFromBlobService_when_invokeGetUser_then_getFailure() { - /* - * Setup - */ - when(blobService.getBlob("users", "user_hash.json")) - .thenReturn(Uni.createFrom().failure(new Exception("synthetic_exception"))); + void testDeleteByUsername() { + when(repository.delete(UserEntity.USERNAME_PRP, "83c0b10f-b398-4cc8-b356-a3e0f0291679")) + .thenReturn(UniGenerator.item(Long.valueOf(1))); + + when(repository.deleteByUsername("83c0b10f-b398-4cc8-b356-a3e0f0291679")) + .thenCallRealMethod(); - /* - * Test - */ - repository.getUser("user_hash") + repository.deleteByUsername("83c0b10f-b398-4cc8-b356-a3e0f0291679") .subscribe() .withSubscriber(UniAssertSubscriber.create()) - .assertFailedWith(Exception.class); + .assertCompleted() + .assertItem(Long.valueOf(1)); } -} +} \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java b/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java index 1b9930aa..1edbc5c8 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/service/TokenByPasswordServiceTest.java @@ -6,20 +6,15 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.text.ParseException; -import java.util.Base64; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; -import org.mockito.MockedStatic; import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; @@ -37,7 +32,6 @@ import it.pagopa.swclient.mil.auth.qualifier.Password; import it.pagopa.swclient.mil.auth.util.AuthError; import it.pagopa.swclient.mil.auth.util.AuthException; -import it.pagopa.swclient.mil.auth.util.PasswordVerifier; import it.pagopa.swclient.mil.auth.util.UniGenerator; import it.pagopa.swclient.mil.bean.Channel; import jakarta.inject.Inject; @@ -96,40 +90,25 @@ void init(TestInfo testInfo) { /** * * @throws ParseException - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_allGoesOk_then_getAccessToken() throws ParseException, NoSuchAlgorithmException { + void given_userCredentials_when_allGoesOk_then_getAccessToken() throws ParseException { /* * Setup */ final String username = "username"; - final String password = "password"; - final String salt = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; - - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); - - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); - - byte[] passwordHashBytes = MessageDigest.getInstance("SHA256").digest(data); + final String password = "3ebcd984-48b1-4df2-99d8-f5d550dbad02"; + final String salt = "TSO2VIJixd6taCapX1Aq9bTIbTAEuDtXzLleB9A3W6NUgppiJkNbAnBX8CVYvpsPMpzJHGhK2ouHDONevrcVUg=="; + final String passwordHash = "gKWXj0IXDkeO5xvrozbm47tO+SXHNGN8pE5ql3W4Hgo="; - String passwordHash = Base64.getEncoder().encodeToString(passwordHashBytes); - - when(repository.getUser(userHash)) - .thenReturn(UniGenerator.item(new UserEntity() + when(repository.findByUsername(username)) + .thenReturn(UniGenerator.item(Optional.of(new UserEntity() .setAcquirerId("acquirer_id") .setChannel(Channel.POS) .setMerchantId("merchant_id") .setPasswordHash(passwordHash) .setSalt(salt) - .setUsername(username))); + .setUsername(username)))); when(clientVerifier.verify("client_id", Channel.POS, null)) .thenReturn(UniGenerator.item(new ClientEntity())); @@ -171,46 +150,16 @@ void given_userCredentials_when_allGoesOk_then_getAccessToken() throws ParseExce * */ @Test - void given_userCredentials_when_messageDigestThrowsException_then_getFailure() { - try (MockedStatic digest = mockStatic(MessageDigest.class)) { - digest.when(() -> MessageDigest.getInstance("SHA256")) - .thenThrow(NoSuchAlgorithmException.class); - - GetAccessTokenRequest request = new GetAccessTokenRequest() - .setAcquirerId("acquirer_id") - .setChannel("channel") - .setClientId("client_id") - .setGrantType(GrantType.PASSWORD) - .setMerchantId("merchant_id") - .setTerminalId("terminal_id") - .setUsername("username") - .setPassword("password"); - - tokenByPasswordService.process(request) - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailedWith(AuthError.class); - } - } - - /** - * - * @throws NoSuchAlgorithmException - */ - @Test - void given_userCredentials_when_userNotFound_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_userNotFound_then_getFailure() { /* * Setup */ final String username = "username"; final String password = "password"; - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - when(repository.getUser(userHash)) - .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()))); + when(repository.findByUsername(username)) + .thenReturn(Uni.createFrom() + .item(Optional.empty())); /* * Test @@ -233,21 +182,16 @@ void given_userCredentials_when_userNotFound_then_getFailure() throws NoSuchAlgo /** * - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_getUserReturns500_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_getUserReturns500_then_getFailure() { /* * Setup */ final String username = "username"; final String password = "password"; - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - when(repository.getUser(userHash)) + when(repository.findByUsername(username)) .thenReturn(Uni.createFrom().failure(new WebApplicationException(Response.status(Response.Status.INTERNAL_SERVER_ERROR).build()))); /* @@ -271,21 +215,16 @@ void given_userCredentials_when_getUserReturns500_then_getFailure() throws NoSuc /** * - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_getUserThrowsAnotherException_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_getUserThrowsAnotherException_then_getFailure() { /* * Setup */ final String username = "username"; final String password = "password"; - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - when(repository.getUser(userHash)) + when(repository.findByUsername(username)) .thenReturn(Uni.createFrom().failure(new Exception("synthetic_exception"))); /* @@ -309,40 +248,25 @@ void given_userCredentials_when_getUserThrowsAnotherException_then_getFailure() /** * - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_consistencyVerificationFailsDueToAcquirerId_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_consistencyVerificationFailsDueToAcquirerId_then_getFailure() { /* * Setup */ final String username = "username"; - final String password = "password"; - final String salt = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; + final String password = "3ebcd984-48b1-4df2-99d8-f5d550dbad02"; + final String salt = "TSO2VIJixd6taCapX1Aq9bTIbTAEuDtXzLleB9A3W6NUgppiJkNbAnBX8CVYvpsPMpzJHGhK2ouHDONevrcVUg=="; + final String passwordHash = "gKWXj0IXDkeO5xvrozbm47tO+SXHNGN8pE5ql3W4Hgo="; - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); - - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); - - byte[] passwordHashBytes = MessageDigest.getInstance("SHA256").digest(data); - - String passwordHash = Base64.getEncoder().encodeToString(passwordHashBytes); - - when(repository.getUser(userHash)) - .thenReturn(UniGenerator.item(new UserEntity() + when(repository.findByUsername(username)) + .thenReturn(UniGenerator.item(Optional.of(new UserEntity() .setAcquirerId("acquirer_id_2") .setChannel("channel") .setMerchantId("merchant_id") .setPasswordHash(passwordHash) .setSalt(salt) - .setUsername(username))); + .setUsername(username)))); when(clientVerifier.verify("client_id", "channel", null)) .thenReturn(UniGenerator.item(new ClientEntity())); @@ -372,40 +296,25 @@ void given_userCredentials_when_consistencyVerificationFailsDueToAcquirerId_then /** * - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_consistencyVerificationFailsDueToChannel_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_consistencyVerificationFailsDueToChannel_then_getFailure() { /* * Setup */ final String username = "username"; - final String password = "password"; - final String salt = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; - - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); - - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); - - byte[] passwordHashBytes = MessageDigest.getInstance("SHA256").digest(data); + final String password = "3ebcd984-48b1-4df2-99d8-f5d550dbad02"; + final String salt = "TSO2VIJixd6taCapX1Aq9bTIbTAEuDtXzLleB9A3W6NUgppiJkNbAnBX8CVYvpsPMpzJHGhK2ouHDONevrcVUg=="; + final String passwordHash = "gKWXj0IXDkeO5xvrozbm47tO+SXHNGN8pE5ql3W4Hgo="; - String passwordHash = Base64.getEncoder().encodeToString(passwordHashBytes); - - when(repository.getUser(userHash)) - .thenReturn(UniGenerator.item(new UserEntity() + when(repository.findByUsername(username)) + .thenReturn(UniGenerator.item(Optional.of(new UserEntity() .setAcquirerId("acquirer_id") .setChannel("channel_2") .setMerchantId("merchant_id") .setPasswordHash(passwordHash) .setSalt(salt) - .setUsername(username))); + .setUsername(username)))); when(clientVerifier.verify("client_id", "channel", null)) .thenReturn(UniGenerator.item(new ClientEntity())); @@ -435,40 +344,25 @@ void given_userCredentials_when_consistencyVerificationFailsDueToChannel_then_ge /** * - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_consistencyVerificationFailsDueToMerchantId_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_consistencyVerificationFailsDueToMerchantId_then_getFailure() { /* * Setup */ final String username = "username"; - final String password = "password"; - final String salt = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; - - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); - - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); + final String password = "3ebcd984-48b1-4df2-99d8-f5d550dbad02"; + final String salt = "TSO2VIJixd6taCapX1Aq9bTIbTAEuDtXzLleB9A3W6NUgppiJkNbAnBX8CVYvpsPMpzJHGhK2ouHDONevrcVUg=="; + final String passwordHash = "gKWXj0IXDkeO5xvrozbm47tO+SXHNGN8pE5ql3W4Hgo="; - byte[] passwordHashBytes = MessageDigest.getInstance("SHA256").digest(data); - - String passwordHash = Base64.getEncoder().encodeToString(passwordHashBytes); - - when(repository.getUser(userHash)) - .thenReturn(UniGenerator.item(new UserEntity() + when(repository.findByUsername(username)) + .thenReturn(UniGenerator.item(Optional.of(new UserEntity() .setAcquirerId("acquirer_id") .setChannel("channel") .setMerchantId("merchant_id_2") .setPasswordHash(passwordHash) .setSalt(salt) - .setUsername(username))); + .setUsername(username)))); when(clientVerifier.verify("client_id", "channel", null)) .thenReturn(UniGenerator.item(new ClientEntity())); @@ -498,40 +392,24 @@ void given_userCredentials_when_consistencyVerificationFailsDueToMerchantId_then /** * - * @throws NoSuchAlgorithmException */ @Test - void given_userCredentials_when_passwordIsWrong_then_getFailure() throws NoSuchAlgorithmException { + void given_userCredentials_when_passwordIsWrong_then_getFailure() { /* * Setup */ final String username = "username"; - final String password = "password"; - final String salt = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; - - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); + final String salt = "TSO2VIJixd6taCapX1Aq9bTIbTAEuDtXzLleB9A3W6NUgppiJkNbAnBX8CVYvpsPMpzJHGhK2ouHDONevrcVUg=="; + final String passwordHash = "gKWXj0IXDkeO5xvrozbm47tO+SXHNGN8pE5ql3W4Hgo="; - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); - - byte[] passwordHashBytes = MessageDigest.getInstance("SHA256").digest(data); - - String passwordHash = Base64.getEncoder().encodeToString(passwordHashBytes); - - when(repository.getUser(userHash)) - .thenReturn(UniGenerator.item(new UserEntity() + when(repository.findByUsername(username)) + .thenReturn(UniGenerator.item(Optional.of(new UserEntity() .setAcquirerId("acquirer_id") .setChannel("channel") .setMerchantId("merchant_id") .setPasswordHash(passwordHash) .setSalt(salt) - .setUsername(username))); + .setUsername(username)))); when(clientVerifier.verify("client_id", "channel", null)) .thenReturn(UniGenerator.item(new ClientEntity())); @@ -558,72 +436,4 @@ void given_userCredentials_when_passwordIsWrong_then_getFailure() throws NoSuchA .withSubscriber(UniAssertSubscriber.create()) .assertFailedWith(AuthException.class); } - - /** - * - * @throws NoSuchAlgorithmException - */ - @Test - void given_userCredentials_when_passwordVerificationThrowsException_then_getFailure() throws NoSuchAlgorithmException { - /* - * Setup - */ - final String username = "username"; - final String password = "password"; - final String salt = "zfN59oSr9RfFiiSASUO1YIcv8bARsj1OAV8tEydQiKC3su5Mlz1TsjbFwvWrGCjXdkDUsbeXGnYZDavJuTKw6Q=="; - - String userHash = Base64.getUrlEncoder() - .encodeToString(MessageDigest.getInstance("SHA256") - .digest(username.getBytes(StandardCharsets.UTF_8))); - - byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8); - byte[] saltBytes = Base64.getDecoder().decode(salt); - - byte[] data = new byte[passwordBytes.length + saltBytes.length]; - System.arraycopy(passwordBytes, 0, data, 0, passwordBytes.length); - System.arraycopy(saltBytes, 0, data, passwordBytes.length, saltBytes.length); - - byte[] passwordHashBytes = MessageDigest.getInstance("SHA256").digest(data); - - String passwordHash = Base64.getEncoder().encodeToString(passwordHashBytes); - - when(repository.getUser(userHash)) - .thenReturn(UniGenerator.item(new UserEntity() - .setAcquirerId("acquirer_id") - .setChannel("channel") - .setMerchantId("merchant_id") - .setPasswordHash(passwordHash) - .setSalt(salt) - .setUsername(username))); - - when(clientVerifier.verify("client_id", "channel", null)) - .thenReturn(UniGenerator.item(new ClientEntity())); - - when(roleFinder.findRoles("acquirer_id", "channel", "client_id", "merchant_id", "terminal_id")) - .thenReturn(UniGenerator.item(new SetOfRolesEntity() - .setRoles(List.of("role")))); - - /* - * Test - */ - try (MockedStatic passwordVerifier = mockStatic(PasswordVerifier.class)) { - passwordVerifier.when(() -> PasswordVerifier.verify(password, salt, passwordHash)) - .thenThrow(NoSuchAlgorithmException.class); - - GetAccessTokenRequest request = new GetAccessTokenRequest() - .setAcquirerId("acquirer_id") - .setChannel("channel") - .setClientId("client_id") - .setGrantType(GrantType.PASSWORD) - .setMerchantId("merchant_id") - .setTerminalId("terminal_id") - .setUsername(username) - .setPassword(password); - - tokenByPasswordService.process(request) - .subscribe() - .withSubscriber(UniAssertSubscriber.create()) - .assertFailedWith(AuthError.class); - } - } } \ No newline at end of file diff --git a/src/test/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidatorTest.java b/src/test/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidatorTest.java index 7c36c152..7c83d6f4 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidatorTest.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/validation/constraints/ValidatorTest.java @@ -1249,6 +1249,401 @@ void given_grantTypeClientCredetialsAndChannelNull_when_scopeIsNotNull_then_getN null)); } + /* + * GRANT TYPE = PASSWORD + CHANNEL = null + */ + @Test + void given_grantTypePasswordAndChannelNull_when_allIsOk_then_getValid() { + assertTrue(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_acquirerIdIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId("acquirer_id") + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_merchantIdIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId("merchant_id") + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_terminalIdIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId("terminal_id") + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_clientSecretIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret("client_secret") + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_extTokentIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken("ext_token") + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_addDataIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData("add_data") + .setRefreshToken(null) + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_refreshTokenIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername("username") + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_usernameIsNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername(null) + .setPassword("password"), + null)); + } + + @Test + void given_grantTypePasswordAndChannelNull_when_passwordIsNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.PASSWORD) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername("username") + .setPassword(null), + null)); + } + + /* + * GRANT TYPE = REFRESH TOKEN + CHANNEL = null + */ + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_allIsOk_then_getValid() { + assertTrue(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_acquirerIdIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId("acquirer_id") + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_merchsntIdIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId("merchant_id") + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_terminalIdIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId("terminal_id") + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_clientSecretIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret("client_secret") + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_extTokenIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken("ext_token") + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_addDatasNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData("add_data") + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_usernamerIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername("username") + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_passwordNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword("password") + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_refreshTokenIsNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken(null) + .setUsername(null) + .setPassword(null) + .setScope(null), + null)); + } + + @Test + void given_grantTypeRefreshTokenAndChannelNull_when_scopeIsNotNull_then_getNotValid() { + assertFalse(new Validator() + .isValid(new GetAccessTokenRequest() + .setGrantType(GrantType.REFRESH_TOKEN) + .setChannel(null) + .setAcquirerId(null) + .setMerchantId(null) + .setTerminalId(null) + .setClientSecret(null) + .setExtToken(null) + .setAddData(null) + .setRefreshToken("refresh_token") + .setUsername(null) + .setPassword(null) + .setScope("scope"), + null)); + } + /* * OTHER GRANT TYPE + CHANNEL */ From 9ea8a5708468ff0a3d18443d3efa61d48a87fbee Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Fri, 22 Nov 2024 16:36:41 +0100 Subject: [PATCH 2/7] Removed references to blob storage no longer used. --- src/main/resources/application.properties | 9 +-------- src/main/terraform/container_app.tf | 11 ----------- .../env/cstar-d-mcshared/terraform.tfvars | 1 - .../env/cstar-p-mcshared/terraform.tfvars | 1 - .../env/cstar-u-mcshared/terraform.tfvars | 1 - src/main/terraform/env/dev-cd/terraform.tfvars | 1 - src/main/terraform/env/uat-cd/terraform.tfvars | 1 - src/main/terraform/variables.tf | 15 --------------- 8 files changed, 1 insertion(+), 39 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 13b168eb..e27e821b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -50,14 +50,7 @@ quarkus.rest-client.poynt-api.url=https://services-eu.poynt.net/ poynt-api.version=1.2 # ------------------------------------------------------------------------------ -# Authorization data repository (users) : used by mil-azure-services -# ------------------------------------------------------------------------------ -azure-storage-blob.version=2019-07-07 -quarkus.rest-client.azure-storage-blob.url=https://mildconfst.blob.core.windows.net -%prod.quarkus.rest-client.azure-storage-blob.url=${auth.data.url} - -# ------------------------------------------------------------------------------ -# MongoDB client configuration (clients, roles) +# MongoDB client configuration (clients, roles, users) # ------------------------------------------------------------------------------ quarkus.mongodb.connect-timeout=${mongodb.connect-timeout:5} quarkus.mongodb.read-timeout=${mongodb.read-timeout:10} diff --git a/src/main/terraform/container_app.tf b/src/main/terraform/container_app.tf index 9f568902..e2fd43ae 100644 --- a/src/main/terraform/container_app.tf +++ b/src/main/terraform/container_app.tf @@ -54,11 +54,6 @@ resource "azurerm_container_app" "auth" { value = var.mil_auth_refresh_duration } - env { - name = "auth.data.url" - secret_name = "storage-account-auth-primary-blob-endpoint" - } - env { name = "auth.keyvault.url" secret_name = "key-vault-auth-vault-uri" @@ -141,12 +136,6 @@ resource "azurerm_container_app" "auth" { identity = data.azurerm_user_assigned_identity.auth.id } - secret { - name = "storage-account-auth-primary-blob-endpoint" - key_vault_secret_id = "${data.azurerm_key_vault.general.vault_uri}secrets/${var.storage_account_primary_blob_endpoint_kv_secret}" - identity = data.azurerm_user_assigned_identity.auth.id - } - secret { name = "key-vault-auth-vault-uri" key_vault_secret_id = "${data.azurerm_key_vault.general.vault_uri}secrets/${var.key_vault_auth_vault_uri_kv_secret}" diff --git a/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars b/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars index fb604f93..d3e33149 100644 --- a/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars +++ b/src/main/terraform/env/cstar-d-mcshared/terraform.tfvars @@ -27,7 +27,6 @@ auth_st_resource_group_name = "cstar-d-mcshared-data-rg" # ------------------------------------------------------------------------------ cosmosdb_account_primary_mongodb_connection_string_kv_secret = "cosmosdb-account-mcshared-primary-mongodb-connection-string" cosmosdb_account_secondary_mongodb_connection_string_kv_secret = "cosmosdb-account-mcshared-secondary-mongodb-connection-string" -storage_account_primary_blob_endpoint_kv_secret = "storage-account-auth-primary-blob-endpoint" key_vault_auth_vault_uri_kv_secret = "key-vault-auth-vault-uri" application_insigths_connection_string_kv_secret = "core-application-insigths-connection-string" diff --git a/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars b/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars index ca83f155..dac266f5 100644 --- a/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars +++ b/src/main/terraform/env/cstar-p-mcshared/terraform.tfvars @@ -27,7 +27,6 @@ auth_st_resource_group_name = "cstar-p-mcshared-data-rg" # ------------------------------------------------------------------------------ cosmosdb_account_primary_mongodb_connection_string_kv_secret = "cosmosdb-account-mcshared-primary-mongodb-connection-string" cosmosdb_account_secondary_mongodb_connection_string_kv_secret = "cosmosdb-account-mcshared-secondary-mongodb-connection-string" -storage_account_primary_blob_endpoint_kv_secret = "storage-account-auth-primary-blob-endpoint" key_vault_auth_vault_uri_kv_secret = "key-vault-auth-vault-uri" application_insigths_connection_string_kv_secret = "core-application-insigths-connection-string" diff --git a/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars b/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars index 00127750..05db8ac5 100644 --- a/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars +++ b/src/main/terraform/env/cstar-u-mcshared/terraform.tfvars @@ -27,7 +27,6 @@ auth_st_resource_group_name = "cstar-u-mcshared-data-rg" # ------------------------------------------------------------------------------ cosmosdb_account_primary_mongodb_connection_string_kv_secret = "cosmosdb-account-mcshared-primary-mongodb-connection-string" cosmosdb_account_secondary_mongodb_connection_string_kv_secret = "cosmosdb-account-mcshared-secondary-mongodb-connection-string" -storage_account_primary_blob_endpoint_kv_secret = "storage-account-auth-primary-blob-endpoint" key_vault_auth_vault_uri_kv_secret = "key-vault-auth-vault-uri" application_insigths_connection_string_kv_secret = "core-application-insigths-connection-string" diff --git a/src/main/terraform/env/dev-cd/terraform.tfvars b/src/main/terraform/env/dev-cd/terraform.tfvars index e0ac0dbc..119671c4 100644 --- a/src/main/terraform/env/dev-cd/terraform.tfvars +++ b/src/main/terraform/env/dev-cd/terraform.tfvars @@ -34,7 +34,6 @@ auth_st_resource_group_name = "mil-d-data-rg" # ------------------------------------------------------------------------------ cosmosdb_account_primary_mongodb_connection_string_kv_secret = "cosmosdb-account-mil-primary-mongodb-connection-string" cosmosdb_account_secondary_mongodb_connection_string_kv_secret = "cosmosdb-account-mil-secondary-mongodb-connection-string" -storage_account_primary_blob_endpoint_kv_secret = "storage-account-auth-primary-blob-endpoint" key_vault_auth_vault_uri_kv_secret = "key-vault-auth-vault-uri" application_insigths_connection_string_kv_secret = "application-insigths-mil-connection-string" diff --git a/src/main/terraform/env/uat-cd/terraform.tfvars b/src/main/terraform/env/uat-cd/terraform.tfvars index 663143aa..415ea4fa 100644 --- a/src/main/terraform/env/uat-cd/terraform.tfvars +++ b/src/main/terraform/env/uat-cd/terraform.tfvars @@ -34,7 +34,6 @@ auth_st_resource_group_name = "mil-u-data-rg" # ------------------------------------------------------------------------------ cosmosdb_account_primary_mongodb_connection_string_kv_secret = "cosmosdb-account-mil-primary-mongodb-connection-string" cosmosdb_account_secondary_mongodb_connection_string_kv_secret = "cosmosdb-account-mil-secondary-mongodb-connection-string" -storage_account_primary_blob_endpoint_kv_secret = "storage-account-auth-primary-blob-endpoint" key_vault_auth_vault_uri_kv_secret = "key-vault-auth-vault-uri" application_insigths_connection_string_kv_secret = "application-insigths-mil-connection-string" diff --git a/src/main/terraform/variables.tf b/src/main/terraform/variables.tf index 32baac59..06af3dcc 100644 --- a/src/main/terraform/variables.tf +++ b/src/main/terraform/variables.tf @@ -90,17 +90,6 @@ variable "auth_kv_resource_group_name" { type = string } -# ------------------------------------------------------------------------------ -# Storage account containing configuration files. -# ------------------------------------------------------------------------------ -variable "auth_st_name" { - type = string -} - -variable "auth_st_resource_group_name" { - type = string -} - # ------------------------------------------------------------------------------ # Names of key vault secrets. # ------------------------------------------------------------------------------ @@ -112,10 +101,6 @@ variable "cosmosdb_account_secondary_mongodb_connection_string_kv_secret" { type = string } -variable "storage_account_primary_blob_endpoint_kv_secret" { - type = string -} - variable "key_vault_auth_vault_uri_kv_secret" { type = string } From 0d8302755b5392e226543b4080d6955ff2c3b828 Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Thu, 12 Dec 2024 10:05:27 +0100 Subject: [PATCH 3/7] Integration tests. --- .github/workflows/integration-tests.yml | 45 ++++ pom.xml | 3 + .../mil/auth/resource/TokenResourceIT.java | 201 ++++++++++++++++++ 3 files changed, 249 insertions(+) create mode 100644 .github/workflows/integration-tests.yml create mode 100644 src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml new file mode 100644 index 00000000..613a3a71 --- /dev/null +++ b/.github/workflows/integration-tests.yml @@ -0,0 +1,45 @@ +name: Integration Tests + +on: + workflow_dispatch: + +jobs: + integration_tets: + runs-on: ubuntu-22.04 + + environment: cstar-d-mcshared + + permissions: + id-token: write # Get OIDC token to authenticate to Azure. + + steps: + # + # Checkout the source code. + # + - name: Checkout the source code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2 + + # + # Setup Java Build Environment. + # + - name: Setup Java Build Environment + uses: pagopa/mil-actions/setup-java-build-env@f782a1b3cdb79afda2c10007ae46b831b31fe640 # 1.1.2 + with: + gh_user: ${{ secrets.GIT_USER }} + gh_token: ${{ secrets.GIT_PAT }} + + # + # Run integration tests. + # + - name: Run integration tests + run: | + ${{ runner.temp }}/maven/bin/mvn verify \ + -DskipUTs=true \ + -DskipITs=false \ + -Dbase_uri=${{ secrets.IT_BASE_URI }} + -Dadmin_client_id=${{ secrets.IT_ADMIN_CLIENT_ID }} \ + -Dadmin_client_secret=${{ secrets.IT_ADMIN_CLIENT_SECRET }} \ + -Dtoken_info_client_id=${{ secrets.IT_TOKEN_INFO_CLIENT_ID }} \ + -Dtoken_info_client_secret=${{ secrets.IT_TOKEN_INFO_CLIENT_SECRET }} \ + -Dtest_username=${{ secrets.IT_TEST_USERNAME }} \ + -Dtest_password=${{ secrets.IT_TEST_PASSWORD }} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 62e5bfa6..2603f107 100644 --- a/pom.xml +++ b/pom.xml @@ -71,6 +71,7 @@ java:S117 src/test/java/**/*.java true + false @@ -268,6 +269,7 @@ maven-surefire-plugin ${surefire-plugin.version} + ${skipUTs} org.jboss.logmanager.LogManager @@ -356,6 +358,7 @@ + ${skipUTs} diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java new file mode 100644 index 00000000..4b3dcae7 --- /dev/null +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java @@ -0,0 +1,201 @@ +/* + * TokenResourceIT.java + * + * 5 dic 2024 + */ +package it.pagopa.swclient.mil.auth.resource; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import java.io.File; + +import org.assertj.core.util.Files; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInfo; + +import com.atlassian.oai.validator.restassured.OpenApiValidationFilter; +import com.nimbusds.jose.util.StandardCharset; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; +import it.pagopa.swclient.mil.auth.bean.AuthFormParamName; +import it.pagopa.swclient.mil.auth.bean.AuthJsonPropertyName; +import it.pagopa.swclient.mil.auth.bean.GrantType; +import it.pagopa.swclient.mil.auth.bean.Scope; +import it.pagopa.swclient.mil.auth.bean.TokenType; +import jakarta.ws.rs.core.MediaType; + +/** + * To run this from your workstation, connect to CSTAR-DEV by VPN and: + * + * // @formatter:off + * + * mvn verify \ + -DskipUTs=true \ + -DskipITs=false \ + -Dbase_uri=https://cstar-d-mcshared-auth-ca.blueforest-569cf489.westeurope.azurecontainerapps.io:443 \ + -Dadmin_client_id=f0ef1b15-c54a-4552-9e9a-2ca4d83260d7 \ + -Dadmin_client_secret=7fc345f5-0f6f-4df1-9f1c-21a6a8a95da0 \ + -Dtoken_info_client_id=null \ + -Dtoken_info_client_secret=null \ + -Dtest_username=null \ + -Dtest_password=null + * + * // @formatter:on + * + * @author antonio.tarricone + */ +@QuarkusTest +class TokenResourceIT { + /* + * + */ + @ConfigProperty(name = "base_uri") + String baseUri; + + @ConfigProperty(name = "port", defaultValue = "443") + int port; + + /* + * + */ + @ConfigProperty(name = "admin_client_id") + String adminClientId; + + @ConfigProperty(name = "admin_client_secret") + String adminClientSecret; + + /* + * + */ + @ConfigProperty(name = "token_info_client_id") + String tokenInfoClientId; + + @ConfigProperty(name = "token_info_client_secret") + String tokenInfoClientSecret; + + /* + * + */ + @ConfigProperty(name = "test_username") + String testUsername; + + @ConfigProperty(name = "test_password") + String testPassword; + + /** + * + */ + @BeforeAll + static void loadOpenApiDescriptor() { + RestAssured.filters( + new OpenApiValidationFilter( + Files.contentOf( + new File("src/main/resources/META-INF/openapi.yaml"), + StandardCharset.UTF_8))/*, + new RequestLoggingFilter(System.out), + new ResponseLoggingFilter(System.out)*/); + } + + /** + * + * @param testInfo + */ + @BeforeEach + void init(TestInfo testInfo) { + String frame = "*".repeat(testInfo.getDisplayName().length() + 11); + System.out.println(frame); + System.out.printf("* %s: START *%n", testInfo.getDisplayName()); + System.out.println(frame); + } + + /** + * + */ + @Test + void given_rightClientCredentials_when_theEndPointIsInvoked_then_getAccessToken() { + given() + .baseUri(baseUri) + .formParam(AuthFormParamName.CLIENT_ID, adminClientId) + .formParam(AuthFormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(AuthFormParamName.CLIENT_SECRET, adminClientSecret) + .when() + .post("/token") + .then() + .statusCode(200) + .body(AuthJsonPropertyName.REFRESH_TOKEN, nullValue()); + } + + /** + * + */ + @Test + void given_wrongClientId_when_theEndPointIsInvoked_then_getError() { + given() + .baseUri(baseUri) + .formParam(AuthFormParamName.CLIENT_ID, "00000000-0000-0000-0000-000000000000") + .formParam(AuthFormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(AuthFormParamName.CLIENT_SECRET, adminClientSecret) + .when() + .post("/token") + .then() + .statusCode(401); + } + + /** + * + */ + @Test + void given_wrongClientSecret_when_theEndPointIsInvoked_then_getError() { + given() + .baseUri(baseUri) + .formParam(AuthFormParamName.CLIENT_ID, adminClientId) + .formParam(AuthFormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(AuthFormParamName.CLIENT_SECRET, "00000000-0000-0000-0000-000000000000") + .when() + .post("/token") + .then() + .statusCode(401); + } + + /** + * + */ + @Test + void given_rightClientCredentialsAndOfflineAccessIsRequired_when_theEndPointIsInvoked_then_getAccessToken() { + given() + .baseUri(baseUri) + .formParam(AuthFormParamName.CLIENT_ID, adminClientId) + .formParam(AuthFormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(AuthFormParamName.CLIENT_SECRET, adminClientSecret) + .formParam(AuthFormParamName.SCOPE, Scope.OFFLINE_ACCESS) + .when() + .post("/token") + .then() + .statusCode(400); + } + + /** + * + */ + @Test + void given_rightClientCredentialsAndFiscalCode_when_theEndPointIsInvoked_then_getAccessToken() { + given() + .baseUri(baseUri) + .formParam(AuthFormParamName.CLIENT_ID, adminClientId) + .formParam(AuthFormParamName.GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) + .formParam(AuthFormParamName.CLIENT_SECRET, adminClientSecret) + .formParam(AuthFormParamName.FISCAL_CODE, "RSSMRA85T10A562S") + .when() + .post("/token") + .then() + .statusCode(200) + .body(AuthJsonPropertyName.REFRESH_TOKEN, nullValue()); + } +} From 4e975eb336533879397576ed9fc3c1e2911bf1d2 Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Thu, 12 Dec 2024 10:05:58 +0100 Subject: [PATCH 4/7] Added default values. --- src/main/resources/application.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e27e821b..37509c13 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -23,9 +23,9 @@ quarkus.log.category."io.quarkus.smallrye.jwt.runtime.auth.MpJwtValidator".min-l %test.quarkus.rest-client.logging.scope=none %prod.quarkus.log.console.json=${auth.json-log:true} -%prod.quarkus.log.level=${auth.quarkus-log-level} -%prod.quarkus.log.category."it.pagopa.swclient.mil".level=${auth.app-log-level} -%prod.quarkus.rest-client.logging.scope=${auth.quarkus-rest-client-logging-scope} +%prod.quarkus.log.level=${auth.quarkus-log-level:ERROR} +%prod.quarkus.log.category."it.pagopa.swclient.mil".level=${auth.app-log-level:ERROR} +%prod.quarkus.rest-client.logging.scope=${auth.quarkus-rest-client-logging-scope:none} # ------------------------------------------------------------------------------ # Cryptoperiod of RSA keys in seconds (86400s = 1d) From c5488e6691fddaa32a2a1fc39de5e42c927fca57 Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Thu, 12 Dec 2024 10:06:29 +0100 Subject: [PATCH 5/7] Min replicas set from 1 to 0. --- src/main/terraform/env/dev-cd/terraform.tfvars | 2 +- src/main/terraform/env/uat-cd/terraform.tfvars | 2 +- src/main/terraform/variables.tf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/terraform/env/dev-cd/terraform.tfvars b/src/main/terraform/env/dev-cd/terraform.tfvars index 119671c4..1c172f3e 100644 --- a/src/main/terraform/env/dev-cd/terraform.tfvars +++ b/src/main/terraform/env/dev-cd/terraform.tfvars @@ -52,7 +52,7 @@ mil_auth_image = "ghcr.io/pagopa/mil-auth:latest" mil_auth_cpu = 1 mil_auth_memory = "2Gi" mil_auth_max_replicas = 5 -mil_auth_min_replicas = 1 +mil_auth_min_replicas = 0 mil_auth_keyvault_maxresults = 20 mil_auth_keyvault_backoff_num_of_attempts = 5 mil_auth_mongodb_connect_timeout = "5s" diff --git a/src/main/terraform/env/uat-cd/terraform.tfvars b/src/main/terraform/env/uat-cd/terraform.tfvars index 415ea4fa..4ae5050a 100644 --- a/src/main/terraform/env/uat-cd/terraform.tfvars +++ b/src/main/terraform/env/uat-cd/terraform.tfvars @@ -52,7 +52,7 @@ mil_auth_image = "ghcr.io/pagopa/mil-auth:latest" mil_auth_cpu = 1 mil_auth_memory = "2Gi" mil_auth_max_replicas = 5 -mil_auth_min_replicas = 1 +mil_auth_min_replicas = 0 mil_auth_keyvault_maxresults = 20 mil_auth_keyvault_backoff_num_of_attempts = 5 mil_auth_mongodb_connect_timeout = "5s" diff --git a/src/main/terraform/variables.tf b/src/main/terraform/variables.tf index 06af3dcc..60278cc6 100644 --- a/src/main/terraform/variables.tf +++ b/src/main/terraform/variables.tf @@ -174,7 +174,7 @@ variable "mil_auth_max_replicas" { variable "mil_auth_min_replicas" { type = number - default = 1 + default = 0 } variable "mil_auth_keyvault_maxresults" { From b4f279eade1b04cacc66e7bf80bd24c3d9a52692 Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Thu, 12 Dec 2024 10:07:06 +0100 Subject: [PATCH 6/7] Corrected the HTTP status code. --- src/main/resources/META-INF/openapi.yaml | 4 ++-- src/main/resources/META-INF/openapi_not_admin.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/META-INF/openapi.yaml b/src/main/resources/META-INF/openapi.yaml index 3b7b5312..5db67e80 100644 --- a/src/main/resources/META-INF/openapi.yaml +++ b/src/main/resources/META-INF/openapi.yaml @@ -49,8 +49,8 @@ paths: requestBody: $ref: '#/components/requestBodies/GetAccessToken' responses: - "201": - #description: Created + "200": + #description: Ok $ref: '#/components/responses/AccessToken' "400": #description: Bad request diff --git a/src/main/resources/META-INF/openapi_not_admin.yaml b/src/main/resources/META-INF/openapi_not_admin.yaml index f4e04c7a..c9e9dc52 100644 --- a/src/main/resources/META-INF/openapi_not_admin.yaml +++ b/src/main/resources/META-INF/openapi_not_admin.yaml @@ -41,8 +41,8 @@ paths: requestBody: $ref: '#/components/requestBodies/GetAccessToken' responses: - "201": - #description: Created + "200": + #description: Ok $ref: '#/components/responses/AccessToken' "400": #description: Bad request From 2c2a50c43005d9ea150c737355e70c6538a096e5 Mon Sep 17 00:00:00 2001 From: antoniotarricone Date: Thu, 12 Dec 2024 10:57:32 +0100 Subject: [PATCH 7/7] =?UTF-8?q?Added=20default=20values=20=E2=80=8B?= =?UTF-8?q?=E2=80=8Bto=20properties.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This because during unit tests Quarkus still tries to initialize the classes dedicated to integration tests! --- .../mil/auth/resource/TokenResourceIT.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java index 4b3dcae7..ec78ae71 100644 --- a/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java +++ b/src/test/java/it/pagopa/swclient/mil/auth/resource/TokenResourceIT.java @@ -56,7 +56,7 @@ class TokenResourceIT { /* * */ - @ConfigProperty(name = "base_uri") + @ConfigProperty(name = "base_uri", defaultValue = "null") String baseUri; @ConfigProperty(name = "port", defaultValue = "443") @@ -65,28 +65,28 @@ class TokenResourceIT { /* * */ - @ConfigProperty(name = "admin_client_id") + @ConfigProperty(name = "admin_client_id", defaultValue = "null") String adminClientId; - @ConfigProperty(name = "admin_client_secret") + @ConfigProperty(name = "admin_client_secret", defaultValue = "null") String adminClientSecret; /* * */ - @ConfigProperty(name = "token_info_client_id") + @ConfigProperty(name = "token_info_client_id", defaultValue = "null") String tokenInfoClientId; - @ConfigProperty(name = "token_info_client_secret") + @ConfigProperty(name = "token_info_client_secret", defaultValue = "null") String tokenInfoClientSecret; /* * */ - @ConfigProperty(name = "test_username") + @ConfigProperty(name = "test_username", defaultValue = "null") String testUsername; - @ConfigProperty(name = "test_password") + @ConfigProperty(name = "test_password", defaultValue = "null") String testPassword; /**