From 48c2cf8b4a27062bff032a47234f801c0a862f7b Mon Sep 17 00:00:00 2001 From: Sachin Rana Date: Sat, 21 Dec 2024 18:40:38 +0530 Subject: [PATCH] [ES-1887] Added additionalConfig validator and new client-mgmt apis (#1016) * Added additionalConfig validator and new client-mgmt apis Signed-off-by: Sachin Rana * handled review comments Signed-off-by: Sachin Rana * resolved review comments Signed-off-by: Sachin Rana * resolved review comments Signed-off-by: Sachin Rana * worked on testcases, code improvement and db script changes Signed-off-by: Sachin Rana * removed redundant constraint Signed-off-by: Sachin Rana --------- Signed-off-by: Sachin Rana Signed-off-by: sacrana0 --- .../services/ClientManagementServiceImpl.java | 3 +- .../esignet/ClientManagementServiceTest.java | 4 +- db_scripts/mosip_esignet/db.sql | 2 +- db_scripts/mosip_esignet/dml.sql | 17 +- .../sql/1.5.0_to_1.5.1_upgrade.sql | 11 +- docker-compose/init.sql | 6 +- .../core/constants/ErrorConstants.java | 1 + .../core/dto/ClientDetailCreateRequestV3.java | 4 +- .../core/dto/ClientDetailUpdateRequestV3.java | 10 +- .../validator/ClientAdditionalConfig.java | 24 + .../ClientAdditionalConfigValidator.java | 81 + .../ClientAdditionalConfigConverterTest.java | 36 + .../io/mosip/esignet/core/ValidatorTest.java | 1554 +++++++++-------- .../additional_config_request_schema.json | 44 + .../ClientManagementController.java | 67 +- .../additional_config_request_schema.json | 44 + .../resources/application-default.properties | 4 + ...ientMgmtV2ControllerParameterizedTest.java | 342 ++++ .../additional_config_request_schema.json | 44 + .../resources/application-test.properties | 1 + 20 files changed, 1509 insertions(+), 790 deletions(-) create mode 100644 esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java create mode 100644 esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java create mode 100644 esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java create mode 100644 esignet-core/src/test/resources/additional_config_request_schema.json create mode 100644 esignet-service/src/main/resources/additional_config_request_schema.json create mode 100644 esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java create mode 100644 esignet-service/src/test/resources/additional_config_request_schema.json diff --git a/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java b/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java index 0a5bdc6ff..0a93b4951 100644 --- a/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java +++ b/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java @@ -276,8 +276,7 @@ public ClientDetailResponseV2 updateClient(String clientId, ClientDetailUpdateRe } public ClientDetail buildClient(ClientDetailCreateRequestV2 clientDetailCreateRequestV2) { - Optional result = clientDetailRepository.findById(clientDetailCreateRequestV2.getClientId()); - if (result.isPresent()) { + if (clientDetailRepository.existsById(clientDetailCreateRequestV2.getClientId())) { log.error("Duplicate Client Id : {}", ErrorConstants.DUPLICATE_CLIENT_ID); throw new EsignetException(ErrorConstants.DUPLICATE_CLIENT_ID); } diff --git a/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java b/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java index 5ec7aae35..adc8208e0 100644 --- a/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java +++ b/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java @@ -124,7 +124,7 @@ public void createClientV2_withValidDetail_thenPass() throws Exception { @Test public void createClientV2_withExistingClientId_thenFail() { - Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.of(new ClientDetail())); + Mockito.when(clientDetailRepository.existsById("client_id_v1")).thenReturn(true); ClientDetailCreateRequestV2 clientCreateV2ReqDto = new ClientDetailCreateRequestV2(); clientCreateV2ReqDto.setClientId("client_id_v1"); try { @@ -164,7 +164,7 @@ public void createClientV3_withValidDetail_thenPass() { @Test public void createClientV3_withExistingClientId_thenFail() { - Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.of(new ClientDetail())); + Mockito.when(clientDetailRepository.existsById("client_id_v1")).thenReturn(true); ClientDetailCreateRequestV3 clientCreateV3ReqDto = new ClientDetailCreateRequestV3(); clientCreateV3ReqDto.setClientId("client_id_v1"); try { diff --git a/db_scripts/mosip_esignet/db.sql b/db_scripts/mosip_esignet/db.sql index 0d9150299..42e3331b9 100644 --- a/db_scripts/mosip_esignet/db.sql +++ b/db_scripts/mosip_esignet/db.sql @@ -6,7 +6,7 @@ CREATE DATABASE mosip_esignet OWNER = postgres TEMPLATE = template0; -COMMENT ON DATABASE mosip_idp IS 'e-Signet related data is stored in this database'; +COMMENT ON DATABASE mosip_esignet IS 'e-Signet related data is stored in this database'; \c mosip_esignet postgres diff --git a/db_scripts/mosip_esignet/dml.sql b/db_scripts/mosip_esignet/dml.sql index 626d1bea1..bdb3eccc3 100644 --- a/db_scripts/mosip_esignet/dml.sql +++ b/db_scripts/mosip_esignet/dml.sql @@ -3,19 +3,4 @@ ----- TRUNCATE esignet.client_detail TABLE Data and It's reference Data and COPY Data from CSV file ----- TRUNCATE TABLE esignet.client_detail cascade ; -\COPY esignet.key_policy_def (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) FROM './dml/esignet-key_policy_def.csv' delimiter ',' HEADER csv; - - - - - - - - - - - - - - - +\COPY esignet.key_policy_def (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) FROM './dml/esignet-key_policy_def.csv' delimiter ',' HEADER csv; \ No newline at end of file diff --git a/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql b/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql index e96ef8a6a..afd2517ba 100644 --- a/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql +++ b/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql @@ -31,17 +31,12 @@ EXCEPTION END; $$ LANGUAGE plpgsql; --- Add the new column with a default value -ALTER TABLE client_detail -ADD COLUMN additional_config jsonb DEFAULT '{}'::jsonb; - --- Update existing entries to set the default value for the new column -UPDATE client_detail -SET additional_config = '{}'::jsonb -WHERE additional_config IS NULL; DO $$ BEGIN +-- Add the new column with a default value +ALTER TABLE client_detail ADD COLUMN additional_config jsonb; + IF NOT is_column_jsonb('client_detail', 'public_key') THEN -- create backup diff --git a/docker-compose/init.sql b/docker-compose/init.sql index 5e4bf5802..16f2e20a4 100644 --- a/docker-compose/init.sql +++ b/docker-compose/init.sql @@ -33,16 +33,18 @@ CREATE TABLE esignet.client_detail( redirect_uris character varying NOT NULL, claims character varying NOT NULL, acr_values character varying NOT NULL, - public_key character varying NOT NULL, + public_key jsonb NOT NULL, grant_types character varying NOT NULL, auth_methods character varying NOT NULL, status character varying(20) NOT NULL, + additional_config jsonb, cr_dtimes timestamp NOT NULL, upd_dtimes timestamp, CONSTRAINT pk_clntdtl_id PRIMARY KEY (id), - CONSTRAINT uk_clntdtl_key UNIQUE (public_key) ); +CREATE UNIQUE INDEX unique_n_value ON client_detail ((public_key->>'n')); + create table esignet.consent_detail ( id UUID NOT NULL, client_id VARCHAR NOT NULL, diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java b/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java index 2b8248378..03cdf8ee5 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java @@ -7,6 +7,7 @@ public class ErrorConstants { + public static final String INVALID_ADDITIONAL_CONFIG="invalid_additional_config"; public static final String INVALID_REQUEST="invalid_request"; public static final String INVALID_CLIENT_ID="invalid_client_id"; public static final String INVALID_CLIENT_NAME="invalid_client_name"; diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java index 42d0a4ec4..a358acf38 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java @@ -1,6 +1,6 @@ package io.mosip.esignet.core.dto; -import lombok.AllArgsConstructor; +import io.mosip.esignet.core.validator.ClientAdditionalConfig; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,6 +10,8 @@ @Data @NoArgsConstructor public class ClientDetailCreateRequestV3 extends ClientDetailCreateRequestV2 { + + @ClientAdditionalConfig private Map additionalConfig; public ClientDetailCreateRequestV3(String clientId, String clientName, Map publicKey, String relyingPartyId, diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java index 1944227c4..2fa677000 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java @@ -1,6 +1,6 @@ package io.mosip.esignet.core.dto; -import lombok.AllArgsConstructor; +import io.mosip.esignet.core.validator.ClientAdditionalConfig; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,10 +10,12 @@ @Data @NoArgsConstructor public class ClientDetailUpdateRequestV3 extends ClientDetailUpdateRequestV2 { + + @ClientAdditionalConfig private Map additionalConfig; - public ClientDetailUpdateRequestV3(String logUri, List redirectUris, List userClaims, List authContextRefs, String status, List grantTypes, String clientName, List clientAuthMethods, Map clientNameLangMap, Map additionalConfig){ - super(logUri,redirectUris,userClaims,authContextRefs,status,grantTypes,clientName,clientAuthMethods, clientNameLangMap); - this.additionalConfig=additionalConfig; + public ClientDetailUpdateRequestV3(String logUri, List redirectUris, List userClaims, List authContextRefs, String status, List grantTypes, String clientName, List clientAuthMethods, Map clientNameLangMap, Map additionalConfig) { + super(logUri, redirectUris, userClaims, authContextRefs, status, grantTypes, clientName, clientAuthMethods, clientNameLangMap); + this.additionalConfig = additionalConfig; } } diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java new file mode 100644 index 000000000..f3989b667 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java @@ -0,0 +1,24 @@ +package io.mosip.esignet.core.validator; + +import io.mosip.esignet.core.constants.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.RetentionPolicy.RUNTIME; + +@Target({FIELD}) +@Retention(RUNTIME) +@Constraint(validatedBy = ClientAdditionalConfigValidator.class) +@Documented +public @interface ClientAdditionalConfig { + String message() default ErrorConstants.INVALID_ADDITIONAL_CONFIG; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java new file mode 100644 index 000000000..3139725e5 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java @@ -0,0 +1,81 @@ +package io.mosip.esignet.core.validator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; +import io.mosip.esignet.core.exception.EsignetException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Set; + +@Slf4j +public class ClientAdditionalConfigValidator implements + ConstraintValidator> { + + @Value("${mosip.esignet.additional-config.schema.url}") + private String schemaUrl; + + private volatile JsonSchema cachedSchema; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private ResourceLoader resourceLoader; + + @Override + public void initialize(ClientAdditionalConfig constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Map additionalConfig, ConstraintValidatorContext context) { + if (additionalConfig == null) { + return false; + } + Set errors = null; + try { + JsonNode jsonNode = objectMapper.valueToTree(additionalConfig); + errors = getCachedSchema().validate(jsonNode); + if (errors.isEmpty()) return true; + } catch (Exception e) { + log.error("Error validating additional_config schema: ", e); + } + log.error("Validation failed for additional_config ---> {}", errors); + return false; + } + + private JsonSchema getCachedSchema() throws EsignetException { + if(cachedSchema!=null ) return cachedSchema; + synchronized (this) { + if (cachedSchema == null) { + InputStream schemaResponse = getResource(schemaUrl); + JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); + cachedSchema = jsonSchemaFactory.getSchema(schemaResponse); + } + } + return cachedSchema; + } + + private InputStream getResource(String url) { + try { + Resource resource = resourceLoader.getResource(url); + return resource.getInputStream(); + } catch (IOException e) { + log.error("Failed to parse data: {}", url, e); + } + throw new EsignetException("invalid_configuration"); + } +} diff --git a/esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java b/esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java new file mode 100644 index 000000000..50828632d --- /dev/null +++ b/esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java @@ -0,0 +1,36 @@ +package io.mosip.esignet.core; + +import io.mosip.esignet.core.exception.InvalidClientException; +import io.mosip.esignet.core.util.ClientAdditionalConfigConverter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ClientAdditionalConfigConverterTest { + + private ClientAdditionalConfigConverter converter; + + @BeforeEach + void setUp() { + converter = new ClientAdditionalConfigConverter(); + } + + @Test + public void convertToDatabaseColumn_NullMap_ReturnsNull() { + Assertions.assertNull(converter.convertToDatabaseColumn(null)); + } + + @Test + public void convertToEntityAttribute_NullJson_ReturnsNull() { + Assertions.assertNull(converter.convertToEntityAttribute(null)); + } + + @Test + public void convertToEntityAttribute_InvalidJson_ThrowsException() { + String invalidJson = "{\"invalid: value}"; + Assertions.assertThrows(InvalidClientException.class, () -> + converter.convertToEntityAttribute(invalidJson) + ); + } + +} diff --git a/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java b/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java index 75ca8b8c3..5ca5da8b5 100644 --- a/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java +++ b/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java @@ -28,15 +28,13 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.client.RestTemplate; + import java.io.IOException; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; + import static org.mockito.Mockito.when; @@ -44,743 +42,817 @@ @RunWith(MockitoJUnitRunner.class) public class ValidatorTest { - @InjectMocks - ClaimsSchemaValidator claimSchemaValidator; - - @Mock - AuthenticationContextClassRefUtil authenticationContextClassRefUtil; - - @Mock - Authenticator authenticator; - - @Mock - Environment environment; - - - @Mock - RestTemplate restTemplate; - - - ResourceLoader resourceLoader= new DefaultResourceLoader(); - - ObjectMapper mapper= new ObjectMapper(); - - - - private Map discoveryMap = new HashMap<>(); - - @Before - public void setup() throws EsignetException { - Set mockACRs = new HashSet<>(); - mockACRs.add("level1"); - mockACRs.add("level2"); - mockACRs.add("level3"); - mockACRs.add("level4"); - discoveryMap.put("claims_supported", Arrays.asList("name", "gender", "address")); - when(authenticationContextClassRefUtil.getSupportedACRValues()).thenReturn(mockACRs); - when(authenticator.isSupportedOtpChannel("email")).thenReturn(true); - } - - // ============================ Display Validator ========================= - - @Test - public void test_displayValidator_valid_thenPass() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertTrue(validator.isValid("wap", null)); - } - - @Test - public void test_displayValidator_invalid_thenFail() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertFalse(validator.isValid("wap2", null)); - } - - @Test - public void test_displayValidator_invalidWithSpace_thenFail() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertFalse(validator.isValid("page wap", null)); - } - - @Test - public void test_displayValidator_nullValue_thenPass() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_displayValidator_EmptyValue_thenFail() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ GranType Validator ========================= - - @Test - public void test_grantTypeValidator_valid_thenPass() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertTrue(validator.isValid("authorization_code", null)); - } - - @Test - public void test_grantTypeValidator_invalid_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid("code", null)); - } - - @Test - public void test_grantTypeValidator_invalidWithSpace_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid(" authorization_code ", null)); - } - - @Test - public void test_grantTypeValidator_nullValue_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_grantTypeValidator_EmptyValue_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ Prompt Validator ========================= - - @Test - public void test_PromptValidator_valid_thenPass() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertTrue(validator.isValid("consent", null)); - } - - @Test - public void test_PromptValidator_invalid_thenFail() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertFalse(validator.isValid("pop-up", null)); - } - - @Test - public void test_PromptValidator_invalidWithSpace_thenFail() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertFalse(validator.isValid(" login ", null)); - } - - @Test - public void test_PromptValidator_nullValue_thenPass() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_PromptValidator_EmptyValue_thenFail() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ ResponseType Validator ========================= - - @Test - public void test_ResponseTypeValidator_valid_thenPass() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertTrue(validator.isValid("code", null)); - } - - @Test - public void test_ResponseTypeValidator_invalid_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid("code----", null)); - } - - @Test - public void test_ResponseTypeValidator_invalidWithSpace_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid(" code ", null)); - } - - @Test - public void test_ResponseTypeValidator_nullValue_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_ResponseTypeValidator_EmptyValue_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ Client Assertion type Validator - // ========================= - - @Test - public void test_ClientAssertionTypeValidator_valid_thenPass() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertTrue(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer", null)); - } - - @Test - public void test_ClientAssertionTypeValidator_invalid_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid("jwt-bearer", null)); - } - - @Test - public void test_ClientAssertionTypeValidator_invalidWithSpace_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer ", null)); - } - - @Test - public void test_ClientAssertionTypeValidator_nullValue_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_ClientAssertionTypeValidator_EmptyValue_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ Optional ACR Validator ========================= - - @Test - public void test_OptionalACRValidator_valid_thenPass() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_OptionalACRValidator_EmptyValue_thenFail() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OptionalACRValidator_SingleValue_thenPass() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertTrue(validator.isValid("level2", null)); - } - - @Test - public void test_OptionalACRValidator_MultipleValue_thenPass() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertTrue(validator.isValid("level4 level2", null)); - } - - @Test - public void test_OptionalACRValidator_InvalidMultipleValue_thenFail() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertFalse(validator.isValid("level5 level1", null)); - } - - @Test - public void test_OptionalACRValidator_throwsException_thenFail() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - when(authenticationContextClassRefUtil.getSupportedACRValues()).thenThrow(EsignetException.class); - Assert.assertFalse(validator.isValid("level5 level1", null)); - } - - // ============================ Request time Validator ========================= - - @Test - public void test_RequestTimeValidator_nullValue_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - Assert.assertFalse(validator.isValid(null, null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_RequestTimeValidator_validValue_thenPass() { - RequestTimeValidator validator = new RequestTimeValidator(); - ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - Assert.assertTrue(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - - requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.plusMinutes(1); - Assert.assertTrue(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - - requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.minusMinutes(1); - Assert.assertTrue(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - } - - @Test - public void test_RequestTimeValidator_futureDateValue_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.plusMinutes(4); - Assert.assertFalse(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - } - - @Test - public void test_RequestTimeValidator_oldDateValue_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.minusMinutes(5); - Assert.assertFalse(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - } - - @Test - public void test_RequestTimeValidator_invalidFormat_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; - Assert.assertFalse(validator.isValid(requestTime.format(DateTimeFormatter.ofPattern(DATETIME_PATTERN)), null)); - } - - // ============================ Otp channel Validator ========================= - - @Test - public void test_OtpChannelValidator_valid_thenPass() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertTrue(validator.isValid("email", null)); - } - - @Test - public void test_OtpChannelValidator_null_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_OtpChannelValidator_invalid_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid("mobile", null)); - } - - @Test - public void test_OtpChannelValidator_blank_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_OtpChannelValidator_spaceAppended_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid(" email ", null)); - } - - // ============================ Format Validator ========================= - - @Test - public void test_FormatValidator_nullValue_thenFail() { - IdFormatValidator validator = new IdFormatValidator(); - Assert.assertFalse(validator.isValid(null, null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_FormatValidator_validValue_thenPass() { - IdFormatValidator validator = new IdFormatValidator(); - Assert.assertTrue(validator.isValid("id-#4_$%", null)); - } - - @Test - public void test_FormatValidator_withValidValue_thenPass() { - IdFormatValidator validator = new IdFormatValidator(); - ReflectionTestUtils.setField(validator, "supportedRegex", "\\S*"); - Assert.assertTrue(validator.isValid("id-#4_$%", null)); - } - - @Test - public void test_FormatValidator_withInvalidValue_thenFail() { - IdFormatValidator validator = new IdFormatValidator(); - Assert.assertFalse(validator.isValid(" id#4$%", null)); - Assert.assertFalse(validator.isValid("id#4$% ", null)); - Assert.assertFalse(validator.isValid("id #4$%", null)); - Assert.assertFalse(validator.isValid("id #4$ %", null)); - } - - // ============================ OIDC Claim Validator ========================= - - @Test - public void test_OIDCClaimValidator_withValidClaim_thenPass() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertTrue(validator.isValid("name", null)); - } - - @Test - public void test_OIDCClaimValidator_withInvalidClaim_thenFail() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertFalse(validator.isValid("email", null)); - } - - @Test - public void test_OIDCClaimValidator_emptyValue_thenFail() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OIDCClaimValidator_nullValue_thenFail() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertFalse(validator.isValid(null, null)); - } - - // ============================ OIDC Client Auth Validator ========================= - - @Test - public void test_OIDCClientAuthValidator_withValidAuth_thenPass() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertTrue(validator.isValid("pwd", null)); - } - - @Test - public void test_OIDCClientAuthValidator_withInvalidAuth_thenFail() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertFalse(validator.isValid("OTP", null)); - } - - @Test - public void test_OIDCClientAuthValidator_withEmptyAuth_thenFail() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OIDCClientAuthValidator_withNullAuth_thenFail() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertFalse(validator.isValid(null, null)); - } - - // ============================ OIDC Scope Validator ========================= - - @Test - public void test_OIDCScopeValidator_withValidScopes_thenPass() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertTrue(validator.isValid("resident-service email openid", null)); - Assert.assertTrue(validator.isValid("resident-service", null)); - Assert.assertTrue(validator.isValid("mosip_identity_json_vc", null)); - } - - @Test - public void test_OIDCScopeValidator_withInvalidScopes_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertFalse(validator.isValid("test scope", null)); - Assert.assertFalse(validator.isValid("resident-service sample_ldp_vc", null)); - } - - @Test - public void test_OIDCScopeValidator_withoutOpenidScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertFalse(validator.isValid("email", null)); - } - - @Test - public void test_OIDCScopeValidator_withEmptyScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OIDCScopeValidator_withNullScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_OIDCScopeValidator_withBothOpenIdAndCredentialScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertFalse(validator.isValid("profile sample_ldp_vc", null)); - } - - // ============================ PKCECodeChallengeMethodValidator Validator ========================= - - @Test - public void test_challengeMethodValidator_withValidValues_thenPass() { - PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); - ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); - Assert.assertTrue(validator.isValid("S256", null)); - Assert.assertTrue(validator.isValid("plain", null)); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_challengeMethodValidator_withInvalidValues_thenFail() { - PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); - ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); - Assert.assertFalse(validator.isValid("s256", null)); - Assert.assertFalse(validator.isValid("PLAIN", null)); - Assert.assertFalse(validator.isValid("null", null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - // ============================ RedirectURLValidator Validator ========================= - - @Test - public void test_redirectURLValidator_withValidValues_thenPass() { - RedirectURLValidator validator = new RedirectURLValidator(); - Assert.assertTrue(validator.isValid("https://domain.com/test", null)); - Assert.assertTrue(validator.isValid("http://localhost:9090/png", null)); - Assert.assertTrue(validator.isValid("http://domain.com/*", null)); - Assert.assertTrue(validator.isValid("https://domain.com/test/*", null)); - Assert.assertTrue(validator.isValid("io.mosip.residentapp://oauth", null)); - Assert.assertTrue(validator.isValid("residentapp://oauth/*", null)); - } - - @Test - public void test_redirectURLValidator_withInvalidValues_thenFail() { - RedirectURLValidator validator = new RedirectURLValidator(); - Assert.assertFalse(validator.isValid("*", null)); - Assert.assertFalse(validator.isValid("https://domain*", null)); - Assert.assertFalse(validator.isValid("io.mosip.residentapp://*", null)); - Assert.assertFalse(validator.isValid("residentapp*", null)); - Assert.assertFalse(validator.isValid("http*", null)); - } + @InjectMocks + ClaimsSchemaValidator claimSchemaValidator; + + @InjectMocks + ClientAdditionalConfigValidator clientAdditionalConfigValidator; + + @Mock + AuthenticationContextClassRefUtil authenticationContextClassRefUtil; + + @Mock + Authenticator authenticator; + + @Mock + Environment environment; + + + @Mock + RestTemplate restTemplate; + + + ResourceLoader resourceLoader = new DefaultResourceLoader(); + + ObjectMapper mapper = new ObjectMapper(); + + + private Map discoveryMap = new HashMap<>(); + + @Before + public void setup() throws EsignetException { + Set mockACRs = new HashSet<>(); + mockACRs.add("level1"); + mockACRs.add("level2"); + mockACRs.add("level3"); + mockACRs.add("level4"); + discoveryMap.put("claims_supported", Arrays.asList("name", "gender", "address")); + when(authenticationContextClassRefUtil.getSupportedACRValues()).thenReturn(mockACRs); + when(authenticator.isSupportedOtpChannel("email")).thenReturn(true); + } + + // ============================ Display Validator ========================= + + @Test + public void test_displayValidator_valid_thenPass() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertTrue(validator.isValid("wap", null)); + } + + @Test + public void test_displayValidator_invalid_thenFail() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertFalse(validator.isValid("wap2", null)); + } + + @Test + public void test_displayValidator_invalidWithSpace_thenFail() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertFalse(validator.isValid("page wap", null)); + } + + @Test + public void test_displayValidator_nullValue_thenPass() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_displayValidator_EmptyValue_thenFail() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ GranType Validator ========================= + + @Test + public void test_grantTypeValidator_valid_thenPass() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertTrue(validator.isValid("authorization_code", null)); + } + + @Test + public void test_grantTypeValidator_invalid_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid("code", null)); + } + + @Test + public void test_grantTypeValidator_invalidWithSpace_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid(" authorization_code ", null)); + } + + @Test + public void test_grantTypeValidator_nullValue_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_grantTypeValidator_EmptyValue_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ Prompt Validator ========================= + + @Test + public void test_PromptValidator_valid_thenPass() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertTrue(validator.isValid("consent", null)); + } + + @Test + public void test_PromptValidator_invalid_thenFail() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertFalse(validator.isValid("pop-up", null)); + } + + @Test + public void test_PromptValidator_invalidWithSpace_thenFail() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertFalse(validator.isValid(" login ", null)); + } + + @Test + public void test_PromptValidator_nullValue_thenPass() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_PromptValidator_EmptyValue_thenFail() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ ResponseType Validator ========================= + + @Test + public void test_ResponseTypeValidator_valid_thenPass() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertTrue(validator.isValid("code", null)); + } + + @Test + public void test_ResponseTypeValidator_invalid_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid("code----", null)); + } + + @Test + public void test_ResponseTypeValidator_invalidWithSpace_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid(" code ", null)); + } + + @Test + public void test_ResponseTypeValidator_nullValue_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_ResponseTypeValidator_EmptyValue_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ Client Assertion type Validator + // ========================= + + @Test + public void test_ClientAssertionTypeValidator_valid_thenPass() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertTrue(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer", null)); + } + + @Test + public void test_ClientAssertionTypeValidator_invalid_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid("jwt-bearer", null)); + } + + @Test + public void test_ClientAssertionTypeValidator_invalidWithSpace_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer ", null)); + } + + @Test + public void test_ClientAssertionTypeValidator_nullValue_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_ClientAssertionTypeValidator_EmptyValue_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ Optional ACR Validator ========================= + + @Test + public void test_OptionalACRValidator_valid_thenPass() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_OptionalACRValidator_EmptyValue_thenFail() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OptionalACRValidator_SingleValue_thenPass() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertTrue(validator.isValid("level2", null)); + } + + @Test + public void test_OptionalACRValidator_MultipleValue_thenPass() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertTrue(validator.isValid("level4 level2", null)); + } + + @Test + public void test_OptionalACRValidator_InvalidMultipleValue_thenFail() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertFalse(validator.isValid("level5 level1", null)); + } + + @Test + public void test_OptionalACRValidator_throwsException_thenFail() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + when(authenticationContextClassRefUtil.getSupportedACRValues()).thenThrow(EsignetException.class); + Assert.assertFalse(validator.isValid("level5 level1", null)); + } + + // ============================ Request time Validator ========================= + + @Test + public void test_RequestTimeValidator_nullValue_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + Assert.assertFalse(validator.isValid(null, null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_RequestTimeValidator_validValue_thenPass() { + RequestTimeValidator validator = new RequestTimeValidator(); + ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + Assert.assertTrue(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + + requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.plusMinutes(1); + Assert.assertTrue(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + + requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.minusMinutes(1); + Assert.assertTrue(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + } + + @Test + public void test_RequestTimeValidator_futureDateValue_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.plusMinutes(4); + Assert.assertFalse(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + } + + @Test + public void test_RequestTimeValidator_oldDateValue_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.minusMinutes(5); + Assert.assertFalse(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + } + + @Test + public void test_RequestTimeValidator_invalidFormat_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; + Assert.assertFalse(validator.isValid(requestTime.format(DateTimeFormatter.ofPattern(DATETIME_PATTERN)), null)); + } + + // ============================ Otp channel Validator ========================= + + @Test + public void test_OtpChannelValidator_valid_thenPass() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertTrue(validator.isValid("email", null)); + } + + @Test + public void test_OtpChannelValidator_null_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_OtpChannelValidator_invalid_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid("mobile", null)); + } + + @Test + public void test_OtpChannelValidator_blank_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_OtpChannelValidator_spaceAppended_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid(" email ", null)); + } + + // ============================ Format Validator ========================= + + @Test + public void test_FormatValidator_nullValue_thenFail() { + IdFormatValidator validator = new IdFormatValidator(); + Assert.assertFalse(validator.isValid(null, null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_FormatValidator_validValue_thenPass() { + IdFormatValidator validator = new IdFormatValidator(); + Assert.assertTrue(validator.isValid("id-#4_$%", null)); + } + + @Test + public void test_FormatValidator_withValidValue_thenPass() { + IdFormatValidator validator = new IdFormatValidator(); + ReflectionTestUtils.setField(validator, "supportedRegex", "\\S*"); + Assert.assertTrue(validator.isValid("id-#4_$%", null)); + } + + @Test + public void test_FormatValidator_withInvalidValue_thenFail() { + IdFormatValidator validator = new IdFormatValidator(); + Assert.assertFalse(validator.isValid(" id#4$%", null)); + Assert.assertFalse(validator.isValid("id#4$% ", null)); + Assert.assertFalse(validator.isValid("id #4$%", null)); + Assert.assertFalse(validator.isValid("id #4$ %", null)); + } + + // ============================ OIDC Claim Validator ========================= + + @Test + public void test_OIDCClaimValidator_withValidClaim_thenPass() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertTrue(validator.isValid("name", null)); + } + + @Test + public void test_OIDCClaimValidator_withInvalidClaim_thenFail() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertFalse(validator.isValid("email", null)); + } + + @Test + public void test_OIDCClaimValidator_emptyValue_thenFail() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OIDCClaimValidator_nullValue_thenFail() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertFalse(validator.isValid(null, null)); + } + + // ============================ OIDC Client Auth Validator ========================= + + @Test + public void test_OIDCClientAuthValidator_withValidAuth_thenPass() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertTrue(validator.isValid("pwd", null)); + } + + @Test + public void test_OIDCClientAuthValidator_withInvalidAuth_thenFail() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertFalse(validator.isValid("OTP", null)); + } + + @Test + public void test_OIDCClientAuthValidator_withEmptyAuth_thenFail() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OIDCClientAuthValidator_withNullAuth_thenFail() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertFalse(validator.isValid(null, null)); + } + + // ============================ OIDC Scope Validator ========================= + + @Test + public void test_OIDCScopeValidator_withValidScopes_thenPass() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertTrue(validator.isValid("resident-service email openid", null)); + Assert.assertTrue(validator.isValid("resident-service", null)); + Assert.assertTrue(validator.isValid("mosip_identity_json_vc", null)); + } + + @Test + public void test_OIDCScopeValidator_withInvalidScopes_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertFalse(validator.isValid("test scope", null)); + Assert.assertFalse(validator.isValid("resident-service sample_ldp_vc", null)); + } + + @Test + public void test_OIDCScopeValidator_withoutOpenidScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertFalse(validator.isValid("email", null)); + } + + @Test + public void test_OIDCScopeValidator_withEmptyScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OIDCScopeValidator_withNullScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_OIDCScopeValidator_withBothOpenIdAndCredentialScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertFalse(validator.isValid("profile sample_ldp_vc", null)); + } + + // ============================ PKCECodeChallengeMethodValidator Validator ========================= + + @Test + public void test_challengeMethodValidator_withValidValues_thenPass() { + PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); + ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); + Assert.assertTrue(validator.isValid("S256", null)); + Assert.assertTrue(validator.isValid("plain", null)); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_challengeMethodValidator_withInvalidValues_thenFail() { + PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); + ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); + Assert.assertFalse(validator.isValid("s256", null)); + Assert.assertFalse(validator.isValid("PLAIN", null)); + Assert.assertFalse(validator.isValid("null", null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + // ============================ RedirectURLValidator Validator ========================= + + @Test + public void test_redirectURLValidator_withValidValues_thenPass() { + RedirectURLValidator validator = new RedirectURLValidator(); + Assert.assertTrue(validator.isValid("https://domain.com/test", null)); + Assert.assertTrue(validator.isValid("http://localhost:9090/png", null)); + Assert.assertTrue(validator.isValid("http://domain.com/*", null)); + Assert.assertTrue(validator.isValid("https://domain.com/test/*", null)); + Assert.assertTrue(validator.isValid("io.mosip.residentapp://oauth", null)); + Assert.assertTrue(validator.isValid("residentapp://oauth/*", null)); + } + + @Test + public void test_redirectURLValidator_withInvalidValues_thenFail() { + RedirectURLValidator validator = new RedirectURLValidator(); + Assert.assertFalse(validator.isValid("*", null)); + Assert.assertFalse(validator.isValid("https://domain*", null)); + Assert.assertFalse(validator.isValid("io.mosip.residentapp://*", null)); + Assert.assertFalse(validator.isValid("residentapp*", null)); + Assert.assertFalse(validator.isValid("http*", null)); + } // ============================ Signature Format Validator ========================= - @Test - public void test_Signature_FormatValidator_nullValue_thenFail() { - SignatureFormatValidator validator = new SignatureFormatValidator(); - Assert.assertFalse(validator.isValid(null, null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_Signature_FormatValidator_validValue_thenPass() { - SignatureFormatValidator validator = new SignatureFormatValidator(); - Assert.assertTrue(validator.isValid("ea12d.iba13", null)); - } - @Test - public void test_Signature_FormatValidator_withInvalidValue_thenFail() { - SignatureFormatValidator validator = new SignatureFormatValidator(); - Assert.assertFalse(validator.isValid("eab234", null)); - Assert.assertFalse(validator.isValid("eabd2314.123cad.123d ", null)); - Assert.assertFalse(validator.isValid("akf.ia*..aha", null)); - Assert.assertFalse(validator.isValid("ajjf", null)); - } - - //=========================== CodeChallengeValidator ==============================// - - @Test - public void test_ValidCodeChallengeValidator_withValidDetails_thenPass(){ - CodeChallengeValidator validator=new CodeChallengeValidator(); - OAuthDetailRequestV2 request=new OAuthDetailRequestV2(); - request.setCodeChallenge("codeChallenge"); - request.setCodeChallengeMethod("codeChallengeMethod"); - Assert.assertTrue(validator.isValid(request,null)); - request.setCodeChallenge(null); - request.setCodeChallengeMethod(null); - Assert.assertTrue(validator.isValid(request,null)); - request.setCodeChallenge(""); - request.setCodeChallengeMethod(""); - Assert.assertTrue(validator.isValid(request,null)); - } - - @Test - public void test_ValidCodeChallengeValidator_withInvalidDetails_thenFail(){ - CodeChallengeValidator validator=new CodeChallengeValidator(); - OAuthDetailRequestV2 request=new OAuthDetailRequestV2(); - request.setCodeChallenge("codeChallenge"); - request.setCodeChallengeMethod(null); - Assert.assertFalse(validator.isValid(request,null)); - request.setCodeChallenge(null); - request.setCodeChallengeMethod("codeChallengeMethod"); - Assert.assertFalse(validator.isValid(request,null)); - request.setCodeChallenge(""); - request.setCodeChallengeMethod("codeChallengeMethod"); - Assert.assertFalse(validator.isValid(request,null)); - } - - // ============================ ClientNameLang Validator ========================= - - @Test - public void test_ClientNameLangValidator_WithValidDetails_thenPass(){ - ClientNameLangValidator validator=new ClientNameLangValidator(); - Assert.assertTrue(validator.isValid("eng", null)); - } - - @Test - public void test_ClientNameLangValidator_WithInValidDetail_thenFail(){ - ClientNameLangValidator validator=new ClientNameLangValidator(); - Assert.assertFalse(validator.isValid("abc", null)); - } - - // =============================ClaimSchemaValidator=============================// - - @Test - public void claimSchemaValidator_withValidDetails_thenPass() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":\"income-tax\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - idTokenMap.put("some_claim", claimDetail); - - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertTrue(claimSchemaValidator.isValid(claimsV2, null)); - } - - @Test - public void claimSchemaValidator_withTrustFrameWorkAsNull_thenFail() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":null}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - - idTokenMap.put("some_claim", claimDetail); - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); - - } - - @Test - public void claimSchemaValidator_withEssentialAsNonBoolean_thenFail() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - - idTokenMap.put("some_claim", claimDetail); - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); - } - - @Test - public void test_ClaimSchemaValidator_withInvalidValue_thenFail() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kf\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - - idTokenMap.put("some_claim", claimDetail); - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); - } + @Test + public void test_Signature_FormatValidator_nullValue_thenFail() { + SignatureFormatValidator validator = new SignatureFormatValidator(); + Assert.assertFalse(validator.isValid(null, null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_Signature_FormatValidator_validValue_thenPass() { + SignatureFormatValidator validator = new SignatureFormatValidator(); + Assert.assertTrue(validator.isValid("ea12d.iba13", null)); + } + + @Test + public void test_Signature_FormatValidator_withInvalidValue_thenFail() { + SignatureFormatValidator validator = new SignatureFormatValidator(); + Assert.assertFalse(validator.isValid("eab234", null)); + Assert.assertFalse(validator.isValid("eabd2314.123cad.123d ", null)); + Assert.assertFalse(validator.isValid("akf.ia*..aha", null)); + Assert.assertFalse(validator.isValid("ajjf", null)); + } + + //=========================== CodeChallengeValidator ==============================// + + @Test + public void test_ValidCodeChallengeValidator_withValidDetails_thenPass() { + CodeChallengeValidator validator = new CodeChallengeValidator(); + OAuthDetailRequestV2 request = new OAuthDetailRequestV2(); + request.setCodeChallenge("codeChallenge"); + request.setCodeChallengeMethod("codeChallengeMethod"); + Assert.assertTrue(validator.isValid(request, null)); + request.setCodeChallenge(null); + request.setCodeChallengeMethod(null); + Assert.assertTrue(validator.isValid(request, null)); + request.setCodeChallenge(""); + request.setCodeChallengeMethod(""); + Assert.assertTrue(validator.isValid(request, null)); + } + + @Test + public void test_ValidCodeChallengeValidator_withInvalidDetails_thenFail() { + CodeChallengeValidator validator = new CodeChallengeValidator(); + OAuthDetailRequestV2 request = new OAuthDetailRequestV2(); + request.setCodeChallenge("codeChallenge"); + request.setCodeChallengeMethod(null); + Assert.assertFalse(validator.isValid(request, null)); + request.setCodeChallenge(null); + request.setCodeChallengeMethod("codeChallengeMethod"); + Assert.assertFalse(validator.isValid(request, null)); + request.setCodeChallenge(""); + request.setCodeChallengeMethod("codeChallengeMethod"); + Assert.assertFalse(validator.isValid(request, null)); + } + + // ============================ ClientNameLang Validator ========================= + + @Test + public void test_ClientNameLangValidator_WithValidDetails_thenPass() { + ClientNameLangValidator validator = new ClientNameLangValidator(); + Assert.assertTrue(validator.isValid("eng", null)); + } + + @Test + public void test_ClientNameLangValidator_WithInValidDetail_thenFail() { + ClientNameLangValidator validator = new ClientNameLangValidator(); + Assert.assertFalse(validator.isValid("abc", null)); + } + + // =============================ClaimSchemaValidator=============================// + + @Test + public void claimSchemaValidator_withValidDetails_thenPass() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":\"income-tax\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + idTokenMap.put("some_claim", claimDetail); + + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertTrue(claimSchemaValidator.isValid(claimsV2, null)); + } + + @Test + public void claimSchemaValidator_withTrustFrameWorkAsNull_thenFail() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":null}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); + + } + + @Test + public void claimSchemaValidator_withEssentialAsNonBoolean_thenFail() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); + } + + @Test + public void test_ClaimSchemaValidator_withInvalidValue_thenFail() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kf\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); + } + + // =============================ClientAdditionalConfigValidator=============================// + + public static Map getValidAdditionalConfig() { + Map validAdditionalConfig = new HashMap<>(); + validAdditionalConfig.put("userinfo_response_type", "JWS"); + validAdditionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", ""), + Map.entry("subTitle", "") + )); + validAdditionalConfig.put("signup_banner_required", true); + validAdditionalConfig.put("forgot_pwd_link_required", true); + validAdditionalConfig.put("consent_expire_in_days", 1); + return validAdditionalConfig; + } + + public static List> getInvalidAdditionalConfigs() { + List> invalidAdditionalConfigs = new ArrayList<>(); + + invalidAdditionalConfigs.add(null); + + Map additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("userinfo_response_type", "ABC"); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Collections.emptyMap()); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", 1), //anything other than string + Map.entry("subTitle", "") + )); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("signup_banner_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("forgot_pwd_link_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("consent_expire_in_days", ""); // anything other than number + invalidAdditionalConfigs.add(additionalConfig); + + return invalidAdditionalConfigs; + } + + @Test + public void test_ClientAdditionalConfigValidator_withValidValue_thenPass() { + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "schemaUrl", "classpath:additional_config_request_schema.json"); + Map validAdditionalConfig = getValidAdditionalConfig(); + Assert.assertTrue(clientAdditionalConfigValidator.isValid(validAdditionalConfig, null)); + } + + @Test + public void test_ClientAdditionalConfigValidator_withInvalidValue_thenFail() { + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "schemaUrl", "classpath:additional_config_request_schema.json"); + for (Map additionalConfig : getInvalidAdditionalConfigs()) { + Assert.assertFalse(clientAdditionalConfigValidator.isValid(additionalConfig, null)); + } + } } diff --git a/esignet-core/src/test/resources/additional_config_request_schema.json b/esignet-core/src/test/resources/additional_config_request_schema.json new file mode 100644 index 000000000..adfda02d4 --- /dev/null +++ b/esignet-core/src/test/resources/additional_config_request_schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "userinfo_response_type": { + "type": "string", + "enum": ["JWS", "JWE"] + }, + "purpose": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "subTitle": { + "type": "string" + } + }, + "required": ["type", "title", "subTitle"], + "additionalProperties": true + }, + "signup_banner_required": { + "type": "boolean" + }, + "forgot_pwd_link_required": { + "type": "boolean" + }, + "consent_expire_in_days": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "userinfo_response_type", + "purpose", + "signup_banner_required", + "forgot_pwd_link_required", + "consent_expire_in_days" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java b/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java index 0fa2303a7..c77862647 100644 --- a/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java +++ b/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java @@ -30,18 +30,16 @@ public class ClientManagementController { @Autowired AuditPlugin auditWrapper; - + @Value("${mosip.esignet.audit.claim-name:preferred_username}") private String claimName; /** - * @deprecated - * This method is no longer acceptable to create oidc client - *

Use {@link ClientManagementController#createOAuthClient(RequestWrapper)}

- * * @param requestWrapper * @return * @throws EsignetException + * @deprecated This method is no longer acceptable to create oidc client + *

Use {@link ClientManagementController#createClientV2(RequestWrapper)}

*/ @Deprecated() @RequestMapping(value = "/client-mgmt/oidc-client", method = RequestMethod.POST, @@ -53,7 +51,7 @@ public ResponseWrapper createClient( response.setResponse(clientManagementService.createOIDCClient(requestWrapper.getRequest())); } catch (EsignetException ex) { auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName), - Action.OIDC_CLIENT_CREATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(requestWrapper.getRequest().getClientId()), ex); + Action.OIDC_CLIENT_CREATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(requestWrapper.getRequest().getClientId()), ex); throw ex; } response.setResponseTime(IdentityProviderUtil.getUTCDateTime()); @@ -61,13 +59,11 @@ public ResponseWrapper createClient( } /** - * @deprecated - * This method is no longer acceptable to update oidc client - *

Use {@link ClientManagementController#updateOAuthClient(String, RequestWrapper)}

- * * @param requestWrapper * @return * @throws EsignetException + * @deprecated This method is no longer acceptable to update oidc client + *

Use {@link ClientManagementController#updateClientV2(String, RequestWrapper)}

*/ @Deprecated() @RequestMapping(value = "/client-mgmt/oidc-client/{client_id}", method = RequestMethod.PUT, @@ -79,13 +75,21 @@ public ResponseWrapper updateClient(@Valid @PathVariable(" response.setResponse(clientManagementService.updateOIDCClient(clientId, requestWrapper.getRequest())); } catch (EsignetException ex) { auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName), - Action.OIDC_CLIENT_UPDATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(clientId), ex); + Action.OIDC_CLIENT_UPDATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(clientId), ex); throw ex; } response.setResponseTime(IdentityProviderUtil.getUTCDateTime()); return response; } + /** + * @param requestWrapper + * @return + * @throws EsignetException + * @deprecated This method is no longer acceptable to create oidc client + *

Use {@link ClientManagementController#createClientV2(RequestWrapper)}

+ */ + @Deprecated @PostMapping(value = "/client-mgmt/oauth-client", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseWrapper createOAuthClient(@Valid @RequestBody RequestWrapper requestWrapper) throws Exception { ResponseWrapper response = new ResponseWrapper(); @@ -100,10 +104,17 @@ public ResponseWrapper createOAuthClient(@Valid @RequestBo return response; } - + /** + * @param requestWrapper + * @return + * @throws EsignetException + * @deprecated This method is no longer acceptable to update oidc client + *

Use {@link ClientManagementController#updateClientV2(String, RequestWrapper)}

+ */ + @Deprecated @PutMapping(value = "/client-mgmt/oauth-client/{client_id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseWrapper updateOAuthClient(@Valid @PathVariable("client_id") String clientId, - @Valid @RequestBody RequestWrapper requestWrapper) throws Exception { + @Valid @RequestBody RequestWrapper requestWrapper) throws Exception { ResponseWrapper response = new ResponseWrapper(); try { response.setResponse(clientManagementService.updateOAuthClient(clientId, requestWrapper.getRequest())); @@ -116,4 +127,34 @@ public ResponseWrapper updateOAuthClient(@Valid @PathVaria return response; } + @PostMapping(value = "/client-mgmt/client", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseWrapper createClientV2(@Valid @RequestBody RequestWrapper requestWrapper) { + ResponseWrapper response = new ResponseWrapper<>(); + try { + response.setResponse(clientManagementService.createClient(requestWrapper.getRequest())); + } catch (EsignetException ex) { + auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName), + Action.OAUTH_CLIENT_CREATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(requestWrapper.getRequest().getClientId()), ex); + throw ex; + } + response.setResponseTime(IdentityProviderUtil.getUTCDateTime()); + return response; + } + + + @PutMapping(value = "client-mgmt/client/{client_id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseWrapper updateClientV2(@Valid @PathVariable("client_id") String clientId, + @Valid @RequestBody RequestWrapper requestWrapper) { + ResponseWrapper response = new ResponseWrapper<>(); + try { + response.setResponse(clientManagementService.updateClient(clientId, requestWrapper.getRequest())); + } catch (EsignetException ex) { + auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName), + Action.OAUTH_CLIENT_UPDATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(clientId), ex); + throw ex; + } + response.setResponseTime(IdentityProviderUtil.getUTCDateTime()); + return response; + } + } diff --git a/esignet-service/src/main/resources/additional_config_request_schema.json b/esignet-service/src/main/resources/additional_config_request_schema.json new file mode 100644 index 000000000..adfda02d4 --- /dev/null +++ b/esignet-service/src/main/resources/additional_config_request_schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "userinfo_response_type": { + "type": "string", + "enum": ["JWS", "JWE"] + }, + "purpose": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "subTitle": { + "type": "string" + } + }, + "required": ["type", "title", "subTitle"], + "additionalProperties": true + }, + "signup_banner_required": { + "type": "boolean" + }, + "forgot_pwd_link_required": { + "type": "boolean" + }, + "consent_expire_in_days": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "userinfo_response_type", + "purpose", + "signup_banner_required", + "forgot_pwd_link_required", + "consent_expire_in_days" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index e247b19d7..7f6a5e64d 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -69,6 +69,10 @@ mosip.esignet.captcha.site-key=${esignet.captcha.site.key} mosip.esignet.signup-id-token-expire-seconds=300 mosip.esignet.signup-id-token-audience=mosip-signup-oauth-client +## Validation schema files +mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json +mosip.esignet.additional-config.schema.url=classpath:additional_config_request_schema.json + ## Transaction halted with prepare-signup-redirect wait time to resume back with complete-signup-redirect API mosip.esignet.signup.halt.expire-seconds=1800 diff --git a/esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java b/esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java new file mode 100644 index 000000000..4836e2fec --- /dev/null +++ b/esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java @@ -0,0 +1,342 @@ +package io.mosip.esignet.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.esignet.TestUtil; +import io.mosip.esignet.core.constants.Constants; +import io.mosip.esignet.core.constants.ErrorConstants; +import io.mosip.esignet.core.dto.ClientDetailCreateRequestV3; +import io.mosip.esignet.core.dto.ClientDetailUpdateRequestV3; +import io.mosip.esignet.core.dto.RequestWrapper; +import lombok.AllArgsConstructor; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Stream; + +import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +public class ClientMgmtV2ControllerParameterizedTest { + + private static Map jwk = TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(); + + @Autowired + private MockMvc mockMvc; + + ObjectMapper objectMapper = new ObjectMapper(); + + @AllArgsConstructor + public static class TestCase { + String title; + ClientDetailCreateRequestV3 clientDetailCreateRequestV3; + ClientDetailUpdateRequestV3 clientDetailUpdateRequestV3; + String clientIdQueryParam; + String errorCode; + } + + @ParameterizedTest(name = "{0}") + @MethodSource("getTestCases") + public void testClientManagementEndpoints(TestCase testCase) throws Exception { + ClientDetailCreateRequestV3 clientDetailCreateRequestV3 = testCase.clientDetailCreateRequestV3; + ClientDetailUpdateRequestV3 clientDetailUpdateRequestV3 = testCase.clientDetailUpdateRequestV3; + String clientIdQueryParam = testCase.clientIdQueryParam; + String errorCode = testCase.errorCode; + + if (clientDetailCreateRequestV3 != null) { + ResultActions createResultActions = mockMvc.perform(post("/client-mgmt/client") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(getRequestWrapper(clientDetailCreateRequestV3))); + evaluateResultActions(createResultActions, clientDetailCreateRequestV3.getClientId(), + Constants.CLIENT_ACTIVE_STATUS, errorCode); + } + + if (clientDetailUpdateRequestV3 != null) { + ResultActions updateResultActions = mockMvc.perform(put("/client-mgmt/client/" + clientIdQueryParam) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(getRequestWrapper(clientDetailUpdateRequestV3))); + evaluateResultActions(updateResultActions, clientIdQueryParam, + clientDetailUpdateRequestV3.getStatus(), errorCode); + } + } + + private static Stream getTestCases() { + Map validAdditionalConfig = getValidAdditionalConfig(); + List TEST_CASES = new ArrayList<>(Arrays.asList( + // test-name, ClientDetailCreateRequest, ClientDetailUpdateRequest, clientIdQueryParam, errorCode + new TestCase("Successful create", new ClientDetailCreateRequestV3("client-id-#12c", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, null), + new TestCase("Duplicate client id", new ClientDetailCreateRequestV3("client-id-#12c", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.DUPLICATE_CLIENT_ID), + new TestCase("With Null ClientId", new ClientDetailCreateRequestV3(null, "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_ID), + new TestCase("With Empty ClientName", new ClientDetailCreateRequestV3("client-id-v2", " ", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_NAME), + new TestCase("With Invalid Language_code", new ClientDetailCreateRequestV3("client-id-v2", "clientname", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("abc", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_NAME_MAP_KEY), + new TestCase("With Invalid public key", new ClientDetailCreateRequestV3("client-id-v2", "Test client", new HashMap<>(), + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_PUBLIC_KEY), + new TestCase("With null public key", new ClientDetailCreateRequestV3("client-id-v2", "Test client", null, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_PUBLIC_KEY), + new TestCase("With null relying party id", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + null, Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_RP_ID), + new TestCase("With empty relying party id", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + " ", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_RP_ID), + new TestCase("With null user claims", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", null, + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLAIM), + new TestCase("With empty user claims", new ClientDetailCreateRequestV3("client-id-v2#2", "Test client", + TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(), + "rp-id", Arrays.asList(), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, null), + new TestCase("With invalid user claims", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", Arrays.asList(null, "given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLAIM), + new TestCase("With valid & invalid user claims", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", Arrays.asList("birthdate", "given_name", "gender", "street"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLAIM), + new TestCase("With invalid acr", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", Arrays.asList("birthdate", "given_name", "gender"), + Arrays.asList("mosip:idp:acr:static-code-1"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_ACR), + new TestCase("With patterned redirectUri", new ClientDetailCreateRequestV3("client-id-v2#3", "Test client", jwk, + "rp-id", Arrays.asList("birthdate", "given_name", "gender"), + Arrays.asList("mosip:idp:acr:static-code-1"), "https://logo-url/png", + Arrays.asList("https://dev.mosip.net/home/**"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_ACR), + new TestCase("ClientId with spaces", new ClientDetailCreateRequestV3("client id", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_ID), + new TestCase("RP-Id with spaces", new ClientDetailCreateRequestV3("cid#1", "client-name", jwk, + "rp id 1", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_RP_ID), + new TestCase("with duplicate key", new ClientDetailCreateRequestV3("client-id-v2#4", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, "unknown_error"), + new TestCase("update with invalid clientId", null, new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name#1", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), "cid#1", "invalid_client_id"), + new TestCase("update with invalid language_code", null, new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("abc", "clientname"); + }}, validAdditionalConfig), "cid#1", "invalid_language_code"), + new TestCase("update client-details", new ClientDetailCreateRequestV3("client-id-up2", "client-name", + TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(), + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png", "io.mosip.residentapp://oauth"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name#1", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), "client-id-up2", null), + new TestCase("Create with app redirect URL", new ClientDetailCreateRequestV3("client-id-v2", "client-name", + TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(), + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("io.mosip.residentapp://oauth", "residentapp://oauth/*"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, null) + )); + List> invalidAdditionalConfigs = getInvalidAdditionalConfigs(); + for (Map additionalConfig : invalidAdditionalConfigs) { + TEST_CASES.add( + new TestCase("with invalid additional config", new ClientDetailCreateRequestV3("client-id-v2", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, + additionalConfig), null, null, ErrorConstants.INVALID_ADDITIONAL_CONFIG) + ); + TEST_CASES.add( + new TestCase("update with invalid additional config", + null, + new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png", "io.mosip.residentapp://oauth"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name#1", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, additionalConfig), "client-id-up2", ErrorConstants.INVALID_ADDITIONAL_CONFIG) + ); + } + return TEST_CASES.stream(); + } + + private String getRequestWrapper(Object request) throws JsonProcessingException { + RequestWrapper wrapper = new RequestWrapper<>(); + wrapper.setRequest(request); + wrapper.setRequestTime(ZonedDateTime + .now(ZoneOffset.UTC) + .format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + return objectMapper.writeValueAsString(wrapper); + } + + private void evaluateResultActions(ResultActions resultActions, String clientId, String status, String errorCode) + throws Exception { + if (errorCode != null) { + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorCode").value(errorCode)); + } else { + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isEmpty()) + .andExpect(jsonPath("$.response").isNotEmpty()) + .andExpect(jsonPath("$.response.clientId").value(clientId)) + .andExpect(jsonPath("$.response.status").value(Constants.CLIENT_ACTIVE_STATUS)); + } + } + + public static Map getValidAdditionalConfig() { + Map validAdditionalConfig = new HashMap<>(); + validAdditionalConfig.put("userinfo_response_type", "JWS"); + validAdditionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", ""), + Map.entry("subTitle", "") + )); + validAdditionalConfig.put("signup_banner_required", true); + validAdditionalConfig.put("forgot_pwd_link_required", true); + validAdditionalConfig.put("consent_expire_in_days", 1); + return validAdditionalConfig; + } + + public static List> getInvalidAdditionalConfigs() { + List> invalidAdditionalConfigs = new ArrayList<>(); + + invalidAdditionalConfigs.add(null); + + Map additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("userinfo_response_type", "ABC"); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Collections.emptyMap()); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", 1), //anything other than string + Map.entry("subTitle", "") + )); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("signup_banner_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("forgot_pwd_link_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("consent_expire_in_days", ""); // anything other than number + invalidAdditionalConfigs.add(additionalConfig); + + return invalidAdditionalConfigs; + } +} \ No newline at end of file diff --git a/esignet-service/src/test/resources/additional_config_request_schema.json b/esignet-service/src/test/resources/additional_config_request_schema.json new file mode 100644 index 000000000..adfda02d4 --- /dev/null +++ b/esignet-service/src/test/resources/additional_config_request_schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "userinfo_response_type": { + "type": "string", + "enum": ["JWS", "JWE"] + }, + "purpose": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "subTitle": { + "type": "string" + } + }, + "required": ["type", "title", "subTitle"], + "additionalProperties": true + }, + "signup_banner_required": { + "type": "boolean" + }, + "forgot_pwd_link_required": { + "type": "boolean" + }, + "consent_expire_in_days": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "userinfo_response_type", + "purpose", + "signup_banner_required", + "forgot_pwd_link_required", + "consent_expire_in_days" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/esignet-service/src/test/resources/application-test.properties b/esignet-service/src/test/resources/application-test.properties index bb746f7b9..295ab384f 100644 --- a/esignet-service/src/test/resources/application-test.properties +++ b/esignet-service/src/test/resources/application-test.properties @@ -53,6 +53,7 @@ mosip.esignet.signup-id-token-audience=mosip-signup-client mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema_test.json +mosip.esignet.additional-config.schema.url=classpath:additional_config_request_schema.json ## ------------------------------------------ e-Signet binding ---------------------------------------------------------