diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateNonPersonalizedOfflineSignaturePayloadRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateNonPersonalizedOfflineSignaturePayloadRequest.java index 79470e879..a86e34f30 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateNonPersonalizedOfflineSignaturePayloadRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreateNonPersonalizedOfflineSignaturePayloadRequest.java @@ -18,6 +18,8 @@ package com.wultra.security.powerauth.client.model.request; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; import lombok.Data; /** @@ -28,7 +30,12 @@ @Data public class CreateNonPersonalizedOfflineSignaturePayloadRequest { + @Schema(description = "The identifier of the application") + @NotBlank(message = "Application ID must not be empty when creating offline signature payload") private String applicationId; + + @Schema(description = "Signed data") + @NotBlank(message = "Signed data must not be empty when creating offline signature payload") private String data; } diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java index 00a54ab82..4aff824c5 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/CreatePersonalizedOfflineSignaturePayloadRequest.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.ToString; @@ -32,7 +33,12 @@ @Data public class CreatePersonalizedOfflineSignaturePayloadRequest { + @Schema(description = "Activation identifier") + @NotBlank(message = "Activation ID must not be empty when creating offline signature payload") private String activationId; + + @Schema(description = "Signed data") + @NotBlank(message = "Signed data must not be empty when creating offline signature payload") private String data; @Schema(description = "Optional nonce (16 bytes base64 encoded into 24 characters), otherwise it will be generated by PowerAuth server. Needed to be set when proximity check is enabled.", maxLength = 24) diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/SignatureAuditRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/SignatureAuditRequest.java index e37a467cb..a16759eb9 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/SignatureAuditRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/SignatureAuditRequest.java @@ -18,6 +18,8 @@ package com.wultra.security.powerauth.client.model.request; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; import lombok.Data; import java.util.Date; @@ -30,9 +32,18 @@ @Data public class SignatureAuditRequest { + @Schema(description = "The identifier of the user") + @NotBlank(message = "User ID must not be empty when requesting signature audit") private String userId; + + @Schema(description = "The identifier of the application") + @NotBlank(message = "Application ID must not be empty when requesting signature audit") private String applicationId; + + @Schema(description = "The timestamp from parameter in date filter") private Date timestampFrom; + + @Schema(description = "The timestamp to parameter in date filter") private Date timestampTo; } diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/UnblockActivationRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/UnblockActivationRequest.java index a82f8a792..bd9267495 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/UnblockActivationRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/UnblockActivationRequest.java @@ -31,7 +31,7 @@ public class UnblockActivationRequest { @Schema(description = "Activation identifier") - @NotBlank(message = "Activation ID must not be empty when blocking activation") + @NotBlank(message = "Activation ID must not be empty when unblocking activation") private String activationId; @Schema(description = "External user identifier") diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java index 3deacc7f0..cb8c0666f 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifyOfflineSignatureRequest.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.ToString; @@ -34,11 +35,23 @@ @Data public class VerifyOfflineSignatureRequest { + @Schema(description = "Activation identifier") + @NotBlank(message = "Activation ID must not be empty when verifying offline signature") private String activationId; + + @Schema(description = "Signed data") + @NotBlank(message = "Parameter data must not be empty when verifying signature") private String data; + + @Schema(description = "Signature") + @NotBlank(message = "Signature must not be empty when verifying signature") @ToString.Exclude private String signature; + + @Schema(description = "Signature component length") private BigInteger componentLength; + + @Schema(description = "Whether biometric factor is allowed") private boolean allowBiometry; @Schema(description = "Optional proximity check configuration of TOTP.") diff --git a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifySignatureRequest.java b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifySignatureRequest.java index cf9921bcf..6d48631bc 100644 --- a/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifySignatureRequest.java +++ b/powerauth-client-model/src/main/java/com/wultra/security/powerauth/client/model/request/VerifySignatureRequest.java @@ -19,6 +19,9 @@ package com.wultra.security.powerauth.client.model.request; import com.wultra.security.powerauth.client.model.enumeration.SignatureType; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.ToString; @@ -30,13 +33,32 @@ @Data public class VerifySignatureRequest { + @Schema(description = "Activation identifier") + @NotBlank(message = "Activation ID must not be empty when verifying signature") private String activationId; + + @Schema(description = "Application key") + @NotBlank(message = "Application key must not be empty when verifying signature") private String applicationKey; + + @Schema(description = "Signed data") + @NotBlank(message = "Parameter data must not be empty when verifying signature") private String data; + + @Schema(description = "Signature") + @NotBlank(message = "Signature must not be empty when verifying signature") @ToString.Exclude private String signature; + + @Schema(description = "Signature") + @NotNull(message = "Signature type must not be null when verifying signature") private SignatureType signatureType; + + @Schema(description = "Signature protocol version") + @NotBlank(message = "Signature version must not be empty when verifying signature") private String signatureVersion; + + @Schema(description = "Forced signature protocol version used during protocol upgrade") private Integer forcedSignatureVersion; } diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/SignatureController.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/SignatureController.java index 376e4bfe9..da6bcfc48 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/SignatureController.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/controller/api/SignatureController.java @@ -26,8 +26,10 @@ import io.getlime.security.powerauth.app.server.service.behavior.tasks.OfflineSignatureServiceBehavior; import io.getlime.security.powerauth.app.server.service.behavior.tasks.OnlineSignatureServiceBehavior; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -43,6 +45,7 @@ @RestController("signatureController") @RequestMapping("/rest/v3/signature") @Tag(name = "PowerAuth Signature Controller (V3)") +@Validated @Slf4j public class SignatureController { @@ -65,10 +68,13 @@ public SignatureController(OnlineSignatureServiceBehavior onlineSignatureService * @throws Exception In case the service throws exception. */ @PostMapping("/verify") - public ObjectResponse verifySignature(@RequestBody ObjectRequest request) throws Exception { - logger.info("VerifySignatureRequest received: {}", request); + public ObjectResponse verifySignature(@Valid @RequestBody ObjectRequest request) throws Exception { + final VerifySignatureRequest req = request.getRequestObject(); + logger.info("action: verifySignature, state: initiated, activationId: {}", req.getActivationId()); + logger.debug("action: verifySignature, state: initiated, request: {}", request); final ObjectResponse response = new ObjectResponse<>(onlineSignatureService.verifySignature(request.getRequestObject(), new ArrayList<>())); - logger.info("VerifySignatureRequest succeeded: {}", request); + logger.info("action: verifySignature, state: succeeded"); + logger.debug("action: verifySignature, state: succeeded, response: {}", response); return response; } @@ -80,10 +86,11 @@ public ObjectResponse verifySignature(@RequestBody Obje * @throws Exception In case the service throws exception. */ @PostMapping("/offline/personalized/create") - public ObjectResponse createPersonalizedOfflineSignaturePayload(@RequestBody ObjectRequest request) throws Exception { - logger.info("action: createPersonalizedOfflineSignaturePayload, state: initiated, activationId: {}", request.getRequestObject().getActivationId()); + public ObjectResponse createPersonalizedOfflineSignaturePayload(@Valid @RequestBody ObjectRequest request) throws Exception { + final CreatePersonalizedOfflineSignaturePayloadRequest req = request.getRequestObject(); + logger.info("action: createPersonalizedOfflineSignaturePayload, state: initiated, activationId: {}", req.getActivationId()); logger.debug("action: createPersonalizedOfflineSignaturePayload, state: initiated, {}", request); - final ObjectResponse response = new ObjectResponse<>(offlineSignatureService.createPersonalizedOfflineSignaturePayload(request.getRequestObject())); + final ObjectResponse response = new ObjectResponse<>(offlineSignatureService.createPersonalizedOfflineSignaturePayload(req)); logger.info("action: createPersonalizedOfflineSignaturePayload, state: succeeded"); logger.debug("action: createPersonalizedOfflineSignaturePayload, state: succeeded, {}", response); return response; @@ -97,12 +104,13 @@ public ObjectResponse createP * @throws Exception In case the service throws exception. */ @PostMapping("/offline/non-personalized/create") - public ObjectResponse createNonPersonalizedOfflineSignaturePayload(@RequestBody ObjectRequest request) throws Exception { - logger.info("action: createNonPersonalizedOfflineSignaturePayload state: initiated, activationId: {}", request.getRequestObject().getApplicationId()); - logger.debug("action: createNonPersonalizedOfflineSignaturePayload state: initiated, {}", request); - final ObjectResponse response = new ObjectResponse<>(offlineSignatureService.createNonPersonalizedOfflineSignaturePayload(request.getRequestObject())); - logger.info("action: createNonPersonalizedOfflineSignaturePayload state: succeeded"); - logger.debug("action: createNonPersonalizedOfflineSignaturePayload state: succeeded, {}", response); + public ObjectResponse createNonPersonalizedOfflineSignaturePayload(@Valid @RequestBody ObjectRequest request) throws Exception { + final CreateNonPersonalizedOfflineSignaturePayloadRequest req = request.getRequestObject(); + logger.info("action: createNonPersonalizedOfflineSignaturePayload, state: initiated, applicationId: {}", req.getApplicationId()); + logger.debug("action: createNonPersonalizedOfflineSignaturePayload, state: initiated, {}", request); + final ObjectResponse response = new ObjectResponse<>(offlineSignatureService.createNonPersonalizedOfflineSignaturePayload(req)); + logger.info("action: createNonPersonalizedOfflineSignaturePayload, state: succeeded"); + logger.debug("action: createNonPersonalizedOfflineSignaturePayload, state: succeeded, {}", response); return response; } @@ -114,10 +122,13 @@ public ObjectResponse crea * @throws Exception In case the service throws exception. */ @PostMapping("/offline/verify") - public ObjectResponse verifyOfflineSignature(@RequestBody ObjectRequest request) throws Exception { - logger.info("VerifyOfflineSignatureRequest received: {}", request); + public ObjectResponse verifyOfflineSignature(@Valid @RequestBody ObjectRequest request) throws Exception { + final VerifyOfflineSignatureRequest req = request.getRequestObject(); + logger.info("action: verifyOfflineSignature, state: initiated, activationId: {}", req.getActivationId()); + logger.debug("action: verifyOfflineSignature, state: initiated, {}", request); final ObjectResponse response = new ObjectResponse<>(offlineSignatureService.verifyOfflineSignature(request.getRequestObject())); - logger.info("VerifyOfflineSignatureRequest succeeded: {}", response); + logger.info("action: verifyOfflineSignature, state: succeeded"); + logger.debug("action: verifyOfflineSignature, state: succeeded, {}", response); return response; } @@ -129,7 +140,7 @@ public ObjectResponse verifyOfflineSignature(@Re * @throws Exception In case the service throws exception. */ @PostMapping("/list") - public ObjectResponse getSignatureAuditLog(@RequestBody ObjectRequest request) throws Exception { + public ObjectResponse getSignatureAuditLog(@Valid @RequestBody ObjectRequest request) throws Exception { logger.info("SignatureAuditRequest received: {}", request); final ObjectResponse response = new ObjectResponse<>(auditingService.getSignatureAuditLog(request.getRequestObject())); logger.info("SignatureAuditRequest succeeded: {}", response); diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java index 13e8276c7..a59d90719 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OfflineSignatureServiceBehavior.java @@ -33,7 +33,6 @@ import io.getlime.security.powerauth.app.server.database.model.entity.MasterKeyPairEntity; import io.getlime.security.powerauth.app.server.database.model.enumeration.ActivationStatus; import io.getlime.security.powerauth.app.server.database.model.enumeration.EncryptionMode; -import io.getlime.security.powerauth.app.server.database.repository.ActivationRepository; import io.getlime.security.powerauth.app.server.database.repository.ApplicationRepository; import io.getlime.security.powerauth.app.server.database.repository.MasterKeyPairRepository; import io.getlime.security.powerauth.app.server.service.exceptions.GenericServiceException; @@ -106,12 +105,6 @@ public class OfflineSignatureServiceBehavior { public VerifyOfflineSignatureResponse verifyOfflineSignature(final VerifyOfflineSignatureRequest request) throws GenericServiceException { try { - if (request.getActivationId() == null || request.getData() == null || request.getSignature() == null) { - logger.warn("Invalid request parameters in method verifyOfflineSignature"); - // Rollback is not required, error occurs before writing to database - throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST); - } - final String activationId = request.getActivationId(); final BigInteger componentLength = request.getComponentLength(); final List allowedSignatureTypes = new ArrayList<>(); // The order of signature types is important. PowerAuth server logs first found signature type @@ -158,12 +151,6 @@ public VerifyOfflineSignatureResponse verifyOfflineSignature(final VerifyOffline @Transactional public CreatePersonalizedOfflineSignaturePayloadResponse createPersonalizedOfflineSignaturePayload(final CreatePersonalizedOfflineSignaturePayloadRequest request) throws GenericServiceException { try { - if (request.getActivationId() == null || request.getData() == null) { - logger.warn("Invalid request parameters in method createPersonalizedOfflineSignaturePayload"); - // Rollback is not required, database is not used for writing - throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST); - } - // Fetch activation details from the repository final String activationId = request.getActivationId(); final ActivationRecordEntity activation = activationQueryService.findActivationWithoutLock(activationId).orElseThrow(() -> { @@ -260,12 +247,6 @@ public CreateNonPersonalizedOfflineSignaturePayloadResponse createNonPersonalize final String applicationId = request.getApplicationId(); final String data = request.getData(); - if (data == null) { - logger.warn("Invalid request parameter data in method createNonPersonalizedOfflineSignaturePayload"); - // Rollback is not required, database is not used for writing - throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST); - } - // Fetch associated master key pair data from the repository final Optional applicationEntityOptional = applicationRepository.findById(applicationId); if (applicationEntityOptional.isEmpty()) { diff --git a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OnlineSignatureServiceBehavior.java b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OnlineSignatureServiceBehavior.java index 681fcb5e1..94f5ccd58 100644 --- a/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OnlineSignatureServiceBehavior.java +++ b/powerauth-java-server/src/main/java/io/getlime/security/powerauth/app/server/service/behavior/tasks/OnlineSignatureServiceBehavior.java @@ -95,13 +95,6 @@ public VerifySignatureResponse verifySignature(VerifySignatureRequest request, L if (request.getForcedSignatureVersion() != null && request.getForcedSignatureVersion() == 3) { forcedSignatureVersion = 3; } - - if (activationId == null || applicationKey == null || dataString == null - || signature == null || signatureType == null || signatureVersion == null) { - logger.warn("Invalid request parameters in method verifySignature"); - // Rollback is not required, error occurs before writing to database - throw localizationProvider.buildExceptionForCode(ServiceError.INVALID_REQUEST); - } return verifySignatureImpl(activationId, signatureType, signature, signatureVersion, additionalInfo, dataString, applicationKey, forcedSignatureVersion, keyConvertor); } catch (InvalidKeySpecException | InvalidKeyException ex) { logger.error(ex.getMessage(), ex);