diff --git a/api-test/pom.xml b/api-test/pom.xml index bf9977b36..0f909aa6f 100644 --- a/api-test/pom.xml +++ b/api-test/pom.xml @@ -163,7 +163,7 @@ - io.mosip.testrig.apirig.testrunner.MosipTestRunner + io.mosip.testrig.apirig.esignet.testrunner.MosipTestRunner @@ -828,21 +828,21 @@ value="esignet/OAuthDetailsRequestV3/OAuthDetailsRequest.yml" /> - + - + - + @@ -850,7 +850,7 @@ value="esignet/PrepareSignupRedirect/PrepareSignupRedirect.yml" /> - + @@ -858,14 +858,14 @@ value="esignet/SignupAuthorize/SignupAuthorize.yml" /> - + - + @@ -873,7 +873,7 @@ value="esignet/SignupAuthorizeCode/SignupAuthorizeCode.yml" /> - + @@ -881,30 +881,30 @@ value="esignet/InitiateIdVerification/InitiateIdVerification.yml" /> - + - + - + - + \ No newline at end of file diff --git a/client-management-service-impl/src/main/java/io/mosip/esignet/entity/ClientDetail.java b/client-management-service-impl/src/main/java/io/mosip/esignet/entity/ClientDetail.java index 0b1f6840a..c9dd8a6cd 100644 --- a/client-management-service-impl/src/main/java/io/mosip/esignet/entity/ClientDetail.java +++ b/client-management-service-impl/src/main/java/io/mosip/esignet/entity/ClientDetail.java @@ -45,7 +45,7 @@ public class ClientDetail { private String redirectUris; @NotBlank(message = INVALID_PUBLIC_KEY) - @Column(name = "public_key", columnDefinition = "TEXT") + @Column(name = "public_key", columnDefinition = "jsonb") private String publicKey; @NotBlank(message = INVALID_CLAIM) diff --git a/db_scripts/mosip_esignet/ddl/esignet-client_detail.sql b/db_scripts/mosip_esignet/ddl/esignet-client_detail.sql index b9fd5a3ef..d2c67de1d 100644 --- a/db_scripts/mosip_esignet/ddl/esignet-client_detail.sql +++ b/db_scripts/mosip_esignet/ddl/esignet-client_detail.sql @@ -23,16 +23,17 @@ CREATE TABLE 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, cr_dtimes timestamp NOT NULL, upd_dtimes timestamp, - CONSTRAINT pk_clntdtl_id PRIMARY KEY (id), - CONSTRAINT uk_clntdtl_key UNIQUE (public_key) + CONSTRAINT pk_clntdtl_id PRIMARY KEY (id) ); +CREATE UNIQUE INDEX unique_n_value ON client_detail ((public_key->>'n')); + COMMENT ON TABLE client_detail IS 'Contains key alias and metadata of all the keys used in MOSIP system.'; COMMENT ON COLUMN client_detail.id IS 'Client ID: Unique id assigned to registered OIDC client.'; diff --git a/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_rollback.sql b/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_rollback.sql index 1a2e0a41c..0506996eb 100644 --- a/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_rollback.sql +++ b/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_rollback.sql @@ -1 +1,49 @@ -\echo 'Rollback Queries not required for transition from $CURRENT_VERSION to $UPGRADE_VERSION' \ No newline at end of file +\c mosip_esignet + +CREATE OR REPLACE FUNCTION is_column_jsonb( + p_table_name text, + p_column_name text, + p_schema_name text DEFAULT current_schema() +) RETURNS boolean AS $$ +DECLARE + v_column_type text; +BEGIN + -- Get the column data type + SELECT data_type INTO v_column_type + FROM information_schema.columns + WHERE table_schema = p_schema_name + AND table_name = p_table_name + AND column_name = p_column_name; + + -- Handle case when column doesn't exist + IF v_column_type IS NULL THEN + RAISE EXCEPTION 'Column %.% does not exist', p_table_name, p_column_name; + END IF; + + -- Return true if jsonb, false otherwise + RETURN v_column_type = 'jsonb'; + +EXCEPTION + WHEN undefined_table THEN + RAISE EXCEPTION 'Table %.% does not exist', p_schema_name, p_table_name; + WHEN OTHERS THEN + RAISE EXCEPTION 'Error checking column type: %', SQLERRM; +END; +$$ LANGUAGE plpgsql; + +DO $$ +BEGIN +IF is_column_jsonb('client_detail', 'public_key') THEN + IF EXISTS ( + SELECT 1 FROM information_schema.tables + WHERE table_name='client_detail_migr_bkp' + ) THEN + DROP TABLE client_detail; + CREATE TABLE client_detail (LIKE client_detail_migr_bkp including ALL); + INSERT INTO client_detail SELECT * FROM client_detail_migr_bkp; + DROP TABLE client_detail_migr_bkp; + ELSE + RAISE EXCEPTION 'Error: Backup doesn''t exist'; + END IF; +END IF; +END $$ \ 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 97229f486..e96ef8a6a 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 @@ -1,3 +1,36 @@ +\c mosip_esignet + +CREATE OR REPLACE FUNCTION is_column_jsonb( + p_table_name text, + p_column_name text, + p_schema_name text DEFAULT current_schema() +) RETURNS boolean AS $$ +DECLARE + v_column_type text; +BEGIN + -- Get the column data type + SELECT data_type INTO v_column_type + FROM information_schema.columns + WHERE table_schema = p_schema_name + AND table_name = p_table_name + AND column_name = p_column_name; + + -- Handle case when column doesn't exist + IF v_column_type IS NULL THEN + RAISE EXCEPTION 'Column %.% does not exist', p_table_name, p_column_name; + END IF; + + -- Return true if jsonb, false otherwise + RETURN v_column_type = 'jsonb'; + +EXCEPTION + WHEN undefined_table THEN + RAISE EXCEPTION 'Table %.% does not exist', p_schema_name, p_table_name; + WHEN OTHERS THEN + RAISE EXCEPTION 'Error checking column type: %', SQLERRM; +END; +$$ LANGUAGE plpgsql; + -- Add the new column with a default value ALTER TABLE client_detail ADD COLUMN additional_config jsonb DEFAULT '{}'::jsonb; @@ -5,4 +38,42 @@ 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; \ No newline at end of file +WHERE additional_config IS NULL; + +DO $$ +BEGIN +IF NOT is_column_jsonb('client_detail', 'public_key') THEN + + -- create backup + DROP TABLE IF EXISTS client_detail_migr_bkp; + CREATE TABLE client_detail_migr_bkp (LIKE client_detail including ALL); + INSERT into client_detail_migr_bkp SELECT * from client_detail; + ---- + + ALTER TABLE client_detail ADD COLUMN public_key_new jsonb; + UPDATE client_detail SET public_key_new = public_key::jsonb; + ALTER TABLE client_detail DROP COLUMN public_key; + ALTER TABLE client_detail RENAME COLUMN public_key_new TO public_key; + + -- inactivating clients with same modulus in public key + WITH duplicates AS ( + SELECT public_key->>'n' as modulus + FROM client_detail + WHERE public_key->>'n' IS NOT NULL + GROUP BY public_key->>'n' + HAVING COUNT(*) > 1 + ) + UPDATE client_detail SET status='INACTIVE', public_key='{}'::jsonb where id IN ( + SELECT + client_detail.id + FROM client_detail + JOIN duplicates ON client_detail.public_key->>'n' = duplicates.modulus); + ---- + + ALTER TABLE client_detail ALTER COLUMN public_key SET NOT NULL; + CREATE UNIQUE INDEX unique_n_value ON client_detail ((public_key->>'n')); + RAISE NOTICE 'Upgrade Successful'; +ELSE + RAISE NOTICE 'Database already uptodate'; +END IF; +END $$ diff --git a/deploy/esignet-apitestrig/README.md b/deploy/esignet-apitestrig/README.md new file mode 100644 index 000000000..a5e277981 --- /dev/null +++ b/deploy/esignet-apitestrig/README.md @@ -0,0 +1,44 @@ +# APITESTRIG + +## Introduction +ApiTestRig will test the working of APIs of the MOSIP modules. + +## Install +* Review `values.yaml` and, Make sure to enable required modules for apitestrig operation. +* Install +```sh +./install.sh +``` +* During the execution of the `install.sh` script, a prompt appears requesting information regarding the presence of a public domain and a valid SSL certificate on the server. +* If the server lacks a public domain and a valid SSL certificate, it is advisable to select the `n` option. Opting it will enable the `init-container` with an `emptyDir` volume and include it in the deployment process. +* The init-container will proceed to download the server's self-signed SSL certificate and mount it to the specified location within the container's Java keystore (i.e., `cacerts`) file. +* This particular functionality caters to scenarios where the script needs to be employed on a server utilizing self-signed SSL certificates. + +## Uninstall +* To uninstall ApiTestRig, run `delete.sh` script. +```sh +./delete.sh +``` + +## Run apitestrig manually + +#### Rancher UI +* Run apitestrig manually via Rancher UI. + ![apitestrig-2.png](../../docs/apitestrig-2.png) +* There are two modes of apitestrig `smoke` & `smokeAndRegression`. +* By default, apitestrig will execute with `smokeAndRegression`.
+ If you want to run apitestrig with only `smoke`.
+ You have to update the `apitestrig` configmap and rerun the specific apitestrig job. + +#### CLI +* Download Kubernetes cluster `kubeconfig` file from `rancher dashboard` to your local. + ![apitestrig-1.png](../../docs/apitestrig-1.png) +* Install `kubectl` package to your local machine. +* Run apitestrig manually via CLI by creating a new job from an existing k8s cronjob. + ``` + kubectl --kubeconfig= -n apitestrig create job --from=cronjob/ + ``` + example: + ``` + kubectl --kubeconfig=/home/xxx/Downloads/qa4.config -n apitestrig create job --from=cronjob/cronjob-apitestrig-masterdata cronjob-apitestrig-masterdata + ``` \ No newline at end of file diff --git a/deploy/esignet-apitestrig/delete.sh b/deploy/esignet-apitestrig/delete.sh new file mode 100755 index 000000000..38c79022c --- /dev/null +++ b/deploy/esignet-apitestrig/delete.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Uninstalls apitestrig +## Usage: ./delete.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function deleting_apitestrig() { + NS=esignet + while true; do + read -p "Are you sure you want to delete apitestrig helm charts?(Y/n) " yn + if [ $yn = "Y" ] + then + helm -n $NS delete esignet-apitestrig + break + else + break + fi + done + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +deleting_apitestrig # calling function \ No newline at end of file diff --git a/deploy/esignet-apitestrig/install.sh b/deploy/esignet-apitestrig/install.sh new file mode 100755 index 000000000..7fed643b6 --- /dev/null +++ b/deploy/esignet-apitestrig/install.sh @@ -0,0 +1,130 @@ +#!/bin/bash +# Installs apitestrig +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +NS=esignet +CHART_VERSION=0.0.1-develop +COPY_UTIL=../copy_cm_func.sh + +echo Create $NS namespace +kubectl create ns $NS + +function installing_apitestrig() { + echo Istio label + kubectl label ns $NS istio-injection=disabled --overwrite + helm repo update + + echo Copy Configmaps + $COPY_UTIL configmap global default $NS + $COPY_UTIL configmap keycloak-host keycloak $NS + $COPY_UTIL configmap artifactory-share artifactory $NS + $COPY_UTIL configmap config-server-share config-server $NS + + echo echo Copy Secrtes + $COPY_UTIL secret keycloak-client-secrets keycloak $NS + $COPY_UTIL secret s3 s3 $NS + $COPY_UTIL secret postgres-postgresql postgres $NS + + echo "Delete s3, db, & apitestrig configmap if exists" + kubectl -n $NS delete --ignore-not-found=true configmap s3 + kubectl -n $NS delete --ignore-not-found=true configmap db + kubectl -n $NS delete --ignore-not-found=true configmap apitestrig + + DB_HOST=$( kubectl -n default get cm global -o json |jq -r '.data."mosip-api-internal-host"' ) + API_INTERNAL_HOST=$( kubectl -n default get cm global -o json |jq -r '.data."mosip-api-internal-host"' ) + ENV_USER=$( kubectl -n default get cm global -o json |jq -r '.data."mosip-api-internal-host"' | awk -F '.' '/api-internal/{print $1"."$2}') + + read -p "Please enter the time(hr) to run the cronjob every day (time: 0-23) : " time + if [ -z "$time" ]; then + echo "ERROT: Time cannot be empty; EXITING;"; + exit 1; + fi + if ! [ $time -eq $time ] 2>/dev/null; then + echo "ERROR: Time $time is not a number; EXITING;"; + exit 1; + fi + if [ $time -gt 23 ] || [ $time -lt 0 ] ; then + echo "ERROR: Time should be in range ( 0-23 ); EXITING;"; + exit 1; + fi + + echo "Do you have public domain & valid SSL? (Y/n) " + echo "Y: if you have public domain & valid ssl certificate" + echo "n: If you don't have a public domain and a valid SSL certificate. Note: It is recommended to use this option only in development environments." + read -p "" flag + + if [ -z "$flag" ]; then + echo "'flag' was provided; EXITING;" + exit 1; + fi + ENABLE_INSECURE='' + if [ "$flag" = "n" ]; then + ENABLE_INSECURE='--set enable_insecure=true'; + fi + + read -p "Please provide the retention days to remove old reports ( Default: 3 )" reportExpirationInDays + + if [[ -z $reportExpirationInDays ]]; then + reportExpirationInDays=3 + fi + if ! [[ $reportExpirationInDays =~ ^[0-9]+$ ]]; then + echo "The variable \"reportExpirationInDays\" should contain only number; EXITING"; + exit 1; + fi + + read -p "Please provide slack webhook URL to notify server end issues on your slack channel : " slackWebhookUrl + + if [ -z $slackWebhookUrl ]; then + echo "slack webhook URL not provided; EXITING;" + exit 1; + fi + + valid_inputs=("yes" "no") + eSignetDeployed="" + + while [[ ! " ${valid_inputs[@]} " =~ " ${eSignetDeployed} " ]]; do + read -p "Is the eSignet service deployed? (yes/no): " eSignetDeployed + eSignetDeployed=${eSignetDeployed,,} # Convert input to lowercase + done + + if [[ $eSignetDeployed == "yes" ]]; then + echo "eSignet service is deployed. Proceeding with installation..." + else + echo "eSignet service is not deployed. hence will be skipping esignet related test-cases..." + fi + + echo Installing esignet apitestrig + helm -n $NS install esignet-apitestrig mosip/apitestrig \ + --set crontime="0 $time * * *" \ + -f values.yaml \ + --version $CHART_VERSION \ + --set apitestrig.configmaps.s3.s3-host='http://minio.minio:9000' \ + --set apitestrig.configmaps.s3.s3-user-key='admin' \ + --set apitestrig.configmaps.s3.s3-region='' \ + --set apitestrig.configmaps.db.db-server="$DB_HOST" \ + --set apitestrig.configmaps.db.db-su-user="postgres" \ + --set apitestrig.configmaps.db.db-port="5432" \ + --set apitestrig.configmaps.apitestrig.ENV_USER="$ENV_USER" \ + --set apitestrig.configmaps.apitestrig.ENV_ENDPOINT="https://$API_INTERNAL_HOST" \ + --set apitestrig.configmaps.apitestrig.ENV_TESTLEVEL="smokeAndRegression" \ + --set apitestrig.configmaps.apitestrig.reportExpirationInDays="$reportExpirationInDays" \ + --set apitestrig.configmaps.apitestrig.slack-webhook-url="$slackWebhookUrl" \ + --set apitestrig.configmaps.apitestrig.eSignetDeployed="$eSignetDeployed" \ + --set apitestrig.configmaps.apitestrig.NS="$NS" \ + $ENABLE_INSECURE + + echo Installed esignet apitestrig. + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +installing_apitestrig # calling function \ No newline at end of file diff --git a/deploy/esignet-apitestrig/values.yaml b/deploy/esignet-apitestrig/values.yaml new file mode 100644 index 000000000..0d4623d75 --- /dev/null +++ b/deploy/esignet-apitestrig/values.yaml @@ -0,0 +1,7 @@ +modules: + esignet: + enabled: true + image: + repository: mosipqa/apitest-esignet + tag: develop + pullPolicy: Always diff --git a/deploy/esignet-global-cm.yaml b/deploy/esignet-global-cm.yaml index 3a5bdb357..8db1d1246 100644 --- a/deploy/esignet-global-cm.yaml +++ b/deploy/esignet-global-cm.yaml @@ -10,6 +10,7 @@ metadata: namespace: esignet data: mosip-version: develop + installation-domain: sandbox.xyz.net mosip-api-host: api.sandbox.xyz.net mosip-iam-external-host: iam.sandbox.xyz.net mosip-api-internal-host: api-internal.sandbox.xyz.net diff --git a/docs/design/enable-new-port.md b/docs/design/enable-new-port.md new file mode 100644 index 000000000..709f42fc2 --- /dev/null +++ b/docs/design/enable-new-port.md @@ -0,0 +1,83 @@ +# Expose an additional port, such as 5433 or any other, for a service in a specific namespace. + +### steps: + +1. Add the Istio Gateway and Virtual service by deploying the istio-addons and update the configuration as given below. + + * Gateway: + ``` + spec: + selector: + istio: ingressgateway-internal + servers: + - hosts: + - ## hostname will be checked only if "protocol" is set to HTTP, not for TCP protocol + port: + name: + number: + protocol: TCP + ``` + + * Virtual-service: + ``` + gateways: + - + hosts: + - '*' + tcp: + - match: + - port: ## ingress gateway container port + route: + - destination: + host: + port: + number: 5432 ## pod's service port + ``` + +2. Update the IstioOperator (IOP) configuration as given below by editing the IOP in the istio-system namespace. + + ``` + $ kubectl -n istio-system edit istiooperator istio-operators-mosip + ``` + + ``` + k8s: + service: + ports: + - name: + nodePort: + port: + protocol: TCP + targetPort: + ``` + +3. Update the configuration as given below within the `stream` block of the nginx.conf file of nginx node. + ``` + upstream { + server :; + server :; + server :; + server :; + server :; + server :; + server :; + server :; + + } + + Note: The upstream block is usually followed by a server block where the traffic from clients is forwarded to the backend upstream group. + server{ + listen :; + proxy_pass ; + } + ``` + +4. Restart the Nginx service. + ``` + sudo systemctl restart nginx + ``` + +5. Expose the port and nodePort from the AWS cloud and UFW firewall. + * < port >: needs to be exposed for the nginx node. + * < nodeport >: needs to be exposed for all the k8's cluster nodes. + diff --git a/esignet-core/pom.xml b/esignet-core/pom.xml index 7b27d474a..2c70fa859 100644 --- a/esignet-core/pom.xml +++ b/esignet-core/pom.xml @@ -22,7 +22,6 @@ 11 2.9.5 2.9.8 - 2.15.0 2.15.0 2.15.0 1.2.1.0 @@ -100,20 +99,44 @@ micrometer-registry-prometheus runtime + + com.networknt + json-schema-validator + 1.5.1 + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + com.fasterxml.jackson.module + jackson-module-afterburner + + + com.fasterxml.jackson.core jackson-core - ${jackson.databind} com.fasterxml.jackson.core jackson-annotations - ${jackson.databind} com.fasterxml.jackson.core jackson-databind - ${jackson.databind} com.fasterxml.jackson.datatype @@ -181,4 +204,5 @@ + 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 57ac0110b..2b8248378 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 @@ -92,6 +92,5 @@ public class ErrorConstants { public static final String INVALID_VERIFICATION = "invalid_verification"; public static final String INVALID_VERIFIED_CLAIMS = "invalid_verified_claims"; public static final String INVALID_PURPOSE="invalid_purpose"; - public static final String VERIFICATION_INCOMPLETE = "verification_incomplete"; } diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/OAuthDetailRequest.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/OAuthDetailRequest.java index 547f265c2..9063f2b40 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/dto/OAuthDetailRequest.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/OAuthDetailRequest.java @@ -5,16 +5,11 @@ */ package io.mosip.esignet.core.dto; -import io.mosip.esignet.api.dto.claim.Claims; import io.mosip.esignet.api.dto.claim.ClaimsV2; -import io.mosip.esignet.core.validator.OIDCDisplay; -import io.mosip.esignet.core.validator.OIDCPrompt; -import io.mosip.esignet.core.validator.OIDCResponseType; -import io.mosip.esignet.core.validator.OIDCScope; +import io.mosip.esignet.core.constants.ErrorConstants; +import io.mosip.esignet.core.validator.*; import lombok.Data; -import io.mosip.esignet.core.validator.RedirectURL; - import javax.validation.Valid; import javax.validation.constraints.NotBlank; @@ -76,6 +71,7 @@ public class OAuthDetailRequest { * names of the individual Claims being requested as the member names. */ @Valid + @ClaimsSchema(message = ErrorConstants.INVALID_CLAIM) private ClaimsV2 claims; /** diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClaimsSchema.java b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClaimsSchema.java new file mode 100644 index 000000000..a53308b6c --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClaimsSchema.java @@ -0,0 +1,26 @@ +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.ElementType.TYPE_USE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD, TYPE_USE}) +@Retention(RUNTIME) +@Constraint(validatedBy = ClaimsSchemaValidator.class) +@Documented +public @interface ClaimsSchema { + + String message() default ErrorConstants.INVALID_CLAIM; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClaimsSchemaValidator.java b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClaimsSchemaValidator.java new file mode 100644 index 000000000..60c70f73e --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClaimsSchemaValidator.java @@ -0,0 +1,77 @@ +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.api.dto.claim.ClaimsV2; +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.*; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import java.util.stream.Collectors; + + +@Slf4j +public class ClaimsSchemaValidator implements ConstraintValidator { + + + @Value("${mosip.esignet.claims.schema.url}") + private String schemaUrl; + + private volatile JsonSchema cachedSchema; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private ResourceLoader resourceLoader; + + + @Override + public boolean isValid(ClaimsV2 claims, ConstraintValidatorContext context) { + Set errors = null; + try { + JsonNode jsonNode = objectMapper.valueToTree(claims); + errors = getCachedSchema().validate(jsonNode); + if(errors.isEmpty())return true; + } catch (Exception e) { + log.error("Error validating claims schema", e); + } + log.error("Validation failed for claims: {}", 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/ValidatorTest.java b/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java index 4db70eb24..75ca8b8c3 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 @@ -5,6 +5,10 @@ */ package io.mosip.esignet.core; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.esignet.api.dto.claim.ClaimDetail; +import io.mosip.esignet.api.dto.claim.ClaimsV2; import io.mosip.esignet.api.spi.Authenticator; import io.mosip.esignet.core.dto.OAuthDetailRequestV2; import io.mosip.esignet.core.exception.EsignetException; @@ -15,11 +19,16 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.Environment; +import org.springframework.core.io.DefaultResourceLoader; +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; @@ -28,12 +37,16 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; - import static org.mockito.Mockito.when; + +@SpringBootTest @RunWith(MockitoJUnitRunner.class) public class ValidatorTest { + @InjectMocks + ClaimsSchemaValidator claimSchemaValidator; + @Mock AuthenticationContextClassRefUtil authenticationContextClassRefUtil; @@ -43,6 +56,17 @@ public class ValidatorTest { @Mock Environment environment; + + @Mock + RestTemplate restTemplate; + + + ResourceLoader resourceLoader= new DefaultResourceLoader(); + + ObjectMapper mapper= new ObjectMapper(); + + + private Map discoveryMap = new HashMap<>(); @Before @@ -647,4 +671,116 @@ public void test_ClientNameLangValidator_WithInValidDetail_thenFail(){ 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)); + } } diff --git a/esignet-core/src/test/resources/verified_claims_request_schema_test.json b/esignet-core/src/test/resources/verified_claims_request_schema_test.json new file mode 100644 index 000000000..fa4590b1f --- /dev/null +++ b/esignet-core/src/test/resources/verified_claims_request_schema_test.json @@ -0,0 +1,531 @@ +{ + "$id": "https://bitbucket.org/openid/ekyc-ida/raw/master/schema/verified_claims_request.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "check_details": { + "type": "array", + "prefixItems": [ + { + "check_id": { + "type": "string" + }, + "check_method": { + "type": "string" + }, + "organization": { + "type": "string" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + ] + }, + "claims_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "minProperties": 1 + } + ] + }, + "constrainable_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + }, + "value": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + } + } + ] + }, + "datetime_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "max_age": { + "type": "integer", + "minimum": 0 + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "document_details": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_issuance": { + "$ref": "#/$defs/datetime_element" + }, + "document_number": { + "$ref": "#/$defs/simple_element" + }, + "issuer": { + "type": "object", + "properties": { + "country": { + "$ref": "#/$defs/simple_element" + }, + "country_code": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "jurisdiction": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + }, + "personal_number": { + "$ref": "#/$defs/simple_element" + }, + "serial_number": { + "$ref": "#/$defs/simple_element" + } + } + }, + "evidence": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "object", + "properties": { + "value": { + "enum": [ + "document", + "electronic_record", + "vouch", + "electronic_signature" + ] + } + } + }, + "attachments": { + "$ref": "#/$defs/simple_element" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "value": "electronic_signature" + } + } + }, + "then": { + "properties": { + "created_at": { + "$ref": "#/$defs/datetime_element" + }, + "issuer": { + "$ref": "#/$defs/simple_element" + }, + "serial_number": { + "$ref": "#/$defs/simple_element" + }, + "signature_type": { + "$ref": "#/$defs/simple_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "document" + } + } + }, + "then": { + "properties": { + "check_details": { + "$ref": "#/$defs/check_details" + }, + "document_details": { + "$ref": "#/$defs/document_details" + }, + "method": { + "$ref": "#/$defs/constrainable_element" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "electronic_record" + } + } + }, + "then": { + "properties": { + "check_details": { + "$ref": "#/$defs/check_details" + }, + "record": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "created_at": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "derived_claims": { + "$ref": "#/$defs/claims_element" + }, + "source": { + "type": "object", + "properties": { + "country": { + "$ref": "#/$defs/simple_element" + }, + "country_code": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + } + } + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "vouch" + } + } + }, + "then": { + "properties": { + "attestation": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_issuance": { + "$ref": "#/$defs/datetime_element" + }, + "derived_claims": { + "$ref": "#/$defs/claims_element" + }, + "reference_number": { + "$ref": "#/$defs/simple_element" + }, + "voucher": { + "type": "object", + "properties": { + "birthdate": { + "$ref": "#/$defs/datetime_element" + }, + "country": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "occupation": { + "$ref": "#/$defs/simple_element" + }, + "organization": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + } + } + }, + "check_details": { + "$ref": "#/$defs/check_details" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + } + ] + }, + "simple_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "verified_claims": { + "oneOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/verified_claims_def" + } + ] + } + }, + { + "$ref": "#/$defs/verified_claims_def" + } + ] + }, + "verified_claims_def": { + "type": "object", + "required": [ + "verification", + "claims" + ], + "additionalProperties": false, + "properties": { + "claims": { + "$ref": "#/$defs/claims_element" + }, + "verification": { + "type": "object", + "required": [ + "trust_framework" + ], + "additionalProperties": true, + "properties": { + "assurance_level": { + "$ref": "#/$defs/constrainable_element" + }, + "assurance_process": { + "type": "object", + "properties": { + "assurance_details": { + "type": "array", + "items": { + "oneOf": [ + { + "assurance_classification": { + "$ref": "#/$defs/constrainable_element" + }, + "assurance_type": { + "$ref": "#/$defs/constrainable_element" + }, + "evidence_ref": { + "type": "object", + "required": [ + "txn" + ], + "additionalProperties": true, + "properties": { + "evidence_classification": { + "$ref": "#/$defs/constrainable_element" + }, + "evidence_metadata": { + "$ref": "#/$defs/constrainable_element" + }, + "txn": { + "$ref": "#/$defs/constrainable_element" + } + } + } + } + ] + }, + "minItems": 1 + }, + "policy": { + "$ref": "#/$defs/constrainable_element" + }, + "procedure": { + "$ref": "#/$defs/constrainable_element" + } + } + }, + "evidence": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/$defs/evidence" + } + ] + }, + "minItems": 1 + }, + "time": { + "$ref": "#/$defs/datetime_element" + }, + "trust_framework": { + "$ref": "#/$defs/constrainable_element" + }, + "verification_process": { + "$ref": "#/$defs/simple_element" + } + } + } + } + } + }, + "properties": { + "id_token": { + "type": "object", + "additionalProperties": true, + "properties": { + "verified_claims": { + "$ref": "#/$defs/verified_claims" + } + } + }, + "userinfo": { + "type": "object", + "additionalProperties": true, + "properties": { + "verified_claims": { + "$ref": "#/$defs/verified_claims" + } + } + } + } +} \ No newline at end of file diff --git a/esignet-service/pom.xml b/esignet-service/pom.xml index 7ed5cee60..d6ebbcb35 100644 --- a/esignet-service/pom.xml +++ b/esignet-service/pom.xml @@ -21,7 +21,6 @@ 11 2.9.5 2.9.8 - 2.12.0 2.12.0 2.12.0 diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index ed7004346..8c5361ede 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -69,6 +69,8 @@ mosip.esignet.captcha.site-key=${esignet.captcha.site.key} mosip.esignet.signup-id-token-expire-seconds=1800 mosip.esignet.signup-id-token-audience=mosip-signup-oauth-client +mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json + ## ------------------------------------------ e-Signet binding --------------------------------------------------------- mosip.esignet.binding.salt-length=16 mosip.esignet.binding.audience-id=esignet-binding diff --git a/esignet-service/src/main/resources/application-local.properties b/esignet-service/src/main/resources/application-local.properties index 802a3a338..5412b7b03 100644 --- a/esignet-service/src/main/resources/application-local.properties +++ b/esignet-service/src/main/resources/application-local.properties @@ -74,6 +74,9 @@ mosip.esignet.captcha.site-key=test-site-key mosip.esignet.signup-id-token-expire-seconds=1800 mosip.esignet.signup-id-token-audience=mosip-signup-oauth-client +mosip.esignet.host=localhost +mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json + ## ------------------------------------------ e-Signet binding --------------------------------------------------------- mosip.esignet.binding.salt-length=16 mosip.esignet.binding.audience-id=esignet-binding @@ -263,6 +266,7 @@ spring.datasource.url=jdbc:postgresql://${mosip.esignet.database.hostname}:${mos spring.datasource.username=${mosip.esignet.database.username} spring.datasource.password=postgres + spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none diff --git a/esignet-service/src/main/resources/bootstrap.properties b/esignet-service/src/main/resources/bootstrap.properties index 4077637b0..b79c7e7a3 100644 --- a/esignet-service/src/main/resources/bootstrap.properties +++ b/esignet-service/src/main/resources/bootstrap.properties @@ -45,4 +45,7 @@ logging.level.io.mosip.esignet=INFO management.endpoint.metrics.enabled=true management.endpoints.web.exposure.include=* management.endpoint.prometheus.enabled=true -management.metrics.export.prometheus.enabled=true \ No newline at end of file +management.metrics.export.prometheus.enabled=true + +# to accept string as valid type for jsonb column +spring.datasource.hikari.data-source-properties=stringtype=unspecified \ No newline at end of file diff --git a/esignet-service/src/main/resources/verified_claims_request_schema.json b/esignet-service/src/main/resources/verified_claims_request_schema.json new file mode 100644 index 000000000..c0a504aaa --- /dev/null +++ b/esignet-service/src/main/resources/verified_claims_request_schema.json @@ -0,0 +1,542 @@ +{ + "$id": "https://bitbucket.org/openid/ekyc-ida/raw/master/schema/verified_claims_request.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "check_details": { + "type": "array", + "prefixItems": [ + { + "check_id": { + "type": "string" + }, + "check_method": { + "type": "string" + }, + "organization": { + "type": "string" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + ] + }, + "claims_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + }, + "value":{ + "type": "string", + "minLength": 3 + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + } + } + ] + }, + "minProperties": 1 + } + ] + }, + "constrainable_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + }, + "value": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + } + } + ] + }, + "datetime_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "max_age": { + "type": "integer", + "minimum": 0 + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "document_details": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_issuance": { + "$ref": "#/$defs/datetime_element" + }, + "document_number": { + "$ref": "#/$defs/simple_element" + }, + "issuer": { + "type": "object", + "properties": { + "country": { + "$ref": "#/$defs/simple_element" + }, + "country_code": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "jurisdiction": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + }, + "personal_number": { + "$ref": "#/$defs/simple_element" + }, + "serial_number": { + "$ref": "#/$defs/simple_element" + } + } + }, + "evidence": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "object", + "properties": { + "value": { + "enum": [ + "document", + "electronic_record", + "vouch", + "electronic_signature" + ] + } + } + }, + "attachments": { + "$ref": "#/$defs/simple_element" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "value": "electronic_signature" + } + } + }, + "then": { + "properties": { + "created_at": { + "$ref": "#/$defs/datetime_element" + }, + "issuer": { + "$ref": "#/$defs/simple_element" + }, + "serial_number": { + "$ref": "#/$defs/simple_element" + }, + "signature_type": { + "$ref": "#/$defs/simple_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "document" + } + } + }, + "then": { + "properties": { + "check_details": { + "$ref": "#/$defs/check_details" + }, + "document_details": { + "$ref": "#/$defs/document_details" + }, + "method": { + "$ref": "#/$defs/constrainable_element" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "electronic_record" + } + } + }, + "then": { + "properties": { + "check_details": { + "$ref": "#/$defs/check_details" + }, + "record": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "created_at": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "derived_claims": { + "$ref": "#/$defs/claims_element" + }, + "source": { + "type": "object", + "properties": { + "country": { + "$ref": "#/$defs/simple_element" + }, + "country_code": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + } + } + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "vouch" + } + } + }, + "then": { + "properties": { + "attestation": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_issuance": { + "$ref": "#/$defs/datetime_element" + }, + "derived_claims": { + "$ref": "#/$defs/claims_element" + }, + "reference_number": { + "$ref": "#/$defs/simple_element" + }, + "voucher": { + "type": "object", + "properties": { + "birthdate": { + "$ref": "#/$defs/datetime_element" + }, + "country": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "occupation": { + "$ref": "#/$defs/simple_element" + }, + "organization": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + } + } + }, + "check_details": { + "$ref": "#/$defs/check_details" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + } + ] + }, + "simple_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "verified_claims": { + "oneOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/verified_claims_def" + } + ] + } + }, + { + "$ref": "#/$defs/verified_claims_def" + } + ] + }, + "verified_claims_def": { + "type": "object", + "required": [ + "verification", + "claims" + ], + "additionalProperties": false, + "properties": { + "claims": { + "$ref": "#/$defs/claims_element" + }, + "verification": { + "type": "object", + "required": [ + "trust_framework" + ], + "additionalProperties": true, + "properties": { + "assurance_level": { + "$ref": "#/$defs/constrainable_element" + }, + "assurance_process": { + "type": "object", + "properties": { + "assurance_details": { + "type": "array", + "items": { + "oneOf": [ + { + "assurance_classification": { + "$ref": "#/$defs/constrainable_element" + }, + "assurance_type": { + "$ref": "#/$defs/constrainable_element" + }, + "evidence_ref": { + "type": "object", + "required": [ + "txn" + ], + "additionalProperties": true, + "properties": { + "evidence_classification": { + "$ref": "#/$defs/constrainable_element" + }, + "evidence_metadata": { + "$ref": "#/$defs/constrainable_element" + }, + "txn": { + "$ref": "#/$defs/constrainable_element" + } + } + } + } + ] + }, + "minItems": 1 + }, + "policy": { + "$ref": "#/$defs/constrainable_element" + }, + "procedure": { + "$ref": "#/$defs/constrainable_element" + } + } + }, + "evidence": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/$defs/evidence" + } + ] + }, + "minItems": 1 + }, + "time": { + "$ref": "#/$defs/datetime_element" + }, + "trust_framework": { + "$ref": "#/$defs/constrainable_element" + }, + "verification_process": { + "$ref": "#/$defs/simple_element" + } + } + } + } + } + }, + "properties": { + "id_token": { + "type": "object", + "additionalProperties": true, + "properties": { + "verified_claims": { + "$ref": "#/$defs/verified_claims" + } + } + }, + "userinfo": { + "type": "object", + "additionalProperties": true, + "properties": { + "verified_claims": { + "$ref": "#/$defs/verified_claims" + } + } + } + } +} \ No newline at end of file diff --git a/esignet-service/src/test/java/io/mosip/esignet/controllers/AuthorizationControllerTest.java b/esignet-service/src/test/java/io/mosip/esignet/controllers/AuthorizationControllerTest.java index 8453bcd6d..24005d003 100644 --- a/esignet-service/src/test/java/io/mosip/esignet/controllers/AuthorizationControllerTest.java +++ b/esignet-service/src/test/java/io/mosip/esignet/controllers/AuthorizationControllerTest.java @@ -5,8 +5,12 @@ */ package io.mosip.esignet.controllers; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.mosip.esignet.api.dto.AuthChallenge; +import io.mosip.esignet.api.dto.claim.ClaimDetail; +import io.mosip.esignet.api.dto.claim.ClaimsV2; import io.mosip.esignet.api.spi.AuditPlugin; import io.mosip.esignet.api.util.ConsentAction; import io.mosip.esignet.core.config.LocalAuthenticationEntryPoint; @@ -27,12 +31,15 @@ import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; + import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.web.client.RestTemplate; import java.time.ZoneOffset; import java.time.ZonedDateTime; @@ -73,18 +80,52 @@ public class AuthorizationControllerTest { @MockBean CacheUtilService cacheUtilService; + + @MockBean + RestTemplate restTemplate; + @MockBean LocalAuthenticationEntryPoint localAuthenticationEntryPoint; + + @Value("${mosip.esignet.claims.schema.url}") + private String schemaUrl; + ObjectMapper objectMapper = new ObjectMapper(); + ClaimDetail claimDetail; + + ClaimsV2 claimsV2; + + + @Before - public void init() throws EsignetException { + public void init() throws EsignetException, JsonProcessingException { HashSet acrValues = new HashSet<>(); acrValues.add("mosip:idp:acr:static-code"); acrValues.add("mosip:idp:acr:biometrics"); acrValues.add("mosip:idp:acr:linked-wallet"); when(authenticationContextClassRefUtil.getSupportedACRValues()).thenReturn(acrValues); + + 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 = objectMapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = objectMapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + + + claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + } @@ -125,6 +166,7 @@ public void getOauthDetails_withInvalidRedirectUri_returnErrorResponse() throws oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -149,11 +191,13 @@ public void getOauthDetails_withInvalidAcr_returnSuccessResponse() throws Except oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); wrapper.setRequest(oauthDetailRequest); + OAuthDetailResponseV1 oauthDetailResponse = new OAuthDetailResponseV1(); oauthDetailResponse.setTransactionId("qwertyId"); when(authorizationService.getOauthDetails(oauthDetailRequest)).thenReturn(oauthDetailResponse); @@ -176,6 +220,7 @@ public void getOauthDetails_withInvalidDisplay_returnErrorResponse() throws Exce oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -200,6 +245,7 @@ public void getOauthDetails_withInvalidPrompt_returnErrorResponse() throws Excep oauthDetailRequest.setPrompt("touch"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -224,6 +270,7 @@ public void getOauthDetails_withInvalidResponseType_returnErrorResponse() throws oauthDetailRequest.setPrompt("none"); oauthDetailRequest.setResponseType("implicit"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -248,6 +295,7 @@ public void getOauthDetails_withOnlyOpenIdScope_returnSuccessResponse() throws E oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -275,6 +323,7 @@ public void getOauthDetails_withOutOpenIdScope_returnSuccessResponse() throws Ex oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -299,6 +348,7 @@ public void getOauthDetails_withOpenIdScope_returnSuccessResponse() throws Excep oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -326,6 +376,7 @@ public void getOauthDetails_withOnlyAuthorizeScope_returnSuccessResponse() throw oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -353,6 +404,7 @@ public void getOauthDetails_withAuthorizeAndOpenIdScope_returnSuccessResponse() oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -396,6 +448,7 @@ public void getOauthDetailsV2_withInvalidTimestamp_returnErrorResponse() throws oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); requestTime = requestTime.plusMinutes(10); @@ -430,6 +483,7 @@ public void getOauthDetailsV2_withInvalidRedirectUri_returnErrorResponse() throw oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -461,6 +515,7 @@ public void getOauthDetailsV2_withInvalidAcr_returnSuccessResponse() throws Exce oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -496,6 +551,7 @@ public void getOauthDetailsV2_withInvalidChallengeCode_returnErrorResponse() thr oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); oauthDetailRequest.setCodeChallenge("123"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -529,6 +585,7 @@ public void getOauthDetailsV2_withUnsupportedChallengeCodeMethod_returnErrorResp oauthDetailRequest.setNonce("23424234TY"); oauthDetailRequest.setCodeChallenge("123"); oauthDetailRequest.setCodeChallengeMethod("S123"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -561,6 +618,7 @@ public void getOauthDetailsV2_withInvalidDisplay_returnErrorResponse() throws Ex oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -592,6 +650,7 @@ public void getOauthDetailsV2_withInvalidPrompt_returnErrorResponse() throws Exc oauthDetailRequest.setPrompt("touch"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -623,6 +682,7 @@ public void getOauthDetailsV2_withInvalidResponseType_returnErrorResponse() thro oauthDetailRequest.setPrompt("none"); oauthDetailRequest.setResponseType("implicit"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -654,6 +714,7 @@ public void getOauthDetailsV2_withOnlyOpenIdScope_returnSuccessResponse() throws oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -688,6 +749,7 @@ public void getOauthDetailsV2_withOutOpenIdScope_returnErrorResponse() throws Ex oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -719,6 +781,7 @@ public void getOauthDetailsV2_withOpenIdScope_returnSuccessResponse() throws Exc oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -753,6 +816,7 @@ public void getOauthDetailsV2_withOnlyAuthorizeScope_returnSuccessResponse() thr oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); @@ -787,6 +851,7 @@ public void getOauthDetailsV2_withAuthorizeAndOpenIdScope_returnSuccessResponse( oauthDetailRequest.setPrompt("login"); oauthDetailRequest.setResponseType("code"); oauthDetailRequest.setNonce("23424234TY"); + oauthDetailRequest.setClaims(claimsV2); ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); RequestWrapper wrapper = new RequestWrapper<>(); wrapper.setRequestTime(requestTime.format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); diff --git a/esignet-service/src/test/java/io/mosip/esignet/flows/AuthCodeFlowTest.java b/esignet-service/src/test/java/io/mosip/esignet/flows/AuthCodeFlowTest.java index 3e7a2e77e..8e8975cea 100644 --- a/esignet-service/src/test/java/io/mosip/esignet/flows/AuthCodeFlowTest.java +++ b/esignet-service/src/test/java/io/mosip/esignet/flows/AuthCodeFlowTest.java @@ -21,8 +21,6 @@ import io.mosip.esignet.TestUtil; import io.mosip.esignet.api.dto.AuthChallenge; import io.mosip.esignet.api.dto.claim.ClaimDetail; -import io.mosip.esignet.api.dto.claim.Claims; -import io.mosip.esignet.api.dto.KycAuthDto; import io.mosip.esignet.api.dto.claim.ClaimsV2; import io.mosip.esignet.api.spi.AuditPlugin; import io.mosip.esignet.api.spi.Authenticator; @@ -40,11 +38,15 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; @@ -63,6 +65,8 @@ import java.util.Map; import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -97,9 +101,6 @@ public class AuthCodeFlowTest { @Autowired private AuthenticationContextClassRefUtil authenticationContextClassRefUtil; - @Autowired - RestTemplate restTemplate; - @Autowired AuditPlugin auditWrapper; @@ -298,6 +299,7 @@ private ResponseWrapper getOauthDetails(String clientId, oAuthDetailRequest.setState(state); ClaimsV2 claims = new ClaimsV2(); claims.setUserinfo(new HashMap<>()); + claims.setId_token(new HashMap<>()); claims.getUserinfo().put("email", getClaimDetail(null, null, true)); oAuthDetailRequest.setClaims(claims); @@ -305,6 +307,27 @@ private ResponseWrapper getOauthDetails(String clientId, request.setRequestTime(ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); request.setRequest(oAuthDetailRequest); + + 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 = objectMapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = objectMapper.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); + + MvcResult result = mockMvc.perform(post("/authorization/oauth-details") .contentType(MediaType.APPLICATION_JSON_UTF8) .content(objectMapper.writeValueAsString(request))) diff --git a/esignet-service/src/test/java/io/mosip/esignet/flows/AuthorizationAPIFlowTest.java b/esignet-service/src/test/java/io/mosip/esignet/flows/AuthorizationAPIFlowTest.java index ce14626ad..2aff68eca 100644 --- a/esignet-service/src/test/java/io/mosip/esignet/flows/AuthorizationAPIFlowTest.java +++ b/esignet-service/src/test/java/io/mosip/esignet/flows/AuthorizationAPIFlowTest.java @@ -21,8 +21,6 @@ import com.nimbusds.jwt.SignedJWT; import io.mosip.esignet.api.dto.AuthChallenge; import io.mosip.esignet.api.dto.claim.ClaimDetail; -import io.mosip.esignet.api.dto.claim.Claims; -import io.mosip.esignet.api.dto.KycAuthDto; import io.mosip.esignet.api.dto.claim.ClaimsV2; import io.mosip.esignet.api.spi.AuditPlugin; import io.mosip.esignet.api.spi.Authenticator; @@ -39,13 +37,14 @@ import org.json.simple.JSONObject; import org.junit.*; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; @@ -61,10 +60,7 @@ import static io.mosip.esignet.api.util.ErrorConstants.AUTH_FAILED; import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo; -import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest @@ -436,6 +432,7 @@ private ResponseWrapper getOauthDetails(String clientId, oAuthDetailRequest.setState(state); ClaimsV2 claims = new ClaimsV2(); claims.setUserinfo(new HashMap<>()); + claims.setId_token(new HashMap<>()); claims.getUserinfo().put("email", getClaimDetail(null, null, true)); oAuthDetailRequest.setClaims(claims); @@ -443,6 +440,25 @@ private ResponseWrapper getOauthDetails(String clientId, request.setRequestTime(ZonedDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); request.setRequest(oAuthDetailRequest); + 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 = objectMapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = objectMapper.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); + MvcResult result = mockMvc.perform(post("/authorization/oauth-details") .param("nonce", nonce).param("state", state) .contentType(MediaType.APPLICATION_JSON_UTF8) diff --git a/esignet-service/src/test/resources/application-test.properties b/esignet-service/src/test/resources/application-test.properties index 10b401447..bb746f7b9 100644 --- a/esignet-service/src/test/resources/application-test.properties +++ b/esignet-service/src/test/resources/application-test.properties @@ -51,6 +51,9 @@ mosip.esignet.host=http://localhost:8088 mosip.esignet.signup-id-token-expire-seconds=180 mosip.esignet.signup-id-token-audience=mosip-signup-client + +mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema_test.json + ## ------------------------------------------ e-Signet binding --------------------------------------------------------- mosip.esignet.binding.salt-length=16 diff --git a/esignet-service/src/test/resources/verified_claims_request_schema_test.json b/esignet-service/src/test/resources/verified_claims_request_schema_test.json new file mode 100644 index 000000000..fa4590b1f --- /dev/null +++ b/esignet-service/src/test/resources/verified_claims_request_schema_test.json @@ -0,0 +1,531 @@ +{ + "$id": "https://bitbucket.org/openid/ekyc-ida/raw/master/schema/verified_claims_request.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "check_details": { + "type": "array", + "prefixItems": [ + { + "check_id": { + "type": "string" + }, + "check_method": { + "type": "string" + }, + "organization": { + "type": "string" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + ] + }, + "claims_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "additionalProperties": { + "anyOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "minProperties": 1 + } + ] + }, + "constrainable_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + }, + "value": { + "type": "string" + }, + "values": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + } + } + ] + }, + "datetime_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "max_age": { + "type": "integer", + "minimum": 0 + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "document_details": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_issuance": { + "$ref": "#/$defs/datetime_element" + }, + "document_number": { + "$ref": "#/$defs/simple_element" + }, + "issuer": { + "type": "object", + "properties": { + "country": { + "$ref": "#/$defs/simple_element" + }, + "country_code": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "jurisdiction": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + }, + "personal_number": { + "$ref": "#/$defs/simple_element" + }, + "serial_number": { + "$ref": "#/$defs/simple_element" + } + } + }, + "evidence": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "object", + "properties": { + "value": { + "enum": [ + "document", + "electronic_record", + "vouch", + "electronic_signature" + ] + } + } + }, + "attachments": { + "$ref": "#/$defs/simple_element" + } + }, + "allOf": [ + { + "if": { + "properties": { + "type": { + "value": "electronic_signature" + } + } + }, + "then": { + "properties": { + "created_at": { + "$ref": "#/$defs/datetime_element" + }, + "issuer": { + "$ref": "#/$defs/simple_element" + }, + "serial_number": { + "$ref": "#/$defs/simple_element" + }, + "signature_type": { + "$ref": "#/$defs/simple_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "document" + } + } + }, + "then": { + "properties": { + "check_details": { + "$ref": "#/$defs/check_details" + }, + "document_details": { + "$ref": "#/$defs/document_details" + }, + "method": { + "$ref": "#/$defs/constrainable_element" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "electronic_record" + } + } + }, + "then": { + "properties": { + "check_details": { + "$ref": "#/$defs/check_details" + }, + "record": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "created_at": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "derived_claims": { + "$ref": "#/$defs/claims_element" + }, + "source": { + "type": "object", + "properties": { + "country": { + "$ref": "#/$defs/simple_element" + }, + "country_code": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + } + } + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + }, + { + "if": { + "properties": { + "type": { + "value": "vouch" + } + } + }, + "then": { + "properties": { + "attestation": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/constrainable_element" + }, + "date_of_expiry": { + "$ref": "#/$defs/datetime_element" + }, + "date_of_issuance": { + "$ref": "#/$defs/datetime_element" + }, + "derived_claims": { + "$ref": "#/$defs/claims_element" + }, + "reference_number": { + "$ref": "#/$defs/simple_element" + }, + "voucher": { + "type": "object", + "properties": { + "birthdate": { + "$ref": "#/$defs/datetime_element" + }, + "country": { + "$ref": "#/$defs/simple_element" + }, + "formatted": { + "$ref": "#/$defs/simple_element" + }, + "locality": { + "$ref": "#/$defs/simple_element" + }, + "name": { + "$ref": "#/$defs/simple_element" + }, + "occupation": { + "$ref": "#/$defs/simple_element" + }, + "organization": { + "$ref": "#/$defs/simple_element" + }, + "postal_code": { + "$ref": "#/$defs/simple_element" + }, + "region": { + "$ref": "#/$defs/simple_element" + }, + "street_address": { + "$ref": "#/$defs/simple_element" + } + } + } + } + }, + "check_details": { + "$ref": "#/$defs/check_details" + }, + "time": { + "$ref": "#/$defs/datetime_element" + } + } + }, + "else": true + } + ] + }, + "simple_element": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "essential": { + "type": "boolean" + }, + "purpose": { + "type": "string", + "maxLength": 300, + "minLength": 3 + } + } + } + ] + }, + "verified_claims": { + "oneOf": [ + { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/$defs/verified_claims_def" + } + ] + } + }, + { + "$ref": "#/$defs/verified_claims_def" + } + ] + }, + "verified_claims_def": { + "type": "object", + "required": [ + "verification", + "claims" + ], + "additionalProperties": false, + "properties": { + "claims": { + "$ref": "#/$defs/claims_element" + }, + "verification": { + "type": "object", + "required": [ + "trust_framework" + ], + "additionalProperties": true, + "properties": { + "assurance_level": { + "$ref": "#/$defs/constrainable_element" + }, + "assurance_process": { + "type": "object", + "properties": { + "assurance_details": { + "type": "array", + "items": { + "oneOf": [ + { + "assurance_classification": { + "$ref": "#/$defs/constrainable_element" + }, + "assurance_type": { + "$ref": "#/$defs/constrainable_element" + }, + "evidence_ref": { + "type": "object", + "required": [ + "txn" + ], + "additionalProperties": true, + "properties": { + "evidence_classification": { + "$ref": "#/$defs/constrainable_element" + }, + "evidence_metadata": { + "$ref": "#/$defs/constrainable_element" + }, + "txn": { + "$ref": "#/$defs/constrainable_element" + } + } + } + } + ] + }, + "minItems": 1 + }, + "policy": { + "$ref": "#/$defs/constrainable_element" + }, + "procedure": { + "$ref": "#/$defs/constrainable_element" + } + } + }, + "evidence": { + "type": "array", + "items": { + "oneOf": [ + { + "$ref": "#/$defs/evidence" + } + ] + }, + "minItems": 1 + }, + "time": { + "$ref": "#/$defs/datetime_element" + }, + "trust_framework": { + "$ref": "#/$defs/constrainable_element" + }, + "verification_process": { + "$ref": "#/$defs/simple_element" + } + } + } + } + } + }, + "properties": { + "id_token": { + "type": "object", + "additionalProperties": true, + "properties": { + "verified_claims": { + "$ref": "#/$defs/verified_claims" + } + } + }, + "userinfo": { + "type": "object", + "additionalProperties": true, + "properties": { + "verified_claims": { + "$ref": "#/$defs/verified_claims" + } + } + } + } +} \ No newline at end of file diff --git a/helm/oidc-ui/templates/deployment.yaml b/helm/oidc-ui/templates/deployment.yaml index 126eab535..b4e32c9d1 100644 --- a/helm/oidc-ui/templates/deployment.yaml +++ b/helm/oidc-ui/templates/deployment.yaml @@ -116,8 +116,10 @@ spec: {{- end }} {{- end }} {{- if .Values.extraEnvVarsSecret }} + {{- range .Values.extraEnvVarsSecret }} - secretRef: - name: {{ include "common.tplvalues.render" (dict "value" .Values.extraEnvVarsSecret "context" $) }} + name: {{ . }} + {{- end }} {{- end }} volumeMounts: - name: nginx-config diff --git a/oidc-ui/public/locales/ar.json b/oidc-ui/public/locales/ar.json index 8e478aaff..d9b5bbaa8 100644 --- a/oidc-ui/public/locales/ar.json +++ b/oidc-ui/public/locales/ar.json @@ -192,8 +192,8 @@ "consentDetails": { "essential_claims": "المطالبات الأساسية", "voluntary_claims": "المطالبات الطوعية", - "verified_claims": "المطالبات التي تم التحقق منها", - "unverified_claims": "مطالبات لم يتم التحقق منها", + "verified_claims": "تم التحقق منه", + "unverified_claims": "لم يتم التحقق منها", "name": "اسم", "given_name": "اسم", "middle_name": "الاسم الأوسط", @@ -455,6 +455,7 @@ "IDA-OTA-010": "نوع هوية الإدخال لا يتطابق مع نوع هوية طلب OTP", "IDA-OTA-011": "معرف الشريك المدخل لا يتطابق مع معرف الشريك لطلب OTP", "IDA-OTA-012": "لا يمكن إجراء التحقق من صحة كلمة المرور لمرة واحدة (OTP) مقابل معرف الفرد هذا. ", + "IDA-OTA-013": "تم حظر طلب/التحقق من صحة OTP مؤقتًا. يرجى المحاولة مرة أخرى بعد وقت ما.", "send_otp_failed": "غير قادر على إرسال OTP. ", "send_otp_failed_msg": "غير قادر على إرسال OTP. ", "invalid_otp_channel": "تم توفير قناة OTP غير صالحة.", diff --git a/oidc-ui/public/locales/en.json b/oidc-ui/public/locales/en.json index 11e7d5d71..d5334fa80 100644 --- a/oidc-ui/public/locales/en.json +++ b/oidc-ui/public/locales/en.json @@ -192,8 +192,8 @@ "consentDetails": { "essential_claims": "Essential Claims", "voluntary_claims": "Voluntary Claims", - "verified_claims": "Verified Claims", - "unverified_claims": "Unverified Claims", + "verified_claims": "Verified", + "unverified_claims": "Not Verified", "name": "Name", "given_name": "Name", "middle_name": "Middle Name", @@ -215,7 +215,7 @@ "verified": "Verified", "not-verified": "Not Verified", "available": "Available", - "not-available": "Not-Available", + "not-available": "Not Available", "message": "Click on proceed to begin with the verification process", "proceed": "Proceed", "cancel": "Cancel", @@ -455,6 +455,7 @@ "IDA-OTA-010": "Input Identity Type does not match Identity Type of OTP Request", "IDA-OTA-011": "Input Partner-ID does not match Partner-ID of OTP Request", "IDA-OTA-012": "OTP validation can't be performed against this Individual-ID. Generate OTP first", + "IDA-OTA-013": "OTP request/validation has been temporarily blocked. Please try again after sometime.", "send_otp_failed": "Unable to send OTP. Please try again.", "send_otp_failed_msg": "Unable to send OTP. Please try again.", "invalid_otp_channel": "Invalid OTP Channel Provided.", diff --git a/oidc-ui/public/locales/hi.json b/oidc-ui/public/locales/hi.json index 45a8f9918..70ca0ab49 100644 --- a/oidc-ui/public/locales/hi.json +++ b/oidc-ui/public/locales/hi.json @@ -192,8 +192,8 @@ "consentDetails": { "essential_claims": "आवश्यक दावे", "voluntary_claims": "स्वैच्छिक दावे", - "verified_claims": "सत्यापित दावे", - "unverified_claims": "असत्यापित दावे", + "verified_claims": "सत्यापित", + "unverified_claims": "सत्यापित नहीं है", "name": "नाम", "given_name": "नाम", "middle_name": "मध्य नाम", @@ -455,6 +455,7 @@ "IDA-OTA-010": "इनपुट पहचान प्रकार ओटीपी अनुरोध के पहचान प्रकार से मेल नहीं खाता", "IDA-OTA-011": "इनपुट पार्टनर-आईडी ओटीपी अनुरोध के पार्टनर-आईडी से मेल नहीं खाता है", "IDA-OTA-012": "इस व्यक्तिगत-आईडी के विरुद्ध ओटीपी सत्यापन नहीं किया जा सकता है। ", + "IDA-OTA-013": "ओटीपी अनुरोध/सत्यापन अस्थायी रूप से अवरुद्ध कर दिया गया है। कृपया कुछ समय बाद पुनः प्रयास करें.", "send_otp_failed": "ओटीपी भेजने में असमर्थ. ", "send_otp_failed_msg": "ओटीपी भेजने में असमर्थ. ", "invalid_otp_channel": "अमान्य ओटीपी चैनल प्रदान किया गया।", diff --git a/oidc-ui/public/locales/km.json b/oidc-ui/public/locales/km.json index bb6e36f90..edbfb7fe9 100644 --- a/oidc-ui/public/locales/km.json +++ b/oidc-ui/public/locales/km.json @@ -192,8 +192,8 @@ "consentDetails": { "essential_claims": "ការទាមទារសំខាន់ៗ", "voluntary_claims": "ការទាមទារដោយស្ម័គ្រចិត្ត", - "verified_claims": "ការទាមទារដែលបានផ្ទៀងផ្ទាត់", - "unverified_claims": "ការទាមទារដែលមិនបានបញ្ជាក់", + "verified_claims": "ផ្ទៀងផ្ទាត់", + "unverified_claims": "មិនបានផ្ទៀងផ្ទាត់", "name": "ឈ្មោះ", "given_name": "ឈ្មោះ", "middle_name": "ឈ្មោះកណ្តាល", @@ -455,6 +455,7 @@ "IDA-OTA-010": "ប្រភេទអត្តសញ្ញាណបញ្ចូលមិនត្រូវគ្នានឹងប្រភេទអត្តសញ្ញាណនៃសំណើ OTP ទេ។", "IDA-OTA-011": "លេខសម្គាល់ដៃគូបញ្ចូលមិនត្រូវគ្នានឹងលេខសម្គាល់ដៃគូនៃសំណើ OTP ទេ។", "IDA-OTA-012": "សុពលភាព OTP មិនអាចត្រូវបានអនុវត្តប្រឆាំងនឹង Individual-ID នេះទេ។ ", + "IDA-OTA-013": "សំណើ/សុពលភាព OTP ត្រូវបានរារាំងជាបណ្តោះអាសន្ន។ សូមព្យាយាមម្តងទៀតបន្ទាប់ពីពេលខ្លះ។", "send_otp_failed": "មិនអាចផ្ញើ OTP បានទេ។ ", "send_otp_failed_msg": "មិនអាចផ្ញើ OTP បានទេ។ ", "invalid_otp_channel": "ប៉ុស្តិ៍ OTP មិនត្រឹមត្រូវត្រូវបានផ្តល់។", diff --git a/oidc-ui/public/locales/kn.json b/oidc-ui/public/locales/kn.json index 8d031f69e..4c48f8403 100644 --- a/oidc-ui/public/locales/kn.json +++ b/oidc-ui/public/locales/kn.json @@ -192,8 +192,8 @@ "consentDetails": { "essential_claims": "ಅಗತ್ಯ ಹಕ್ಕುಗಳು", "voluntary_claims": "ಸ್ವಯಂಪ್ರೇರಿತ ಹಕ್ಕುಗಳು", - "verified_claims": "ಪರಿಶೀಲಿಸಿದ ಹಕ್ಕುಗಳು", - "unverified_claims": "ಪರಿಶೀಲಿಸದ ಹಕ್ಕುಗಳು", + "verified_claims": "ಪರಿಶೀಲಿಸಲಾಗಿದೆ", + "unverified_claims": "ಪರಿಶೀಲಿಸಲಾಗಿಲ್ಲ", "name": "ಹೆಸರು", "given_name": "ಹೆಸರು", "middle_name": "ಮಧ್ಯದ ಹೆಸರು", @@ -455,6 +455,7 @@ "IDA-OTA-010": "ಇನ್‌ಪುಟ್ ಗುರುತಿನ ಪ್ರಕಾರವು OTP ವಿನಂತಿಯ ಗುರುತಿನ ಪ್ರಕಾರಕ್ಕೆ ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ", "IDA-OTA-011": "OTP ವಿನಂತಿಯ ಪಾಲುದಾರ-ID ಇನ್‌ಪುಟ್ ಪಾಲುದಾರ-ID ಹೊಂದಿಕೆಯಾಗುವುದಿಲ್ಲ", "IDA-OTA-012": "ಈ ವೈಯಕ್ತಿಕ-ಐಡಿ ವಿರುದ್ಧ OTP ಮೌಲ್ಯೀಕರಣವನ್ನು ನಿರ್ವಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ", + "IDA-OTA-013": "OTP ವಿನಂತಿ/ಮೌಲ್ಯಮಾಪನವನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಸ್ವಲ್ಪ ಸಮಯದ ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ.", "send_otp_failed": "OTP ಕಳುಹಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ", "send_otp_failed_msg": "OTP ಕಳುಹಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ", "invalid_otp_channel": "ಅಮಾನ್ಯ OTP ಚಾನಲ್ ಒದಗಿಸಲಾಗಿದೆ.", diff --git a/oidc-ui/public/locales/ta.json b/oidc-ui/public/locales/ta.json index 7ff1e3643..dcc53d1e8 100644 --- a/oidc-ui/public/locales/ta.json +++ b/oidc-ui/public/locales/ta.json @@ -192,8 +192,8 @@ "consentDetails": { "essential_claims": "அத்தியாவசிய உரிமைகோரல்கள்", "voluntary_claims": "தன்னார்வ உரிமைகோரல்கள்", - "verified_claims": "சரிபார்க்கப்பட்ட உரிமைகோரல்கள்", - "unverified_claims": "சரிபார்க்கப்படாத உரிமைகோரல்கள்", + "verified_claims": "சரிபார்க்கப்பட்டது", + "unverified_claims": "சரிபார்க்கப்படவில்லை", "name": "பெயர்", "given_name": "பெயர்", "middle_name": "நடுத்தர பெயர்", @@ -215,7 +215,7 @@ "verified": "சரிபார்க்கப்பட்டது", "not-verified": "சரிபார்க்கப்படவில்லை", "available": "கிடைக்கும்", - "not-available": "இல்லை-கிடைக்கவில்லை", + "not-available": "கிடைக்கவில்லை", "message": "சரிபார்ப்பு செயல்முறையைத் தொடங்க தொடர என்பதைக் கிளிக் செய்யவும்", "proceed": "தொடரவும்", "cancel": "ரத்து செய்", @@ -455,6 +455,7 @@ "IDA-OTA-010": "உள்ளீட்டு அடையாள வகை, OTP கோரிக்கையின் அடையாள வகையுடன் பொருந்தவில்லை", "IDA-OTA-011": "உள்ளீட்டு பார்ட்னர் ஐடி, OTP கோரிக்கையின் பார்ட்னர் ஐடியுடன் பொருந்தவில்லை", "IDA-OTA-012": "இந்த தனிநபர் ஐடிக்கு எதிராக OTP சரிபார்ப்பைச் செய்ய முடியாது. ", + "IDA-OTA-013": "OTP கோரிக்கை/சரிபார்ப்பு தற்காலிகமாக தடுக்கப்பட்டது. சிறிது நேரம் கழித்து மீண்டும் முயற்சிக்கவும்.", "send_otp_failed": "OTP ஐ அனுப்ப முடியவில்லை. ", "send_otp_failed_msg": "OTP ஐ அனுப்ப முடியவில்லை. ", "invalid_otp_channel": "தவறான OTP சேனல் வழங்கப்பட்டது.", diff --git a/oidc-ui/src/components/ClaimDetails.js b/oidc-ui/src/components/ClaimDetails.js index 86f114a8e..5b8469e5c 100644 --- a/oidc-ui/src/components/ClaimDetails.js +++ b/oidc-ui/src/components/ClaimDetails.js @@ -89,7 +89,18 @@ const ClaimDetails = ({ } else if (label === "voluntary") { return (
-

{t1("voluntaryClaimsTooltip")}

+

+ {t1("voluntary_claims")}: + {t1("voluntaryClaimsTooltip")} +

+

+ {t1("verified_claims")}: + {t1("verifiedClaimTooltip")} +

+

+ {t1("unverified_claims")}: + {t1("unverifiedClaimTooltip")} +

); } diff --git a/pom.xml b/pom.xml index ffbac7975..ed686c59d 100644 --- a/pom.xml +++ b/pom.xml @@ -89,7 +89,7 @@ 3.7.0.1746 3.2.0 2.3 - + 2.15.0 Hoxton.SR8 2.3.6.RELEASE @@ -209,6 +209,26 @@ + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.databind} + + + com.fasterxml.jackson.core + jackson-core + ${jackson.databind} + + + +