diff --git a/pom.xml b/pom.xml index 2a7c5143..42badbe2 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ 4.0.0 io.mosip.signup signup-parent - 1.2.1-SNAPSHOT + 0.0.1-SNAPSHOT pom esignet-signup Parent project of MOSIP e-Signet diff --git a/signup-service/.gitignore b/signup-service/.gitignore index 0b431265..f0f8877f 100644 --- a/signup-service/.gitignore +++ b/signup-service/.gitignore @@ -1,3 +1,56 @@ +# Miscellaneous +*.java.hsp +*.sonarj +*.sw* +.DS_Store +build.sh +integration-repo +ivy-cache +argfile* +activemq-data/ +classes/ + +# Log files +jxl.log +jmx.log +derby.log + +# Gradle artifacts +.gradle +.gradletasknamecache +/build +buildSrc/build +/spring-*/build +/framework-*/build +/integration-tests/build +/src/asciidoc/build +spring-test/test-output/ + +# Maven artifacts +pom.xml +/target/ + +# Eclipse artifacts, including WTP generated manifests +bin +.classpath +.project +.settings +.springBeans +spring-*/src/main/java/META-INF/MANIFEST.MF + +# IDEA artifacts and output dirs +*.iml +*.ipr +*.iws +.idea +out +test-output +atlassian-ide-plugin.xml + +# VS Code +.vscode/ + +cached-antora-playbook.yml # Compiled class file *.class diff --git a/signup-service/src/main/java/io/mosip/signup/config/ExceptionHandlerAdvice.java b/signup-service/src/main/java/io/mosip/signup/config/ExceptionHandlerAdvice.java index 1e374cac..98ff37d8 100644 --- a/signup-service/src/main/java/io/mosip/signup/config/ExceptionHandlerAdvice.java +++ b/signup-service/src/main/java/io/mosip/signup/config/ExceptionHandlerAdvice.java @@ -76,7 +76,7 @@ protected ResponseEntity handleTypeMismatch(TypeMismatchException ex, Ht @ExceptionHandler(value = { Exception.class, RuntimeException.class }) public ResponseEntity handleExceptions(Exception ex, WebRequest request) { - log.error("Unhandled exception encountered in handler advice", ex); + log.error("Exception encountered while serving request {}: ", request.getDescription(false), ex); if(ex instanceof MethodArgumentNotValidException) { List errors = new ArrayList<>(); diff --git a/signup-service/src/main/java/io/mosip/signup/controllers/CsrfController.java b/signup-service/src/main/java/io/mosip/signup/controllers/CsrfController.java new file mode 100644 index 00000000..74ca19d8 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/controllers/CsrfController.java @@ -0,0 +1,16 @@ +package io.mosip.signup.controllers; + +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/csrf") +public class CsrfController { + + @GetMapping("/token") + public CsrfToken getCsrfToken(CsrfToken csrfToken) { + return csrfToken; + } +} diff --git a/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java b/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java index 63a39cea..64bed9f1 100644 --- a/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java +++ b/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java @@ -6,6 +6,8 @@ import io.mosip.signup.dto.GenerateChallengeRequest; import io.mosip.signup.dto.GenerateChallengeResponse; import io.mosip.signup.dto.RegistrationStatusResponse; +import io.mosip.signup.dto.RegisterRequest; +import io.mosip.signup.dto.RegisterResponse; import io.mosip.signup.dto.VerifyChallengeRequest; import io.mosip.signup.dto.VerifyChallengeResponse; import io.mosip.signup.exception.SignUpException; @@ -27,6 +29,15 @@ public class RegistrationController { @Autowired RegistrationService registrationService; + @PostMapping("/generate-challenge") + public ResponseWrapper generateChallenge ( + @Valid @RequestBody RequestWrapper requestWrapper, @CookieValue(name = SignUpConstants.TRANSACTION_ID, defaultValue = "") String transactionId) throws SignUpException { + ResponseWrapper responseWrapper = new ResponseWrapper<>(); + responseWrapper.setResponse(registrationService.generateChallenge(requestWrapper.getRequest(), transactionId)); + responseWrapper.setResponseTime(IdentityProviderUtil.getUTCDateTime()); + return responseWrapper; + } + @PostMapping("/verify-challenge") public ResponseWrapper verifyChallenge(@Valid @RequestBody RequestWrapper requestWrapper, @CookieValue(SignUpConstants.TRANSACTION_ID) String transactionId) @@ -37,13 +48,14 @@ public ResponseWrapper verifyChallenge(@Valid @RequestB return responseWrapper; } - @PostMapping("/generate-challenge") - public ResponseWrapper generateChallenge ( - @Valid @RequestBody RequestWrapper requestWrapper, @CookieValue(name = SignUpConstants.TRANSACTION_ID, defaultValue = "") String transactionId) throws SignUpException { - ResponseWrapper responseWrapper = new ResponseWrapper<>(); - responseWrapper.setResponse(registrationService.generateChallenge(requestWrapper.getRequest(), transactionId)); + @PostMapping("/register") + public ResponseWrapper register(@Valid @RequestBody RequestWrapper requestWrapper, + @CookieValue(SignUpConstants.TRANSACTION_ID) String transactionId) + throws SignUpException { + ResponseWrapper responseWrapper = new ResponseWrapper<>(); + responseWrapper.setResponse(registrationService.register(requestWrapper.getRequest(), transactionId)); responseWrapper.setResponseTime(IdentityProviderUtil.getUTCDateTime()); - return responseWrapper; + return responseWrapper; } @GetMapping("/status") diff --git a/signup-service/src/main/java/io/mosip/signup/dto/AddIdentityRequest.java b/signup-service/src/main/java/io/mosip/signup/dto/AddIdentityRequest.java new file mode 100644 index 00000000..f16f1340 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/AddIdentityRequest.java @@ -0,0 +1,14 @@ +package io.mosip.signup.dto; + +import lombok.Data; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Size; +import java.io.Serializable; + +@Data +public class AddIdentityRequest implements Serializable { + + private String registrationId; + private Identity identity; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/AddIdentityResponse.java b/signup-service/src/main/java/io/mosip/signup/dto/AddIdentityResponse.java new file mode 100644 index 00000000..eb6b3477 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/AddIdentityResponse.java @@ -0,0 +1,12 @@ +package io.mosip.signup.dto; + +import lombok.Data; + +@Data +public class AddIdentityResponse { + + private String status; + private String identity; + private String documents; + private String verifiedAttributes; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/Identity.java b/signup-service/src/main/java/io/mosip/signup/dto/Identity.java new file mode 100644 index 00000000..881e9163 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/Identity.java @@ -0,0 +1,23 @@ +package io.mosip.signup.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class Identity implements Serializable { + + @JsonProperty("UIN") + private String UIN; + + @JsonProperty("IDSchemaVersion") + private float IDSchemaVersion; + + private List fullName; + private String phone; + private String preferredLang; + private Password password; + private String registrationType; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/LanguageTaggedValue.java b/signup-service/src/main/java/io/mosip/signup/dto/LanguageTaggedValue.java new file mode 100644 index 00000000..c126d24e --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/LanguageTaggedValue.java @@ -0,0 +1,19 @@ +package io.mosip.signup.dto; + +import io.mosip.signup.util.ErrorConstants; +import io.mosip.signup.validator.Language; +import lombok.AllArgsConstructor; +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +@Data +@AllArgsConstructor +public class LanguageTaggedValue { + + @Language + private String language; + + @NotBlank(message = ErrorConstants.INVALID_VALUE) + private String value; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/Password.java b/signup-service/src/main/java/io/mosip/signup/dto/Password.java new file mode 100644 index 00000000..e370e59b --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/Password.java @@ -0,0 +1,28 @@ +package io.mosip.signup.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class Password { + + private String hash; + private String salt; + + @Data + @AllArgsConstructor + public static class PasswordPlaintext{ + private String inputData; + } + + @Data + public static class PasswordHash { + private String hashValue; + private String salt; + } +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/RegisterRequest.java b/signup-service/src/main/java/io/mosip/signup/dto/RegisterRequest.java new file mode 100644 index 00000000..2a0aed12 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/RegisterRequest.java @@ -0,0 +1,28 @@ +package io.mosip.signup.dto; + +import io.mosip.signup.util.ErrorConstants; +import io.mosip.signup.validator.Password; +import io.mosip.signup.validator.Username; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +@Data +public class RegisterRequest { + + @Username + private String username; + + @Password + private String password; + + @NotBlank(message = ErrorConstants.INVALID_CONSENT) + @Pattern(message = ErrorConstants.INVALID_CONSENT, regexp = "^(DISAGREE)|(AGREE)$") + private String consent; + + @NotNull(message = ErrorConstants.INVALID_USERINFO) + private @Valid UserInfoMap userInfo; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/RegisterResponse.java b/signup-service/src/main/java/io/mosip/signup/dto/RegisterResponse.java new file mode 100644 index 00000000..e7bf516b --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/RegisterResponse.java @@ -0,0 +1,9 @@ +package io.mosip.signup.dto; + +import lombok.Data; + +@Data +public class RegisterResponse { + + private String status; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/RestError.java b/signup-service/src/main/java/io/mosip/signup/dto/RestError.java new file mode 100644 index 00000000..61bb81cc --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/RestError.java @@ -0,0 +1,13 @@ +package io.mosip.signup.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class RestError { + + private String errorCode; + private String message; + +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/RestRequestWrapper.java b/signup-service/src/main/java/io/mosip/signup/dto/RestRequestWrapper.java index 6e1d37e7..8f8a66f7 100644 --- a/signup-service/src/main/java/io/mosip/signup/dto/RestRequestWrapper.java +++ b/signup-service/src/main/java/io/mosip/signup/dto/RestRequestWrapper.java @@ -6,6 +6,7 @@ @Data public class RestRequestWrapper implements Serializable { + private String id; private String version; private String requesttime; diff --git a/signup-service/src/main/java/io/mosip/signup/dto/RestResponseWrapper.java b/signup-service/src/main/java/io/mosip/signup/dto/RestResponseWrapper.java index 36e0ec7e..782326e8 100644 --- a/signup-service/src/main/java/io/mosip/signup/dto/RestResponseWrapper.java +++ b/signup-service/src/main/java/io/mosip/signup/dto/RestResponseWrapper.java @@ -7,10 +7,11 @@ @Data public class RestResponseWrapper implements Serializable { + private String id; private String version; private String responsetime; private String metadata; private T response; - private ArrayList errors; + private ArrayList errors; } diff --git a/signup-service/src/main/java/io/mosip/signup/dto/UINResponse.java b/signup-service/src/main/java/io/mosip/signup/dto/UINResponse.java new file mode 100644 index 00000000..f3f47011 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/UINResponse.java @@ -0,0 +1,13 @@ +package io.mosip.signup.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +@Data +public class UINResponse implements Serializable { + + @JsonProperty("uin") + private String UIN; +} diff --git a/signup-service/src/main/java/io/mosip/signup/dto/UserInfoMap.java b/signup-service/src/main/java/io/mosip/signup/dto/UserInfoMap.java new file mode 100644 index 00000000..133bea08 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/dto/UserInfoMap.java @@ -0,0 +1,24 @@ +package io.mosip.signup.dto; + +import io.mosip.signup.util.ErrorConstants; +import io.mosip.signup.validator.PhoneNumber; +import io.mosip.signup.validator.Language; +import lombok.Data; + +import javax.validation.Valid; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.util.List; + +@Data +public class UserInfoMap { + + @PhoneNumber + private String phone; + + @NotEmpty(message = ErrorConstants.INVALID_FULLNAME) + private List<@Valid LanguageTaggedValue> fullName; + + @Language + private String preferredLang; +} diff --git a/signup-service/src/main/java/io/mosip/signup/exception/SignUpException.java b/signup-service/src/main/java/io/mosip/signup/exception/SignUpException.java index 7415dbfc..f914de4f 100644 --- a/signup-service/src/main/java/io/mosip/signup/exception/SignUpException.java +++ b/signup-service/src/main/java/io/mosip/signup/exception/SignUpException.java @@ -1,9 +1,12 @@ package io.mosip.signup.exception; import io.mosip.esignet.core.exception.EsignetException; +import io.mosip.signup.util.ErrorConstants; public class SignUpException extends EsignetException { + public SignUpException() {super(ErrorConstants.UNKNOWN_ERROR);} + public SignUpException(String errorCode){ super(errorCode); } diff --git a/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java b/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java index b0878578..713b8324 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java +++ b/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java @@ -8,32 +8,63 @@ import io.mosip.signup.exception.SignUpException; import io.mosip.signup.util.ActionStatus; import io.mosip.signup.util.ErrorConstants; +import io.mosip.signup.util.RegistrationStatus; +import io.mosip.signup.exception.CaptchaException; import io.mosip.signup.util.SignUpConstants; +import io.mosip.signup.exception.GenerateChallengeException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.client.RestTemplate; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; -import io.mosip.signup.exception.CaptchaException; -import io.mosip.signup.exception.GenerateChallengeException; +import static io.mosip.signup.util.SignUpConstants.CONSENT_DISAGREE; -@Service @Slf4j +@Service public class RegistrationService { @Autowired - CacheUtilService cacheUtilService; + private CacheUtilService cacheUtilService; @Autowired - HttpServletResponse response; + private GoogleRecaptchaValidatorService googleRecaptchaValidatorService; @Autowired - GoogleRecaptchaValidatorService googleRecaptchaValidatorService; + private ChallengeManagerService challengeManagerService; @Autowired - ChallengeManagerService challengeManagerService; + HttpServletResponse response; + + @Autowired + @Qualifier("selfTokenRestTemplate") + private RestTemplate selfTokenRestTemplate; + + @Value("${mosip.signup.id-schema.version}") + private float idSchemaVersion; + + @Value("${mosip.signup.add-identity.request.id}") + private String addIdentityRequestID; + + @Value("${mosip.signup.add-identity.request.version}") + private String addIdentityRequestVersion; + + @Value("${mosip.signup.add-identity.endpoint}") + private String addIdentityEndpoint; + + @Value("${mosip.signup.generate-hash.endpoint}") + private String generateHashEndpoint; + + @Value("${mosip.signup.get-uin.endpoint}") + private String getUinEndpoint; @Value("${mosip.signup.cookie.max-age}") private int cookieMaxAge; @@ -46,31 +77,142 @@ public class RegistrationService { public VerifyChallengeResponse verifyChallenge(VerifyChallengeRequest verifyChallengeRequest, String transactionId) throws SignUpException { + + log.debug("Transaction {} : start verify challenge", transactionId); RegistrationTransaction transaction = cacheUtilService.getChallengeGeneratedTransaction(transactionId); - if(transaction == null) { - log.error("verify-challenge failed: transaction null"); + if(transaction == null){ + log.error("Transaction {} : not found in ChallengeGeneratedTransaction cache", transactionId); throw new InvalidTransactionException(); } if(!verifyChallengeRequest.getIdentifier().equals(transaction.getIdentifier())) { - log.error("verify-challenge failed: invalid identifier"); + log.error("Transaction {} : contain identifier not the same with identifier user request", transactionId); throw new InvalidIdentifierException(); } - String challengeHash = IdentityProviderUtil.generateB64EncodedHash(IdentityProviderUtil.ALGO_SHA3_256, verifyChallengeRequest.getChallengeInfo().getChallenge()); if(!challengeHash.equals(transaction.getChallengeHash())) { - log.error("verify-challenge failed: challenge not match"); + log.error("Transaction {} : challenge not match", transactionId); throw new ChallengeFailedException(); } - cacheUtilService.setChallengeVerifiedTransaction(transactionId, transaction); VerifyChallengeResponse verifyChallengeResponse = new VerifyChallengeResponse(); verifyChallengeResponse.setStatus(ActionStatus.SUCCESS); + log.debug("Transaction {} : verify challenge status {}", transactionId, ActionStatus.SUCCESS); return verifyChallengeResponse; } - public GenerateChallengeResponse generateChallenge( - GenerateChallengeRequest generateChallengeRequest, String transactionId) - throws SignUpException { + public RegisterResponse register(RegisterRequest registerRequest, String transactionId) throws SignUpException { + + log.debug("Transaction {} : start do registration", transactionId); + RegistrationTransaction transaction = cacheUtilService.getChallengeVerifiedTransaction(transactionId); + if(transaction == null) { + log.error("Transaction {} : not found in ChallengeVerifiedTransaction cache", transactionId); + throw new InvalidTransactionException(); + } + if(!registerRequest.getUsername().equals(registerRequest.getUserInfo().getPhone())) { + log.error("Transaction {} : given unsupported username in L1", transactionId); + throw new SignUpException(ErrorConstants.UNSUPPORTED_USERNAME); + } + if(registerRequest.getConsent().equals(CONSENT_DISAGREE)) { + log.error("Transaction {} : disagrees consent", transactionId); + throw new SignUpException(ErrorConstants.CONSENT_REQUIRED); + } + + saveIdentityData(registerRequest, transactionId, transaction.getApplicationId()); + + transaction.setRegistrationStatus(RegistrationStatus.PENDING); + cacheUtilService.setRegisteredTransaction(transactionId, transaction); + + RegisterResponse registration = new RegisterResponse(); + registration.setStatus(ActionStatus.PENDING); + log.debug("Transaction {} : registration status {}", transactionId, RegistrationStatus.PENDING); + return registration; + } + + private void saveIdentityData(RegisterRequest registerRequest, String transactionId, String applicationId) throws SignUpException{ + + UserInfoMap userInfoMap = registerRequest.getUserInfo(); + + Identity identity = new Identity(); + identity.setPreferredLang(userInfoMap.getPreferredLang()); + identity.setPhone(userInfoMap.getPhone()); + identity.setFullName(userInfoMap.getFullName()); + identity.setIDSchemaVersion(idSchemaVersion); + identity.setRegistrationType("L1"); + + String uin = getUniqueIdentifier(transactionId); + identity.setUIN(uin); + + Password password = generateSaltedHash(registerRequest.getPassword(), transactionId); + identity.setPassword(password); + + AddIdentityRequest addIdentityRequest = new AddIdentityRequest(); + addIdentityRequest.setRegistrationId(applicationId); + addIdentityRequest.setIdentity(identity); + + addIdentity(addIdentityRequest, transactionId); + } + + private void addIdentity(AddIdentityRequest addIdentityRequest, String transactionId) throws SignUpException{ + + RestRequestWrapper restRequest = new RestRequestWrapper<>(); + restRequest.setId(addIdentityRequestID); + restRequest.setVersion(addIdentityRequestVersion); + restRequest.setRequesttime(IdentityProviderUtil.getUTCDateTime()); + restRequest.setRequest(addIdentityRequest); + + log.debug("Transaction {} : start add identity", transactionId); + HttpEntity> resReq = new HttpEntity<>(restRequest); + RestResponseWrapper restResponseWrapper = selfTokenRestTemplate.exchange(addIdentityEndpoint, HttpMethod.POST, resReq, new ParameterizedTypeReference>() {}).getBody(); + + if (restResponseWrapper != null && restResponseWrapper.getResponse() != null && + restResponseWrapper.getResponse().getStatus().equals("ACTIVATED")) { + return; + } + + log.error("Transaction {} : Add identity failed with response {}", transactionId, restResponseWrapper); + throw new SignUpException(restResponseWrapper != null && !CollectionUtils.isEmpty(restResponseWrapper.getErrors()) ? + restResponseWrapper.getErrors().get(0).getErrorCode() : ErrorConstants.ADD_IDENTITY_FAILED); + } + + private Password generateSaltedHash(String password, String transactionId) throws SignUpException{ + + RestRequestWrapper restRequestWrapper = new RestRequestWrapper<>(); + restRequestWrapper.setRequesttime(IdentityProviderUtil.getUTCDateTime()); + restRequestWrapper.setRequest(new Password.PasswordPlaintext(password)); + + HttpEntity> resReq = new HttpEntity<>(restRequestWrapper); + log.debug("Transaction {} : Generate salted hash started", transactionId); + RestResponseWrapper restResponseWrapper = selfTokenRestTemplate.exchange(generateHashEndpoint, HttpMethod.POST, resReq, new ParameterizedTypeReference>(){}).getBody(); + + if (restResponseWrapper != null && restResponseWrapper.getResponse() != null && + !StringUtils.isEmpty(restResponseWrapper.getResponse().getHashValue()) && + !StringUtils.isEmpty(restResponseWrapper.getResponse().getSalt())) { + return new Password(restResponseWrapper.getResponse().getHashValue(), + restResponseWrapper.getResponse().getSalt()); + } + + log.error("Transaction {} : Generate salted hash failed with response {}", transactionId, restResponseWrapper); + throw new SignUpException(restResponseWrapper != null && !CollectionUtils.isEmpty(restResponseWrapper.getErrors()) ? + restResponseWrapper.getErrors().get(0).getErrorCode() : ErrorConstants.HASH_GENERATE_FAILED); + } + + private String getUniqueIdentifier(String transactionId) throws SignUpException { + + RestResponseWrapper restResponseWrapper = selfTokenRestTemplate.exchange(getUinEndpoint, + HttpMethod.GET, null, + new ParameterizedTypeReference>() {}).getBody(); + + if (restResponseWrapper != null && restResponseWrapper.getResponse() != null && + !StringUtils.isEmpty(restResponseWrapper.getResponse().getUIN()) ) { + return restResponseWrapper.getResponse().getUIN(); + } + + log.error("Transaction {} : Get unique identifier failed with response {}", transactionId, restResponseWrapper); + throw new SignUpException(restResponseWrapper != null && !CollectionUtils.isEmpty(restResponseWrapper.getErrors()) ? + restResponseWrapper.getErrors().get(0).getErrorCode() : ErrorConstants.GET_UIN_FAILED); + } + + public GenerateChallengeResponse generateChallenge(GenerateChallengeRequest generateChallengeRequest, String transactionId) throws SignUpException { if (!googleRecaptchaValidatorService.validateCaptcha(generateChallengeRequest.getCaptchaToken())) { log.error("generate-challenge failed: invalid captcha"); throw new CaptchaException(ErrorConstants.INVALID_CAPTCHA); @@ -131,7 +273,7 @@ private void validateTransaction(RegistrationTransaction transaction, String ide if(transaction.getLastRetryToNow() <= resendDelay) { log.error("generate-challenge failed: too early attempts"); - throw new GenerateChallengeException(ErrorConstants.TOO_EARLY_ATTEMPTS); + throw new GenerateChallengeException(ErrorConstants.ACTIVE_CHALLENGE_FOUND); } } diff --git a/signup-service/src/main/java/io/mosip/signup/util/ActionStatus.java b/signup-service/src/main/java/io/mosip/signup/util/ActionStatus.java index 22833d1f..524fe60e 100644 --- a/signup-service/src/main/java/io/mosip/signup/util/ActionStatus.java +++ b/signup-service/src/main/java/io/mosip/signup/util/ActionStatus.java @@ -2,4 +2,5 @@ public class ActionStatus { public static final String SUCCESS = "SUCCESS"; + public static final String PENDING = "PENDING"; } diff --git a/signup-service/src/main/java/io/mosip/signup/util/ErrorConstants.java b/signup-service/src/main/java/io/mosip/signup/util/ErrorConstants.java index 99350c51..788da063 100644 --- a/signup-service/src/main/java/io/mosip/signup/util/ErrorConstants.java +++ b/signup-service/src/main/java/io/mosip/signup/util/ErrorConstants.java @@ -4,6 +4,12 @@ public class ErrorConstants { public static final String CHALLENGE_FAILED = "challenge_failed"; public static final String INVALID_CHALLENGE_INFO = "invalid_challenge_info"; public static final String INVALID_IDENTIFIER = "invalid_identifier"; + public static final String INVALID_USERNAME = "invalid_username"; + public static final String INVALID_PASSWORD = "invalid_password"; + public static final String INVALID_USERINFO = "invalid_username"; + public static final String INVALID_CONSENT = "invalid_consent"; + public static final String UNSUPPORTED_USERNAME = "unsupported_username"; + public static final String CONSENT_REQUIRED = "consent_required"; public static final String INVALID_TRANSACTION="invalid_transaction"; public static final String INVALID_CHALLENGE_CHANNEL ="invalid_challenge_channel"; public static final String INVALID_CAPTCHA="invalid_captcha"; @@ -11,8 +17,15 @@ public class ErrorConstants { public static final String ACTIVE_CHALLENGE_FOUND="active_challenge_found"; public static final String UNKNOWN_ERROR="unknown_error"; public static final String TOO_MANY_ATTEMPTS="too_many_attempts"; - public static final String TOO_EARLY_ATTEMPTS="too_early_attempts"; public static final String INVALID_REQUEST="invalid_request"; public static final String INVALID_CHALLENGE="invalid_challenge"; public static final String INVALID_CHALLENGE_FORMAT="invalid_challenge_format"; + public static final String INVALID_PHONE_NUMBER = "invalid_phone_number"; + public static final String UNSUPPORTED_LANGUAGE = "unsupported_language"; + public static final String INVALID_VALUE = "invalid_value"; + public static final String ADD_IDENTITY_FAILED = "add_identity_failed"; + public static final String HASH_GENERATE_FAILED = "hash_generate_failed"; + public static final String GET_UIN_FAILED = "get_uin_failed"; + public static final String INVALID_FULLNAME = "invalid_fullname"; + } diff --git a/signup-service/src/main/java/io/mosip/signup/util/SignUpConstants.java b/signup-service/src/main/java/io/mosip/signup/util/SignUpConstants.java index 1b973268..61d340ef 100644 --- a/signup-service/src/main/java/io/mosip/signup/util/SignUpConstants.java +++ b/signup-service/src/main/java/io/mosip/signup/util/SignUpConstants.java @@ -5,4 +5,5 @@ public class SignUpConstants { public static final String CHALLENGE_VERIFIED = "challenge_verified"; public static final String REGISTERED_CACHE = "registered"; public static final String TRANSACTION_ID = "TRANSACTION_ID"; + public static final String CONSENT_DISAGREE = "DISAGREE"; } diff --git a/signup-service/src/main/java/io/mosip/signup/validator/Language.java b/signup-service/src/main/java/io/mosip/signup/validator/Language.java new file mode 100644 index 00000000..1df8b9ff --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/Language.java @@ -0,0 +1,23 @@ +package io.mosip.signup.validator; + +import io.mosip.signup.util.ErrorConstants; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = LanguageValidator.class) +@Documented +public @interface Language { + String message() default ErrorConstants.UNSUPPORTED_LANGUAGE; + Class[] groups() default { }; + Class[] payload() default { }; +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/LanguageValidator.java b/signup-service/src/main/java/io/mosip/signup/validator/LanguageValidator.java new file mode 100644 index 00000000..f76e976e --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/LanguageValidator.java @@ -0,0 +1,21 @@ +package io.mosip.signup.validator; + +import org.springframework.beans.factory.annotation.Value; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.List; + +public class LanguageValidator implements ConstraintValidator { + + + @Value("#{${mosip.signup.supported-languages}}") + private List supportedLanguages; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if(value == null || value.isBlank()) + return false; + return supportedLanguages.contains(value); + } +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/Password.java b/signup-service/src/main/java/io/mosip/signup/validator/Password.java new file mode 100644 index 00000000..17138e34 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/Password.java @@ -0,0 +1,23 @@ +package io.mosip.signup.validator; + +import io.mosip.signup.util.ErrorConstants; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = PasswordValidator.class) +@Documented +public @interface Password { + String message() default ErrorConstants.INVALID_PASSWORD; + Class[] groups() default { }; + Class[] payload() default { }; +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/PasswordValidator.java b/signup-service/src/main/java/io/mosip/signup/validator/PasswordValidator.java new file mode 100644 index 00000000..99542a75 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/PasswordValidator.java @@ -0,0 +1,19 @@ +package io.mosip.signup.validator; + +import org.springframework.beans.factory.annotation.Value; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class PasswordValidator implements ConstraintValidator { + + @Value("#{${mosip.signup.password.max-length}}") + private Integer maxLengthPassword; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if(value == null || value.isBlank()) + return false; + return value.length() <= maxLengthPassword; + } +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/PhoneNumber.java b/signup-service/src/main/java/io/mosip/signup/validator/PhoneNumber.java new file mode 100644 index 00000000..bcc8f533 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/PhoneNumber.java @@ -0,0 +1,23 @@ +package io.mosip.signup.validator; + +import io.mosip.signup.util.ErrorConstants; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = PhoneNumberValidator.class) +@Documented +public @interface PhoneNumber { + String message() default ErrorConstants.INVALID_PHONE_NUMBER; + Class[] groups() default { }; + Class[] payload() default { }; +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/PhoneNumberValidator.java b/signup-service/src/main/java/io/mosip/signup/validator/PhoneNumberValidator.java new file mode 100644 index 00000000..327566e0 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/PhoneNumberValidator.java @@ -0,0 +1,19 @@ +package io.mosip.signup.validator; + +import org.springframework.beans.factory.annotation.Value; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class PhoneNumberValidator implements ConstraintValidator { + + @Value("${mosip.signup.identifier.regex}") + private String phoneNumberRegex; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if(value == null || value.isBlank()) + return false; + return value.matches(phoneNumberRegex); + } +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/Username.java b/signup-service/src/main/java/io/mosip/signup/validator/Username.java new file mode 100644 index 00000000..2e17415e --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/Username.java @@ -0,0 +1,23 @@ +package io.mosip.signup.validator; + +import io.mosip.signup.util.ErrorConstants; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = UsernameValidator.class) +@Documented +public @interface Username { + String message() default ErrorConstants.INVALID_USERNAME; + Class[] groups() default { }; + Class[] payload() default { }; +} diff --git a/signup-service/src/main/java/io/mosip/signup/validator/UsernameValidator.java b/signup-service/src/main/java/io/mosip/signup/validator/UsernameValidator.java new file mode 100644 index 00000000..fd88ff11 --- /dev/null +++ b/signup-service/src/main/java/io/mosip/signup/validator/UsernameValidator.java @@ -0,0 +1,19 @@ +package io.mosip.signup.validator; + +import org.springframework.beans.factory.annotation.Value; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; + +public class UsernameValidator implements ConstraintValidator { + + @Value("${mosip.signup.identifier.regex}") + private String usernameRegex; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if(value == null || value.isBlank()) + return false; + return value.matches(usernameRegex); + } +} diff --git a/signup-service/src/main/resources/application-default.properties b/signup-service/src/main/resources/application-default.properties index 1f37e94b..eaae50c2 100644 --- a/signup-service/src/main/resources/application-default.properties +++ b/signup-service/src/main/resources/application-default.properties @@ -66,8 +66,15 @@ mosip.service.exclude.auth.allowed.method=GET,POST ## --------------------------------- mosip.signup.generate-challenge.endpoint=${mosip.kernel.authmanager.url}/v1/otpmanager/otp/generate +mosip.signup.add-identity.endpoint=https://api-internal.camdgc-dev.mosip.net/idrepository/v1/identity/ +mosip.signup.generate-hash.endpoint=https://api-internal.camdgc-dev.mosip.net/v1/keymanager/generateArgon2Hash +mosip.signup.get-uin.endpoint=https://api-internal.camdgc-dev.mosip.net/v1/idgenerator/uin +## --------------------------------- mosip.signup.supported.challenge-format-types={'alpha-numeric'} -# captcha validator +mosip.signup.id-schema.version=0.2 +mosip.signup.add-identity.request.id=mosip.id.create +mosip.signup.add-identity.request.version=v1 +# captcha validator----------------- mosip.signup.send-challenge.captcha-required=false mosip.signup.integration.captcha-validator=GoogleRecaptchaValidatorService mosip.signup.captcha-validator.url=https://www.google.com/recaptcha/api/siteverify @@ -77,6 +84,8 @@ mosip.signup.cookie.max-age=60 mosip.signup.challenge.resend-attempt=3 mosip.signup.challenge.resend-delay=30 mosip.signup.identifier.regex=\\+855\\d{8,9} +mosip.signup.supported-languages={'khm', 'eng'} +mosip.signup.password.max-length=20 ## --------------------------------- mosip.signup.ui.config.key-values={\ \'identifier.pattern': '\\d{8,9}', \ diff --git a/signup-service/src/test/java/io/mosip/signup/controllers/CsrfControllerTest.java b/signup-service/src/test/java/io/mosip/signup/controllers/CsrfControllerTest.java new file mode 100644 index 00000000..56083a50 --- /dev/null +++ b/signup-service/src/test/java/io/mosip/signup/controllers/CsrfControllerTest.java @@ -0,0 +1,34 @@ +package io.mosip.signup.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.DefaultCsrfToken; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(value = CsrfController.class, excludeAutoConfiguration = {SecurityAutoConfiguration.class}) +class CsrfControllerTest { + + @Autowired + MockMvc mockMvc; + + ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void getCsrfToken_withValidToken_returnSuccessResponse() throws Exception, JsonProcessingException { + CsrfToken csrfToken = new DefaultCsrfToken("headerName", "parameterName", "token"); + mockMvc.perform(get("/csrf/token").content(objectMapper.writeValueAsString(csrfToken)) + .contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); + } +} diff --git a/signup-service/src/test/java/io/mosip/signup/controllers/RegistrationControllerTest.java b/signup-service/src/test/java/io/mosip/signup/controllers/RegistrationControllerTest.java index dcd5fdf5..b99fc76c 100644 --- a/signup-service/src/test/java/io/mosip/signup/controllers/RegistrationControllerTest.java +++ b/signup-service/src/test/java/io/mosip/signup/controllers/RegistrationControllerTest.java @@ -8,13 +8,13 @@ import io.mosip.signup.exception.ChallengeFailedException; import io.mosip.signup.exception.InvalidIdentifierException; import io.mosip.signup.exception.InvalidTransactionException; -import io.mosip.signup.services.CacheUtilService; import io.mosip.signup.services.RegistrationService; import io.mosip.signup.util.ActionStatus; import io.mosip.signup.util.ErrorConstants; import io.mosip.signup.util.SignUpConstants; import org.junit.Before; import io.mosip.signup.util.RegistrationStatus; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -31,6 +31,9 @@ import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -50,9 +53,6 @@ public class RegistrationControllerTest { @MockBean RegistrationService registrationService; - @MockBean - CacheUtilService cacheUtilService; - ObjectMapper objectMapper = new ObjectMapper(); private GenerateChallengeRequest generateChallengeRequest; @@ -238,7 +238,6 @@ public void doVerifyChallenge_withoutIdentifier_returnErrorResponse() throws Exc registrationTransaction.setChallengeHash("mock"); registrationTransaction.setIdentifier("mock"); - when(cacheUtilService.getChallengeGeneratedTransaction(mockTransactionID)).thenReturn(null); when(registrationService.verifyChallenge(verifyChallengeRequest, mockTransactionID)).thenThrow(new InvalidIdentifierException()); mockMvc.perform(post("/registration/verify-challenge") @@ -258,7 +257,6 @@ public void doVerifyChallenge_withInvalidTransaction_returnErrorResponse() throw registrationTransaction.setChallengeHash("mock"); registrationTransaction.setIdentifier("mock"); - when(cacheUtilService.getChallengeGeneratedTransaction(mockTransactionID)).thenReturn(null); when(registrationService.verifyChallenge(verifyChallengeRequest, mockTransactionID)).thenThrow(new InvalidTransactionException()); mockMvc.perform(post("/registration/verify-challenge") @@ -278,7 +276,6 @@ public void doVerifyChallenge_withVerifyChallengeRaiseChallengeFailedException_r registrationTransaction.setChallengeHash("mock"); registrationTransaction.setIdentifier("mock"); - when(cacheUtilService.getChallengeGeneratedTransaction(mockTransactionID)).thenReturn(null); when(registrationService.verifyChallenge(verifyChallengeRequest, mockTransactionID)).thenThrow(new ChallengeFailedException()); mockMvc.perform(post("/registration/verify-challenge") @@ -298,7 +295,6 @@ public void doVerifyChallenge_withVerifyChallengeRaiseInvalidIdentifierException registrationTransaction.setChallengeHash("mock"); registrationTransaction.setIdentifier("mock"); - when(cacheUtilService.getChallengeGeneratedTransaction(mockTransactionID)).thenReturn(null); when(registrationService.verifyChallenge(verifyChallengeRequest, mockTransactionID)).thenThrow(new InvalidIdentifierException()); mockMvc.perform(post("/registration/verify-challenge") @@ -324,7 +320,6 @@ public void doVerifyChallenge_withMultipleInvalidRequest_returnErrorResponse() t registrationTransaction.setChallengeHash("mock"); registrationTransaction.setIdentifier("mock"); - when(cacheUtilService.getChallengeGeneratedTransaction(mockTransactionID)).thenReturn(null); when(registrationService.verifyChallenge(verifyChallengeRequest, mockTransactionID)).thenThrow(new InvalidIdentifierException()); mockMvc.perform(post("/registration/verify-challenge") @@ -439,4 +434,492 @@ public void doGetRegistrationStatus_returnFailedResponse() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.response.status").value("FAILED")); } + +// Register endpoint + @Test + public void register_thenPass() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + RegisterResponse registerResponse = new RegisterResponse(); + registerResponse.setStatus(ActionStatus.PENDING); + when(registrationService.register(registerRequest, mockTransactionID)).thenReturn(registerResponse); + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response.status").value("PENDING")); + } + + @Test + public void register_withBlankConsent_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("eng", "Panharith AN")); + fullNames.add(new LanguageTaggedValue("khm", "Panharith AN")); + userInfo.setFullName(fullNames); + userInfo.setPhone("+85512345678"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.consent: invalid_consent")); + } + + @Test + public void register_withUnsupportedConsent_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("not agree"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.consent: invalid_consent")); + } + + @Test + public void register_withInvalidPhoneNumber_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "Panharith AN"))); + userInfo.setPhone("+8551234567890"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.phone: invalid_phone_number")); + } + + @Test + public void register_withBlankPhoneNumber_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "Panharith AN"))); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.phone: invalid_phone_number")); + } + + @Test + public void register_withBlankPreferredLang_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "Panharith AN"))); + userInfo.setPhone("+855123456789"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.preferredLang: unsupported_language")); + } + + @Test + public void register_withUnsupportedPreferredLang_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "Panharith AN"))); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("usa"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.preferredLang: unsupported_language")); + } + + @Test + public void register_withNullFullName_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.fullName: invalid_fullname")); + } + + @Test + @Ignore + public void register_withInvalidFullNameInKhm_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("khm", "qkITAu9BW5hfiZcLCwPuefQqu6QIthy2J9R"))); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.fullName: invalid_fullname")); + } + + @Test + @Ignore + public void register_withValidFullNameInKhmAndInvalidFullNameInEng_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("khm", "Mengleang Ngoun")); + fullNames.add(new LanguageTaggedValue("eng", "qkITAu9BW5hfiZcLCwPuefQqu6QIthy2J9R")); + userInfo.setFullName(fullNames); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.userInfo.fullName: invalid_fullname")); + } + + @Test + public void register_withValidFullNameInKhmAndFullNameInEng_returnSuccessResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("khm", "Mengleang Ngoun")); + fullNames.add(new LanguageTaggedValue("eng", "Mengleang Ngoun")); + userInfo.setFullName(fullNames); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + RegisterResponse registerResponse = new RegisterResponse(); + registerResponse.setStatus(ActionStatus.PENDING); + when(registrationService.register(registerRequest, mockTransactionID)).thenReturn(registerResponse); + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response.status").value("PENDING")); + } + + @Test + public void register_withInvalidPassword_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("khm", "Mengleang Ngoun")); + fullNames.add(new LanguageTaggedValue("eng", "Mengleang Ngoun")); + userInfo.setFullName(fullNames); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setPassword("qkITAu9BW5hfiZcLCwPuefQqu6QIthy2J9R"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + RegisterResponse registerResponse = new RegisterResponse(); + registerResponse.setStatus(ActionStatus.PENDING); + when(registrationService.register(registerRequest, mockTransactionID)).thenReturn(registerResponse); + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.password: invalid_password")); + } + + @Test + public void register_withBlankPassword_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("khm", "Mengleang Ngoun")); + fullNames.add(new LanguageTaggedValue("eng", "Mengleang Ngoun")); + userInfo.setFullName(fullNames); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+85512345678"); + registerRequest.setConsent("AGREE"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + RegisterResponse registerResponse = new RegisterResponse(); + registerResponse.setStatus(ActionStatus.PENDING); + when(registrationService.register(registerRequest, mockTransactionID)).thenReturn(registerResponse); + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.password: invalid_password")); + } + + @Test + public void register_withBlankUsername_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("khm", "Mengleang Ngoun")); + fullNames.add(new LanguageTaggedValue("eng", "Mengleang Ngoun")); + userInfo.setFullName(fullNames); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setConsent("AGREE"); + registerRequest.setPassword("12345678"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + RegisterResponse registerResponse = new RegisterResponse(); + registerResponse.setStatus(ActionStatus.PENDING); + when(registrationService.register(registerRequest, mockTransactionID)).thenReturn(registerResponse); + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.username: invalid_username")); + } + + @Test + public void register_withNotMatchUsernameRegex_returnErrorResponse() throws Exception{ + + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPhone("+855123456789"); + userInfo.setPreferredLang("khm"); + List fullNames = new ArrayList<>(); + fullNames.add(new LanguageTaggedValue("khm", "Mengleang Ngoun")); + fullNames.add(new LanguageTaggedValue("eng", "Mengleang Ngoun")); + userInfo.setFullName(fullNames); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setConsent("AGREE"); + registerRequest.setUsername("+8551234567890"); + registerRequest.setPassword("12345678"); + + RequestWrapper wrapper = new RequestWrapper(); + wrapper.setRequestTime(IdentityProviderUtil.getUTCDateTime()); + wrapper.setRequest(registerRequest); + + String mockTransactionID = "123456789"; + + RegisterResponse registerResponse = new RegisterResponse(); + registerResponse.setStatus(ActionStatus.PENDING); + when(registrationService.register(registerRequest, mockTransactionID)).thenReturn(registerResponse); + + mockMvc.perform(post("/registration/register") + .content(objectMapper.writeValueAsString(wrapper)) + .cookie(new Cookie("TRANSACTION_ID", mockTransactionID)) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.response").isEmpty()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorMessage").value("request.username: invalid_username")); + } } diff --git a/signup-service/src/test/java/io/mosip/signup/services/RegistrationServiceTest.java b/signup-service/src/test/java/io/mosip/signup/services/RegistrationServiceTest.java index fd49e9c1..01f8719e 100644 --- a/signup-service/src/test/java/io/mosip/signup/services/RegistrationServiceTest.java +++ b/signup-service/src/test/java/io/mosip/signup/services/RegistrationServiceTest.java @@ -1,21 +1,35 @@ package io.mosip.signup.services; -import com.fasterxml.jackson.databind.ObjectMapper; import io.mosip.esignet.core.util.IdentityProviderUtil; import io.mosip.signup.dto.*; import io.mosip.signup.exception.*; import io.mosip.signup.exception.ChallengeFailedException; import io.mosip.signup.exception.InvalidIdentifierException; import io.mosip.signup.exception.InvalidTransactionException; +import io.mosip.signup.util.ErrorConstants; import io.mosip.signup.util.RegistrationStatus; +import io.mosip.signup.exception.SignUpException; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; -import org.junit.Before; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -23,17 +37,21 @@ import io.mosip.esignet.core.exception.EsignetException; -import java.io.IOException; import java.time.LocalDateTime; -@RunWith(MockitoJUnitRunner.class) +@RunWith(SpringRunner.class) +@TestPropertySource(locations = "classpath:application-test.properties") public class RegistrationServiceTest { + @InjectMocks RegistrationService registrationService; @Mock CacheUtilService cacheUtilService; + @Mock + RestTemplate selfTokenRestTemplate; + @Mock ChallengeManagerService challengeManagerService; @@ -43,10 +61,15 @@ public class RegistrationServiceTest { @Mock GoogleRecaptchaValidatorService googleRecaptchaValidatorService; - ObjectMapper objectMapper = new ObjectMapper(); + private final String addIdentityEndpoint = "addIdentityEndpoint"; + private final String generateHashEndpoint = "generateHashEndpoint"; + private final String getUinEndpoint = "getUinEndpoint"; @Before - public void setUp() throws IOException { + public void setUp(){ + ReflectionTestUtils.setField(registrationService, addIdentityEndpoint, addIdentityEndpoint); + ReflectionTestUtils.setField(registrationService, generateHashEndpoint, generateHashEndpoint); + ReflectionTestUtils.setField(registrationService, getUinEndpoint, getUinEndpoint); ReflectionTestUtils.setField( registrationService, "resendAttempts", 3); ReflectionTestUtils.setField( @@ -145,6 +168,455 @@ public void doVerifyChallenge_withIdentifierNotMatch_throwsException() throws Ex } } + // ## register--------------------------------- + @Test + public void register_thenPass() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(userInfo.getPhone(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + AddIdentityResponse addIdentityResponse = new AddIdentityResponse(); + addIdentityResponse.setStatus("ACTIVATED"); + UINResponse uinResponse = new UINResponse(); + uinResponse.setUIN("mockUIN"); + Password.PasswordHash passwordHash = new Password.PasswordHash(); + passwordHash.setSalt("mockSalt"); + passwordHash.setHashValue("mockHashValue"); + + RestResponseWrapper mockRestResponseWrapperAddIdentityResponse = new RestResponseWrapper(); + mockRestResponseWrapperAddIdentityResponse.setResponse(addIdentityResponse); + RestResponseWrapper mockRestResponseWrapperUINResponse = new RestResponseWrapper(); + mockRestResponseWrapperUINResponse.setResponse(uinResponse); + RestResponseWrapper mockRestResponseWrapperPasswordHash = new RestResponseWrapper(); + mockRestResponseWrapperPasswordHash.setResponse(passwordHash); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperUINResponse, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(generateHashEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperPasswordHash, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(addIdentityEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperAddIdentityResponse, HttpStatus.OK)); + + RegisterResponse registerResponse = registrationService.register(registerRequest, mockTransactionID); + Assert.assertNotNull(registerResponse); + Assert.assertEquals("PENDING", registerResponse.getStatus()); + } + + @Test + public void register_thenUinEndpointResponseNullBody_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(null, HttpStatus.OK)); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals(ErrorConstants.GET_UIN_FAILED, signUpException.getErrorCode()); + } + } + + @Test + public void register_thenUinEndpointResponseErrors_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + RestResponseWrapper mockRestResponseWrapperUINResponse = new RestResponseWrapper<>(); + ArrayList errors = new ArrayList<>(); + errors.add(new RestError("server_error", "server_error")); + mockRestResponseWrapperUINResponse.setErrors(errors); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperUINResponse, HttpStatus.OK)); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals("server_error", signUpException.getErrorCode()); + } + } + + @Test + public void register_thenGenerateHashEndpointResponseNullBody_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + AddIdentityResponse addIdentityResponse = new AddIdentityResponse(); + addIdentityResponse.setStatus("ACTIVATED"); + UINResponse uinResponse = new UINResponse(); + uinResponse.setUIN("mockUIN"); + Password.PasswordHash passwordHash = new Password.PasswordHash(); + passwordHash.setSalt("mockSalt"); + passwordHash.setHashValue("mockHashValue"); + + RestResponseWrapper mockRestResponseWrapperUINResponse = new RestResponseWrapper(); + mockRestResponseWrapperUINResponse.setResponse(uinResponse); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperUINResponse, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(generateHashEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(null, HttpStatus.OK)); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals(ErrorConstants.HASH_GENERATE_FAILED, signUpException.getErrorCode()); + } + } + + @Test + public void register_thenGenerateHashEndpointResponseErrors_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + AddIdentityResponse addIdentityResponse = new AddIdentityResponse(); + addIdentityResponse.setStatus("ACTIVATED"); + UINResponse uinResponse = new UINResponse(); + uinResponse.setUIN("mockUIN"); + Password.PasswordHash passwordHash = new Password.PasswordHash(); + passwordHash.setSalt("mockSalt"); + passwordHash.setHashValue("mockHashValue"); + + RestResponseWrapper mockRestResponseWrapperUINResponse = new RestResponseWrapper(); + mockRestResponseWrapperUINResponse.setResponse(uinResponse); + RestResponseWrapper mockRestResponseWrapperPasswordHash = new RestResponseWrapper(); + ArrayList errors = new ArrayList<>(); + errors.add(new RestError("server_error","server_error")); + mockRestResponseWrapperPasswordHash.setErrors(errors); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperUINResponse, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(generateHashEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperPasswordHash, HttpStatus.OK)); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals("server_error", signUpException.getErrorCode()); + } + } + + @Test + public void register_thenAddIdentityEndpointResponseNullBody_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + AddIdentityResponse addIdentityResponse = new AddIdentityResponse(); + addIdentityResponse.setStatus("ACTIVATED"); + UINResponse uinResponse = new UINResponse(); + uinResponse.setUIN("mockUIN"); + Password.PasswordHash passwordHash = new Password.PasswordHash(); + passwordHash.setSalt("mockSalt"); + passwordHash.setHashValue("mockHashValue"); + + RestResponseWrapper mockRestResponseWrapperUINResponse = new RestResponseWrapper(); + mockRestResponseWrapperUINResponse.setResponse(uinResponse); + RestResponseWrapper mockRestResponseWrapperPasswordHash = new RestResponseWrapper(); + mockRestResponseWrapperPasswordHash.setResponse(passwordHash); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperUINResponse, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(generateHashEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperPasswordHash, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(addIdentityEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(null, HttpStatus.OK)); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals("add_identity_failed", signUpException.getMessage()); + } + } + + @Test + public void register_thenAddIdentityEndpointResponseErrors_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + AddIdentityResponse addIdentityResponse = new AddIdentityResponse(); + addIdentityResponse.setStatus("ACTIVATED"); + UINResponse uinResponse = new UINResponse(); + uinResponse.setUIN("mockUIN"); + Password.PasswordHash passwordHash = new Password.PasswordHash(); + passwordHash.setSalt("mockSalt"); + passwordHash.setHashValue("mockHashValue"); + + RestResponseWrapper mockRestResponseWrapperAddIdentityResponse = new RestResponseWrapper(); + ArrayList errors = new ArrayList<>(); + errors.add(new RestError("server_error", "server_error")); + mockRestResponseWrapperAddIdentityResponse.setErrors(errors); + RestResponseWrapper mockRestResponseWrapperUINResponse = new RestResponseWrapper(); + mockRestResponseWrapperUINResponse.setResponse(uinResponse); + RestResponseWrapper mockRestResponseWrapperPasswordHash = new RestResponseWrapper(); + mockRestResponseWrapperPasswordHash.setResponse(passwordHash); + + when(selfTokenRestTemplate.exchange( + eq(getUinEndpoint), + eq(HttpMethod.GET), + eq(null), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperUINResponse, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(generateHashEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperPasswordHash, HttpStatus.OK)); + when(selfTokenRestTemplate.exchange( + eq(addIdentityEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))).thenReturn(new ResponseEntity<>(mockRestResponseWrapperAddIdentityResponse, HttpStatus.OK)); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals("server_error", signUpException.getErrorCode()); + } + } + + @Test + public void register_withInvalidTransaction_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (InvalidTransactionException invalidTransactionException){ + Assert.assertEquals("invalid_transaction", invalidTransactionException.getErrorCode()); + } + } + + @Test + public void register_withInvalidUsername_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855321444123"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("AGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals("unsupported_username", signUpException.getErrorCode()); + } + } + + @Test + public void register_withInvalidConsent_throwException() throws Exception{ + UserInfoMap userInfo = new UserInfoMap(); + userInfo.setPreferredLang("khm"); + userInfo.setFullName(List.of(new LanguageTaggedValue("eng", "Panharith AN"))); + userInfo.setPhone("+855219718732"); + + RegisterRequest registerRequest = new RegisterRequest(); + registerRequest.setUserInfo(userInfo); + registerRequest.setUsername("+855219718732"); + registerRequest.setPassword("123123"); + registerRequest.setConsent("DISAGREE"); + + String mockTransactionID = "123456789"; + + RegistrationTransaction mockRegistrationTransaction = new RegistrationTransaction(registerRequest.getUsername(), mockTransactionID); + mockRegistrationTransaction.setChallengeHash("123456"); + mockRegistrationTransaction.setIdentifier(userInfo.getPhone()); + + when(cacheUtilService.getChallengeVerifiedTransaction(mockTransactionID)) + .thenReturn(mockRegistrationTransaction); + + try{ + registrationService.register(registerRequest, mockTransactionID); + Assert.fail(); + }catch (SignUpException signUpException){ + Assert.assertEquals("consent_required", signUpException.getErrorCode()); + } + } + // Generate Challenge OTP test cases @Test public void doGenerateChallenge_withoutTransactionId_thenPass() throws SignUpException, IOException { @@ -284,7 +756,7 @@ public void doGenerateChallenge_withToEarlyAttemptTransactionId_thenFail() throw registrationService.generateChallenge(generateChallengeRequest, transactionId); Assert.fail(); } catch (GenerateChallengeException ex) { - Assert.assertEquals("too_early_attempts", ex.getErrorCode()); + Assert.assertEquals(ErrorConstants.ACTIVE_CHALLENGE_FOUND, ex.getErrorCode()); } } diff --git a/signup-service/src/test/resources/application-test.properties b/signup-service/src/test/resources/application-test.properties index 9d1894c6..4d445168 100644 --- a/signup-service/src/test/resources/application-test.properties +++ b/signup-service/src/test/resources/application-test.properties @@ -1,7 +1,7 @@ ## Application Name spring.application.name=signup -server.port=8080 +server.port=8088 server.servlet.path=/v1/signup openapi.info.title=Signup Service @@ -60,6 +60,10 @@ mosip.iam.adapter.self-token-renewal-enable=true ## --------------------------------- mosip.authmanager.generate-otp-endpoint=${mosip.kernel.authmanager.url}/v1/otpmanager/otp/generate mosip.signup.supported.challenge-format-types={'alpha-numeric'} +mosip.signup.id-schema.version=0.2 +## --------------------------------- +mosip.idrepo.identity.add-identiry-url=https://dev.test.net/idrepository/v1/identity/ +mosip.idrepo.identity.hashing-password=https://dev.test.net/v1/keymanager/generateArgon2Hash # captcha validator mosip.signup.send-challenge.captcha-required=false mosip.signup.integration.captcha-validator=GoogleRecaptchaValidatorService @@ -70,6 +74,8 @@ mosip.signup.cookie.max-age=60 mosip.signup.challenge.resend-attempt=3 mosip.signup.challenge.resend-delay=30 mosip.signup.identifier.regex=\\+855\\d{8,9} +mosip.signup.supported-languages={'khm', 'eng'} +mosip.signup.password.max-length=20 ## --------------------------------- mosip.signup.ui.config.key-values={\ \'identifier.pattern': '\\d{8,9}', \