diff --git a/.github/workflows/push-trigger.yml b/.github/workflows/push-trigger.yml index b5ce06a2f..a102ddfba 100644 --- a/.github/workflows/push-trigger.yml +++ b/.github/workflows/push-trigger.yml @@ -71,21 +71,6 @@ jobs: name: ${{ env.NEW_BUILD_ARTIFACT }} path: ${{ env.NEW_BUILD_ARTIFACT }}.zip - - publish_to_nexus: - if: "${{ !contains(github.ref, 'master') && github.event_name != 'pull_request' }}" - needs: build-mimoto - uses: mosip/kattu/.github/workflows/maven-publish-to-nexus.yml@master-java21 - with: - SERVICE_LOCATION: ./ - secrets: - OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }} - OSSRH_USER: ${{ secrets.OSSRH_USER }} - OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }} - OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} - GPG_SECRET: ${{ secrets.GPG_SECRET }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_INJI_TEAM }} - sonar_analysis: needs: build-mimoto if: "${{ github.event_name != 'pull_request' }}" @@ -109,6 +94,7 @@ jobs: - SERVICE_LOCATION: '../mimoto' SERVICE_NAME: 'mimoto' BUILD_ARTIFACT: 'mimoto-recreated' + SQUASH_LAYERS: '8' fail-fast: false uses: mosip/kattu/.github/workflows/docker-build.yml@master-java21 name: ${{ matrix.SERVICE_NAME }} @@ -116,6 +102,7 @@ jobs: SERVICE_LOCATION: ${{ matrix.SERVICE_LOCATION }} SERVICE_NAME: ${{ matrix.SERVICE_NAME }} BUILD_ARTIFACT: ${{ matrix.BUILD_ARTIFACT }} + SQUASH_LAYERS: ${{ matrix.SQUASH_LAYERS }} secrets: DEV_NAMESPACE_DOCKER_HUB: ${{ secrets.DEV_NAMESPACE_DOCKER_HUB }} ACTOR_DOCKER_HUB: ${{ secrets.ACTOR_DOCKER_HUB }} @@ -134,20 +121,6 @@ jobs: GPG_SECRET: ${{ secrets.GPG_SECRET }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_INJI_TEAM }} - publish_to_nexus_apitest_mimoto: - if: "${{ !contains(github.ref, 'master') && github.event_name != 'pull_request' && github.event_name != 'release' && github.event_name != 'prerelease' && github.event_name != 'publish' }}" - needs: build-maven-apitest-mimoto - uses: mosip/kattu/.github/workflows/maven-publish-to-nexus.yml@master-java21 - with: - SERVICE_LOCATION: ./api-test - secrets: - OSSRH_USER: ${{ secrets.OSSRH_USER }} - OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }} - OSSRH_URL: ${{ secrets.OSSRH_SNAPSHOT_URL }} - OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} - GPG_SECRET: ${{ secrets.GPG_SECRET }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_INJI_TEAM }} - build-apitest-mimoto-local: needs: build-maven-apitest-mimoto runs-on: ubuntu-latest diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml deleted file mode 100644 index c92d40377..000000000 --- a/.github/workflows/sonarqube.yml +++ /dev/null @@ -1,74 +0,0 @@ -name: SonarQube checks -on: - push: - branches: - - develop - -jobs: - build: - name: Build - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 - uses: actions/setup-java@v1 - with: - java-version: 11 - - name: Setup branch and env - run: | - # Strip git ref prefix from version - echo "BRANCH_NAME=$(echo ${{ github.ref }} | sed -e 's,.*/\(.*\),\1,')" >> $GITHUB_ENV - echo "GPG_TTY=$(tty)" >> $GITHUB_ENV - - name: Setup branch and GPG public key - run: | - # Strip git ref prefix from version - echo ${{ env.BRANCH_NAME }} - echo ${{ env.GPG_TTY }} - sudo apt-get --yes install gnupg2 - gpg2 --import ./.github/keys/mosipgpgkey_pub.gpg - gpg2 --quiet --batch --passphrase=${{secrets.gpg_secret}} --allow-secret-key-import --import ./.github/keys/mosipgpgkey_sec.gpg - - name: Cache SonarQube packages - uses: actions/cache@v4 - with: - path: ~/.sonar/cache - key: ${{ runner.os }}-sonar - restore-keys: ${{ runner.os }}-sonar - - name: Cache Maven packages - uses: actions/cache@v4 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Build and analyze - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} - run: mvn -Dgpg.skip -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=mimoto - - - name: Install jq - run: sudo apt-get -y install jq - - - name: Check for critical bugs - run: | - # Fetch SonarQube issues using the API - SONAR_HOST_URL=https://sonarcloud.io - SONAR_PROJECT_KEY=mosip_mimoto - - # Fetch issues using the SonarQube API - response=$(curl -s "${SONAR_HOST_URL}/api/issues/search?componentKeys=${SONAR_PROJECT_KEY}&severities=CRITICAL&statuses=OPEN") - echo "The response is $response" - - # Extract the value of "issues" field - issues_count=$(echo "$response" | jq '.issues | length') - echo "The number of issues $issues_count" - - if [ "$issues_count" -eq 0 ]; then - echo "No critical issues found." - else - echo "Critical issues found. Failing the pipeline" - exit 1 - fi - diff --git a/.talismanrc b/.talismanrc index 6bb31fd6e..08cb56921 100644 --- a/.talismanrc +++ b/.talismanrc @@ -23,6 +23,8 @@ fileignoreconfig: checksum: 81b14da4f42f335cf6a3094489844962b92fc629c89691487558c71c3d44a915 - filename: src/main/java/io/mosip/mimoto/controller/CredentialShareController.java checksum: 666c22ca63adb8770de901e220f8efeb5ec1d3f63064a58dea33ba49b8e15872 +- filename: src/test/java/io/mosip/mimoto/service/CredentialPDFGeneratorServiceTest.java + checksum: b04c0ff467788706b21b1576db06f1482c5d1684744fb0e6c4236aa61037c8aa - filename: src/main/java/io/mosip/mimoto/controller/IssuersController.java checksum: 856d860de2562fa019ac23bd724b84e1d67b834883b332c12415e6714de9bb05 - filename: helm/mimoto/templates/deployment.yaml @@ -74,4 +76,13 @@ fileignoreconfig: - filename: docker-compose/docker-compose.yml checksum: 857f5dcebdef810161b9ef09ad078c091c7d9969035bc45132975b296675dac9 - filename: docker-compose/config/mimoto-default.properties - checksum: 746b60fa10a8c2c10c00bd35313407186db7aef0762f99e919bbc42fdc597824 \ No newline at end of file + checksum: 746b60fa10a8c2c10c00bd35313407186db7aef0762f99e919bbc42fdc597824 +- filename: src/test/java/io/mosip/mimoto/service/WalletServiceTest.java + checksum: cd48690314dcb0f1153b52e95cc96fd5766a123dbe90ce7247be9a7da2d1a676 +- filename: src/main/java/io/mosip/mimoto/controller/WalletsController.java + checksum: f54bbacbba3af5e0beff7761245c6aa40cdc676e55b557e1aa0dd157a4eda03a +- filename: src/test/java/io/mosip/mimoto/controller/WalletsControllerTest.java + checksum: 63a4620f43c88a39771ec5e492dc860e9c6cd62ab895243bb284dd04c5be6937 +- filename: src/test/java/io/mosip/mimoto/util/WalletValidatorTest.java + checksum: 90dce7daa19f0eb9050194a3c77a88d449a83c337d9041bfdeb7bfded3bbd12c +version: "" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 21ddf0bd9..f83100aa6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -65,6 +65,9 @@ WORKDIR /home/${container_user} ENV work_dir=/home/${container_user} +# create directory - data under working directory to store JSON files in OTP flow credential download +RUN mkdir -p ${work_dir}/data + # change volume to whichever storage directory you want to use for this container. VOLUME ${work_dir}/logs ${work_dir}/Glowroot diff --git a/README.md b/README.md index 176be48e0..5d873a7f5 100644 --- a/README.md +++ b/README.md @@ -8,16 +8,32 @@ This repository contains source code for backend service of Inji Mobile and Inji ## Build & run (for developers) -The project requires JDK 21 +The project requires JDK 21, postgres, redis and google client credentials ### without docker-compose Build & install -1. build the jar -``` +1. Install pgadmin, redis and update application-local.properties file with values + ``` + spring.datasource.username= + spring.datasource.password= + spring.redis.password= + ``` +2. Refer to the [How to create Google Client Credentials](docker-compose/README.md#how-to-create-google-client-credentials) section to create + Google client credentials and update below properties in `application-local.properties`. + ``` + spring.security.oauth2.client.registration.google.client-id= + spring.security.oauth2.client.registration.google.client-secret= + ``` +3. Run the SQLs using /deploy.sh script. from [db_scripts folder](db_scripts/inji_mimoto) + ``` + ./deploy.sh deploy.properties + ``` +4. Build the jar + ``` mvn clean install -Dgpg.skip=true -Dmaven.javadoc.skip=true -DskipTests=true -``` -2. Run following command -``` + ``` +5. Run following command + ``` mvn spring-boot:run -Dspring.profiles.active=local -``` + ``` ### with docker-compose 1. To simplify running mimoto in local for developers we have added [Docker Compose Setup](docker-compose/README.md). This docker-compose includes mimoto service and nginx service to server static data. 2. Follow the below steps to use custom build image in docker-compose @@ -27,40 +43,6 @@ The project requires JDK 21 ```docker build -t .``` * Use newly built docker image in docker-compose file -## Deployment - -### Install - -1. Execute config-server install script -``` -cd deploy/config-server -./install.sh -``` -* Review values.yaml and make sure git repository parameters are as per your installation. - -2. Execute Onboarder install script -``` -cd partner-onboarder -./install.sh -``` -* During the execution of the `install.sh` script, a prompt appears requesting information for the S3 bucket, including its name and URL. -* Once the job is completed, log in to S3 and check the reports. There should not be any failures. - -3. Execute mimoto install script - -``` -cd helm/mimoto -./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. - -### For Onboarding new Issuer for VCI: - -- create a folder "certs" in the root and a file "oidckeystore.p12" inside certs and store the keys as different aliases for every issuers. for more details refer [here](https://docs.mosip.io/inji/inji-mobile-wallet/customization-overview/credential_providers) - ## Credits Credits listed [here](/Credits.md) diff --git a/api-test/.temp-Functional Test-classpath-arg-1659588646071.txt b/api-test/.temp-Functional Test-classpath-arg-1659588646071.txt deleted file mode 100644 index bb81464f5..000000000 --- a/api-test/.temp-Functional Test-classpath-arg-1659588646071.txt +++ /dev/null @@ -1 +0,0 @@ --classpath D:\Mosip_Automation_Test\Docker_Test\MOSIP-19726\mosip-functional-tests\automationtests\target\test-classes;D:\Mosip_Automation_Test\Docker_Test\MOSIP-19726\mosip-functional-tests\automationtests\target\classes;C:\Users\Sohan.Dey\.m2\repository\com\opencsv\opencsv\4.1\opencsv-4.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-lang3\3.6\commons-lang3-3.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-text\1.1\commons-text-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\ibm\icu\icu4j\63.1\icu4j-63.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-admin-client\17.0.1\keycloak-admin-client-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-core\17.0.1\keycloak-core-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-common\17.0.1\keycloak-common-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-client\3.13.2.Final\resteasy-client-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\ws\rs\jboss-jaxrs-api_2.1_spec\2.0.1.Final\jboss-jaxrs-api_2.1_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxrs\3.13.2.Final\resteasy-jaxrs-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\annotation\jboss-annotations-api_1.3_spec\2.0.1.Final\jboss-annotations-api_1.3_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\activation\jakarta.activation\1.2.1\jakarta.activation-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-multipart-provider\3.13.2.Final\resteasy-multipart-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\mail\jakarta.mail\1.6.5\jakarta.mail-1.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\james\apache-mime4j\0.6\apache-mime4j-0.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jackson2-provider\3.13.2.Final\resteasy-jackson2-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-json-provider\2.10.5\jackson-jaxrs-json-provider-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-base\2.10.5\jackson-jaxrs-base-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\json-patch\1.9\json-patch-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\jackson-coreutils\1.6\jackson-coreutils-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\msg-simple\1.1\msg-simple-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\btf\1.2\btf-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxb-provider\3.13.2.Final\resteasy-jaxb-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\xml\bind\jboss-jaxb-api_2.3_spec\2.0.0.Final\jboss-jaxb-api_2.3_spec-2.0.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\javassist\javassist\3.25.0-GA\javassist-3.25.0-GA.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jknack\handlebars\3.0.0\handlebars-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\antlr\antlr4-runtime\4.5.1-1\antlr4-runtime-4.5.1-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mozilla\rhino\1.7R4\rhino-1.7R4.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-api\1.6.4\slf4j-api-1.6.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-collections4\4.3\commons-collections4-4.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\jsonwebtoken\jjwt\0.6.0\jjwt-0.6.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\flipkart\zjsonpatch\zjsonpatch\0.4.7\zjsonpatch-0.4.7.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk\1.11.368\aws-java-sdk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dlm\1.11.368\aws-java-sdk-dlm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\jmespath-java\1.11.368\jmespath-java-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-macie\1.11.368\aws-java-sdk-macie-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-eks\1.11.368\aws-java-sdk-eks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediatailor\1.11.368\aws-java-sdk-mediatailor-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-neptune\1.11.368\aws-java-sdk-neptune-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pi\1.11.368\aws-java-sdk-pi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickprojects\1.11.368\aws-java-sdk-iot1clickprojects-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickdevices\1.11.368\aws-java-sdk-iot1clickdevices-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotanalytics\1.11.368\aws-java-sdk-iotanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acmpca\1.11.368\aws-java-sdk-acmpca-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-secretsmanager\1.11.368\aws-java-sdk-secretsmanager-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-fms\1.11.368\aws-java-sdk-fms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-connect\1.11.368\aws-java-sdk-connect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-transcribe\1.11.368\aws-java-sdk-transcribe-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscalingplans\1.11.368\aws-java-sdk-autoscalingplans-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workmail\1.11.368\aws-java-sdk-workmail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicediscovery\1.11.368\aws-java-sdk-servicediscovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloud9\1.11.368\aws-java-sdk-cloud9-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-serverlessapplicationrepository\1.11.368\aws-java-sdk-serverlessapplicationrepository-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-alexaforbusiness\1.11.368\aws-java-sdk-alexaforbusiness-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroups\1.11.368\aws-java-sdk-resourcegroups-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-comprehend\1.11.368\aws-java-sdk-comprehend-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-translate\1.11.368\aws-java-sdk-translate-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemaker\1.11.368\aws-java-sdk-sagemaker-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotjobsdataplane\1.11.368\aws-java-sdk-iotjobsdataplane-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemakerruntime\1.11.368\aws-java-sdk-sagemakerruntime-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesisvideo\1.11.368\aws-java-sdk-kinesisvideo-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-http\4.1.17.Final\netty-codec-http-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec\4.1.17.Final\netty-codec-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler\4.1.17.Final\netty-handler-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-buffer\4.1.17.Final\netty-buffer-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-common\4.1.17.Final\netty-common-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport\4.1.17.Final\netty-transport-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-resolver\4.1.17.Final\netty-resolver-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appsync\1.11.368\aws-java-sdk-appsync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-guardduty\1.11.368\aws-java-sdk-guardduty-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mq\1.11.368\aws-java-sdk-mq-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediaconvert\1.11.368\aws-java-sdk-mediaconvert-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastore\1.11.368\aws-java-sdk-mediastore-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastoredata\1.11.368\aws-java-sdk-mediastoredata-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-medialive\1.11.368\aws-java-sdk-medialive-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediapackage\1.11.368\aws-java-sdk-mediapackage-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costexplorer\1.11.368\aws-java-sdk-costexplorer-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pricing\1.11.368\aws-java-sdk-pricing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mobile\1.11.368\aws-java-sdk-mobile-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsmv2\1.11.368\aws-java-sdk-cloudhsmv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glue\1.11.368\aws-java-sdk-glue-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-migrationhub\1.11.368\aws-java-sdk-migrationhub-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dax\1.11.368\aws-java-sdk-dax-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-greengrass\1.11.368\aws-java-sdk-greengrass-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-athena\1.11.368\aws-java-sdk-athena-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplaceentitlement\1.11.368\aws-java-sdk-marketplaceentitlement-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codestar\1.11.368\aws-java-sdk-codestar-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lexmodelbuilding\1.11.368\aws-java-sdk-lexmodelbuilding-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroupstaggingapi\1.11.368\aws-java-sdk-resourcegroupstaggingapi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pinpoint\1.11.368\aws-java-sdk-pinpoint-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-xray\1.11.368\aws-java-sdk-xray-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworkscm\1.11.368\aws-java-sdk-opsworkscm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-support\1.11.368\aws-java-sdk-support-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpledb\1.11.368\aws-java-sdk-simpledb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicecatalog\1.11.368\aws-java-sdk-servicecatalog-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servermigration\1.11.368\aws-java-sdk-servermigration-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpleworkflow\1.11.368\aws-java-sdk-simpleworkflow-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-storagegateway\1.11.368\aws-java-sdk-storagegateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-route53\1.11.368\aws-java-sdk-route53-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-s3\1.11.368\aws-java-sdk-s3-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-importexport\1.11.368\aws-java-sdk-importexport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sts\1.11.368\aws-java-sdk-sts-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sqs\1.11.368\aws-java-sdk-sqs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rds\1.11.368\aws-java-sdk-rds-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-redshift\1.11.368\aws-java-sdk-redshift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticbeanstalk\1.11.368\aws-java-sdk-elasticbeanstalk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glacier\1.11.368\aws-java-sdk-glacier-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iam\1.11.368\aws-java-sdk-iam-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-datapipeline\1.11.368\aws-java-sdk-datapipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancing\1.11.368\aws-java-sdk-elasticloadbalancing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancingv2\1.11.368\aws-java-sdk-elasticloadbalancingv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-emr\1.11.368\aws-java-sdk-emr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticache\1.11.368\aws-java-sdk-elasticache-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elastictranscoder\1.11.368\aws-java-sdk-elastictranscoder-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ec2\1.11.368\aws-java-sdk-ec2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dynamodb\1.11.368\aws-java-sdk-dynamodb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sns\1.11.368\aws-java-sdk-sns-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-budgets\1.11.368\aws-java-sdk-budgets-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudtrail\1.11.368\aws-java-sdk-cloudtrail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatch\1.11.368\aws-java-sdk-cloudwatch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-logs\1.11.368\aws-java-sdk-logs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-events\1.11.368\aws-java-sdk-events-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidentity\1.11.368\aws-java-sdk-cognitoidentity-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitosync\1.11.368\aws-java-sdk-cognitosync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directconnect\1.11.368\aws-java-sdk-directconnect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudformation\1.11.368\aws-java-sdk-cloudformation-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudfront\1.11.368\aws-java-sdk-cloudfront-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-clouddirectory\1.11.368\aws-java-sdk-clouddirectory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesis\1.11.368\aws-java-sdk-kinesis-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworks\1.11.368\aws-java-sdk-opsworks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ses\1.11.368\aws-java-sdk-ses-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscaling\1.11.368\aws-java-sdk-autoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudsearch\1.11.368\aws-java-sdk-cloudsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatchmetrics\1.11.368\aws-java-sdk-cloudwatchmetrics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codedeploy\1.11.368\aws-java-sdk-codedeploy-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codepipeline\1.11.368\aws-java-sdk-codepipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kms\1.11.368\aws-java-sdk-kms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-config\1.11.368\aws-java-sdk-config-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lambda\1.11.368\aws-java-sdk-lambda-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecs\1.11.368\aws-java-sdk-ecs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecr\1.11.368\aws-java-sdk-ecr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsm\1.11.368\aws-java-sdk-cloudhsm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ssm\1.11.368\aws-java-sdk-ssm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workspaces\1.11.368\aws-java-sdk-workspaces-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-machinelearning\1.11.368\aws-java-sdk-machinelearning-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directory\1.11.368\aws-java-sdk-directory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-efs\1.11.368\aws-java-sdk-efs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codecommit\1.11.368\aws-java-sdk-codecommit-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-devicefarm\1.11.368\aws-java-sdk-devicefarm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticsearch\1.11.368\aws-java-sdk-elasticsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-waf\1.11.368\aws-java-sdk-waf-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacecommerceanalytics\1.11.368\aws-java-sdk-marketplacecommerceanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-inspector\1.11.368\aws-java-sdk-inspector-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot\1.11.368\aws-java-sdk-iot-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-api-gateway\1.11.368\aws-java-sdk-api-gateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acm\1.11.368\aws-java-sdk-acm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-gamelift\1.11.368\aws-java-sdk-gamelift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dms\1.11.368\aws-java-sdk-dms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacemeteringservice\1.11.368\aws-java-sdk-marketplacemeteringservice-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidp\1.11.368\aws-java-sdk-cognitoidp-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-discovery\1.11.368\aws-java-sdk-discovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-applicationautoscaling\1.11.368\aws-java-sdk-applicationautoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-snowball\1.11.368\aws-java-sdk-snowball-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rekognition\1.11.368\aws-java-sdk-rekognition-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-polly\1.11.368\aws-java-sdk-polly-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lightsail\1.11.368\aws-java-sdk-lightsail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-stepfunctions\1.11.368\aws-java-sdk-stepfunctions-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-health\1.11.368\aws-java-sdk-health-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costandusagereport\1.11.368\aws-java-sdk-costandusagereport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codebuild\1.11.368\aws-java-sdk-codebuild-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appstream\1.11.368\aws-java-sdk-appstream-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-shield\1.11.368\aws-java-sdk-shield-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-batch\1.11.368\aws-java-sdk-batch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lex\1.11.368\aws-java-sdk-lex-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mechanicalturkrequester\1.11.368\aws-java-sdk-mechanicalturkrequester-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-organizations\1.11.368\aws-java-sdk-organizations-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workdocs\1.11.368\aws-java-sdk-workdocs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-core\1.11.368\aws-java-sdk-core-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\software\amazon\ion\ion-java\1.0.2\ion-java-1.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-cbor\2.6.7\jackson-dataformat-cbor-2.6.7.jar;C:\Users\Sohan.Dey\.m2\repository\joda-time\joda-time\2.8.1\joda-time-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-models\1.11.368\aws-java-sdk-models-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-swf-libraries\1.11.22\aws-java-sdk-swf-libraries-1.11.22.jar;C:\Users\Sohan.Dey\.m2\repository\com\aventstack\extentreports\3.0.0\extentreports-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\freemarker\freemarker\2.3.23\freemarker-2.3.23.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver\3.3.0\mongodb-driver-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\bson\3.3.0\bson-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver-core\3.3.0\mongodb-driver-core-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpmime\4.5.2\httpmime-4.5.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\relevantcodes\extentreports\2.41.2\extentreports-2.41.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jsoup\jsoup\1.8.3\jsoup-1.8.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\sqlite-jdbc\3.8.11.1\sqlite-jdbc-3.8.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\jayway\jsonpath\json-path\2.4.0\json-path-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model\3.3.9\maven-model-3.3.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-utils\3.0.22\plexus-utils-3.0.22.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured\3.0.7\rest-assured-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy\2.4.12\groovy-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-xml\2.4.12\groovy-xml-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\ccil\cowan\tagsoup\tagsoup\1.2.1\tagsoup-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\json-path\3.0.7\json-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-json\2.4.12\groovy-json-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured-common\3.0.7\rest-assured-common-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\xml-path\3.0.7\xml-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\testng\testng\6.11\testng-6.11.jar;C:\Users\Sohan.Dey\.m2\repository\com\beust\jcommander\1.64\jcommander-1.64.jar;C:\Users\Sohan.Dey\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\Sohan.Dey\.m2\repository\org\zeroturnaround\zt-zip\1.13\zt-zip-1.13.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.1\jackson-core-2.10.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.5\jackson-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.5\jackson-databind-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-xml\2.9.5\jackson-dataformat-xml-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-jaxb-annotations\2.9.5\jackson-module-jaxb-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\woodstox\stax2-api\3.1.4\stax2-api-3.1.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\woodstox\woodstox-core\5.0.3\woodstox-core-5.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\json\json\20180130\json-20180130.jar;C:\Users\Sohan.Dey\.m2\repository\com\googlecode\json-simple\json-simple\1.1.1\json-simple-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\junit\junit\4.10\junit-4.10.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\gson\gson\2.8.4\gson-2.8.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\plugins\maven-assembly-plugin\3.1.0\maven-assembly-plugin-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-plugin-api\3.0\maven-plugin-api-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-plexus\1.4.2\sisu-inject-plexus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-bean\1.4.2\sisu-inject-bean-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-guice\2.1.7\sisu-guice-2.1.7-noaop.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-core\3.0\maven-core-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings\3.0\maven-settings-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings-builder\3.0\maven-settings-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-repository-metadata\3.0\maven-repository-metadata-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model-builder\3.0\maven-model-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-aether-provider\3.0\maven-aether-provider-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-impl\1.7\aether-impl-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-spi\1.7\aether-spi-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-api\1.7\aether-api-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-util\1.7\aether-util-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-classworlds\2.2.3\plexus-classworlds-2.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-component-annotations\1.5.5\plexus-component-annotations-1.5.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-sec-dispatcher\1.3\plexus-sec-dispatcher-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-cipher\1.4\plexus-cipher-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-artifact\3.0\maven-artifact-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-common-artifact-filters\3.0.1\maven-common-artifact-filters-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-utils\3.1.0\maven-shared-utils-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-artifact-transfer\0.9.0\maven-artifact-transfer-0.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-interpolation\1.24\plexus-interpolation-1.24.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-archiver\3.5\plexus-archiver-3.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-compress\1.14\commons-compress-1.14.jar;C:\Users\Sohan.Dey\.m2\repository\org\iq80\snappy\snappy\0.4\snappy-0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\tukaani\xz\1.6\xz-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\file-management\3.0.0\file-management-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-io\3.0.0\maven-shared-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-compat\3.0\maven-compat-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\wagon\wagon-provider-api\2.10\wagon-provider-api-2.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-filtering\3.1.1\maven-filtering-3.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-build-api\0.0.7\plexus-build-api-0.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-io\3.0.0\plexus-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-archiver\3.2.0\maven-archiver-3.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\guava\guava\19.0\guava-19.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\hibernate-core\5.4.2.Final\hibernate-core-5.4.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy\1.9.10\byte-buddy-1.9.10.jar;C:\Users\Sohan.Dey\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.1.0.Final\hibernate-commons-annotations-5.1.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\jaxb-runtime\2.3.1\jaxb-runtime-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\txw2\2.3.1\txw2-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\istack\istack-commons-runtime\3.0.7\istack-commons-runtime-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jvnet\staxex\stax-ex\1.8\stax-ex-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\fastinfoset\FastInfoset\1.2.15\FastInfoset-1.2.15.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils\1.9.2\commons-beanutils-1.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-annotations\1.5.20\swagger-annotations-1.5.20.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.5\jackson-datatype-jsr310-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\postgresql\postgresql\42.2.2\postgresql-42.2.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-log4j12\1.6.2\slf4j-log4j12-1.6.2.jar;C:\Users\Sohan.Dey\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.1\log4j-api-2.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\lingala\zip4j\zip4j\1.3.2\zip4j-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpclient\4.5.3\httpclient-4.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpcore\4.4.6\httpcore-4.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-client\2.8.1\hadoop-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-common\2.8.1\hadoop-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\Users\Sohan.Dey\.m2\repository\commons-net\commons-net\3.1\commons-net-3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-sslengine\6.1.26\jetty-sslengine-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\jsp\jsp-api\2.1\jsp-api-2.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\avro\avro\1.7.4\avro-1.7.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\thoughtworks\paranamer\paranamer\2.3\paranamer-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\snappy\snappy-java\1.0.4.1\snappy-java-1.0.4.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-auth\2.8.1\hadoop-auth-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-kerberos-codec\2.0.0-M15\apacheds-kerberos-codec-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-i18n\2.0.0-M15\apacheds-i18n-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-asn1-api\1.0.0-M20\api-asn1-api-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-util\1.0.0-M20\api-util-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-framework\2.7.1\curator-framework-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-client\2.7.1\curator-client-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-recipes\2.7.1\curator-recipes-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\jsr305\3.0.0\jsr305-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\htrace\htrace-core4\4.0.1-incubating\htrace-core4-4.0.1-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\zookeeper\zookeeper\3.4.6\zookeeper-3.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs\2.8.1\hadoop-hdfs-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs-client\2.8.1\hadoop-hdfs-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okhttp\okhttp\2.4.0\okhttp-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okio\okio\1.4.0\okio-1.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-app\2.8.1\hadoop-mapreduce-client-app-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-common\2.8.1\hadoop-mapreduce-client-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-client\2.8.1\hadoop-yarn-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-server-common\2.8.1\hadoop-yarn-server-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.8.1\hadoop-mapreduce-client-shuffle-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-api\2.8.1\hadoop-yarn-api-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-core\2.8.1\hadoop-mapreduce-client-core-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-common\2.8.1\hadoop-yarn-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-core\1.9\jersey-core-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-client\1.9\jersey-client-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-jaxrs\1.9.13\jackson-jaxrs-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-xc\1.9.13\jackson-xc-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.8.1\hadoop-mapreduce-client-jobclient-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-annotations\2.8.1\hadoop-annotations-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk16\1.45\bcprov-jdk16-1.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\commons-io\commons-io\2.6\commons-io-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\core\3.3.3\core-3.3.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\javase\2.0\javase-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-all\1.3\hamcrest-all-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\velocity\velocity\1.7\velocity-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\commons-lang\commons-lang\2.4\commons-lang-2.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\html2pdf\2.0.0\html2pdf-2.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\forms\7.1.0\forms-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\kernel\7.1.0\kernel-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\io\7.1.0\io-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\layout\7.1.0\layout-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\itextpdf\5.5.13\itextpdf-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\javax\mail\javax.mail-api\1.6.2\javax.mail-api-1.6.2.jar;D:\Mosip_Automation_Test\Docker_Test\MOSIP-19726\mosip-functional-tests\authentication-demo-service\target\classes;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.0.2.RELEASE\spring-boot-starter-web-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.0.2.RELEASE\spring-boot-starter-json-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.5\jackson-datatype-jdk8-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.5\jackson-module-parameter-names-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.0.2.RELEASE\spring-boot-starter-tomcat-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.31\tomcat-embed-el-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.31\tomcat-embed-websocket-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.9.Final\hibernate-validator-6.0.9.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-web\5.0.6.RELEASE\spring-web-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webmvc\5.0.6.RELEASE\spring-webmvc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-expression\5.0.6.RELEASE\spring-expression-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-test\2.0.2.RELEASE\spring-boot-starter-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test\2.0.2.RELEASE\spring-boot-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test-autoconfigure\2.0.2.RELEASE\spring-boot-test-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\assertj\assertj-core\3.9.1\assertj-core-3.9.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-core\5.0.6.RELEASE\spring-core-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jcl\5.0.6.RELEASE\spring-jcl-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-test\5.0.6.RELEASE\spring-test-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\xmlunit\xmlunit-core\2.5.1\xmlunit-core-2.5.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-ui\2.9.2\springfox-swagger-ui-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spring-web\2.9.2\springfox-spring-web-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger2\2.9.2\springfox-swagger2-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spi\2.9.2\springfox-spi-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-core\2.9.2\springfox-core-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-schema\2.9.2\springfox-schema-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-common\2.9.2\springfox-swagger-common-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\mapstruct\mapstruct\1.2.0.Final\mapstruct-1.2.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\javax.ws.rs-api\2.0\javax.ws.rs-api-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\authentication\authentication-core\1.2.0.1-SNAPSHOT\authentication-core-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-cache\2.0.2.RELEASE\spring-boot-starter-cache-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context-support\5.0.6.RELEASE\spring-context-support-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\sourceafis\sourceafis\3.4.0\sourceafis-3.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\sf\trove4j\trove4j\3.0.3\trove4j-3.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\noexception\noexception\1.3.2\noexception-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\mhshams\jnbis\2.0.1\jnbis-2.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\sanselan\sanselan\0.97-incubator\sanselan-0.97-incubator.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\idrepository\id-repository-core\1.2.0\id-repository-core-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biosdk-provider\1.2.0\kernel-biosdk-provider-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biometrics-api\1.2.0\kernel-biometrics-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-demographics-api\1.2.0\kernel-demographics-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-webflux\2.0.2.RELEASE\spring-boot-starter-webflux-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-reactor-netty\2.0.2.RELEASE\spring-boot-starter-reactor-netty-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\ipc\reactor-netty\0.7.7.RELEASE\reactor-netty-0.7.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler-proxy\4.1.24.Final\netty-handler-proxy-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-socks\4.1.24.Final\netty-codec-socks-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-epoll\4.1.24.Final\netty-transport-native-epoll-4.1.24.Final-linux-x86_64.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.24.Final\netty-transport-native-unix-common-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webflux\5.0.6.RELEASE\spring-webflux-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\reactor-core\3.1.7.RELEASE\reactor-core-3.1.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-multipart-parser\1.1.0\nio-multipart-parser-1.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-stream-storage\1.1.3\nio-stream-storage-1.1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-ui\1.5.10\springdoc-openapi-ui-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-webmvc-core\1.5.10\springdoc-openapi-webmvc-core-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-common\1.5.10\springdoc-openapi-common-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-models\2.1.10\swagger-models-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-annotations\2.1.10\swagger-annotations-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-integration\2.1.10\swagger-integration-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-core\2.1.10\swagger-core-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\xml\bind\jakarta.xml.bind-api\2.3.2\jakarta.xml.bind-api-2.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\activation\jakarta.activation-api\1.2.1\jakarta.activation-api-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.12.1\jackson-dataformat-yaml-2.12.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\github\classgraph\classgraph\4.8.69\classgraph-4.8.69.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\swagger-ui\3.51.1\swagger-ui-3.51.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\webjars-locator-core\0.45\webjars-locator-core-0.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\mockito\mockito-core\2.23.4\mockito-core-2.23.4.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy-agent\1.9.3\byte-buddy-agent-1.9.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\objenesis\objenesis\2.6\objenesis-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-websubclient-api\1.2.0.1-SNAPSHOT\kernel-websubclient-api-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aspects\5.0.6.RELEASE\spring-aspects-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjweaver\1.8.13\aspectjweaver-1.8.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\owasp\encoder\encoder\1.2.3\encoder-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\tensorflow\1.12.0\tensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow\1.12.0\libtensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow_jni\1.12.0\libtensorflow_jni-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-templatemanager-velocity\1.2.0.1-SNAPSHOT\kernel-templatemanager-velocity-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\javax.servlet-api\4.0.1\javax.servlet-api-4.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-config\2.0.0.RELEASE\spring-cloud-starter-config-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter\2.0.0.RELEASE\spring-cloud-starter-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-context\2.0.0.RELEASE\spring-cloud-context-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-crypto\5.0.6.RELEASE\spring-security-crypto-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-commons\2.0.0.RELEASE\spring-cloud-commons-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-rsa\1.0.5.RELEASE\spring-security-rsa-1.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-config-client\2.0.0.RELEASE\spring-cloud-config-client-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-jpeg2000\1.3.0\jai-imageio-jpeg2000-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-core\1.3.0\jai-imageio-core-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keymanager-service\1.2.0.1-SNAPSHOT\kernel-keymanager-service-1.2.0.1-SNAPSHOT-lib.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-test\5.0.5.RELEASE\spring-security-test-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-core\5.0.5.RELEASE\spring-security-core-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-pdfgenerator-itext\1.2.0.1-SNAPSHOT\kernel-pdfgenerator-itext-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\barcodes\7.1.0\barcodes-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\font-asian\7.1.0\font-asian-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\hyph\7.1.0\hyph-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\pdfa\7.1.0\pdfa-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\sign\7.1.0\sign-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\tool\xmlworker\5.5.13\xmlworker-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.66\bcpkix-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-data-jpa\2.0.2.RELEASE\spring-boot-starter-data-jpa-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.0.2.RELEASE\spring-boot-starter-jdbc-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\zaxxer\HikariCP\2.7.9\HikariCP-2.7.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\h2database\h2\1.4.197\h2-1.4.197.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\microsoft\TSS.Java\0.3.0\TSS.Java-0.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\java\dev\jna\jna\4.4.0\jna-4.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-api\2.4.1.Final\cache2k-api-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-core\2.4.1.Final\cache2k-core-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\nimbusds\nimbus-jose-jwt\9.14\nimbus-jose-jwt-9.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-core\1.2.0.1-SNAPSHOT\kernel-core-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter\2.0.2.RELEASE\spring-boot-starter-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot\2.0.2.RELEASE\spring-boot-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.0.2.RELEASE\spring-boot-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.0.2.RELEASE\spring-boot-starter-logging-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.10.0\log4j-to-slf4j-2.10.0.jar;C:\Users\Sohan.Dey\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-jpa\2.0.7.RELEASE\spring-data-jpa-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-commons\2.0.7.RELEASE\spring-data-commons-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-orm\5.0.6.RELEASE\spring-orm-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jdbc\5.0.6.RELEASE\spring-jdbc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context\5.0.6.RELEASE\spring-context-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aop\5.0.6.RELEASE\spring-aop-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-tx\5.0.6.RELEASE\spring-tx-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-beans\5.0.6.RELEASE\spring-beans-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjrt\1.8.12\aspectjrt-1.8.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-security\2.0.2.RELEASE\spring-boot-starter-security-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-config\5.0.5.RELEASE\spring-security-config-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-web\5.0.5.RELEASE\spring-security-web-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\javax\transaction\javax.transaction-api\1.3\javax.transaction-api-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-math3\3.6.1\commons-math3-3.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.66\bcprov-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\javax\interceptor\javax.interceptor-api\1.2\javax.interceptor-api-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\annotations\3.0.1\annotations-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\jcip\jcip-annotations\1.0\jcip-annotations-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-models\1.5.21\swagger-models-1.5.21.jar;C:\Users\Sohan.Dey\.m2\repository\com\auth0\java-jwt\3.8.1\java-jwt-3.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-core\1.4.2\micrometer-core-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\hdrhistogram\HdrHistogram\2.1.12\HdrHistogram-2.1.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\latencyutils\LatencyUtils\2.0.3\LatencyUtils-2.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-registry-prometheus\1.4.2\micrometer-registry-prometheus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient_common\0.8.1\simpleclient_common-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient\0.8.1\simpleclient-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\activation\1.1\activation-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-sleuth\2.0.4.RELEASE\spring-cloud-starter-sleuth-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.0.9.RELEASE\spring-boot-starter-aop-2.0.9.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-sleuth-core\2.0.4.RELEASE\spring-cloud-sleuth-core-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave\5.6.1\brave-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\zipkin2\zipkin\2.12.0\zipkin-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\reporter2\zipkin-reporter\2.7.14\zipkin-reporter-2.7.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-context-log4j2\5.6.1\brave-context-log4j2-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-web\5.6.1\brave-instrumentation-spring-web-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-http\5.6.1\brave-instrumentation-http-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-rabbit\5.6.1\brave-instrumentation-spring-rabbit-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-kafka-clients\5.6.1\brave-instrumentation-kafka-clients-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpclient\5.6.1\brave-instrumentation-httpclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpasyncclient\5.6.1\brave-instrumentation-httpasyncclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-webmvc\5.6.1\brave-instrumentation-spring-webmvc-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-servlet\5.6.1\brave-instrumentation-servlet-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-actuator\2.0.2.RELEASE\spring-boot-starter-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator-autoconfigure\2.0.2.RELEASE\spring-boot-actuator-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator\2.0.2.RELEASE\spring-boot-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.55\tomcat-embed-core-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\tomcat-annotations-api\8.5.55\tomcat-annotations-api-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\retry\spring-retry\1.2.1.RELEASE\spring-retry-1.2.1.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-afterburner\2.12.0\jackson-module-afterburner-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-registration-packet-manager\1.1.5.3\kernel-registration-packet-manager-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keygenerator-bouncycastle\1.1.5.3\kernel-keygenerator-bouncycastle-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-cbeffutil-api\1.1.5.3\kernel-cbeffutil-api-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-logger-logback\1.1.5.3\kernel-logger-logback-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\logstash\logback\logstash-logback-encoder\6.4\logstash-logback-encoder-6.4.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-access\1.2.3\logback-access-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-crypto-jce\1.1.5.3\kernel-crypto-jce-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\bitbucket\b_c\jose4j\0.6.5\jose4j-0.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j-provider\1.1.2-incubating\wink-json4j-provider-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-common\1.1.2-incubating\wink-common-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.1.1\jaxb-impl-2.2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-annotation_1.1_spec\1.0\geronimo-annotation_1.1_spec-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j\1.1.2-incubating\wink-json4j-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\jsr311-api\1.1.1\jsr311-api-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\checkerframework\checker-qual\2.9.0\checker-qual-2.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-broker\5.15.9\activemq-broker-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-client\5.15.9\activemq-client-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-jms_1.1_spec\1.1.1\geronimo-jms_1.1_spec-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\hawtbuf\hawtbuf\1.11\hawtbuf-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-j2ee-management_1.1_spec\1.0.1\geronimo-j2ee-management_1.1_spec-1.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-openwire-legacy\5.15.9\activemq-openwire-legacy-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\projectlombok\lombok\1.18.8\lombok-1.18.8.jar \ No newline at end of file diff --git a/api-test/.temp-Functional Test-classpath-arg-1659589592502.txt b/api-test/.temp-Functional Test-classpath-arg-1659589592502.txt deleted file mode 100644 index bb81464f5..000000000 --- a/api-test/.temp-Functional Test-classpath-arg-1659589592502.txt +++ /dev/null @@ -1 +0,0 @@ --classpath D:\Mosip_Automation_Test\Docker_Test\MOSIP-19726\mosip-functional-tests\automationtests\target\test-classes;D:\Mosip_Automation_Test\Docker_Test\MOSIP-19726\mosip-functional-tests\automationtests\target\classes;C:\Users\Sohan.Dey\.m2\repository\com\opencsv\opencsv\4.1\opencsv-4.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-lang3\3.6\commons-lang3-3.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-text\1.1\commons-text-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\ibm\icu\icu4j\63.1\icu4j-63.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-admin-client\17.0.1\keycloak-admin-client-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-core\17.0.1\keycloak-core-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-common\17.0.1\keycloak-common-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-client\3.13.2.Final\resteasy-client-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\ws\rs\jboss-jaxrs-api_2.1_spec\2.0.1.Final\jboss-jaxrs-api_2.1_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxrs\3.13.2.Final\resteasy-jaxrs-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\annotation\jboss-annotations-api_1.3_spec\2.0.1.Final\jboss-annotations-api_1.3_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\activation\jakarta.activation\1.2.1\jakarta.activation-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-multipart-provider\3.13.2.Final\resteasy-multipart-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\mail\jakarta.mail\1.6.5\jakarta.mail-1.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\james\apache-mime4j\0.6\apache-mime4j-0.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jackson2-provider\3.13.2.Final\resteasy-jackson2-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-json-provider\2.10.5\jackson-jaxrs-json-provider-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-base\2.10.5\jackson-jaxrs-base-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\json-patch\1.9\json-patch-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\jackson-coreutils\1.6\jackson-coreutils-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\msg-simple\1.1\msg-simple-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\btf\1.2\btf-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxb-provider\3.13.2.Final\resteasy-jaxb-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\xml\bind\jboss-jaxb-api_2.3_spec\2.0.0.Final\jboss-jaxb-api_2.3_spec-2.0.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\javassist\javassist\3.25.0-GA\javassist-3.25.0-GA.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jknack\handlebars\3.0.0\handlebars-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\antlr\antlr4-runtime\4.5.1-1\antlr4-runtime-4.5.1-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mozilla\rhino\1.7R4\rhino-1.7R4.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-api\1.6.4\slf4j-api-1.6.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-collections4\4.3\commons-collections4-4.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\jsonwebtoken\jjwt\0.6.0\jjwt-0.6.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\flipkart\zjsonpatch\zjsonpatch\0.4.7\zjsonpatch-0.4.7.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk\1.11.368\aws-java-sdk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dlm\1.11.368\aws-java-sdk-dlm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\jmespath-java\1.11.368\jmespath-java-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-macie\1.11.368\aws-java-sdk-macie-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-eks\1.11.368\aws-java-sdk-eks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediatailor\1.11.368\aws-java-sdk-mediatailor-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-neptune\1.11.368\aws-java-sdk-neptune-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pi\1.11.368\aws-java-sdk-pi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickprojects\1.11.368\aws-java-sdk-iot1clickprojects-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickdevices\1.11.368\aws-java-sdk-iot1clickdevices-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotanalytics\1.11.368\aws-java-sdk-iotanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acmpca\1.11.368\aws-java-sdk-acmpca-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-secretsmanager\1.11.368\aws-java-sdk-secretsmanager-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-fms\1.11.368\aws-java-sdk-fms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-connect\1.11.368\aws-java-sdk-connect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-transcribe\1.11.368\aws-java-sdk-transcribe-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscalingplans\1.11.368\aws-java-sdk-autoscalingplans-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workmail\1.11.368\aws-java-sdk-workmail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicediscovery\1.11.368\aws-java-sdk-servicediscovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloud9\1.11.368\aws-java-sdk-cloud9-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-serverlessapplicationrepository\1.11.368\aws-java-sdk-serverlessapplicationrepository-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-alexaforbusiness\1.11.368\aws-java-sdk-alexaforbusiness-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroups\1.11.368\aws-java-sdk-resourcegroups-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-comprehend\1.11.368\aws-java-sdk-comprehend-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-translate\1.11.368\aws-java-sdk-translate-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemaker\1.11.368\aws-java-sdk-sagemaker-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotjobsdataplane\1.11.368\aws-java-sdk-iotjobsdataplane-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemakerruntime\1.11.368\aws-java-sdk-sagemakerruntime-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesisvideo\1.11.368\aws-java-sdk-kinesisvideo-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-http\4.1.17.Final\netty-codec-http-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec\4.1.17.Final\netty-codec-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler\4.1.17.Final\netty-handler-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-buffer\4.1.17.Final\netty-buffer-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-common\4.1.17.Final\netty-common-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport\4.1.17.Final\netty-transport-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-resolver\4.1.17.Final\netty-resolver-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appsync\1.11.368\aws-java-sdk-appsync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-guardduty\1.11.368\aws-java-sdk-guardduty-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mq\1.11.368\aws-java-sdk-mq-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediaconvert\1.11.368\aws-java-sdk-mediaconvert-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastore\1.11.368\aws-java-sdk-mediastore-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastoredata\1.11.368\aws-java-sdk-mediastoredata-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-medialive\1.11.368\aws-java-sdk-medialive-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediapackage\1.11.368\aws-java-sdk-mediapackage-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costexplorer\1.11.368\aws-java-sdk-costexplorer-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pricing\1.11.368\aws-java-sdk-pricing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mobile\1.11.368\aws-java-sdk-mobile-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsmv2\1.11.368\aws-java-sdk-cloudhsmv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glue\1.11.368\aws-java-sdk-glue-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-migrationhub\1.11.368\aws-java-sdk-migrationhub-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dax\1.11.368\aws-java-sdk-dax-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-greengrass\1.11.368\aws-java-sdk-greengrass-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-athena\1.11.368\aws-java-sdk-athena-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplaceentitlement\1.11.368\aws-java-sdk-marketplaceentitlement-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codestar\1.11.368\aws-java-sdk-codestar-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lexmodelbuilding\1.11.368\aws-java-sdk-lexmodelbuilding-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroupstaggingapi\1.11.368\aws-java-sdk-resourcegroupstaggingapi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pinpoint\1.11.368\aws-java-sdk-pinpoint-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-xray\1.11.368\aws-java-sdk-xray-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworkscm\1.11.368\aws-java-sdk-opsworkscm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-support\1.11.368\aws-java-sdk-support-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpledb\1.11.368\aws-java-sdk-simpledb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicecatalog\1.11.368\aws-java-sdk-servicecatalog-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servermigration\1.11.368\aws-java-sdk-servermigration-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpleworkflow\1.11.368\aws-java-sdk-simpleworkflow-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-storagegateway\1.11.368\aws-java-sdk-storagegateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-route53\1.11.368\aws-java-sdk-route53-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-s3\1.11.368\aws-java-sdk-s3-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-importexport\1.11.368\aws-java-sdk-importexport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sts\1.11.368\aws-java-sdk-sts-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sqs\1.11.368\aws-java-sdk-sqs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rds\1.11.368\aws-java-sdk-rds-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-redshift\1.11.368\aws-java-sdk-redshift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticbeanstalk\1.11.368\aws-java-sdk-elasticbeanstalk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glacier\1.11.368\aws-java-sdk-glacier-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iam\1.11.368\aws-java-sdk-iam-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-datapipeline\1.11.368\aws-java-sdk-datapipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancing\1.11.368\aws-java-sdk-elasticloadbalancing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancingv2\1.11.368\aws-java-sdk-elasticloadbalancingv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-emr\1.11.368\aws-java-sdk-emr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticache\1.11.368\aws-java-sdk-elasticache-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elastictranscoder\1.11.368\aws-java-sdk-elastictranscoder-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ec2\1.11.368\aws-java-sdk-ec2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dynamodb\1.11.368\aws-java-sdk-dynamodb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sns\1.11.368\aws-java-sdk-sns-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-budgets\1.11.368\aws-java-sdk-budgets-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudtrail\1.11.368\aws-java-sdk-cloudtrail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatch\1.11.368\aws-java-sdk-cloudwatch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-logs\1.11.368\aws-java-sdk-logs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-events\1.11.368\aws-java-sdk-events-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidentity\1.11.368\aws-java-sdk-cognitoidentity-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitosync\1.11.368\aws-java-sdk-cognitosync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directconnect\1.11.368\aws-java-sdk-directconnect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudformation\1.11.368\aws-java-sdk-cloudformation-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudfront\1.11.368\aws-java-sdk-cloudfront-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-clouddirectory\1.11.368\aws-java-sdk-clouddirectory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesis\1.11.368\aws-java-sdk-kinesis-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworks\1.11.368\aws-java-sdk-opsworks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ses\1.11.368\aws-java-sdk-ses-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscaling\1.11.368\aws-java-sdk-autoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudsearch\1.11.368\aws-java-sdk-cloudsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatchmetrics\1.11.368\aws-java-sdk-cloudwatchmetrics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codedeploy\1.11.368\aws-java-sdk-codedeploy-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codepipeline\1.11.368\aws-java-sdk-codepipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kms\1.11.368\aws-java-sdk-kms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-config\1.11.368\aws-java-sdk-config-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lambda\1.11.368\aws-java-sdk-lambda-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecs\1.11.368\aws-java-sdk-ecs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecr\1.11.368\aws-java-sdk-ecr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsm\1.11.368\aws-java-sdk-cloudhsm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ssm\1.11.368\aws-java-sdk-ssm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workspaces\1.11.368\aws-java-sdk-workspaces-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-machinelearning\1.11.368\aws-java-sdk-machinelearning-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directory\1.11.368\aws-java-sdk-directory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-efs\1.11.368\aws-java-sdk-efs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codecommit\1.11.368\aws-java-sdk-codecommit-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-devicefarm\1.11.368\aws-java-sdk-devicefarm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticsearch\1.11.368\aws-java-sdk-elasticsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-waf\1.11.368\aws-java-sdk-waf-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacecommerceanalytics\1.11.368\aws-java-sdk-marketplacecommerceanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-inspector\1.11.368\aws-java-sdk-inspector-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot\1.11.368\aws-java-sdk-iot-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-api-gateway\1.11.368\aws-java-sdk-api-gateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acm\1.11.368\aws-java-sdk-acm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-gamelift\1.11.368\aws-java-sdk-gamelift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dms\1.11.368\aws-java-sdk-dms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacemeteringservice\1.11.368\aws-java-sdk-marketplacemeteringservice-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidp\1.11.368\aws-java-sdk-cognitoidp-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-discovery\1.11.368\aws-java-sdk-discovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-applicationautoscaling\1.11.368\aws-java-sdk-applicationautoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-snowball\1.11.368\aws-java-sdk-snowball-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rekognition\1.11.368\aws-java-sdk-rekognition-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-polly\1.11.368\aws-java-sdk-polly-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lightsail\1.11.368\aws-java-sdk-lightsail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-stepfunctions\1.11.368\aws-java-sdk-stepfunctions-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-health\1.11.368\aws-java-sdk-health-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costandusagereport\1.11.368\aws-java-sdk-costandusagereport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codebuild\1.11.368\aws-java-sdk-codebuild-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appstream\1.11.368\aws-java-sdk-appstream-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-shield\1.11.368\aws-java-sdk-shield-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-batch\1.11.368\aws-java-sdk-batch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lex\1.11.368\aws-java-sdk-lex-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mechanicalturkrequester\1.11.368\aws-java-sdk-mechanicalturkrequester-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-organizations\1.11.368\aws-java-sdk-organizations-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workdocs\1.11.368\aws-java-sdk-workdocs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-core\1.11.368\aws-java-sdk-core-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\software\amazon\ion\ion-java\1.0.2\ion-java-1.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-cbor\2.6.7\jackson-dataformat-cbor-2.6.7.jar;C:\Users\Sohan.Dey\.m2\repository\joda-time\joda-time\2.8.1\joda-time-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-models\1.11.368\aws-java-sdk-models-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-swf-libraries\1.11.22\aws-java-sdk-swf-libraries-1.11.22.jar;C:\Users\Sohan.Dey\.m2\repository\com\aventstack\extentreports\3.0.0\extentreports-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\freemarker\freemarker\2.3.23\freemarker-2.3.23.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver\3.3.0\mongodb-driver-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\bson\3.3.0\bson-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver-core\3.3.0\mongodb-driver-core-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpmime\4.5.2\httpmime-4.5.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\relevantcodes\extentreports\2.41.2\extentreports-2.41.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jsoup\jsoup\1.8.3\jsoup-1.8.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\sqlite-jdbc\3.8.11.1\sqlite-jdbc-3.8.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\jayway\jsonpath\json-path\2.4.0\json-path-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model\3.3.9\maven-model-3.3.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-utils\3.0.22\plexus-utils-3.0.22.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured\3.0.7\rest-assured-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy\2.4.12\groovy-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-xml\2.4.12\groovy-xml-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\ccil\cowan\tagsoup\tagsoup\1.2.1\tagsoup-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\json-path\3.0.7\json-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-json\2.4.12\groovy-json-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured-common\3.0.7\rest-assured-common-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\xml-path\3.0.7\xml-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\testng\testng\6.11\testng-6.11.jar;C:\Users\Sohan.Dey\.m2\repository\com\beust\jcommander\1.64\jcommander-1.64.jar;C:\Users\Sohan.Dey\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\Sohan.Dey\.m2\repository\org\zeroturnaround\zt-zip\1.13\zt-zip-1.13.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.1\jackson-core-2.10.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.5\jackson-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.5\jackson-databind-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-xml\2.9.5\jackson-dataformat-xml-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-jaxb-annotations\2.9.5\jackson-module-jaxb-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\woodstox\stax2-api\3.1.4\stax2-api-3.1.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\woodstox\woodstox-core\5.0.3\woodstox-core-5.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\json\json\20180130\json-20180130.jar;C:\Users\Sohan.Dey\.m2\repository\com\googlecode\json-simple\json-simple\1.1.1\json-simple-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\junit\junit\4.10\junit-4.10.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\gson\gson\2.8.4\gson-2.8.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\plugins\maven-assembly-plugin\3.1.0\maven-assembly-plugin-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-plugin-api\3.0\maven-plugin-api-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-plexus\1.4.2\sisu-inject-plexus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-bean\1.4.2\sisu-inject-bean-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-guice\2.1.7\sisu-guice-2.1.7-noaop.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-core\3.0\maven-core-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings\3.0\maven-settings-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings-builder\3.0\maven-settings-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-repository-metadata\3.0\maven-repository-metadata-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model-builder\3.0\maven-model-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-aether-provider\3.0\maven-aether-provider-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-impl\1.7\aether-impl-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-spi\1.7\aether-spi-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-api\1.7\aether-api-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-util\1.7\aether-util-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-classworlds\2.2.3\plexus-classworlds-2.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-component-annotations\1.5.5\plexus-component-annotations-1.5.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-sec-dispatcher\1.3\plexus-sec-dispatcher-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-cipher\1.4\plexus-cipher-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-artifact\3.0\maven-artifact-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-common-artifact-filters\3.0.1\maven-common-artifact-filters-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-utils\3.1.0\maven-shared-utils-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-artifact-transfer\0.9.0\maven-artifact-transfer-0.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-interpolation\1.24\plexus-interpolation-1.24.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-archiver\3.5\plexus-archiver-3.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-compress\1.14\commons-compress-1.14.jar;C:\Users\Sohan.Dey\.m2\repository\org\iq80\snappy\snappy\0.4\snappy-0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\tukaani\xz\1.6\xz-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\file-management\3.0.0\file-management-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-io\3.0.0\maven-shared-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-compat\3.0\maven-compat-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\wagon\wagon-provider-api\2.10\wagon-provider-api-2.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-filtering\3.1.1\maven-filtering-3.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-build-api\0.0.7\plexus-build-api-0.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-io\3.0.0\plexus-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-archiver\3.2.0\maven-archiver-3.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\guava\guava\19.0\guava-19.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\hibernate-core\5.4.2.Final\hibernate-core-5.4.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy\1.9.10\byte-buddy-1.9.10.jar;C:\Users\Sohan.Dey\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.1.0.Final\hibernate-commons-annotations-5.1.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\jaxb-runtime\2.3.1\jaxb-runtime-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\txw2\2.3.1\txw2-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\istack\istack-commons-runtime\3.0.7\istack-commons-runtime-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jvnet\staxex\stax-ex\1.8\stax-ex-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\fastinfoset\FastInfoset\1.2.15\FastInfoset-1.2.15.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils\1.9.2\commons-beanutils-1.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-annotations\1.5.20\swagger-annotations-1.5.20.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.5\jackson-datatype-jsr310-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\postgresql\postgresql\42.2.2\postgresql-42.2.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-log4j12\1.6.2\slf4j-log4j12-1.6.2.jar;C:\Users\Sohan.Dey\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.1\log4j-api-2.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\lingala\zip4j\zip4j\1.3.2\zip4j-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpclient\4.5.3\httpclient-4.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpcore\4.4.6\httpcore-4.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-client\2.8.1\hadoop-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-common\2.8.1\hadoop-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\Users\Sohan.Dey\.m2\repository\commons-net\commons-net\3.1\commons-net-3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-sslengine\6.1.26\jetty-sslengine-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\jsp\jsp-api\2.1\jsp-api-2.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\avro\avro\1.7.4\avro-1.7.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\thoughtworks\paranamer\paranamer\2.3\paranamer-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\snappy\snappy-java\1.0.4.1\snappy-java-1.0.4.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-auth\2.8.1\hadoop-auth-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-kerberos-codec\2.0.0-M15\apacheds-kerberos-codec-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-i18n\2.0.0-M15\apacheds-i18n-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-asn1-api\1.0.0-M20\api-asn1-api-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-util\1.0.0-M20\api-util-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-framework\2.7.1\curator-framework-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-client\2.7.1\curator-client-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-recipes\2.7.1\curator-recipes-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\jsr305\3.0.0\jsr305-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\htrace\htrace-core4\4.0.1-incubating\htrace-core4-4.0.1-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\zookeeper\zookeeper\3.4.6\zookeeper-3.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs\2.8.1\hadoop-hdfs-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs-client\2.8.1\hadoop-hdfs-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okhttp\okhttp\2.4.0\okhttp-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okio\okio\1.4.0\okio-1.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-app\2.8.1\hadoop-mapreduce-client-app-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-common\2.8.1\hadoop-mapreduce-client-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-client\2.8.1\hadoop-yarn-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-server-common\2.8.1\hadoop-yarn-server-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.8.1\hadoop-mapreduce-client-shuffle-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-api\2.8.1\hadoop-yarn-api-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-core\2.8.1\hadoop-mapreduce-client-core-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-common\2.8.1\hadoop-yarn-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-core\1.9\jersey-core-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-client\1.9\jersey-client-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-jaxrs\1.9.13\jackson-jaxrs-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-xc\1.9.13\jackson-xc-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.8.1\hadoop-mapreduce-client-jobclient-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-annotations\2.8.1\hadoop-annotations-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk16\1.45\bcprov-jdk16-1.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\commons-io\commons-io\2.6\commons-io-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\core\3.3.3\core-3.3.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\javase\2.0\javase-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-all\1.3\hamcrest-all-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\velocity\velocity\1.7\velocity-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\commons-lang\commons-lang\2.4\commons-lang-2.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\html2pdf\2.0.0\html2pdf-2.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\forms\7.1.0\forms-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\kernel\7.1.0\kernel-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\io\7.1.0\io-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\layout\7.1.0\layout-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\itextpdf\5.5.13\itextpdf-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\javax\mail\javax.mail-api\1.6.2\javax.mail-api-1.6.2.jar;D:\Mosip_Automation_Test\Docker_Test\MOSIP-19726\mosip-functional-tests\authentication-demo-service\target\classes;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.0.2.RELEASE\spring-boot-starter-web-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.0.2.RELEASE\spring-boot-starter-json-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.5\jackson-datatype-jdk8-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.5\jackson-module-parameter-names-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.0.2.RELEASE\spring-boot-starter-tomcat-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.31\tomcat-embed-el-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.31\tomcat-embed-websocket-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.9.Final\hibernate-validator-6.0.9.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-web\5.0.6.RELEASE\spring-web-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webmvc\5.0.6.RELEASE\spring-webmvc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-expression\5.0.6.RELEASE\spring-expression-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-test\2.0.2.RELEASE\spring-boot-starter-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test\2.0.2.RELEASE\spring-boot-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test-autoconfigure\2.0.2.RELEASE\spring-boot-test-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\assertj\assertj-core\3.9.1\assertj-core-3.9.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-core\5.0.6.RELEASE\spring-core-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jcl\5.0.6.RELEASE\spring-jcl-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-test\5.0.6.RELEASE\spring-test-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\xmlunit\xmlunit-core\2.5.1\xmlunit-core-2.5.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-ui\2.9.2\springfox-swagger-ui-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spring-web\2.9.2\springfox-spring-web-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger2\2.9.2\springfox-swagger2-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spi\2.9.2\springfox-spi-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-core\2.9.2\springfox-core-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-schema\2.9.2\springfox-schema-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-common\2.9.2\springfox-swagger-common-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\mapstruct\mapstruct\1.2.0.Final\mapstruct-1.2.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\javax.ws.rs-api\2.0\javax.ws.rs-api-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\authentication\authentication-core\1.2.0.1-SNAPSHOT\authentication-core-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-cache\2.0.2.RELEASE\spring-boot-starter-cache-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context-support\5.0.6.RELEASE\spring-context-support-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\sourceafis\sourceafis\3.4.0\sourceafis-3.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\sf\trove4j\trove4j\3.0.3\trove4j-3.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\noexception\noexception\1.3.2\noexception-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\mhshams\jnbis\2.0.1\jnbis-2.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\sanselan\sanselan\0.97-incubator\sanselan-0.97-incubator.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\idrepository\id-repository-core\1.2.0\id-repository-core-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biosdk-provider\1.2.0\kernel-biosdk-provider-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biometrics-api\1.2.0\kernel-biometrics-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-demographics-api\1.2.0\kernel-demographics-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-webflux\2.0.2.RELEASE\spring-boot-starter-webflux-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-reactor-netty\2.0.2.RELEASE\spring-boot-starter-reactor-netty-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\ipc\reactor-netty\0.7.7.RELEASE\reactor-netty-0.7.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler-proxy\4.1.24.Final\netty-handler-proxy-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-socks\4.1.24.Final\netty-codec-socks-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-epoll\4.1.24.Final\netty-transport-native-epoll-4.1.24.Final-linux-x86_64.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.24.Final\netty-transport-native-unix-common-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webflux\5.0.6.RELEASE\spring-webflux-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\reactor-core\3.1.7.RELEASE\reactor-core-3.1.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-multipart-parser\1.1.0\nio-multipart-parser-1.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-stream-storage\1.1.3\nio-stream-storage-1.1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-ui\1.5.10\springdoc-openapi-ui-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-webmvc-core\1.5.10\springdoc-openapi-webmvc-core-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-common\1.5.10\springdoc-openapi-common-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-models\2.1.10\swagger-models-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-annotations\2.1.10\swagger-annotations-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-integration\2.1.10\swagger-integration-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-core\2.1.10\swagger-core-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\xml\bind\jakarta.xml.bind-api\2.3.2\jakarta.xml.bind-api-2.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\activation\jakarta.activation-api\1.2.1\jakarta.activation-api-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.12.1\jackson-dataformat-yaml-2.12.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\github\classgraph\classgraph\4.8.69\classgraph-4.8.69.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\swagger-ui\3.51.1\swagger-ui-3.51.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\webjars-locator-core\0.45\webjars-locator-core-0.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\mockito\mockito-core\2.23.4\mockito-core-2.23.4.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy-agent\1.9.3\byte-buddy-agent-1.9.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\objenesis\objenesis\2.6\objenesis-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-websubclient-api\1.2.0.1-SNAPSHOT\kernel-websubclient-api-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aspects\5.0.6.RELEASE\spring-aspects-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjweaver\1.8.13\aspectjweaver-1.8.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\owasp\encoder\encoder\1.2.3\encoder-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\tensorflow\1.12.0\tensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow\1.12.0\libtensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow_jni\1.12.0\libtensorflow_jni-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-templatemanager-velocity\1.2.0.1-SNAPSHOT\kernel-templatemanager-velocity-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\javax.servlet-api\4.0.1\javax.servlet-api-4.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-config\2.0.0.RELEASE\spring-cloud-starter-config-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter\2.0.0.RELEASE\spring-cloud-starter-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-context\2.0.0.RELEASE\spring-cloud-context-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-crypto\5.0.6.RELEASE\spring-security-crypto-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-commons\2.0.0.RELEASE\spring-cloud-commons-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-rsa\1.0.5.RELEASE\spring-security-rsa-1.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-config-client\2.0.0.RELEASE\spring-cloud-config-client-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-jpeg2000\1.3.0\jai-imageio-jpeg2000-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-core\1.3.0\jai-imageio-core-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keymanager-service\1.2.0.1-SNAPSHOT\kernel-keymanager-service-1.2.0.1-SNAPSHOT-lib.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-test\5.0.5.RELEASE\spring-security-test-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-core\5.0.5.RELEASE\spring-security-core-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-pdfgenerator-itext\1.2.0.1-SNAPSHOT\kernel-pdfgenerator-itext-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\barcodes\7.1.0\barcodes-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\font-asian\7.1.0\font-asian-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\hyph\7.1.0\hyph-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\pdfa\7.1.0\pdfa-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\sign\7.1.0\sign-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\tool\xmlworker\5.5.13\xmlworker-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.66\bcpkix-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-data-jpa\2.0.2.RELEASE\spring-boot-starter-data-jpa-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.0.2.RELEASE\spring-boot-starter-jdbc-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\zaxxer\HikariCP\2.7.9\HikariCP-2.7.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\h2database\h2\1.4.197\h2-1.4.197.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\microsoft\TSS.Java\0.3.0\TSS.Java-0.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\java\dev\jna\jna\4.4.0\jna-4.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-api\2.4.1.Final\cache2k-api-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-core\2.4.1.Final\cache2k-core-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\nimbusds\nimbus-jose-jwt\9.14\nimbus-jose-jwt-9.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-core\1.2.0.1-SNAPSHOT\kernel-core-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter\2.0.2.RELEASE\spring-boot-starter-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot\2.0.2.RELEASE\spring-boot-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.0.2.RELEASE\spring-boot-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.0.2.RELEASE\spring-boot-starter-logging-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.10.0\log4j-to-slf4j-2.10.0.jar;C:\Users\Sohan.Dey\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-jpa\2.0.7.RELEASE\spring-data-jpa-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-commons\2.0.7.RELEASE\spring-data-commons-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-orm\5.0.6.RELEASE\spring-orm-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jdbc\5.0.6.RELEASE\spring-jdbc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context\5.0.6.RELEASE\spring-context-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aop\5.0.6.RELEASE\spring-aop-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-tx\5.0.6.RELEASE\spring-tx-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-beans\5.0.6.RELEASE\spring-beans-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjrt\1.8.12\aspectjrt-1.8.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-security\2.0.2.RELEASE\spring-boot-starter-security-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-config\5.0.5.RELEASE\spring-security-config-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-web\5.0.5.RELEASE\spring-security-web-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\javax\transaction\javax.transaction-api\1.3\javax.transaction-api-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-math3\3.6.1\commons-math3-3.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.66\bcprov-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\javax\interceptor\javax.interceptor-api\1.2\javax.interceptor-api-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\annotations\3.0.1\annotations-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\jcip\jcip-annotations\1.0\jcip-annotations-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-models\1.5.21\swagger-models-1.5.21.jar;C:\Users\Sohan.Dey\.m2\repository\com\auth0\java-jwt\3.8.1\java-jwt-3.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-core\1.4.2\micrometer-core-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\hdrhistogram\HdrHistogram\2.1.12\HdrHistogram-2.1.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\latencyutils\LatencyUtils\2.0.3\LatencyUtils-2.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-registry-prometheus\1.4.2\micrometer-registry-prometheus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient_common\0.8.1\simpleclient_common-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient\0.8.1\simpleclient-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\activation\1.1\activation-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-sleuth\2.0.4.RELEASE\spring-cloud-starter-sleuth-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.0.9.RELEASE\spring-boot-starter-aop-2.0.9.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-sleuth-core\2.0.4.RELEASE\spring-cloud-sleuth-core-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave\5.6.1\brave-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\zipkin2\zipkin\2.12.0\zipkin-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\reporter2\zipkin-reporter\2.7.14\zipkin-reporter-2.7.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-context-log4j2\5.6.1\brave-context-log4j2-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-web\5.6.1\brave-instrumentation-spring-web-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-http\5.6.1\brave-instrumentation-http-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-rabbit\5.6.1\brave-instrumentation-spring-rabbit-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-kafka-clients\5.6.1\brave-instrumentation-kafka-clients-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpclient\5.6.1\brave-instrumentation-httpclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpasyncclient\5.6.1\brave-instrumentation-httpasyncclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-webmvc\5.6.1\brave-instrumentation-spring-webmvc-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-servlet\5.6.1\brave-instrumentation-servlet-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-actuator\2.0.2.RELEASE\spring-boot-starter-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator-autoconfigure\2.0.2.RELEASE\spring-boot-actuator-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator\2.0.2.RELEASE\spring-boot-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.55\tomcat-embed-core-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\tomcat-annotations-api\8.5.55\tomcat-annotations-api-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\retry\spring-retry\1.2.1.RELEASE\spring-retry-1.2.1.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-afterburner\2.12.0\jackson-module-afterburner-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-registration-packet-manager\1.1.5.3\kernel-registration-packet-manager-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keygenerator-bouncycastle\1.1.5.3\kernel-keygenerator-bouncycastle-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-cbeffutil-api\1.1.5.3\kernel-cbeffutil-api-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-logger-logback\1.1.5.3\kernel-logger-logback-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\logstash\logback\logstash-logback-encoder\6.4\logstash-logback-encoder-6.4.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-access\1.2.3\logback-access-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-crypto-jce\1.1.5.3\kernel-crypto-jce-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\bitbucket\b_c\jose4j\0.6.5\jose4j-0.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j-provider\1.1.2-incubating\wink-json4j-provider-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-common\1.1.2-incubating\wink-common-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.1.1\jaxb-impl-2.2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-annotation_1.1_spec\1.0\geronimo-annotation_1.1_spec-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j\1.1.2-incubating\wink-json4j-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\jsr311-api\1.1.1\jsr311-api-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\checkerframework\checker-qual\2.9.0\checker-qual-2.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-broker\5.15.9\activemq-broker-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-client\5.15.9\activemq-client-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-jms_1.1_spec\1.1.1\geronimo-jms_1.1_spec-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\hawtbuf\hawtbuf\1.11\hawtbuf-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-j2ee-management_1.1_spec\1.0.1\geronimo-j2ee-management_1.1_spec-1.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-openwire-legacy\5.15.9\activemq-openwire-legacy-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\projectlombok\lombok\1.18.8\lombok-1.18.8.jar \ No newline at end of file diff --git a/api-test/.temp-MosipFunctionalTest-classpath-arg-1695652238739.txt b/api-test/.temp-MosipFunctionalTest-classpath-arg-1695652238739.txt deleted file mode 100644 index 0fc13dd5a..000000000 --- a/api-test/.temp-MosipFunctionalTest-classpath-arg-1695652238739.txt +++ /dev/null @@ -1 +0,0 @@ --classpath D:\Mosip_Automation_Test\MOSIP_FUNCTIONAL_TESTS\mosip-functional-tests\automationtests\target\test-classes;D:\Mosip_Automation_Test\MOSIP_FUNCTIONAL_TESTS\mosip-functional-tests\automationtests\target\classes;C:\Users\Sohan.Dey\.m2\repository\com\opencsv\opencsv\4.1\opencsv-4.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-lang3\3.6\commons-lang3-3.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-text\1.1\commons-text-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\ibm\icu\icu4j\63.1\icu4j-63.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-admin-client\17.0.1\keycloak-admin-client-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-core\17.0.1\keycloak-core-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-common\17.0.1\keycloak-common-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-client\3.13.2.Final\resteasy-client-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\ws\rs\jboss-jaxrs-api_2.1_spec\2.0.1.Final\jboss-jaxrs-api_2.1_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxrs\3.13.2.Final\resteasy-jaxrs-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\annotation\jboss-annotations-api_1.3_spec\2.0.1.Final\jboss-annotations-api_1.3_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\activation\jakarta.activation\1.2.1\jakarta.activation-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-multipart-provider\3.13.2.Final\resteasy-multipart-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\mail\jakarta.mail\1.6.5\jakarta.mail-1.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\james\apache-mime4j\0.6\apache-mime4j-0.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jackson2-provider\3.13.2.Final\resteasy-jackson2-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-json-provider\2.10.5\jackson-jaxrs-json-provider-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-base\2.10.5\jackson-jaxrs-base-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\json-patch\1.9\json-patch-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\jackson-coreutils\1.6\jackson-coreutils-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\msg-simple\1.1\msg-simple-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\btf\1.2\btf-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxb-provider\3.13.2.Final\resteasy-jaxb-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\xml\bind\jboss-jaxb-api_2.3_spec\2.0.0.Final\jboss-jaxb-api_2.3_spec-2.0.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\javassist\javassist\3.25.0-GA\javassist-3.25.0-GA.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jknack\handlebars\3.0.0\handlebars-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\antlr\antlr4-runtime\4.5.1-1\antlr4-runtime-4.5.1-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mozilla\rhino\1.7R4\rhino-1.7R4.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-api\1.6.4\slf4j-api-1.6.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-collections4\4.3\commons-collections4-4.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\jsonwebtoken\jjwt\0.6.0\jjwt-0.6.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\flipkart\zjsonpatch\zjsonpatch\0.4.7\zjsonpatch-0.4.7.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk\1.11.368\aws-java-sdk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dlm\1.11.368\aws-java-sdk-dlm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\jmespath-java\1.11.368\jmespath-java-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-macie\1.11.368\aws-java-sdk-macie-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-eks\1.11.368\aws-java-sdk-eks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediatailor\1.11.368\aws-java-sdk-mediatailor-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-neptune\1.11.368\aws-java-sdk-neptune-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pi\1.11.368\aws-java-sdk-pi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickprojects\1.11.368\aws-java-sdk-iot1clickprojects-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickdevices\1.11.368\aws-java-sdk-iot1clickdevices-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotanalytics\1.11.368\aws-java-sdk-iotanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acmpca\1.11.368\aws-java-sdk-acmpca-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-secretsmanager\1.11.368\aws-java-sdk-secretsmanager-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-fms\1.11.368\aws-java-sdk-fms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-connect\1.11.368\aws-java-sdk-connect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-transcribe\1.11.368\aws-java-sdk-transcribe-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscalingplans\1.11.368\aws-java-sdk-autoscalingplans-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workmail\1.11.368\aws-java-sdk-workmail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicediscovery\1.11.368\aws-java-sdk-servicediscovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloud9\1.11.368\aws-java-sdk-cloud9-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-serverlessapplicationrepository\1.11.368\aws-java-sdk-serverlessapplicationrepository-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-alexaforbusiness\1.11.368\aws-java-sdk-alexaforbusiness-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroups\1.11.368\aws-java-sdk-resourcegroups-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-comprehend\1.11.368\aws-java-sdk-comprehend-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-translate\1.11.368\aws-java-sdk-translate-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemaker\1.11.368\aws-java-sdk-sagemaker-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotjobsdataplane\1.11.368\aws-java-sdk-iotjobsdataplane-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemakerruntime\1.11.368\aws-java-sdk-sagemakerruntime-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesisvideo\1.11.368\aws-java-sdk-kinesisvideo-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-http\4.1.17.Final\netty-codec-http-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec\4.1.17.Final\netty-codec-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler\4.1.17.Final\netty-handler-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-buffer\4.1.17.Final\netty-buffer-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-common\4.1.17.Final\netty-common-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport\4.1.17.Final\netty-transport-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-resolver\4.1.17.Final\netty-resolver-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appsync\1.11.368\aws-java-sdk-appsync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-guardduty\1.11.368\aws-java-sdk-guardduty-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mq\1.11.368\aws-java-sdk-mq-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediaconvert\1.11.368\aws-java-sdk-mediaconvert-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastore\1.11.368\aws-java-sdk-mediastore-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastoredata\1.11.368\aws-java-sdk-mediastoredata-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-medialive\1.11.368\aws-java-sdk-medialive-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediapackage\1.11.368\aws-java-sdk-mediapackage-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costexplorer\1.11.368\aws-java-sdk-costexplorer-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pricing\1.11.368\aws-java-sdk-pricing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mobile\1.11.368\aws-java-sdk-mobile-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsmv2\1.11.368\aws-java-sdk-cloudhsmv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glue\1.11.368\aws-java-sdk-glue-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-migrationhub\1.11.368\aws-java-sdk-migrationhub-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dax\1.11.368\aws-java-sdk-dax-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-greengrass\1.11.368\aws-java-sdk-greengrass-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-athena\1.11.368\aws-java-sdk-athena-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplaceentitlement\1.11.368\aws-java-sdk-marketplaceentitlement-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codestar\1.11.368\aws-java-sdk-codestar-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lexmodelbuilding\1.11.368\aws-java-sdk-lexmodelbuilding-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroupstaggingapi\1.11.368\aws-java-sdk-resourcegroupstaggingapi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pinpoint\1.11.368\aws-java-sdk-pinpoint-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-xray\1.11.368\aws-java-sdk-xray-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworkscm\1.11.368\aws-java-sdk-opsworkscm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-support\1.11.368\aws-java-sdk-support-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpledb\1.11.368\aws-java-sdk-simpledb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicecatalog\1.11.368\aws-java-sdk-servicecatalog-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servermigration\1.11.368\aws-java-sdk-servermigration-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpleworkflow\1.11.368\aws-java-sdk-simpleworkflow-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-storagegateway\1.11.368\aws-java-sdk-storagegateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-route53\1.11.368\aws-java-sdk-route53-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-s3\1.11.368\aws-java-sdk-s3-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-importexport\1.11.368\aws-java-sdk-importexport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sts\1.11.368\aws-java-sdk-sts-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sqs\1.11.368\aws-java-sdk-sqs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rds\1.11.368\aws-java-sdk-rds-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-redshift\1.11.368\aws-java-sdk-redshift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticbeanstalk\1.11.368\aws-java-sdk-elasticbeanstalk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glacier\1.11.368\aws-java-sdk-glacier-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iam\1.11.368\aws-java-sdk-iam-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-datapipeline\1.11.368\aws-java-sdk-datapipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancing\1.11.368\aws-java-sdk-elasticloadbalancing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancingv2\1.11.368\aws-java-sdk-elasticloadbalancingv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-emr\1.11.368\aws-java-sdk-emr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticache\1.11.368\aws-java-sdk-elasticache-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elastictranscoder\1.11.368\aws-java-sdk-elastictranscoder-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ec2\1.11.368\aws-java-sdk-ec2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dynamodb\1.11.368\aws-java-sdk-dynamodb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sns\1.11.368\aws-java-sdk-sns-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-budgets\1.11.368\aws-java-sdk-budgets-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudtrail\1.11.368\aws-java-sdk-cloudtrail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatch\1.11.368\aws-java-sdk-cloudwatch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-logs\1.11.368\aws-java-sdk-logs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-events\1.11.368\aws-java-sdk-events-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidentity\1.11.368\aws-java-sdk-cognitoidentity-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitosync\1.11.368\aws-java-sdk-cognitosync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directconnect\1.11.368\aws-java-sdk-directconnect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudformation\1.11.368\aws-java-sdk-cloudformation-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudfront\1.11.368\aws-java-sdk-cloudfront-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-clouddirectory\1.11.368\aws-java-sdk-clouddirectory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesis\1.11.368\aws-java-sdk-kinesis-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworks\1.11.368\aws-java-sdk-opsworks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ses\1.11.368\aws-java-sdk-ses-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscaling\1.11.368\aws-java-sdk-autoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudsearch\1.11.368\aws-java-sdk-cloudsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatchmetrics\1.11.368\aws-java-sdk-cloudwatchmetrics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codedeploy\1.11.368\aws-java-sdk-codedeploy-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codepipeline\1.11.368\aws-java-sdk-codepipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kms\1.11.368\aws-java-sdk-kms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-config\1.11.368\aws-java-sdk-config-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lambda\1.11.368\aws-java-sdk-lambda-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecs\1.11.368\aws-java-sdk-ecs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecr\1.11.368\aws-java-sdk-ecr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsm\1.11.368\aws-java-sdk-cloudhsm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ssm\1.11.368\aws-java-sdk-ssm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workspaces\1.11.368\aws-java-sdk-workspaces-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-machinelearning\1.11.368\aws-java-sdk-machinelearning-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directory\1.11.368\aws-java-sdk-directory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-efs\1.11.368\aws-java-sdk-efs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codecommit\1.11.368\aws-java-sdk-codecommit-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-devicefarm\1.11.368\aws-java-sdk-devicefarm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticsearch\1.11.368\aws-java-sdk-elasticsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-waf\1.11.368\aws-java-sdk-waf-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacecommerceanalytics\1.11.368\aws-java-sdk-marketplacecommerceanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-inspector\1.11.368\aws-java-sdk-inspector-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot\1.11.368\aws-java-sdk-iot-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-api-gateway\1.11.368\aws-java-sdk-api-gateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acm\1.11.368\aws-java-sdk-acm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-gamelift\1.11.368\aws-java-sdk-gamelift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dms\1.11.368\aws-java-sdk-dms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacemeteringservice\1.11.368\aws-java-sdk-marketplacemeteringservice-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidp\1.11.368\aws-java-sdk-cognitoidp-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-discovery\1.11.368\aws-java-sdk-discovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-applicationautoscaling\1.11.368\aws-java-sdk-applicationautoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-snowball\1.11.368\aws-java-sdk-snowball-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rekognition\1.11.368\aws-java-sdk-rekognition-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-polly\1.11.368\aws-java-sdk-polly-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lightsail\1.11.368\aws-java-sdk-lightsail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-stepfunctions\1.11.368\aws-java-sdk-stepfunctions-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-health\1.11.368\aws-java-sdk-health-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costandusagereport\1.11.368\aws-java-sdk-costandusagereport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codebuild\1.11.368\aws-java-sdk-codebuild-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appstream\1.11.368\aws-java-sdk-appstream-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-shield\1.11.368\aws-java-sdk-shield-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-batch\1.11.368\aws-java-sdk-batch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lex\1.11.368\aws-java-sdk-lex-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mechanicalturkrequester\1.11.368\aws-java-sdk-mechanicalturkrequester-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-organizations\1.11.368\aws-java-sdk-organizations-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workdocs\1.11.368\aws-java-sdk-workdocs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-core\1.11.368\aws-java-sdk-core-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\software\amazon\ion\ion-java\1.0.2\ion-java-1.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-cbor\2.6.7\jackson-dataformat-cbor-2.6.7.jar;C:\Users\Sohan.Dey\.m2\repository\joda-time\joda-time\2.8.1\joda-time-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-models\1.11.368\aws-java-sdk-models-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-swf-libraries\1.11.22\aws-java-sdk-swf-libraries-1.11.22.jar;C:\Users\Sohan.Dey\.m2\repository\com\aventstack\extentreports\3.0.0\extentreports-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\freemarker\freemarker\2.3.23\freemarker-2.3.23.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver\3.3.0\mongodb-driver-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\bson\3.3.0\bson-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver-core\3.3.0\mongodb-driver-core-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpmime\4.5.2\httpmime-4.5.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\relevantcodes\extentreports\2.41.2\extentreports-2.41.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jsoup\jsoup\1.8.3\jsoup-1.8.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\sqlite-jdbc\3.8.11.1\sqlite-jdbc-3.8.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\jayway\jsonpath\json-path\2.4.0\json-path-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model\3.3.9\maven-model-3.3.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-utils\3.0.22\plexus-utils-3.0.22.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured\3.0.7\rest-assured-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy\2.4.12\groovy-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-xml\2.4.12\groovy-xml-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\ccil\cowan\tagsoup\tagsoup\1.2.1\tagsoup-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\json-path\3.0.7\json-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-json\2.4.12\groovy-json-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured-common\3.0.7\rest-assured-common-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\xml-path\3.0.7\xml-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\testng\testng\6.11\testng-6.11.jar;C:\Users\Sohan.Dey\.m2\repository\com\beust\jcommander\1.64\jcommander-1.64.jar;C:\Users\Sohan.Dey\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\Sohan.Dey\.m2\repository\org\zeroturnaround\zt-zip\1.13\zt-zip-1.13.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.1\jackson-core-2.10.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.5\jackson-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.5\jackson-databind-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-xml\2.9.5\jackson-dataformat-xml-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-jaxb-annotations\2.9.5\jackson-module-jaxb-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\woodstox\stax2-api\3.1.4\stax2-api-3.1.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\woodstox\woodstox-core\5.0.3\woodstox-core-5.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\json\json\20180130\json-20180130.jar;C:\Users\Sohan.Dey\.m2\repository\com\googlecode\json-simple\json-simple\1.1.1\json-simple-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\junit\junit\4.10\junit-4.10.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\gson\gson\2.8.4\gson-2.8.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\plugins\maven-assembly-plugin\3.1.0\maven-assembly-plugin-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-plugin-api\3.0\maven-plugin-api-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-plexus\1.4.2\sisu-inject-plexus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-bean\1.4.2\sisu-inject-bean-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-guice\2.1.7\sisu-guice-2.1.7-noaop.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-core\3.0\maven-core-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings\3.0\maven-settings-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings-builder\3.0\maven-settings-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-repository-metadata\3.0\maven-repository-metadata-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model-builder\3.0\maven-model-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-aether-provider\3.0\maven-aether-provider-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-impl\1.7\aether-impl-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-spi\1.7\aether-spi-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-api\1.7\aether-api-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-util\1.7\aether-util-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-classworlds\2.2.3\plexus-classworlds-2.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-component-annotations\1.5.5\plexus-component-annotations-1.5.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-sec-dispatcher\1.3\plexus-sec-dispatcher-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-cipher\1.4\plexus-cipher-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-artifact\3.0\maven-artifact-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-common-artifact-filters\3.0.1\maven-common-artifact-filters-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-utils\3.1.0\maven-shared-utils-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-artifact-transfer\0.9.0\maven-artifact-transfer-0.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-interpolation\1.24\plexus-interpolation-1.24.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-archiver\3.5\plexus-archiver-3.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-compress\1.14\commons-compress-1.14.jar;C:\Users\Sohan.Dey\.m2\repository\org\iq80\snappy\snappy\0.4\snappy-0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\tukaani\xz\1.6\xz-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\file-management\3.0.0\file-management-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-io\3.0.0\maven-shared-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-compat\3.0\maven-compat-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\wagon\wagon-provider-api\2.10\wagon-provider-api-2.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-filtering\3.1.1\maven-filtering-3.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-build-api\0.0.7\plexus-build-api-0.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-io\3.0.0\plexus-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-archiver\3.2.0\maven-archiver-3.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\guava\guava\19.0\guava-19.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\hibernate-core\5.4.2.Final\hibernate-core-5.4.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy\1.9.10\byte-buddy-1.9.10.jar;C:\Users\Sohan.Dey\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.1.0.Final\hibernate-commons-annotations-5.1.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\jaxb-runtime\2.3.1\jaxb-runtime-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\txw2\2.3.1\txw2-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\istack\istack-commons-runtime\3.0.7\istack-commons-runtime-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jvnet\staxex\stax-ex\1.8\stax-ex-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\fastinfoset\FastInfoset\1.2.15\FastInfoset-1.2.15.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils\1.9.2\commons-beanutils-1.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-annotations\1.5.20\swagger-annotations-1.5.20.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.5\jackson-datatype-jsr310-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\postgresql\postgresql\42.2.2\postgresql-42.2.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-log4j12\1.6.2\slf4j-log4j12-1.6.2.jar;C:\Users\Sohan.Dey\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.1\log4j-api-2.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\lingala\zip4j\zip4j\1.3.2\zip4j-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpclient\4.5.3\httpclient-4.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpcore\4.4.6\httpcore-4.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-client\2.8.1\hadoop-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-common\2.8.1\hadoop-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\Users\Sohan.Dey\.m2\repository\commons-net\commons-net\3.1\commons-net-3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-sslengine\6.1.26\jetty-sslengine-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\jsp\jsp-api\2.1\jsp-api-2.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\avro\avro\1.7.4\avro-1.7.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\thoughtworks\paranamer\paranamer\2.3\paranamer-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\snappy\snappy-java\1.0.4.1\snappy-java-1.0.4.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-auth\2.8.1\hadoop-auth-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-kerberos-codec\2.0.0-M15\apacheds-kerberos-codec-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-i18n\2.0.0-M15\apacheds-i18n-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-asn1-api\1.0.0-M20\api-asn1-api-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-util\1.0.0-M20\api-util-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-framework\2.7.1\curator-framework-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-client\2.7.1\curator-client-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-recipes\2.7.1\curator-recipes-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\jsr305\3.0.0\jsr305-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\htrace\htrace-core4\4.0.1-incubating\htrace-core4-4.0.1-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\zookeeper\zookeeper\3.4.6\zookeeper-3.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs\2.8.1\hadoop-hdfs-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs-client\2.8.1\hadoop-hdfs-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okhttp\okhttp\2.4.0\okhttp-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okio\okio\1.4.0\okio-1.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-app\2.8.1\hadoop-mapreduce-client-app-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-common\2.8.1\hadoop-mapreduce-client-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-client\2.8.1\hadoop-yarn-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-server-common\2.8.1\hadoop-yarn-server-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.8.1\hadoop-mapreduce-client-shuffle-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-api\2.8.1\hadoop-yarn-api-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-core\2.8.1\hadoop-mapreduce-client-core-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-common\2.8.1\hadoop-yarn-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-core\1.9\jersey-core-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-client\1.9\jersey-client-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-xc\1.9.13\jackson-xc-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.8.1\hadoop-mapreduce-client-jobclient-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-annotations\2.8.1\hadoop-annotations-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.56\bcprov-jdk15on-1.56.jar;C:\Users\Sohan.Dey\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\commons-io\commons-io\2.6\commons-io-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\core\3.3.3\core-3.3.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\javase\2.0\javase-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-all\1.3\hamcrest-all-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\velocity\velocity\1.7\velocity-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\commons-lang\commons-lang\2.4\commons-lang-2.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\html2pdf\2.0.0\html2pdf-2.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\forms\7.1.0\forms-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\kernel\7.1.0\kernel-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\io\7.1.0\io-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\layout\7.1.0\layout-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\itextpdf\5.5.13\itextpdf-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\javax\mail\javax.mail-api\1.6.2\javax.mail-api-1.6.2.jar;D:\Mosip_Automation_Test\MOSIP_FUNCTIONAL_TESTS\mosip-functional-tests\authentication-demo-service\target\classes;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.0.2.RELEASE\spring-boot-starter-web-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.0.2.RELEASE\spring-boot-starter-json-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.5\jackson-datatype-jdk8-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.5\jackson-module-parameter-names-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.0.2.RELEASE\spring-boot-starter-tomcat-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.31\tomcat-embed-el-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.31\tomcat-embed-websocket-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.9.Final\hibernate-validator-6.0.9.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-web\5.0.6.RELEASE\spring-web-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webmvc\5.0.6.RELEASE\spring-webmvc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-expression\5.0.6.RELEASE\spring-expression-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-test\2.0.2.RELEASE\spring-boot-starter-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test\2.0.2.RELEASE\spring-boot-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test-autoconfigure\2.0.2.RELEASE\spring-boot-test-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\assertj\assertj-core\3.9.1\assertj-core-3.9.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-core\5.0.6.RELEASE\spring-core-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jcl\5.0.6.RELEASE\spring-jcl-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-test\5.0.6.RELEASE\spring-test-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\xmlunit\xmlunit-core\2.5.1\xmlunit-core-2.5.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-ui\2.9.2\springfox-swagger-ui-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spring-web\2.9.2\springfox-spring-web-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger2\2.9.2\springfox-swagger2-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spi\2.9.2\springfox-spi-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-core\2.9.2\springfox-core-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-schema\2.9.2\springfox-schema-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-common\2.9.2\springfox-swagger-common-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\mapstruct\mapstruct\1.2.0.Final\mapstruct-1.2.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\javax.ws.rs-api\2.0\javax.ws.rs-api-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\authentication\authentication-core\1.2.0.1-B3\authentication-core-1.2.0.1-B3.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-cache\2.0.2.RELEASE\spring-boot-starter-cache-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context-support\5.0.6.RELEASE\spring-context-support-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\sourceafis\sourceafis\3.4.0\sourceafis-3.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\sf\trove4j\trove4j\3.0.3\trove4j-3.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\noexception\noexception\1.3.2\noexception-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\mhshams\jnbis\2.0.1\jnbis-2.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\sanselan\sanselan\0.97-incubator\sanselan-0.97-incubator.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\idrepository\id-repository-core\1.2.0.1-B1\id-repository-core-1.2.0.1-B1.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biosdk-provider\1.2.0.1-B1\kernel-biosdk-provider-1.2.0.1-B1.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biometrics-api\1.2.0.1-B1\kernel-biometrics-api-1.2.0.1-B1.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-demographics-api\1.2.0.1-B1\kernel-demographics-api-1.2.0.1-B1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-webflux\2.0.2.RELEASE\spring-boot-starter-webflux-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-reactor-netty\2.0.2.RELEASE\spring-boot-starter-reactor-netty-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\ipc\reactor-netty\0.7.7.RELEASE\reactor-netty-0.7.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler-proxy\4.1.24.Final\netty-handler-proxy-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-socks\4.1.24.Final\netty-codec-socks-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-epoll\4.1.24.Final\netty-transport-native-epoll-4.1.24.Final-linux-x86_64.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.24.Final\netty-transport-native-unix-common-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webflux\5.0.6.RELEASE\spring-webflux-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-multipart-parser\1.1.0\nio-multipart-parser-1.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-stream-storage\1.1.3\nio-stream-storage-1.1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-ui\1.5.10\springdoc-openapi-ui-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-webmvc-core\1.5.10\springdoc-openapi-webmvc-core-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-common\1.5.10\springdoc-openapi-common-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-models\2.1.10\swagger-models-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-annotations\2.1.10\swagger-annotations-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-integration\2.1.10\swagger-integration-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-core\2.1.10\swagger-core-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\xml\bind\jakarta.xml.bind-api\2.3.2\jakarta.xml.bind-api-2.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\activation\jakarta.activation-api\1.2.1\jakarta.activation-api-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.12.1\jackson-dataformat-yaml-2.12.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\github\classgraph\classgraph\4.8.69\classgraph-4.8.69.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\swagger-ui\3.51.1\swagger-ui-3.51.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\webjars-locator-core\0.45\webjars-locator-core-0.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\mockito\mockito-core\2.23.4\mockito-core-2.23.4.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy-agent\1.9.3\byte-buddy-agent-1.9.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\objenesis\objenesis\2.6\objenesis-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-websubclient-api\1.2.0.1-SNAPSHOT\kernel-websubclient-api-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aspects\5.0.6.RELEASE\spring-aspects-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjweaver\1.8.13\aspectjweaver-1.8.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\owasp\encoder\encoder\1.2.3\encoder-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-actuator\2.0.2.RELEASE\spring-boot-starter-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator-autoconfigure\2.0.2.RELEASE\spring-boot-actuator-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator\2.0.2.RELEASE\spring-boot-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\tensorflow\1.12.0\tensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow\1.12.0\libtensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow_jni\1.12.0\libtensorflow_jni-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-templatemanager-velocity\1.2.0.1-SNAPSHOT\kernel-templatemanager-velocity-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\javax.servlet-api\4.0.1\javax.servlet-api-4.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-config\2.0.0.RELEASE\spring-cloud-starter-config-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter\2.0.0.RELEASE\spring-cloud-starter-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-context\2.0.0.RELEASE\spring-cloud-context-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-crypto\5.0.6.RELEASE\spring-security-crypto-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-commons\2.0.0.RELEASE\spring-cloud-commons-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-rsa\1.0.5.RELEASE\spring-security-rsa-1.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-config-client\2.0.0.RELEASE\spring-cloud-config-client-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-jpeg2000\1.3.0\jai-imageio-jpeg2000-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-core\1.3.0\jai-imageio-core-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keymanager-service\1.2.0.1-SNAPSHOT\kernel-keymanager-service-1.2.0.1-SNAPSHOT-lib.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-test\5.0.5.RELEASE\spring-security-test-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-core\5.0.5.RELEASE\spring-security-core-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-pdfgenerator-itext\1.2.0.1-SNAPSHOT\kernel-pdfgenerator-itext-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\barcodes\7.1.0\barcodes-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\font-asian\7.1.0\font-asian-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\hyph\7.1.0\hyph-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\pdfa\7.1.0\pdfa-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\sign\7.1.0\sign-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\tool\xmlworker\5.5.13\xmlworker-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.66\bcpkix-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-data-jpa\2.0.2.RELEASE\spring-boot-starter-data-jpa-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.0.2.RELEASE\spring-boot-starter-jdbc-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\zaxxer\HikariCP\2.7.9\HikariCP-2.7.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\h2database\h2\1.4.197\h2-1.4.197.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\microsoft\TSS.Java\0.3.0\TSS.Java-0.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\java\dev\jna\jna\4.4.0\jna-4.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-api\2.4.1.Final\cache2k-api-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-core\2.4.1.Final\cache2k-core-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\nimbusds\nimbus-jose-jwt\9.14\nimbus-jose-jwt-9.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\reactor-core\3.1.7.RELEASE\reactor-core-3.1.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-core\1.2.0.1-B1\kernel-core-1.2.0.1-B1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter\2.0.2.RELEASE\spring-boot-starter-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot\2.0.2.RELEASE\spring-boot-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.0.2.RELEASE\spring-boot-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.0.2.RELEASE\spring-boot-starter-logging-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.10.0\log4j-to-slf4j-2.10.0.jar;C:\Users\Sohan.Dey\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-jpa\2.0.7.RELEASE\spring-data-jpa-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-commons\2.0.7.RELEASE\spring-data-commons-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-orm\5.0.6.RELEASE\spring-orm-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jdbc\5.0.6.RELEASE\spring-jdbc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context\5.0.6.RELEASE\spring-context-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aop\5.0.6.RELEASE\spring-aop-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-tx\5.0.6.RELEASE\spring-tx-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-beans\5.0.6.RELEASE\spring-beans-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjrt\1.8.12\aspectjrt-1.8.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-security\2.0.2.RELEASE\spring-boot-starter-security-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-config\5.0.5.RELEASE\spring-security-config-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-web\5.0.5.RELEASE\spring-security-web-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\javax\transaction\javax.transaction-api\1.3\javax.transaction-api-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-math3\3.6.1\commons-math3-3.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\interceptor\javax.interceptor-api\1.2\javax.interceptor-api-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\annotations\3.0.1\annotations-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\jcip\jcip-annotations\1.0\jcip-annotations-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-models\1.5.21\swagger-models-1.5.21.jar;C:\Users\Sohan.Dey\.m2\repository\com\auth0\java-jwt\3.8.1\java-jwt-3.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-core\1.4.2\micrometer-core-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\hdrhistogram\HdrHistogram\2.1.12\HdrHistogram-2.1.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\latencyutils\LatencyUtils\2.0.3\LatencyUtils-2.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-registry-prometheus\1.4.2\micrometer-registry-prometheus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient_common\0.8.1\simpleclient_common-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient\0.8.1\simpleclient-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\activation\1.1\activation-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-sleuth\2.0.4.RELEASE\spring-cloud-starter-sleuth-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.0.9.RELEASE\spring-boot-starter-aop-2.0.9.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-sleuth-core\2.0.4.RELEASE\spring-cloud-sleuth-core-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave\5.6.1\brave-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\zipkin2\zipkin\2.12.0\zipkin-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\reporter2\zipkin-reporter\2.7.14\zipkin-reporter-2.7.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-context-log4j2\5.6.1\brave-context-log4j2-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-web\5.6.1\brave-instrumentation-spring-web-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-http\5.6.1\brave-instrumentation-http-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-rabbit\5.6.1\brave-instrumentation-spring-rabbit-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-kafka-clients\5.6.1\brave-instrumentation-kafka-clients-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpclient\5.6.1\brave-instrumentation-httpclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpasyncclient\5.6.1\brave-instrumentation-httpasyncclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-webmvc\5.6.1\brave-instrumentation-spring-webmvc-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-servlet\5.6.1\brave-instrumentation-servlet-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.55\tomcat-embed-core-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\tomcat-annotations-api\8.5.55\tomcat-annotations-api-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\retry\spring-retry\1.2.1.RELEASE\spring-retry-1.2.1.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-afterburner\2.12.0\jackson-module-afterburner-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-registration-packet-manager\1.1.5.3\kernel-registration-packet-manager-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keygenerator-bouncycastle\1.1.5.3\kernel-keygenerator-bouncycastle-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-cbeffutil-api\1.1.5.3\kernel-cbeffutil-api-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-logger-logback\1.1.5.3\kernel-logger-logback-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\logstash\logback\logstash-logback-encoder\6.4\logstash-logback-encoder-6.4.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-access\1.2.3\logback-access-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-crypto-jce\1.1.5.3\kernel-crypto-jce-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\bitbucket\b_c\jose4j\0.6.5\jose4j-0.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j-provider\1.1.2-incubating\wink-json4j-provider-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-common\1.1.2-incubating\wink-common-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.1.1\jaxb-impl-2.2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-annotation_1.1_spec\1.0\geronimo-annotation_1.1_spec-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j\1.1.2-incubating\wink-json4j-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\jsr311-api\1.1.1\jsr311-api-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\checkerframework\checker-qual\2.9.0\checker-qual-2.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-broker\5.15.9\activemq-broker-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-client\5.15.9\activemq-client-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-jms_1.1_spec\1.1.1\geronimo-jms_1.1_spec-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\hawtbuf\hawtbuf\1.11\hawtbuf-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-j2ee-management_1.1_spec\1.0.1\geronimo-j2ee-management_1.1_spec-1.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-openwire-legacy\5.15.9\activemq-openwire-legacy-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\projectlombok\lombok\1.18.8\lombok-1.18.8.jar \ No newline at end of file diff --git a/api-test/.temp-New_configuration (1)-classpath-arg-1658840665646.txt b/api-test/.temp-New_configuration (1)-classpath-arg-1658840665646.txt deleted file mode 100644 index 1aa22a4a9..000000000 --- a/api-test/.temp-New_configuration (1)-classpath-arg-1658840665646.txt +++ /dev/null @@ -1 +0,0 @@ --classpath D:\Mosip_Automation_Test\Docker_Test\mosip-functional-tests\automationtests\target\test-classes;D:\Mosip_Automation_Test\Docker_Test\mosip-functional-tests\automationtests\target\classes;C:\Users\Sohan.Dey\.m2\repository\com\opencsv\opencsv\4.1\opencsv-4.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-lang3\3.6\commons-lang3-3.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-text\1.1\commons-text-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\ibm\icu\icu4j\63.1\icu4j-63.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-admin-client\17.0.1\keycloak-admin-client-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-core\17.0.1\keycloak-core-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\keycloak\keycloak-common\17.0.1\keycloak-common-17.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-client\3.13.2.Final\resteasy-client-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\ws\rs\jboss-jaxrs-api_2.1_spec\2.0.1.Final\jboss-jaxrs-api_2.1_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxrs\3.13.2.Final\resteasy-jaxrs-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\reactivestreams\reactive-streams\1.0.3\reactive-streams-1.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\validation\jakarta.validation-api\2.0.2\jakarta.validation-api-2.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\annotation\jboss-annotations-api_1.3_spec\2.0.1.Final\jboss-annotations-api_1.3_spec-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\activation\jakarta.activation\1.2.1\jakarta.activation-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\stephenc\jcip\jcip-annotations\1.0-1\jcip-annotations-1.0-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-multipart-provider\3.13.2.Final\resteasy-multipart-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\mail\jakarta.mail\1.6.5\jakarta.mail-1.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\james\apache-mime4j\0.6\apache-mime4j-0.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jackson2-provider\3.13.2.Final\resteasy-jackson2-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-json-provider\2.10.5\jackson-jaxrs-json-provider-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\jaxrs\jackson-jaxrs-base\2.10.5\jackson-jaxrs-base-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\json-patch\1.9\json-patch-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\jackson-coreutils\1.6\jackson-coreutils-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\msg-simple\1.1\msg-simple-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\fge\btf\1.2\btf-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\resteasy\resteasy-jaxb-provider\3.13.2.Final\resteasy-jaxb-provider-3.13.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\xml\bind\jboss-jaxb-api_2.3_spec\2.0.0.Final\jboss-jaxb-api_2.3_spec-2.0.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\javassist\javassist\3.25.0-GA\javassist-3.25.0-GA.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jknack\handlebars\3.0.0\handlebars-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\antlr\antlr4-runtime\4.5.1-1\antlr4-runtime-4.5.1-1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mozilla\rhino\1.7R4\rhino-1.7R4.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-api\1.6.4\slf4j-api-1.6.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-collections4\4.3\commons-collections4-4.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\jsonwebtoken\jjwt\0.6.0\jjwt-0.6.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\flipkart\zjsonpatch\zjsonpatch\0.4.7\zjsonpatch-0.4.7.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk\1.11.368\aws-java-sdk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dlm\1.11.368\aws-java-sdk-dlm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\jmespath-java\1.11.368\jmespath-java-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-macie\1.11.368\aws-java-sdk-macie-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-eks\1.11.368\aws-java-sdk-eks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediatailor\1.11.368\aws-java-sdk-mediatailor-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-neptune\1.11.368\aws-java-sdk-neptune-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pi\1.11.368\aws-java-sdk-pi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickprojects\1.11.368\aws-java-sdk-iot1clickprojects-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot1clickdevices\1.11.368\aws-java-sdk-iot1clickdevices-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotanalytics\1.11.368\aws-java-sdk-iotanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acmpca\1.11.368\aws-java-sdk-acmpca-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-secretsmanager\1.11.368\aws-java-sdk-secretsmanager-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-fms\1.11.368\aws-java-sdk-fms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-connect\1.11.368\aws-java-sdk-connect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-transcribe\1.11.368\aws-java-sdk-transcribe-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscalingplans\1.11.368\aws-java-sdk-autoscalingplans-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workmail\1.11.368\aws-java-sdk-workmail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicediscovery\1.11.368\aws-java-sdk-servicediscovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloud9\1.11.368\aws-java-sdk-cloud9-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-serverlessapplicationrepository\1.11.368\aws-java-sdk-serverlessapplicationrepository-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-alexaforbusiness\1.11.368\aws-java-sdk-alexaforbusiness-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroups\1.11.368\aws-java-sdk-resourcegroups-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-comprehend\1.11.368\aws-java-sdk-comprehend-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-translate\1.11.368\aws-java-sdk-translate-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemaker\1.11.368\aws-java-sdk-sagemaker-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iotjobsdataplane\1.11.368\aws-java-sdk-iotjobsdataplane-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sagemakerruntime\1.11.368\aws-java-sdk-sagemakerruntime-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesisvideo\1.11.368\aws-java-sdk-kinesisvideo-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-http\4.1.17.Final\netty-codec-http-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec\4.1.17.Final\netty-codec-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler\4.1.17.Final\netty-handler-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-buffer\4.1.17.Final\netty-buffer-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-common\4.1.17.Final\netty-common-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport\4.1.17.Final\netty-transport-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-resolver\4.1.17.Final\netty-resolver-4.1.17.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appsync\1.11.368\aws-java-sdk-appsync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-guardduty\1.11.368\aws-java-sdk-guardduty-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mq\1.11.368\aws-java-sdk-mq-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediaconvert\1.11.368\aws-java-sdk-mediaconvert-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastore\1.11.368\aws-java-sdk-mediastore-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediastoredata\1.11.368\aws-java-sdk-mediastoredata-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-medialive\1.11.368\aws-java-sdk-medialive-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mediapackage\1.11.368\aws-java-sdk-mediapackage-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costexplorer\1.11.368\aws-java-sdk-costexplorer-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pricing\1.11.368\aws-java-sdk-pricing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mobile\1.11.368\aws-java-sdk-mobile-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsmv2\1.11.368\aws-java-sdk-cloudhsmv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glue\1.11.368\aws-java-sdk-glue-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-migrationhub\1.11.368\aws-java-sdk-migrationhub-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dax\1.11.368\aws-java-sdk-dax-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-greengrass\1.11.368\aws-java-sdk-greengrass-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-athena\1.11.368\aws-java-sdk-athena-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplaceentitlement\1.11.368\aws-java-sdk-marketplaceentitlement-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codestar\1.11.368\aws-java-sdk-codestar-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lexmodelbuilding\1.11.368\aws-java-sdk-lexmodelbuilding-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-resourcegroupstaggingapi\1.11.368\aws-java-sdk-resourcegroupstaggingapi-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-pinpoint\1.11.368\aws-java-sdk-pinpoint-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-xray\1.11.368\aws-java-sdk-xray-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworkscm\1.11.368\aws-java-sdk-opsworkscm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-support\1.11.368\aws-java-sdk-support-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpledb\1.11.368\aws-java-sdk-simpledb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servicecatalog\1.11.368\aws-java-sdk-servicecatalog-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-servermigration\1.11.368\aws-java-sdk-servermigration-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-simpleworkflow\1.11.368\aws-java-sdk-simpleworkflow-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-storagegateway\1.11.368\aws-java-sdk-storagegateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-route53\1.11.368\aws-java-sdk-route53-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-s3\1.11.368\aws-java-sdk-s3-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-importexport\1.11.368\aws-java-sdk-importexport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sts\1.11.368\aws-java-sdk-sts-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sqs\1.11.368\aws-java-sdk-sqs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rds\1.11.368\aws-java-sdk-rds-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-redshift\1.11.368\aws-java-sdk-redshift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticbeanstalk\1.11.368\aws-java-sdk-elasticbeanstalk-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-glacier\1.11.368\aws-java-sdk-glacier-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iam\1.11.368\aws-java-sdk-iam-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-datapipeline\1.11.368\aws-java-sdk-datapipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancing\1.11.368\aws-java-sdk-elasticloadbalancing-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticloadbalancingv2\1.11.368\aws-java-sdk-elasticloadbalancingv2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-emr\1.11.368\aws-java-sdk-emr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticache\1.11.368\aws-java-sdk-elasticache-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elastictranscoder\1.11.368\aws-java-sdk-elastictranscoder-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ec2\1.11.368\aws-java-sdk-ec2-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dynamodb\1.11.368\aws-java-sdk-dynamodb-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-sns\1.11.368\aws-java-sdk-sns-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-budgets\1.11.368\aws-java-sdk-budgets-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudtrail\1.11.368\aws-java-sdk-cloudtrail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatch\1.11.368\aws-java-sdk-cloudwatch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-logs\1.11.368\aws-java-sdk-logs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-events\1.11.368\aws-java-sdk-events-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidentity\1.11.368\aws-java-sdk-cognitoidentity-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitosync\1.11.368\aws-java-sdk-cognitosync-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directconnect\1.11.368\aws-java-sdk-directconnect-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudformation\1.11.368\aws-java-sdk-cloudformation-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudfront\1.11.368\aws-java-sdk-cloudfront-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-clouddirectory\1.11.368\aws-java-sdk-clouddirectory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kinesis\1.11.368\aws-java-sdk-kinesis-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-opsworks\1.11.368\aws-java-sdk-opsworks-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ses\1.11.368\aws-java-sdk-ses-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-autoscaling\1.11.368\aws-java-sdk-autoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudsearch\1.11.368\aws-java-sdk-cloudsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudwatchmetrics\1.11.368\aws-java-sdk-cloudwatchmetrics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codedeploy\1.11.368\aws-java-sdk-codedeploy-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codepipeline\1.11.368\aws-java-sdk-codepipeline-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-kms\1.11.368\aws-java-sdk-kms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-config\1.11.368\aws-java-sdk-config-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lambda\1.11.368\aws-java-sdk-lambda-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecs\1.11.368\aws-java-sdk-ecs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ecr\1.11.368\aws-java-sdk-ecr-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cloudhsm\1.11.368\aws-java-sdk-cloudhsm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-ssm\1.11.368\aws-java-sdk-ssm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workspaces\1.11.368\aws-java-sdk-workspaces-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-machinelearning\1.11.368\aws-java-sdk-machinelearning-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-directory\1.11.368\aws-java-sdk-directory-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-efs\1.11.368\aws-java-sdk-efs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codecommit\1.11.368\aws-java-sdk-codecommit-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-devicefarm\1.11.368\aws-java-sdk-devicefarm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-elasticsearch\1.11.368\aws-java-sdk-elasticsearch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-waf\1.11.368\aws-java-sdk-waf-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacecommerceanalytics\1.11.368\aws-java-sdk-marketplacecommerceanalytics-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-inspector\1.11.368\aws-java-sdk-inspector-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-iot\1.11.368\aws-java-sdk-iot-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-api-gateway\1.11.368\aws-java-sdk-api-gateway-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-acm\1.11.368\aws-java-sdk-acm-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-gamelift\1.11.368\aws-java-sdk-gamelift-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-dms\1.11.368\aws-java-sdk-dms-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-marketplacemeteringservice\1.11.368\aws-java-sdk-marketplacemeteringservice-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-cognitoidp\1.11.368\aws-java-sdk-cognitoidp-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-discovery\1.11.368\aws-java-sdk-discovery-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-applicationautoscaling\1.11.368\aws-java-sdk-applicationautoscaling-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-snowball\1.11.368\aws-java-sdk-snowball-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-rekognition\1.11.368\aws-java-sdk-rekognition-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-polly\1.11.368\aws-java-sdk-polly-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lightsail\1.11.368\aws-java-sdk-lightsail-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-stepfunctions\1.11.368\aws-java-sdk-stepfunctions-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-health\1.11.368\aws-java-sdk-health-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-costandusagereport\1.11.368\aws-java-sdk-costandusagereport-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-codebuild\1.11.368\aws-java-sdk-codebuild-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-appstream\1.11.368\aws-java-sdk-appstream-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-shield\1.11.368\aws-java-sdk-shield-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-batch\1.11.368\aws-java-sdk-batch-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-lex\1.11.368\aws-java-sdk-lex-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-mechanicalturkrequester\1.11.368\aws-java-sdk-mechanicalturkrequester-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-organizations\1.11.368\aws-java-sdk-organizations-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-workdocs\1.11.368\aws-java-sdk-workdocs-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-core\1.11.368\aws-java-sdk-core-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\software\amazon\ion\ion-java\1.0.2\ion-java-1.0.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-cbor\2.6.7\jackson-dataformat-cbor-2.6.7.jar;C:\Users\Sohan.Dey\.m2\repository\joda-time\joda-time\2.8.1\joda-time-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-models\1.11.368\aws-java-sdk-models-1.11.368.jar;C:\Users\Sohan.Dey\.m2\repository\com\amazonaws\aws-java-sdk-swf-libraries\1.11.22\aws-java-sdk-swf-libraries-1.11.22.jar;C:\Users\Sohan.Dey\.m2\repository\com\aventstack\extentreports\3.0.0\extentreports-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\freemarker\freemarker\2.3.23\freemarker-2.3.23.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver\3.3.0\mongodb-driver-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\bson\3.3.0\bson-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\mongodb\mongodb-driver-core\3.3.0\mongodb-driver-core-3.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpmime\4.5.2\httpmime-4.5.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\relevantcodes\extentreports\2.41.2\extentreports-2.41.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\jsoup\jsoup\1.8.3\jsoup-1.8.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\sqlite-jdbc\3.8.11.1\sqlite-jdbc-3.8.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\jayway\jsonpath\json-path\2.4.0\json-path-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\json-smart\2.3\json-smart-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\minidev\accessors-smart\1.2\accessors-smart-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\ow2\asm\asm\5.0.4\asm-5.0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model\3.3.9\maven-model-3.3.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-utils\3.0.22\plexus-utils-3.0.22.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured\3.0.7\rest-assured-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy\2.4.12\groovy-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-xml\2.4.12\groovy-xml-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-library\1.3\hamcrest-library-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\ccil\cowan\tagsoup\tagsoup\1.2.1\tagsoup-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\json-path\3.0.7\json-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\groovy\groovy-json\2.4.12\groovy-json-2.4.12.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\rest-assured-common\3.0.7\rest-assured-common-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\io\rest-assured\xml-path\3.0.7\xml-path-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\testng\testng\6.11\testng-6.11.jar;C:\Users\Sohan.Dey\.m2\repository\com\beust\jcommander\1.64\jcommander-1.64.jar;C:\Users\Sohan.Dey\.m2\repository\org\yaml\snakeyaml\1.17\snakeyaml-1.17.jar;C:\Users\Sohan.Dey\.m2\repository\org\zeroturnaround\zt-zip\1.13\zt-zip-1.13.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.10.1\jackson-core-2.10.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.5\jackson-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.10.5\jackson-databind-2.10.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-xml\2.9.5\jackson-dataformat-xml-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-jaxb-annotations\2.9.5\jackson-module-jaxb-annotations-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\woodstox\stax2-api\3.1.4\stax2-api-3.1.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\woodstox\woodstox-core\5.0.3\woodstox-core-5.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\json\json\20180130\json-20180130.jar;C:\Users\Sohan.Dey\.m2\repository\com\googlecode\json-simple\json-simple\1.1.1\json-simple-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\junit\junit\4.10\junit-4.10.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\gson\gson\2.8.4\gson-2.8.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\plugins\maven-assembly-plugin\3.1.0\maven-assembly-plugin-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-plugin-api\3.0\maven-plugin-api-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-plexus\1.4.2\sisu-inject-plexus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-inject-bean\1.4.2\sisu-inject-bean-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\sisu\sisu-guice\2.1.7\sisu-guice-2.1.7-noaop.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-core\3.0\maven-core-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings\3.0\maven-settings-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-settings-builder\3.0\maven-settings-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-repository-metadata\3.0\maven-repository-metadata-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-model-builder\3.0\maven-model-builder-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-aether-provider\3.0\maven-aether-provider-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-impl\1.7\aether-impl-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-spi\1.7\aether-spi-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-api\1.7\aether-api-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\aether\aether-util\1.7\aether-util-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-classworlds\2.2.3\plexus-classworlds-2.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-component-annotations\1.5.5\plexus-component-annotations-1.5.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-sec-dispatcher\1.3\plexus-sec-dispatcher-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-cipher\1.4\plexus-cipher-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-artifact\3.0\maven-artifact-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-common-artifact-filters\3.0.1\maven-common-artifact-filters-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-utils\3.1.0\maven-shared-utils-3.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-artifact-transfer\0.9.0\maven-artifact-transfer-0.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-interpolation\1.24\plexus-interpolation-1.24.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-archiver\3.5\plexus-archiver-3.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-compress\1.14\commons-compress-1.14.jar;C:\Users\Sohan.Dey\.m2\repository\org\iq80\snappy\snappy\0.4\snappy-0.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\tukaani\xz\1.6\xz-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\file-management\3.0.0\file-management-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-shared-io\3.0.0\maven-shared-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-compat\3.0\maven-compat-3.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\wagon\wagon-provider-api\2.10\wagon-provider-api-2.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\shared\maven-filtering\3.1.1\maven-filtering-3.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\sonatype\plexus\plexus-build-api\0.0.7\plexus-build-api-0.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\plexus\plexus-io\3.0.0\plexus-io-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\maven\maven-archiver\3.2.0\maven-archiver-3.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\guava\guava\19.0\guava-19.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\hibernate-core\5.4.2.Final\hibernate-core-5.4.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy\1.9.10\byte-buddy-1.9.10.jar;C:\Users\Sohan.Dey\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\spec\javax\transaction\jboss-transaction-api_1.2_spec\1.1.1.Final\jboss-transaction-api_1.2_spec-1.1.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\classmate\1.3.4\classmate-1.3.4.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\dom4j\dom4j\2.1.1\dom4j-2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.1.0.Final\hibernate-commons-annotations-5.1.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\jaxb-runtime\2.3.1\jaxb-runtime-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\glassfish\jaxb\txw2\2.3.1\txw2-2.3.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\istack\istack-commons-runtime\3.0.7\istack-commons-runtime-3.0.7.jar;C:\Users\Sohan.Dey\.m2\repository\org\jvnet\staxex\stax-ex\1.8\stax-ex-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\fastinfoset\FastInfoset\1.2.15\FastInfoset-1.2.15.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils\1.9.2\commons-beanutils-1.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\commons-logging\commons-logging\1.1.1\commons-logging-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-collections\commons-collections\3.2.1\commons-collections-3.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-annotations\1.5.20\swagger-annotations-1.5.20.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.5\jackson-datatype-jsr310-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\postgresql\postgresql\42.2.2\postgresql-42.2.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\slf4j-log4j12\1.6.2\slf4j-log4j12-1.6.2.jar;C:\Users\Sohan.Dey\.m2\repository\log4j\log4j\1.2.16\log4j-1.2.16.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.1\log4j-api-2.11.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\lingala\zip4j\zip4j\1.3.2\zip4j-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpclient\4.5.3\httpclient-4.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\httpcomponents\httpcore\4.4.6\httpcore-4.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-client\2.8.1\hadoop-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-common\2.8.1\hadoop-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-cli\commons-cli\1.2\commons-cli-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\xmlenc\xmlenc\0.52\xmlenc-0.52.jar;C:\Users\Sohan.Dey\.m2\repository\commons-net\commons-net\3.1\commons-net-3.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-sslengine\6.1.26\jetty-sslengine-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\jsp\jsp-api\2.1\jsp-api-2.1.jar;C:\Users\Sohan.Dey\.m2\repository\commons-configuration\commons-configuration\1.6\commons-configuration-1.6.jar;C:\Users\Sohan.Dey\.m2\repository\commons-digester\commons-digester\1.8\commons-digester-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\commons-beanutils\commons-beanutils-core\1.8.0\commons-beanutils-core-1.8.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-core-asl\1.9.13\jackson-core-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-mapper-asl\1.9.13\jackson-mapper-asl-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\avro\avro\1.7.4\avro-1.7.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\thoughtworks\paranamer\paranamer\2.3\paranamer-2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\xerial\snappy\snappy-java\1.0.4.1\snappy-java-1.0.4.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-auth\2.8.1\hadoop-auth-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-kerberos-codec\2.0.0-M15\apacheds-kerberos-codec-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\server\apacheds-i18n\2.0.0-M15\apacheds-i18n-2.0.0-M15.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-asn1-api\1.0.0-M20\api-asn1-api-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\directory\api\api-util\1.0.0-M20\api-util-1.0.0-M20.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-framework\2.7.1\curator-framework-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-client\2.7.1\curator-client-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\curator\curator-recipes\2.7.1\curator-recipes-2.7.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\jsr305\3.0.0\jsr305-3.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\htrace\htrace-core4\4.0.1-incubating\htrace-core4-4.0.1-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\zookeeper\zookeeper\3.4.6\zookeeper-3.4.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty\3.7.0.Final\netty-3.7.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs\2.8.1\hadoop-hdfs-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-hdfs-client\2.8.1\hadoop-hdfs-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okhttp\okhttp\2.4.0\okhttp-2.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\squareup\okio\okio\1.4.0\okio-1.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-app\2.8.1\hadoop-mapreduce-client-app-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-common\2.8.1\hadoop-mapreduce-client-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-client\2.8.1\hadoop-yarn-client-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-server-common\2.8.1\hadoop-yarn-server-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-shuffle\2.8.1\hadoop-mapreduce-client-shuffle-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\leveldbjni\leveldbjni-all\1.8\leveldbjni-all-1.8.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-api\2.8.1\hadoop-yarn-api-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-core\2.8.1\hadoop-mapreduce-client-core-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-yarn-common\2.8.1\hadoop-yarn-common-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\servlet-api\2.5\servlet-api-2.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\mortbay\jetty\jetty-util\6.1.26\jetty-util-6.1.26.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-core\1.9\jersey-core-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\jersey\jersey-client\1.9\jersey-client-1.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-jaxrs\1.9.13\jackson-jaxrs-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\codehaus\jackson\jackson-xc\1.9.13\jackson-xc-1.9.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-mapreduce-client-jobclient\2.8.1\hadoop-mapreduce-client-jobclient-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\hadoop\hadoop-annotations\2.8.1\hadoop-annotations-2.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk16\1.45\bcprov-jdk16-1.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-codec\commons-codec\1.11\commons-codec-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\commons-io\commons-io\2.6\commons-io-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\core\3.3.3\core-3.3.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\zxing\javase\2.0\javase-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\hamcrest\hamcrest-all\1.3\hamcrest-all-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\velocity\velocity\1.7\velocity-1.7.jar;C:\Users\Sohan.Dey\.m2\repository\commons-lang\commons-lang\2.4\commons-lang-2.4.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\html2pdf\2.0.0\html2pdf-2.0.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\forms\7.1.0\forms-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\kernel\7.1.0\kernel-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\io\7.1.0\io-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\layout\7.1.0\layout-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\itextpdf\5.5.13\itextpdf-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\javax\mail\javax.mail-api\1.6.2\javax.mail-api-1.6.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\authentication\authentication-demo-service\1.2.0.1-SNAPSHOT\authentication-demo-service-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.0.2.RELEASE\spring-boot-starter-web-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.0.2.RELEASE\spring-boot-starter-json-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.5\jackson-datatype-jdk8-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.5\jackson-module-parameter-names-2.9.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.0.2.RELEASE\spring-boot-starter-tomcat-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\8.5.31\tomcat-embed-el-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\8.5.31\tomcat-embed-websocket-8.5.31.jar;C:\Users\Sohan.Dey\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.9.Final\hibernate-validator-6.0.9.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-web\5.0.6.RELEASE\spring-web-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webmvc\5.0.6.RELEASE\spring-webmvc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-expression\5.0.6.RELEASE\spring-expression-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-test\2.0.2.RELEASE\spring-boot-starter-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test\2.0.2.RELEASE\spring-boot-test-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-test-autoconfigure\2.0.2.RELEASE\spring-boot-test-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\assertj\assertj-core\3.9.1\assertj-core-3.9.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\skyscreamer\jsonassert\1.5.0\jsonassert-1.5.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\vaadin\external\google\android-json\0.0.20131108.vaadin1\android-json-0.0.20131108.vaadin1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-core\5.0.6.RELEASE\spring-core-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jcl\5.0.6.RELEASE\spring-jcl-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-test\5.0.6.RELEASE\spring-test-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\xmlunit\xmlunit-core\2.5.1\xmlunit-core-2.5.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-ui\2.9.2\springfox-swagger-ui-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spring-web\2.9.2\springfox-spring-web-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger2\2.9.2\springfox-swagger2-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-spi\2.9.2\springfox-spi-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-core\2.9.2\springfox-core-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-schema\2.9.2\springfox-schema-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\springfox\springfox-swagger-common\2.9.2\springfox-swagger-common-2.9.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-core\1.2.0.RELEASE\spring-plugin-core-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\plugin\spring-plugin-metadata\1.2.0.RELEASE\spring-plugin-metadata-1.2.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\mapstruct\mapstruct\1.2.0.Final\mapstruct-1.2.0.Final.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\javax.ws.rs-api\2.0\javax.ws.rs-api-2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\authentication\authentication-core\1.2.0.1-SNAPSHOT\authentication-core-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-cache\2.0.2.RELEASE\spring-boot-starter-cache-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context-support\5.0.6.RELEASE\spring-context-support-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\sourceafis\sourceafis\3.4.0\sourceafis-3.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\sf\trove4j\trove4j\3.0.3\trove4j-3.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\com\machinezoo\noexception\noexception\1.3.2\noexception-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\mhshams\jnbis\2.0.1\jnbis-2.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\sanselan\sanselan\0.97-incubator\sanselan-0.97-incubator.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\idrepository\id-repository-core\1.2.0\id-repository-core-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biosdk-provider\1.2.0\kernel-biosdk-provider-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-biometrics-api\1.2.0\kernel-biometrics-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-demographics-api\1.2.0\kernel-demographics-api-1.2.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-webflux\2.0.2.RELEASE\spring-boot-starter-webflux-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-reactor-netty\2.0.2.RELEASE\spring-boot-starter-reactor-netty-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\ipc\reactor-netty\0.7.7.RELEASE\reactor-netty-0.7.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-handler-proxy\4.1.24.Final\netty-handler-proxy-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-codec-socks\4.1.24.Final\netty-codec-socks-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-epoll\4.1.24.Final\netty-transport-native-epoll-4.1.24.Final-linux-x86_64.jar;C:\Users\Sohan.Dey\.m2\repository\io\netty\netty-transport-native-unix-common\4.1.24.Final\netty-transport-native-unix-common-4.1.24.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-webflux\5.0.6.RELEASE\spring-webflux-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\projectreactor\reactor-core\3.1.7.RELEASE\reactor-core-3.1.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-multipart-parser\1.1.0\nio-multipart-parser-1.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\synchronoss\cloud\nio-stream-storage\1.1.3\nio-stream-storage-1.1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-ui\1.5.10\springdoc-openapi-ui-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-webmvc-core\1.5.10\springdoc-openapi-webmvc-core-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\org\springdoc\springdoc-openapi-common\1.5.10\springdoc-openapi-common-1.5.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-models\2.1.10\swagger-models-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-annotations\2.1.10\swagger-annotations-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-integration\2.1.10\swagger-integration-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\core\v3\swagger-core\2.1.10\swagger-core-2.1.10.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\xml\bind\jakarta.xml.bind-api\2.3.2\jakarta.xml.bind-api-2.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\jakarta\activation\jakarta.activation-api\1.2.1\jakarta.activation-api-1.2.1.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\dataformat\jackson-dataformat-yaml\2.12.1\jackson-dataformat-yaml-2.12.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\github\classgraph\classgraph\4.8.69\classgraph-4.8.69.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\swagger-ui\3.51.1\swagger-ui-3.51.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\webjars\webjars-locator-core\0.45\webjars-locator-core-0.45.jar;C:\Users\Sohan.Dey\.m2\repository\commons-fileupload\commons-fileupload\1.4\commons-fileupload-1.4.jar;C:\Users\Sohan.Dey\.m2\repository\org\mockito\mockito-core\2.23.4\mockito-core-2.23.4.jar;C:\Users\Sohan.Dey\.m2\repository\net\bytebuddy\byte-buddy-agent\1.9.3\byte-buddy-agent-1.9.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\objenesis\objenesis\2.6\objenesis-2.6.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-websubclient-api\1.2.0.1-SNAPSHOT\kernel-websubclient-api-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aspects\5.0.6.RELEASE\spring-aspects-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjweaver\1.8.13\aspectjweaver-1.8.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\owasp\encoder\encoder\1.2.3\encoder-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\tensorflow\1.12.0\tensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow\1.12.0\libtensorflow-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\tensorflow\libtensorflow_jni\1.12.0\libtensorflow_jni-1.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-templatemanager-velocity\1.2.0.1-SNAPSHOT\kernel-templatemanager-velocity-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\javax\servlet\javax.servlet-api\4.0.1\javax.servlet-api-4.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-config\2.0.0.RELEASE\spring-cloud-starter-config-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter\2.0.0.RELEASE\spring-cloud-starter-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-context\2.0.0.RELEASE\spring-cloud-context-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-crypto\5.0.6.RELEASE\spring-security-crypto-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-commons\2.0.0.RELEASE\spring-cloud-commons-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-rsa\1.0.5.RELEASE\spring-security-rsa-1.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-config-client\2.0.0.RELEASE\spring-cloud-config-client-2.0.0.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-jpeg2000\1.3.0\jai-imageio-jpeg2000-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\jai-imageio\jai-imageio-core\1.3.0\jai-imageio-core-1.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keymanager-service\1.2.0.1-SNAPSHOT\kernel-keymanager-service-1.2.0.1-SNAPSHOT-lib.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-test\5.0.5.RELEASE\spring-security-test-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-core\5.0.5.RELEASE\spring-security-core-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-pdfgenerator-itext\1.2.0.1-SNAPSHOT\kernel-pdfgenerator-itext-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\barcodes\7.1.0\barcodes-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\font-asian\7.1.0\font-asian-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\hyph\7.1.0\hyph-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\pdfa\7.1.0\pdfa-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\sign\7.1.0\sign-7.1.0.jar;C:\Users\Sohan.Dey\.m2\repository\com\itextpdf\tool\xmlworker\5.5.13\xmlworker-5.5.13.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcpkix-jdk15on\1.66\bcpkix-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-data-jpa\2.0.2.RELEASE\spring-boot-starter-data-jpa-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.0.2.RELEASE\spring-boot-starter-jdbc-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\zaxxer\HikariCP\2.7.9\HikariCP-2.7.9.jar;C:\Users\Sohan.Dey\.m2\repository\com\h2database\h2\1.4.197\h2-1.4.197.jar;C:\Users\Sohan.Dey\.m2\repository\com\github\microsoft\TSS.Java\0.3.0\TSS.Java-0.3.0.jar;C:\Users\Sohan.Dey\.m2\repository\net\java\dev\jna\jna\4.4.0\jna-4.4.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-api\2.4.1.Final\cache2k-api-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\org\cache2k\cache2k-core\2.4.1.Final\cache2k-core-2.4.1.Final.jar;C:\Users\Sohan.Dey\.m2\repository\com\nimbusds\nimbus-jose-jwt\9.14\nimbus-jose-jwt-9.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-core\1.2.0.1-SNAPSHOT\kernel-core-1.2.0.1-SNAPSHOT.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter\2.0.2.RELEASE\spring-boot-starter-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot\2.0.2.RELEASE\spring-boot-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.0.2.RELEASE\spring-boot-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.0.2.RELEASE\spring-boot-starter-logging-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.10.0\log4j-to-slf4j-2.10.0.jar;C:\Users\Sohan.Dey\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-jpa\2.0.7.RELEASE\spring-data-jpa-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\data\spring-data-commons\2.0.7.RELEASE\spring-data-commons-2.0.7.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-orm\5.0.6.RELEASE\spring-orm-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-jdbc\5.0.6.RELEASE\spring-jdbc-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-context\5.0.6.RELEASE\spring-context-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-aop\5.0.6.RELEASE\spring-aop-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-tx\5.0.6.RELEASE\spring-tx-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\spring-beans\5.0.6.RELEASE\spring-beans-5.0.6.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\aspectj\aspectjrt\1.8.12\aspectjrt-1.8.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-security\2.0.2.RELEASE\spring-boot-starter-security-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-config\5.0.5.RELEASE\spring-security-config-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\security\spring-security-web\5.0.5.RELEASE\spring-security-web-5.0.5.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\javax\transaction\javax.transaction-api\1.3\javax.transaction-api-1.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\commons\commons-math3\3.6.1\commons-math3-3.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\bouncycastle\bcprov-jdk15on\1.66\bcprov-jdk15on-1.66.jar;C:\Users\Sohan.Dey\.m2\repository\javax\interceptor\javax.interceptor-api\1.2\javax.interceptor-api-1.2.jar;C:\Users\Sohan.Dey\.m2\repository\com\google\code\findbugs\annotations\3.0.1\annotations-3.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\net\jcip\jcip-annotations\1.0\jcip-annotations-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\swagger\swagger-models\1.5.21\swagger-models-1.5.21.jar;C:\Users\Sohan.Dey\.m2\repository\com\auth0\java-jwt\3.8.1\java-jwt-3.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-core\1.4.2\micrometer-core-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\org\hdrhistogram\HdrHistogram\2.1.12\HdrHistogram-2.1.12.jar;C:\Users\Sohan.Dey\.m2\repository\org\latencyutils\LatencyUtils\2.0.3\LatencyUtils-2.0.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\micrometer\micrometer-registry-prometheus\1.4.2\micrometer-registry-prometheus-1.4.2.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient_common\0.8.1\simpleclient_common-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\prometheus\simpleclient\0.8.1\simpleclient-0.8.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jul-to-slf4j\1.7.25\jul-to-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\org\slf4j\jcl-over-slf4j\1.7.25\jcl-over-slf4j-1.7.25.jar;C:\Users\Sohan.Dey\.m2\repository\javax\activation\activation\1.1\activation-1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-starter-sleuth\2.0.4.RELEASE\spring-cloud-starter-sleuth-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.0.9.RELEASE\spring-boot-starter-aop-2.0.9.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\cloud\spring-cloud-sleuth-core\2.0.4.RELEASE\spring-cloud-sleuth-core-2.0.4.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave\5.6.1\brave-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\zipkin2\zipkin\2.12.0\zipkin-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\reporter2\zipkin-reporter\2.7.14\zipkin-reporter-2.7.14.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-context-log4j2\5.6.1\brave-context-log4j2-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-web\5.6.1\brave-instrumentation-spring-web-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-http\5.6.1\brave-instrumentation-http-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-rabbit\5.6.1\brave-instrumentation-spring-rabbit-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-kafka-clients\5.6.1\brave-instrumentation-kafka-clients-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpclient\5.6.1\brave-instrumentation-httpclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-httpasyncclient\5.6.1\brave-instrumentation-httpasyncclient-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-spring-webmvc\5.6.1\brave-instrumentation-spring-webmvc-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\io\zipkin\brave\brave-instrumentation-servlet\5.6.1\brave-instrumentation-servlet-5.6.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-starter-actuator\2.0.2.RELEASE\spring-boot-starter-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator-autoconfigure\2.0.2.RELEASE\spring-boot-actuator-autoconfigure-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\boot\spring-boot-actuator\2.0.2.RELEASE\spring-boot-actuator-2.0.2.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\8.5.55\tomcat-embed-core-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\tomcat\tomcat-annotations-api\8.5.55\tomcat-annotations-api-8.5.55.jar;C:\Users\Sohan.Dey\.m2\repository\org\springframework\retry\spring-retry\1.2.1.RELEASE\spring-retry-1.2.1.RELEASE.jar;C:\Users\Sohan.Dey\.m2\repository\com\fasterxml\jackson\module\jackson-module-afterburner\2.12.0\jackson-module-afterburner-2.12.0.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-registration-packet-manager\1.1.5.3\kernel-registration-packet-manager-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-keygenerator-bouncycastle\1.1.5.3\kernel-keygenerator-bouncycastle-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-cbeffutil-api\1.1.5.3\kernel-cbeffutil-api-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-logger-logback\1.1.5.3\kernel-logger-logback-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\net\logstash\logback\logstash-logback-encoder\6.4\logstash-logback-encoder-6.4.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\ch\qos\logback\logback-access\1.2.3\logback-access-1.2.3.jar;C:\Users\Sohan.Dey\.m2\repository\io\mosip\kernel\kernel-crypto-jce\1.1.5.3\kernel-crypto-jce-1.1.5.3.jar;C:\Users\Sohan.Dey\.m2\repository\org\bitbucket\b_c\jose4j\0.6.5\jose4j-0.6.5.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j-provider\1.1.2-incubating\wink-json4j-provider-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-common\1.1.2-incubating\wink-common-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\com\sun\xml\bind\jaxb-impl\2.2.1.1\jaxb-impl-2.2.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-annotation_1.1_spec\1.0\geronimo-annotation_1.1_spec-1.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\wink\wink-json4j\1.1.2-incubating\wink-json4j-1.1.2-incubating.jar;C:\Users\Sohan.Dey\.m2\repository\javax\ws\rs\jsr311-api\1.1.1\jsr311-api-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\checkerframework\checker-qual\2.9.0\checker-qual-2.9.0.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-broker\5.15.9\activemq-broker-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-client\5.15.9\activemq-client-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-jms_1.1_spec\1.1.1\geronimo-jms_1.1_spec-1.1.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\fusesource\hawtbuf\hawtbuf\1.11\hawtbuf-1.11.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\geronimo\specs\geronimo-j2ee-management_1.1_spec\1.0.1\geronimo-j2ee-management_1.1_spec-1.0.1.jar;C:\Users\Sohan.Dey\.m2\repository\org\apache\activemq\activemq-openwire-legacy\5.15.9\activemq-openwire-legacy-5.15.9.jar;C:\Users\Sohan.Dey\.m2\repository\org\projectlombok\lombok\1.18.8\lombok-1.18.8.jar \ No newline at end of file diff --git a/api-test/README.md b/api-test/README.md index a414add81..73b445d3c 100644 --- a/api-test/README.md +++ b/api-test/README.md @@ -166,6 +166,14 @@ To run the tests for both **Smoke** and **Regression**: 1. Ensure the correct environment and test level parameters are set. 2. Execute the tests as shown in the command above to validate mimoto API functionalities. +### Configuration maps + +Update the below **config maps** while running the automation in rancher or in mimoto.propertes file for local run: + +1. google.client.id +2. google.client.secret +3. google.refresh.token + --- ## License diff --git a/api-test/pom.xml b/api-test/pom.xml index 7d73160ed..2cf8a0d03 100644 --- a/api-test/pom.xml +++ b/api-test/pom.xml @@ -8,7 +8,7 @@ apitest-mimoto Parent project of apitest-mimoto https://github.com/mosip/mimoto - 0.15.0-SNAPSHOT + 0.18.0-SNAPSHOT @@ -32,6 +32,18 @@ https://github.com/mosip/mimoto + + + + ossrh-central + MavenCentralRepository + https://central.sonatype.com/repository/maven-snapshots/ + default + + true + + + UTF-8 @@ -49,14 +61,14 @@ 2.2.1 3.0.1 - apitest-mimoto-0.15.0-SNAPSHOT-jar-with-dependencies + apitest-mimoto-0.18.0-SNAPSHOT-jar-with-dependencies io.mosip.testrig.apitest.commons apitest-commons - 1.3.1 + 1.3.3 @@ -265,4 +277,4 @@ - \ No newline at end of file + diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testrunner/MosipTestRunner.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testrunner/MosipTestRunner.java index 55a7bc0fe..05b3ecc16 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testrunner/MosipTestRunner.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testrunner/MosipTestRunner.java @@ -26,6 +26,7 @@ import io.mosip.testrig.apirig.dataprovider.BiometricDataProvider; import io.mosip.testrig.apirig.dbaccess.DBManager; import io.mosip.testrig.apirig.mimoto.utils.MimotoConfigManager; +import io.mosip.testrig.apirig.mimoto.utils.MimotoUtil; import io.mosip.testrig.apirig.report.EmailableReport; import io.mosip.testrig.apirig.testrunner.BaseTestCase; import io.mosip.testrig.apirig.testrunner.ExtractResource; @@ -35,7 +36,9 @@ import io.mosip.testrig.apirig.utils.AuthTestsUtil; import io.mosip.testrig.apirig.utils.CertsUtil; import io.mosip.testrig.apirig.utils.GlobalConstants; +import io.mosip.testrig.apirig.utils.GlobalMethods; import io.mosip.testrig.apirig.utils.JWKKeyUtil; +import io.mosip.testrig.apirig.utils.KernelAuthentication; import io.mosip.testrig.apirig.utils.KeyCloakUserAndAPIKeyGeneration; import io.mosip.testrig.apirig.utils.KeycloakUserManager; import io.mosip.testrig.apirig.utils.MispPartnerAndLicenseKeyGeneration; @@ -65,12 +68,8 @@ public class MosipTestRunner { public static void main(String[] arg) { try { - - Map envMap = System.getenv(); - LOGGER.info("** ------------- Get ALL ENV varibales --------------------------------------------- **"); - for (String envName : envMap.keySet()) { - LOGGER.info(String.format("ENV %s = %s%n", envName, envMap.get(envName))); - } + LOGGER.info("** ------------- API Test Rig Run Started --------------------------------------------- **"); + BaseTestCase.setRunContext(getRunType(), jarUrl); ExtractResource.removeOldMosipTestTestResource(); if (getRunType().equalsIgnoreCase("JAR")) { @@ -82,15 +81,15 @@ public static void main(String[] arg) { MimotoConfigManager.init(); suiteSetup(getRunType()); SkipTestCaseHandler.loadTestcaseToBeSkippedList("testCaseSkippedList.txt"); + GlobalMethods.setModuleNameAndReCompilePattern(MimotoConfigManager.getproperty("moduleNamePattern")); setLogLevels(); + MimotoConfigManager.getEsignetBaseUrl(); - // For now we are not doing health check for qa-115. - if (BaseTestCase.isTargetEnvLTS()) { - HealthChecker healthcheck = new HealthChecker(); - healthcheck.setCurrentRunningModule(BaseTestCase.currentModule); - Thread trigger = new Thread(healthcheck); - trigger.start(); - } + HealthChecker healthcheck = new HealthChecker(); + healthcheck.setCurrentRunningModule(BaseTestCase.currentModule); + Thread trigger = new Thread(healthcheck); + trigger.start(); + KeycloakUserManager.removeUser(); KeycloakUserManager.createUsers(); KeycloakUserManager.closeKeycloakInstance(); @@ -109,11 +108,13 @@ public static void main(String[] arg) { } catch (Exception e) { LOGGER.error("Exception " + e.getMessage()); } + + KeycloakUserManager.removeUser(); + KeycloakUserManager.closeKeycloakInstance(); OTPListener.bTerminate = true; - if (BaseTestCase.isTargetEnvLTS()) - HealthChecker.bTerminate = true; + HealthChecker.bTerminate = true; System.exit(0); @@ -154,6 +155,12 @@ private static void setLogLevels() { MispPartnerAndLicenseKeyGeneration.setLogLevel(); JWKKeyUtil.setLogLevel(); CertsUtil.setLogLevel(); + KernelAuthentication.setLogLevel(); + BaseTestCase.setLogLevel(); + MimotoUtil.setLogLevel(); + KeycloakUserManager.setLogLevel(); + DBManager.setLogLevel(); + BiometricDataProvider.setLogLevel(); } /** diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/AddIdentity.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/AddIdentity.java index 5a5b28383..e0eed492b 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/AddIdentity.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/AddIdentity.java @@ -38,9 +38,10 @@ import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; import io.mosip.testrig.apirig.utils.RestClient; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class AddIdentity extends AdminTestUtil implements ITest { +public class AddIdentity extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(AddIdentity.class); protected String testCaseName = ""; public Response response = null; @@ -84,7 +85,7 @@ public Object[] getTestCaseList(ITestContext context) { * @throws AdminTestException */ @Test(dataProvider = "testcaselist") - public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException { + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); if (HealthChecker.signalTerminateExecution) { throw new SkipException( diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/DeleteWithParam.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/DeleteWithParam.java index fa83f24ae..ff91a401b 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/DeleteWithParam.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/DeleteWithParam.java @@ -31,9 +31,10 @@ import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class DeleteWithParam extends AdminTestUtil implements ITest { +public class DeleteWithParam extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(DeleteWithParam.class); protected String testCaseName = ""; public Response response = null; @@ -76,7 +77,7 @@ public Object[] getTestCaseList(ITestContext context) { * @throws AdminTestException */ @Test(dataProvider = "testcaselist") - public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException { + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); testCaseDTO = MimotoUtil.changeContextURLByFlag(testCaseDTO); @@ -126,8 +127,6 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad if (MimotoConfigManager.getSunbirdBaseURL() != null && !MimotoConfigManager.getSunbirdBaseURL().isBlank()) tempUrl = MimotoConfigManager.getSunbirdBaseURL(); - //Once sunbird registry is pointing to specific env, remove the above line and uncomment below line - //tempUrl = ApplnURI.replace(GlobalConstants.API_INTERNAL, MimotoConfigManager.getSunBirdBaseURL()); testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$SUNBIRDBASEURL$", "")); } diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParam.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParam.java index 7da3058da..ff5614a58 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParam.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParam.java @@ -33,9 +33,10 @@ import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class GetWithParam extends AdminTestUtil implements ITest { +public class GetWithParam extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(GetWithParam.class); protected String testCaseName = ""; public Response response = null; @@ -81,7 +82,7 @@ public Object[] getTestCaseList(ITestContext context) { * @throws AdminTestException */ @Test(dataProvider = "testcaselist") - public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException { + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); testCaseDTO = MimotoUtil.changeContextURLByFlag(testCaseDTO); @@ -136,6 +137,9 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad inputJson = MimotoUtil.inputstringKeyWordHandeler(inputJson, testCaseName); + String outputJson = getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()); + outputJson = MimotoUtil.inputstringKeyWordHandeler(outputJson, testCaseName); + if (testCaseName.contains("ESignet_")) { if (MimotoConfigManager.isInServiceNotDeployedList(GlobalConstants.ESIGNET)) { throw new SkipException("esignet is not deployed hence skipping the testcase"); @@ -148,13 +152,11 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad if (MimotoConfigManager.isInServiceNotDeployedList("sunbirdrc")) throw new SkipException(GlobalConstants.SERVICE_NOT_DEPLOYED_MESSAGE); - if (MimotoConfigManager.getSunbirdBaseURL() != null && !MimotoConfigManager.getSunbirdBaseURL().isBlank()) + if (MimotoConfigManager.getSunbirdBaseURL() != null + && !MimotoConfigManager.getSunbirdBaseURL().isBlank()) tempUrl = MimotoConfigManager.getSunbirdBaseURL(); - //Once sunbird registry is pointing to specific env, remove the above line and uncomment below line - // tempUrl = ApplnURI.replace(GlobalConstants.API_INTERNAL, - // MimotoConfigManager.getSunBirdBaseURL()); - testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$SUNBIRDBASEURL$", "")); - } + testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$SUNBIRDBASEURL$", "")); + } response = getWithPathParamAndCookie(tempUrl + testCaseDTO.getEndPoint(), inputJson, auditLogCheck, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), sendEsignetToken); @@ -173,8 +175,7 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad ouputValid = new HashMap<>(); ouputValid.put(GlobalConstants.EXPECTED_VS_ACTUAL, List.of(customResponse)); } else { - ouputValid = OutputValidationUtil.doJsonOutputValidation(response.asString(), - getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()), testCaseDTO, + ouputValid = OutputValidationUtil.doJsonOutputValidation(response.asString(), outputJson, testCaseDTO, response.getStatusCode()); } diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParamAndHeader.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParamAndHeader.java new file mode 100644 index 000000000..e0b6151f2 --- /dev/null +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/GetWithParamAndHeader.java @@ -0,0 +1,191 @@ +package io.mosip.testrig.apirig.mimoto.testscripts; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.json.JSONObject; +import org.testng.ITest; +import org.testng.ITestContext; +import org.testng.ITestResult; +import org.testng.Reporter; +import org.testng.SkipException; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.internal.BaseTestMethod; +import org.testng.internal.TestResult; + +import com.itextpdf.text.pdf.PdfReader; +import com.itextpdf.text.pdf.parser.PdfTextExtractor; + +import io.mosip.testrig.apirig.dto.OutputValidationDto; +import io.mosip.testrig.apirig.dto.TestCaseDTO; +import io.mosip.testrig.apirig.mimoto.utils.MimotoConfigManager; +import io.mosip.testrig.apirig.mimoto.utils.MimotoUtil; +import io.mosip.testrig.apirig.testrunner.HealthChecker; +import io.mosip.testrig.apirig.utils.AdminTestException; +import io.mosip.testrig.apirig.utils.AdminTestUtil; +import io.mosip.testrig.apirig.utils.AuthenticationTestException; +import io.mosip.testrig.apirig.utils.GlobalConstants; +import io.mosip.testrig.apirig.utils.GlobalMethods; +import io.mosip.testrig.apirig.utils.OutputValidationUtil; +import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; +import io.restassured.response.Response; + +public class GetWithParamAndHeader extends MimotoUtil implements ITest { + private static final Logger logger = Logger.getLogger(GetWithParamAndHeader.class); + protected String testCaseName = ""; + public Response response = null; + public String pathParams = null; + public String headers = null; + public byte[] pdf = null; + public String pdfAsText = null; + + @BeforeClass + public static void setLogLevel() { + if (MimotoConfigManager.IsDebugEnabled()) + logger.setLevel(Level.ALL); + else + logger.setLevel(Level.ERROR); + } + + /** + * get current testcaseName + */ + @Override + public String getTestName() { + return testCaseName; + } + + /** + * Data provider class provides test case list + * + * @return object of data provider + */ + @DataProvider(name = "testcaselist") + public Object[] getTestCaseList(ITestContext context) { + String ymlFile = context.getCurrentXmlTest().getLocalParameters().get("ymlFile"); + headers = context.getCurrentXmlTest().getLocalParameters().get("headers"); + pathParams = context.getCurrentXmlTest().getLocalParameters().get("pathParams"); + logger.info("Started executing yml: " + ymlFile); + return getYmlTestData(ymlFile); + } + + /** + * Test method for OTP Generation execution + * + * @param objTestParameters + * @param testScenario + * @param testcaseName + * @throws AuthenticationTestException + * @throws AdminTestException + */ + @Test(dataProvider = "testcaselist") + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { + testCaseName = testCaseDTO.getTestCaseName(); + testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); + testCaseDTO = MimotoUtil.changeContextURLByFlag(testCaseDTO); + if (HealthChecker.signalTerminateExecution) { + throw new SkipException( + GlobalConstants.TARGET_ENV_HEALTH_CHECK_FAILED + HealthChecker.healthCheckFailureMapS); + } + + String[] templateFields = testCaseDTO.getTemplateFields(); + + if (testCaseDTO.getTemplateFields() != null && templateFields.length > 0) { + ArrayList inputtestCases = AdminTestUtil.getInputTestCase(testCaseDTO); + ArrayList outputtestcase = AdminTestUtil.getOutputTestCase(testCaseDTO); + for (int i = 0; i < languageList.size(); i++) { + response = getWithPathParamsBodyHeadersAndCookie(ApplnURI + testCaseDTO.getEndPoint(), + getJsonFromTemplate(inputtestCases.get(i).toString(), testCaseDTO.getInputTemplate()), + COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), pathParams, headers); + + Map> ouputValid = OutputValidationUtil.doJsonOutputValidation( + response.asString(), + getJsonFromTemplate(outputtestcase.get(i).toString(), testCaseDTO.getOutputTemplate()), + testCaseDTO, response.getStatusCode()); + Reporter.log(ReportUtil.getOutputValidationReport(ouputValid)); + + if (!OutputValidationUtil.publishOutputResult(ouputValid)) + throw new AdminTestException("Failed at output validation"); + } + } + + else { + String inputJson = getJsonFromTemplate(testCaseDTO.getInput(), testCaseDTO.getInputTemplate()); + + inputJson = MimotoUtil.inputstringKeyWordHandeler(inputJson, testCaseName); + + String outputJson = getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()); + outputJson = MimotoUtil.inputstringKeyWordHandeler(outputJson, testCaseName); + + response = getWithPathParamsBodyHeadersAndCookie(ApplnURI + testCaseDTO.getEndPoint(), inputJson, + COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), pathParams, headers); + + String contentType = response.getHeader("Content-Type"); + if (contentType != null && contentType.contains("application/pdf")) { + pdf = response.asByteArray(); + + PdfReader pdfReader = null; + ByteArrayInputStream bIS = null; + + try { + bIS = new ByteArrayInputStream(pdf); + pdfReader = new PdfReader(bIS); + pdfAsText = PdfTextExtractor.getTextFromPage(pdfReader, 1); + } catch (IOException e) { + Reporter.log("Exception : " + e.getMessage()); + } finally { + AdminTestUtil.closeByteArrayInputStream(bIS); + AdminTestUtil.closePdfReader(pdfReader); + } + + if (pdf != null && (new String(pdf).contains("errors") || pdfAsText == null)) { + GlobalMethods.reportResponse(null, ApplnURI + testCaseDTO.getEndPoint(), + "Not able to download issuer credential"); + throw new AdminTestException("Not able to download issuer credential"); + } else { + GlobalMethods.reportResponse(null, ApplnURI + testCaseDTO.getEndPoint(), pdfAsText, true); + } + + } else { + Map> ouputValid = null; + ouputValid = OutputValidationUtil.doJsonOutputValidation(response.asString(), outputJson, testCaseDTO, + response.getStatusCode()); + + Reporter.log(ReportUtil.getOutputValidationReport(ouputValid)); + if (!OutputValidationUtil.publishOutputResult(ouputValid)) + throw new AdminTestException("Failed at output validation"); + } + + } + } + + /** + * The method ser current test name to result + * + * @param result + */ + @AfterMethod(alwaysRun = true) + public void setResultTestName(ITestResult result) { + try { + Field method = TestResult.class.getDeclaredField("m_method"); + method.setAccessible(true); + method.set(result, result.getMethod().clone()); + BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod(); + Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName"); + f.setAccessible(true); + f.set(baseTestMethod, testCaseName); + } catch (Exception e) { + Reporter.log("Exception : " + e.getMessage()); + } + } +} diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithAutogenIdWithOtpGenerate.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithAutogenIdWithOtpGenerate.java index 0a8180ebf..d7c7914f4 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithAutogenIdWithOtpGenerate.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithAutogenIdWithOtpGenerate.java @@ -32,9 +32,10 @@ import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class PostWithAutogenIdWithOtpGenerate extends AdminTestUtil implements ITest { +public class PostWithAutogenIdWithOtpGenerate extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(PostWithAutogenIdWithOtpGenerate.class); protected String testCaseName = ""; public String idKeyName = null; @@ -83,7 +84,7 @@ public Object[] getTestCaseList(ITestContext context) { */ @Test(dataProvider = "testcaselist") public void test(TestCaseDTO testCaseDTO) - throws AuthenticationTestException, AdminTestException, NumberFormatException, InterruptedException { + throws AuthenticationTestException, AdminTestException, NumberFormatException, InterruptedException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); if (HealthChecker.signalTerminateExecution) { throw new SkipException( @@ -138,8 +139,6 @@ public void test(TestCaseDTO testCaseDTO) + " as UIN not available in database"); try { Thread.sleep(Long.parseLong(properties.getProperty("uinGenDelayTime"))); -// SlackChannelIntegration.sendMessageToSlack("UIN not available in database in :" + ApplnURI + "Env") ; - } catch (NumberFormatException | InterruptedException e) { logger.error(e.getMessage()); Thread.currentThread().interrupt(); @@ -169,7 +168,6 @@ public void test(TestCaseDTO testCaseDTO) if (!OutputValidationUtil.publishOutputResult(ouputValidOtp)) { if (otpResponse.asString().contains("IDA-OTA-001")) { -// SlackChannelIntegration.sendMessageToSlack("Exceeded number of OTP requests in a given time, :" + ApplnURI + "Env") ; throw new AdminTestException( "Exceeded number of OTP requests in a given time, Increase otp.request.flooding.max-count"); } @@ -190,8 +188,6 @@ public void test(TestCaseDTO testCaseDTO) throw new SkipException("esignet is not deployed hence skipping the testcase"); } String tempUrl = MimotoConfigManager.getEsignetBaseUrl(); -// String inputJson = getJsonFromTemplate(testCaseDTO.getInput(), testCaseDTO.getInputTemplate()); -// inputJson = inputJsonKeyWordHandeler(inputJson, testCaseName); inputJson = MimotoUtil.getOTPFromSMTP(inputJson, testCaseDTO); response = postRequestWithCookieAuthHeaderAndXsrfTokenForAutoGenId(tempUrl + testCaseDTO.getEndPoint(), inputJson, COOKIENAME, testCaseDTO.getTestCaseName(), idKeyName); @@ -236,9 +232,6 @@ public void waittime() { && MimotoUtil.isOTPEnabled().equals("true")) { try { long delayTime = Long.parseLong(properties.getProperty("Delaytime")); - if (!BaseTestCase.isTargetEnvLTS()) - delayTime = Long.parseLong(properties.getProperty("uinGenDelayTime")) - * Long.parseLong(properties.getProperty("uinGenMaxLoopCount")); logger.info("waiting for " + delayTime + " mili secs after VID Generation In RESIDENT SERVICES"); Thread.sleep(delayTime); } catch (Exception e) { diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithBodyWithOtpGenerate.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithBodyWithOtpGenerate.java index 48178be33..e480e4c19 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithBodyWithOtpGenerate.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithBodyWithOtpGenerate.java @@ -31,9 +31,10 @@ import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class PostWithBodyWithOtpGenerate extends AdminTestUtil implements ITest { +public class PostWithBodyWithOtpGenerate extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(PostWithBodyWithOtpGenerate.class); protected String testCaseName = ""; public Response response = null; @@ -79,7 +80,7 @@ public Object[] getTestCaseList(ITestContext context) { * @throws AdminTestException */ @Test(dataProvider = "testcaselist") - public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException { + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); if (HealthChecker.signalTerminateExecution) { throw new SkipException( diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithFormDataBodyForPdfDownload.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithFormDataBodyForPdfDownload.java index 337c11b1e..680ed9424 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithFormDataBodyForPdfDownload.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithFormDataBodyForPdfDownload.java @@ -31,9 +31,10 @@ import io.mosip.testrig.apirig.utils.AuthenticationTestException; import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.GlobalMethods; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class PostWithFormDataBodyForPdfDownload extends AdminTestUtil implements ITest { +public class PostWithFormDataBodyForPdfDownload extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(PostWithFormDataBodyForPdfDownload.class); protected String testCaseName = ""; public Response response = null; @@ -79,7 +80,7 @@ public Object[] getTestCaseList(ITestContext context) { * @throws AdminTestException */ @Test(dataProvider = "testcaselist") - public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException { + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); testCaseDTO = MimotoUtil.changeContextURLByFlag(testCaseDTO); diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithPathParamsAndCookie.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithPathParamsAndCookie.java new file mode 100644 index 000000000..924436c66 --- /dev/null +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithPathParamsAndCookie.java @@ -0,0 +1,124 @@ +package io.mosip.testrig.apirig.mimoto.testscripts; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.testng.ITest; +import org.testng.ITestContext; +import org.testng.ITestResult; +import org.testng.Reporter; +import org.testng.SkipException; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.internal.BaseTestMethod; +import org.testng.internal.TestResult; + +import io.mosip.testrig.apirig.dto.OutputValidationDto; +import io.mosip.testrig.apirig.dto.TestCaseDTO; +import io.mosip.testrig.apirig.mimoto.utils.MimotoConfigManager; +import io.mosip.testrig.apirig.mimoto.utils.MimotoUtil; +import io.mosip.testrig.apirig.testrunner.HealthChecker; +import io.mosip.testrig.apirig.utils.AdminTestException; +import io.mosip.testrig.apirig.utils.AuthenticationTestException; +import io.mosip.testrig.apirig.utils.GlobalConstants; +import io.mosip.testrig.apirig.utils.OutputValidationUtil; +import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; +import io.restassured.response.Response; + +public class PostWithPathParamsAndCookie extends MimotoUtil implements ITest { + private static final Logger logger = Logger.getLogger(PostWithPathParamsAndCookie.class); + protected String testCaseName = ""; + public String pathParams = null; + + @BeforeClass + public static void setLogLevel() { + if (MimotoConfigManager.IsDebugEnabled()) + logger.setLevel(Level.ALL); + else + logger.setLevel(Level.ERROR); + } + + /** + * get current testcaseName + */ + @Override + public String getTestName() { + return testCaseName; + } + + /** + * Data provider class provides test case list + * + * @return object of data provider + */ + @DataProvider(name = "testcaselist") + public Object[] getTestCaseList(ITestContext context) { + String ymlFile = context.getCurrentXmlTest().getLocalParameters().get("ymlFile"); + pathParams = context.getCurrentXmlTest().getLocalParameters().get("pathParams"); + logger.info("Started executing yml: " + ymlFile); + return getYmlTestData(ymlFile); + } + + /** + * Test method for OTP Generation execution + * + * @param objTestParameters + * @param testScenario + * @param testcaseName + * @throws AuthenticationTestException + * @throws AdminTestException + */ + @Test(dataProvider = "testcaselist") + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { + testCaseName = testCaseDTO.getTestCaseName(); + testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); + + if (HealthChecker.signalTerminateExecution) { + throw new SkipException( + GlobalConstants.TARGET_ENV_HEALTH_CHECK_FAILED + HealthChecker.healthCheckFailureMapS); + } + + + String inputJson = getJsonFromTemplate(testCaseDTO.getInput(), testCaseDTO.getInputTemplate()); + inputJson = MimotoUtil.inputstringKeyWordHandeler(inputJson, testCaseName); + + Response response = postWithPathParamsBodyAndCookie(ApplnURI + testCaseDTO.getEndPoint(), inputJson, COOKIENAME, + testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), pathParams); + + Map> ouputValid = OutputValidationUtil.doJsonOutputValidation( + response.asString(), getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()), + testCaseDTO, response.getStatusCode()); + Reporter.log(ReportUtil.getOutputValidationReport(ouputValid)); + + if (!OutputValidationUtil.publishOutputResult(ouputValid)) + throw new AdminTestException("Failed at output validation"); + + } + + /** + * The method ser current test name to result + * + * @param result + */ + @AfterMethod(alwaysRun = true) + public void setResultTestName(ITestResult result) { + try { + Field method = TestResult.class.getDeclaredField("m_method"); + method.setAccessible(true); + method.set(result, result.getMethod().clone()); + BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod(); + Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName"); + f.setAccessible(true); + f.set(baseTestMethod, testCaseName); + } catch (Exception e) { + Reporter.log("Exception : " + e.getMessage()); + } + } + +} diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithPathParamsHeadersAndCookieForAutoGenId.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithPathParamsHeadersAndCookieForAutoGenId.java new file mode 100644 index 000000000..3c72bfa67 --- /dev/null +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/PostWithPathParamsHeadersAndCookieForAutoGenId.java @@ -0,0 +1,128 @@ +package io.mosip.testrig.apirig.mimoto.testscripts; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.testng.ITest; +import org.testng.ITestContext; +import org.testng.ITestResult; +import org.testng.Reporter; +import org.testng.SkipException; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.testng.internal.BaseTestMethod; +import org.testng.internal.TestResult; + +import io.mosip.testrig.apirig.dto.OutputValidationDto; +import io.mosip.testrig.apirig.dto.TestCaseDTO; +import io.mosip.testrig.apirig.mimoto.utils.MimotoConfigManager; +import io.mosip.testrig.apirig.mimoto.utils.MimotoUtil; +import io.mosip.testrig.apirig.testrunner.HealthChecker; +import io.mosip.testrig.apirig.utils.AdminTestException; +import io.mosip.testrig.apirig.utils.AuthenticationTestException; +import io.mosip.testrig.apirig.utils.GlobalConstants; +import io.mosip.testrig.apirig.utils.OutputValidationUtil; +import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; +import io.restassured.response.Response; + +public class PostWithPathParamsHeadersAndCookieForAutoGenId extends MimotoUtil implements ITest { + private static final Logger logger = Logger.getLogger(PostWithPathParamsHeadersAndCookieForAutoGenId.class); + protected String testCaseName = ""; + public String pathParams = null; + public String headers = null; + public String idKeyName = null; + + @BeforeClass + public static void setLogLevel() { + if (MimotoConfigManager.IsDebugEnabled()) + logger.setLevel(Level.ALL); + else + logger.setLevel(Level.ERROR); + } + + /** + * get current testcaseName + */ + @Override + public String getTestName() { + return testCaseName; + } + + /** + * Data provider class provides test case list + * + * @return object of data provider + */ + @DataProvider(name = "testcaselist") + public Object[] getTestCaseList(ITestContext context) { + String ymlFile = context.getCurrentXmlTest().getLocalParameters().get("ymlFile"); + pathParams = context.getCurrentXmlTest().getLocalParameters().get("pathParams"); + idKeyName = context.getCurrentXmlTest().getLocalParameters().get("idKeyName"); + headers = context.getCurrentXmlTest().getLocalParameters().get("headers"); + logger.info("Started executing yml: " + ymlFile); + return getYmlTestData(ymlFile); + } + + /** + * Test method for OTP Generation execution + * + * @param objTestParameters + * @param testScenario + * @param testcaseName + * @throws AuthenticationTestException + * @throws AdminTestException + */ + @Test(dataProvider = "testcaselist") + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { + testCaseName = testCaseDTO.getTestCaseName(); + testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); + + if (HealthChecker.signalTerminateExecution) { + throw new SkipException( + GlobalConstants.TARGET_ENV_HEALTH_CHECK_FAILED + HealthChecker.healthCheckFailureMapS); + } + + + String inputJson = getJsonFromTemplate(testCaseDTO.getInput(), testCaseDTO.getInputTemplate()); + inputJson = MimotoUtil.inputstringKeyWordHandeler(inputJson, testCaseName); + + Response response = postWithPathParamsBodyHeadersAndCookieForAutoGeneratedId(ApplnURI + testCaseDTO.getEndPoint(), inputJson, COOKIENAME, + testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), pathParams, idKeyName, headers); + + Map> ouputValid = OutputValidationUtil.doJsonOutputValidation( + response.asString(), getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()), + testCaseDTO, response.getStatusCode()); + Reporter.log(ReportUtil.getOutputValidationReport(ouputValid)); + + if (!OutputValidationUtil.publishOutputResult(ouputValid)) + throw new AdminTestException("Failed at output validation"); + + } + + /** + * The method ser current test name to result + * + * @param result + */ + @AfterMethod(alwaysRun = true) + public void setResultTestName(ITestResult result) { + try { + Field method = TestResult.class.getDeclaredField("m_method"); + method.setAccessible(true); + method.set(result, result.getMethod().clone()); + BaseTestMethod baseTestMethod = (BaseTestMethod) result.getMethod(); + Field f = baseTestMethod.getClass().getSuperclass().getDeclaredField("m_methodName"); + f.setAccessible(true); + f.set(baseTestMethod, testCaseName); + } catch (Exception e) { + Reporter.log("Exception : " + e.getMessage()); + } + } + +} diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePost.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePost.java index 73a98782c..a773a1103 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePost.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePost.java @@ -33,9 +33,10 @@ import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class SimplePost extends AdminTestUtil implements ITest { +public class SimplePost extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(SimplePost.class); protected String testCaseName = ""; public Response response = null; @@ -81,7 +82,7 @@ public Object[] getTestCaseList(ITestContext context) { * @throws AdminTestException */ @Test(dataProvider = "testcaselist") - public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException { + public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, AdminTestException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); testCaseDTO = MimotoUtil.isTestCaseValidForTheExecution(testCaseDTO); testCaseDTO = MimotoUtil.changeContextURLByFlag(testCaseDTO); @@ -136,8 +137,6 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad if (MimotoConfigManager.getSunbirdBaseURL() != null && !MimotoConfigManager.getSunbirdBaseURL().isBlank()) tempUrl = MimotoConfigManager.getSunbirdBaseURL(); - //Once sunbird registry is pointing to specific env, remove the above line and uncomment below line - //tempUrl = ApplnURI.replace(GlobalConstants.API_INTERNAL, MimotoConfigManager.getSunBirdBaseURL()); testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$SUNBIRDBASEURL$", "")); response = postWithBodyAndCookie(tempUrl + testCaseDTO.getEndPoint(), inputJson, auditLogCheck, @@ -148,9 +147,9 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad if (MimotoConfigManager.isInServiceNotDeployedList("sunbirdrc")) throw new SkipException(GlobalConstants.SERVICE_NOT_DEPLOYED_MESSAGE); - if (MimotoConfigManager.getproperty("esignetSunBirdBaseURL") != null - && !MimotoConfigManager.getproperty("esignetSunBirdBaseURL").isBlank()) - tempUrl = MimotoConfigManager.getproperty("esignetSunBirdBaseURL"); + String esignetSunBirdBaseURL = MimotoConfigManager.getEsignetSunBirdBaseURL(); + if (esignetSunBirdBaseURL != null && !esignetSunBirdBaseURL.isBlank()) + tempUrl = esignetSunBirdBaseURL; testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$ESIGNETMOCKBASEURL$", "")); response = postRequestWithCookieAuthHeaderAndXsrfToken(tempUrl + testCaseDTO.getEndPoint(), diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePostForAutoGenId.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePostForAutoGenId.java index 18b6d734f..ff93957fe 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePostForAutoGenId.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/testscripts/SimplePostForAutoGenId.java @@ -34,9 +34,10 @@ import io.mosip.testrig.apirig.utils.GlobalConstants; import io.mosip.testrig.apirig.utils.OutputValidationUtil; import io.mosip.testrig.apirig.utils.ReportUtil; +import io.mosip.testrig.apirig.utils.SecurityXSSException; import io.restassured.response.Response; -public class SimplePostForAutoGenId extends AdminTestUtil implements ITest { +public class SimplePostForAutoGenId extends MimotoUtil implements ITest { private static final Logger logger = Logger.getLogger(SimplePostForAutoGenId.class); protected String testCaseName = ""; public String idKeyName = null; @@ -86,7 +87,7 @@ public Object[] getTestCaseList(ITestContext context) { */ @Test(dataProvider = "testcaselist") public void test(TestCaseDTO testCaseDTO) - throws AuthenticationTestException, AdminTestException, NoSuchAlgorithmException { + throws AuthenticationTestException, AdminTestException, NoSuchAlgorithmException, SecurityXSSException { testCaseName = testCaseDTO.getTestCaseName(); if (HealthChecker.signalTerminateExecution) { throw new SkipException( @@ -107,7 +108,6 @@ public void test(TestCaseDTO testCaseDTO) inputJson = getJsonFromTemplate(testCaseDTO.getInput(), testCaseDTO.getInputTemplate()); inputJson = MimotoUtil.inputstringKeyWordHandeler(inputJson, testCaseName); - String outputJson = getJsonFromTemplate(testCaseDTO.getOutput(), testCaseDTO.getOutputTemplate()); if (testCaseDTO.getTemplateFields() != null && templateFields.length > 0) { ArrayList inputtestCases = AdminTestUtil.getInputTestCase(testCaseDTO); @@ -141,9 +141,9 @@ public void test(TestCaseDTO testCaseDTO) if (MimotoConfigManager.isInServiceNotDeployedList("sunbirdrc")) throw new SkipException(GlobalConstants.SERVICE_NOT_DEPLOYED_MESSAGE); - if (MimotoConfigManager.getproperty("esignetSunBirdBaseURL") != null - && !MimotoConfigManager.getproperty("esignetSunBirdBaseURL").isBlank()) - tempUrl = MimotoConfigManager.getproperty("esignetSunBirdBaseURL"); + String esignetSunBirdBaseURL = MimotoConfigManager.getEsignetSunBirdBaseURL(); + if (esignetSunBirdBaseURL != null && !esignetSunBirdBaseURL.isBlank()) + tempUrl = esignetSunBirdBaseURL; testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$ESIGNETMOCKBASEURL$", "")); } else if (testCaseDTO.getEndPoint().startsWith("$SUNBIRDBASEURL$") && testCaseName.contains("SunBirdR")) { @@ -152,22 +152,15 @@ public void test(TestCaseDTO testCaseDTO) if (MimotoConfigManager.getSunbirdBaseURL() != null && !MimotoConfigManager.getSunbirdBaseURL().isBlank()) tempUrl = MimotoConfigManager.getSunbirdBaseURL(); - //Once sunbird registry is pointing to specific env, remove the above line and uncomment below line - //tempUrl = ApplnURI.replace(GlobalConstants.API_INTERNAL, MimotoConfigManager.getSunBirdBaseURL()); testCaseDTO.setEndPoint(testCaseDTO.getEndPoint().replace("$SUNBIRDBASEURL$", "")); } - if (inputJson.contains("$GETCLIENTIDFROMMIMOTOACTUATOR$")) { - String clientIdSection = MimotoUtil.getClientIdSection(tempUrl); - inputJson = replaceKeywordWithValue(inputJson, "$GETCLIENTIDFROMMIMOTOACTUATOR$", - getValueFromMimotoActuator("overrides", clientIdSection)); - } if (testCaseName.contains("_AuthorizationCode_")) { response = postRequestWithCookieAuthHeaderAndXsrfTokenForAutoGenId( tempUrl + testCaseDTO.getEndPoint(), inputJson, COOKIENAME, testCaseDTO.getTestCaseName(), idKeyName); - } else { - //Adding the while loop for try creating sunbird policy in registry for 3 times if gets failed + } else if (testCaseName.contains("SunBirdR_CreatePolicy_")) { + // Adding the while loop for try creating sunbird policy in registry for 3 times if gets failed int currLoopCount = 0; do { response = postWithBodyAndBearerTokenForAutoGeneratedId(tempUrl + testCaseDTO.getEndPoint(), @@ -178,8 +171,15 @@ public void test(TestCaseDTO testCaseDTO) } currLoopCount++; } while (currLoopCount < 10); + } else { + response = postWithBodyAndBearerTokenForAutoGeneratedId(tempUrl + testCaseDTO.getEndPoint(), + inputJson, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName); } - } else { + } else if (testCaseName.contains("GoogleLoginToken_")) { + response = postWithBodyAndBearerTokenForAutoGeneratedId(ApplnURI + testCaseDTO.getEndPoint(), + inputJson, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName); + } + else { response = postWithBodyAndCookieForAutoGeneratedId(ApplnURI + testCaseDTO.getEndPoint(), inputJson, auditLogCheck, COOKIENAME, testCaseDTO.getRole(), testCaseDTO.getTestCaseName(), idKeyName, sendEsignetToken); diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConfigManager.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConfigManager.java index 3050fe84c..d46ed1418 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConfigManager.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConfigManager.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.Properties; +import org.apache.log4j.Level; import org.apache.log4j.Logger; import io.mosip.testrig.apirig.mimoto.testrunner.MosipTestRunner; @@ -13,6 +14,9 @@ public class MimotoConfigManager extends ConfigManager{ private static final Logger LOGGER = Logger.getLogger(MimotoConfigManager.class); public static void init() { + Logger configManagerLogger = Logger.getLogger(ConfigManager.class); + configManagerLogger.setLevel(Level.WARN); + Map moduleSpecificPropertiesMap = new HashMap<>(); // Load scope specific properties try { @@ -20,8 +24,7 @@ public static void init() { Properties props = getproperties(path); // Convert Properties to Map and add to moduleSpecificPropertiesMap for (String key : props.stringPropertyNames()) { - String value = System.getenv(key) == null ? props.getProperty(key) : System.getenv(key); - moduleSpecificPropertiesMap.put(key, value); + moduleSpecificPropertiesMap.put(key, props.getProperty(key)); } } catch (Exception e) { LOGGER.error(e.getMessage()); @@ -33,5 +36,22 @@ public static void init() { public static String getSunbirdBaseURL() { return MimotoUtil.getValueFromMimotoActuator("overrides", "mosip.sunbird.url"); } + + public static String getEsignetBaseUrl() { + String esignetBaseUrl = null; + if (getproperty("runPlugin").equals("mosipid")) { + esignetBaseUrl = "https://" + MimotoUtil.getValueFromMimotoActuator("overrides", getproperty("mosipid-identity-esignet-host")); + } else if (getproperty("runPlugin").equals("mockid")) { + esignetBaseUrl = "https://" + MimotoUtil.getValueFromMimotoActuator("overrides", getproperty("mock-identity-esignet-host")); + } + if(esignetBaseUrl != null) { + propertiesMap.put("eSignetbaseurl", esignetBaseUrl); + } + return esignetBaseUrl; + } + + public static String getEsignetSunBirdBaseURL() { + return "https://" + MimotoUtil.getValueFromMimotoActuator("overrides", getproperty("sunbirdrc-insurance-esignet-host")); + } } diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConstants.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConstants.java new file mode 100644 index 000000000..8e004b25b --- /dev/null +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoConstants.java @@ -0,0 +1,9 @@ +package io.mosip.testrig.apirig.mimoto.utils; + +public class MimotoConstants { + + public static final String SUNBIRD_INSURANCE_AUTH_FACTOR_TYPE = "KBI"; + + public static final String SUNBIRD_INSURANCE_AUTH_FACTOR_TYPE_STRING = "sunbirdInsuranceAuthFactorType"; + +} \ No newline at end of file diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoUtil.java b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoUtil.java index 19661928f..72bfc2e70 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoUtil.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/mimoto/utils/MimotoUtil.java @@ -4,25 +4,51 @@ import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PublicKey; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Calendar; import java.util.HashMap; +import java.util.Map; +import javax.ws.rs.core.MediaType; + +import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; +import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import org.testng.SkipException; +import com.github.javafaker.Faker; + import io.mosip.testrig.apirig.dataprovider.BiometricDataProvider; import io.mosip.testrig.apirig.dto.TestCaseDTO; import io.mosip.testrig.apirig.mimoto.testrunner.MosipTestRunner; import io.mosip.testrig.apirig.testrunner.OTPListener; import io.mosip.testrig.apirig.utils.AdminTestUtil; +import io.mosip.testrig.apirig.utils.ConfigManager; import io.mosip.testrig.apirig.utils.GlobalConstants; +import io.mosip.testrig.apirig.utils.GlobalMethods; +import io.mosip.testrig.apirig.utils.RestClient; import io.mosip.testrig.apirig.utils.SkipTestCaseHandler; +import io.restassured.response.Response; public class MimotoUtil extends AdminTestUtil { private static final Logger logger = Logger.getLogger(MimotoUtil.class); private static String otpEnabled = "true"; + private static Faker faker = new Faker(); + private static String fullNameForSunBirdR = generateFullNameForSunBirdR(); + private static String dobForSunBirdR = generateDobForSunBirdR(); + private static String policyNumberForSunBirdR = generateRandomNumberString(9); + + public static void setLogLevel() { + if (MimotoConfigManager.IsDebugEnabled()) + logger.setLevel(Level.ALL); + else + logger.setLevel(Level.ERROR); + } public static String isOTPEnabled() { String value = getValueFromMimotoActuator("/mimoto-default.properties", "mosip.otp.download.enable").isBlank() @@ -60,6 +86,14 @@ public static TestCaseDTO isTestCaseValidForTheExecution(TestCaseDTO testCaseDTO String endpoint = testCaseDTO.getEndPoint(); String inputJson = testCaseDTO.getInput(); + //When the captcha is enabled we cannot execute the test case as we can not generate the captcha token + if (isCaptchaEnabled() == true) { + GlobalMethods.reportCaptchaStatus(GlobalConstants.CAPTCHA_ENABLED, true); + throw new SkipException(GlobalConstants.CAPTCHA_ENABLED_MESSAGE); + }else { + GlobalMethods.reportCaptchaStatus(GlobalConstants.CAPTCHA_ENABLED, false); + } + if (MosipTestRunner.skipAll == true) { throw new SkipException(GlobalConstants.PRE_REQUISITE_FAILED_MESSAGE); } @@ -111,8 +145,10 @@ public static String getOTPFromSMTP(String inputJson, TestCaseDTO testCaseDTO) { emailId = request.getJSONObject(GlobalConstants.REQUEST) .getJSONArray(GlobalConstants.CHALLENGELIST).getJSONObject(0) .getString(GlobalConstants.CHALLENGE); - if (emailId.endsWith(GlobalConstants.OTP_AS_PHONE)) + if (emailId.endsWith(GlobalConstants.OTP_AS_PHONE)) { emailId = emailId.replace(GlobalConstants.OTP_AS_PHONE, ""); + emailId = removeLeadingPlusSigns(emailId); + } logger.info(emailId); otp = OTPListener.getOtp(emailId); request.getJSONObject(GlobalConstants.REQUEST).getJSONArray(GlobalConstants.CHALLENGELIST) @@ -128,45 +164,52 @@ public static String getOTPFromSMTP(String inputJson, TestCaseDTO testCaseDTO) { return inputJson; } - public static String getClientIdSection(String baseURL) { - if (baseURL.contains("esignet-mosipid")) { - return MimotoConfigManager.getproperty("mimoto-oidc-mosipid-partner-clientid"); - } - if (baseURL.contains("esignet-insurance")) { - return MimotoConfigManager.getproperty("mimoto-oidc-sunbird-partner-clientid"); - } - - return MimotoConfigManager.getproperty("mimoto-oidc-partner-clientid"); - } - public static String inputstringKeyWordHandeler(String jsonString, String testCaseName) { if (jsonString.contains("$ID:")) { - String autoGenIdFileName = mimotoAutoGeneratedIdPropFileName; - jsonString = replaceIdWithAutogeneratedId(jsonString, "$ID:", autoGenIdFileName); + jsonString = replaceIdWithAutogeneratedId(jsonString, "$ID:"); } if (jsonString.contains(GlobalConstants.TIMESTAMP)) { jsonString = replaceKeywordValue(jsonString, GlobalConstants.TIMESTAMP, generateCurrentUTCTimeStamp()); } + if (jsonString.contains("$UNIQUENONCEVALUEFORESIGNET$")) { + jsonString = replaceKeywordValue(jsonString, "$UNIQUENONCEVALUEFORESIGNET$", + String.valueOf(Calendar.getInstance().getTimeInMillis())); + } + + if (jsonString.contains("$SUNBIRDINSURANCEAUTHFACTORTYPE$")) { + String authFactorType = MimotoConfigManager + .getproperty(MimotoConstants.SUNBIRD_INSURANCE_AUTH_FACTOR_TYPE_STRING); + + String valueToReplace = (authFactorType != null && !authFactorType.isBlank()) ? authFactorType + : MimotoConstants.SUNBIRD_INSURANCE_AUTH_FACTOR_TYPE; + + jsonString = replaceKeywordValue(jsonString, "$SUNBIRDINSURANCEAUTHFACTORTYPE$", valueToReplace); + + } + + if (jsonString.contains("$GOOGLE_IDT_TOKEN$")) { + jsonString = replaceKeywordValue(jsonString, "$GOOGLE_IDT_TOKEN$", getGoogleIdToken()); + } + if (jsonString.contains("$POLICYNUMBERFORSUNBIRDRC$")) { - jsonString = replaceKeywordValue(jsonString, "$POLICYNUMBERFORSUNBIRDRC$", - properties.getProperty("policyNumberForSunBirdRC")); + jsonString = replaceKeywordValue(jsonString, "$POLICYNUMBERFORSUNBIRDRC$", policyNumberForSunBirdR); } if (jsonString.contains("$FULLNAMEFORSUNBIRDRC$")) { - jsonString = replaceKeywordValue(jsonString, "$FULLNAMEFORSUNBIRDRC$", fullNameForSunBirdRC); + jsonString = replaceKeywordValue(jsonString, "$FULLNAMEFORSUNBIRDRC$", fullNameForSunBirdR); } if (jsonString.contains("$DOBFORSUNBIRDRC$")) { - jsonString = replaceKeywordValue(jsonString, "$DOBFORSUNBIRDRC$", dobForSunBirdRC); + jsonString = replaceKeywordValue(jsonString, "$DOBFORSUNBIRDRC$", dobForSunBirdR); } if (jsonString.contains("$CHALLENGEVALUEFORSUNBIRDC$")) { HashMap mapForChallenge = new HashMap(); - mapForChallenge.put(GlobalConstants.FULLNAME, fullNameForSunBirdRC); - mapForChallenge.put(GlobalConstants.DOB, dobForSunBirdRC); + mapForChallenge.put(GlobalConstants.FULLNAME, fullNameForSunBirdR); + mapForChallenge.put(GlobalConstants.DOB, dobForSunBirdR); String challenge = gson.toJson(mapForChallenge); @@ -185,6 +228,15 @@ public static String inputstringKeyWordHandeler(String jsonString, String testCa ApplnURI.replace(GlobalConstants.API_INTERNAL, "injiweb") + "/redirect"); } + if (jsonString.contains("$GETCLIENTIDFORMOSIPIDFROMMIMOTOACTUATOR$")) { + String clientIdSection = MimotoConfigManager.getproperty("mimoto-oidc-mosipid-partner-clientid"); + jsonString = replaceKeywordWithValue(jsonString, "$GETCLIENTIDFORMOSIPIDFROMMIMOTOACTUATOR$", + getValueFromMimotoActuator("overrides", clientIdSection)); + } else if (jsonString.contains("$GETCLIENTIDFORINSURANCEFROMMIMOTOACTUATOR$")) { + String clientIdSection = MimotoConfigManager.getproperty("mimoto-oidc-sunbird-partner-clientid"); + jsonString = replaceKeywordWithValue(jsonString, "$GETCLIENTIDFORINSURANCEFROMMIMOTOACTUATOR$", + getValueFromMimotoActuator("overrides", clientIdSection)); + } return jsonString; @@ -228,4 +280,95 @@ public static String generatePublicKeyForMimoto() { } return vcString; } + + public static String generateFullNameForSunBirdR() { + return faker.name().fullName(); + } + + public static String generateDobForSunBirdR() { + Faker faker = new Faker(); + LocalDate dob = faker.date().birthday().toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + return dob.format(formatter); + } + + public static JSONArray mimotoActuatorResponseArray = null; + + public static String getValueFromMimotoActuator(String section, String key) { + String url = ApplnURI + ConfigManager.getproperty("actuatorMimotoEndpoint"); + if (!(System.getenv("useOldContextURL") == null) + && !(System.getenv("useOldContextURL").isBlank()) + && System.getenv("useOldContextURL").equalsIgnoreCase("true")) { + if (url.contains("/v1/mimoto/")) { + url = url.replace("/v1/mimoto/", "/residentmobileapp/"); + } + } + String actuatorCacheKey = url + section + key; + String value = actuatorValueCache.get(actuatorCacheKey); + if (value != null && !value.isEmpty()) + return value; + + try { + if (mimotoActuatorResponseArray == null) { + Response response = null; + JSONObject responseJson = null; + response = RestClient.getRequest(url, MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON); + + responseJson = new JSONObject(response.getBody().asString()); + mimotoActuatorResponseArray = responseJson.getJSONArray("propertySources"); + } + for (int i = 0, size = mimotoActuatorResponseArray.length(); i < size; i++) { + JSONObject eachJson = mimotoActuatorResponseArray.getJSONObject(i); + if (eachJson.get("name").toString().contains(section)) { + value = eachJson.getJSONObject(GlobalConstants.PROPERTIES).getJSONObject(key) + .get(GlobalConstants.VALUE).toString(); + if (ConfigManager.IsDebugEnabled()) + logger.info("Actuator: " + url + " key: " + key + " value: " + value); + break; + } + } + actuatorValueCache.put(actuatorCacheKey, value); + + return value; + } catch (Exception e) { + logger.error(GlobalConstants.EXCEPTION_STRING_2 + e); + logger.error("Unable to fetch the value from the actuator. URL = " + url + " section = " + section + " key " + + key); + return ""; + } + + } + + private static String getGoogleIdToken() { + String idToken = null; + + Map requestMap = new HashMap<>(); + requestMap.put("clientId", MimotoConfigManager.getproperty("google.client.id")); + requestMap.put("clientSecret", MimotoConfigManager.getproperty("google.client.secret")); + requestMap.put("refreshToken", MimotoConfigManager.getproperty("google.refresh.token")); + requestMap.put("grant_type", "refresh_token"); + String url = props.getProperty("googleIdToken"); + + Response response = RestClient.postRequestWithFormDataBody(url, requestMap); + + if (response.getStatusCode() != 200) { + String errorResponse = response.getBody().toString(); + throw new RuntimeException("Failed to get ID token. HTTP status code: " + response.getStatusCode() + + ", response body: " + errorResponse); + } + + JSONObject jsonObject = new JSONObject(response.getBody().asString()); + + if (jsonObject != null) { + idToken = jsonObject.get("id_token").toString(); + } + + if (idToken == null || idToken.isEmpty()) { + throw new RuntimeException("id_token not found in response: " + response); + } + + logger.info("Obtained id_token: " + idToken); // Debug log + return idToken; + + } } \ No newline at end of file diff --git a/api-test/src/main/resources/config/application.properties b/api-test/src/main/resources/config/application.properties index 2da96db1c..aefc909c9 100644 --- a/api-test/src/main/resources/config/application.properties +++ b/api-test/src/main/resources/config/application.properties @@ -1,6 +1,4 @@ ## End point(s) relative URLs -internalSignEndpoint=/idauthentication/v1/internal/jwtSign -signJsonPath=config/sign.json encryptionPath=v1/identity/encrypt?isInternal=false internalEncryptionPath=v1/identity/encrypt?isInternal=true encodePath=v1/identity/encode @@ -45,21 +43,7 @@ generateArgon2HashURL=/v1/keymanager/generateArgon2Hash appointmentavailabilityurl=/preregistration/v1/appointment/availability/ validateSignatureUrl=v1/identity/validateSign vciContextURL=https://www.w3.org/2018/credentials/v1 - -## Auto generated properties while running the test rig(s) -adminAutoGeneratedIdPropFileName=/admin/autoGeneratedId.properties -masterDataAutoGeneratedIdPropFileName=/masterdata/autoGeneratedId.properties -syncDataAutoGeneratedIdPropFileName=/syncdata/autoGeneratedId.properties -preregAutoGeneratedIdPropFileName=/preReg/autoGeneratedId.properties -partnerAutoGeneratedIdPropFileName=/partner/autoGeneratedId.properties -idrepoAutoGeneratedIdPropFileName=/idRepository/autoGeneratedId.properties -residentAutoGeneratedIdPropFileName=/resident/autoGeneratedId.properties -esignetAutoGeneratedIdPropFileName=/esignet/autoGeneratedId.properties -mimotoAutoGeneratedIdPropFileName=/mimoto/autoGeneratedId.properties -authAutoGeneratedIdPropFileName=/ida/autoGeneratedId.properties -prerequisiteAutoGeneratedIdPropFileName=/prerequisite/autoGeneratedId.properties -regProcAutoGeneratedIdPropFileName=/regProc/autoGeneratedId.properties -mobileIdAutoGeneratedIdPropFileName=/mobileId/autoGeneratedId.properties +googleIdToken=https://oauth2.googleapis.com/token ## As below are non changble values, move these out from properties file appIdForCertificate=IDA diff --git a/api-test/src/main/resources/config/mimoto.properties b/api-test/src/main/resources/config/mimoto.properties index 6e07fb819..215b2b21f 100644 --- a/api-test/src/main/resources/config/mimoto.properties +++ b/api-test/src/main/resources/config/mimoto.properties @@ -1,6 +1,96 @@ #---------------------------------- End point(s) relative URLs ----------------------------------# actuatorMimotoEndpoint=/v1/mimoto/actuator/env mimoto-oidc-mosipid-partner-clientid=mimoto.oidc.mosipid.partner.clientid -mimoto-oidc-sunbird-partner-clientid=mimoto.oidc.sunbird.partner.clientid -mimoto-oidc-partner-clientid=mimoto.oidc.partner.clientid -esignetSunBirdBaseURL= \ No newline at end of file +mimoto-oidc-sunbird-partner-clientid=mimoto.oidc.insurance.partner.clientid +sunbirdrc-insurance-esignet-host=sunbirdrc.insurance.esignet.host +mosipid-identity-esignet-host=mosipid.identity.esignet.host +mock-identity-esignet-host=mock.identity.esignet.host +runPlugin=mosipid + +# Uncomment the line below if the eSignet version is older than 1.5.1 for compatibility +#sunbirdInsuranceAuthFactorType=KBA + +#These properties to be added for google login +google.client.id = +google.client.secret = +google.refresh.token = + +#---------------------------------- Modifiable Properties ----------------------------------------------------------# + +#------------------------ Environment URLs and Database Connections ------------------------# + +# Keycloak URL. +keycloak-external-url = https://iam.released.mosip.net + +# PostgreSQL URLs for audit and partner databases. +audit_url = jdbc:postgresql://released.mosip.net:5432/mosip_audit +partner_url = jdbc:postgresql://released.mosip.net:5432/mosip_ida + +# Database server for connections. +db-server = released.mosip.net + + +#------------------------ secrets and passwords ------------------------# + +#------------------------ Keycloak Passwords ------------------------# +# Used for Keycloak authentication. +keycloak_Password = + +#------------------------ PostgreSQL Database Passwords ------------------------# +# Credentials for connecting to Postgres databases. +audit_password = +partner_password = +postgres-password = + +#-------- Client Secret Keys ----------# +# These keys are used for various services, make sure to update the values as required when running locally. + +mosip_partner_client_secret = +mosip_pms_client_secret = +mosip_resident_client_secret = +mosip_idrepo_client_secret = +mosip_reg_client_secret = +mosip_admin_client_secret = +mosip_hotlist_client_secret = +mosip_regproc_client_secret = +mpartner_default_mobile_secret = +mosip_testrig_client_secret = +AuthClientSecret = +mosip_crvs1_client_secret = + + +#-------- Generic Configuration ----------# + +# Enable or disable debugging mode (yes/no). +enableDebug = no + +# Whether to use pre-configured OTP (true/false). +usePreConfiguredOtp = false + +# Mock Notification Channels (email/phone/email,phone). +mockNotificationChannel = email,phone + + +#------------------------ Mosip Components Base URLs ------------------------# +# Define base URLs for different components if required. +# Example: +# mosip_components_base_urls = auditmanager=api-internal.released.mosip.net;idrepository=api-internal.released.mosip.net;authmanager=api-internal.released.mosip.net;resident=api-internal.released.mosip.net;partnermanager=api-internal.released.mosip.net;idauthentication=api-internal.released.mosip.net;masterdata=api-internal.released.mosip.net;idgenerator=api-internal.released.mosip.net;policymanager=api-internal.released.mosip.net;preregistration=api-internal.released.mosip.net;keymanager=api-internal.released.mosip.net;mock-identity-system=api.released.mosip.net +# Feel free to add more components as needed. +mosip_components_base_urls = + +#------------------------ Module Name Pattern ------------------------# +# Define module name pattern if required. +# Example: +# moduleNamePattern = (mimoto|resident) +# Feel free to add more values as needed. +moduleNamePattern = + + +#------------------------ Uncomment for Local Run ------------------------# + +# Path to the authentication certificates (if running locally, uncomment the below line and keep the value empty). +# authCertsPath = + +# X-XSS-Protection: Controls the XSS (Cross-Site Scripting) filter in browsers. +# Values: (yes/no) +xssProtectionCheck = no \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/AddIdentity/AddIdentity.yml b/api-test/src/main/resources/mimoto/AddIdentity/AddIdentity.yml index afbe8186d..829a391cf 100644 --- a/api-test/src/main/resources/mimoto/AddIdentity/AddIdentity.yml +++ b/api-test/src/main/resources/mimoto/AddIdentity/AddIdentity.yml @@ -84,4 +84,33 @@ AddIdentity: }' output: '{ "status":"ACTIVATED" +}' + + Mimoto_AddIdentity_downloadIssuerCredentialWithGoogleLogin_smoke_Pos: + endPoint: /idrepository/v1/identity/ + description: Creating a new identity for downloading issuer credential + uniqueIdentifier: TC_Mimoto_AddIdentity_03 + role: idrepo + restMethod: post + inputTemplate: mimoto/AddIdentity/addIdentity_$LANGNUMBER$ + outputTemplate: mimoto/AddIdentity/addIdentityResult + input: '{ + "value": "$BIOVALUE$", + "id": "mosip.id.create", + "registrationId": "$RID$", + "biometricReferenceId": "23452353", + "UIN": "$UIN$", + "dateOfBirth": "1992/04/15", + "postalCode": "14022", + "email": "Mimoto_AddIdentity_downloadIssuerCredentialWithGoogleLogin_smoke_Pos@mosip.net", + "phone": "9876543210", + "referenceIdentityNumber": "6789545678878", + "version": "v1", + "introducerRID": "212124324784879", + "introducerUIN": "212124324784879", + "category": "individualBiometrics", + "requesttime": "$TIMESTAMP$" +}' + output: '{ + "status":"ACTIVATED" }' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/BindingOtp/BindingOtp.yml b/api-test/src/main/resources/mimoto/BindingOtp/BindingOtp.yml index 3341d8d2a..cec15c91f 100644 --- a/api-test/src/main/resources/mimoto/BindingOtp/BindingOtp.yml +++ b/api-test/src/main/resources/mimoto/BindingOtp/BindingOtp.yml @@ -76,7 +76,7 @@ BindingOtp: output: '{ "errors": [ { - "errorCode": "invalid_identifier" + "errorCode": "RESIDENT-APP-011" } ] }' @@ -246,13 +246,12 @@ BindingOtp: outputTemplate: mimoto/error input: '{ "requestTime": "$TIMESTAMP$", - "individualId": "$ID:AddIdentity_Binding_smoke_Pos_UIN$", - "otpChannels": "$IGNORE$" + "individualId": "$ID:AddIdentity_Binding_smoke_Pos_UIN$" }' output: '{ "errors": [ { - "errorCode": "invalid_otp_channel" + "errorCode": "RESIDENT-APP-011" } ] }' @@ -450,7 +449,7 @@ BindingOtp: output: '{ "errors": [ { - "errorCode": "invalid_identifier" + "errorCode": "RESIDENT-APP-011" } ] }' @@ -620,13 +619,12 @@ BindingOtp: outputTemplate: mimoto/error input: '{ "requestTime": "$TIMESTAMP$", - "individualId": "$ID:Generate_Perpetual_VID_Binding_Valid_Smoke_sid_vid$", - "otpChannels": "$IGNORE$" + "individualId": "$ID:Generate_Perpetual_VID_Binding_Valid_Smoke_sid_vid$" }' output: '{ "errors": [ { - "errorCode": "invalid_otp_channel" + "errorCode": "RESIDENT-APP-011" } ] }' diff --git a/api-test/src/main/resources/mimoto/DownloadIssuerCredential/DownloadIssuerCredential.hbs b/api-test/src/main/resources/mimoto/DownloadIssuerCredential/DownloadIssuerCredential.hbs index 8491d6f96..082575d78 100644 --- a/api-test/src/main/resources/mimoto/DownloadIssuerCredential/DownloadIssuerCredential.hbs +++ b/api-test/src/main/resources/mimoto/DownloadIssuerCredential/DownloadIssuerCredential.hbs @@ -3,6 +3,7 @@ "code": "{{code}}", "redirect_uri": "{{redirect_uri}}", "code_verifier": "{{code_verifier}}", + "vcStorageExpiryLimitInTimes": 1, "issuer": "{{issuer}}", "credential": "{{credential}}" } \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl.hbs b/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfiguration.hbs similarity index 100% rename from api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl.hbs rename to api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfiguration.hbs diff --git a/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfiguration.yml b/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfiguration.yml new file mode 100644 index 000000000..7209be4f0 --- /dev/null +++ b/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfiguration.yml @@ -0,0 +1,32 @@ +GetIssuerConfiguration: + Mimoto_GetIssuerConfiguration_Mosip_All_Valid_Smoke: + endPoint: /v1/mimoto/issuers/Mosip/configuration + description: Retrieving verifiers list with all valid parameters + uniqueIdentifier: TC_Mimoto_GetIssuerConfiguration_01 + role: resident + restMethod: get + inputTemplate: mimoto/GetIssuerConfiguration/GetIssuerConfiguration + outputTemplate: mimoto/GetIssuerConfiguration/GetIssuerConfigurationResult + input: '{ +}' + output: '{ + "name": "MosipVerifiableCredential" +}' + Mimoto_GetIssuerConfiguration_With_Issuer_Removed_From_URL_Neg: + endPoint: /v1/mimoto/issuers/configuration + description: Retrieving verifiers list with invalid URL + uniqueIdentifier: TC_Mimoto_GetIssuerConfiguration_02 + role: resident + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/GetIssuerConfiguration/GetIssuerConfiguration + outputTemplate: mimoto/error + input: '{ +}' + output: '{ + "errors":[ + { + "errorCode":"RESIDENT-APP-035" + } + ] +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfigurationResult.hbs b/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfigurationResult.hbs new file mode 100644 index 000000000..d57337fe0 --- /dev/null +++ b/api-test/src/main/resources/mimoto/GetIssuerConfiguration/GetIssuerConfigurationResult.hbs @@ -0,0 +1,9 @@ +{ + "response": { + "credentials_supported": [ + { + "name": "{{name}}" + } + ] + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl.yml b/api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl.yml deleted file mode 100644 index bcd471ba8..000000000 --- a/api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl.yml +++ /dev/null @@ -1,32 +0,0 @@ -GetWellknownIssuerMockMdl: - Mimoto_GetWellknownIssuerMockMdl_All_Valid_Smoke: - endPoint: /v1/mimoto/issuers/MockMdl/well-known-proxy - description: Retrieving verifiers list with all valid parameters - uniqueIdentifier: TC_Mimoto_GetWellknownIssuerMockMdl_01 - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl - outputTemplate: mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdlResult - input: '{ -}' - output: '{ -}' - Mimoto_GetWellknownIssuerMockMdl_With_MockMdl_Removed_From_URL_Neg: - endPoint: /v1/mimoto/issuers/well-known-proxy - description: Retrieving verifiers list with invalid URL - uniqueIdentifier: TC_Mimoto_GetWellknownIssuerMockMdl_02 - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdl - outputTemplate: mimoto/error - input: '{ -}' - output: '{ - "errors":[ - { - "errorCode":"RESIDENT-APP-035" - } - ] -}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken.hbs new file mode 100644 index 000000000..d2939c083 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken.hbs @@ -0,0 +1,3 @@ +{ + "cookie": "{{cookie}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken.yml b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken.yml new file mode 100644 index 000000000..4682fc502 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken.yml @@ -0,0 +1,76 @@ +GoogleLoginToken: + Mimoto_GoogleLoginToken_all_Valid_Smoke: + endPoint: /v1/mimoto/auth/google/token-login + description: Creating the sesion token for google login + uniqueIdentifier: TC_Mimoto_GoogleLoginToken_01 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$GOOGLE_IDT_TOKEN$" +}' + output: '{ + "responseCode": "200" +}' + + Mimoto_GoogleLoginToken_Invalid_Provider_Neg: + endPoint: /v1/mimoto/auth/invalid/token-login + description: Creating the sesion token for google login with invalid provider + uniqueIdentifier: TC_Mimoto_GoogleLoginToken_02 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$GOOGLE_IDT_TOKEN$" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_GoogleLoginToken_InvalidToken_Neg: + endPoint: /v1/mimoto/auth/google/token-login + description: Creating the sesion token for google login with invalid IDT token and expects it to fail + uniqueIdentifier: TC_Mimoto_GoogleLoginToken_03 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken + outputTemplate: mimoto/error2 + input: '{ + "cookie": "invalid" +}' + output: '{ + "errorCode": "invalid_token" +}' + + Mimoto_GoogleLoginToken_ExpiredToken_Neg: + endPoint: /v1/mimoto/auth/google/token-login + description: Creating the sesion token for google login with expired token and expects it to fail + uniqueIdentifier: TC_Mimoto_GoogleLoginToken_04 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken + outputTemplate: mimoto/error2 + input: '{ + "cookie": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFiYjc3NGJkODcyOWVhMzhlOWMyZmUwYzY0ZDJjYTk0OGJmNjZmMGYiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiIxODg4MjgxNzM0OTktMHA0Zjk1Y2RxanM5aHR0bTEzMTBka2FlNmwzazNzam4uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiIxODg4MjgxNzM0OTktMHA0Zjk1Y2RxanM5aHR0bTEzMTBka2FlNmwzazNzam4uYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTAwODI5NzU4ODc2MDU2NjIyOTAiLCJlbWFpbCI6Im1vc2lwdGVzdGN5YmVycHduQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoiZExSUmM1WVFZaW1TTXVjaFZWRXhpQSIsIm5hbWUiOiJNb3NpcCB0ZXN0IEN5YmVycHduIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FDZzhvY0pZLWpRUE5UR3FLVHdiX3lOWHZ1c0FzSjVuSTBlcHhOczZWaWF2clVfd3hNMDdrZz1zOTYtYyIsImdpdmVuX25hbWUiOiJNb3NpcCB0ZXN0IiwiZmFtaWx5X25hbWUiOiJDeWJlcnB3biIsImlhdCI6MTc1MDc0NzExOSwiZXhwIjoxNzUwNzUwNzE5fQ.YGH3pgVvZoXyXS_eUPLmul5AXMdAdVaAoJRBJty_ZYBy7oKT7FGKwhJ9bLhXLiRG031noCaM-3EYv2N2oezvApuOGYM6xK5Mb6WdGzv-y0oR-00wniTTbuUEtgZGxPunZ2lfrI6WwkIq8JHhFh1_l4SnRGYfRqNA-vhLNMfcZsm0yG9NAt6TWgHEqy_JtMXSQTsvLfLijCLOqZCfQuTDqzk5EhtGaEMtppPN_au-Xc8fTDhY0nZtWTQ2262nqLXGSomika66dKH1SU50DtQEEdtz8Cbo_li5S4hSV1nDa7MzbIgiVqqN6fFalxRy4kvvvvaWNj7U46DsKjunINoNMQ" +}' + output: '{ + "errorCode": "invalid_token" +}' + + Mimoto_GoogleLoginToken_EmptyToken_Neg: + endPoint: /v1/mimoto/auth/google/token-login + description: Creating the sesion token for google login with expired token and expects it to fail + uniqueIdentifier: TC_Mimoto_GoogleLoginToken_05 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginToken + outputTemplate: mimoto/error2 + input: '{ + "cookie": "" +}' + output: '{ + "errorCode": "invalid_request" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdlResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginTokenResult.hbs similarity index 100% rename from api-test/src/main/resources/mimoto/GetWellknownIssuerMockMdl/GetWellknownIssuerMockMdlResult.hbs rename to api-test/src/main/resources/mimoto/LoginFlow/Authorization/GoogleLoginToken/GoogleLoginTokenResult.hbs diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/Logout.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/Logout.hbs new file mode 100644 index 000000000..1c946c7fc --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/Logout.hbs @@ -0,0 +1,4 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/Logout.yml b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/Logout.yml new file mode 100644 index 000000000..48b4d13a4 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/Logout.yml @@ -0,0 +1,51 @@ +Logout: + Mimoto_Logout_all_Valid_Smoke: + endPoint: /v1/mimoto/logout + description: Logout the session that is created + uniqueIdentifier: TC_Mimoto_Logout_01 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Authorization/Logout/Logout + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "200" +}' + + Mimoto_Logout_WithoutLogin_Neg: + endPoint: /v1/mimoto/logout + description: Logout the session without creating the login session and providing random cookie value and expects it to fail + uniqueIdentifier: TC_Mimoto_Logout_02 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Authorization/Logout/Logout + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "abcdef", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "404" +}' + + Mimoto_Logout_WithExpiredSession_Neg: + endPoint: /v1/mimoto/logout + description: Logout the session with expired login session and expects it to fail + uniqueIdentifier: TC_Mimoto_Logout_03 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Authorization/Logout/Logout + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "404" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/LogoutResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/LogoutResult.hbs new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Authorization/Logout/LogoutResult.hbs @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser.hbs new file mode 100644 index 000000000..43ef2d127 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser.hbs @@ -0,0 +1,15 @@ +{ + "encodedHash": "{{encodedHash}}", + "requestTime": "{{requestTime}}", + "request": { + "transactionId": "{{transactionId}}", + "individualId": "{{individualId}}", + "challengeList" : [ + { + "authFactorType" : "{{authFactorType}}", + "challenge" : "{{challenge}}", + "format": "alpha-numeric" + } + ] + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser.yml new file mode 100644 index 000000000..ba2db0ebe --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser.yml @@ -0,0 +1,70 @@ +AuthenticateUserForGoogleLogin: + Mimoto_ESignet_AuthenticateUserIDPForGoogleLogin_uin_Otp_Valid_Smoke: + endPoint: /v1/esignet/authorization/authenticate + description: Authenticate user to download credentials with all valid parameters + uniqueIdentifier: TC_Mimoto_AuthenticateUserForGoogleLogin_01 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUserResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid_transactionId$", + "individualId": "$ID:AddIdentity_downloadIssuerCredential_smoke_Pos_UIN$", + "authFactorType" : "OTP", + "challenge" : "$ID:AddIdentity_downloadIssuerCredential_smoke_Pos_EMAIL$", + "sendOtp":{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid_transactionId$", + "individualId": "$ID:AddIdentity_downloadIssuerCredential_smoke_Pos_UIN$", + "otpChannels": [{channel: "email"},{channel: "phone"}], + "sendOtpReqTemplate": "mimoto/SendOtpEsignet/SendOtp", + "sendOtpEndPoint": "/v1/esignet/authorization/send-otp" + } + }' + output: '{ + "sendOtpResp":{ + "maskedMobile": "$IGNORE$", + "sendOtpResTemplate":"esignet/SendOtp/SendOtpResult", + "maskedEmail": "$IGNORE$" + } +}' + + Mimoto_ESignet_AuthenticateUserIDPForGoogleLogin_uin_Otp_Valid_ForNeg: + endPoint: /v1/esignet/authorization/authenticate + description: Authenticate user to download credentials with all valid parameters for neg scenario + uniqueIdentifier: TC_Mimoto_AuthenticateUserForGoogleLogin_02 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUser + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUserResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg_transactionId$", + "individualId": "$ID:AddIdentity_downloadIssuerCredential_smoke_Pos_UIN$", + "authFactorType" : "OTP", + "challenge" : "$ID:AddIdentity_downloadIssuerCredential_smoke_Pos_EMAIL$", + "sendOtp":{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg_transactionId$", + "individualId": "$ID:AddIdentity_downloadIssuerCredential_smoke_Pos_UIN$", + "otpChannels": [{channel: "email"},{channel: "phone"}], + "sendOtpReqTemplate": "mimoto/SendOtpEsignet/SendOtp", + "sendOtpEndPoint": "/v1/esignet/authorization/send-otp" + } + }' + output: '{ + "sendOtpResp":{ + "maskedMobile": "$IGNORE$", + "sendOtpResTemplate":"esignet/SendOtp/SendOtpResult", + "maskedEmail": "$IGNORE$" + } +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUserResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUserResult.hbs new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthenticateUser/AuthenticateUserResult.hbs @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode.hbs new file mode 100644 index 000000000..491dca1ba --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode.hbs @@ -0,0 +1,19 @@ +{ + "encodedHash": "{{encodedHash}}", + "requestTime": "{{requestTime}}", + "request": { + "transactionId": "{{transactionId}}", + "acceptedClaims": [ + {{#each acceptedClaims}} + "{{claim}}" + {{#unless @last}},{{/unless}} + {{/each}} + ], + "permittedAuthorizeScopes": [ + {{#each permittedAuthorizeScopes}} + "{{scope}}" + {{#unless @last}},{{/unless}} + {{/each}} + ] + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode.yml new file mode 100644 index 000000000..ce5e45db3 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode.yml @@ -0,0 +1,36 @@ +AuthorizationCodeForGoogleLogin: + Mimoto_ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_Smoke_sid: + endPoint: /v1/esignet/authorization/auth-code + description: Generate authorization code to download credential issuer + uniqueIdentifier: TC_Mimoto_AuthorizationCodeForGoogleLogin_01 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCodeResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid_transactionId$" +}' + output: '{ +}' + + Mimoto_ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg: + endPoint: /v1/esignet/authorization/auth-code + description: Generate authorization code to download credential issuer for Neg scenario + uniqueIdentifier: TC_Mimoto_AuthorizationCodeForGoogleLogin_02 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCode + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCodeResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg_transactionId$" +}' + output: '{ +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCodeResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCodeResult.hbs new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/AuthorizationCode/AuthorizationCodeResult.hbs @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials.hbs new file mode 100644 index 000000000..06cd1f431 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials.hbs @@ -0,0 +1,6 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletId": "{{walletId}}", + "credentialId": "{{credentialId}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials.yml new file mode 100644 index 000000000..e7c219b6b --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials.yml @@ -0,0 +1,147 @@ +DeleteCredentials: + Mimoto_DeleteCredentials_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential that is saved for the wallet + uniqueIdentifier: TC_Mimoto_DeleteCredentials_01 + role: userDefinedCookie + restMethod: delete + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadMultipleStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$" +}' + output: '{ + "responseCode": "200" +}' + + Mimoto_DeleteCredentials_allReadyDeleted_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential that is already deleted for the wallet and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteCredentials_02 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadMultipleStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$" +}' + output: '{ + "errorCode":"resource_not_found" +}' + + Mimoto_DeleteCredentials_Invalid_CredentialId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential invalid credential id for the wallet and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteCredentials_03 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "invalid" +}' + output: '{ + "errorCode":"resource_not_found" +}' + + Mimoto_DeleteCredentials_Invalid_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential invalid wallet id for the wallet and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteCredentials_04 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "invalid", + "credentialId": "$ID:DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DeleteCredentials_EmptyOrSpace_CredentialId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential empty or space in credential id and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteCredentials_05 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": " " +}' + output: '{ + "errorCode":"resource_not_found" +}' + + Mimoto_DeleteCredentials_WithSession_Expired_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential for the wallet with expired session and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteCredentials_07 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DeleteCredentials_WithSession_Expired_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential for the wallet without login and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteCredentials_08 + role: userDefinedCookie + restMethod: delete + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "withoutLogin", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_DeleteCredentials_MultipleCredentialCreated_valid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId} + description: Delete the credential for the wallet with multiple credentials created + uniqueIdentifier: TC_Mimoto_DeleteCredentials_06 + role: userDefinedCookie + restMethod: delete + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentials + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$" +}' + output: '{ + "responseCode": "200" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentialsResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentialsResult.hbs new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DeleteCredentials/DeleteCredentialsResult.hbs @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential.hbs new file mode 100644 index 000000000..c877ba377 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential.hbs @@ -0,0 +1,11 @@ +{ + "grantType": "{{grantType}}", + "code": "{{code}}", + "redirectUri": "{{redirectUri}}", + "codeVerifier": "{{codeVerifier}}", + "issuer": "{{issuer}}", + "credentialConfigurationId": "{{credentialConfigurationId}}", + "walletId": "{{walletId}}", + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential.yml new file mode 100644 index 000000000..f8b55677d --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential.yml @@ -0,0 +1,923 @@ +DownloadMosipIssuerCredentialWithGoogleLogin: + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with all valid parameters along with google login + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_01 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredentialResult + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_Smoke_sid_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "issuerDisplayName": "National Identity Department", + "credentialTypeDisplayName": "MOSIP National ID" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_DownloadSameVC_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading same credential with all valid parameters along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_02 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"credential_download_error" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_With_Diff_Active_session_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with all valid parameters along with google login when a different session is active and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_38 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_WithoutUnlock_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidWallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid wallet id along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_03 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "invalid-wallet-id", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInWallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with sapce value in wallet id along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_04 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": " ", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NullInWallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with null value in wallet id along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_05 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "null", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericValueInWallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric value in wallet id along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_06 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "145672", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidGrantType_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid grant type along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_07 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "invalid", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyGrantType_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty grant type along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_08 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInGrantType_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with space in grant type along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_09 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": " ", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_MissingGrantType_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with missing grant type along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_10 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "$REMOVE$", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericGrantType_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric grant type along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_11 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "5454453", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidCode_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid code along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_12 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "invalid", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyCode_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty code along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_13 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInCode_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with space in code along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_14 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_MissingCode_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with missing code along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_15 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$REMOVE$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInCode_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric in code along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_16 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "7554432", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidRedirectUri_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid redirect uri along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_17 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "invalid", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyRedirectUri_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty redirect uri along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_18 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInRedirectUri_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with space in redirect uri along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_19 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": " ", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_MissingRedirectUri_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with missing redirect uri along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_20 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$REMOVE$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInRedirectUri_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric in redirect uri along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_21 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "8654678", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidCodeVerifier_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid code verifier along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_22 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "invalid", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyCodeVerifier_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty code verifier along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_23 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInCodeVerifier_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with space in code verifier along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_24 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": " ", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_MissingCodeVerifier_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with missing code verifier along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_25 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$REMOVE$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInCodeVerifier_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric in code verifier along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_26 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "8665542", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidIssuer_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid issuer along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_27 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "invalid", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyIssuer_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty issuer along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_28 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInIssuer_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with space in issuer along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_29 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": " ", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_MissingIssuer_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with missing issuer along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_30 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "$REMOVE$", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInIssuer_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric in issuer along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_31 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "86654556", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidCredential_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with invalid credential along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_32 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "invalid", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyCredential_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty credential along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_33 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_SpaceInCredential_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with space in credential along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_34 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": " ", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_MissingCredential_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with missing credential along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_35 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "$REMOVE$", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInCredential_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with numeric in credential along with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_36 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "655671754", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_EmptyRequestBody_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with empty request body with google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_37 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "$REMOVE$", + "code": "$REMOVE$", + "redirectUri": "$REMOVE$", + "codeVerifier": "$REMOVE$", + "issuer": "$REMOVE$", + "credentialConfigurationId": "$REMOVE$", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_WithoutLogin_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential without google login and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_39 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/responseCode + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "withoutLogin", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_WithSessionExpired_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading credential with expired google login session and expects to fail + uniqueIdentifier: TC_Mimoto_DownloadIssuerCredentialWithGoogleLogin_40 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/responseCode + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_GoogleLogin_uin_All_Valid_sid_ForNeg_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredentialResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredentialResult.hbs new file mode 100644 index 000000000..757fa4a2e --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/DownloadIssuerCredential/DownloadIssuerCredentialResult.hbs @@ -0,0 +1,4 @@ +{ + "issuerDisplayName": "{{issuerDisplayName}}", + "credentialTypeDisplayName": "{{credentialTypeDisplayName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials.hbs new file mode 100644 index 000000000..c3c22cf33 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials.hbs @@ -0,0 +1,6 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletId": "{{walletId}}", + "Accept-Language": "{{acceptLanguage}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials.yml new file mode 100644 index 000000000..b82553c1b --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials.yml @@ -0,0 +1,122 @@ +FetchAllCredentials: + Mimoto_FetchAllCredentials_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials saved for the wallet + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_01 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "acceptLanguage": "en" +}' + output: '{ +}' + + Mimoto_FetchAllCredentials_Invalid_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials with invalid wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_02 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "invalid" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_FetchAllCredentials_Empty_WalletId_And_Empty_Accepted_claims_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials with empty wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_03 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_FetchAllCredentials_Space_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials with space wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_04 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": " " +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_FetchAllCredentials_Invalid_Session_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials with invalid session and expects it to fail + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_05 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "invalid", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_FetchAllCredentials_WithoutUnlock_Wallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials without unlocking wallet and expects it to fail + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_06 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_WithoutUnlock_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_FetchAllCredentials_Expired_Session_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Get list of all credentials with expired session and expects it to fail + uniqueIdentifier: TC_Mimoto_FetchAllCredentials_07 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentials + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentialsResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentialsResult.hbs new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/FetchAllCredentials/FetchAllCredentialsResult.hbs @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest.hbs new file mode 100644 index 000000000..6dec068b5 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest.hbs @@ -0,0 +1,17 @@ +{ + "requestTime": "{{requestTime}}", + "request": { + "clientId": "{{clientId}}", + "scope": "{{scope}}", + "responseType": "{{responseType}}", + "redirectUri": "{{redirectUri}}", + "display": "{{display}}", + "prompt": "{{prompt}}", + "acrValues": "{{acrValues}}", + "nonce" : "{{nonce}}", + "state" : "{{state}}", + "claimsLocales" : "{{claimsLocales}}", + "codeChallenge" : "{{codeChallenge}}", + "codeChallengeMethod" : "{{codeChallengeMethod}}" + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest.yml new file mode 100644 index 000000000..bf688e3e2 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest.yml @@ -0,0 +1,56 @@ +OAuthDetailsRequestForGoogleLogin: + Mimoto_ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_Smoke_sid: + endPoint: /v1/esignet/authorization/v2/oauth-details + description: Creating Oauth's details with all valid parameters + uniqueIdentifier: TC_Mimoto_OAuthDetailsRequestForGoogleLogin_01 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequestResult + input: '{ + "requestTime": "$TIMESTAMP$", + "clientId": "$GETCLIENTIDFORMOSIPIDFROMMIMOTOACTUATOR$", + "scope": "mosip_identity_vc_ldp", + "responseType": "code", + "redirectUri": "$INJIREDIRECTURI$", + "display": "popup", + "prompt": "login", + "acrValues": "mosip:idp:acr:generated-code mosip:idp:acr:linked-wallet mosip:idp:acr:biometrics", + "nonce": "$UNIQUENONCEVALUEFORESIGNET$", + "state": "eree2311", + "claimsLocales": "en", + "codeChallenge": "$CODECHALLENGE$", + "codeChallengeMethod": "S256" +}' + output: '{ + +}' + + Mimoto_ESignet_OAuthDetailsRequest_GoogleLogin_all_Valid_sid_ForNeg: + endPoint: /v1/esignet/authorization/v2/oauth-details + description: Creating Oauth's details with all valid parameters for Neg scenario + uniqueIdentifier: TC_Mimoto_OAuthDetailsRequestForGoogleLogin_02 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequest + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequestResult + input: '{ + "requestTime": "$TIMESTAMP$", + "clientId": "$GETCLIENTIDFORMOSIPIDFROMMIMOTOACTUATOR$", + "scope": "mosip_identity_vc_ldp", + "responseType": "code", + "redirectUri": "$INJIREDIRECTURI$", + "display": "popup", + "prompt": "login", + "acrValues": "mosip:idp:acr:generated-code mosip:idp:acr:linked-wallet mosip:idp:acr:biometrics", + "nonce": "$UNIQUENONCEVALUEFORESIGNET$", + "state": "eree2311", + "claimsLocales": "en", + "codeChallenge": "$CODECHALLENGE$", + "codeChallengeMethod": "S256" +}' + output: '{ + +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequestResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequestResult.hbs new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/OAuthDetailsRequest/OAuthDetailsRequestResult.hbs @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential.hbs new file mode 100644 index 000000000..a384c2855 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential.hbs @@ -0,0 +1,8 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletId": "{{walletId}}", + "credentialId": "{{credentialId}}", + "Accept-Language": "{{acceptLanguage}}", + "acceptHeader": "{{acceptHeader}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential.yml new file mode 100644 index 000000000..c474c8877 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential.yml @@ -0,0 +1,366 @@ +ViewCredential: + Mimoto_ViewCredential_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with credential ID + uniqueIdentifier: TC_Mimoto_ViewCredential_01 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ +}' + + Mimoto_ViewCredential_MultipleCredential_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the another credential saved for the wallet with credential ID + uniqueIdentifier: TC_Mimoto_ViewCredential_02 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "$REMOVE$", + "acceptHeader": "application/pdf" +}' + output: '{ +}' + + Mimoto_ViewCredential_DownloadCredential_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=download + description: Download the credential saved for the wallet with credential ID + uniqueIdentifier: TC_Mimoto_ViewCredential_03 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ +}' + + Mimoto_ViewCredential_EmptyQueryParam_all_Valid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action= + description: View the credential saved for the wallet with credential ID when action query is empty + uniqueIdentifier: TC_Mimoto_ViewCredential_04 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ +}' + + Mimoto_ViewCredential_SpaceQueryParam_all_Valid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action= + description: View the credential saved for the wallet with credential ID when action query has only space + uniqueIdentifier: TC_Mimoto_ViewCredential_05 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ +}' + + Mimoto_ViewCredential_InvalidQueryParam_all_Valid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=invalid + description: View the credential saved for the wallet with credential ID when action query as invalid + uniqueIdentifier: TC_Mimoto_ViewCredential_06 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "400" +}' + + Mimoto_ViewCredential_Invalid_CredentialId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with invalid credential ID and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_07 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "invalid", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "errorCode": "resource_not_found" +}' + + Mimoto_ViewCredential_Empty_CredentialId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with empty credential ID and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_08 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "404" +}' + + Mimoto_ViewCredential_SpaceIn_CredentialId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with space in credential ID and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_09 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": " ", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "400" +}' + + Mimoto_ViewCredential_Invalid_Wallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with invalid wallet ID and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_10 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "invalid", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_ViewCredential_Empty_Wallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with empty wallet ID and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_11 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "404" +}' + + Mimoto_ViewCredential_SpaceIn_Wallet_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with space in wallet ID and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_12 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": " ", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "400" +}' + + Mimoto_ViewCredential_Invalid_AcceptLanguage_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with invalid accept language and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_13 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "oo", + "acceptHeader": "application/pdf" +}' + output: '{ + "errorCode": "credential_fetch_error" +}' + + Mimoto_ViewCredential_SpaceIn_AcceptLanguage_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with space in accept language and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_14 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": " ", + "acceptHeader": "application/pdf" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_ViewCredential_Invalid_AcceptHeader_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with invalid accept header and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_14 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "invalid" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_ViewCredential_Without_AcceptHeader_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet without accept header and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_15 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "$REMOVE$" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_ViewCredential_SpaceIn_AcceptHeader_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet space in accept header and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_16 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": " " +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_ViewCredential_WithExpired_Session_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet with expired token and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_17 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredentialAfterLogout.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredentialAfterLogout.yml new file mode 100644 index 000000000..04f72fcb2 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredentialAfterLogout.yml @@ -0,0 +1,21 @@ +ViewCredentialAfterLogout: + Mimoto_ViewCredential_AfterLogout_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials/{credentialId}?action=inline + description: View the credential saved for the wallet after logout and expects it to fail + uniqueIdentifier: TC_Mimoto_ViewCredential_18 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredential + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "credentialId": "$ID:DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_All_Valid_Smoke_sid_credentialId$", + "acceptLanguage": "en", + "acceptHeader": "application/pdf" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredentialResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredentialResult.hbs new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadMosipIssuerCredential/ViewCredential/ViewCredentialResult.hbs @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.hbs new file mode 100644 index 000000000..d58a65555 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.hbs @@ -0,0 +1,15 @@ +{ + "encodedHash": "{{encodedHash}}", + "requestTime": "{{requestTime}}", + "request": { + "transactionId": "{{transactionId}}", + "individualId": "{{individualId}}", + "challengeList" : [ + { + "authFactorType" : "{{authFactorType}}", + "challenge" : "{{challenge}}", + "format": "{{format}}" + } + ] + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml new file mode 100644 index 000000000..36176f49b --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml @@ -0,0 +1,44 @@ +AuthenticateUserSunBirdCForGoogleLogin: + Mimoto_ESignet_AuthenticateUserSunBirdCForGoogleLogin_SunBirdC_Valid_Smoke: + endPoint: $ESIGNETMOCKBASEURL$/v1/esignet/authorization/authenticate + description: Authenticating user for downloading stayprotected credential with google login + uniqueIdentifier: TC_Mimoto_AuthenticateUserSunBirdCForGoogleLogin_01 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdCResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_all_Valid_Smoke_sid_transactionId$", + "individualId": "$POLICYNUMBERFORSUNBIRDRC$", + "authFactorType" : "$SUNBIRDINSURANCEAUTHFACTORTYPE$", + "challenge" : "$CHALLENGEVALUEFORSUNBIRDC$", + "format": "base64url-encoded-json" + }' + output: '{ +}' + + Mimoto_ESignet_AuthenticateUserSunBirdCForGoogleLogin_SunBirdC_MultipleVC_Valid_Smoke: + endPoint: $ESIGNETMOCKBASEURL$/v1/esignet/authorization/authenticate + description: Authenticating user for downloading stayprotected credential with google login + uniqueIdentifier: TC_Mimoto_AuthenticateUserSunBirdCForGoogleLogin_02 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdCResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_MultipleVC_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_MultipleVC_all_Valid_Smoke_sid_transactionId$", + "individualId": "$POLICYNUMBERFORSUNBIRDRC$", + "authFactorType" : "$SUNBIRDINSURANCEAUTHFACTORTYPE$", + "challenge" : "$CHALLENGEVALUEFORSUNBIRDC$", + "format": "base64url-encoded-json" + }' + output: '{ +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdCResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdCResult.hbs new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthenticateUserSunBirdC/AuthenticateUserSunBirdCResult.hbs @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC.hbs new file mode 100644 index 000000000..491dca1ba --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC.hbs @@ -0,0 +1,19 @@ +{ + "encodedHash": "{{encodedHash}}", + "requestTime": "{{requestTime}}", + "request": { + "transactionId": "{{transactionId}}", + "acceptedClaims": [ + {{#each acceptedClaims}} + "{{claim}}" + {{#unless @last}},{{/unless}} + {{/each}} + ], + "permittedAuthorizeScopes": [ + {{#each permittedAuthorizeScopes}} + "{{scope}}" + {{#unless @last}},{{/unless}} + {{/each}} + ] + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC.yml new file mode 100644 index 000000000..aeb203528 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC.yml @@ -0,0 +1,38 @@ +AuthorizationCodeSunBirdCForGoogleLogin: + Mimoto_ESignet_AuthorizationCode_SunBirdCForGoogleLogin_SunBirdC_All_Valid_Smoke_sid: + endPoint: $ESIGNETMOCKBASEURL$/v1/esignet/authorization/auth-code + description: Generating authorization code for downloading sunbird VC with google login + uniqueIdentifier: TC_Mimoto_AuthorizationCodeSunBirdC_01 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdCResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_all_Valid_Smoke_sid_transactionId$", + "permittedAuthorizeScopes": [{scope: "sunbird_rc_insurance_vc_ldp"}] +}' + output: '{ +}' + + Mimoto_ESignet_AuthorizationCode_SunBirdCForGoogleLogin_MultipleVC_SunBirdC_All_Valid_Smoke_sid: + endPoint: $ESIGNETMOCKBASEURL$/v1/esignet/authorization/auth-code + description: Generating authorization code for downloading multiple sunbird VC with google login + uniqueIdentifier: TC_Mimoto_AuthorizationCodeSunBirdC_02 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + validityCheckRequired: true + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdC + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdCResult + input: '{ + "encodedHash": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_MultipleVC_all_Valid_Smoke_sid_encodedResp$", + "requestTime": "$TIMESTAMP$", + "transactionId": "$ID:ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_MultipleVC_all_Valid_Smoke_sid_transactionId$", + "permittedAuthorizeScopes": [{scope: "sunbird_rc_insurance_vc_ldp"}] +}' + output: '{ +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdCResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdCResult.hbs new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/AuthorizationCodeSunBirdC/AuthorizationCodeSunBirdCResult.hbs @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential.hbs new file mode 100644 index 000000000..b358f636c --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential.hbs @@ -0,0 +1,12 @@ +{ + "grantType": "{{grantType}}", + "code": "{{code}}", + "redirectUri": "{{redirectUri}}", + "codeVerifier": "{{codeVerifier}}", + "issuer": "{{issuer}}", + "credentialConfigurationId": "{{credentialConfigurationId}}", + "walletId": "{{walletId}}", + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "Accept-Language": "{{acceptLanguage}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential.yml new file mode 100644 index 000000000..1f4be6bb5 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential.yml @@ -0,0 +1,122 @@ +DownloadStayProtectedIssuerCredentialWithGoogleLogin: + Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading sunbird credential with valid authorizationCode after google login + uniqueIdentifier: TC_Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_01 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredentialResult + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_SunBirdCForGoogleLogin_SunBirdC_All_Valid_Smoke_sid_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "StayProtected", + "credentialConfigurationId": "InsuranceCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "acceptLanguage": "en" +}' + output: '{ + "issuerDisplayName": "StayProtected Insurance", + "credentialTypeDisplayName": "Health Insurance" +}' + + Mimoto_DownloadMultipleStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_All_Valid_Smoke_sid: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading Multiple sunbird credential for policy already downloaded with valid authorizationCode after google login + uniqueIdentifier: TC_Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_02 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredentialResult + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_SunBirdCForGoogleLogin_MultipleVC_SunBirdC_All_Valid_Smoke_sid_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "StayProtected", + "credentialConfigurationId": "InsuranceCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "acceptLanguage": "" +}' + output: '{ + "issuerDisplayName": "StayProtected Insurance", + "credentialTypeDisplayName": "Health Insurance" +}' + + Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_InvalidLocale_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading sunbird credential with valid authorizationCode after google login with invalid locale and expects it to fail + uniqueIdentifier: TC_Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_03 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_SunBirdCForGoogleLogin_SunBirdC_All_Valid_Smoke_sid_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "StayProtected", + "credentialConfigurationId": "InsuranceCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "acceptLanguage": "oo" +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_SpaceInLocale_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading sunbird credential with valid authorizationCode after google login with space in locale and expects it to fail + uniqueIdentifier: TC_Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_04 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_SunBirdCForGoogleLogin_SunBirdC_All_Valid_Smoke_sid_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "StayProtected", + "credentialConfigurationId": "InsuranceCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "acceptLanguage": " " +}' + output: '{ + "errorCode":"invalid_request" +}' + + Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_NumericInLocale_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/credentials + description: Downloading sunbird credential with valid authorizationCode after google login with numeric in locale and expects it to fail + uniqueIdentifier: TC_Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_05 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredential + outputTemplate: mimoto/error2 + input: '{ + "grantType": "authorization_code", + "code": "$ID:ESignet_AuthorizationCode_SunBirdCForGoogleLogin_SunBirdC_All_Valid_Smoke_sid_code$", + "redirectUri": "$INJIREDIRECTURI$", + "codeVerifier": "$CODEVERIFIER$", + "issuer": "StayProtected", + "credentialConfigurationId": "InsuranceCredential", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "acceptLanguage": "14" +}' + output: '{ + "errorCode":"invalid_request" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredentialResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredentialResult.hbs new file mode 100644 index 000000000..757fa4a2e --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/DownloadIssuerCredential/DownloadIssuerCredentialResult.hbs @@ -0,0 +1,4 @@ +{ + "issuerDisplayName": "{{issuerDisplayName}}", + "credentialTypeDisplayName": "{{credentialTypeDisplayName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.hbs new file mode 100644 index 000000000..6dec068b5 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.hbs @@ -0,0 +1,17 @@ +{ + "requestTime": "{{requestTime}}", + "request": { + "clientId": "{{clientId}}", + "scope": "{{scope}}", + "responseType": "{{responseType}}", + "redirectUri": "{{redirectUri}}", + "display": "{{display}}", + "prompt": "{{prompt}}", + "acrValues": "{{acrValues}}", + "nonce" : "{{nonce}}", + "state" : "{{state}}", + "claimsLocales" : "{{claimsLocales}}", + "codeChallenge" : "{{codeChallenge}}", + "codeChallengeMethod" : "{{codeChallengeMethod}}" + } +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml new file mode 100644 index 000000000..b3416e907 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml @@ -0,0 +1,56 @@ +OAuthDetailsRequestSunBirdCForGoogleLogin: + Mimoto_ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_all_Valid_Smoke_sid: + endPoint: $ESIGNETMOCKBASEURL$/v1/esignet/authorization/v2/oauth-details + description: Getting auth details for downloading sunbird credentials with google login + uniqueIdentifier: TC_Mimoto_OAuthDetailsRequestSunBirdCForGoogleLogin_01 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdCResult + input: '{ + "requestTime": "$TIMESTAMP$", + "clientId": "$GETCLIENTIDFORINSURANCEFROMMIMOTOACTUATOR$", + "scope": "sunbird_rc_insurance_vc_ldp", + "responseType": "code", + "redirectUri": "$INJIREDIRECTURI$", + "display": "popup", + "prompt": "login", + "acrValues": "mosip:idp:acr:knowledge", + "nonce": "$UNIQUENONCEVALUEFORESIGNET$", + "state": "eree2311", + "claimsLocales": "en", + "codeChallenge": "$CODECHALLENGE$", + "codeChallengeMethod": "S256" +}' + output: '{ + +}' + + Mimoto_ESignet_OAuthDetailsRequest_GoogleLogin_SunBirdC_MultipleVC_all_Valid_Smoke_sid: + endPoint: $ESIGNETMOCKBASEURL$/v1/esignet/authorization/v2/oauth-details + description: Getting auth details for downloading multiple sunbird credentials with google login + uniqueIdentifier: TC_Mimoto_OAuthDetailsRequestSunBirdCForGoogleLogin_02 + role: resident + restMethod: post + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC + outputTemplate: mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdCResult + input: '{ + "requestTime": "$TIMESTAMP$", + "clientId": "$GETCLIENTIDFORINSURANCEFROMMIMOTOACTUATOR$", + "scope": "sunbird_rc_insurance_vc_ldp", + "responseType": "code", + "redirectUri": "$INJIREDIRECTURI$", + "display": "popup", + "prompt": "login", + "acrValues": "mosip:idp:acr:knowledge", + "nonce": "$UNIQUENONCEVALUEFORESIGNET$", + "state": "eree2311", + "claimsLocales": "en", + "codeChallenge": "$CODECHALLENGE$", + "codeChallengeMethod": "S256" +}' + output: '{ + +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdCResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdCResult.hbs new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/DownloadStayProtectedIssuerCredentialWithGoogleLogin/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdCResult.hbs @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile.hbs new file mode 100644 index 000000000..1c946c7fc --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile.hbs @@ -0,0 +1,4 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile.yml b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile.yml new file mode 100644 index 000000000..141a3757e --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile.yml @@ -0,0 +1,17 @@ +GetUserProfile: + Mimoto_GetUserProfile_all_Valid_Smoke: + endPoint: /v1/mimoto/users/me + description: Get the user profile details for the session login + uniqueIdentifier: TC_Mimoto_GetUserProfile_01 + role: userDefinedCookie + restMethod: get + inputTemplate: mimoto/LoginFlow/Users/GetUserProfile/GetUserProfile + outputTemplate: mimoto/LoginFlow/Users/GetUserProfile/GetUserProfileResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "displayName": "Mosip test Cyberpwn", + "email": "mosiptestcyberpwn@gmail.com" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfileResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfileResult.hbs new file mode 100644 index 000000000..2155b167a --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfile/GetUserProfileResult.hbs @@ -0,0 +1,4 @@ +{ + "displayName": "{{displayName}}", + "email": "{{email}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout.hbs new file mode 100644 index 000000000..1c946c7fc --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout.hbs @@ -0,0 +1,4 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout.yml b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout.yml new file mode 100644 index 000000000..1e68a1ae0 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout.yml @@ -0,0 +1,17 @@ +GetUserProfileAfterLogout: + Mimoto_GetUserProfileAfterLogout_all_Valid_Smoke: + endPoint: /v1/mimoto/users/me + description: Get the user profile details for the session login + uniqueIdentifier: TC_Mimoto_GetUserProfileAfterLogout_01 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogout + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogoutResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogoutResult.hbs new file mode 100644 index 000000000..2155b167a --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Users/GetUserProfileAfterLogout/GetUserProfileAfterLogoutResult.hbs @@ -0,0 +1,4 @@ +{ + "displayName": "{{displayName}}", + "email": "{{email}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet.hbs new file mode 100644 index 000000000..e8506e57c --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet.hbs @@ -0,0 +1,7 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletName": "{{walletName}}", + "walletPin": "{{walletPin}}", + "confirmWalletPin": "{{confirmWalletPin}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet.yml b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet.yml new file mode 100644 index 000000000..5f46d3f7c --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet.yml @@ -0,0 +1,421 @@ +CreateWallet: + Mimoto_CreateWallet_all_Valid_Smoke_sid: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with successful login + uniqueIdentifier: TC_Mimoto_CreateWallet_01 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWallet", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "walletName": "AutomationWallet" +}' + + Mimoto_CreateWallet_WithoutUnlock_all_Valid_Smoke_sid: + endPoint: /v1/mimoto/wallets + description: Creating new wallet for the same user with successful login + uniqueIdentifier: TC_Mimoto_CreateWallet_02 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletWithoutUnlock", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "walletName": "AutomationWalletWithoutUnlock" +}' + + Mimoto_CreateWallet_SameName_Valid: + endPoint: /v1/mimoto/wallets + description: Creating new wallet for the same user with same name with successful login + uniqueIdentifier: TC_Mimoto_CreateWallet_03 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWallet", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "walletName": "AutomationWallet" +}' + + Mimoto_CreateWallet_WithoutLogin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet without login and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_04 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "withoutLogin", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg1", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_CreateWallet_SessionExpired_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with expired session and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_05 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg2", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_CreateWallet_InvalidWalletName_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with invalid wallet name and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_06 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "********", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_EmptyWalletName_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with empty wallet name and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_07 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_SpaceInWalletName_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with space in wallet name and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_08 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": " ", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_MissingWalletName_Valid: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with missing wallet name + uniqueIdentifier: TC_Mimoto_CreateWallet_09 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "$REMOVE$", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "responseCode": "200" +}' + + Mimoto_CreateWallet_MoreThan50Characters_WalletName_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with more than 50 characters in wallet name and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_10 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "abcdefghijklmnopqrstuvwxyz1234567890abcdefghijklmnopqrstuvwxyz", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_WalletNameWithSpaces_valid: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with wallet name contains spaces + uniqueIdentifier: TC_Mimoto_CreateWallet_11 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "Automation Wallet", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "walletName": "Automation Wallet" +}' + + Mimoto_CreateWallet_WalletNameInSpecialCharacters_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with special characters as wallet name and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_12 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "%$!*&^", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_WalletNameInNumbers_Valid: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with numbers as wallet name + uniqueIdentifier: TC_Mimoto_CreateWallet_13 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "5675441", + "walletPin": "123456", + "confirmWalletPin": "123456" +}' + output: '{ + "walletName": "5675441" +}' + + Mimoto_CreateWallet_MoreThan6Digits_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with wallet pin with more than 6 digits and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_14 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg3", + "walletPin": "12345678", + "confirmWalletPin": "12345678" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_Empty_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with empty wallet pin and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_15 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": "", + "confirmWalletPin": "" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_Space_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with space in wallet pin and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_16 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": " ", + "confirmWalletPin": " " +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_Missing_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with missing wallet pin and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_17 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": "$REMOVE$", + "confirmWalletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_invalid_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with invalid wallet pin and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_18 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": "12345", + "confirmWalletPin": "12345" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_StringValue_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with string value in wallet pin and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_19 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": "abcdef", + "confirmWalletPin": "abcdef" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_SpecialCharacters_WalletPin_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with special characters in wallet pin and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_20 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": "^%$@*!", + "confirmWalletPin": "^%$@*!" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_WalletPin_ConfirmWalletPin_Mismatch_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet with wallet pin and confirm wallet pin mismatch and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_21 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "AutomationWalletNeg4", + "walletPin": "123456", + "confirmWalletPin": "654321" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_CreateWallet_Without_WalletPin_And_WalletName_Neg: + endPoint: /v1/mimoto/wallets + description: Creating new wallet without wallet pin and wallet name and expects to fail + uniqueIdentifier: TC_Mimoto_CreateWallet_22 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/CreateWallet/CreateWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletName": "$REMOVE$", + "walletPin": "$REMOVE$", + "confirmWalletPin": "$REMOVE$" +}' + output: '{ + "errorCode": "invalid_request" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult.hbs new file mode 100644 index 000000000..c6fe5ba21 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/CreateWallet/CreateWalletResult.hbs @@ -0,0 +1,3 @@ +{ + "walletName": "{{walletName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet.hbs new file mode 100644 index 000000000..3ed69cbaa --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet.hbs @@ -0,0 +1,5 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletId": "{{walletId}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet.yml b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet.yml new file mode 100644 index 000000000..695fd7d7e --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet.yml @@ -0,0 +1,122 @@ +UnlockWallet: + Mimoto_DeleteWallet_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet that is created + uniqueIdentifier: TC_Mimoto_DeleteWallet_01 + role: userDefinedCookie + restMethod: delete + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "responseCode": "200" +}' + + Mimoto_DeleteWallet_AlreadyDeleted_Neg: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet that is already deleted and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteWallet_02 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_DeleteWallet_Invalid_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet with invalid wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteWallet_03 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "invalid" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_DeleteWallet_Empty_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet with empty wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteWallet_04 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "" +}' + output: '{ + "errorCode": "internal_server_error" +}' + + Mimoto_DeleteWallet_Space_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet with space wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteWallet_05 + role: userDefinedCookie + restMethod: delete + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": " " +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_DeleteWallet_WithoutLogin_Neg: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet without login session and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteWallet_06 + role: userDefinedCookie + restMethod: delete + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "withoutLogin", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_DeleteWallet_ExpiredSession_Neg: + endPoint: /v1/mimoto/wallets/{walletId} + description: Delete the wallet with expired login session and expects it to fail + uniqueIdentifier: TC_Mimoto_DeleteWallet_07 + role: userDefinedCookie + restMethod: delete + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWalletResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWalletResult.hbs new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/DeleteWallet/DeleteWalletResult.hbs @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWallets.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWallets.hbs new file mode 100644 index 000000000..1c946c7fc --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWallets.hbs @@ -0,0 +1,4 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWallets.yml b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWallets.yml new file mode 100644 index 000000000..ffd511a23 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWallets.yml @@ -0,0 +1,50 @@ +GetWallets: + Mimoto_GetWallets_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets + description: Get list of all the wallets that is present in DB + uniqueIdentifier: TC_Mimoto_GetWallets_01 + role: userDefinedCookie + restMethod: get + checkErrorsOnlyInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/GetWallets/GetWallets + outputTemplate: mimoto/LoginFlow/Wallet/GetWallets/GetWalletsResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION" +}' + output: '{ +}' + + Mimoto_GetWallets_WithoutLogin_Neg: + endPoint: /v1/mimoto/wallets + description: Get list of all the wallets that is present in DB without login and expects to fail + uniqueIdentifier: TC_Mimoto_GetWallets_02 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/GetWallets/GetWallets + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "withoutLogin", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_GetWallets_WithExpiredSession_Neg: + endPoint: /v1/mimoto/wallets + description: Get list of all the wallets that is present in DB with expired login and expects to fail + uniqueIdentifier: TC_Mimoto_GetWallets_03 + role: userDefinedCookie + restMethod: get + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/GetWallets/GetWallets + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "OGYwNWI5YWQtZjg0MS00NmM2LWJiZTgtOGRmMWYwMGYzNzcy", + "cookieName": "SESSION" +}' + output: '{ + "responseCode": "401" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWalletsResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWalletsResult.hbs new file mode 100644 index 000000000..7a73a41bf --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/GetWallets/GetWalletsResult.hbs @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet.hbs new file mode 100644 index 000000000..59db2a6ff --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet.hbs @@ -0,0 +1,6 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletId": "{{walletId}}", + "walletPin": "{{walletPin}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet.yml b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet.yml new file mode 100644 index 000000000..ced8054b5 --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet.yml @@ -0,0 +1,92 @@ +UnlockWallet: + Mimoto_UnlockWallet_all_Valid_Smoke: + endPoint: /v1/mimoto/wallets/{walletId}/unlock + description: Unlock the wallet that is created + uniqueIdentifier: TC_Mimoto_UnlockWallet_01 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet + outputTemplate: mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWalletResult + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "walletPin": "123456" +}' + output: '{ + "walletName": "$ID:CreateWallet_all_Valid_Smoke_sid_walletName$", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$" +}' + + Mimoto_UnlockWallet_Invalid_WalletId_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/unlock + description: Unlock the wallet with invalid wallet id and expects it to fail + uniqueIdentifier: TC_Mimoto_UnlockWallet_02 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "invalid", + "walletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' + + Mimoto_UnlockWallet_Incorrect_WalletPin_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/unlock + description: Unlock the wallet with incorrect wallet Pin and expects it to fail + uniqueIdentifier: TC_Mimoto_UnlockWallet_03 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "walletPin": "876532" +}' + output: '{ + "errorCode": "invalid_pin" +}' + + Mimoto_UnlockWallet_WithoutLogin_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/unlock + description: Unlock the wallet without login session and expects it to fail + uniqueIdentifier: TC_Mimoto_UnlockWallet_04 + role: userDefinedCookie + restMethod: post + checkOnlyStatusCodeInResponse: true + inputTemplate: mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet + outputTemplate: mimoto/responseCode + input: '{ + "cookie": "withoutLogin", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "walletPin": "123456" +}' + output: '{ + "responseCode": "401" +}' + + Mimoto_UnlockWallet_Invalid_WalletPin_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/unlock + description: Unlock the wallet with invalid wallet Pin and expects it to fail + uniqueIdentifier: TC_Mimoto_UnlockWallet_06 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWallet + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "walletPin": "87653" +}' + output: '{ + "errorCode": "invalid_request" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWalletResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWalletResult.hbs new file mode 100644 index 000000000..8a296140c --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWallet/UnlockWalletResult.hbs @@ -0,0 +1,4 @@ +{ + "walletName": "{{walletName}}", + "walletId": "{{walletId}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete.hbs new file mode 100644 index 000000000..59db2a6ff --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete.hbs @@ -0,0 +1,6 @@ +{ + "cookie": "{{cookie}}", + "cookieName": "{{cookieName}}", + "walletId": "{{walletId}}", + "walletPin": "{{walletPin}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete.yml b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete.yml new file mode 100644 index 000000000..9d984aa3f --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete.yml @@ -0,0 +1,18 @@ +UnlockWalletAfterDelete: + Mimoto_UnlockWalletAfterDelete_Neg: + endPoint: /v1/mimoto/wallets/{walletId}/unlock + description: Unlock the wallet that is already deleted and expects it to fail + uniqueIdentifier: TC_Mimoto_UnlockWalletAfterDelete_01 + role: userDefinedCookie + restMethod: post + inputTemplate: mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDelete + outputTemplate: mimoto/error2 + input: '{ + "cookie": "$ID:GoogleLoginToken_all_Valid_Smoke_sessionCookie$", + "cookieName": "SESSION", + "walletId": "$ID:CreateWallet_all_Valid_Smoke_sid_walletId$", + "walletPin": "123456" +}' + output: '{ + "errorCode": "invalid_request" +}' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDeleteResult.hbs b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDeleteResult.hbs new file mode 100644 index 000000000..8a296140c --- /dev/null +++ b/api-test/src/main/resources/mimoto/LoginFlow/Wallet/UnlockWalletAfterDelete/UnlockWalletAfterDeleteResult.hbs @@ -0,0 +1,4 @@ +{ + "walletName": "{{walletName}}", + "walletId": "{{walletId}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/OAuthDetailsRequest/OAuthDetailsRequest.yml b/api-test/src/main/resources/mimoto/OAuthDetailsRequest/OAuthDetailsRequest.yml index aaf157c4a..ceb013f81 100644 --- a/api-test/src/main/resources/mimoto/OAuthDetailsRequest/OAuthDetailsRequest.yml +++ b/api-test/src/main/resources/mimoto/OAuthDetailsRequest/OAuthDetailsRequest.yml @@ -10,14 +10,14 @@ OAuthDetailsRequest: outputTemplate: mimoto/OAuthDetailsRequest/OAuthDetailsRequestResult input: '{ "requestTime": "$TIMESTAMP$", - "clientId": "$GETCLIENTIDFROMMIMOTOACTUATOR$", + "clientId": "$GETCLIENTIDFORMOSIPIDFROMMIMOTOACTUATOR$", "scope": "mosip_identity_vc_ldp", "responseType": "code", "redirectUri": "$INJIREDIRECTURI$", "display": "popup", "prompt": "login", "acrValues": "mosip:idp:acr:generated-code mosip:idp:acr:linked-wallet mosip:idp:acr:biometrics", - "nonce": "973eieljzng", + "nonce": "$UNIQUENONCEVALUEFORESIGNET$", "state": "eree2311", "claimsLocales": "en", "codeChallenge": "$CODECHALLENGE$", diff --git a/api-test/src/main/resources/mimoto/SunBirdC/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml b/api-test/src/main/resources/mimoto/SunBirdC/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml index a4962b1f8..f8a88b1fe 100644 --- a/api-test/src/main/resources/mimoto/SunBirdC/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml +++ b/api-test/src/main/resources/mimoto/SunBirdC/AuthenticateUserSunBirdC/AuthenticateUserSunBirdC.yml @@ -14,7 +14,7 @@ AuthenticateUserSunBirdC: "requestTime": "$TIMESTAMP$", "transactionId": "$ID:ESignet_OAuthDetailsRequest_SunBirdC_all_Valid_Smoke_sid_transactionId$", "individualId": "$POLICYNUMBERFORSUNBIRDRC$", - "authFactorType" : "KBA", + "authFactorType" : "$SUNBIRDINSURANCEAUTHFACTORTYPE$", "challenge" : "$CHALLENGEVALUEFORSUNBIRDC$", "format": "base64url-encoded-json" }' diff --git a/api-test/src/main/resources/mimoto/SunBirdC/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml b/api-test/src/main/resources/mimoto/SunBirdC/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml index 4b717219e..c4778e3a2 100644 --- a/api-test/src/main/resources/mimoto/SunBirdC/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml +++ b/api-test/src/main/resources/mimoto/SunBirdC/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdC.yml @@ -10,14 +10,14 @@ OAuthDetailsRequestSunBirdC: outputTemplate: mimoto/SunBirdC/OAuthDetailsRequestSunBirdC/OAuthDetailsRequestSunBirdCResult input: '{ "requestTime": "$TIMESTAMP$", - "clientId": "$GETCLIENTIDFROMMIMOTOACTUATOR$", + "clientId": "$GETCLIENTIDFORINSURANCEFROMMIMOTOACTUATOR$", "scope": "sunbird_rc_insurance_vc_ldp", "responseType": "code", "redirectUri": "$INJIREDIRECTURI$", "display": "popup", "prompt": "login", "acrValues": "mosip:idp:acr:knowledge", - "nonce": "973eieljzng", + "nonce": "$UNIQUENONCEVALUEFORESIGNET$", "state": "eree2311", "claimsLocales": "en", "codeChallenge": "$CODECHALLENGE$", diff --git a/api-test/src/main/resources/mimoto/SunBirdR/GetPolicySunBirdR/GetPolicySunBirdR.yml b/api-test/src/main/resources/mimoto/SunBirdR/GetPolicySunBirdR/GetPolicySunBirdR.yml index f3272c103..d1a517eb2 100644 --- a/api-test/src/main/resources/mimoto/SunBirdR/GetPolicySunBirdR/GetPolicySunBirdR.yml +++ b/api-test/src/main/resources/mimoto/SunBirdR/GetPolicySunBirdR/GetPolicySunBirdR.yml @@ -13,5 +13,5 @@ GetPolicySunBirdR: "insuranceid": "$ID:ESignet_SunBirdR_CreatePolicy_NOAUTH_Valid_Smoke_Sid_osid$" }' output: '{ - "policyNumber": "326253801" + "policyNumber": "$POLICYNUMBERFORSUNBIRDRC$" }' \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/WalletBinding/WalletBinding.yml b/api-test/src/main/resources/mimoto/WalletBinding/WalletBinding.yml index db69714f2..99a95197c 100644 --- a/api-test/src/main/resources/mimoto/WalletBinding/WalletBinding.yml +++ b/api-test/src/main/resources/mimoto/WalletBinding/WalletBinding.yml @@ -277,6 +277,7 @@ WalletBinding: validityCheckRequired: true restMethod: post checkErrorsOnlyInResponse: true + allowedErrorCodes: IDA-MLC-009,binding_auth_failed inputTemplate: mimoto/WalletBinding/WalletBinding outputTemplate: mimoto/error input: '{ @@ -299,7 +300,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed" + "errorCode": "IDA-MLC-009" } ], "sendOtpResp": { @@ -357,6 +358,7 @@ WalletBinding: validityCheckRequired: true restMethod: post checkErrorsOnlyInResponse: true + allowedErrorCodes: IDA-MLC-009,binding_auth_failed inputTemplate: mimoto/WalletBinding/WalletBinding outputTemplate: mimoto/error input: '{ @@ -379,7 +381,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed" + "errorCode": "IDA-MLC-009" } ], "sendOtpResp": { @@ -1812,6 +1814,7 @@ WalletBinding: validityCheckRequired: true restMethod: post checkErrorsOnlyInResponse: true + allowedErrorCodes: IDA-MLC-009,binding_auth_failed inputTemplate: mimoto/WalletBinding/WalletBinding outputTemplate: mimoto/error input: '{ @@ -1834,7 +1837,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed" + "errorCode": "IDA-MLC-009" } ], "sendOtpResp": { @@ -1892,6 +1895,7 @@ WalletBinding: validityCheckRequired: true restMethod: post checkErrorsOnlyInResponse: true + allowedErrorCodes: IDA-MLC-009,binding_auth_failed inputTemplate: mimoto/WalletBinding/WalletBinding outputTemplate: mimoto/error input: '{ @@ -1914,7 +1918,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed" + "errorCode": "IDA-MLC-009" } ], "sendOtpResp": { diff --git a/api-test/src/main/resources/mimoto/autoGeneratedId.properties b/api-test/src/main/resources/mimoto/autoGeneratedId.properties deleted file mode 100644 index f7bbba8b4..000000000 --- a/api-test/src/main/resources/mimoto/autoGeneratedId.properties +++ /dev/null @@ -1 +0,0 @@ -DefinePolicyGroup_All_Valid_Smoke_sid=456789567 \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/error2.hbs b/api-test/src/main/resources/mimoto/error2.hbs new file mode 100644 index 000000000..bf46cc93a --- /dev/null +++ b/api-test/src/main/resources/mimoto/error2.hbs @@ -0,0 +1,3 @@ +{ + "errorCode": "{{errorCode}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/mimoto/responseCode.hbs b/api-test/src/main/resources/mimoto/responseCode.hbs new file mode 100644 index 000000000..f6db3ef22 --- /dev/null +++ b/api-test/src/main/resources/mimoto/responseCode.hbs @@ -0,0 +1,3 @@ +{ + "responseCode": "{{responseCode}}" +} \ No newline at end of file diff --git a/api-test/src/main/resources/testCaseSkippedList.txt b/api-test/src/main/resources/testCaseSkippedList.txt index 76c50d7e3..451e65437 100644 --- a/api-test/src/main/resources/testCaseSkippedList.txt +++ b/api-test/src/main/resources/testCaseSkippedList.txt @@ -1,2 +1,18 @@ ##### JIRA number;testcase -#MOSIP-12456------Mimoto_AddIdentity_Binding_smoke_Pos \ No newline at end of file +#MOSIP-12456------Test_Case_Name +INJIWEB-1629------Mimoto_CreateWallet_SpaceInWalletName_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidCodeVerifier_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidCode_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidCredential_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidGrantType_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidIssuer_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_InvalidRedirectUri_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericGrantType_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInCodeVerifier_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInCode_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInCredential_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInIssuer_Neg +INJIWEB-1629------Mimoto_DownloadIssuerCredentialWithGoogleLogin_IssuerMosip_NumericInRedirectUri_Neg +INJIWEB-1629------Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_InvalidLocale_Neg +INJIWEB-1629------Mimoto_DownloadStayProtectedIssuerCredentialWithGoogleLogin_SunBirdC_SpaceInLocale_Neg +INJIWEB-1629------Mimoto_FetchAllCredentials_Empty_WalletId_And_Empty_Accepted_claims_Neg \ No newline at end of file diff --git a/api-test/testNgXmlFiles/mimotoSuite.xml b/api-test/testNgXmlFiles/mimotoSuite.xml index 405a6aa26..55169bf73 100644 --- a/api-test/testNgXmlFiles/mimotoSuite.xml +++ b/api-test/testNgXmlFiles/mimotoSuite.xml @@ -14,21 +14,23 @@ - + - + - + - + @@ -36,42 +38,46 @@ - - + + - + - + - + - - + + - + - - + + - + - + @@ -79,17 +85,19 @@ - - + + - + - + @@ -97,30 +105,34 @@ - + - + - + - + - + @@ -141,7 +153,8 @@ - + @@ -155,21 +168,217 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -182,16 +391,16 @@ - + - + + value="mimoto/GetIssuerConfiguration/GetIssuerConfiguration.yml" /> diff --git a/db_scripts/README.md b/db_scripts/README.md new file mode 100644 index 000000000..23fdf5e09 --- /dev/null +++ b/db_scripts/README.md @@ -0,0 +1,29 @@ +# Mimoto Database +Mimoto Database acts as a centralized data repository, ensuring that both Inji Mobile and Inji Web applications can access consistent and up-to-date information + +## Overview +This folder contains SQL scripts to create databases and tables in postgres. The table structure is described under the `/ddl/` folder. Default data populated in the tables can be found in the `/dml` folder. + +## Prerequisites +Before starting the installation, ensure the following: + +1. **Command line utilities**: + - `kubectl`: Kubernetes command-line tool. + - `helm`: Kubernetes package manager. + +2. **Helm Repositories**: + Add the required Helm repositories to your environment: + ```sh + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo add mosip https://mosip.github.io/mosip-helm +## Install in existing MOSIP K8 Cluster +These scripts are automatically run with below mentioned script in existing k8 cluster with Postgres installed. +### Install +* Set your kube_config file or kube_config variable on PC. +* Update `init_values.yaml` with db-common-password from the postgres namespace in the required field `dbUserPasswords.dbuserPassword` and ensure `databases.inji_mimoto` is enabled. + ``` + ./init_db.sh` + ``` + +## Install for developers +Developers may run the SQLs using `/deploy.sh` script. \ No newline at end of file diff --git a/db_scripts/copy_cm_func.sh b/db_scripts/copy_cm_func.sh new file mode 100755 index 000000000..691f1dbf5 --- /dev/null +++ b/db_scripts/copy_cm_func.sh @@ -0,0 +1,31 @@ +#!/bin/sh +# Copy configmap and secret from one namespace to another. +# ./copy_cm_func.sh [name] +# Parameters: +# resource: configmap|secret +# name: Optional new name of the configmap or secret in destination namespace. This may be needed if there is +# clash of names + +if [ $1 = "configmap" ] +then + RESOURCE=configmap +elif [ $1 = "secret" ] +then + RESOURCE=secret +else + echo "Incorrect resource $1. Exiting.." + exit 1 +fi + + +if [ $# -ge 5 ] +then + kubectl -n $4 delete --ignore-not-found=true $RESOURCE $5 + kubectl -n $3 get $RESOURCE $2 -o yaml | sed "s/namespace: $3/namespace: $4/g" | sed "s/name: $2/name: $5/g" | kubectl -n $4 create -f - +else + kubectl -n $4 delete --ignore-not-found=true $RESOURCE $2 + kubectl -n $3 get $RESOURCE $2 -o yaml | sed "s/namespace: $3/namespace: $4/g" | kubectl -n $4 create -f - +fi + + + diff --git a/db_scripts/init_db.sh b/db_scripts/init_db.sh new file mode 100755 index 000000000..410711b72 --- /dev/null +++ b/db_scripts/init_db.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Script to initialize mimoto DB. +## Usage: ./init_db.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +NS=injiweb +CHART_VERSION=0.0.1-develop + +helm repo add mosip https://mosip.github.io/mosip-helm +helm repo update + +while true; do + read -p "CAUTION: Do we already have Postgres installed? Also make sure the mimoto DB is backed up as the same will be overriden. Do you still want to continue?" yn + if [ $yn = "Y" ] + then + DB_USER_PASSWORD=$( kubectl -n postgres get secrets db-common-secrets -o jsonpath={.data.db-dbuser-password} | base64 -d ) + + kubectl create ns $NS + + echo Removing existing inji_mimoto DB installation + helm -n $NS delete postgres-init-mimoto + + echo Copy Postgres secrets + ./copy_cm_func.sh secret postgres-postgresql postgres $NS + + echo Delete existing DB common sets + kubectl -n $NS delete secret db-common-secrets + + echo Initializing DB + helm -n $NS install postgres-init-mimoto mosip/postgres-init -f init_values.yaml \ + --version $CHART_VERSION \ + --set dbUserPasswords.dbuserPassword="$DB_USER_PASSWORD" \ + --wait --wait-for-jobs + break + else + break + fi +done \ No newline at end of file diff --git a/db_scripts/init_values.yaml b/db_scripts/init_values.yaml new file mode 100644 index 000000000..0bcb34b17 --- /dev/null +++ b/db_scripts/init_values.yaml @@ -0,0 +1,15 @@ +dbUserPasswords: + dbuserPassword: "" + +databases: + inji_mimoto: + enabled: true + host: "postgres-postgresql.postgres" + port: 5432 + su: + user: postgres + secret: + name: postgres-postgresql + key: postgres-password + dml: 1 + branch: develop \ No newline at end of file diff --git a/db_scripts/inji_mimoto/db.sql b/db_scripts/inji_mimoto/db.sql new file mode 100644 index 000000000..477b47a16 --- /dev/null +++ b/db_scripts/inji_mimoto/db.sql @@ -0,0 +1,17 @@ +CREATE DATABASE inji_mimoto + ENCODING = 'UTF8' + LC_COLLATE = 'en_US.UTF-8' + LC_CTYPE = 'en_US.UTF-8' + TABLESPACE = pg_default + OWNER = postgres + TEMPLATE = template0; + +COMMENT ON DATABASE mosip_idp IS 'mimoto related data is stored in this database'; + +\c inji_mimoto postgres + +DROP SCHEMA IF EXISTS mimoto CASCADE; +CREATE SCHEMA mimoto; +ALTER SCHEMA mimoto OWNER TO postgres; +ALTER DATABASE inji_mimoto SET search_path TO mimoto,pg_catalog,public; + diff --git a/db_scripts/inji_mimoto/ddl.sql b/db_scripts/inji_mimoto/ddl.sql new file mode 100644 index 000000000..8464f8043 --- /dev/null +++ b/db_scripts/inji_mimoto/ddl.sql @@ -0,0 +1,10 @@ +\c inji_mimoto + +\ir ddl/mimoto-key_alias.sql +\ir ddl/mimoto-key_policy_def.sql +\ir ddl/mimoto-key_store.sql +\ir ddl/mimoto-ca_cert_store.sql +\ir ddl/mimoto-user_metadata.sql +\ir ddl/mimoto-wallet.sql +\ir ddl/mimoto-proof_signing_keys.sql +\ir ddl/mimoto-verifiable_credentials.sql diff --git a/db_scripts/inji_mimoto/ddl/mimoto-ca_cert_store.sql b/db_scripts/inji_mimoto/ddl/mimoto-ca_cert_store.sql new file mode 100644 index 000000000..2c9fd9520 --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-ca_cert_store.sql @@ -0,0 +1,54 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : ca_cert_store +-- Purpose : Key Store table +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ +CREATE TABLE ca_cert_store( + cert_id character varying(36) NOT NULL, + cert_subject character varying(500) NOT NULL, + cert_issuer character varying(500) NOT NULL, + issuer_id character varying(36) NOT NULL, + cert_not_before timestamp, + cert_not_after timestamp, + crl_uri character varying(120), + cert_data character varying, + cert_thumbprint character varying(100), + cert_serial_no character varying(50), + partner_domain character varying(36), + cr_by character varying(256), + cr_dtimes timestamp, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + ca_cert_type character varying(25), + CONSTRAINT pk_cacs_id PRIMARY KEY (cert_id), + CONSTRAINT cert_thumbprint_unique UNIQUE (cert_thumbprint,partner_domain) + +); +COMMENT ON TABLE ca_cert_store IS 'Certificate Authority Certificate Store: Store details of all the certificate provided by certificate authority which will be used by MOSIP'; +COMMENT ON COLUMN ca_cert_store.cert_id IS 'Certificate ID: Unique ID (UUID) will be generated and assigned to the uploaded CA/Sub-CA certificate'; +COMMENT ON COLUMN ca_cert_store.cert_subject IS 'Certificate Subject: Subject DN of the certificate'; +COMMENT ON COLUMN ca_cert_store.cert_issuer IS 'Certificate Issuer: Issuer DN of the certificate'; +COMMENT ON COLUMN ca_cert_store.issuer_id IS 'Issuer UUID of the certificate. (Issuer certificate should be available in the DB)'; +COMMENT ON COLUMN ca_cert_store.cert_not_before IS 'Certificate Start Date: Certificate Interval - Validity Start Date & Time'; +COMMENT ON COLUMN ca_cert_store.cert_not_after IS 'Certificate Validity end Date: Certificate Interval - Validity End Date & Time'; +COMMENT ON COLUMN ca_cert_store.crl_uri IS 'CRL URL: CRL URI of the issuer.'; +COMMENT ON COLUMN ca_cert_store.cert_data IS 'Certificate Data: PEM Encoded actual certificate data.'; +COMMENT ON COLUMN ca_cert_store.cert_thumbprint IS 'Certificate Thumb Print: SHA1 generated certificate thumbprint.'; +COMMENT ON COLUMN ca_cert_store.cert_serial_no IS 'Certificate Serial No: Serial Number of the certificate.'; +COMMENT ON COLUMN ca_cert_store.partner_domain IS 'Partner Domain : To add Partner Domain in CA/Sub-CA certificate chain'; +COMMENT ON COLUMN ca_cert_store.cr_by IS 'Created By : ID or name of the user who create / insert record'; +COMMENT ON COLUMN ca_cert_store.cr_dtimes IS 'Created DateTimestamp : Date and Timestamp when the record is created/inserted'; +COMMENT ON COLUMN ca_cert_store.upd_by IS 'Updated By : ID or name of the user who update the record with new values'; +COMMENT ON COLUMN ca_cert_store.upd_dtimes IS 'Updated DateTimestamp : Date and Timestamp when any of the fields in the record is updated with new values.'; +COMMENT ON COLUMN ca_cert_store.is_deleted IS 'IS_Deleted : Flag to mark whether the record is Soft deleted.'; +COMMENT ON COLUMN ca_cert_store.del_dtimes IS 'Deleted DateTimestamp : Date and Timestamp when the record is soft deleted with is_deleted=TRUE'; +COMMENT ON COLUMN ca_cert_store.ca_cert_type IS 'CA Certificate Type : Indicates if the certificate is a ROOT or INTERMEDIATE CA certificate'; diff --git a/db_scripts/inji_mimoto/ddl/mimoto-key_alias.sql b/db_scripts/inji_mimoto/ddl/mimoto-key_alias.sql new file mode 100644 index 000000000..83254481f --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-key_alias.sql @@ -0,0 +1,47 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : key_alias +-- Purpose : Key Alias table +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ +CREATE TABLE key_alias( + id character varying(36) NOT NULL, + app_id character varying(36) NOT NULL, + ref_id character varying(128), + key_gen_dtimes timestamp, + key_expire_dtimes timestamp, + status_code character varying(36), + lang_code character varying(3), + cr_by character varying(256) NOT NULL, + cr_dtimes timestamp NOT NULL, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + cert_thumbprint character varying(100), + uni_ident character varying(50), + CONSTRAINT pk_keymals_id PRIMARY KEY (id), + CONSTRAINT uni_ident_const UNIQUE (uni_ident) +); + +COMMENT ON TABLE key_alias IS 'Contains key alias and metadata of all the keys used in MOSIP system.'; + +COMMENT ON COLUMN key_alias.id IS 'Unique identifier (UUID) used for referencing keys in key_store table and HSM'; +COMMENT ON COLUMN key_alias.app_id IS 'To reference a Module key'; +COMMENT ON COLUMN key_alias.ref_id IS 'To reference a Encryption key '; +COMMENT ON COLUMN key_alias.key_gen_dtimes IS 'Date and time when the key was generated.'; +COMMENT ON COLUMN key_alias.key_expire_dtimes IS 'Date and time when the key will be expired. This will be derived based on the configuration / policy defined in Key policy definition.'; +COMMENT ON COLUMN key_alias.status_code IS 'Status of the key, whether it is active or expired.'; +COMMENT ON COLUMN key_alias.lang_code IS 'For multilanguage implementation this attribute Refers master.language.code. The value of some of the attributes in current record is stored in this respective language. '; +COMMENT ON COLUMN key_alias.cr_by IS 'ID or name of the user who create / insert record'; +COMMENT ON COLUMN key_alias.cr_dtimes IS 'Date and Timestamp when the record is created/inserted'; +COMMENT ON COLUMN key_alias.upd_by IS 'ID or name of the user who update the record with new values'; +COMMENT ON COLUMN key_alias.upd_dtimes IS 'Date and Timestamp when any of the fields in the record is updated with new values.'; +COMMENT ON COLUMN key_alias.is_deleted IS 'Flag to mark whether the record is Soft deleted.'; +COMMENT ON COLUMN key_alias.del_dtimes IS 'Date and Timestamp when the record is soft deleted with is_deleted=TRUE'; \ No newline at end of file diff --git a/db_scripts/inji_mimoto/ddl/mimoto-key_policy_def.sql b/db_scripts/inji_mimoto/ddl/mimoto-key_policy_def.sql new file mode 100644 index 000000000..6fdd9d63a --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-key_policy_def.sql @@ -0,0 +1,36 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : key_policy_def +-- Purpose : Key Policy definition table +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ +CREATE TABLE key_policy_def( + app_id character varying(36) NOT NULL, + key_validity_duration smallint, + is_active boolean NOT NULL, + pre_expire_days smallint, + access_allowed character varying(1024), + cr_by character varying(256) NOT NULL, + cr_dtimes timestamp NOT NULL, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + CONSTRAINT pk_keypdef_id PRIMARY KEY (app_id) +); +COMMENT ON TABLE key_policy_def IS 'Key Policy Defination: Policy related to encryption key management is defined here. For eg. Expiry duration of a key generated.'; +COMMENT ON COLUMN key_policy_def.app_id IS 'Application ID: Application id for which the key policy is defined'; +COMMENT ON COLUMN key_policy_def.key_validity_duration IS 'Key Validity Duration: Duration for which key is valid'; +COMMENT ON COLUMN key_policy_def.is_active IS 'IS_Active : Flag to mark whether the record is Active or In-active'; +COMMENT ON COLUMN key_policy_def.cr_by IS 'Created By : ID or name of the user who create / insert record'; +COMMENT ON COLUMN key_policy_def.cr_dtimes IS 'Created DateTimestamp : Date and Timestamp when the record is created/inserted'; +COMMENT ON COLUMN key_policy_def.upd_by IS 'Updated By : ID or name of the user who update the record with new values'; +COMMENT ON COLUMN key_policy_def.upd_dtimes IS 'Updated DateTimestamp : Date and Timestamp when any of the fields in the record is updated with new values.'; +COMMENT ON COLUMN key_policy_def.is_deleted IS 'IS_Deleted : Flag to mark whether the record is Soft deleted.'; +COMMENT ON COLUMN key_policy_def.del_dtimes IS 'Deleted DateTimestamp : Date and Timestamp when the record is soft deleted with is_deleted=TRUE'; \ No newline at end of file diff --git a/db_scripts/inji_mimoto/ddl/mimoto-key_store.sql b/db_scripts/inji_mimoto/ddl/mimoto-key_store.sql new file mode 100644 index 000000000..654ff760c --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-key_store.sql @@ -0,0 +1,37 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : key_store +-- Purpose : Key Store table +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ +CREATE TABLE key_store( + id character varying(36) NOT NULL, + master_key character varying(36) NOT NULL, + private_key character varying(2500) NOT NULL, + certificate_data character varying NOT NULL, + cr_by character varying(256) NOT NULL, + cr_dtimes timestamp NOT NULL, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + CONSTRAINT pk_keystr_id PRIMARY KEY (id) +); + +COMMENT ON TABLE key_store IS 'Stores Encryption (Base) private keys along with certificates'; +COMMENT ON COLUMN key_store.id IS 'Unique identifier (UUID) for referencing keys'; +COMMENT ON COLUMN key_store.master_key IS 'UUID of the master key used to encrypt this key'; +COMMENT ON COLUMN key_store.private_key IS 'Encrypted private key'; +COMMENT ON COLUMN key_store.certificate_data IS 'X.509 encoded certificate data'; +COMMENT ON COLUMN key_store.cr_by IS 'ID or name of the user who create / insert record'; +COMMENT ON COLUMN key_store.cr_dtimes IS 'Date and Timestamp when the record is created/inserted'; +COMMENT ON COLUMN key_store.upd_by IS 'ID or name of the user who update the record with new values'; +COMMENT ON COLUMN key_store.upd_dtimes IS 'Date and Timestamp when any of the fields in the record is updated with new values.'; +COMMENT ON COLUMN key_store.is_deleted IS 'Flag to mark whether the record is Soft deleted.'; +COMMENT ON COLUMN key_store.del_dtimes IS 'Date and Timestamp when the record is soft deleted with is_deleted=TRUE'; \ No newline at end of file diff --git a/db_scripts/inji_mimoto/ddl/mimoto-proof_signing_keys.sql b/db_scripts/inji_mimoto/ddl/mimoto-proof_signing_keys.sql new file mode 100644 index 000000000..3ae51b065 --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-proof_signing_keys.sql @@ -0,0 +1,30 @@ +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : proof_signing_key +-- Purpose : Stores wallet key-related information, including encrypted secret keys and metadata +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- 2025-03-27 User Initial table creation, referencing wallet for proof_signing_key encryption +-- ------------------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS proof_signing_key ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + wallet_id character varying(36) NOT NULL, -- Foreign key referencing the wallet table + public_key TEXT NOT NULL, -- Public key for wallet + secret_key TEXT NOT NULL, -- Secret key, encrypted using proof_signing_key + key_metadata JSONB NOT NULL, -- Metadata about the public and private keys + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now(), -- Timestamp of last update (defaults to current time) + + CONSTRAINT fk_wallet_id FOREIGN KEY (wallet_id) REFERENCES wallet (id) ON DELETE CASCADE +); + +COMMENT ON TABLE proof_signing_key IS 'Wallet Keys: Contains information about the wallet keys, including encrypted keys and metadata'; +COMMENT ON COLUMN proof_signing_key.id IS 'Primary Key: Unique identifier for the key'; +COMMENT ON COLUMN proof_signing_key.wallet_id IS 'Wallet ID: Foreign key referring to the wallet table'; +COMMENT ON COLUMN proof_signing_key.public_key IS 'Public Key: The public key of the wallet'; +COMMENT ON COLUMN proof_signing_key.secret_key IS 'Secret Key: Encrypted using the proof_signing_key from wallet table'; +COMMENT ON COLUMN proof_signing_key.key_metadata IS 'Key Metadata: Contains additional information about the public and private keys'; +COMMENT ON COLUMN proof_signing_key.created_at IS 'Created At: The date and time when the key information was created'; +COMMENT ON COLUMN proof_signing_key.updated_at IS 'Updated At: The date and time when the key information was last updated'; \ No newline at end of file diff --git a/db_scripts/inji_mimoto/ddl/mimoto-user_metadata.sql b/db_scripts/inji_mimoto/ddl/mimoto-user_metadata.sql new file mode 100644 index 000000000..334d39ed4 --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-user_metadata.sql @@ -0,0 +1,33 @@ +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : user_metadata +-- Purpose : User Metadata table +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ + +CREATE TABLE IF NOT EXISTS user_metadata ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + provider_subject_id character varying(255) NOT NULL, -- Unique identifier for the provider subject + identity_provider character varying(255) NOT NULL, -- Unique identifier for the identity provider + display_name TEXT NOT NULL, -- Display name of the user + profile_picture_url TEXT, -- URL of the user's profile picture + phone_number TEXT, -- Phone number of the user + email TEXT NOT NULL, -- Email of the user (Required field) + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now() -- Timestamp of last update (defaults to current time) +); + +COMMENT ON TABLE user_metadata IS 'User Metadata: Contains details about the user such as identity provider, contact details, and display name'; + +COMMENT ON COLUMN user_metadata.id IS 'Primary Key: Unique identifier for the user metadata'; +COMMENT ON COLUMN user_metadata.provider_subject_id IS 'Provider Subject ID: Unique identifier for the subject assigned by the identity provider'; +COMMENT ON COLUMN user_metadata.identity_provider IS 'Identity Provider: The identity provider associated with the user'; +COMMENT ON COLUMN user_metadata.display_name IS 'Display Name: Name of the user'; +COMMENT ON COLUMN user_metadata.profile_picture_url IS 'Profile Picture URL: The URL link to the user''s profile picture'; +COMMENT ON COLUMN user_metadata.phone_number IS 'Phone Number: User''s phone number, if available'; +COMMENT ON COLUMN user_metadata.email IS 'Email: User''s email address'; +COMMENT ON COLUMN user_metadata.created_at IS 'Created At: The date and time when the metadata was created'; +COMMENT ON COLUMN user_metadata.updated_at IS 'Updated At: The date and time when the metadata was last updated'; diff --git a/db_scripts/inji_mimoto/ddl/mimoto-verifiable_credentials.sql b/db_scripts/inji_mimoto/ddl/mimoto-verifiable_credentials.sql new file mode 100644 index 000000000..7535addd6 --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-verifiable_credentials.sql @@ -0,0 +1,27 @@ +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : verifiable_credentials +-- Purpose : Stores verifiable credentials related to the user, encrypted with wallet_key +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS verifiable_credentials ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + wallet_id character varying(36) NOT NULL, -- Foreign key referring to the wallet table (wallet.id) + credential TEXT NOT NULL, -- Encrypted credential (using wallet_key for encryption/decryption) + credential_metadata JSONB NOT NULL, -- Metadata about the credential + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now(), -- Timestamp of last update (defaults to current time) + + CONSTRAINT fk_wallet_id FOREIGN KEY (wallet_id) REFERENCES wallet (id) ON DELETE CASCADE +); + +COMMENT ON TABLE verifiable_credentials IS 'Verifiable Credentials: Contains user credentials, encrypted using wallet key'; +COMMENT ON COLUMN verifiable_credentials.id IS 'Primary Key: Unique identifier for the verifiable credential record'; +COMMENT ON COLUMN verifiable_credentials.wallet_id IS 'Wallet ID: Foreign key referring to the wallet table, linked to the user''s wallet'; +COMMENT ON COLUMN verifiable_credentials.credential IS 'Credential: Encrypted credential using the wallet''s key'; +COMMENT ON COLUMN verifiable_credentials.credential_metadata IS 'Credential Metadata: Additional information about the credential (e.g., issuer, claims)'; +COMMENT ON COLUMN verifiable_credentials.created_at IS 'Created At: The date and time when the credential was created'; +COMMENT ON COLUMN verifiable_credentials.updated_at IS 'Updated At: The date and time when the credential was last updated'; diff --git a/db_scripts/inji_mimoto/ddl/mimoto-wallet.sql b/db_scripts/inji_mimoto/ddl/mimoto-wallet.sql new file mode 100644 index 000000000..f827bcd98 --- /dev/null +++ b/db_scripts/inji_mimoto/ddl/mimoto-wallet.sql @@ -0,0 +1,27 @@ +-- ------------------------------------------------------------------------------------------------- +-- Database Name: inji_mimoto +-- Table Name : wallet +-- Purpose : Wallet Information table for user, encrypted with AES256-GCM, including key metadata +-- +-- +-- Modified Date Modified By Comments / Remarks +-- ------------------------------------------------------------------------------------------ +-- ------------------------------------------------------------------------------------------ +CREATE TABLE IF NOT EXISTS wallet ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + user_id character varying(36) NOT NULL, -- Foreign key referencing user_metadata + wallet_key TEXT NOT NULL, -- Encrypted wallet key (retained here) + wallet_metadata JSONB NOT NULL, -- Metadata about the wallet, including encryption info + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now(), -- Timestamp of last update (defaults to current time) + + CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES user_metadata (id) ON DELETE CASCADE +); + +COMMENT ON TABLE wallet IS 'Wallet: Contains general information about the user''s wallet and metadata'; +COMMENT ON COLUMN wallet.id IS 'Primary Key: Unique identifier for the wallet'; +COMMENT ON COLUMN wallet.user_id IS 'User ID: Foreign key referring to the user_metadata table'; +COMMENT ON COLUMN wallet.wallet_key IS 'Wallet Key: Encrypted wallet key used to encrypt secret keys in wallet_keys'; +COMMENT ON COLUMN wallet.wallet_metadata IS 'Wallet Metadata: Contains information about the wallet, including encryption and PIN usage'; +COMMENT ON COLUMN wallet.created_at IS 'Created At: The date and time when the wallet was created'; +COMMENT ON COLUMN wallet.updated_at IS 'Updated At: The date and time when the wallet was last updated'; \ No newline at end of file diff --git a/db_scripts/inji_mimoto/deploy.properties b/db_scripts/inji_mimoto/deploy.properties new file mode 100644 index 000000000..1c60735db --- /dev/null +++ b/db_scripts/inji_mimoto/deploy.properties @@ -0,0 +1,6 @@ +DB_SERVERIP= +DB_PORT=5432 +SU_USER=postgres +DEFAULT_DB_NAME=postgres +MOSIP_DB_NAME=inji_mimoto +DML_FLAG=1 \ No newline at end of file diff --git a/db_scripts/inji_mimoto/deploy.sh b/db_scripts/inji_mimoto/deploy.sh new file mode 100755 index 000000000..e15eecbef --- /dev/null +++ b/db_scripts/inji_mimoto/deploy.sh @@ -0,0 +1,43 @@ + +## Properties file +set -e +properties_file="$1" +echo `date "+%m/%d/%Y %H:%M:%S"` ": $properties_file" +if [ -f "$properties_file" ] +then + echo `date "+%m/%d/%Y %H:%M:%S"` ": Property file \"$properties_file\" found." + while IFS='=' read -r key value || [ -n "$key" ] + do + key=$(echo $key | tr '.' '_') + eval ${key}=\${value} + done < "$properties_file" +else + echo `date "+%m/%d/%Y %H:%M:%S"` ": Property file not found, Pass property file name as argument." +fi + +## Terminate existing connections +echo "Terminating active connections" +CONN=$(PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -t -c "SELECT count(pg_terminate_backend(pg_stat_activity.pid)) FROM pg_stat_activity WHERE datname = '$MOSIP_DB_NAME' AND pid <> pg_backend_pid()";exit;) +echo "Terminated connections" + +## Drop db and role +PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -f drop_db.sql +PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -f drop_role.sql + +## Create users +echo `date "+%m/%d/%Y %H:%M:%S"` ": Creating database users" | tee +PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -f role_dbuser.sql -v dbuserpwd=\'$DBUSER_PWD\' + +## Create DB +PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -f db.sql +PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -f ddl.sql + +## Grants +PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -f grants.sql + +## Populate tables +if [ ${DML_FLAG} == 1 ] +then + echo `date "+%m/%d/%Y %H:%M:%S"` ": Deploying DML for ${MOSIP_DB_NAME} database" + PGPASSWORD=$SU_USER_PWD psql -v ON_ERROR_STOP=1 --username=$SU_USER --host=$DB_SERVERIP --port=$DB_PORT --dbname=$DEFAULT_DB_NAME -a -b -f dml.sql +fi \ No newline at end of file diff --git a/db_scripts/inji_mimoto/dml.sql b/db_scripts/inji_mimoto/dml.sql new file mode 100644 index 000000000..77f39b405 --- /dev/null +++ b/db_scripts/inji_mimoto/dml.sql @@ -0,0 +1,23 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- + +\c inji_mimoto + +\COPY mimoto.key_policy_def (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) FROM './dml/mimoto-key_policy_def.csv' delimiter ',' HEADER csv; + + + + + + + + + + + + + + + diff --git a/db_scripts/inji_mimoto/dml/mimoto-key_policy_def.csv b/db_scripts/inji_mimoto/dml/mimoto-key_policy_def.csv new file mode 100644 index 000000000..88de66fa0 --- /dev/null +++ b/db_scripts/inji_mimoto/dml/mimoto-key_policy_def.csv @@ -0,0 +1,4 @@ +app_id,key_validity_duration,pre_expire_days,access_allowed,is_active,cr_by,cr_dtimes +ROOT,2920,1125,NA,TRUE,mosipadmin,now() +MIMOTO,1095,60,NA,TRUE,mosipadmin,now() +BASE,730,60,NA,TRUE,mosipadmin,now() diff --git a/db_scripts/inji_mimoto/drop_db.sql b/db_scripts/inji_mimoto/drop_db.sql new file mode 100644 index 000000000..73d434700 --- /dev/null +++ b/db_scripts/inji_mimoto/drop_db.sql @@ -0,0 +1,7 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- + +DROP DATABASE IF EXISTS inji_mimoto; + diff --git a/db_scripts/inji_mimoto/drop_role.sql b/db_scripts/inji_mimoto/drop_role.sql new file mode 100644 index 000000000..4b2e8776a --- /dev/null +++ b/db_scripts/inji_mimoto/drop_role.sql @@ -0,0 +1,6 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- + +drop role if exists mimotouser; diff --git a/db_scripts/inji_mimoto/grants.sql b/db_scripts/inji_mimoto/grants.sql new file mode 100644 index 000000000..b9d52fa56 --- /dev/null +++ b/db_scripts/inji_mimoto/grants.sql @@ -0,0 +1,22 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- + +\c inji_mimoto + +GRANT CONNECT + ON DATABASE inji_mimoto + TO mimotouser; + +GRANT USAGE + ON SCHEMA mimoto + TO mimotouser; + +GRANT SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES + ON ALL TABLES IN SCHEMA mimoto + TO mimotouser; + +ALTER DEFAULT PRIVILEGES IN SCHEMA mimoto + GRANT SELECT,INSERT,UPDATE,DELETE,REFERENCES ON TABLES TO mimotouser; + diff --git a/db_scripts/inji_mimoto/role_dbuser.sql b/db_scripts/inji_mimoto/role_dbuser.sql new file mode 100644 index 000000000..6093042c5 --- /dev/null +++ b/db_scripts/inji_mimoto/role_dbuser.sql @@ -0,0 +1,9 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this +-- file, You can obtain one at https://mozilla.org/MPL/2.0/. +-- ------------------------------------------------------------------------------------------------- + +CREATE ROLE mimotouser WITH + INHERIT + LOGIN + PASSWORD :dbuserpwd; diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 000000000..5678bb215 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,32 @@ + +# Install Onboarder + +* Execute redis install script +``` +cd redis +./install.sh +``` +* Execute Onboarder install script +``` +cd ../partner-onboarder +./install.sh + ``` +* During the execution of the `install.sh` script, a prompt appears requesting information for the S3 bucket, including its name and URL. +* Once the job is completed, log in to S3 / NFS and check the reports. There should not be any failures. +* Note: If you are running the Onboarder in a separate INJI cluster, update the extraEnvVars section accordingly in [values.yaml](../partner-onboarder/values.yaml). + +# Install mimoto +* Execute mimoto install script +* Before installing Mimoto, please ensure that the database host and port are correctly configured in the [values.yaml](mimoto/values.yaml) file. +``` +cd mimoto +./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. + +## For Onboarding new Issuer for VCI: + +- create a folder "certs" in the root and a file "oidckeystore.p12" inside certs and store the keys as different aliases for every issuers. for more details refer [here](https://docs.inji.io/inji-wallet/inji-mobile/technical-overview/customization-overview/credential_providers) diff --git a/deploy/config-server/README.md b/deploy/config-server/README.md deleted file mode 100755 index a6ced3571..000000000 --- a/deploy/config-server/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Config server - -## Introduction -Config server serves all properties required by Inji modules. This must be installed before any other Inji modules. - -## Install -* Review `values.yaml` and make sure git repository parameters are as per your installation. -* Install -```sh -./install.sh -``` \ No newline at end of file diff --git a/deploy/config-server/install.sh b/deploy/config-server/install.sh deleted file mode 100755 index cbcd9feb4..000000000 --- a/deploy/config-server/install.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash -# Installs config-server -## Usage: ./install.sh [kubeconfig] - -if [ $# -ge 1 ] ; then - export KUBECONFIG=$1 -fi - -NS=config-server -CHART_VERSION=0.0.1-develop - - echo Create $NS namespace - kubectl create ns $NS - - # 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 - - echo Istio label - kubectl label ns $NS istio-injection=enabled --overwrite - helm repo update - - echo Copy configmaps - COPY_UTIL=../copy_cm_func.sh - $COPY_UTIL configmap global default $NS - - echo Copy secrets - $COPY_UTIL secret db-common-secrets postgres $NS - $COPY_UTIL secret conf-secrets-various conf-secrets $NS - - echo Installing config-server - helm -n $NS install config-server mosip/config-server -f values.yaml --wait --version $CHART_VERSION - echo Installed config-server. diff --git a/deploy/config-server/restart.sh b/deploy/config-server/restart.sh deleted file mode 100755 index 1c5d585a4..000000000 --- a/deploy/config-server/restart.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -# Restart the config-server service -## Usage: ./restart.sh [kubeconfig] - -if [ $# -ge 1 ] ; then - export KUBECONFIG=$1 -fi - -function config_server() { - NS=config-server - kubectl -n $NS rollout restart deploy - - kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status - - echo Restarted config-server services - 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 -config_server # calling function \ No newline at end of file diff --git a/deploy/config-server/values.yaml b/deploy/config-server/values.yaml deleted file mode 100755 index 50c6c4bec..000000000 --- a/deploy/config-server/values.yaml +++ /dev/null @@ -1,126 +0,0 @@ -gitRepo: - uri: https://github.com/mosip/inji-config - version: develop - ## Folders within the base repo where properties may be found. - searchFolders: "" - private: false - ## User name of user who has access to the private repo. Ignore for public repo - username: "" - token: "" - -envVariables: - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_API_PUBLIC_HOST - valueFrom: - configMapKeyRef: - name: global - key: mosip-api-host - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_API_INTERNAL_HOST - valueFrom: - configMapKeyRef: - name: global - key: mosip-api-internal-host - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_PARTNER_CRYPTO_P12_PASSWORD - valueFrom: - secretKeyRef: - key: mosip-partner-crypto-p12-password - name: conf-secrets-various - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MPARTNER_DEFAULT_MOBILE_SECRET - valueFrom: - secretKeyRef: - key: mpartner_default_mobile_secret - name: keycloak-client-secrets - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_KEYCLOAK_INTERNAL_URL - valueFrom: - configMapKeyRef: - name: keycloak-host - key: keycloak-internal-url - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_KEYCLOAK_EXTERNAL_URL - valueFrom: - configMapKeyRef: - name: keycloak-host - key: keycloak-external-url - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_KEYCLOAK_INTERNAL_HOST - valueFrom: - configMapKeyRef: - name: keycloak-host - key: keycloak-internal-host - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_KEYCLOAK_EXTERNAL_HOST - valueFrom: - configMapKeyRef: - name: keycloak-host - key: keycloak-external-host - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_DB_DBUSER_PASSWORD - valueFrom: - secretKeyRef: - name: db-common-secrets - key: db-dbuser-password - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_S3_ACCESSKEY - valueFrom: - configMapKeyRef: - name: s3 - key: s3-user-key - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_S3_REGION - valueFrom: - configMapKeyRef: - name: s3 - key: s3-region - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_S3_SECRETKEY - valueFrom: - secretKeyRef: - name: s3 - key: s3-user-secret - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_ESIGNET_HOST - valueFrom: - configMapKeyRef: - key: mosip-esignet-host - name: global - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_ESIGNET_INSURANCE_HOST - valueFrom: - configMapKeyRef: - key: mosip-esignet-insurance-host - name: global - enabled: true - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_INJI_DATASHARE_HOST - valueFrom: - configMapKeyRef: - key: mosip-inji-datashare-host - name: global - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_INJIWEB_HOST - valueFrom: - configMapKeyRef: - key: mosip-injiweb-host - name: global - - - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_INJIVERIFY_HOST - valueFrom: - configMapKeyRef: - key: mosip-injiverify-host - name: global diff --git a/deploy/mimoto-apitestrig/install.sh b/deploy/mimoto-apitestrig/install.sh index 4ef967344..46adb0f4c 100755 --- a/deploy/mimoto-apitestrig/install.sh +++ b/deploy/mimoto-apitestrig/install.sh @@ -7,19 +7,17 @@ if [ $# -ge 1 ] ; then fi NS=injiweb -CHART_VERSION=12.0.1 +CHART_VERSION=0.0.1-develop 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=../copy_cm_func.sh - $COPY_UTIL configmap global default $NS + $COPY_UTIL configmap inji-stack-config 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 @@ -34,9 +32,9 @@ function installing_apitestrig() { 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}') + DB_HOST=$( kubectl -n default get cm inji-stack-config -o json |jq -r '.data."api-internal-host"' ) + API_INTERNAL_HOST=$( kubectl -n default get cm inji-stack-config -o json |jq -r '.data."api-internal-host"' ) + ENV_USER=$( kubectl -n default get cm inji-stack-config -o json |jq -r '.data."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 diff --git a/deploy/mimoto/README.md b/deploy/mimoto/README.md index 360296351..f8a4a0515 100644 --- a/deploy/mimoto/README.md +++ b/deploy/mimoto/README.md @@ -8,4 +8,12 @@ Helm chart for installing MOSIP mimoto Application. $ helm repo add mosip https://mosip.github.io $ helm install my-release mosip/mimoto ``` - +```console +./install.sh +``` +```console +./delete.sh +``` +```console +./restart.sh +``` \ No newline at end of file diff --git a/deploy/mimoto/install.sh b/deploy/mimoto/install.sh index 8fa1f30f5..3607d3211 100755 --- a/deploy/mimoto/install.sh +++ b/deploy/mimoto/install.sh @@ -7,7 +7,10 @@ if [ $# -ge 1 ] ; then fi NS=injiweb -MIMOTO_CHART_VERSION=0.0.1-develop +CHART_VERSION=0.0.1-develop +KEYGEN_CHART_VERSION=1.3.0-beta.2 +SOFTHSM_NS=softhsm +SOFTHSM_CHART_VERSION=1.3.0-beta.2 echo Create $NS namespace kubectl create ns $NS @@ -20,12 +23,14 @@ function installing_mimoto() { echo Copy Configmaps COPY_UTIL=../copy_cm_func.sh - $COPY_UTIL configmap global default $NS + $COPY_UTIL configmap inji-stack-config default $NS $COPY_UTIL configmap artifactory-share artifactory $NS $COPY_UTIL configmap config-server-share config-server $NS + $COPY_UTIL configmap redis-config redis $NS echo Copy Secrets - $COPY_UTIL secret keycloak-client-secrets keycloak $NS + $COPY_UTIL secret redis redis $NS + $COPY_UTIL secret db-common-secrets postgres $NS echo "Do you have public domain & valid SSL? (Y/n) " echo "Y: if you have public domain & valid ssl certificate" @@ -33,7 +38,7 @@ function installing_mimoto() { read -p "" flag if [ -z "$flag" ]; then - echo "'flag' was provided; EXITING;" + echo "'flag' was not provided; EXITING;" exit 1; fi ENABLE_INSECURE='' @@ -41,6 +46,66 @@ function installing_mimoto() { ENABLE_INSECURE='--set enable_insecure=true'; fi + echo "Do you want to use SoftHSM or .p12 keystore for key management?" + echo "1) SoftHSM" + echo "2) .p12 keystore" + echo "3) External HSM" + read -p "Enter 1, 2 or 3 [default: 2]: " keystore_choice + keystore_choice=${keystore_choice:-2} + + if [[ "$keystore_choice" == "1" ]]; then + echo Installing SoftHSM + helm -n $SOFTHSM_NS install softhsm-mimoto mosip/softhsm -f softhsm-values.yaml --version $SOFTHSM_CHART_VERSION --wait + + echo Copy SoftHSM configMap and Secret to $NS and config-server + $COPY_UTIL configmap softhsm-mimoto-share softhsm $NS + $COPY_UTIL secret softhsm-mimoto softhsm config-server + $COPY_UTIL secret softhsm-mimoto softhsm $NS + kubectl -n config-server set env --keys=security-pin --from secret/softhsm-mimoto deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SOFTHSM_MIMOTO_ + + elif [[ "$keystore_choice" == "2" ]]; then + volume_size=200M + volume_mount_path='/home/mosip/encryption' + PVC_CLAIM_NAME='mimoto-keygen-keymanager' + MIMOTO_KEYGEN_HELM_ARGS='--set springConfigNameEnv="mimoto"' + MIMOTO_HELM_ARGS='' + + if kubectl -n $NS get pvc "$PVC_CLAIM_NAME" >/dev/null 2>&1; then + echo "PVC $PVC_CLAIM_NAME already exists. Skipping keygen job." + else + echo "Creating new PVC and running keygen job" + MIMOTO_KEYGEN_HELM_ARGS="--set persistence.enabled=true \ + --set volumePermissions.enabled=true \ + --set persistence.size=$volume_size \ + --set persistence.mountDir=\"$volume_mount_path\" \ + --set springConfigNameEnv='mimoto' \ + --set persistence.pvc_claim_name=\"$PVC_CLAIM_NAME\" \ + --set keysDir=\"$volume_mount_path\" \ + --set keyStore.fileName=\"encryptionkeystore.p12\" \ + --set skipIfFileExists=true" + + echo "MIMOTO KEYGEN HELM ARGS $MIMOTO_KEYGEN_HELM_ARGS" + echo "Running mimoto keygen" + helm -n $NS install mimoto-keygen mosip/keygen -f keygen-mimoto.yaml $MIMOTO_KEYGEN_HELM_ARGS --wait --wait-for-jobs --version $KEYGEN_CHART_VERSION + fi + + elif [[ "$keystore_choice" == "3" ]]; then + echo "Setting up External HSM configuration" + read -p "Please provide the URL where externalhsm client zip is located: " externalhsmclient + read -p "Please provide the host URL for externalhsm: " externalhsmhosturl + read -p "Please provide the password for the externalhsm: " externalhsmpassword + + kubectl create configmap softhsm-mimoto-share --from-literal=hsm_client_zip_url_env="$externalhsmclient" --from-literal=PKCS11_PROXY_SOCKET="$externalhsmhosturl" -n softhsm --dry-run=client -o yaml | kubectl apply -f - + kubectl create secret generic softhsm-mimoto --from-literal=security-pin="$externalhsmpassword" -n softhsm --dry-run=client -o yaml | kubectl apply -f - + $COPY_UTIL configmap softhsm-mimoto-share softhsm $NS + $COPY_UTIL secret softhsm-mimoto softhsm config-server + $COPY_UTIL secret softhsm-mimoto softhsm $NS + kubectl -n config-server set env --keys=security-pin --from secret/softhsm-mimoto deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SOFTHSM_MIMOTO_ + else + echo "Invalid option. Please choose 1, 2, or 3." + exit 1 + fi + echo "Copy secrets to config-server namespace" ../copy_cm_func.sh secret mimoto-wallet-binding-partner-api-key injiweb config-server ../copy_cm_func.sh secret mimoto-oidc-partner-clientid injiweb config-server @@ -52,11 +117,27 @@ function installing_mimoto() { kubectl -n config-server set env --keys=mimoto-oidc-partner-clientid --from secret/mimoto-oidc-partner-clientid deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_ kubectl -n config-server set env --keys=mimoto-oidc-keystore-password --from secret/mimoto-oidc-keystore-password deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_ - kubectl -n config-server rollout restart deployment config-server - kubectl -n config-server rollout status deployment config-server + kubectl -n config-server get deploy -o name | xargs -n1 -t kubectl -n config-server rollout status + + echo "Please proceed with generating the Google Client ID and Secret Key required for integration." + read -p "Please enter the Google Client ID: " clientId + + if [ -z "$clientId" ]; then + echo "'clientId' was not provided; EXITING;" + exit 1; + fi + read -p "Please enter the Google Secret Key: " secretKey + + if [ -z "$secretKey" ]; then + echo "'secretKey' was not provided; EXITING;" + exit 1; + fi + echo Installing mimoto - helm -n $NS install mimoto mosip/mimoto --version $MIMOTO_CHART_VERSION $ENABLE_INSECURE + helm -n $NS install mimoto mosip/mimoto --version $CHART_VERSION -f values.yaml $ENABLE_INSECURE \ + --set mimoto.secrets.google-client.MOSIP_INJIWEB_GOOGLE_CLIENT_ID="$clientId" \ + --set mimoto.secrets.google-client.MOSIP_INJIWEB_GOOGLE_CLIENT_SECRET="$secretKey" kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status diff --git a/deploy/mimoto/keygen-mimoto.yaml b/deploy/mimoto/keygen-mimoto.yaml new file mode 100644 index 000000000..ceb6fe335 --- /dev/null +++ b/deploy/mimoto/keygen-mimoto.yaml @@ -0,0 +1,10 @@ +extraEnvVars: + - name: DB_DBUSER_PASSWORD + valueFrom: + secretKeyRef: + name: db-common-secrets + key: db-dbuser-password + - name: MOSIP_MIMOTO_DATABASE_HOSTNAME + value: postgres-postresql.postgres + - name: MOSIP_MIMOTO_DATABASE_PORT + value: "5432" diff --git a/deploy/mimoto/softhsm-values.yaml b/deploy/mimoto/softhsm-values.yaml new file mode 100644 index 000000000..581bbd139 --- /dev/null +++ b/deploy/mimoto/softhsm-values.yaml @@ -0,0 +1,7 @@ +resources: + limits: {} + # cpu: 250m + # memory: 1Gi + requests: + cpu: 100m + memory: 20Mi diff --git a/deploy/mimoto/values.yaml b/deploy/mimoto/values.yaml new file mode 100644 index 000000000..ffab8e700 --- /dev/null +++ b/deploy/mimoto/values.yaml @@ -0,0 +1,25 @@ +extraEnvVars: + - name: REDIS_HOST + valueFrom: + configMapKeyRef: + name: redis-config + key: redis-host + - name: REDIS_PORT + valueFrom: + configMapKeyRef: + name: redis-config + key: redis-port + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis + key: redis-password + - name: DB_DBUSER_PASSWORD + valueFrom: + secretKeyRef: + name: db-common-secrets + key: db-dbuser-password + - name: MOSIP_MIMOTO_DATABASE_HOSTNAME + value: postgres-postgresql.postgres + - name: MOSIP_MIMOTO_DATABASE_PORT + value: "5432" diff --git a/deploy/redis/README.md b/deploy/redis/README.md new file mode 100644 index 000000000..74c4649e5 --- /dev/null +++ b/deploy/redis/README.md @@ -0,0 +1,13 @@ +# Redis + +* Redis is an open-source, in-memory data store widely used by developers as a database, cache, streaming engine, and message broker. +* In Mimoto, Redis is utilized as a cache store to enhance performance and speed up data retrieval. + +## Install +``` +$ ./install.sh +``` +## Delete +``` +$ ./delete.sh +``` diff --git a/deploy/config-server/delete.sh b/deploy/redis/delete.sh similarity index 59% rename from deploy/config-server/delete.sh rename to deploy/redis/delete.sh index f10a7bd8b..79199627d 100755 --- a/deploy/config-server/delete.sh +++ b/deploy/redis/delete.sh @@ -1,20 +1,19 @@ #!/bin/bash -# Uninstalls config server +# Deleted Redis helm chart ## Usage: ./delete.sh [kubeconfig] if [ $# -ge 1 ] ; then export KUBECONFIG=$1 fi -function config_server() { - NS=config-server +function Deleting_redis() { + NS=redis while true; do - read -p "Are you sure you want to delete config-server helm charts?(Y/n) " yn + read -p "Are you sure you want to delete redis helm chart? Y/n ?" yn if [ $yn = "Y" ] then - kubectl -n $NS delete configmap global keycloak-host - kubectl -n $NS delete secret db-common-secrets keycloak keycloak-client-secrets - helm -n $NS delete config-server + helm -n $NS delete redis + echo Deleted Redis services. break else break @@ -29,4 +28,4 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true 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 -config_server # calling function +Deleting_redis # calling function diff --git a/deploy/redis/install.sh b/deploy/redis/install.sh new file mode 100755 index 000000000..9bf297b73 --- /dev/null +++ b/deploy/redis/install.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Installs redis helm chart +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function installing_redis() { + + while true; do + read -p "Do you want to install Redis in redis namespace? (y/n) :" ans + if [ "$ans" = "n" ] || [ "$ans" = "N" ]; then + read -p "Please confirm if redis-config is already present in redis namespace. (y/n):" response + if [ "$response" = "Y" ] || [ "$response" = "y" ]; then + echo "Skipping Redis installation as well as redis-config creation." + exit 1 + elif [ "$response" = "N" ] || [ "$response" = "n" ]; then + echo "Skipping Redis installation and continuing with redis-config creation." + kubectl apply -f redis-config.yaml + echo "redis-config configmap creation completed." + exit 1 + else + echo "Incorrect response. Please re-execute the redis installation script." + exit 1 + fi + elif [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then + break + else + echo "Please provide a correct option (Y or N)" + fi + done + + NS=redis + CHART_VERSION=17.3.14 + + echo Create $NS namespace + kubectl create ns $NS || true + + echo Istio label + kubectl label ns $NS istio-injection=enabled --overwrite + + echo Updating helm repos + helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo update + + echo Installing redis + helm -n $NS install redis bitnami/redis --wait --version $CHART_VERSION + + echo Installed redis service + + kubectl apply -f redis-config.yaml + + 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_redis # calling function diff --git a/deploy/redis/redis-config.yaml b/deploy/redis/redis-config.yaml new file mode 100644 index 000000000..c828fb86e --- /dev/null +++ b/deploy/redis/redis-config.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: redis-config + namespace: redis + labels: + app: redis +data: + redis-host: "redis-master-0.redis-headless.redis.svc.cluster.local" + redis-port: "6379" diff --git a/docker-compose/README.md b/docker-compose/README.md index b19ca73e8..524564c4e 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -11,19 +11,26 @@ This is the docker-compose setup to run mimoto which act as BFF for Inji mobile ## How to run this setup? -1. Add Id providers as an issuer in mimoto-issuers-config.json +1. Add Id providers as issuers in mimoto-issuers-config.json. For each provider, include the token_endpoint property, which should be an HTTPS URL. This can either be an exposed domain or, for local setups, an ngrok URL if you're using mimoto for local testing with the Inji mobile wallet. 2. Add verifiers clientId and redirect Uris in mimoto-trusted-verifiers.json for Online Sharing 3. Start esignet services and update esignet host references in mimoto-default.properties and mimoto-issuers-config.json -4. Start the data share services and update data share host references in mimoto-default.properties. data share service helm is available in the [Inji Web Helm](https://github.com/mosip/inji-web/tree/release-0.10.x/helm/inji-web) - -5. Create certs folder in the same directory and create OIDC client. Add key in oidckeystore.p12 and copy this file under certs folder. -Refer [here](https://docs.mosip.io/inji/inji-mobile-wallet/customization-overview/credential_providers) to create client +4. Create certs folder in the same directory and create OIDC client. Add key in oidckeystore.p12 and copy this file under certs folder. +Refer [here](https://docs.inji.io/inji-wallet/inji-mobile/technical-overview/customization-overview/credential_providers) to create client * Update client_id and client_alias as per onboarding in mimoto-issuers-config.json file. +* Update oidc_p12_password in docker-compose.yml to match the password set for the oidckeystore.p12 file. +5. Refer to the [How to create Google Client Credentials](#how-to-create-google-client-credentials) section to create + Google client credentials. + - Replace the placeholders in the `docker-compose.yml` file with the generated credentials: + + ```yaml + environment: + - GOOGLE_OAUTH_CLIENT_ID= + - GOOGLE_OAUTH_CLIENT_SECRET= -5. Start the docker-compose file +6. Start the docker-compose file > docker-compose up @@ -33,6 +40,48 @@ Refer [here](https://docs.mosip.io/inji/inji-mobile-wallet/customization-overvie * http://localhost:8099/v1/mimoto/issuers/StayProtected * http://localhost:8099/v1/mimoto/issuers/StayProtected/well-known-proxy +## How to create Google Client Credentials + +To enable Google OAuth2.0 authentication, follow these steps: + +1. **Go to the Google Cloud Console**: + - Visit [Google Cloud Console](https://console.cloud.google.com/). + +2. **Create a New Project**: + - If you don’t already have a project, create a new one by clicking on the project dropdown and selecting "New Project". + +3. **Enable the OAuth Consent Screen**: + - Navigate to "APIs & Services" > "OAuth consent screen". + - Select "External" for the user type and configure the required fields (e.g., app name, support email, etc.). + - Save the changes. +4. **Create OAuth 2.0 Credentials**: + - Navigate to "APIs & Services" > "Credentials". + - Click "Create Credentials" > "OAuth 2.0 Client IDs". + - Select "Web application" as the application type. + +5. **Configure Authorized JavaScript Origins**: + Depending on your environment, use the following values: + + - **Local or Docker**: + ``` + http://localhost:8099 + ``` + - **Deployed domain (e.g., collab.mosip.net)**: + ``` + https://collab.mosip.net + +6. **Configure Authorized Redirect URIs**: + - **Local or Docker**: + ``` + http://localhost:8099/v1/mimoto/oauth2/callback/google + ``` + - **Deployed domain (e.g., collab.mosip.net)**: + ``` + https://collab.mosip.net/v1/mimoto/oauth2/callback/google + ``` + +7. **Save and Retrieve Client Credentials**: + - After saving, you will receive a `Client ID` and `Client Secret`. Note: - Replace mosipbox.public.url, mosip.api.public.url with your public accessible domain. For dev or local env [ngrok](https://ngrok.com/docs/getting-started/) is recommended. diff --git a/docker-compose/config/mimoto-default.properties b/docker-compose/config/mimoto-default.properties index ed6f73cf4..9283d79f9 100644 --- a/docker-compose/config/mimoto-default.properties +++ b/docker-compose/config/mimoto-default.properties @@ -193,13 +193,47 @@ mosip.kernel.pin.length=6 #length of the token id mosip.kernel.tokenid.length=36 +#------------------------------------ Key-manager specific properties -------------------------------------------------- + +#Crypto asymmetric algorithm name +mosip.kernel.crypto.asymmetric-algorithm-name=RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING +#Crypto symmetric algorithm name +mosip.kernel.crypto.symmetric-algorithm-name=AES/GCM/NoPadding +#Encrypted data and encrypted symmetric key separator +mosip.kernel.data-key-splitter=#KEY_SPLITTER# +#GCM tag length +mosip.kernel.crypto.gcm-tag-length=128 +#Hash algo name +mosip.kernel.crypto.hash-algorithm-name=PBKDF2WithHmacSHA512 +#Symmtric key length used in hash +mosip.kernel.crypto.hash-symmetric-key-length=256 +#No of iterations in hash +mosip.kernel.crypto.hash-iteration=10000 +mosip.kernel.keygenerator.symmetric-algorithm-name=AES +mosip.kernel.keygenerator.symmetric-key-length=256 +#Asymmetric algorithm key length +mosip.kernel.keygenerator.asymmetric-key-length=2048 +#Keygenerator asymmetric algorithm name +mosip.kernel.keygenerator.asymmetric-algorithm-name=RSA +mosip.kernel.keymanager.hsm.config-path=${SPRING_CONFIG_LOCATION}certs/oidckeystore.p12 +mosip.kernel.keymanager.hsm.keystore-type=PKCS12 +mosip.kernel.keymanager.hsm.keystore-pass=${oidc_p12_password} + + +mosip.kernel.keymanager.certificate.default.common-name=www.example.com +mosip.kernel.keymanager.certificate.default.organizational-unit=EXAMPLE-CENTER +mosip.kernel.keymanager.certificate.default.organization=IIITB +mosip.kernel.keymanager.certificate.default.location=BANGALORE +mosip.kernel.keymanager.certificate.default.state=KA +mosip.kernel.keymanager.certificate.default.country=IN + # log level logging.level.root=WARN logging.level.io.mosip=INFO # logging.level.io.mosip.kernel.auth.defaultadapter.filter=INFO logging.level.io.mosip.kernel.auth.defaultadapter=DEBUG logging.level.org.springframework.http.client=DEBUG -logging.level.io.mosip.residentapp=INFO +logging.level.io.mosip.mimoto=INFO logging.level.reactor.netty.http.client=INFO # tomcat access logs server.tomcat.accesslog.enabled=true @@ -215,7 +249,7 @@ registration.processor.unMaskedUin.length=5 IDSchema.Version=1.0 registration.processor.identityjson=identity-mapping.json registration.processor.demographic.identity=identity -CREATEDATASHARE=${mosip.datashare.url}/v1/datashare/create +CREATEDATASHARE=${mosip.injiweb.datashare.url}/v1/datashare/create DECRYPTPINBASSED=${mosip.kernel.keymanager.url}/v1/keymanager/decryptWithPin @@ -271,9 +305,12 @@ mosip.openid.verifiers=mimoto-trusted-verifiers.json #Inji Web Config mosip.inji.web.url=http://localhost:3004 mosip.inji.web.redirect.url=${mosip.inji.web.url}/authorize +mosip.inji.web.authentication.success.redirect.url=${mosip.inji.web.url}/user/passcode mosip.inji.qr.data.size.limit=10000 mosip.inji.qr.code.height=650 mosip.inji.qr.code.width=650 +mosip.inji.user.wallet.pin.validation.regex=^\\d{6}$ +mosip.inji.user.wallet.name.validation.regex=^[A-Za-z0-9 _.-]{0,50}$ #OVP Config mosip.inji.ovp.qrdata.pattern=INJI_OVP://http://localhost:${server.port}/v1/mimoto/authorize?response_type=vp_token&resource=%s&presentation_definition=%s @@ -296,4 +333,46 @@ cache.issuers-config.expiry-time-in-min = 60 # Cache expiry time in minutes for the authentication server's well-known endpoint response. cache.credential-issuer.authserver-wellknown.expiry-time-in-min = 60 # Default cache expiry time in minutes for others cache types -cache.default.expiry-time-in-min = 60 \ No newline at end of file +cache.default.expiry-time-in-min = 60 + +#oauth2 config +spring.security.oauth2.client.registration.google.client-id=${GOOGLE_OAUTH_CLIENT_ID} +spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_OAUTH_CLIENT_SECRET} +spring.security.oauth2.client.registration.google.scope=profile,email +spring.security.oauth2.client.registration.google.redirect-uri=http://localhost:8099/v1/mimoto/oauth2/callback/google +spring.security.oauth2.client.registration.google.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.google.client-name=Google +spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/auth?prompt=select_account +spring.security.oauth2.client.provider.google.token-uri=https://oauth2.googleapis.com/token +spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo +spring.security.oauth2.client.provider.google.jwk-set-uri=https://www.googleapis.com/oauth2/v3/certs +spring.security.oauth2.client.provider.google.userNameAttribute=sub +spring.security.oauth2.client.provider.google.nameAttribute=name +spring.security.oauth2.client.provider.google.emailAttribute=email +spring.security.oauth2.client.provider.google.pictureAttribute=picture +spring.security.oauth2.client.provider.google.phoneNumberAttribute=phone_number + +#database config +spring.datasource.url=${SPRING_DATASOURCE_URL} +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.jpa.hibernate.ddl-auto=none +spring.jpa.show-sql=true +spring.datasource.driver-class-name=org.postgresql.Driver + +#Redis config +spring.session.store-type=redis +spring.data.redis.host=${SPRING_REDIS_HOST} +spring.data.redis.port=${SPRING_REDIS_PORT} +server.servlet.session.timeout=30m +spring.session.redis.namespace=injiweb:session: + +mosip.security.ignore-auth-urls=/safetynet/**,/actuator/**,/swagger-ui/**,/v3/api-docs/**,\ + /allProperties,/credentials/**,/credentialshare/**,/binding-otp,/wallet-binding,/get-token/**,\ + /issuers,/issuers/**,/authorize,/req/otp,/vid,/req/auth/**,/req/individualId/otp,/aid/get-individual-id,\ + /verifiers, /auth/*/token-login + +mosip.inji.app.id=MIMOTO +mosip.inji.encryption.algorithm=AES +mosip.inji.encryption.key.size=256 +mosip.inji.jwt.default.algorithm=RS256 \ No newline at end of file diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 1d97e1e5d..970df012e 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -37,9 +37,27 @@ services: - ./config/mimoto-trusted-verifiers.json:/config/server/mimoto-trusted-verifiers.json - ./config/credential-template.html:/config/server/credential-template.html - ./nginx.conf:/etc/nginx/nginx.conf + + postgres: + image: 'postgres:latest' + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - "5432:5432" + volumes: + - postgres-data:/var/lib/postgresql/data + - ./mimoto_init.sql:/docker-entrypoint-initdb.d/mimoto_init.sql + + redis: + image: redis:alpine + ports: + - "6379:6379" + volumes: + - redis-data:/data mimoto-service: container_name: 'Mimoto-Service' - image: 'mosipdev/mimoto:release-0.17.x' + image: 'mosipdev/mimoto:develop' user: root ports: - '8099:8099' @@ -49,10 +67,21 @@ services: - SPRING_CONFIG_NAME=mimoto - SPRING_CONFIG_LOCATION=/home/mosip/ - oidc_p12_password=dummypassword + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/inji_mimoto + - SPRING_REDIS_HOST=redis + - SPRING_REDIS_PORT=6379 + - GOOGLE_OAUTH_CLIENT_ID=clientId + - GOOGLE_OAUTH_CLIENT_SECRET=clientSecret volumes: - ./config/mimoto-default.properties:/home/mosip/mimoto-default.properties - ./config/mimoto-issuers-config.json:/home/mosip/mimoto-issuers-config.json - ./config/mimoto-trusted-verifiers.json:/home/mosip/mimoto-trusted-verifiers.json - ./certs/oidckeystore.p12:/home/mosip/certs/oidckeystore.p12 depends_on: - - nginx + - datashare + - postgres + - redis + +volumes: + postgres-data: + redis-data: \ No newline at end of file diff --git a/docker-compose/mimoto_init.sql b/docker-compose/mimoto_init.sql new file mode 100644 index 000000000..75c310b08 --- /dev/null +++ b/docker-compose/mimoto_init.sql @@ -0,0 +1,142 @@ +CREATE DATABASE inji_mimoto + ENCODING = 'UTF8' + LC_COLLATE = 'en_US.UTF-8' + LC_CTYPE = 'en_US.UTF-8' + TABLESPACE = pg_default + OWNER = postgres + TEMPLATE = template0; + +COMMENT ON DATABASE inji_mimoto IS 'mimoto related data is stored in this database'; + +\c inji_mimoto postgres + +DROP SCHEMA IF EXISTS mimoto CASCADE; +CREATE SCHEMA mimoto; +ALTER SCHEMA mimoto OWNER TO postgres; +ALTER DATABASE inji_mimoto SET search_path TO mimoto,pg_catalog,public; + +--- keymanager specific DB changes --- +CREATE TABLE mimoto.key_alias( + id character varying(36) NOT NULL, + app_id character varying(36) NOT NULL, + ref_id character varying(128), + key_gen_dtimes timestamp, + key_expire_dtimes timestamp, + status_code character varying(36), + lang_code character varying(3), + cr_by character varying(256) NOT NULL, + cr_dtimes timestamp NOT NULL, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + cert_thumbprint character varying(100), + uni_ident character varying(50), + CONSTRAINT pk_keymals_id PRIMARY KEY (id), + CONSTRAINT uni_ident_const UNIQUE (uni_ident) +); + +CREATE TABLE mimoto.key_policy_def( + app_id character varying(36) NOT NULL, + key_validity_duration smallint, + is_active boolean NOT NULL, + pre_expire_days smallint, + access_allowed character varying(1024), + cr_by character varying(256) NOT NULL, + cr_dtimes timestamp NOT NULL, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + CONSTRAINT pk_keypdef_id PRIMARY KEY (app_id) +); + +CREATE TABLE mimoto.key_store( + id character varying(36) NOT NULL, + master_key character varying(36) NOT NULL, + private_key character varying(2500) NOT NULL, + certificate_data character varying NOT NULL, + cr_by character varying(256) NOT NULL, + cr_dtimes timestamp NOT NULL, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + CONSTRAINT pk_keystr_id PRIMARY KEY (id) +); + +CREATE TABLE mimoto.ca_cert_store( + cert_id character varying(36) NOT NULL, + cert_subject character varying(500) NOT NULL, + cert_issuer character varying(500) NOT NULL, + issuer_id character varying(36) NOT NULL, + cert_not_before timestamp, + cert_not_after timestamp, + crl_uri character varying(120), + cert_data character varying, + cert_thumbprint character varying(100), + cert_serial_no character varying(50), + partner_domain character varying(36), + cr_by character varying(256), + cr_dtimes timestamp, + upd_by character varying(256), + upd_dtimes timestamp, + is_deleted boolean DEFAULT FALSE, + del_dtimes timestamp, + ca_cert_type character varying(25), + CONSTRAINT pk_cacs_id PRIMARY KEY (cert_id), + CONSTRAINT cert_thumbprint_unique UNIQUE (cert_thumbprint,partner_domain) + +); + +CREATE TABLE IF NOT EXISTS user_metadata ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + provider_subject_id character varying(255) NOT NULL, -- Unique identifier for the provider subject + identity_provider character varying(255) NOT NULL, -- Unique identifier for the identity provider + display_name TEXT NOT NULL, -- Display name of the user + profile_picture_url TEXT, -- URL of the user's profile picture + phone_number TEXT, -- Phone number of the user + email TEXT NOT NULL, -- Email of the user (Required field) + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now() -- Timestamp of last update (defaults to current time) +); + +CREATE TABLE IF NOT EXISTS wallet ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + user_id character varying(36) NOT NULL, -- Foreign key referencing user_metadata + wallet_key TEXT NOT NULL, -- Encrypted wallet key (retained here) + wallet_metadata JSONB NOT NULL, -- Metadata about the wallet, including encryption info + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now(), -- Timestamp of last update (defaults to current time) + + CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES user_metadata (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS verifiable_credentials ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + wallet_id character varying(36) NOT NULL, -- Foreign key referring to the wallet table (wallet.id) + credential TEXT NOT NULL, -- Encrypted credential (using wallet_key for encryption/decryption) + credential_metadata JSONB NOT NULL, -- Metadata about the credential + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now(), -- Timestamp of last update (defaults to current time) + + CONSTRAINT fk_wallet_id FOREIGN KEY (wallet_id) REFERENCES wallet (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS proof_signing_key ( + id character varying(36) PRIMARY KEY, -- Primary key for the table + wallet_id character varying(36) NOT NULL, -- Foreign key referencing the wallet table + public_key TEXT NOT NULL, -- Public key for wallet + secret_key TEXT NOT NULL, -- Secret key, encrypted using proof_signing_key + key_metadata JSONB NOT NULL, -- Metadata about the public and private keys + created_at TIMESTAMP DEFAULT now(), -- Timestamp of record creation (defaults to current time) + updated_at TIMESTAMP DEFAULT now(), -- Timestamp of last update (defaults to current time) + + CONSTRAINT fk_wallet_id FOREIGN KEY (wallet_id) REFERENCES wallet (id) ON DELETE CASCADE +); + +INSERT INTO mimoto.key_policy_def(APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) VALUES('ROOT', 2920, 1125, 'NA', true, 'mosipadmin', now()); +INSERT INTO mimoto.key_policy_def(APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) VALUES('BASE', 1095, 60, 'NA', true, 'mosipadmin', now()); +INSERT INTO mimoto.key_policy_def(APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) VALUES('MIMOTO', 1095, 60, 'NA', true, 'mosipadmin', now()); + + diff --git a/docs/GoogleOauth2LoginIntegration.md b/docs/GoogleOauth2LoginIntegration.md new file mode 100644 index 000000000..5d3cd657a --- /dev/null +++ b/docs/GoogleOauth2LoginIntegration.md @@ -0,0 +1,91 @@ +# Google OAuth2 Login Integration Guide + +## Overview +This documentation explains the implementation of the login feature using Google OAuth2 login. It also covers the authentication mechanism for APIs in : + - `UsersController` + - `WalletController` + - `WalletCredentialsController`. + +--- + +## Prerequisites +Before you begin, ensure you have: +1. **A Google Developer Console account.** +2. **A project created in the Google Developer Console.** +3. **OAuth2 enabled for the project.** +4. **A Client ID and Client Secret generated.** +--- + +## Google OAuth2 Integration Steps + +1. **Configure OAuth2 Login**: + - Google client ID and secret in `application-default.properties`: + ``` + spring.security.oauth2.client.registration.google.client-id=${mosip.injiweb.google.client.id} + spring.security.oauth2.client.registration.google.client-secret=${mosip.injiweb.google.client.secret} + ``` + - Make sure value of `mosip.injiweb.google.client.id` and `mosip.injiweb.google.client.secret` are set in the environment. +2. **Authorized Host URL**: + - Register the base domain of your mimoto in Google Developer Console, e.g., `https://your-mimoto-domain`. + +3. **Redirect URI**: + - Under Authorized redirect URIs in Google Developer Console, add: + `https:///oauth2/callback/*` + Replace `` with your application's domain. + - Ensure the redirect URI matches the one configured in the Google Developer Console. +--- +## Sequence Diagram +### Google Login Flow +```mermaid +sequenceDiagram + participant User + participant Browser + participant mimoto + participant GoogleOAuth2 + participant Redis + + User->>Browser: Navigate to Login Page + Browser->>mimoto: Request Login + mimoto->>GoogleOAuth2: Redirect to Google Login + GoogleOAuth2->>User: Prompt for Credentials + User->>GoogleOAuth2: Enter Credentials + GoogleOAuth2->>mimoto: Send Authorization Code + mimoto->>GoogleOAuth2: Exchange Code for Access Token + GoogleOAuth2->>mimoto: Return Access Token & User Info + mimoto->>Redis: Store Session ID and User Metadata + mimoto->>Browser: Set Session ID in Cookie / Response + Browser->>User: Redirect to Dashboard + ``` +### Authenticated API Access Flow +```mermaid + sequenceDiagram + participant User + participant Browser + participant mimoto + participant Redis + + User->>Browser: Request API + Browser->>mimoto: Send API Request with Session ID + mimoto->>Redis: Validate Session ID + Redis->>mimoto: Return User Metadata + mimoto->>Browser: Respond with Data +``` +--- +## Key Points +### Session-Based Authentication: +- On successful login, a session ID is generated and stored in Redis. +- All APIs in UsersController, WalletController, and WalletCredentialsController validate the session ID stored in Redis. + +### Integration Points in Application +### Component Responsibility + +| **Component** | **Responsibility** | +|--------------------------------|-------------------------------------------------------------------------------------| +| `Config` | Configures OAuth2 login, session management, and security settings. | +| `OAuth2AuthenticationSuccessHandler` | Handles successful authentication, stores user metadata in the session, and redirects to the dashboard. | +| `OAuth2AuthenticationFailureHandler` | Handles authentication failures, logs errors, and redirects to the login page with error details. | +| `CustomOAuth2UserService` | Retrieves and processes user information from the OAuth2 provider. | +| `TokenAuthController` | Provides API for token-based authentication and session creation. | +| `UsersController` | Manages user profile retrieval using session-based authentication. | +| `WalletsController` | Handles wallet creation, unlocking, and deletion using session-based authentication. | +| `WalletCredentialsController` | Manages credential download, retrieval, and deletion for wallets. |--- diff --git a/docs/InjiCustomTemplateFeature.md b/docs/InjiCustomTemplateFeature.md new file mode 100644 index 000000000..14b4dbc25 --- /dev/null +++ b/docs/InjiCustomTemplateFeature.md @@ -0,0 +1,41 @@ +# Download Custom Credential Templates + +## Overview +This feature provides option to download a Verifiable Credential (VC) as a PDF using custom templates. +The templates is fetched from a configuration server. Code first tries to find a template using the pattern `issuerId-credentialType-template.html`. +If this specific template is not found, it will use `credential-template.html`. + +## Sequence Diagram + +```mermaid +sequenceDiagram +participant Client +participant CredentialsController +participant Utilities +participant ConfigServer + + Client->>+CredentialsController: POST /download + CredentialsController->>+Utilities: getCredentialSupportedTemplateString(issuerId, credentialType) + alt Template found in Config Server + Utilities->>+ConfigServer: Fetch template (issuerId-credentialType-template.html) + ConfigServer-->>-Utilities: Return custom template + else Template not found + Utilities->>+ConfigServer: Fetch default template (credential-template.html) + ConfigServer-->>-Utilities: Return default template + end + Utilities-->>-CredentialsController: Return template + CredentialsController->>CredentialsController: Generate PDF using template + CredentialsController-->>Client: Return PDF (200 OK) + CredentialsController->>+Utilities: handleExceptionWithErrorCode(exception, errorCode) + Utilities-->>-CredentialsController: Return error details + CredentialsController-->>-Client: ResponseEntity with error (400 Bad Request) +``` + +## Custom Template Samples + +Below are the links to the custom template samples that you can use: + +- [Stay Protected Insurance Credential Template](https://github.com/mosip/inji-config/blob/release-0.7.x/StayProtected-InsuranceCredential-template.html) +- [Mosip Verifiable Credential Template](https://github.com/mosip/inji-config/blob/release-0.7.x/Mosip-MosipVerifiableCredential-template.html) + +You can view and download the templates from the links above. Feel free to explore and customize them as per your requirements. \ No newline at end of file diff --git a/docs/InjiIssuerConfigurationFeature.md b/docs/InjiIssuerConfigurationFeature.md new file mode 100644 index 000000000..cac23429d --- /dev/null +++ b/docs/InjiIssuerConfigurationFeature.md @@ -0,0 +1,71 @@ +# Issuer Configuration Feature + +## Overview + +This feature offers an endpoint to fetch the configuration of a credential issuer. +The configuration is retrieved from both the issuer's well-known endpoint and the authorization server's well-known +endpoint. To enhance performance and minimize the load on external services, the results are cached using Caffeine. +This endpoint is invoked in inji-web when an issuer is selected, allowing its configurations to be retrieved. + +## Sequence Diagram + +The following sequence diagram illustrates the flow of the `getIssuerConfiguration` method in the `IssuersController` class, +including interactions with the `IssuersService`, `IssuerConfigUtil`, `Utilities` and caching mechanisms. + +```mermaid +sequenceDiagram + participant Client + participant IssuersController + participant IssuersService + participant IssuerConfigUtil + participant Cache + participant Utilities + + Client->>+IssuersController: GET /issuers/{issuer-id}/configuration + IssuersController->>+IssuersService: getIssuerConfiguration(issuerId) + alt Cache hit + IssuersService->>+Cache: Check cache for issuer configuration (Caffeine) + Cache-->>-IssuersService: Return cached configuration + else Cache miss + IssuersService->>+IssuersService: getIssuerDetails(issuerId) + IssuersService->>+IssuerConfigUtil: getIssuerWellknown(issuerDetails.getCredential_issuer_host()) + IssuerConfigUtil-->>-IssuersService: CredentialIssuerWellKnownResponse + IssuersService->>+IssuerConfigUtil: getAuthServerWellknown(credentialIssuerWellKnownResponse.getAuthorizationServers().get(0)) + IssuerConfigUtil-->>-IssuersService: AuthorizationServerWellKnownResponse + IssuersService->>+Cache: Store configuration in cache (expiry: 60 min) + end + IssuersService-->>-IssuersController: Return issuer configuration + IssuersController->>+Utilities: handleExceptionWithErrorCode(exception, errorCode) + Utilities-->>-IssuersController: Return error details + IssuersController-->>-Client: ResponseEntity with configuration or error +``` + +## Configuration + +### Cache Configuration + +The caching mechanism used in this project is Caffeine. Cache timeout properties are defined in the +`application-local.properties` file for the local setup, and in the `mimoto-default.properties` file for the environment setup. + +#### Cache Timeout Properties + +- `cache.credential-issuer.wellknown.expiry-time-in-min`: Cache expiry time in minutes for the issuer's well-known endpoint response. +- `cache.issuers-config.expiry-time-in-min`: Cache expiry time in minutes for issuers configurations read from a config file. +- `cache.credential-issuer.authserver-wellknown.expiry-time-in-min`: Cache expiry time in minutes for the authentication server's well-known endpoint response. +- `cache.default.expiry-time-in-min`: Default cache expiry time in minutes for other cache types. + +### Increasing Cache Time + +To increase the cache time, you can modify the properties in the `application-local.properties` or +`mimoto-default.properties` file. For example, to set the cache expiry time to 120 minutes, update the properties as follows: + +```properties +# Cache expiry time in minutes for the issuer's well-known endpoint response. +cache.credential-issuer.wellknown.expiry-time-in-min = 120 +# Cache expiry time in minutes for issuers configurations read from a config file. +cache.issuers-config.expiry-time-in-min = 120 +# Cache expiry time in minutes for the authentication server's well-known endpoint response. +cache.credential-issuer.authserver-wellknown.expiry-time-in-min = 120 +# Default cache expiry time in minutes for other cache types. +cache.default.expiry-time-in-min = 120 +``` \ No newline at end of file diff --git a/docs/UserDataEncryptionWithPinBasedKey.md b/docs/UserDataEncryptionWithPinBasedKey.md new file mode 100644 index 000000000..28fa34c92 --- /dev/null +++ b/docs/UserDataEncryptionWithPinBasedKey.md @@ -0,0 +1,114 @@ +## Securing User Data in the Mimoto Database with PIN-derived Key Encryption + + +## Overview of Encryption Requirement and Goals + +The encryption mechanism is designed to protect sensitive data stored in the database, such as wallet keys and user metadata. The primary goals are: + +- Ensuring confidentiality of sensitive data. +- Preventing unauthorized access to sensitive information. +- Supporting secure key management and retrieval processes. +- Enabling record-level encryption for granular security. + +## Encryption Algorithm and Mode Used + +- **Algorithm:** AES (Advanced Encryption Standard) +- **Key Size:** 256 bits +- **Mode:** AES-256 GCM + +## Key Management Strategy + +- **Key Generation:** The AES key is generated dynamically during wallet creation. +- **Key Encryption:** The AES key is encrypted using the wallet PIN provided by the user. +- **Key Storage:** The encrypted AES key is stored securely in the database. +- **Key Retrieval:** During wallet unlocking, the wallet PIN is used to decrypt the AES key. + +## Record-Level Encryption Design + +- Each sensitive record (e.g., user signing keys) is encrypted using a unique AES key for a user. +- The AES key itself is encrypted using the wallet PIN, ensuring that only the user with the correct PIN can access the data. +--- +# Wallet Key Lifecycle + +### 1. 🔧 Wallet Creation +The user submits a POST request with a PIN to create a new wallet. + +- The system generates a new 256-bit AES wallet key: +```java +SecretKey aesKey = KeyGenerationUtil.generateEncryptionKey("AES", 256); +``` +- A salt (32 bytes) and IV (12 bytes) are generated randomly. +- The user's PIN is used to derive a key using PBKDF2WithHmacSHA512. +- The AES key is encrypted using AES/GCM/NoPadding and stored as: + + ```Base64(salt + IV + ciphertext)``` +- The original PIN and raw AES key are never stored—only the AES key encrypted with a PIN-derived key is saved. + +### 2. 🔓 **Wallet Unlock (Session Start)** +When the user wants to access the wallet, they re-enter their PIN via an unlock API. + +- The system retrieves the encrypted wallet key. +- Extracts the salt and IV. +- Derives the decryption key from the entered PIN and stored salt. +- The ```decryptWithPin``` method from the ```keyManager``` library handles the decryption and returns the Base64-encoded AES wallet key. +- The Base64-encoded AES key is stored in the HTTP session. +### 3. 🔐 **In-Session Operations** +Once the AES wallet key is in session: +- It is Base64-decoded and used to encrypt or decrypt sensitive data like credentials or keys. +- The user does not need to re-enter the PIN during the session. +- Encryption and decryption continue using AES/GCM/NoPadding for both confidentiality and integrity. +### 4. 🔐 **Session Timeout Configuration** +The property +```properties +server.servlet.session.timeout=30m +``` +in ```application-default.properties``` sets the HTTP session timeout to 30 minutes. +This means that if a user is inactive for 30 minutes, their session will automatically expire, +requiring them to log in again. Adjust this value to control how long user sessions remain active after inactivity. + +### 5. 🔚 **Session End / Logout** + +- The Base64-encoded AES key is removed from the session. +- On the next login, in unlock wallet API, the user must provide their PIN again to decrypt the key. + +--- + +## Flow Diagram + +```mermaid +flowchart TD +%% Wallet Creation Flow +A[Wallet Creation] --> B[Gen AES Wallet Key] +B --> C[Gen Salt & IV] +C --> D[Derive Key
from PIN + Salt] +D --> E[Encrypt AES Key
using Derived Key] +E --> F[Store Encrypted Key
as Base64] +F --> G[DB] + + %% Wallet Unlock Subgraph + subgraph Unlock Flow + H[Wallet Unlock] --> I[Retrieve Encrypted Key
from DB] + I --> J[Extract Salt & IV] + J --> K[Derive Key
from Entered PIN] + K --> L[Receive Base64 AES Key
from decryptWithPin] + L --> M[Store Base64 AES Key
in Session] + end + + %% Session Operations + M --> N[Session Ops:
Base64 Decode Key → Encrypt/Decrypt Data] + N --> O[Session End / Logout] + O --> P[Remove Base64 AES Key
from Session] + P --> H + + %% Session Timeout Node + Q[Session Timeout] --> O + + %% Style classes + classDef stylePrimary fill:#ADD8E6,stroke:#000080,stroke-width:2px,color:#000000; + classDef styleSuccess fill:#90EE90,stroke:#006400,stroke-width:2px,color:#000000; + classDef styleWarning fill:#FFD700,stroke:#B8860B,stroke-width:2px,color:#000000; + + class A,B,C,D,E,F,G,H,I,J,K,L,M,N stylePrimary + class O,P,Q styleWarning +``` +This flow ensures that sensitive user data is securely encrypted and accessible only to the user who knows the PIN. \ No newline at end of file diff --git a/docs/api-documentation-openapi.json b/docs/api-documentation-openapi.json index 4ed01fb08..6ce65f50f 100644 --- a/docs/api-documentation-openapi.json +++ b/docs/api-documentation-openapi.json @@ -60,13 +60,17 @@ "type": "string" }, "str": { - "type": "null" + "type": "string", + "nullable": true }, "responsetime": { - "type": "null" + "type": "string", + "format": "date-time", + "nullable": true }, "metadata": { - "type": "null" + "type": "object", + "nullable": true }, "response": { "type": "object", @@ -80,7 +84,19 @@ } }, "errors": { - "type": "null" + "type": "array", + "items": { + "type": "object", + "properties": { + "errorCode": { + "type": "string" + }, + "errorMessage": { + "type": "string" + } + } + }, + "nullable": true } }, "x-examples": { @@ -140,17 +156,29 @@ "schema": { "type": "object", "properties": { - "verifiers": [ - { - "client_id": "string", - "redirect_uris": [ - "string" - ], - "response_uris": [ - "string" - ] + "verifiers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "client_id": { + "type": "string" + }, + "redirect_uris": { + "type": "array", + "items": { + "type": "string" + } + }, + "response_uris": { + "type": "array", + "items": { + "type": "string" + } + } + } } - ] + } } }, "examples": { @@ -435,13 +463,17 @@ "type": "string" }, "str": { - "type": "null" + "type": "string", + "nullable": true }, "responsetime": { - "type": "string" + "type": "string", + "format": "date-time", + "nullable": true }, "metadata": { - "type": "null" + "type": "object", + "nullable": true }, "response": { "type": "object", @@ -455,7 +487,19 @@ } }, "errors": { - "type": "null" + "type": "array", + "items": { + "type": "object", + "properties": { + "errorCode": { + "type": "string" + }, + "errorMessage": { + "type": "string" + } + } + }, + "nullable": true } }, "x-examples": { @@ -537,21 +581,6 @@ "schema": { "type": "object", "properties": { - "id": { - "type": "null" - }, - "version": { - "type": "null" - }, - "str": { - "type": "null" - }, - "responsetime": { - "type": "string" - }, - "metadata": { - "type": "null" - }, "response": { "type": "object", "properties": { @@ -563,24 +592,19 @@ }, "statusCode": { "type": "string" + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": {} + } } } - }, - "errors": { - "type": "array", - "items": { - "type": "object", - "properties": {} - } } }, "x-examples": { "Example 1": { - "id": null, - "version": null, - "str": null, - "responsetime": "2022-10-31T05:10:58.337Z", - "metadata": null, "response": { "id": "2057135194", "requestId": "2bc3bb7f-6156-46d1-ae03-bf7b76e0c257", @@ -593,11 +617,6 @@ "examples": { "success response": { "value": { - "id": null, - "version": null, - "str": null, - "responsetime": "2022-10-31T05:10:58.337Z", - "metadata": null, "response": { "id": "2057135194", "requestId": "2bc3bb7f-6156-46d1-ae03-bf7b76e0c257", @@ -1310,88 +1329,49 @@ "/allProperties": { "get": { "operationId": "getAllProperties", - "responses": {}, - "description": "get all configurable properties for inji.", - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "version": { - "type": "string" - }, - "str": { - "type": "null" - }, - "responsetime": { - "type": "string" - }, - "metadata": { - "type": "null" - }, - "response": { - "type": "object", - "properties": { - "internal.auth.types.allowed": { - "type": "string" - }, - "auth.types.allowed": { - "type": "string" - }, - "ekyc.auth.types.allowed": { + "responses": { + "200": { + "description": "Successfully retrieved all configurable properties for Inji.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "response": { + "type": "object", + "additionalProperties": { "type": "string" } - } - }, - "errors": { - "type": "array", - "items": { - "type": "object", - "properties": {} + }, + "errors": { + "type": "array", + "items": { + "type": "object" + } } } }, - "x-examples": { - "Example 1": { - "id": "mosip.inji.properties", - "version": "v1", - "str": null, - "responsetime": "2022-10-31T05:22:21.009Z", - "metadata": null, - "response": { - "internal.auth.types.allowed": "otp,bio-Finger,bio-Iris,bio-Face", - "auth.types.allowed": "demo,otp,bio-Finger,bio-Iris,bio-Face", - "ekyc.auth.types.allowed": "demo,otp,bio-Finger,bio-Iris,bio-Face" - }, - "errors": [] - } - } - }, - "examples": { - "example-1": { - "value": { - "id": "mosip.inji.properties", - "version": "v1", - "str": null, - "responsetime": "2022-10-31T05:22:21.009Z", - "metadata": null, - "response": { - "internal.auth.types.allowed": "otp,bio-Finger,bio-Iris,bio-Face", - "auth.types.allowed": "demo,otp,bio-Finger,bio-Iris,bio-Face", - "ekyc.auth.types.allowed": "demo,otp,bio-Finger,bio-Iris,bio-Face" - }, - "errors": [] + "examples": { + "example-1": { + "value": { + "response": { + "internal.auth.types.allowed": "otp,bio-Finger,bio-Iris,bio-Face", + "auth.types.allowed": "demo,otp,bio-Finger,bio-Iris,bio-Face", + "ekyc.auth.types.allowed": "demo,otp,bio-Finger,bio-Iris,bio-Face" + }, + "errors": [] + } } } } } } }, - "summary": "Get all Inji app properties" + "description": "Get all configurable properties for Inji.", + "summary": "Get all Inji app properties", + "tags": [ + "Inji Config" + ] } }, "/issuers": { @@ -1419,45 +1399,89 @@ "schema": { "type": "object", "properties": { - "issuers": [ - { - "issuer_id": "string", - "protocol": "string", - "display": [ - { - "name": "string", - "logo": { - "url": "string", - "alt_text": "string" - }, - "title": "string", - "description": "string", - "language": "string" + "issuers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "issuer_id": { + "type": "string" + }, + "protocol": { + "type": "string" + }, + "display": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "logo": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "alt_text": { + "type": "string" + } + } + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "language": { + "type": "string" + } + } + } + }, + "client_id": { + "type": "string" + }, + "wellknown_endpoint": { + "type": "string" + }, + "redirect_uri": { + "type": "string" + }, + "authorization_audience": { + "type": "string" + }, + "token_endpoint": { + "type": "string" + }, + "proxy_token_endpoint": { + "type": "string" + }, + "client_alias": { + "type": "string" + }, + "qr_code_type": { + "type": "string", + "enum": [ + "OnlineSharing", + "EmbeddedVC", + "None" + ] + }, + "enabled": { + "type": "string" + }, + "credential_issuer": { + "type": "string" + }, + "credential_issuer_host": { + "type": "string" } - ], - "client_id": "string", - "redirect_uri": "string", - "scopes_supported": [ - "string" - ], - "authorization_endpoint": "string", - "token_endpoint": "string", - "proxy_token_endpoint": "string", - "credential_endpoint": "string", - "credential_type": [ - "string" - ], - "credential_audience": "string", - "client_alias": "string", - "additional_headers": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string" - }, - "wellknown_endpoint": "string", - "credential_issuer": "string" + } } - ] + } } }, "x-examples": { @@ -1471,6 +1495,8 @@ "issuers": [ { "issuer_id": "MOSIPInsurance", + "credential_issuer": "MOSIPInsurance", + "credential_issuer_host": "https://injicertify-insurance.collab.mosip.net", "protocol": "OpenId4VCI", "display": [ { @@ -1485,11 +1511,12 @@ } ], "client_id": "3yz7-j3xRzU3SODdoNgSGvO_cD8UijH3AIWRDAg1x-M", - ".well-known": "http://localhost:8088/.well-known/openid-credential-issuer", - "credential_issuer": "http://localhost:8088" + "wellknown_endpoint": "http://localhost:8088/.well-known/openid-credential-issuer" }, { "issuer_id": "MOSIPNationalID", + "credential_issuer": "MOSIPNationalID", + "credential_issuer_host": "https://injicertify-mosipid.released.mosip.net", "protocol": "OpenId4VCI", "display": [ { @@ -1504,13 +1531,12 @@ } ], "client_id": "3yz7-j3xRzU3SODdoNgSGvO_cD8UijH3AIWRDAg1x-M", - ".well-known": "http://localhost:8088/.well-known/openid-credential-issuer", - "credential_issuer": "http://localhost:8088" + "wellknown_endpoint": "http://localhost:8088/.well-known/openid-credential-issuer" } ] - } - }, - "errors": null + }, + "errors": null + } }, "examples": { "Success response": { @@ -1524,6 +1550,8 @@ "issuers": [ { "issuer_id": "MOSIPInsurance", + "credential_issuer": "MOSIPInsurance", + "credential_issuer_host": "https://injicertify-insurance.collab.mosip.net", "protocol": "OpenId4VCI", "display": [ { @@ -1538,11 +1566,12 @@ } ], "client_id": "3yz7-j3xRzU3SODdoNgSGvO_cD8UijH3AIWRDAg1x-M", - ".well-known": "http://localhost:8088/.well-known/openid-credential-issuer", - "credential_issuer": "http://localhost:8088" + ".well-known": "http://localhost:8088/.well-known/openid-credential-issuer" }, { "issuer_id": "MOSIPNationalID", + "credential_issuer": "MOSIPNationalID", + "credential_issuer_host": "https://injicertify-mosipid.released.mosip.net", "protocol": "OpenId4VCI", "display": [ { @@ -1557,13 +1586,11 @@ } ], "client_id": "3yz7-j3xRzU3SODdoNgSGvO_cD8UijH3AIWRDAg1x-M", - ".well-known": "http://localhost:8088/.well-known/openid-credential-issuer", - "credential_issuer": "http://localhost:8088" + ".well-known": "http://localhost:8088/.well-known/openid-credential-issuer" } ] } - }, - "errors": null + } }, "Failure response": { "value": { @@ -1604,7 +1631,7 @@ "tags": [ "Trusted Issuers" ], - "description": "This API provides the complete configuration details for the specific issuers passed in the path variable.", + "description": "This API provides the complete configuration details for the specific issuers passed in the path variable", "responses": { "200": { "description": "OK", @@ -1613,117 +1640,137 @@ "schema": { "type": "object", "properties": { - "issuers": [ - { - "credential_issuer": "string", - "protocol": "string", - "display": [ - { - "name": "string", - "logo": { - "url": "string", - "alt_text": "string" - }, - "title": "string", - "description": "string", - "language": "string" - } - ], - "client_id": "string", - "redirect_uri": "string", - "scopes_supported": [ - "string" - ], - "authorization_endpoint": "string", - "authorization_audience": "string", - "token_endpoint": "string", - "proxy_token_endpoint": "string", - "credential_endpoint": "string", - "credential_type": [ - "string" - ], - "credential_audience": "string", - "client_alias": "string", - "additional_headers": { - "additionalProp1": "string", - "additionalProp2": "string", - "additionalProp3": "string" - }, - "wellknown_endpoint": "string" - } - ] - } - }, - "x-examples": { - "Example 1": { - "id": "mosip.mimoto.issuers", - "version": "v1", - "str": null, - "responsetime": "2024-04-25T05:56:55.890Z", - "metadata": null, "response": { - "issuer_id": "ESignet", - "protocol": "OpenId4VCI", - "display": [ - { - "name": "e-Signet", - "logo": { - "url": "https://api.collab.mosip.net/inji/mosip-logo.png", - "alt_text": "mosip-logo" - }, - "title": "Download MOSIP Credentials", - "description": "Download credentials by providing UIN or VID", - "language": "en" + "type": "object", + "properties": { + "issuer_id": { + "type": "string" }, - { - "name": "e-Signet", - "logo": { - "url": "https://api.collab.mosip.net/inji/mosip-logo.png", - "alt_text": "شعار موسيب" - }, - "title": "قم بتنزيل بيانات اعتماد MOSIP", - "description": "توفير UIN أو VIDقم بتنزيل بيانات الاعتماد عن طريق", - "language": "ar" + "protocol": { + "type": "string" }, - { - "name": "e-Signet", - "logo": { - "url": "https://api.collab.mosip.net/inji/mosip-logo.png", - "alt_text": "मोसिप लोगो" - }, - "title": "MOSIP क्रेडेंशियल डाउनलोड करेंं", - "description": "यूआईएन या वीआईडी प्रदान करके क्रेडेंशियल डाउनलोड करें", - "language": "hi" + "display": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "logo": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "alt_text": { + "type": "string" + } + } + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "language": { + "type": "string" + } + } + } + }, + "client_id": { + "type": "string" + }, + "wellknown_endpoint": { + "type": "string" + }, + "redirect_uri": { + "type": "string" + }, + "authorization_audience": { + "type": "string" + }, + "token_endpoint": { + "type": "string" + }, + "proxy_token_endpoint": { + "type": "string" + }, + "client_alias": { + "type": "string" }, + "qr_code_type": { + "type": "string", + "enum": [ + "OnlineSharing", + "EmbeddedVC", + "None" + ] + }, + "enabled": { + "type": "boolean" + }, + "credential_issuer": { + "type": "string" + }, + "credential_issuer_host": { + "type": "string" + } + } + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } + } + }, + "x-examples": { + "Example 1": { + "response": { + "issuer_id": "ESignet", + "protocol": "OpenId4VCI", + "display": [ { "name": "e-Signet", "logo": { "url": "https://api.collab.mosip.net/inji/mosip-logo.png", - "alt_text": "mosip ಲೋಗೋ" + "alt_text": "mosip-logo" }, - "title": "MOSIP ರುಜುವಾತುಗಳನ್ನು ಡೌನ್ಲೋಡ್ ಮಾಡಿ", - "description": "UIN ಅಥವಾ VID ಒದಗಿಸುವ ಮೂಲಕ ರುಜುವಾತುಗಳನ್ನು ಡೌನ್ಲೋಡ್ ಮಾಡಿ", - "language": "kn" + "title": "Download MOSIP Credentials", + "description": "Download credentials by providing UIN or VID", + "language": "en" }, { "name": "e-Signet", "logo": { "url": "https://api.collab.mosip.net/inji/mosip-logo.png", - "alt_text": "mosip லோகோ" + "alt_text": "شعار موسيب" }, - "title": "MOSIP சான்றுகளைப் பதிவிறக்கவும்", - "description": "UIN அல்லது VIDஐ வழங்குவதன் மூலம் நற்சான்றிதழ்களைப் பதிவிறக்கவும்", - "language": "ta" + "title": "قم بتنزيل بيانات اعتماد MOSIP", + "description": "توفير UIN أو VIDقم بتنزيل بيانات الاعتماد عن طريق", + "language": "ar" }, { "name": "e-Signet", "logo": { "url": "https://api.collab.mosip.net/inji/mosip-logo.png", - "alt_text": "logo ng mosip" + "alt_text": "मोसिप लोगो" }, - "title": "I-download ang Mga Kredensyal ng MOSIP", - "description": "Mag-download ng mga kredensyal sa pamamagitan ng pagbibigay ng UIN o VID", - "language": "fil" + "title": "MOSIP क्रेडेंशियल डाउनलोड करेंं", + "description": "यूआईएन या वीआईडी प्रदान करके क्रेडेंशियल डाउनलोड करें", + "language": "hi" } ], "client_id": "*****", @@ -1745,8 +1792,7 @@ "additional_headers": { "Accept": "application/json" }, - ".well-known": "https://esignet.collab.mosip.net/.well-known/openid-credential-issuer", - "credential_issuer": "https://esignet.collab.mosip.net" + "wellknown_endpoint": "https://esignet.collab.mosip.net/.well-known/openid-credential-issuer" }, "errors": [] }, @@ -1761,7 +1807,7 @@ "responsetime": "2024-04-25T05:56:55.890Z", "metadata": null, "response": { - "issuer_id": "ESignet", + "issuer_id": "mosip.mimoto.issuers", "protocol": "OpenId4VCI", "display": [ { @@ -1827,10 +1873,6 @@ ], "client_id": "DEqVWfdKe9cQWikLdjak3vlDF0Pq7jtnwTcGdEXoT1I", "redirect_uri": "io.mosip.residentapp.inji://oauthredirect", - "scopes_supported": [ - "mosip_identity_vc_ldp" - ], - "authorization_endpoint": "https://esignet.collab.mosip.net/authorize", "authorization_audience": "https://esignet.collab.mosip.net/v1/esignet/oauth/v2/token", "token_endpoint": "https://api.collab.mosip.net/v1/mimoto/get-token/ESignet", "proxy_token_endpoint": "https://esignet.collab.mosip.net/v1/esignet/oauth/v2/token", @@ -1844,12 +1886,10 @@ "additional_headers": { "Accept": "application/json" }, - ".well-known": "https://esignet.collab.mosip.net/.well-known/openid-credential-issuer", - "credential_issuer": "https://esignet.collab.mosip.net" + "wellknown_endpoint": "https://esignet.collab.mosip.net/.well-known/openid-credential-issuer" }, "errors": [] - }, - "errors": null + } }, "Failure response": { "value": { @@ -1899,8 +1939,131 @@ "application/json": { "schema": { "type": "object", - "errors": { - "type": "null" + "properties": { + "credential_issuer": { + "type": "string" + }, + "authorization_servers": { + "type": "array", + "items": { + "type": "string" + } + }, + "credential_endpoint": { + "type": "string" + }, + "credential_configurations_supported": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "format": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "doctype": { + "type": "string" + }, + "proof_types_supported": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "proof_signing_alg_values_supported": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "claims": { + "type": "object" + }, + "credential_definition": { + "type": "object", + "properties": { + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "credentialSubject": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "display": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "locale": { + "type": "string" + } + } + } + } + } + } + } + } + }, + "display": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "logo": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "alt_text": { + "type": "string" + } + } + }, + "background_image": { + "type": "object", + "properties": { + "uri": { + "type": "string" + } + } + }, + "background_color": { + "type": "string" + }, + "text_color": { + "type": "string" + } + } + } + }, + "order": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } } }, "examples": { @@ -2505,97 +2668,1646 @@ } ] } - } - }, - "components": { - "schemas": { - "IssuerConfigurationSuccessResponse": { - "type": "object", - "properties": { - "response": { - "type": "object", - "properties": { - "credentials_supported": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Credential" - } - }, - "authorization_endpoint": { - "type": "string", - "format": "uri" - }, - "grant_types_supported": { - "type": "array", - "items": { - "type": "string" + }, + "/users/me": { + "get": { + "tags": [ + "Users" + ], + "summary": "Retrieve user metadata", + "description": "First attempts to retrieve user metadata from the session cache. If not available, fetches from the database. This API is secured using session-based authentication.", + "operationId": "getUserProfile", + "security": [ + { + "SessionAuth": [] + } + ], + "responses": { + "200": { + "description": "User profile retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserMetadataDTO" + }, + "examples": { + "Success response from DB": { + "value": { + "displayName": "John Doe", + "profilePictureUrl": "https://example.com/profile.jpg", + "email": "john.doe@example.com" + } + }, + "Success response from cache": { + "value": { + "displayName": "John Doe", + "profilePictureUrl": "john.doe@example.com", + "email": "john.doe@example.com", + "walletId": "123e4567-e89b-12d3-a456-426614174000" + } + } } } } }, - "errors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Error" - }, - "example": [] - } - } - }, - "IssuerConfigurationErrorResponse": { - "type": "object", - "properties": { - "response": { - "type": "object", - "nullable": true - }, - "errors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Error" + "401": { + "description": "User data not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "User data not available in DB" : { + "value": { + "errorCode": "unauthorized", + "errorMessage": "User not found. Please check your credentials or login again" + } + } + } + } } - } - } - }, - "Credential": { - "type": "object", - "properties": { - "name": { - "type": "string" }, - "scope": { - "type": "string" + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + }, + "Decryption Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "Failed to process user data" + } + } + } + } + } }, - "display": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Display" + "503": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } } } } - }, - "Display": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "locale": { - "type": "string" - }, - "logo": { - "type": "string" + } + }, + "/wallets/{walletId}/credentials": { + "post": { + "tags": [ + "Wallet Credentials" + ], + "summary": "Download and store a Verifiable Credential under a specific Wallet", + "description": "This API is secured using session-based authentication. Upon receiving a request, the session ID is extracted from the Cookie header and used to retrieve session details from Redis for authentication. It then retrieves the wallet key from the session and use it to decrypt the signing algorithm's secret key(which is used for signing the JWT in credential request) and encrypt the downloaded Verifiable Credential. If the process completes successfully, the credential is stored in the database and certain fields will be returned in the response. In case of any issues, an appropriate error response is returned.", + "operationId": "downloadCredential", + "security": [ + { + "SessionAuth": [] } - } + ], + "parameters": [ + { + "name": "walletId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the user's wallet where the credential will be stored" + }, + { + "name": "Accept-Language", + "in": "header", + "required": false, + "schema": { + "type": "string", + "default": "en" + }, + "description": "Locale is used to determine the language in which credential's issuer display details should be stored. Input should be a 2-letter code (e.g., 'en' for English, 'fr' for French). If not provided, defaults to 'en'." + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerifiableCredentialRequestDTO" + }, + "examples": { + "Default download": { + "value": { + "issuer": "Mosip", + "credentialConfigurationId": "MosipVerifiableCredential", + "code": "abc123", + "grant_type": "authorization_code", + "redirectUri": "https://example.com/redirect", + "codeVerifier": "xyz-secret-verifier" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Verifiable Credential downloaded and stored successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerifiableCredentialResponseDTO" + }, + "examples": { + "Success response": { + "value": { + "issuerDisplayName": "Mosip", + "issuerLogo": "https://example.com/logo.png", + "credentialTypeDisplayName": "National Identity Department Mosip", + "credentialTypeLogo": "https://example.com/credential-logo.png", + "credentialId": "1234567890" + } + } + } + } + } + }, + "400": { + "description": "Bad request - Wallet key is null / blank or Wallet ID is null / blank / mismatch with session Wallet ID or required params are missing / invalid or any invalid request data", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Wallet ID is null or blank": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet ID cannot be blank" + } + }, + "Invalid Accept-Language header": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Locale must be a 2-letter code" + } + }, + "Invalid Wallet ID": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Invalid Wallet ID. Session and request Wallet ID do not match" + } + }, + "Wallet ID not found in session": { + "value": { + "errorCode": "wallet_locked", + "errorMessage": "Wallet is locked" + } + }, + "Wallet key not found in session" : { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet key not found in session" + } + }, + "Invalid issuer": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "issuerId cannot be blank" + } + }, + "Invalid credentialConfigurationId": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "credentialConfigurationId cannot be blank" + } + }, + "Invalid code": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "code cannot be blank" + } + }, + "Invalid grantType": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "grantType cannot be blank" + } + }, + "Invalid redirectUri": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "redirectUri cannot be blank" + } + }, + "Invalid codeVerifier": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "codeVerifier cannot be blank" + } + }, + "Credential already exists for given Issuer and Credential Type in the wallet": { + "value": { + "errorCode": "credential_download_error", + "errorMessage": "Duplicate credential for issuer and type" + } + } + } + } + } + }, + "500": { + "description": "Internal server error - along with below examples it will return this status code when error occurred while serializing the VC response or unable to store the response in data share or unable to encrypt the credential", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Exception occurred while fetching Issuer config": { + "value": { + "errorCode": "credential_download_error", + "errorMessage": "Unable to fetch issuer configuration" + } + }, + "Failed to generate VC Credential request": { + "value": { + "errorCode": "credential_download_error", + "errorMessage": "Unable to generate credential request" + } + }, + "Signature verification failed for downloaded Credential": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + }, + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + }, + "503": { + "description": "Service unavailable - along with below examples it will return this status code when any error occurred while fetching Issuer wellknown or Auth Server wellknown", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "API is not accessible": { + "value": { + "errorCode": "credential_download_error", + "errorMessage": "Failed to download and store the credential" + } + }, + "Failed to download credential from Issuer": { + "value": { + "errorCode": "server_unavailable", + "errorMessage": "Unable to download credential from issuer" + } + }, + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } + } + } + } + }, + "get": { + "tags": [ + "Wallet Credentials" + ], + "summary": "Fetch all credentials for the given wallet", + "description": "This API is secured using session-based authentication. When a request is made, the session ID is extracted from the Cookie header and used to fetch session details from Redis for authentication. It then retrieves the wallet key from the session and uses it to decrypt all stored Verifiable Credentials for the given wallet. If successful, it returns a list of credentials otherwise an appropriate error is returned.", + "operationId": "fetchAllCredentialsForWallet", + "security": [ + { + "SessionAuth": [] + } + ], + "parameters": [ + { + "name": "walletId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the user's wallet from where the credential will be fetched" + }, + { + "name": "Accept-Language", + "in": "header", + "required": false, + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language preference in which the user expects the Verifiable Credential to be rendered. Input should be a 2-letter code (e.g., 'en' for English, 'fr' for French). If not provided, defaults to 'en'." + } + ], + "responses": { + "200": { + "description": "Credentials retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VerifiableCredentialResponseDTO" + } + }, + "examples": { + "Success response": { + "value": [ + { + "issuerDisplayName": "Mosip", + "issuerLogo": "https://example.com/logo.png", + "credentialTypeDisplayName": "National Identity Department Mosip", + "credentialTypeLogo": "https://example.com/credential-logo.png", + "credentialId": "1234567890" + } + ] + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Invalid Wallet Id": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Invalid Wallet ID. Session and request Wallet ID do not match" + } + }, + "Wallet ID is not available in session" : { + "value": { + "errorCode": "wallet_locked", + "errorMessage": "Wallet is locked" + } + }, + "Wallet key not found in session": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet key not found in session" + } + }, + "Invalid Accept-Language header": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Locale must be a 2-letter code" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + }, + "503": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } + } + } + } + } + }, + "/wallets/{walletId}/credentials/{credentialId}": { + "get": { + "tags": [ + "Wallet Credentials" + ], + "summary": "Fetch a Verifiable Credential by ID to either preview it or download it based on the requirement", + "description": "This API is protected using session-based authentication. When a request is received, the session ID is extracted from the Cookie header and used to fetch session data from Redis for user authentication. Upon successful authentication, the wallet key is retrieved from the session and used to decrypt the credential data obtained from the database.\n\nThe API retrieves a specific Verifiable Credential based on the provided wallet ID and credential ID. Depending on the action query parameter (inline or download) received in the request, the Content-Disposition header in the response is adjusted to either display the credential in the browser or prompt a file download. On success, the API returns the credential as a PDF byte stream. In case of any errors, an appropriate error response is returned.", + "operationId": "fetchVerifiableCredentialById", + "security": [ + { + "SessionAuth": [] + } + ], + "parameters": [ + { + "name": "walletId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the user's wallet" + }, + { + "name": "credentialId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the Verifiable Credential to be retrieved" + }, + { + "name": "action", + "in": "query", + "required": false, + "schema": { + "type": "string", + "default": "inline" + }, + "description": "Controls how the credential should be displayed. If it is sent as download, the response will include a header to download the file. For any other value or if not provided, it defaults to inline to preview the file in the browser." + }, + { + "name": "Accept-Language", + "in": "header", + "required": false, + "schema": { + "type": "string", + "default": "en" + }, + "description": "Language preference in which the user expects the Verifiable Credential to be rendered. Input should be a 2-letter code (e.g., 'en' for English, 'fr' for French). If not provided, defaults to 'en'." + }, + { + "name": "Accept", + "in": "header", + "required": true, + "schema": { + "type": "string", + "example": "application/pdf" + }, + "description": "The Accept header must be set to application/pdf to indicate that the response should be in PDF format. If not provided or set to a different value, the API will return a 400 Bad Request error." + } + ], + "responses": { + "200": { + "description": "Verifiable Credential fetched successfully", + "headers": { + "Content-Disposition": { + "description": "Indicates if the verifiable credential is displayed inline or downloaded", + "schema": { + "type": "string", + "example": "inline; filename=\"credential.pdf\"" + } + } + }, + "content": { + "application/pdf": { + "schema": { + "type": "string", + "format": "binary" + }, + "examples": { + "Success response": { + "value": "PDF content in Binary Format" + } + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Invalid Wallet Id": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Invalid Wallet ID. Session and request Wallet ID do not match" + } + }, + "Wallet ID is not available in session" : { + "value": { + "errorCode": "wallet_locked", + "errorMessage": "Wallet is locked" + } + }, + "Wallet key not found in session": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet key not found in session" + } + }, + "Invalid Accept-Language header": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Locale must be a 2-letter code" + } + }, + "Invalid Accept header" : { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Accept header must be application/pdf" + } + } + } + } + } + }, + "404": { + "description": "Not Found - Credential not found for given Wallet ID and Credential ID", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "No credentials found for the given Credential ID and Wallet ID": { + "value": { + "errorCode": "resource_not_found", + "errorMessage": "The requested resource doesn’t exist." + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Failed to decrypt the Credential": { + "value": { + "errorCode": "credential_fetch_error", + "errorMessage": "Decryption failed" + } + }, + "Credential Type is invalid": { + "value": { + "errorCode": "credential_fetch_error", + "errorMessage": "Invalid credential type configuration" + } + }, + "Exception occurred while fetching Issuer config": { + "value": { + "errorCode": "credential_download_error", + "errorMessage": "Unable to fetch issuer configuration" + } + }, + "Exception occurred when generating PDF": { + "value": { + "errorCode": "credential_download_error", + "errorMessage": "Failed to generate credential PDF" + } + }, + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + }, + "503": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } + } + } + } + }, + "delete": { + "tags": [ + "Wallet Credentials" + ], + "summary": "Delete a credential from a wallet", + "description": "This endpoint allows you to delete a specific credential from a wallet. The API is secured using session-based authentication. The session ID is extracted from the Cookie header to authenticate the user. The wallet ID is validated against the session to ensure the user has access to the wallet. If the credential is successfully deleted, a 200 OK response is returned.", + "operationId": "deleteCredential", + "security": [ + { + "SessionAuth": [] + } + ], + "parameters": [ + { + "name": "walletId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the wallet containing the credential" + }, + { + "name": "credentialId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the credential to be deleted" + } + ], + "responses": { + "200": { + "description": "Credential successfully deleted" + }, + "400": { + "description": "Provided request is invalid", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Invalid Wallet Id": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Invalid Wallet ID. Session and request Wallet ID do not match" + } + }, + "Wallet ID is not available in session" : { + "value": { + "errorCode": "wallet_locked", + "errorMessage": "Wallet is locked" + } + } + } + } + } + }, + "404": { + "description": "Credential not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Credential not found": { + "value": { + "errorCode": "resource_not_found", + "errorMessage": "The requested resource doesn’t exist." + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + } + } + } + }, + "/wallets": { + "post": { + "tags": [ + "Wallets" + ], + "summary": "Create a new wallet", + "description": "This API is secured using session-based authentication. The session ID is extracted from the Cookie header and used to retrieve session details from Redis for authentication. The user ID is then obtained from the session and along with the provided wallet name and PIN is used to create a new wallet in the database. If the wallet is created successfully, response including unique identifier of the wallet and and the provided name is returned otherwise an appropriate error response is provided.", + "operationId": "createWallet", + "security": [ + { + "SessionAuth": [] + } + ], + "requestBody": { + "required": true, + "description": "Wallet creation request containing the wallet name and wallet PIN", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWalletRequestDto" + }, + "examples": { + "Create wallet request": { + "value": { + "walletPin": "1234", + "confirmWalletPin": "1234", + "walletName": "My Personal Wallet" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Wallet created successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletResponseDTO" + }, + "examples": { + "Success response": { + "value": { + "walletId": "123e4567-e89b-12d3-a456-426614174000", + "walletName": "My Personal Wallet" + } + } + } + } + } + }, + "400": { + "description": "Invalid wallet creation request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Invalid User ID": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "User ID cannot be null or empty" + } + }, + "Invalid Wallet name": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet name must be alphanumeric with allowed special characters" + } + }, + "Invalid Wallet PIN": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "PIN must be numeric with 6 digits" + } + }, + "Entered Pin and Confirm Pin Mismatch": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Entered PIN and Confirm PIN do not match" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized user creating a wallet", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "User ID is not present in session": { + "value": { + "errorCode": "unauthorized", + "errorMessage": "User ID not found in session" + } + } + } + } + } + }, + "500": { + "description": "Internal server error while creating Wallet - error occurred while generating Wallet key or encrypting it with Wallet PIN", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + }, + "503": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } + } + } + } + }, + "get": { + "tags": [ + "Wallets" + ], + "summary": "Retrieve all wallets for the user", + "description": "This API is secured using session-based authentication. Upon receiving a request, the session is first retrieved using the session ID extracted from the Cookie header to authenticate the user. Once authenticated, the user's ID is obtained from the session stored in Redis. Using this user ID, the API fetches all wallets associated with the user from the database and returns them. If an error occurs while retrieving the wallets, an appropriate error response is returned.", + "operationId": "getWallets", + "security": [ + { + "SessionAuth": [] + } + ], + "responses": { + "200": { + "description": "List of wallets retrieved successfully", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WalletResponseDto" + } + }, + "examples": { + "Success response": { + "value": [ + { + "walletId": "123e4567-e89b-12d3-a456-426614174000", + "walletName": "My Personal Wallet" + }, + { + "walletId": "223e4567-e89b-12d3-a456-426614174001", + "walletName": "Wallet Main" + } + ] + } + } + } + } + }, + "400": { + "description": "Invalid Wallets fetching request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Invalid User ID": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "User ID cannot be null or empty" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized user fetching wallets", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "User ID is not present in session": { + "value": { + "errorCode": "unauthorized", + "errorMessage": "User ID not found in session" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + }, + "503": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } + } + } + } + } + }, + "/wallets/{walletId}": { + "delete": { + "tags": [ + "Wallets" + ], + "summary": "Delete a Wallet", + "description": "This endpoint allows you to delete a specific wallet. The API is secured using session-based authentication. The session ID is extracted from the Cookie header to authenticate the user. The user's ID is obtained from the session and used to validate ownership of the Wallet before deletion. If the Wallet is successfully deleted, a 200 OK response is returned; otherwise, an appropriate error response is returned.", + "operationId": "deleteWallet", + "security": [ + { + "SessionAuth": [] + } + ], + "parameters": [ + { + "name": "walletId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the Wallet to be deleted" + } + ], + "responses": { + "200": { + "description": "Wallet successfully deleted" + }, + "401": { + "description": "Unauthorized user deleting a Wallet", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "User ID not found in session": { + "value": { + "errorCode": "unauthorized", + "errorMessage": "User ID not found in session" + } + } + } + } + } + }, + "400": { + "description": "Invalid wallet deletion request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Invalid User ID": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "User ID cannot be null or empty" + } + }, + "Invalid Wallet ID": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Invalid Wallet ID. Session and request Wallet ID do not match" + } + }, + "Wallet ID not found in session": { + "value": { + "errorCode": "wallet_locked", + "errorMessage": "Wallet is locked" + } + }, + "Wallet not found in database": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet not found" + } + } + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + } + } + } + }, + "/wallets/{walletId}/unlock": { + "post": { + "tags": [ + "Wallets" + ], + "summary": "Unlock an existing Wallet", + "description": "This API is secured using session-based authentication. Upon receiving a request, the session is first retrieved using the session ID extracted from the Cookie header to authenticate the user. Once authenticated, the user's ID is obtained from the session stored in Redis. Using this user ID along with the provided wallet ID and PIN, the corresponding wallet key is fetched and stored in the session. If successful then the API returns a response containing the wallet ID otherwise an appropriate error response is returned.", + "operationId": "unlockWallet", + "security": [ + { + "SessionAuth": [] + } + ], + "parameters": [ + { + "name": "walletId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "Unique identifier of the wallet to be unlocked", + "example": "123e4567-e89b-12d3-a456-426614174000" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnlockWalletRequestDto" + }, + "examples": { + "Unlock wallet request": { + "value": { + "walletPin": "1234" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "Wallet unlocked successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WalletResponseDto" + }, + "examples": { + "Success response": { + "value": { + "walletId": "123e4567-e89b-12d3-a456-426614174000", + "walletName": "My Personal Wallet" + } + } + } + } + } + }, + "400": { + "description": "Invalid Wallet unlock request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Wallet not found": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "Wallet not found" + } + }, + "Invalid User ID": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "User ID cannot be null or empty" + } + }, + "Invalid Wallet PIN": { + "value": { + "errorCode": "invalid_request", + "errorMessage": "PIN must be numeric with 6 digits" + } + }, + "Incorrect Wallet PIN": { + "value": { + "errorCode": "invalid_pin", + "errorMessage": "Invalid PIN or wallet key provided" + } + } + } + } + } + }, + "401": { + "description": "Unauthorized user unlocking wallet", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "User ID is not present in session": { + "value": { + "errorCode": "unauthorized", + "errorMessage": "User ID not found in session" + } + } + } + } + } + }, + "500": { + "description": "Internal server error - any error occurred while decrypting the Wallet key with Wallet PIN or when fetching Wallet details from the database", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Unexpected Server Error": { + "value": { + "errorCode": "internal_server_error", + "errorMessage": "We are unable to process request now" + } + } + } + } + } + }, + "503": { + "description": "Service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "Database connection failure": { + "value": { + "errorCode": "database_unavailable", + "errorMessage": "Failed to connect to the database" + } + } + } + } + } + } + } + } + }, + "/auth/{provider}/token-login": { + "post": { + "tags": ["OAuth2 Authentication"], + "summary": "Login and create session using OAuth2 ID token", + "description": "This API accepts an OAuth2 ID token in the Authorization header and establishes a session by populating Spring Security context.\n\nFetch the ID token from a supported OAuth2 provider (such as Google or Microsoft) and provide it in the request as a Bearer token.", + "operationId": "loginWithOAuth2IdToken", + "security": [ + { + "bearerAuth": [] + } + ], + "parameters": [ + { + "name": "provider", + "in": "path", + "required": true, + "schema": { + "type": "string", + "example": "google" + }, + "description": "The OAuth2 provider to use for login. Example values: 'google', 'microsoft', 'facebook'." + }, + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string", + "example": "Bearer " + }, + "description": "The OAuth2 ID token that must be provided in the 'Authorization' header, prefixed with 'Bearer '." + } + ], + "responses": { + "200": { + "description": "Successfully logged in and session created", + "content": { + "application/json": { + "schema": { + "type": "string", + "description": "Success message indicating that the session has been created" + }, + "examples": { + "Success response": { + "value": "Session created." + } + } + } + } + }, + "400": { + "description": "Bad Request - Missing/invalid header or unsupported provider.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + }, + "examples": { + "UnsupportedProvider": { + "value": { + "errorCode": "INVALID_REQUEST", + "errorMessage": "Unsupported provider: provider123" + } + }, + "MissingToken": { + "value": { + "errorCode": "INVALID_REQUEST", + "errorMessage": "Bearer ID token required." + } + }, + "InvalidToken": { + "value": { + "errorCode": "invalid_token", + "errorMessage": "An error occurred while attempting to decode the Jwt: Signed JWT rejected: Invalid signature" + } + }, + "ExpiredToken": { + "value": { + "errorCode": "invalid_token", + "errorMessage": "An error occurred while attempting to decode the Jwt: Jwt expired at 2025-06-10T12:00:00Z" + } + } + } + } + } + } + } + } + } + }, + "components": { + "securitySchemes": { + "SessionAuth": { + "type": "apiKey", + "in": "cookie", + "name": "SESSION", + "description": "Session-based authentication using a session ID stored in the cookie. The client must send the 'SESSION' cookie (e.g., SESSION=) with each request." + }, + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "JWT-based authentication. Use a valid OAuth2 ID token (e.g., from Google or Microsoft) in the Authorization header as a Bearer token. Example: 'Authorization: Bearer eyJhbGciOi...'. This token will be used to authenticate the user and establish a session." + } + }, + "schemas": { + "UserMetadataDTO": { + "type": "object", + "properties": { + "display_name": { + "type": "string", + "description": "The display name of the user provided by the Identity Provider" + }, + "profile_picture_url": { + "type": "string", + "format": "uri", + "description": "The URL of the user's profile picture provided by the Identity Provider" + }, + "email": { + "type": "string", + "format": "email", + "description": "The email address of the user provided by the Identity Provider" + }, + "walletId": { + "type": "string", + "format": "email", + "description": "The unique identifier of the user's wallet. This is available if data is fetched from session else its null" + } + } + }, + "VerifiableCredentialRequestDTO": { + "type": "object", + "properties": { + "issuer": { + "type": "string", + "description": "The unique identifier of the issuer" + }, + "credentialConfigurationId": { + "type": "string", + "description": "The unique identifier of the credential type from the issuer well-known configuration" + }, + "code": { + "type": "string", + "description": "The authorization code received from the authorization server" + }, + "grantType": { + "type": "string", + "description": "The grant type for the authorization request" + }, + "redirectUri": { + "type": "string", + "description": "The redirect URI for the authorization request" + }, + "codeVerifier": { + "type": "string", + "description": "The code verifier used for PKCE (Proof Key for Code Exchange)" + } + }, + "required": [ + "issuer", + "credentialConfigurationId", + "code", + "grantType", + "redirectUri", + "codeVerifier" + ] + }, + "VerifiableCredentialResponseDTO": { + "type": "object", + "properties": { + "issuerDisplayName": { + "type": "string", + "description": "Name of the Issuer" + }, + "issuerLogo": { + "type": "string", + "description": "Logo of the Issuer" + }, + "credentialTypeDisplayName": { + "type": "string", + "description": "The name of the Credential Type issued by the Issuer" + }, + "credentialTypeLogo": { + "type": "string", + "description": "Logo of the Credential Type" + }, + "credentialId": { + "type": "string", + "description": "Unique Identifier of the Credential in the database" + } + } + }, + "CreateWalletRequestDto": { + "type": "object", + "properties": { + "walletPin": { + "type": "string", + "description": "PIN used to unlock the Wallet" + }, + "confirmWalletPin": { + "type": "string", + "description": "Re-entered PIN used to confirm the Wallet PIN" + }, + "walletName": { + "type": "string", + "description": "Name of the Wallet" + } + }, + "required": [ + "walletPin", + "confirmWalletPin" + ] + }, + "WalletResponseDTO": { + "type": "object", + "properties": { + "walletId": { + "type": "string", + "description": "Unique identifier of the Wallet" + }, + "walletName": { + "type": "string", + "description": "Wallet name provided by the user" + } + } + }, + "UnlockWalletRequestDto": { + "type": "object", + "properties": { + "walletName": { + "type": "string", + "description": "Name of the Wallet" + }, + "walletPin": { + "type": "string", + "description": "PIN used to unlock the Wallet" + } + }, + "required": [ + "walletPin" + ] + }, + "WalletResponseDto": { + "type": "object", + "properties": { + "walletId": { + "type": "string", + "description": "Unique identifier of the Wallet" + }, + "walletName" : { + "type": "string", + "description": "Name of the Wallet" + } + } + }, + "IssuerConfigurationSuccessResponse": { + "type": "object", + "properties": { + "response": { + "type": "object", + "properties": { + "credentials_supported": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Credential" + } + }, + "authorization_endpoint": { + "type": "string", + "format": "uri" + }, + "grant_types_supported": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Error" + }, + "example": [] + } + } + }, + "IssuerConfigurationErrorResponse": { + "type": "object", + "properties": { + "response": { + "type": "object", + "nullable": true + }, + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "Credential": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "display": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Display" + } + } + } + }, + "Display": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "locale": { + "type": "string" + }, + "logo": { + "type": "string" + } + } }, "Error": { "type": "object", "properties": { "errorCode": { - "type": "string" + "type": "string", + "description": "It represents the type or category of the error" }, "errorMessage": { - "type": "string" + "type": "string", + "description": "A human-readable message providing more details about the error" } } }, diff --git a/docs/postman-collections/LocalDevelopment.postman_environment.json b/docs/postman-collections/LocalDevelopment.postman_environment.json new file mode 100644 index 000000000..7b4da5048 --- /dev/null +++ b/docs/postman-collections/LocalDevelopment.postman_environment.json @@ -0,0 +1,111 @@ +{ + "id": "384e12f7-b68a-40f9-b2ff-49905294ba0a", + "name": "LocalDevelopment", + "values": [ + { + "key": "walletPin", + "value": "123456", + "type": "default", + "enabled": true + }, + { + "key": "walletId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "HOST", + "value": "http://localhost:8099/v1/mimoto", + "type": "default", + "enabled": true + }, + { + "key": "credentialId", + "value": "123445", + "type": "default", + "enabled": true + }, + { + "key": "clientId", + "value": "clientId", + "type": "secret", + "enabled": true + }, + { + "key": "clientSecret", + "value": "", + "type": "secret", + "enabled": true + }, + { + "key": "credentialConfigurationId", + "value": "MockVerifiableCredential", + "type": "default", + "enabled": true + }, + { + "key": "issuer", + "value": "Mock", + "type": "default", + "enabled": true + }, + { + "key": "codeVerifier", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "code", + "value": "", + "type": "any", + "enabled": true + }, + { + "key": "authClientId", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "authRedirectUri", + "value": "http://localhost:3004/redirect", + "type": "default", + "enabled": true + }, + { + "key": "authServerUrl", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "clientIdForVcDownload", + "value": "", + "type": "default", + "enabled": true + }, + { + "key": "redirectionUrlForVcDownload", + "value": "http://localhost:3004/redirect", + "type": "default", + "enabled": true + }, + { + "key": "individual_id", + "value": "xxxxxxxxxx", + "type": "default", + "enabled": true + }, + { + "key": "authCode", + "value": "", + "type": "any", + "enabled": true + } + ], + "_postman_variable_scope": "environment", + "_postman_exported_at": "2025-05-29T12:19:39.153Z", + "_postman_exported_using": "Postman/11.47.0" +} \ No newline at end of file diff --git a/docs/postman-collections/MIMOTO.postman_collection.json b/docs/postman-collections/MIMOTO.postman_collection.json index 14e640b1d..4f2de57f9 100644 --- a/docs/postman-collections/MIMOTO.postman_collection.json +++ b/docs/postman-collections/MIMOTO.postman_collection.json @@ -1,9 +1,9 @@ { "info": { - "_postman_id": "070c5a99-ffc3-48fb-9259-d9170916c728", + "_postman_id": "75c40eda-2aec-4fd7-a70b-ac2a40e8e04b", "name": "MIMOTO", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "26539037" + "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json", + "_exporter_id": "29153193" }, "item": [ { @@ -14,16 +14,7 @@ "request": { "method": "GET", "header": [], - "url": { - "raw": "{{HOST}}/issuers", - "host": [ - "{{HOST}}" - ], - "path": [ - "issuers", - "" - ] - } + "url": "{{HOST}}/issuers" }, "response": [] }, @@ -32,16 +23,7 @@ "request": { "method": "GET", "header": [], - "url": { - "raw": "{{HOST}}/issuers/StayProtected", - "host": [ - "{{HOST}}" - ], - "path": [ - "issuers", - "StayProtected" - ] - } + "url": "{{HOST}}/issuers/StayProtected" }, "response": [] }, @@ -50,17 +32,7 @@ "request": { "method": "GET", "header": [], - "url": { - "raw": "{{HOST}}/issuers/StayProtected/well-known-proxy", - "host": [ - "{{HOST}}" - ], - "path": [ - "issuers", - "StayProtected", - "well-known-proxy" - ] - } + "url": "{{HOST}}/issuers/StayProtected/well-known-proxy" }, "response": [] }, @@ -69,17 +41,7 @@ "request": { "method": "GET", "header": [], - "url": { - "raw": "{{HOST}}/issuers/StayProtected/configuration", - "host": [ - "{{HOST}}" - ], - "path": [ - "issuers", - "StayProtected", - "configuration" - ] - } + "url": "{{HOST}}/issuers/StayProtected/configuration" }, "response": [] }, @@ -156,16 +118,7 @@ } ] }, - "url": { - "raw": "{{HOST}}/credentials/download", - "host": [ - "{{HOST}}" - ], - "path": [ - "credentials", - "download" - ] - } + "url": "{{HOST}}/credentials/download" }, "response": [] }, @@ -227,16 +180,7 @@ } } }, - "url": { - "raw": "{{HOST}}/req/otp", - "host": [ - "{{HOST}}" - ], - "path": [ - "req", - "otp" - ] - } + "url": "{{HOST}}/req/otp" }, "response": [] }, @@ -265,16 +209,7 @@ } } }, - "url": { - "raw": "{{HOST}}/credentialshare/request", - "host": [ - "{{HOST}}" - ], - "path": [ - "credentialshare", - "request" - ] - } + "url": "{{HOST}}/credentialshare/request" }, "response": [] }, @@ -295,18 +230,7 @@ } } }, - "url": { - "raw": "{{HOST}}/credentialshare/request/status/c0fc00ae-6fc7-4e9f-b818-f51827582c2f", - "host": [ - "{{HOST}}" - ], - "path": [ - "credentialshare", - "request", - "status", - "c0fc00ae-6fc7-4e9f-b818-f51827582c2f" - ] - } + "url": "{{HOST}}/credentialshare/request/status/c0fc00ae-6fc7-4e9f-b818-f51827582c2f" }, "response": [] }, @@ -335,16 +259,7 @@ } } }, - "url": { - "raw": "{{HOST}}/credentialshare/download", - "host": [ - "{{HOST}}" - ], - "path": [ - "credentialshare", - "download" - ] - } + "url": "{{HOST}}/credentialshare/download" }, "response": [] } @@ -371,15 +286,7 @@ "mode": "raw", "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"individualId\": \"4536710485\",\n \"otpChannels\": [\n \"EMAIL\",\n \"PHONE\"\n ]\n }\n}" }, - "url": { - "raw": "{{HOST}}/binding-otp", - "host": [ - "{{HOST}}" - ], - "path": [ - "binding-otp" - ] - } + "url": "{{HOST}}/binding-otp" }, "response": [] }, @@ -401,15 +308,7 @@ "mode": "raw", "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"individualId\": \"6148072053\",\n \"challengeList\": [\n {\n \"authFactorType\": \"OTP\",\n \"challenge\": \"111111\",\n \"format\": \"alpha-numeric\"\n }\n ],\n \"publicKey\": \"-----BEGIN RSA PUBLIC KEY-----\\nMIICCgKCAgEArk/N6Us8TkfH+r1UGCqTGWegJ3UzqHXmj49uE3l9ygIPrTB7Hee8\\ntiVnlNKcQR/7EipumAcNzOFzaysUUIX8rh/QSdlwYYXvC4OT9fLg5tmToYc5x3pN\\nO1k7qg9fnMb/Ksr4D95xUUxQjMLlbojBNWjLhvdP4da1gWUeAdfjIfyLjZGYiD8r\\nUqsVqOfmrec9Y0nLsIsUq735NVl2WIP9mMfjXuRId4MgEE3mN6x/Gj9Ov0AWfb48\\n22wLgybggTdIJZlCBAnPn/n+oiowqUdXADAB4CIaVH8hDJ1fz3Ag2RetriqwrcLF\\nVSK+PyVuLD261BhIeQrgyHdKC6qilVBOYIMqtXjazCdjGICNFoEa1nVP6mu85RN1\\ny8TTS+4ATUTm44++TFSNfsEEpagvd6O/BWlj6mJNGB9dmtEohowwR5fcSHgKbGtP\\ng5J49fKeSDapEln2A/xL7Ubk8KFzzgcQvPWttGaazs7tqdOE4iOMkI72XFbyvIVz\\nzwQEB7vOuRJRclP30SkcR81u694vCtXJ2t2pFUMQVJXat9ypg5BtiZMwBtAnc0Pz\\nDWdGCR5LIpJIaDISmPumHGAzeeRfREbsoVaCdi99vORoxBjuscpEOa5+MgYDKM3S\\nsJZWl2E2/7Kt9KCRVJoqeEkFFKq696zKVD3qbhbZJG3TjBcgYfbN2jMCAwEAAQ==\\n-----END RSA PUBLIC KEY-----\\n\",\n \"authFactorType\": \"WLA\",\n \"format\": \"jwt\"\n }\n}" }, - "url": { - "raw": "{{HOST}}/wallet-binding", - "host": [ - "{{HOST}}" - ], - "path": [ - "wallet-binding" - ] - } + "url": "{{HOST}}/wallet-binding" }, "response": [] } @@ -423,17 +322,575 @@ "request": { "method": "GET", "header": [], + "url": "{{HOST}}/verifiers" + }, + "response": [] + } + ] + }, + { + "name": "Wallets", + "item": [ + { + "name": "Create Wallet", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Store the response body in a variable", + "let responseBody = pm.response.json();", + "", + "// Parse the response body to extract the walletId", + "let walletId = responseBody.walletId;", + "", + "pm.environment.set(\"walletId\", walletId);", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"walletName\": \"default\",\n \"walletPin\": 123456,\n \"confirmWalletPin\": 123456\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{HOST}}/wallets" + }, + "response": [] + }, + { + "name": "Get Wallets", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let responseBody = pm.response.json();", + "", + "// Parse the response body to extract the walletId", + "let walletId = responseBody[0].walletId;", + "", + "pm.environment.set(\"walletId\", walletId);", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [], + "url": "{{HOST}}/wallets" + }, + "response": [] + }, + { + "name": "Unlock Wallet", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"walletPin\": \"{{walletPin}}\"\n}" + }, + "url": "{{HOST}}/wallets/{{walletId}}/unlock" + }, + "response": [] + }, + { + "name": "Delete Wallet", + "request": { + "method": "DELETE", + "header": [], + "url": "{{HOST}}/wallets/" + }, + "response": [] + } + ] + }, + { + "name": "Users", + "item": [ + { + "name": "Get User Profile", + "request": { + "method": "GET", + "header": [ + { + "key": "Cookie", + "value": "SESSION={{SESSION_TOKEN}}", + "type": "text" + } + ], + "url": "{{HOST}}/users/me" + }, + "response": [] + } + ] + }, + { + "name": "Wallet Credentials", + "item": [ + { + "name": "authCode for VC download (pre-requisite)", + "item": [ + { + "name": "Get CSRF token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var token = pm.cookies.get(\"XSRF-TOKEN\")", + "pm.collectionVariables.set(\"csrf_token\", token);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "packages": {}, + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{authServerUrl}}/csrf/token" + }, + "response": [] + }, + { + "name": "Authorize / OAuthdetails request V2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var btoa = require('btoa');", + "", + "var token = pm.cookies.get(\"XSRF-TOKEN\")", + "pm.collectionVariables.set(\"csrf_token\", token);", + "", + "pm.test(\"Validate transactionId\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.response.transactionId).not.equals(null);", + " pm.collectionVariables.set(\"transaction_id\", jsonData.response.transactionId);", + "});", + "", + "pm.test(\"Validate auth factors\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.response.authFactors[0].name).to.eql(pm.environment.get(\"expected_amr\"));", + "});", + "", + "pm.test(\"set oauth-details-hash\", function () {", + " var jsonData = pm.response.json();", + " var sha256Hash = CryptoJS.SHA256(JSON.stringify(jsonData.response));", + " var base64Encoded = sha256Hash.toString(CryptoJS.enc.Base64);", + " // Remove padding characters", + " base64Encoded = base64Encoded.replace(/=+$/, '');", + " // Replace '+' with '-' and '/' with '_' to convert to base64 URL encoding", + " base64Encoded = base64Encoded.replace(/\\+/g, '-').replace(/\\//g, '_');", + " pm.collectionVariables.set(\"oauth_details_key\", jsonData.response.transactionId);", + " pm.collectionVariables.set(\"oauth_details_hash\", base64Encoded);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "eval(pm.globals.get('pmlib_code'))", + "", + "const pkce = pmlib.pkceChallenge();", + "pm.collectionVariables.set(\"codeChallenge\",pkce.code_challenge);", + "pm.collectionVariables.set(\"codeChallengeMethod\",pkce.code_challenge_method);", + "pm.environment.set(\"codeVerifier\",pkce.code_verifier);", + "", + "", + "// Generate a random nonce (UUID-style or random hex)", + "const nonce = Math.random().toString(36).substring(2) + Date.now().toString(36);", + "pm.collectionVariables.set(\"nonce\", nonce);", + "", + "", + "const generateRandomString = (length = 43, charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~') => {", + " let randomString = '';", + " for (let i = 0; i < length; i++) {", + " const randomIndex = Math.floor(Math.random() * charset.length);", + " randomString += charset[randomIndex];", + " }", + " return randomString;", + "};", + "", + "const state = generateRandomString();", + "pm.collectionVariables.set(\"stateForDownload\", state);", + "", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-XSRF-TOKEN", + "value": "{{csrf_token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"clientId\": \"{{clientIdForVcDownload}}\",\n \"scope\": \"mock_identity_vc_ldp\",\n \"responseType\": \"code\",\n \"redirectUri\": \"{{redirectionUrlForVcDownload}}\",\n \"display\": \"popup\",\n \"prompt\": \"login\",\n \"acrValues\": \"mosip:idp:acr:generated-code\",\n \"nonce\" : \"{{nonce}}\",\n \"state\" : \"{{stateForDownload}}\",\n \"claimsLocales\" : \"en\",\n \"codeChallenge\" : \"{{codeChallenge}}\",\n \"codeChallengeMethod\" : \"{{codeChallengeMethod}}\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{authServerUrl}}/authorization/v2/oauth-details" + }, + "response": [] + }, + { + "name": "Send OTP", + "request": { + "method": "POST", + "header": [ + { + "key": "X-XSRF-TOKEN", + "value": "{{csrf_token}}", + "type": "text" + }, + { + "key": "oauth-details-key", + "value": "{{oauth_details_key}}", + "type": "text" + }, + { + "key": "oauth-details-hash", + "value": "{{oauth_details_hash}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"transactionId\": \"{{transaction_id}}\",\n \"individualId\": \"{{individual_id}}\",\n \"otpChannels\" : [\"email\",\"phone\"],\n \"captchaToken\" : \"dummy\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{authServerUrl}}/authorization/send-otp" + }, + "response": [] + }, + { + "name": "Authenticate User", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var token = pm.cookies.get(\"XSRF-TOKEN\")", + "pm.collectionVariables.set(\"csrf_token\", token);" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-XSRF-TOKEN", + "value": "{{csrf_token}}", + "type": "text" + }, + { + "key": "oauth-details-key", + "value": "{{oauth_details_key}}", + "type": "text" + }, + { + "key": "oauth-details-hash", + "value": "{{oauth_details_hash}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"transactionId\": \"{{transaction_id}}\",\n \"individualId\": \"{{individual_id}}\",\n \"challengeList\" : [\n {\n \"authFactorType\" : \"OTP\",\n \"challenge\" : \"111111\",\n \"format\" : \"alpha-numeric\"\n }\n ]\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{authServerUrl}}/authorization/v3/authenticate" + }, + "response": [] + }, + { + "name": "Authorization Code", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var token = pm.cookies.get(\"XSRF-TOKEN\")", + "pm.collectionVariables.set(\"csrf_token\", token);", + "", + "var jsonData = pm.response.json();", + "pm.expect(jsonData.response.code).not.equals(null);", + "pm.environment.set(\"authCode\", jsonData.response.code);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "followRedirects": false + }, + "request": { + "method": "POST", + "header": [ + { + "key": "X-XSRF-TOKEN", + "value": "{{csrf_token}}", + "type": "text" + }, + { + "key": "oauth-details-key", + "value": "{{oauth_details_key}}", + "type": "text" + }, + { + "key": "oauth-details-hash", + "value": "{{oauth_details_hash}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"transactionId\": \"{{transaction_id}}\",\n \"acceptedClaims\": [],\n \"permittedAuthorizeScopes\" : []\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": "{{authServerUrl}}/authorization/auth-code" + }, + "response": [] + } + ] + }, + { + "name": "Save Credential", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "let response = pm.response.json();", + "pm.environment.set(\"credentialId\", response.credentialId);", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept-Language", + "value": "en", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"grantType\": \"authorization_code\",\n \"code\": \"{{authCode}}\",\n \"codeVerifier\": \"{{codeVerifier}}\",\n \"redirectUri\": \"http://localhost:3004/redirect\",\n \"credentialConfigurationId\": \"{{credentialConfigurationId}}\",\n \"issuer\": \"{{issuer}}\"\n}" + }, + "url": "{{HOST}}/wallets/{{walletId}}/credentials" + }, + "response": [] + }, + { + "name": "Fetch All Credentials", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept-Language", + "value": "en", + "type": "text" + } + ], "url": { - "raw": "{{HOST}}/verifiers", + "raw": "{{HOST}}/wallets/{{walletId}}/credentials", "host": [ "{{HOST}}" ], "path": [ - "verifiers" + "wallets", + "{{walletId}}", + "credentials" + ], + "query": [ + { + "key": "locale", + "value": "", + "disabled": true + } ] } }, "response": [] + }, + { + "name": "View or Save Credential", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept-Language", + "value": "en", + "type": "text" + }, + { + "key": "Accept", + "value": "application/pdf", + "type": "text" + } + ], + "url": { + "raw": "{{HOST}}/wallets/{{walletId}}/credentials/{{credentialId}}?action=inline", + "host": [ + "{{HOST}}" + ], + "path": [ + "wallets", + "{{walletId}}", + "credentials", + "{{credentialId}}" + ], + "query": [ + { + "key": "action", + "value": "inline" + } + ] + } + }, + "response": [] + }, + { + "name": "Delete Credential", + "request": { + "method": "DELETE", + "header": [], + "url": "{{HOST}}/wallets/{{walletId}}/credentials/{{credentialId}}" + }, + "response": [] + } + ] + }, + { + "name": "Authentication", + "item": [ + { + "name": "Token login", + "request": { + "auth": { + "type": "oauth2", + "oauth2": { + "clientSecret": "{{clientSecret}}", + "clientId": "{{clientId}}", + "accessTokenUrl": "https://oauth2.googleapis.com/token", + "scope": "profile email", + "redirect_uri": "http://localhost:8099/v1/mimoto/oauth2/callback/google", + "useBrowser": false, + "authUrl": "https://accounts.google.com/o/oauth2/auth?prompt=select_account", + "tokenName": "AuthToken", + "addTokenTo": "header" + } + }, + "method": "POST", + "header": [], + "url": "{{HOST}}/auth/google/token-login" + }, + "response": [] + }, + { + "name": "Logout", + "request": { + "method": "POST", + "header": [], + "url": "{{HOST}}/logout" + }, + "response": [] } ] }, @@ -442,15 +899,7 @@ "request": { "method": "GET", "header": [], - "url": { - "raw": "{{HOST}}/allProperties", - "host": [ - "{{HOST}}" - ], - "path": [ - "allProperties" - ] - } + "url": "{{HOST}}/allProperties" }, "response": [] }, @@ -468,19 +917,81 @@ } } }, - "url": { - "raw": "{{HOST}}/req/individualId/otp", - "host": [ - "{{HOST}}" - ], - "path": [ - "req", - "individualId", - "otp" - ] - } + "url": "{{HOST}}/req/individualId/otp" }, "response": [] } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "proof_jwt", + "value": "" + }, + { + "key": "codeChallenge", + "value": "" + }, + { + "key": "codeChallengeMethod", + "value": "" + }, + { + "key": "codeVerifier", + "value": "" + }, + { + "key": "code", + "value": "" + }, + { + "key": "authCode", + "value": "" + }, + { + "key": "nonce", + "value": "" + }, + { + "key": "stateForDownload", + "value": "" + }, + { + "key": "csrf_token", + "value": "" + }, + { + "key": "transaction_id", + "value": "" + }, + { + "key": "oauth_details_key", + "value": "" + }, + { + "key": "oauth_details_hash", + "value": "" + } ] } \ No newline at end of file diff --git a/helm/mimoto/templates/clusterrolebinding.yaml b/helm/mimoto/templates/clusterrolebinding.yaml deleted file mode 100644 index a00045072..000000000 --- a/helm/mimoto/templates/clusterrolebinding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -kind: ClusterRoleBinding -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -metadata: - labels: {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonLabels }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} - {{- end }} - name: {{ template "common.names.fullname" . }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "common.names.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ template "mimoto.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} diff --git a/helm/mimoto/templates/deployment.yaml b/helm/mimoto/templates/deployment.yaml index 9ca07eb7f..21225ee56 100644 --- a/helm/mimoto/templates/deployment.yaml +++ b/helm/mimoto/templates/deployment.yaml @@ -8,8 +8,8 @@ metadata: name: {{ template "common.names.fullname" . }} annotations: {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} spec: replicas: {{ .Values.replicaCount }} {{- if .Values.updateStrategy }} @@ -21,8 +21,8 @@ spec: metadata: annotations: {{- if or .Values.podAnnotations .Values.metrics.enabled }} - {{- include "mimoto.podAnnotations" . | nindent 8 }} - {{- end }} + {{- include "mimoto.podAnnotations" . | nindent 8 }} + {{- end }} labels: {{- include "common.labels.standard" . | nindent 8 }} {{- if .Values.podLabels }} {{- include "common.tplvalues.render" (dict "value" .Values.podLabels "context" $) | nindent 8 }} @@ -51,37 +51,50 @@ spec: priorityClassName: {{ .Values.priorityClassName | quote }} {{- end }} {{- if .Values.podSecurityContext.enabled }} - securityContext: {{- omit .Values.podSecurityContext "enabled" | toYaml | nindent 8 }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} {{- end }} initContainers: - {{- if and .Values.volumePermissions.enabled .Values.persistence.enabled }} + {{- if and .Values.volumePermissions.enabled .Values.extraVolumeMounts }} - name: volume-permissions - image: {{ include "mimoto.volumePermissions.image" . }} + image: "{{ .Values.volumePermissions.image.registry }}/{{ .Values.volumePermissions.image.repository }}:{{ .Values.volumePermissions.image.tag }}" imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} command: - - %%commands%% + - /bin/bash + - -c + - | + {{- range .Values.extraVolumeMounts }} + mkdir -p {{ .mountPath }} + chown -R {{ $.Values.podSecurityContext.fsGroup }}:{{ $.Values.podSecurityContext.fsGroup }} {{ .mountPath }} + chmod -R 775 {{ .mountPath }} + {{- end }} securityContext: runAsUser: 0 {{- if .Values.volumePermissions.resources }} resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} {{- end }} volumeMounts: - - name: foo - mountPath: bar + {{- toYaml .Values.extraVolumeMounts | nindent 12 }} {{- end }} {{- if .Values.enable_insecure }} - {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} + {{- include "common.tplvalues.render" (dict "value" .Values.initContainers "context" $) | nindent 8 }} {{- end }} containers: - - name: mimoto + - name: {{ .Chart.Name }} image: {{ template "mimoto.image" . }} imagePullPolicy: {{ .Values.image.pullPolicy }} {{- if .Values.lifecycleHooks }} lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.lifecycleHooks "context" $) | nindent 12 }} {{- end }} {{- if .Values.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} + securityContext: + runAsUser: {{ .Values.containerSecurityContext.runAsUser }} + runAsNonRoot: {{ .Values.containerSecurityContext.runAsNonRoot }} + {{- with .Values.containerSecurityContext.capabilities }} + capabilities: + {{- toYaml . | nindent 14 }} + {{- end }} + {{- end }} {{- if .Values.command }} command: {{- include "common.tplvalues.render" (dict "value" .Values.command "context" $) | nindent 12 }} {{- end }} @@ -90,27 +103,27 @@ spec: {{- end }} env: - name: container_user - value: {{ .Values.containerSecurityContext.runAsUser }} + value: {{ .Values.containerSecurityContext.runAsUser | quote }} - name: JDK_JAVA_OPTIONS - value: {{ .Values.additionalResources.javaOpts }} + value: {{ .Values.additionalResources.javaOpts | quote }} - name: KEYSTORE_P12_PATH - value: {{ .Values.volumes.secrets.mimotooidc.path }} + value: {{ .Values.volumes.secrets.mimotooidc.path | quote }} {{- if .Values.extraEnvVars }} {{- include "common.tplvalues.render" (dict "value" .Values.extraEnvVars "context" $) | nindent 12 }} {{- end }} envFrom: {{- if .Values.extraEnvVarsCM }} - {{- range .Values.extraEnvVarsCM }} - - configMapRef: - name: {{ . }} - {{- end }} - {{- end }} - {{- if .Values.extraEnvVarsSecret }} - {{- range .Values.extraEnvVarsSecret }} - - secretRef: - name: {{ . }} - {{- end }} - {{- end }} + {{- range .Values.extraEnvVarsCM }} + - configMapRef: + name: {{ . | quote }} + {{- end }} + {{- end }} + {{- if .Values.extraEnvVarsSecret }} + {{- range .Values.extraEnvVarsSecret }} + - secretRef: + name: {{ . | quote }} + {{- end }} + {{- end }} volumeMounts: - name: landing-folder mountPath: {{ .Values.persistence.mountDir }} @@ -125,6 +138,9 @@ spec: name: cacerts subPath: cacerts {{- end }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} ports: [] {{- if .Values.resources }} resources: {{- toYaml .Values.resources | nindent 12 }} @@ -144,9 +160,9 @@ spec: {{- else if .Values.customReadinessProbe }} readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} {{- end }} - {{- if .Values.sidecars }} - {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} - {{- end }} + {{- if .Values.sidecars }} + {{- include "common.tplvalues.render" ( dict "value" .Values.sidecars "context" $) | nindent 8 }} + {{- end }} volumes: - name: landing-folder {{- if .Values.persistence.enabled }} @@ -166,3 +182,6 @@ spec: - name: cacerts emptyDir: {} {{- end }} + {{- if .Values.extraVolumes }} + {{- toYaml .Values.extraVolumes | nindent 8 }} + {{- end }} \ No newline at end of file diff --git a/helm/mimoto/templates/pvc.yaml b/helm/mimoto/templates/pvc.yaml index 7db9bc8a7..6fb903db1 100644 --- a/helm/mimoto/templates/pvc.yaml +++ b/helm/mimoto/templates/pvc.yaml @@ -10,17 +10,17 @@ metadata: {{- end }} annotations: {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - "helm.sh/resource-policy": keep + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + "helm.sh/resource-policy": keep spec: accessModes: {{- if not (empty .Values.persistence.accessModes) }} {{- range .Values.persistence.accessModes }} - - {{ . | quote }} + - {{ . | quote }} {{- end }} {{- else }} - - {{ .Values.persistence.accessMode | quote }} + - {{ .Values.persistence.accessMode | quote }} {{- end }} resources: requests: @@ -29,4 +29,32 @@ spec: {{- if .Values.persistence.dataSource }} dataSource: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.dataSource "context" $) | nindent 4 }} {{- end }} -{{- end }} + {{- end }} + {{- if .Values.extraVolumes }} + {{- range .Values.extraVolumes }} + {{- if and (eq .kind "persistentVolumeClaim") (eq .name "mimoto-encryption-volume") (not .persistentVolumeClaim.claimName) }} +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: mimoto-keygen-keymanager + namespace: {{ $.Release.Namespace | quote }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if $.Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + "helm.sh/resource-policy": keep +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ $.Values.encryption.volume.size | default "1Gi" | quote }} + {{- include "common.storage.class" (dict "persistence" $.Values.persistence "global" $.Values.global) | nindent 2 }} + {{- end }} + {{- end }} + {{- end }} \ No newline at end of file diff --git a/helm/mimoto/templates/secrets.yaml b/helm/mimoto/templates/secrets.yaml new file mode 100644 index 000000000..667dba388 --- /dev/null +++ b/helm/mimoto/templates/secrets.yaml @@ -0,0 +1,24 @@ +{{- if .Values.mimoto.secrets }} +{{- range $secret_name, $secret_value := .Values.mimoto.secrets }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secret_name }} + namespace: {{ $.Release.Namespace }} + labels: + {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + {{- range $key, $value := $secret_value }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} +--- +{{- end }} +{{- end }} diff --git a/helm/mimoto/values.yaml b/helm/mimoto/values.yaml index 3a0f132de..757dbff3d 100644 --- a/helm/mimoto/values.yaml +++ b/helm/mimoto/values.yaml @@ -32,7 +32,7 @@ replicaCount: 1 service: type: ClusterIP port: 80 - containerPort: 8088 + containerPort: 8099 ## loadBalancerIP for the SuiteCRM Service (optional, cloud specific) ## ref: http://kubernetes.io/docs/user-guide/services/#type-loadbalancer ## @@ -69,14 +69,14 @@ image: ## Configure extra options for liveness and readiness probes ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes -## + ## TODO: enable probes once health urls are available startupProbe: enabled: true httpGet: path: /v1/mimoto/actuator/health - port: 8088 + port: 8099 initialDelaySeconds: 0 periodSeconds: 10 timeoutSeconds: 5 @@ -87,7 +87,7 @@ livenessProbe: enabled: true httpGet: path: /v1/mimoto/actuator/health - port: 8088 + port: 8099 initialDelaySeconds: 120 periodSeconds: 10 timeoutSeconds: 5 @@ -98,7 +98,7 @@ readinessProbe: enabled: true httpGet: path: /v1/mimoto/actuator/health - port: 8088 + port: 8099 initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 @@ -126,11 +126,11 @@ resources: # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. limits: - cpu: 500m - memory: 3000Mi + cpu: 1000m + memory: 5000Mi requests: - cpu: 100m - memory: 1000Mi + cpu: 200m + memory: 2000Mi additionalResources: ## Specify any JAVA_OPTS string here. These typically will be specified in conjunction with above resources @@ -236,30 +236,55 @@ updateStrategy: ## Additional environment variables to set ## Example: -## extraEnvVars: -## - name: FOO -## value: "bar" -## extraEnvVars: [] +# - name: REDIS_HOST +# valueFrom: +# configMapKeyRef: +# name: redis-config +# key: redis-host +# - name: REDIS_PORT +# valueFrom: +# configMapKeyRef: +# name: redis-config +# key: redis-port +# - name: REDIS_PASSWORD +# valueFrom: +# secretKeyRef: +# name: redis +# key: redis-password +# - name: DB_DBUSER_PASSWORD +# valueFrom: +# secretKeyRef: +# name: db-common-secrets +# key: db-dbuser-password +# - name: MOSIP_MIMOTO_DATABASE_HOSTNAME +# value: postgres-postgresql.postgres ## ConfigMap with extra environment variables that used ## extraEnvVarsCM: - - global + - inji-stack-config - config-server-share - artifactory-share ## Secret with extra environment variables ## -extraEnvVarsSecret: [] +extraEnvVarsSecret: + - google-client ## Extra volumes to add to the deployment ## -extraVolumes: [] +extraVolumes: + - name: mimoto-encryption-volume + persistentVolumeClaim: + claimName: mimoto-keygen-keymanager ## Extra volume mounts to add to the container ## -extraVolumeMounts: [] +extraVolumeMounts: + - name: mimoto-encryption-volume + mountPath: /home/mosip/encryption/encryptionkeystore.p12 + subPath: encryptionkeystore.p12 ## Add init containers to the pods. ## Example: @@ -318,7 +343,7 @@ sidecars: {} persistence: enabled: false - storageClass: longhorn + storageClass: nfs-csi ## If defined, storageClassName: ## If set to "-", storageClassName: "", which disables dynamic provisioning ## If undefined (the default) or set to null, no storageClassName spec is @@ -349,11 +374,11 @@ persistence: ## volumePermissions: Change the owner and group of the persistent volume mountpoint to runAsUser:fsGroup values from the securityContext section. ## volumePermissions: - enabled: false + enabled: true image: registry: docker.io - repository: bitnami/bitnami-shell - tag: "10" + repository: bitnami/os-shell + tag: "12-debian-12-r47" pullPolicy: Always ## Optionally specify an array of imagePullSecrets. ## Secrets must be manually created in the namespace. @@ -455,10 +480,9 @@ metrics: istio: enabled: true gateways: - - istio-system/internal - - istio-system/public - prefix1: /residentmobileapp - prefix2: /v1/mimoto + - inji-web-gateway + prefix1: /v1/mimoto + prefix2: /residentmobileapp rewrite: /v1/mimoto enable_insecure: false @@ -468,3 +492,9 @@ volumes: mimotooidc: key: oidckeystore.p12 path: /home/mosip/certs + +mimoto: + secrets: + google-client: + MOSIP_INJIWEB_GOOGLE_CLIENT_ID: "your-app-google-client-id" + MOSIP_INJIWEB_GOOGLE_CLIENT_SECRET: "your-app-google-client-secret" diff --git a/partner-onboarder/README.md b/partner-onboarder/README.md index f757cd409..1ef9f8d24 100644 --- a/partner-onboarder/README.md +++ b/partner-onboarder/README.md @@ -3,7 +3,19 @@ ## Overview * Upload certificate for default partners. Refer [mosip-onboarding repo](https://github.com/mosip/mosip-onboarding). -## Install +## Install +* Create a directory for onboarder on the NFS server at `/srv/nfs//onboarder/`: +``` +mkdir -p /srv/nfs/mosip//onboarder/ +``` +* Ensure the directory has 777 permissions: +``` +chmod 777 /srv/nfs/mosip//onboarder +``` +* Add the following entry to the /etc/exports file: +``` +/srv/nfs/mosip//onboarder *(ro,sync,no_root_squash,no_all_squash,insecure,subtree_check) +``` * Set `values.yaml` to run onboarder for specific modules. * run `./install.sh`. ``` diff --git a/partner-onboarder/copy_cm.sh b/partner-onboarder/copy_cm.sh index 8070b2c88..d4a95fa07 100755 --- a/partner-onboarder/copy_cm.sh +++ b/partner-onboarder/copy_cm.sh @@ -3,8 +3,8 @@ # DST_NS: Destination namespace COPY_UTIL=./copy_cm_func.sh -DST_NS=injiweb +DST_NS=$( printenv NS ) -$COPY_UTIL configmap global default $DST_NS -$COPY_UTIL configmap keycloak-env-vars keycloak $DST_NS -$COPY_UTIL configmap keycloak-host keycloak $DST_NS +$COPY_UTIL configmap inji-stack-config default $DST_NS +$COPY_UTIL configmap keycloak-env-vars keycloak $DST_NS || { echo "Ignore this error if Keycloak is deployed externally and its details have been added to values.yaml. If not, please update values.yaml as per the instructions in README.md."; true; } +$COPY_UTIL configmap keycloak-host keycloak $DST_NS || { echo "Ignore this error if Keycloak is deployed externally and its details have been added to values.yaml. If not, please update values.yaml as per the instructions in README.md."; true; } diff --git a/partner-onboarder/copy_secrets.sh b/partner-onboarder/copy_secrets.sh index c9693c236..6c46332b3 100755 --- a/partner-onboarder/copy_secrets.sh +++ b/partner-onboarder/copy_secrets.sh @@ -3,8 +3,7 @@ # DST_NS: Destination namespace COPY_UTIL=./copy_cm_func.sh -DST_NS=injiweb - -$COPY_UTIL secret s3 s3 $DST_NS -$COPY_UTIL secret keycloak keycloak $DST_NS -$COPY_UTIL secret keycloak-client-secrets keycloak $DST_NS +DST_NS=injiwebDST_NS=$( printenv NS ) +#$COPY_UTIL secret s3 s3 $DST_NS +$COPY_UTIL secret keycloak keycloak $DST_NS || { echo "Ignore this error if Keycloak is deployed externally and its details have been added to values.yaml. If not, please update values.yaml as per the instructions in README.md."; true; } +$COPY_UTIL secret keycloak-client-secrets keycloak $DST_NS || { echo "Ignore this error if Keycloak is deployed externally and its details have been added to values.yaml. If not, please update values.yaml as per the instructions in README.md."; true; } diff --git a/partner-onboarder/install.sh b/partner-onboarder/install.sh index bdb130dcb..033711df3 100644 --- a/partner-onboarder/install.sh +++ b/partner-onboarder/install.sh @@ -68,7 +68,12 @@ fi echo "Invalid input. Please respond with Y (yes) or N (no)." fi done - + echo "Choose the namespace to run the onboarder in:" + read -p "Enter the custom namespace: " custom_ns + if [[ -z "$custom_ns" ]]; then + echo "No namespace provided; 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. It will add an ssl certificate in onboarder docker. Only recommended in testing env." @@ -83,7 +88,7 @@ fi ENABLE_INSECURE='--set onboarding.configmaps.onboarding.ENABLE_INSECURE=true'; fi - NS=injiweb + export NS=$custom_ns CHART_VERSION=0.0.1-develop echo Create $NS namespace @@ -106,6 +111,7 @@ fi $NFS_OPTION \ $S3_OPTION \ --set onboarding.variables.push_reports_to_s3=$push_reports_to_s3 \ + --set extraEnvVars[0].name=customnamespace,extraEnvVars[0].value=$NS \ --set extraEnvVarsCM[0]=global \ --set extraEnvVarsCM[1]=keycloak-env-vars \ --set extraEnvVarsCM[2]=keycloak-host \ @@ -116,11 +122,11 @@ fi echo Updating mimoto-oidc-keystore-password value kubectl -n $NS create secret generic mimoto-oidc-keystore-password --from-literal=mimoto-oidc-keystore-password='mosip123' --dry-run=client -o yaml | kubectl apply -f - - ./copy_cm_func.sh secret mimoto-oidc-keystore-password injiweb config-server + ./copy_cm_func.sh secret mimoto-oidc-keystore-password $NS config-server echo Updating Mimoto wallet binding partner api key and Mimoto OIDC Partner Client ID - ./copy_cm_func.sh secret mimoto-wallet-binding-partner-api-key injiweb config-server - ./copy_cm_func.sh secret mimoto-oidc-partner-clientid injiweb config-server + ./copy_cm_func.sh secret mimoto-wallet-binding-partner-api-key $NS config-server + ./copy_cm_func.sh secret mimoto-oidc-partner-clientid $NS config-server kubectl -n config-server set env --keys=mimoto-wallet-binding-partner-api-key --from secret/mimoto-wallet-binding-partner-api-key deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_ kubectl -n config-server set env --keys=mimoto-oidc-partner-clientid --from secret/mimoto-oidc-partner-clientid deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_ kubectl -n config-server set env --keys=mimoto-oidc-keystore-password --from secret/mimoto-oidc-keystore-password deployment/config-server --prefix=SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_ @@ -139,4 +145,4 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true 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_onboarder # calling function +installing_onboarder # calling function \ No newline at end of file diff --git a/partner-onboarder/values.yaml b/partner-onboarder/values.yaml index 0d4e584b1..448d0409f 100644 --- a/partner-onboarder/values.yaml +++ b/partner-onboarder/values.yaml @@ -3,6 +3,30 @@ # repository: mosipdev/partner-onboarder # tag: develop +### If you are running Mimoto Onboarder in a separate inji cluster where PMS or Keycloak doesn't exist, +### then uncomment lines 8 to 25 and update the values accordingly. +#extraEnvVarsCM: [] +#extraEnvVarsSecret: [] +#extraEnvVars: +# - name: installation-domain +# value: dev.mosip.net +# - name: mosip-api-host +# value: api.dev.mosip.net + #### pms domain +# - name: mosip-api-internal-host +# value: api-internal.dev.mosip.net +# - name: keycloak-external-url +# value: https://iam.dev.mosip.net +# - name: KEYCLOAK_ADMIN_USER +# value: admin + ###keycloak admin password +# - name: admin-password +# value: 5wIOk3DgKW +# - name: mosip_pms_client_secret +# value: GnUMOiUSJm9oKjYE +# - name: mosip-esignet-host +# value: esignet.dev.mosip.net + onboarding: modules: - name: mobileid diff --git a/pom.xml b/pom.xml index 718ac9fc3..8c1eebff9 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 io.mosip mimoto - 0.17.0-SNAPSHOT + 0.18.0-SNAPSHOT mimoto https://github.com/mosip/mimoto Mobile server backend supporting Inji. @@ -54,7 +54,8 @@ 3.2.3 - + 3.4.0 + 3.4.1 2.1.210 @@ -91,8 +92,10 @@ 2.9.2 1.5.10 0.9.1 + 42.7.2 + 0.7.0 - **/constant/**,**/config/**,**/httpfilter/**,**/cache/**,**/dto/**,**/entity/**,**/model/**,**/exception/**,**/repository/**,**/security/**,**/*Config.java,**/*BootApplication.java,**/*VertxApplication.java,**/cbeffutil/**,**/core/http/**,**/util/** + **/constant/**,**/config/**,**/httpfilter/**,**/cache/**,**/dto/**,**/entity/**,**/dbentity/**,**/model/**,**/exception/**,**/repository/**,**/security/**,**/*Config.java,**/*BootApplication.java,**/*VertxApplication.java,**/cbeffutil/**,**/core/http/**,**/util/** **/dto/**,**/entity/**,**/config/** @@ -141,22 +144,12 @@ io.mosip vcverifier-jar - 1.1.0 + 1.3.0-SNAPSHOT io.micrometer micrometer-registry-prometheus - - io.springfox - springfox-swagger2 - ${swagger.version} - - - io.springfox - springfox-swagger-ui - ${swagger.version} - org.springdoc springdoc-openapi-starter-webmvc-ui @@ -200,12 +193,31 @@ test - + + org.springframework.security + spring-security-core + test + org.springframework.boot spring-boot-starter-data-jpa ${spring.boot.version} + + org.springframework.boot + spring-boot-starter-oauth2-client + ${spring.boot.version} + + + org.springframework.session + spring-session-data-redis + ${session.redis.version} + + + org.springframework.boot + spring-boot-starter-data-redis + ${starter.redis.version} + org.junit.vintage junit-vintage-engine @@ -222,6 +234,12 @@ io.mosip.kernel kernel-auth-adapter ${mosip.kernel.auth.adapter.version} + + + org.springframework.security + spring-security-test + + @@ -431,7 +449,7 @@ io.mosip pixelpass-jar - 0.5.0 + 0.7.0-SNAPSHOT @@ -462,19 +480,50 @@ jakarta.validation-api 3.0.2 - + + + org.postgresql + postgresql + ${postgresql.version} + + + io.mosip.kernel + kernel-keymanager-service + 1.3.0-beta.2 + lib + + + org.springframework.security + spring-security-test + + + + + io.vertx + vertx-web + 3.9.13 + - ossrh - Central Repository - https://oss.sonatype.org/content/repositories/snapshots + ossrh-central + MavenCentralRepository + https://central.sonatype.com/repository/maven-snapshots default true + + central + MavenCentral + default + https://repo1.maven.org/maven2 + + false + + google-maven https://maven.google.com/ @@ -488,11 +537,11 @@ ossrh - https://oss.sonatype.org/content/repositories/snapshots + https://central.sonatype.com/repository/maven-snapshots ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://central.sonatype.com/api/v1/publisher @@ -637,14 +686,13 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.14 + org.sonatype.central + central-publishing-maven-plugin + ${central.publishing.maven.plugin.version} true - ossrh - https://oss.sonatype.org/ - false + ossrh + false diff --git a/src/main/java/io/mosip/mimoto/MimotoServiceApplication.java b/src/main/java/io/mosip/mimoto/MimotoServiceApplication.java index 9d6ab1a8a..6c9bfc0d0 100644 --- a/src/main/java/io/mosip/mimoto/MimotoServiceApplication.java +++ b/src/main/java/io/mosip/mimoto/MimotoServiceApplication.java @@ -4,16 +4,19 @@ import io.mosip.kernel.biometrics.spi.CbeffUtil; import io.mosip.kernel.cbeffutil.impl.CbeffImpl; +import io.mosip.kernel.keygenerator.bouncycastle.KeyGenerator; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.security.SecurityScheme; import lombok.extern.slf4j.Slf4j; import org.json.simple.JSONObject; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration; +import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; @@ -22,16 +25,29 @@ @SpringBootApplication(scanBasePackages = { "io.mosip.mimoto.*", "io.mosip.kernel.websub.*", + "io.mosip.kernel.cryptomanager.*", + "io.mosip.kernel.keymanager.*", + "io.mosip.kernel.keymanagerservice.helper", + "io.mosip.kernel.keymanagerservice.util", + "io.mosip.kernel.keymanagerservice.service", + "io.mosip.kernel.keymanagerservice.validator", + "io.mosip.kernel.crypto", "${mosip.auth.adapter.impl.basepackage}" }, exclude = { - SecurityAutoConfiguration.class, - DataSourceAutoConfiguration.class, - HibernateJpaAutoConfiguration.class, - CacheAutoConfiguration.class + SecurityAutoConfiguration.class }) +@EntityScan(basePackages = {"io.mosip.mimoto.model", "io.mosip.kernel.keymanagerservice.entity"}) +@EnableJpaRepositories(basePackages = {"io.mosip.mimoto.repository", "io.mosip.kernel.keymanagerservice.repository"}) @Slf4j @EnableScheduling @EnableAsync +@SecurityScheme( + name = "SessionAuth", + type = SecuritySchemeType.APIKEY, + in = SecuritySchemeIn.COOKIE, + paramName = "SESSION", + description = "Session-based authentication using a session ID stored in the cookie. The client must send the 'SESSION' cookie (e.g., SESSION=) with each request." +) public class MimotoServiceApplication { @Bean @@ -48,6 +64,11 @@ public ThreadPoolTaskScheduler taskScheduler() { return threadPoolTaskScheduler; } + @Bean + public KeyGenerator keyGenerator() { + return new KeyGenerator(); + } + public static JSONObject getGitProp() { try { return (new ObjectMapper()).readValue( diff --git a/src/main/java/io/mosip/mimoto/config/AppConfig.java b/src/main/java/io/mosip/mimoto/config/AppConfig.java new file mode 100644 index 000000000..6420327e5 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/config/AppConfig.java @@ -0,0 +1,20 @@ +package io.mosip.mimoto.config; + + +import io.mosip.pixelpass.PixelPass; +import io.mosip.vercred.vcverifier.CredentialsVerifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AppConfig { + @Bean + public CredentialsVerifier credentialsVerifier() { + return new CredentialsVerifier(); + } + + @Bean + public PixelPass pixelPass() { + return new PixelPass(); + } +} diff --git a/src/main/java/io/mosip/mimoto/config/Config.java b/src/main/java/io/mosip/mimoto/config/Config.java index cb69a53ff..5d452e2a9 100644 --- a/src/main/java/io/mosip/mimoto/config/Config.java +++ b/src/main/java/io/mosip/mimoto/config/Config.java @@ -1,28 +1,42 @@ package io.mosip.mimoto.config; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import io.mosip.mimoto.security.oauth2.CustomOAuth2UserService; +import io.mosip.mimoto.security.oauth2.OAuth2AuthenticationFailureHandler; +import io.mosip.mimoto.security.oauth2.OAuth2AuthenticationSuccessHandler; +import io.mosip.mimoto.service.LogoutService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; +import org.springframework.http.HttpStatus; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.HttpStatusEntryPoint; +import org.springframework.session.SessionRepository; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import static io.mosip.mimoto.exception.ErrorConstants.LOGIN_SESSION_INVALIDATE_EXCEPTION; @Configuration @EnableWebSecurity @EnableMethodSecurity @Order(1) +@Slf4j public class Config { @Value("${mosipbox.public.url}") @@ -37,14 +51,32 @@ public class Config { @Value("${mosip.security.origins:localhost:8088}") private String origins; + @Value("${mosip.security.ignore-auth-urls}") + private String[] ignoreAuthUrls; + + @Value("${mosip.inji.web.url}") + private String injiWebUrl; + + @Autowired + private LogoutService logoutService; + @Bean @ConfigurationProperties(prefix = "mosip.inji") public Map injiConfig() { return new HashMap<>(); } + @Autowired + private OAuth2AuthenticationSuccessHandler oAuth2AuthenticationSuccessHandler; + + @Autowired + private OAuth2AuthenticationFailureHandler oAuth2AuthenticationFailureHandler; + + @Autowired + private CustomOAuth2UserService customOAuth2UserService; + @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + public SecurityFilterChain filterChain(HttpSecurity http, SessionRepository sessionRepository) throws Exception { if (!isCSRFEnable) { http.csrf(AbstractHttpConfigurer::disable); } @@ -58,18 +90,77 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { headersEntry.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin); }); + setupOauth2Config(http, sessionRepository); + + http.exceptionHandling(exceptionHandling -> + exceptionHandling.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)) + ); + + return http.build(); } + private void setupOauth2Config(HttpSecurity http, SessionRepository sessionRepository) throws Exception { + configureOAuth2Login(http); + configureLogout(http, sessionRepository); + configureAuthorization(http); + configureSessionManagement(http); + } + + private void configureOAuth2Login(HttpSecurity http) throws Exception { + http.oauth2Login(oauth2Login -> oauth2Login + .loginPage(injiWebUrl + "/") + .authorizationEndpoint(authorization -> authorization.baseUri("/oauth2/authorize")) + .redirectionEndpoint(redirect -> redirect.baseUri("/oauth2/callback/*")) + .userInfoEndpoint(userInfo -> userInfo.userService(customOAuth2UserService)) + .successHandler(oAuth2AuthenticationSuccessHandler) + .failureHandler(oAuth2AuthenticationFailureHandler) + ); + } + + private void configureLogout(HttpSecurity http, SessionRepository sessionRepository) throws Exception { + http.logout(logout -> logout + .invalidateHttpSession(false) + .clearAuthentication(false) + .logoutUrl("/logout") + .logoutSuccessHandler((request, response, authentication) -> { + try { + logoutService.handleLogout(request, response, sessionRepository); + } catch (OAuth2AuthenticationException e) { + response.setStatus(e.getStatus().value()); + response.setContentType("application/json"); + String jsonResponse = String.format("{\"errors\":[{\"errorCode\":\"%s\",\"errorMessage\":\"%s\"}]}", + LOGIN_SESSION_INVALIDATE_EXCEPTION.getErrorCode(), + LOGIN_SESSION_INVALIDATE_EXCEPTION.getErrorMessage()); + response.getWriter().write(jsonResponse); + } + }) + .clearAuthentication(true) + ); + } + + + private void configureAuthorization(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authz -> authz + .requestMatchers(ignoreAuthUrls).permitAll() + .anyRequest().authenticated() + ); + } + + private void configureSessionManagement(HttpSecurity http) throws Exception { + http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)); + } + // Define CORS configuration @Bean public CorsConfigurationSource corsConfigurationSource() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); - corsConfiguration.addAllowedOrigin(origins); // Allow all origins + corsConfiguration.setAllowedOrigins(Arrays.asList(origins.split(","))); // Allow all origins corsConfiguration.addAllowedHeader("*"); // Allow all headers corsConfiguration.addAllowedMethod("*"); // Allow all HTTP methods + corsConfiguration.setAllowCredentials(true);// Allow cookies to be sent source.registerCorsConfiguration("/**", corsConfiguration); return source; } diff --git a/src/main/java/io/mosip/mimoto/config/IssuersValidationConfig.java b/src/main/java/io/mosip/mimoto/config/IssuersValidationConfig.java index de18e7b29..b706846b1 100644 --- a/src/main/java/io/mosip/mimoto/config/IssuersValidationConfig.java +++ b/src/main/java/io/mosip/mimoto/config/IssuersValidationConfig.java @@ -1,5 +1,6 @@ package io.mosip.mimoto.config; +import io.mosip.mimoto.dto.IssuerDTO; import io.mosip.mimoto.dto.IssuersDTO; import io.mosip.mimoto.exception.ApiNotAccessibleException; import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; @@ -12,10 +13,10 @@ import org.springframework.stereotype.Component; import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.Errors; +import org.springframework.validation.FieldError; import org.springframework.validation.Validator; import java.io.IOException; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; @Slf4j @@ -23,61 +24,74 @@ public class IssuersValidationConfig implements ApplicationRunner { @Autowired IssuersService issuersService; - @Autowired private Validator validator; - private final String VALIDATION_ERROR_MSG = "\n\nValidation failed in Mimoto-issuers-config.json:"; - @Override public void run(ApplicationArguments args) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { log.info("Validation for mimoto-issuers-config.json STARTED"); - AtomicReference errors = new AtomicReference<>(); - AtomicReference fieldErrors = new AtomicReference<>(""); + List allErrors = new ArrayList<>(); AtomicReference> credentialIssuers = new AtomicReference<>(new HashSet<>()); IssuersDTO issuerDTOList = null; try { issuerDTOList = issuersService.getAllIssuers(); } catch (Exception e) { - log.error(VALIDATION_ERROR_MSG , e); + log.error(VALIDATION_ERROR_MSG, e); throw new RuntimeException(VALIDATION_ERROR_MSG); } if (issuerDTOList != null) { - issuerDTOList.getIssuers().forEach(issuerDTO -> { + for (int index = 0; index < issuerDTOList.getIssuers().size(); index++) { + IssuerDTO issuerDTO = issuerDTOList.getIssuers().get(index); if (!issuerDTO.getProtocol().equals("OTP")) { - errors.set(new BeanPropertyBindingResult(issuerDTO, "issuerDTO")); - validator.validate(issuerDTO, errors.get()); + Errors errors = new BeanPropertyBindingResult(issuerDTO, "issuerDTO"); + validator.validate(issuerDTO, errors); String issuerId = issuerDTO.getIssuer_id(); - if (errors.get() != null && errors.get().hasErrors()) { - log.error("{} for issuer {}: {}", VALIDATION_ERROR_MSG, issuerId, errors.get()); + boolean issuerHasErrors = false; + + StringBuilder issuerErrors = new StringBuilder(); + issuerErrors.append(String.format("Errors for issuer at index: %d with issuerId - %s%n", index, issuerId)); + + if (errors.hasErrors()) { + issuerHasErrors = true; + errors.getFieldErrors().stream() + .sorted(Comparator.comparing(FieldError::getField)) + .forEach(error -> issuerErrors.append(String.format("- %s %s%n", error.getField(), error.getDefaultMessage()))); + } + String[] tokenEndpointArray = issuerDTO.getToken_endpoint().split("/"); Set currentIssuers = credentialIssuers.get(); + if (!currentIssuers.add(issuerId)) { - log.error(VALIDATION_ERROR_MSG + "duplicate value found " + issuerId); - throw new RuntimeException(VALIDATION_ERROR_MSG); + issuerHasErrors = true; + issuerErrors.append("- Duplicate value found for the issuerId. More than one issuer is having the same issuerId").append("\n"); } + if (!tokenEndpointArray[tokenEndpointArray.length - 1].equals(issuerId)) { - log.error(VALIDATION_ERROR_MSG + "TokenEndpoint does not match with the credential issuer " + issuerId); - throw new RuntimeException(VALIDATION_ERROR_MSG); + issuerHasErrors = true; + issuerErrors.append("- TokenEndpoint does not match with the credential issuerId").append("\n"); + } + + if (issuerHasErrors) { + allErrors.add(issuerErrors.toString()); } + credentialIssuers.set(currentIssuers); } - }); + }; } - - if (errors.get() != null && errors.get().hasErrors()) { - errors.get().getFieldErrors().forEach(error -> { - fieldErrors.set(fieldErrors.get() + error.getField() + " " + error.getDefaultMessage() + "\n"); - }); - log.error(VALIDATION_ERROR_MSG + fieldErrors.get()); - throw new RuntimeException(VALIDATION_ERROR_MSG); + if (!allErrors.isEmpty()) { + StringBuilder fieldErrorsBuilder = new StringBuilder(); + allErrors.forEach(fieldErrorsBuilder::append); + String fieldErrorsString = VALIDATION_ERROR_MSG + "\n" + fieldErrorsBuilder; + log.error(fieldErrorsString); + throw new RuntimeException(fieldErrorsString); } log.info("Validation for mimoto-issuers-config.json COMPLETED"); } -} +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/config/JwtConfig.java b/src/main/java/io/mosip/mimoto/config/JwtConfig.java new file mode 100644 index 000000000..bcbec0233 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/config/JwtConfig.java @@ -0,0 +1,18 @@ +package io.mosip.mimoto.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; + +@Configuration +public class JwtConfig { + + @Bean + @Qualifier("googleJwtDecoder") + public JwtDecoder googleIdTokenDecoder(@Value("${spring.security.oauth2.client.provider.google.jwk-set-uri}") String jwkSetUri) { + return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build(); + } +} diff --git a/src/main/java/io/mosip/mimoto/config/KeyManagerConfig.java b/src/main/java/io/mosip/mimoto/config/KeyManagerConfig.java new file mode 100644 index 000000000..ebd80e3cf --- /dev/null +++ b/src/main/java/io/mosip/mimoto/config/KeyManagerConfig.java @@ -0,0 +1,37 @@ +package io.mosip.mimoto.config; + +import io.mosip.kernel.keymanagerservice.dto.KeyPairGenerateRequestDto; +import io.mosip.kernel.keymanagerservice.service.KeymanagerService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class KeyManagerConfig implements ApplicationRunner { + + + @Autowired + private KeymanagerService keymanagerService; + + + @Override + public void run(ApplicationArguments args) throws Exception { + log.info("===================== MIMOTOSERVICE ROOT KEY CHECK ========================"); + String objectType = "CSR"; + KeyPairGenerateRequestDto rootKeyRequest = new KeyPairGenerateRequestDto(); + rootKeyRequest.setApplicationId("ROOT"); + // Set the reference id to empty string, as keymanager is expecting the same for initialization + rootKeyRequest.setReferenceId(org.apache.commons.lang3.StringUtils.EMPTY); + keymanagerService.generateMasterKey(objectType, rootKeyRequest); + log.info("===================== MIMOTOSERVICE MASTER KEY CHECK ========================"); + KeyPairGenerateRequestDto masterKeyRequest = new KeyPairGenerateRequestDto(); + masterKeyRequest.setApplicationId("MIMOTO"); + // Set the reference id to empty string, as keymanager is expecting the same for initialization + masterKeyRequest.setReferenceId(org.apache.commons.lang3.StringUtils.EMPTY); + keymanagerService.generateMasterKey(objectType, masterKeyRequest); + log.info("===================== MIMOTO KEY SETUP COMPLETED ========================"); + } +} diff --git a/src/main/java/io/mosip/mimoto/config/oauth2/OAuth2ProviderProperties.java b/src/main/java/io/mosip/mimoto/config/oauth2/OAuth2ProviderProperties.java new file mode 100644 index 000000000..e79b72929 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/config/oauth2/OAuth2ProviderProperties.java @@ -0,0 +1,29 @@ +package io.mosip.mimoto.config.oauth2; + +import io.mosip.mimoto.dto.ProviderDataConfig; +import jakarta.annotation.PostConstruct; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; + +@Configuration +@ConfigurationProperties(prefix = "spring.security.oauth2.client") +@Slf4j +@Data +public class OAuth2ProviderProperties { + private Map provider; + + @PostConstruct + public void logProviders() { + if (provider == null || provider.isEmpty()) { + log.warn("No OAuth2 providers configured under 'spring.security.oauth2.client.provider'"); + } else { + log.info("Loaded OAuth2 providers: {}", provider.keySet()); + provider.forEach((key, prov) -> log.debug("Provider {}: {}", key, prov)); + } + } + +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/constant/SessionKeys.java b/src/main/java/io/mosip/mimoto/constant/SessionKeys.java new file mode 100644 index 000000000..581671419 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/constant/SessionKeys.java @@ -0,0 +1,8 @@ +package io.mosip.mimoto.constant; + +public class SessionKeys { + public static final String USER_ID = "userId"; + public static final String USER_METADATA = "userMetadata"; + public static final String WALLET_KEY = "wallet_key"; + public static final String WALLET_ID = "wallet_id"; +} diff --git a/src/main/java/io/mosip/mimoto/constant/SigningAlgorithm.java b/src/main/java/io/mosip/mimoto/constant/SigningAlgorithm.java new file mode 100644 index 000000000..545483478 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/constant/SigningAlgorithm.java @@ -0,0 +1,37 @@ +package io.mosip.mimoto.constant; + +import com.nimbusds.jose.JWSAlgorithm; + +public enum SigningAlgorithm { + RS256(JWSAlgorithm.RS256, "RSA"), + ES256(JWSAlgorithm.ES256, "EC"), + ES256K(JWSAlgorithm.ES256K, "EC"), + ED25519(JWSAlgorithm.EdDSA, "Ed25519"); + + private final JWSAlgorithm jwsAlgorithm; + private final String keyFactoryAlgorithm; + + SigningAlgorithm(JWSAlgorithm jwsAlgorithm, String keyFactoryAlgorithm) { + this.jwsAlgorithm = jwsAlgorithm; + this.keyFactoryAlgorithm = keyFactoryAlgorithm; + } + + public JWSAlgorithm getJWSAlgorithm() { + return jwsAlgorithm; + } + + public String getKeyFactoryAlgorithm() { + return keyFactoryAlgorithm; + } + + public static SigningAlgorithm fromString(String value) { + return switch (value.toUpperCase()) { + case "RS256" -> RS256; + case "ES256" -> ES256; + case "ES256K" -> ES256K; + case "ED25519" -> ED25519; + default -> throw new IllegalArgumentException("Unsupported signing algorithm: " + value); + }; + } + +} diff --git a/src/main/java/io/mosip/mimoto/constant/SwaggerExampleConstants.java b/src/main/java/io/mosip/mimoto/constant/SwaggerExampleConstants.java index f22d378e6..8174ed3ee 100644 --- a/src/main/java/io/mosip/mimoto/constant/SwaggerExampleConstants.java +++ b/src/main/java/io/mosip/mimoto/constant/SwaggerExampleConstants.java @@ -3,33 +3,72 @@ public class SwaggerExampleConstants { public static final String ALL_PROPERTIES_EXAMPLE = """ - { - "response": { - "modelDownloadMaxRetry": "10", - "audience": "ida-binding", - "datashare.host": "https://datashare-inji.collab.mosip.net", - "allowedInternalAuthType": "otp,bio-Finger,bio-Iris,bio-Face", - "openId4VCIDownloadVCTimeout": "30000", - "qr.code.height": "650", - "ovp.error.redirect.url.pattern": "%s?error=%s&error_description=%s", - "vcDownloadMaxRetry": "10", - "ovp.qrdata.pattern": "INJI_OVP://https://injiweb.collab.mosip.net/authorize?response_type=vp_token&resource=%s&presentation_definition=%s", - "minStorageRequiredForAuditEntry": "2", - "minStorageRequired": "2", - "vcDownloadPoolInterval": "6000", - "issuer": "residentapp", - "ovp.redirect.url.pattern": "%s#vp_token=%s&presentation_submission=%s", - "allowedAuthType": "demo,otp,bio-Finger,bio-Iris,bio-Face", - "allowedEkycAuthType": "demo,otp,bio-Finger,bio-Iris,bio-Face", - "warningDomainName": "https://api.collab.mosip.net", - "qr.code.width": "650", - "web.host": "https://injiweb.collab.mosip.net", - "web.redirect.url": "https://injiweb.collab.mosip.net/authorize", - "aboutInjiUrl": "https://docs.mosip.io/inji/inji-mobile-wallet/overview", - "qr.data.size.limit": "10000", - "faceSdkModelUrl": "https://api.collab.mosip.net/inji" - }, - "errors": [] - } - """; + { + "response": { + "modelDownloadMaxRetry": "10", + "audience": "ida-binding", + "datashare.host": "https://datashare-inji.collab.mosip.net", + "allowedInternalAuthType": "otp,bio-Finger,bio-Iris,bio-Face", + "openId4VCIDownloadVCTimeout": "30000", + "qr.code.height": "650", + "ovp.error.redirect.url.pattern": "%s?error=%s&error_description=%s", + "vcDownloadMaxRetry": "10", + "ovp.qrdata.pattern": "INJI_OVP://https://injiweb.collab.mosip.net/authorize?response_type=vp_token&resource=%s&presentation_definition=%s", + "minStorageRequiredForAuditEntry": "2", + "minStorageRequired": "2", + "vcDownloadPoolInterval": "6000", + "issuer": "residentapp", + "ovp.redirect.url.pattern": "%s#vp_token=%s&presentation_submission=%s", + "allowedAuthType": "demo,otp,bio-Finger,bio-Iris,bio-Face", + "allowedEkycAuthType": "demo,otp,bio-Finger,bio-Iris,bio-Face", + "warningDomainName": "https://api.collab.mosip.net", + "qr.code.width": "650", + "web.host": "https://injiweb.collab.mosip.net", + "web.redirect.url": "https://injiweb.collab.mosip.net/authorize", + "aboutInjiUrl": "https://docs.mosip.io/inji/inji-mobile-wallet/overview", + "qr.data.size.limit": "10000", + "faceSdkModelUrl": "https://api.collab.mosip.net/inji" + }, + "errors": [] + } + """; + + public static final String FETCH_USER_PROFILE_FROM_DB_SUCCESS = """ + { + "display_name": "John Doe", + "profile_picture_url": "https://example.com/profile.jpg", + "email": "john.doe@example.com", + } + """; + public static final String FETCH_USER_CACHE_PROFILE_SUCCESS = """ + { + "display_name": "John Doe", + "profile_picture_url": "https://example.com/profile.jpg", + "email": "john.doe@example.com", + "walletId": "123e4567-e89b-12d3-a456-426614174000" + } + """; + + public static final String FETCH_ALL_WALLETS_OF_USER_SUCCESS = """ + [ + { + "walletId": "123e4567-e89b-12d3-a456-426614174000" + }, + { + "walletId": "223e4567-e89b-12d3-a456-426614174001" + } + ] + """; + + public static final String FETCH_ALL_CREDENTIALS_OF_WALLET_SUCCESS = """ + [ + { + "issuer_name": "Mosip", + "issuer_logo": "https://example.com/logo.png", + "credential_type": "MosipVerifiableCredential", + "credential_type_logo": "https://example.com/credential-logo.png", + "credential_id": "1234567890" + } + ] + """; } diff --git a/src/main/java/io/mosip/mimoto/constant/SwaggerLiteralConstants.java b/src/main/java/io/mosip/mimoto/constant/SwaggerLiteralConstants.java index 761c76c82..e468b9da9 100644 --- a/src/main/java/io/mosip/mimoto/constant/SwaggerLiteralConstants.java +++ b/src/main/java/io/mosip/mimoto/constant/SwaggerLiteralConstants.java @@ -2,78 +2,100 @@ public class SwaggerLiteralConstants { - /* Attestation Controller */ - public static final String ATTESTATION_NAME = "Attestation" ; - public static final String ATTESTATION_DESCRIPTION = "All the attestation related endpoints" ; + /* Attestation Controller */ + public static final String ATTESTATION_NAME = "Attestation"; + public static final String ATTESTATION_DESCRIPTION = "All the attestation related endpoints"; + /* Common Inji Controller */ + public static final String COMMON_INJI_NAME = "Inji Wallet Properties"; + public static final String COMMON_INJI_DESCRIPTION = "All endpoints related to Inji Wallet properties"; + public static final String COMMON_INJI_GET_PROPERTIES_SUMMARY = "Retrieve all Inji Wallet properties"; + public static final String COMMON_INJI_GET_PROPERTIES_DESCRIPTION = "This endpoint allow you to retrieve all the Inji Wallet properties"; - /* Common Inji Controller */ - public static final String COMMON_INJI_NAME = "Inji Wallet Properties" ; - public static final String COMMON_INJI_DESCRIPTION = "All endpoints related to Inji Wallet properties" ; - public static final String COMMON_INJI_GET_PROPERTIES_SUMMARY = "Retrieve all Inji Wallet properties" ; - public static final String COMMON_INJI_GET_PROPERTIES_DESCRIPTION = "This endpoint allow you to retrieve all the Inji Wallet properties" ; + /* Credentials Controller */ + public static final String CREDENTIALS_NAME = "Credentials download using OpenId4VCI"; + public static final String CREDENTIALS_DESCRIPTION = "All the credentials related endpoints"; + public static final String CREDENTIALS_DOWNLOAD_VC_SUMMARY = "Download credentials as PDF"; + public static final String CREDENTIALS_DOWNLOAD_VC_DESCRIPTION = "This endpoint allow you to download the credentials as PDF"; - /* Credentials Controller */ - public static final String CREDENTIALS_NAME = "Credentials download using OpenId4VCI" ; - public static final String CREDENTIALS_DESCRIPTION = "All the credentials related endpoints" ; - public static final String CREDENTIALS_DOWNLOAD_VC_SUMMARY = "Download credentials as PDF" ; - public static final String CREDENTIALS_DOWNLOAD_VC_DESCRIPTION = "This endpoint allow you to download the credentials as PDF" ; + /* Credentials Share Controller */ + public static final String CREDENTIALS_SHARE_NAME = "Credential Share"; + public static final String CREDENTIALS_SHARE_DESCRIPTION = "All the credential download endpoints"; + public static final String CREDENTIALS_SHARE_HANDLE_SUBSCRIBED_EVENT_SUMMARY = "Notify through web sub once credential is downloaded"; + public static final String CREDENTIALS_SHARE_HANDLE_SUBSCRIBED_EVENT_DESCRIPTION = "This endpoint allow web sub to callback once the credential is issued"; + public static final String CREDENTIALS_SHARE_REQUEST_VC_SUMMARY = "request for credential issue"; + public static final String CREDENTIALS_SHARE_REQUEST_VC_DESCRIPTION = "This endpoint allow you to request for credential issue"; + public static final String CREDENTIALS_SHARE_REQUEST_VC_STATUS_SUMMARY = "polling for credential issue status"; + public static final String CREDENTIALS_SHARE_REQUEST_VC_STATUS_DESCRIPTION = "This endpoint allow you to poll for credential issue status"; + public static final String CREDENTIALS_SHARE_DOWNLOAD_VC_SUMMARY = "Download the credential using OTP Flow"; + public static final String CREDENTIALS_SHARE_DOWNLOAD_VC_DESCRIPTION = "This endpoint allow you to download credential issued"; - /* Credentials Share Controller */ - public static final String CREDENTIALS_SHARE_NAME = "Credential Share" ; - public static final String CREDENTIALS_SHARE_DESCRIPTION = "All the credential download endpoints" ; - public static final String CREDENTIALS_SHARE_HANDLE_SUBSCRIBED_EVENT_SUMMARY = "Notify through web sub once credential is downloaded" ; - public static final String CREDENTIALS_SHARE_HANDLE_SUBSCRIBED_EVENT_DESCRIPTION = "This endpoint allow web sub to callback once the credential is issued" ; - public static final String CREDENTIALS_SHARE_REQUEST_VC_SUMMARY = "request for credential issue" ; - public static final String CREDENTIALS_SHARE_REQUEST_VC_DESCRIPTION = "This endpoint allow you to request for credential issue" ; - public static final String CREDENTIALS_SHARE_REQUEST_VC_STATUS_SUMMARY = "polling for credential issue status" ; - public static final String CREDENTIALS_SHARE_REQUEST_VC_STATUS_DESCRIPTION = "This endpoint allow you to poll for credential issue status" ; - public static final String CREDENTIALS_SHARE_DOWNLOAD_VC_SUMMARY = "Download the credential using OTP Flow" ; - public static final String CREDENTIALS_SHARE_DOWNLOAD_VC_DESCRIPTION = "This endpoint allow you to download credential issued" ; + /* IDP Controller */ + public static final String IDP_NAME = "Wallet Binding"; + public static final String IDP_DESCRIPTION = "All the authorization related endpoints"; + public static final String IDP_BINDING_OTP_SUMMARY = "Invoke OTP request for wallet binding"; + public static final String IDP_BINDING_OTP_DESCRIPTION = "This endpoint allow you to invoke OTP for wallet binding"; + public static final String IDP_WALLET_BINDING_SUMMARY = "Wallet Binding"; + public static final String IDP_WALLET_BINDING_DESCRIPTION = "This endpoint allow you to perform the wallet binding"; + public static final String IDP_GET_TOKEN_SUMMARY = "Retrieve accessToken for OIDC flow"; + public static final String IDP_GET_TOKEN_DESCRIPTION = "This endpoint allow you to retrieve the access token in exchange for authorization code"; - /* IDP Controller */ - public static final String IDP_NAME = "Wallet Binding" ; - public static final String IDP_DESCRIPTION = "All the authorization related endpoints" ; - public static final String IDP_BINDING_OTP_SUMMARY = "Invoke OTP request for wallet binding" ; - public static final String IDP_BINDING_OTP_DESCRIPTION = "This endpoint allow you to invoke OTP for wallet binding" ; - public static final String IDP_WALLET_BINDING_SUMMARY = "Wallet Binding" ; - public static final String IDP_WALLET_BINDING_DESCRIPTION = "This endpoint allow you to perform the wallet binding" ; - public static final String IDP_GET_TOKEN_SUMMARY = "Retrieve accessToken for OIDC flow" ; - public static final String IDP_GET_TOKEN_DESCRIPTION = "This endpoint allow you to retrieve the access token in exchange for authorization code" ; - - /* Issuers Controller */ - public static final String ISSUERS_NAME = "Issuers" ; - public static final String ISSUERS_DESCRIPTION = "All the issuers related endpoints" ; - public static final String ISSUERS_GET_ISSUERS_SUMMARY = "Retrieve all onboarded issuers" ; - public static final String ISSUERS_GET_ISSUERS_DESCRIPTION = "This endpoint allow you to retrieve all the onboarded issuers" ; - public static final String ISSUERS_GET_SPECIFIC_ISSUER_SUMMARY = "Retrieve specific issuer's config" ; - public static final String ISSUERS_GET_SPECIFIC_ISSUER_DESCRIPTION = "This endpoint allow you to retrieve the complete configuration of the specific issuer" ; - public static final String ISSUERS_GET_ISSUER_WELLKNOWN_SUMMARY = "Retrieve specific issuer's well known" ; + /* Issuers Controller */ + public static final String ISSUERS_NAME = "Issuers"; + public static final String ISSUERS_DESCRIPTION = "All the issuers related endpoints"; + public static final String ISSUERS_GET_ISSUERS_SUMMARY = "Retrieve all onboarded issuers"; + public static final String ISSUERS_GET_ISSUERS_DESCRIPTION = "This endpoint allow you to retrieve all the onboarded issuers"; + public static final String ISSUERS_GET_SPECIFIC_ISSUER_SUMMARY = "Retrieve specific issuer's config"; + public static final String ISSUERS_GET_SPECIFIC_ISSUER_DESCRIPTION = "This endpoint allow you to retrieve the complete configuration of the specific issuer"; + public static final String ISSUERS_GET_ISSUER_WELLKNOWN_SUMMARY = "Retrieve specific issuer's well known"; public static final String ISSUERS_GET_ISSUER_WELLKNOWN_DESCRIPTION = "This endpoint allow you to retrieve the well known of the specific issuer. Since version 0.16.0, this endpoint is deprecated and will be removed in a future release. Use issuers new endpoint issuers/{issuer-id}/configuration instead."; public static final String ISSUERS_GET_ISSUER_CONFIGURATION_SUMMARY = "Retrieve specific issuer's and its corresponding authorization server well-known config"; public static final String ISSUERS_GET_ISSUER_CONFIGURATION_DESCRIPTION = "This endpoint allows you to retrieve the well-known configuration of a specific issuer and its corresponding authorization server"; - /* Prensentation Controller */ - public static final String PRESENTATION_NAME = "Presentation" ; - public static final String PRESENTATION_DESCRIPTION = "All the online sharing related endpoints" ; - public static final String PRESENTATION_AUTHORIZE_SUMMARY = "Perform the authorization" ; - public static final String PRESENTATION_AUTHORIZE_DESCRIPTION = "This endpoint allow you to redirect the token back to the caller post authorization" ; + /* Prensentation Controller */ + public static final String PRESENTATION_NAME = "Presentation"; + public static final String PRESENTATION_DESCRIPTION = "All the online sharing related endpoints"; + public static final String PRESENTATION_AUTHORIZE_SUMMARY = "Perform the authorization"; + public static final String PRESENTATION_AUTHORIZE_DESCRIPTION = "This endpoint allow you to redirect the token back to the caller post authorization"; + + /* Resident Service Controller */ + public static final String RESIDENT_NAME = "Resident Service"; + public static final String RESIDENT_DESCRIPTION = "All the resident service related endpoints"; + public static final String RESIDENT_REQUEST_OTP_SUMMARY = "Request for OTP"; + public static final String RESIDENT_REQUEST_OTP_DESCRIPTION = "This endpoint allow you to request OTP for credential download"; + public static final String RESIDENT_REQUEST_INDIVIDUALID_OTP_SUMMARY = "Request OTP for retrieving Individual Id"; + public static final String RESIDENT_REQUEST_INDIVIDUALID_OTP_DESCRIPTION = "This endpoint allow you to request OTP to retrieve Individual Id"; + public static final String RESIDENT_GET_INDIVIDUALID_SUMMARY = "Retrieve Individual Id using AID"; + public static final String RESIDENT_GET_INDIVIDUALID_DESCRIPTION = "This endpoint allow you to retrieve the Individual Id using AID"; + + /* Verifiers Controller */ + public static final String VERIFIERS_NAME = "Verifiers"; + public static final String VERIFIERS_DESCRIPTION = "All the verifiers related endpoints"; + public static final String VERIFIERS_GET_VERIFIERS_SUMMARY = "Retrieve all trusted verifiers"; + public static final String VERIFIERS_GET_VERIFIERS_DESCRIPTION = "This endpoint allow you to retrieve all the trusted verifiers"; + + /* Users Controller */ + public static final String USERS_NAME = "Users"; + public static final String USERS_DESCRIPTION = "All the User Profile related endpoints"; - /* Resident Service Controller */ - public static final String RESIDENT_NAME = "Resident Service" ; - public static final String RESIDENT_DESCRIPTION = "All the resident service related endpoints" ; - public static final String RESIDENT_REQUEST_OTP_SUMMARY = "Request for OTP" ; - public static final String RESIDENT_REQUEST_OTP_DESCRIPTION = "This endpoint allow you to request OTP for credential download" ; - public static final String RESIDENT_REQUEST_INDIVIDUALID_OTP_SUMMARY = "Request OTP for retrieving Individual Id" ; - public static final String RESIDENT_REQUEST_INDIVIDUALID_OTP_DESCRIPTION = "This endpoint allow you to request OTP to retrieve Individual Id" ; - public static final String RESIDENT_GET_INDIVIDUALID_SUMMARY = "Retrieve Individual Id using AID" ; - public static final String RESIDENT_GET_INDIVIDUALID_DESCRIPTION = "This endpoint allow you to retrieve the Individual Id using AID" ; + /* Wallets Controller */ + public static final String RETRIEVE_ALL_WALLETS_SUMMARY = "Retrieve all wallets for the user"; + public static final String RETRIEVE_ALL_WALLETS_DESCRIPTION = "This API is secured using session-based authentication. The session ID is extracted from the Cookie header to authenticate the user. The user's ID is obtained from the session stored in Redis, and all wallets associated with the user are fetched from the database. If successful, the list of wallets is returned; otherwise, an appropriate error response is returned."; + public static final String WALLETS_NAME = "Wallets"; + public static final String WALLETS_DESCRIPTION = "All the Wallet related endpoints"; + public static final String WALLETS_DELETE_SUMMARY = "Delete a Wallet"; + public static final String WALLETS_DELETE_DESCRIPTION = "This endpoint allows you to delete a specific wallet. The API is secured using session-based authentication. The session ID is extracted from the Cookie header to authenticate the user. The user's ID is obtained from the session and used to validate ownership of the Wallet before deletion. If the Wallet is successfully deleted, a 200 OK response is returned; otherwise, an appropriate error response is returned."; - /* Verifiers Controller */ - public static final String VERIFIERS_NAME = "Verifiers" ; - public static final String VERIFIERS_DESCRIPTION = "All the verifiers related endpoints" ; - public static final String VERIFIERS_GET_VERIFIERS_SUMMARY = "Retrieve all trusted verifiers" ; - public static final String VERIFIERS_GET_VERIFIERS_DESCRIPTION = "This endpoint allow you to retrieve all the trusted verifiers" ; + /* Wallet Credentials Controller */ + public static final String WALLET_CREDENTIALS_NAME = "Wallet Credentials"; + public static final String WALLET_CREDENTIALS_DESCRIPTION = "All the Wallet Credentials related endpoints"; + public static final String WALLET_CREDENTIALS_DELETE_SUMMARY = "Delete a credential from a wallet"; + public static final String WALLET_CREDENTIALS_DELETE_DESCRIPTION = "This endpoint allows you to delete a specific credential from a wallet"; + public static final String WALLET_CREDENTIALS_FETCH_ALL_SUMMARY = "Fetch all credentials for a wallet"; + public static final String WALLET_CREDENTIALS_FETCH_ALL_DESCRIPTION = "This endpoint allows you to retrieve all credentials for a specific wallet"; + /* OAuth2 ID Token Authentication Controller */ + public static final String ID_TOKEN_AUTHENTICATION_NAME = "OAuth2 ID Token Authentication"; + public static final String ID_TOKEN_AUTHENTICATION_DESCRIPTION = "All the OAuth2 ID Token Authentication related endpoints"; } diff --git a/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java b/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java index 89da9553e..fc2e93093 100644 --- a/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java +++ b/src/main/java/io/mosip/mimoto/controller/CredentialShareController.java @@ -32,7 +32,7 @@ import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -81,7 +81,7 @@ public class CredentialShareController { * @return * @throws Exception */ - @PostMapping(path = "/callback/notify", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + @PostMapping(path = {"/callback/notify", "/callback/notify/"},consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthenticateContentAndVerifyIntent(secret = "${mosip.event.secret}", callback = "/v1/mimoto/credentialshare/callback/notify", topic = "${mosip.event.topic}") @Operation(summary = SwaggerLiteralConstants.CREDENTIALS_SHARE_HANDLE_SUBSCRIBED_EVENT_SUMMARY, description = SwaggerLiteralConstants.CREDENTIALS_SHARE_HANDLE_SUBSCRIBED_EVENT_DESCRIPTION) public ResponseEntity handleSubscribeEvent(@RequestBody EventModel eventModel) diff --git a/src/main/java/io/mosip/mimoto/controller/CredentialsController.java b/src/main/java/io/mosip/mimoto/controller/CredentialsController.java index e612bb52a..cd1e62ae2 100644 --- a/src/main/java/io/mosip/mimoto/controller/CredentialsController.java +++ b/src/main/java/io/mosip/mimoto/controller/CredentialsController.java @@ -2,12 +2,12 @@ import io.mosip.mimoto.constant.SwaggerLiteralConstants; import io.mosip.mimoto.core.http.ResponseWrapper; -import io.mosip.mimoto.dto.ErrorDTO; import io.mosip.mimoto.dto.idp.TokenResponseDTO; import io.mosip.mimoto.exception.ApiNotAccessibleException; import io.mosip.mimoto.exception.InvalidCredentialResourceException; import io.mosip.mimoto.exception.VCVerificationException; import io.mosip.mimoto.service.CredentialService; +import io.mosip.mimoto.service.IdpService; import io.mosip.mimoto.util.Utilities; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -29,10 +29,9 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.util.List; import java.util.Map; -import static io.mosip.mimoto.exception.PlatformErrorMessages.*; +import static io.mosip.mimoto.exception.PlatformErrorMessages.MIMOTO_PDF_SIGN_EXCEPTION; @RestController @RequestMapping(value = "/credentials") @@ -43,13 +42,15 @@ public class CredentialsController { @Autowired CredentialService credentialService; + @Autowired + IdpService idpService; + @Operation(summary = SwaggerLiteralConstants.CREDENTIALS_DOWNLOAD_VC_SUMMARY, description = SwaggerLiteralConstants.CREDENTIALS_DOWNLOAD_VC_DESCRIPTION) @ApiResponses({ @ApiResponse(responseCode = "200", content = {@Content(mediaType = "application/pdf")}), @ApiResponse(responseCode = "400", content = {@Content(schema = @Schema(implementation = ResponseWrapper.class), mediaType = "application/json")})}) @PostMapping("/download") public ResponseEntity downloadCredentialAsPDF(@RequestParam Map params) { - ResponseWrapper responseWrapper = new ResponseWrapper<>(); //TODO: remove this default value after the apitest is updated params.putIfAbsent("vcStorageExpiryLimitInTimes", "-1"); @@ -59,7 +60,7 @@ public ResponseEntity downloadCredentialAsPDF(@RequestParam Map downloadCredentialAsPDF(@RequestParam Map> handleException(Exception exception, ResponseWrapper responseWrapper) { - String errorCode = MIMOTO_PDF_SIGN_EXCEPTION.getCode(); - String[] errorObj = Utilities.handleExceptionWithErrorCode(exception, errorCode); - List errors = Utilities.getErrors(errorObj[0], errorObj[1]); - responseWrapper.setErrors(errors); - - HttpStatus status = exception instanceof ApiNotAccessibleException || exception instanceof IOException || exception instanceof InvalidCredentialResourceException || exception instanceof VCVerificationException - ? HttpStatus.BAD_REQUEST - : HttpStatus.INTERNAL_SERVER_ERROR; - - return ResponseEntity.status(status) - .contentType(MediaType.APPLICATION_JSON) - .body(responseWrapper); - } } diff --git a/src/main/java/io/mosip/mimoto/controller/IdpController.java b/src/main/java/io/mosip/mimoto/controller/IdpController.java index f8c4a1070..43ea97ec4 100644 --- a/src/main/java/io/mosip/mimoto/controller/IdpController.java +++ b/src/main/java/io/mosip/mimoto/controller/IdpController.java @@ -10,9 +10,7 @@ import io.mosip.mimoto.exception.IdpException; import io.mosip.mimoto.exception.PlatformErrorMessages; import io.mosip.mimoto.service.IdpService; -import io.mosip.mimoto.service.IssuersService; import io.mosip.mimoto.service.RestClientService; -import io.mosip.mimoto.service.impl.CredentialServiceImpl; import io.mosip.mimoto.util.JoseUtil; import io.mosip.mimoto.util.RequestValidator; import io.mosip.mimoto.util.Utilities; @@ -30,7 +28,7 @@ import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; -import javax.validation.Valid; +import jakarta.validation.Valid; import java.util.List; import java.util.Map; @@ -46,18 +44,12 @@ public class IdpController { @Autowired private JoseUtil joseUtil; - @Autowired - IssuersService issuersService; - @Autowired IdpService idpService; @Autowired RequestValidator requestValidator; - @Autowired - CredentialServiceImpl credentialService; - @Operation(summary = SwaggerLiteralConstants.IDP_BINDING_OTP_SUMMARY, description = SwaggerLiteralConstants.IDP_BINDING_OTP_DESCRIPTION) @PostMapping(value = "/binding-otp", produces = MediaType.APPLICATION_JSON_VALUE) @SuppressWarnings("unchecked") @@ -88,7 +80,7 @@ public ResponseEntity> request(@Reques throws Exception { log.debug("Received wallet-binding request : " + JsonUtils.javaObjectToJsonString(requestDTO)); - ResponseWrapper responseWrapper = new ResponseWrapper(); + ResponseWrapper responseWrapper = new ResponseWrapper<>(); try { WalletBindingInnerRequestDto innerRequestDto = new WalletBindingInnerRequestDto(); innerRequestDto.setChallengeList(requestDTO.getRequest().getChallengeList()); @@ -120,14 +112,15 @@ public ResponseEntity> request(@Reques @Operation(summary = SwaggerLiteralConstants.IDP_GET_TOKEN_SUMMARY, description = SwaggerLiteralConstants.IDP_GET_TOKEN_DESCRIPTION) @ApiResponses({ - @ApiResponse(responseCode = "200", content = { @Content(schema = @Schema(implementation = TokenResponseDTO.class), mediaType = "application/json") }), - @ApiResponse(responseCode = "400", content = { @Content(schema = @Schema(implementation = ResponseWrapper.class), mediaType = "application/json") }) }) + @ApiResponse(responseCode = "200", content = {@Content(schema = @Schema(implementation = TokenResponseDTO.class), mediaType = "application/json")}), + @ApiResponse(responseCode = "400", content = {@Content(schema = @Schema(implementation = ResponseWrapper.class), mediaType = "application/json")})}) @PostMapping(value = {"/get-token/{issuer}"}, consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}, produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity getToken(@RequestParam Map params, @PathVariable(required = true, name= "issuer") String issuer) { + public ResponseEntity getToken(@RequestParam Map params, @PathVariable(required = true, name = "issuer") String issuer) { log.info("Reached the getToken Controller for Issuer " + issuer); ResponseWrapper responseWrapper = new ResponseWrapper<>(); try { - TokenResponseDTO response = credentialService.getTokenResponse(params, issuer); + params.put("issuer", issuer); + TokenResponseDTO response = idpService.getTokenResponse(params); return ResponseEntity.status(HttpStatus.OK).body(response); } catch (Exception ex) { log.error("Exception Occurred while Invoking the Token Endpoint : ", ex); diff --git a/src/main/java/io/mosip/mimoto/controller/IssuersController.java b/src/main/java/io/mosip/mimoto/controller/IssuersController.java index 71961e3a8..5aaf10628 100644 --- a/src/main/java/io/mosip/mimoto/controller/IssuersController.java +++ b/src/main/java/io/mosip/mimoto/controller/IssuersController.java @@ -7,9 +7,7 @@ import io.mosip.mimoto.dto.IssuersDTO; import io.mosip.mimoto.dto.mimoto.*; import io.mosip.mimoto.exception.ApiNotAccessibleException; -import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; import io.mosip.mimoto.exception.InvalidIssuerIdException; -import io.mosip.mimoto.exception.InvalidWellknownResponseException; import io.mosip.mimoto.service.IssuersService; import io.mosip.mimoto.util.Utilities; import io.swagger.v3.oas.annotations.Operation; @@ -33,11 +31,9 @@ @RequestMapping(value = "/issuers") @Tag(name = SwaggerLiteralConstants.ISSUERS_NAME, description = SwaggerLiteralConstants.ISSUERS_DESCRIPTION) public class IssuersController { - @Autowired - IssuersService issuersService; @Autowired - Utilities utilities; + IssuersService issuersService; @Operation(summary = SwaggerLiteralConstants.ISSUERS_GET_ISSUERS_SUMMARY, description = SwaggerLiteralConstants.ISSUERS_GET_ISSUERS_DESCRIPTION) @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @@ -45,8 +41,7 @@ public ResponseEntity> getAllIssuers(@RequestParam(r ResponseWrapper responseWrapper = new ResponseWrapper<>(); try { responseWrapper.setResponse(issuersService.getIssuers(search)); - } catch (ApiNotAccessibleException | IOException | AuthorizationServerWellknownResponseException | - InvalidWellknownResponseException e) { + } catch (ApiNotAccessibleException | IOException e) { log.error("Exception occurred while fetching issuers ", e); responseWrapper.setErrors(List.of(new ErrorDTO(API_NOT_ACCESSIBLE_EXCEPTION.getCode(), API_NOT_ACCESSIBLE_EXCEPTION.getMessage()))); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(responseWrapper); @@ -87,7 +82,7 @@ public ResponseEntity> getIssuerConfig(@PathVariable( responseWrapper.setResponse(issuerDTO); return ResponseEntity.status(HttpStatus.OK).body(responseWrapper); } catch (InvalidIssuerIdException exception) { - log.error("invalid issuer id passed - ", issuerId); + log.error("invalid issuer id {} passed - ", issuerId); responseWrapper.setErrors(List.of(new ErrorDTO(INVALID_ISSUER_ID_EXCEPTION.getCode(), INVALID_ISSUER_ID_EXCEPTION.getMessage()))); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(responseWrapper); } catch (Exception exception) { diff --git a/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java b/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java index e0502b088..977dfd462 100644 --- a/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java +++ b/src/main/java/io/mosip/mimoto/controller/ResidentServiceController.java @@ -21,7 +21,7 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; +import jakarta.validation.Valid; @RestController @Tag(name = SwaggerLiteralConstants.RESIDENT_NAME, description = SwaggerLiteralConstants.RESIDENT_DESCRIPTION) diff --git a/src/main/java/io/mosip/mimoto/controller/TokenAuthController.java b/src/main/java/io/mosip/mimoto/controller/TokenAuthController.java new file mode 100644 index 000000000..fe429670f --- /dev/null +++ b/src/main/java/io/mosip/mimoto/controller/TokenAuthController.java @@ -0,0 +1,129 @@ +package io.mosip.mimoto.controller; + +import io.mosip.mimoto.constant.SwaggerLiteralConstants; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import io.mosip.mimoto.service.TokenService; +import io.mosip.mimoto.service.TokenServiceFactory; +import io.mosip.mimoto.util.Utilities; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; + +@RestController +@Tag(name = SwaggerLiteralConstants.ID_TOKEN_AUTHENTICATION_NAME, description = SwaggerLiteralConstants.ID_TOKEN_AUTHENTICATION_DESCRIPTION) +public class TokenAuthController { + + private static final String BEARER_PREFIX = "Bearer "; + private static final String SESSION_CREATED = "Session created."; + private static final String INVALID_TOKEN_MESSAGE = "Bearer ID token required."; + + + private final TokenServiceFactory tokenServiceFactory; + + @Autowired + public TokenAuthController(TokenServiceFactory tokenServiceFactory) { + this.tokenServiceFactory = tokenServiceFactory; + } + + @Operation( + summary = "Login and create session using OAuth2 ID token", + description = """ + This API accepts an OAuth2 ID token in the Authorization header and establishes a session + by populating the Spring Security context. + + Fetch the ID token from a supported OAuth2 provider (such as Google or Microsoft) and provide + it in the request as a Bearer token. + """, + operationId = "loginWithOAuth2IdToken", + security = @SecurityRequirement(name = "bearerAuth"), + parameters = { + @Parameter( + name = "provider", + in = ParameterIn.PATH, + required = true, + description = "OAuth2 provider name. Example values: 'google', 'microsoft', 'facebook'.", + schema = @Schema(type = "string", example = "google") + ), + @Parameter( + name = "Authorization", + in = ParameterIn.HEADER, + required = true, + description = "OAuth2 ID token prefixed with 'Bearer '.", + schema = @Schema(type = "string", example = "Bearer eyJhbGciOi...") + ) + }, + responses = { + @ApiResponse( + responseCode = "200", + description = "Successfully logged in and session created.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(type = "string"), + examples = @ExampleObject(value = "Session created.") + ) + ), + @ApiResponse( + responseCode = "400", + description = "Bad Request - Missing/invalid header or unsupported provider.", + content = @Content( + mediaType = MediaType.APPLICATION_JSON_VALUE, + schema = @Schema(implementation = io.mosip.mimoto.dto.ErrorDTO.class), + examples = { + + @ExampleObject(name = "MissingToken", value = """ + { + "errorCode": "INVALID_REQUEST", + "errorMessage": "Bearer ID token required." + } + """), + @ExampleObject(name = "InvalidToken", value = """ + { + "errorCode": "invalid_token", + "errorMessage": "An error occurred while attempting to decode the Jwt: Signed JWT rejected: Invalid signature" + } + """), + @ExampleObject(name = "ExpiredToken", value = """ + { + "errorCode": "invalid_token", + "errorMessage": "An error occurred while attempting to decode the Jwt: Jwt expired at 2025-06-10T12:00:00Z" + } + """ + ) + } + ) + ), + } + ) + @PostMapping("/auth/{provider}/token-login") + public ResponseEntity createSessionFromIdToken(@RequestHeader(value = "Authorization", required = false) String authorization, @PathVariable("provider") String provider, HttpServletRequest request, HttpServletResponse response) { + if (authorization == null || !authorization.startsWith(BEARER_PREFIX)) { + return Utilities.buildErrorResponse(HttpStatus.BAD_REQUEST, ErrorConstants.INVALID_REQUEST.getErrorCode(), INVALID_TOKEN_MESSAGE); + } + + String idToken = authorization.substring(BEARER_PREFIX.length()); + try { + TokenService tokenService = tokenServiceFactory.getTokenService(provider); + tokenService.processToken(idToken, provider, request, response); + return ResponseEntity.ok(SESSION_CREATED); + } catch (OAuth2AuthenticationException e) { + return Utilities.getErrorResponseEntityWithoutWrapper(e, e.getErrorCode(), HttpStatus.UNAUTHORIZED, MediaType.APPLICATION_JSON); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/controller/UsersController.java b/src/main/java/io/mosip/mimoto/controller/UsersController.java new file mode 100644 index 000000000..193963208 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/controller/UsersController.java @@ -0,0 +1,141 @@ +package io.mosip.mimoto.controller; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.constant.SwaggerExampleConstants; +import io.mosip.mimoto.constant.SwaggerLiteralConstants; +import io.mosip.mimoto.model.UserMetadata; +import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.UnauthorizedAccessException; +import io.mosip.mimoto.repository.UserMetadataRepository; +import io.mosip.mimoto.service.EncryptionService; +import io.mosip.mimoto.util.Utilities; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping(value = "/users/me") +@Tag(name = SwaggerLiteralConstants.USERS_NAME, description = SwaggerLiteralConstants.USERS_DESCRIPTION) +public class UsersController { + @Autowired + private UserMetadataRepository userMetadataRepository; + + @Autowired + private EncryptionService encryptionService; + + /** + * Retrieves user profile information, first checking the cache and then the database if needed + * + * @param authentication The authentication object containing user information + * @param session The HTTP session + * @return ResponseEntity containing user metadata + */ + @Operation( + summary = "Retrieve user metadata", + description = "First attempts to retrieve user metadata from the session cache. If not available, fetches from the database. This API is secured using session-based authentication.", + operationId = "getUserProfile", + security = @SecurityRequirement(name = "SessionAuth") + ) + @ApiResponse( + responseCode = "200", + description = "User profile retrieved successfully", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = UserMetadataDTO.class), + examples = { + @ExampleObject(name = "Success response from DB", value = SwaggerExampleConstants.FETCH_USER_PROFILE_FROM_DB_SUCCESS), + @ExampleObject(name = "Success response from cache", value = SwaggerExampleConstants.FETCH_USER_CACHE_PROFILE_SUCCESS) + } + ) + ) + @ApiResponse(responseCode = "401", description = "User data not found", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "User not found", value = "{\"errorCode\": \"unauthorized\", \"errorMessage\": \"User not found. Please check your credentials or login again\"}"))) + @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"), + @ExampleObject(name = "Decryption Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"Failed to process user data\"}"), + })) + @ApiResponse( + responseCode = "503", + description = "Service unavailable", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = ErrorDTO.class), + examples = @ExampleObject( + name = "Database connection failure", + value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}" + ) + ) + ) + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity getUserProfileInfo(Authentication authentication, HttpSession session) throws Exception { + UserMetadataDTO userMetadataDTO = (UserMetadataDTO) session.getAttribute(SessionKeys.USER_METADATA); + + if (userMetadataDTO == null) { + log.info("User metadata not found in cache, fetching from database"); + String identityProvider = (String) session.getAttribute("clientRegistrationId"); + UserMetadata userMetadata; + try { + userMetadata = fetchUserMetadata(authentication.getName(), identityProvider); + + // In case of fetching user info from DB, walletId is null. + // Here login is required as data is unavailable in session + userMetadataDTO = new UserMetadataDTO( + encryptionService.decrypt(userMetadata.getDisplayName()), + encryptionService.decrypt(userMetadata.getProfilePictureUrl()), + encryptionService.decrypt(userMetadata.getEmail()), + null + ); + session.setAttribute(SessionKeys.USER_METADATA, userMetadataDTO); + } catch (UnauthorizedAccessException exception) { + log.error("Error occurred while retrieving user profile: ", exception); + return Utilities.getErrorResponseEntityWithoutWrapper( + exception, + ErrorConstants.INVALID_USER.getErrorCode(), + HttpStatus.UNAUTHORIZED, + MediaType.APPLICATION_JSON + ); + } catch (DecryptionException e) { + log.error("Error occurred while decrypting user data: ", e); + return Utilities.getErrorResponseEntityWithoutWrapper( + new RuntimeException("Failed to process user data"), + ErrorConstants.INTERNAL_SERVER_ERROR.getErrorCode(), + HttpStatus.INTERNAL_SERVER_ERROR, + MediaType.APPLICATION_JSON + ); + } + } else { + // If data is available in cache, take walletId also from cache + Object walletIdObj = session.getAttribute(SessionKeys.WALLET_ID); + if (walletIdObj instanceof String walletId) { + userMetadataDTO.setWalletId(walletId); + } + log.info("Retrieved user metadata from cache"); + } + + return ResponseEntity.status(HttpStatus.OK).body(userMetadataDTO); + } + + private UserMetadata fetchUserMetadata(String providerSubjectId, String identityProvider) throws UnauthorizedAccessException { + return userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(providerSubjectId, identityProvider). + orElseThrow(() -> + new UnauthorizedAccessException(ErrorConstants.UNAUTHORIZED_ACCESS.getErrorCode(), "User not found. Please check your credentials or login again") + ); + } +} diff --git a/src/main/java/io/mosip/mimoto/controller/WalletCredentialsController.java b/src/main/java/io/mosip/mimoto/controller/WalletCredentialsController.java new file mode 100644 index 000000000..c1f28d0e0 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/controller/WalletCredentialsController.java @@ -0,0 +1,300 @@ +package io.mosip.mimoto.controller; + +import io.mosip.mimoto.constant.SwaggerExampleConstants; +import io.mosip.mimoto.constant.SwaggerLiteralConstants; +import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.dto.VerifiableCredentialRequestDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.VerifiableCredentialResponseDTO; +import io.mosip.mimoto.dto.resident.WalletCredentialResponseDTO; +import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.service.WalletCredentialService; +import io.mosip.mimoto.service.IdpService; +import io.mosip.mimoto.util.Utilities; +import io.mosip.mimoto.util.WalletUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpSession; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +import static io.mosip.mimoto.exception.ErrorConstants.CREDENTIAL_DOWNLOAD_EXCEPTION; +import static io.mosip.mimoto.util.WalletUtil.validateWalletId; + +/** + * REST controller for managing wallet credentials. + */ +@Slf4j +@RestController +@RequestMapping(value = "/wallets/{walletId}/credentials") +@Tag(name = SwaggerLiteralConstants.WALLET_CREDENTIALS_NAME, description = SwaggerLiteralConstants.WALLET_CREDENTIALS_DESCRIPTION) +public class WalletCredentialsController { + + private final WalletCredentialService walletCredentialService; + private final IdpService idpService; + + @Autowired + public WalletCredentialsController(WalletCredentialService walletCredentialService, + IdpService idpService) { + this.walletCredentialService = walletCredentialService; + this.idpService = idpService; + } + + /** + * Downloads and stores a Verifiable Credential in the specified wallet. + * + * @param walletId The unique identifier of the wallet. + * @param verifiableCredentialRequest Request body parameters including issuer, credential type, storage expiry, and locale. + * @param httpSession The HTTP session containing wallet key and ID. + * @return The stored Verifiable Credential details. + * @throws InvalidRequestException If input parameters or session are invalid. + */ + @Operation(summary = "Download and store a Verifiable Credential under a specific Wallet", description = "This API is secured using session-based authentication. Upon receiving a request, the session ID is extracted from the Cookie header and used to retrieve session details from Redis for authentication. It then retrieves the wallet key from the session and use it to decrypt the signing algorithm's secret key(which is used for signing the JWT in credential request) and encrypt the downloaded Verifiable Credential. If the process completes successfully, the credential is stored in the database and certain fields will be returned in the response. In case of any issues, an appropriate error response is returned.", operationId = "downloadCredential", security = @SecurityRequirement(name = "SessionAuth"), parameters = { + @Parameter(name = "walletId", in = ParameterIn.PATH, required = true, description = "Unique identifier of the wallet", schema = @Schema(type = "string")), + @Parameter(name = "Accept-Language", in = ParameterIn.HEADER, description = "The locale for the Verifiable Credential. It follows 2 letter language code", schema = @Schema(type = "string", defaultValue = "en"))}, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + description = "Request body parameters including issuer, credential type, storage expiry, and locale.", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = VerifiableCredentialRequestDTO.class), + examples = @ExampleObject( + name = "VerifiableCredentialRequest Example", + value = "{ \"issuer\": \"issuerId\", \"credentialConfigurationId\": \"MockVerifiableCredential\", \"code\": \"authCode\", \"grantType\": \"authorization_code\", \"redirectUri\": \"https://example.com/cb\", \"codeVerifier\": \"verifier\" }" + ) + ) + ) + ) + @ApiResponse(responseCode = "200", description = "Verifiable Credential downloaded and stored successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = VerifiableCredentialResponseDTO.class))) + @ApiResponse(responseCode = "400", description = "Bad request - Wallet key is null / blank or Wallet ID is null / blank / mismatch with session Wallet ID or required params are missing / invalid", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Invalid header Accept-Language", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Locale must be a 2-letter code\"}"), + @ExampleObject(name = "Wallet ID is empty or blank", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet ID cannot be blank\"}"), + @ExampleObject(name = "Wallet ID not found in session", value = "{\"errorCode\": \"wallet_locked\", \"errorMessage\": \"Wallet is locked\"}"), + @ExampleObject(name = "Invalid Wallet ID", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Invalid Wallet ID. Session and request Wallet ID do not match\"}"), + @ExampleObject(name = "Wallet key not found in session", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet key not found in session\"}"), + @ExampleObject(name = "Invalid issuer", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"issuerId cannot be blank\"}"), + @ExampleObject(name = "Invalid credentialConfigurationId", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"credentialConfigurationId cannot be blank\"}"), + @ExampleObject(name = "Invalid code", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"code cannot be blank\"}"), + @ExampleObject(name = "Invalid grantType", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"grantType cannot be blank\"}"), + @ExampleObject(name = "Invalid redirectUri", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"redirectUri cannot be blank\"}"), + @ExampleObject(name = "Invalid codeVerifier", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"codeVerifier cannot be blank\"}")}) + ) + @ApiResponse(responseCode = "500", description = "Internal server error - error occurred while serializing the VC response, encrypting the credential, or storing it", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = {@ExampleObject(name = "Credential already exists", value = "{\"errorCode\": \"credential_download_error\", \"errorMessage\": \"Duplicate credential for issuer and type\"}"), @ExampleObject(name = "Issuer config error", value = "{\"errorCode\": \"credential_download_error\", \"errorMessage\": \"Unable to fetch issuer configuration\"}"), @ExampleObject(name = "Failed to generate VC request", value = "{\"errorCode\": \"credential_download_error\", \"errorMessage\": \"Unable to generate credential request\"}"), @ExampleObject(name = "Signature verification failed", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"), @ExampleObject(name = "Unexpected server error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}")})) + @ApiResponse(responseCode = "503", description = "Service unavailable - error while fetching issuer or auth server well-known, downloading credential, or DB connection failure", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = {@ExampleObject(name = "API is not accessible", value = "{\"errorCode\": \"credential_download_error\", \"errorMessage\": \"Failed to download and store the credential\"}"), @ExampleObject(name = "Failed to download credential", value = "{\"errorCode\": \"server_unavailable\", \"errorMessage\": \"Unable to download credential from issuer\"}"), @ExampleObject(name = "Database connection failure", value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}")})) + @PostMapping + public ResponseEntity downloadCredential( + @RequestHeader(value = "Accept-Language", required = false, defaultValue = "en") @Pattern(regexp = "^[a-z]{2}$", message = "Locale must be a 2-letter code") String locale, + @PathVariable("walletId") @NotBlank(message = "Wallet ID cannot be blank") String walletId, + @RequestBody @Valid VerifiableCredentialRequestDTO verifiableCredentialRequest, + HttpSession httpSession) throws InvalidRequestException { + + validateWalletId(httpSession, walletId); + String base64EncodedWalletKey = WalletUtil.getSessionWalletKey(httpSession); + + String issuerId = verifiableCredentialRequest.getIssuer(); + String credentialConfigurationId = verifiableCredentialRequest.getCredentialConfigurationId(); + + log.info("Initiating token call for issuer: {}", issuerId); + TokenResponseDTO tokenResponse; + try { + tokenResponse = idpService.getTokenResponse(verifiableCredentialRequest); + } catch (ApiNotAccessibleException | IOException | AuthorizationServerWellknownResponseException | + InvalidWellknownResponseException e) { + log.error("Error fetching token response for issuer: {}", issuerId, e); + return Utilities.getErrorResponseEntityFromPlatformErrorMessage( + CREDENTIAL_DOWNLOAD_EXCEPTION, HttpStatus.SERVICE_UNAVAILABLE, MediaType.APPLICATION_JSON); + } + + log.info("Fetching and storing Verifiable Credential for walletId: {}", walletId); + + try { + VerifiableCredentialResponseDTO credentialResponseDTO = walletCredentialService.downloadVCAndStoreInDB( + issuerId, credentialConfigurationId, tokenResponse, locale, walletId, base64EncodedWalletKey); + return ResponseEntity.status(HttpStatus.OK).body(credentialResponseDTO); + } catch (ExternalServiceUnavailableException e) { + return Utilities.getErrorResponseEntityWithoutWrapper( + e, e.getErrorCode(), HttpStatus.SERVICE_UNAVAILABLE, MediaType.APPLICATION_JSON); + } catch (CredentialProcessingException e) { + log.error("Error processing credential download for walletId: {}", walletId, e); + return Utilities.getErrorResponseEntityWithoutWrapper( + e, e.getErrorCode(), HttpStatus.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON); + } + } + + /** + * Fetches all credentials for a given wallet. + * + * @param walletId The unique identifier of the wallet. + * @param locale The locale for credential retrieval. + * @param httpSession The HTTP session containing wallet key and ID. + * @return List of Verifiable Credential details. + * @throws InvalidRequestException If session or wallet ID is invalid. + */ + @Operation(summary = "Fetch all credentials for the given wallet", description = "This API is secured using session-based authentication. When a request is made, the session ID is extracted from the Cookie header and used to fetch session details from Redis for authentication. It then retrieves the wallet key from the session and uses it to decrypt all stored Verifiable Credentials for the given wallet. If successful, it returns a list of credentials otherwise an appropriate error is returned.", operationId = "fetchAllCredentialsForWallet", security = @SecurityRequirement(name = "SessionAuth"), parameters = { + @Parameter(name = "walletId", in = ParameterIn.PATH, required = true, description = "Unique identifier of the user's wallet from where the credential will be fetched", schema = @Schema(type = "string")), + @Parameter(name = "Accept-Language", in = ParameterIn.HEADER, description = "Locale is used to determine the language in which credentials should be rendered. It follows 2 letter language code", schema = @Schema(type = "string"))}) + @ApiResponse(responseCode = "200", description = "Credentials retrieved successfully", content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = VerifiableCredentialResponseDTO.class)), examples = @ExampleObject(value = SwaggerExampleConstants.FETCH_ALL_CREDENTIALS_OF_WALLET_SUCCESS))) + @ApiResponse(responseCode = "400", description = "Bad request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Invalid Wallet Id", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Invalid Wallet ID. Session and request Wallet ID do not match\"}"), + @ExampleObject(name = "Wallet ID not found in session", value = "{\"errorCode\": \"wallet_locked\", \"errorMessage\": \"Wallet is locked\"}"), + @ExampleObject(name = "Wallet key not found in session", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet key not found in session\"}"), + @ExampleObject(name = "Invalid Accept-Language header", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Locale must be a 2-letter code\"}"), + })) + @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"))) + @ApiResponse(responseCode = "503", description = "Service unavailable", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Database connection failure", value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}"))) + @GetMapping + public ResponseEntity> fetchAllCredentialsForGivenWallet( + @PathVariable("walletId") @NotBlank(message = "Wallet ID cannot be blank") String walletId, + @RequestHeader(value = "Accept-Language", defaultValue = "en") @Pattern(regexp = "^[a-z]{2}$", message = "Locale must be a 2-letter code") String locale, + HttpSession httpSession) throws InvalidRequestException { + + validateWalletId(httpSession, walletId); + String base64EncodedWalletKey = WalletUtil.getSessionWalletKey(httpSession); + + log.info("Fetching all credentials for walletId: {}", walletId); + + List credentials = walletCredentialService.fetchAllCredentialsForWallet( + walletId, base64EncodedWalletKey, locale); + return ResponseEntity.status(HttpStatus.OK).body(credentials); + + } + + /** + * Fetches a specific Verifiable Credential as a PDF. + * + * @param walletId The unique identifier of the wallet. + * @param credentialId The unique identifier of the credential. + * @param locale The locale for credential retrieval. + * @param action The action (inline or download) for PDF display. + * @param httpSession The HTTP session containing wallet key and ID. + * @return The Verifiable Credential as a PDF stream. + * @throws InvalidRequestException If session or parameters are invalid. + */ + @Operation(summary = "Fetch a specific Verifiable Credential", description = "This API is protected using session-based authentication. When a request is received, the session ID is extracted from the Cookie header and used to fetch session data from Redis for user authentication. Upon successful authentication, the wallet key is retrieved from the session and used to decrypt the credential data obtained from the database.\n\nThe API retrieves a specific Verifiable Credential based on the provided wallet ID and credential ID. Depending on the action query parameter (inline or download) received in the request, the Content-Disposition header in the response is adjusted to either display the credential in the browser or prompt a file download. On success, the API returns the credential as a PDF byte stream. In case of any errors, an appropriate error response is returned.", operationId = "fetchVerifiableCredentialById", security = @SecurityRequirement(name = "SessionAuth"), parameters = { + @Parameter(name = "walletId", in = ParameterIn.PATH, required = true, description = "Unique identifier of the user's wallet", schema = @Schema(type = "string")), + @Parameter(name = "credentialId", in = ParameterIn.PATH, required = true, description = "Unique identifier of the Verifiable Credential to be retrieved", schema = @Schema(type = "string")), + @Parameter(name = "Accept-Language", in = ParameterIn.QUERY, required = true, description = "Language preference in which the user expects the Verifiable Credential to be rendered", schema = @Schema(type = "string")), + @Parameter(name = "action", in = ParameterIn.QUERY, description = "Controls how the credential should be displayed. If sent as download, the response will include a header to download the file. Defaults to inline.", schema = @Schema(type = "string", defaultValue = "inline"))}) + @ApiResponse(responseCode = "200", description = "Verifiable Credential fetched successfully", headers = @Header(name = HttpHeaders.CONTENT_DISPOSITION, description = "Indicates if the verifiable credential is displayed inline or downloaded", schema = @Schema(type = "string", example = "inline; filename=\"credential.pdf\"")), content = @Content(mediaType = "application/pdf", schema = @Schema(type = "string", format = "binary"), examples = @ExampleObject(value = "PDF content in Binary Format"))) + @ApiResponse(responseCode = "400", description = "Bad request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Invalid Wallet Id", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Invalid Wallet ID. Session and request Wallet ID do not match\"}"), + @ExampleObject(name = "Wallet key not found in session", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet key not found in session\"}"), + @ExampleObject(name = "Wallet ID not found in session", value = "{\"errorCode\": \"wallet_locked\", \"errorMessage\": \"Wallet is locked\"}"), + @ExampleObject(name = "Invalid Accept header", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Accept header must be application/pdf\"}"), + })) + @ApiResponse(responseCode = "404", description = "Not Found - Credential not found for given Wallet ID and Credential ID", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "No credentials found", value = "{\"errorCode\": \"resource_not_found\", \"errorMessage\": \"The requested resource doesn’t exist.\"}"))) + @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = {@ExampleObject(name = "Failed to decrypt the Credential", value = "{\"errorCode\": \"credential_fetch_error\", \"errorMessage\": \"Decryption failed\"}"), @ExampleObject(name = "Credential Type is invalid", value = "{\"errorCode\": \"credential_fetch_error\", \"errorMessage\": \"Invalid credential type configuration\"}"), @ExampleObject(name = "Exception occurred while fetching Issuer config", value = "{\"errorCode\": \"credential_download_error\", \"errorMessage\": \"Unable to fetch issuer configuration\"}"), @ExampleObject(name = "Exception occurred when generating PDF", value = "{\"errorCode\": \"credential_download_error\", \"errorMessage\": \"Failed to generate credential PDF\"}"), @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}")})) + @ApiResponse(responseCode = "503", description = "Service unavailable", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Database connection failure", value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}"))) + @GetMapping("/{credentialId}") + public ResponseEntity getVerifiableCredential( + @PathVariable("walletId") @NotBlank(message = "Wallet ID cannot be blank") String walletId, + @PathVariable("credentialId") @NotBlank(message = "Credential ID cannot be blank") String credentialId, + @RequestHeader(value = "Accept") String accept, + @RequestHeader(value = "Accept-Language", defaultValue = "en") @Pattern(regexp = "^[a-z]{2}$", message = "Locale must be a 2-letter code") String locale, + @RequestParam(value = "action", defaultValue = "inline") @Pattern(regexp = "^(inline|download)$", message = "Action must be 'inline' or 'download'") String action, + HttpSession httpSession) throws InvalidRequestException { + + try { + if (!Objects.equals(accept, MediaType.APPLICATION_PDF_VALUE)) { + log.error("Invalid Accept header: {}. Valid Accept headers are - [\"application/pdf\"]", accept); + throw new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Accept header must be application/pdf"); + } + validateWalletId(httpSession, walletId); + String base64EncodedWalletKey = WalletUtil.getSessionWalletKey(httpSession); + + log.info("Fetching credentialId: {} from walletId: {}", credentialId, walletId); + + WalletCredentialResponseDTO walletCredentialResponseDTO = walletCredentialService.fetchVerifiableCredential( + walletId, credentialId, base64EncodedWalletKey, locale); + + String dispositionType = "download".equalsIgnoreCase(action) ? "attachment" : "inline"; + String contentDisposition = String.format("%s; filename=\"%s\"", dispositionType, walletCredentialResponseDTO.getFileName()); + + return ResponseEntity.ok() + .header(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION) + .header(HttpHeaders.CONTENT_DISPOSITION, contentDisposition) + .contentType(MediaType.APPLICATION_PDF) + .body(walletCredentialResponseDTO.getFileContentStream()); + } catch (CredentialNotFoundException e) { + log.error("Credential not found for walletId: {} and credentialId: {}", walletId, credentialId, e); + return Utilities.getErrorResponseEntityWithoutWrapper( + e, e.getErrorCode(), HttpStatus.NOT_FOUND, MediaType.APPLICATION_JSON); + } catch (CredentialProcessingException e) { + log.error("Error processing credential for walletId: {} and credentialId: {}", walletId, credentialId, e); + return Utilities.getErrorResponseEntityWithoutWrapper( + e, e.getErrorCode(), HttpStatus.INTERNAL_SERVER_ERROR, MediaType.APPLICATION_JSON); + } catch (InvalidRequestException exception) { + log.error("Error processing credential for walletId: {} and credentialId: {}", walletId, credentialId, exception); + return Utilities.getErrorResponseEntityWithoutWrapper( + exception, exception.getErrorCode(), HttpStatus.BAD_REQUEST, MediaType.APPLICATION_JSON); + } + } + + /** + * Deletes a credential by its ID + * + * @param walletId The ID of the wallet that owns the credential + * @param credentialId The ID of the credential to delete + * @return ResponseEntity with HTTP status 200 if successful, 404 if credential not found, or 500 for other errors + */ + @Operation(summary = SwaggerLiteralConstants.WALLET_CREDENTIALS_DELETE_SUMMARY, description = SwaggerLiteralConstants.WALLET_CREDENTIALS_DELETE_DESCRIPTION) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Credential successfully deleted"), + @ApiResponse(responseCode = "400", description = "Bad request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Invalid Wallet Id", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Invalid Wallet ID. Session and request Wallet ID do not match\"}"), + @ExampleObject(name = "Wallet ID not found in session", value = "{\"errorCode\": \"wallet_locked\", \"errorMessage\": \"Wallet is locked\"}"), + })), + @ApiResponse(responseCode = "404", description = "Credential not found"), + @ApiResponse(responseCode = "500", description = "Internal server error", content = {@Content(mediaType = "application/json")})}) + @DeleteMapping("/{credentialId}") + public ResponseEntity deleteCredential(@PathVariable("walletId") String walletId, + @PathVariable("credentialId") String credentialId, + HttpSession httpSession) { + try { + log.info("Deleting credential with ID: {} for walletId: {}", credentialId, walletId); + + validateWalletId(httpSession, walletId); + + // Delete the credential + walletCredentialService.deleteCredential(credentialId, walletId); + return ResponseEntity.status(HttpStatus.OK).build(); + } catch (InvalidRequestException exception) { + log.error("Invalid request: {}", exception.getMessage()); + return Utilities.getErrorResponseEntityWithoutWrapper( + exception, + exception.getErrorCode(), + HttpStatus.BAD_REQUEST, + MediaType.APPLICATION_JSON); + } catch (CredentialNotFoundException exception) { + log.error("Credential not found: {}", exception.getMessage()); + return Utilities.getErrorResponseEntityWithoutWrapper( + exception, + exception.getErrorCode(), + HttpStatus.NOT_FOUND, + MediaType.APPLICATION_JSON); + } + } +} diff --git a/src/main/java/io/mosip/mimoto/controller/WalletsController.java b/src/main/java/io/mosip/mimoto/controller/WalletsController.java new file mode 100644 index 000000000..7c6b76ea5 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/controller/WalletsController.java @@ -0,0 +1,197 @@ +package io.mosip.mimoto.controller; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.constant.SwaggerLiteralConstants; +import io.mosip.mimoto.dto.CreateWalletRequestDto; +import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.dto.UnlockWalletRequestDto; +import io.mosip.mimoto.dto.WalletResponseDto; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.UnauthorizedAccessException; +import io.mosip.mimoto.service.WalletService; +import io.mosip.mimoto.util.WalletUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpSession; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static io.mosip.mimoto.constant.SwaggerLiteralConstants.RETRIEVE_ALL_WALLETS_DESCRIPTION; +import static io.mosip.mimoto.constant.SwaggerLiteralConstants.RETRIEVE_ALL_WALLETS_SUMMARY; + + +/** + * REST controller for managing wallets. + */ +@Slf4j +@RestController +@RequestMapping(value = "/wallets") +@Tag(name = SwaggerLiteralConstants.WALLETS_NAME, description = SwaggerLiteralConstants.WALLETS_DESCRIPTION) +public class WalletsController { + + private final WalletService walletService; + + @Autowired + public WalletsController(WalletService walletService) { + this.walletService = walletService; + } + + /** + * Creates a new Wallet for a user. + * + * @param wallet The Wallet creation request containing name and PIN. + * @param httpSession The HTTP session containing user details. + * @return The unique identifier of the created Wallet. + * @throws InvalidRequestException If the request is invalid. + * Example: Wallet PIN and Confirm Wallet PIN received in request don't match + */ + @Operation( + summary = "Create a new wallet", + description = "This API is secured using session-based authentication. The session ID is extracted from the Cookie header and used to retrieve session details from Redis for authentication. The user ID is obtained from the session and used with the provided wallet name and PIN to create a new wallet in the database. If successful, a unique identifier for the wallet is returned; otherwise, an appropriate error response is provided.", + operationId = "createWallet", + security = @SecurityRequirement(name = "SessionAuth"), + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody( + required = true, + description = "Wallet creation request containing the wallet name and PIN", + content = @Content( + mediaType = "application/json", + schema = @Schema(implementation = CreateWalletRequestDto.class), + examples = @ExampleObject(value = "{\"walletName\": \"My Personal Wallet\", \"walletPin\": \"123456\"}")) + ) + ) + @ApiResponse(responseCode = "200", description = "Wallet created successfully", content = @Content(mediaType = "application/json", schema = @Schema(type = "string", description = "Unique identifier of the Wallet stored in the database"), examples = @ExampleObject(name = "Success response", value = "{\"walletId\":\"d94a2e70-1234-4f2f-9eae-98765\", \"walletName\":\"My Wallet\"}"))) + @ApiResponse(responseCode = "400", description = "Invalid wallet creation request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Invalid User ID", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"User ID cannot be null or empty\"}"), + @ExampleObject(name = "Invalid Wallet name", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet name must be alphanumeric with allowed special characters\"}"), + @ExampleObject(name = "Invalid Wallet Pin", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"\"}"), + @ExampleObject(name = "Wallet Pin and confirm Pin Mismatch", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Entered PIN and Confirm PIN do not match\"}")})) + @ApiResponse(responseCode = "401", description = "Unauthorized access", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "User ID not found in session", value = "{\"errorCode\": \"unauthorized\", \"errorMessage\": \"User ID not found in session\"}"))) + @ApiResponse(responseCode = "500", description = "Internal server error while creating wallet", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"))) + @ApiResponse(responseCode = "503", description = "Service unavailable", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Database connection failure", value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}"))) + @PostMapping + public ResponseEntity createWallet( + @RequestBody @Valid CreateWalletRequestDto wallet, + HttpSession httpSession) throws InvalidRequestException, UnauthorizedAccessException { + String userId = (String) httpSession.getAttribute(SessionKeys.USER_ID); + log.info("Creating wallet for user: {}, name: {}", userId, wallet.getWalletName()); + WalletResponseDto walletResponseDto = walletService.createWallet(userId, wallet.getWalletName(), wallet.getWalletPin(), wallet.getConfirmWalletPin()); + return ResponseEntity.status(HttpStatus.OK).body(walletResponseDto); + } + + /** + * Retrieves all wallets for the authenticated user. + * + * @param httpSession The HTTP session containing user details. + * @return List of Wallet details. + * @throws InvalidRequestException If the request fails. + */ + @Operation( + summary = RETRIEVE_ALL_WALLETS_SUMMARY, + description = RETRIEVE_ALL_WALLETS_DESCRIPTION, + operationId = "getWallets", + security = @SecurityRequirement(name = "SessionAuth") + ) + @ApiResponse(responseCode = "200", description = "List of wallets retrieved successfully", content = @Content(mediaType = "application/json", array = @ArraySchema(schema = @Schema(implementation = WalletResponseDto.class)), examples = @ExampleObject(name = "Success response", value = "[{\"walletId\": \"123e4567-e89b-12d3-a456-426614174000\"}, {\"walletId\": \"223e4567-e89b-12d3-a456-426614174001\"}]"))) + @ApiResponse(responseCode = "400", description = "Invalid Wallets fetching request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Invalid User ID", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"User ID cannot be null or empty\"}"))) + @ApiResponse(responseCode = "401", description = "Unauthorized access", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "User ID not found in session", value = "{\"errorCode\": \"unauthorized\", \"errorMessage\": \"User ID not found in session\"}"))) + @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"))) + @ApiResponse(responseCode = "503", description = "Service unavailable", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Database connection failure", value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}"))) + @GetMapping + public ResponseEntity> getWallets(HttpSession httpSession) throws InvalidRequestException { + String userId = (String) httpSession.getAttribute(SessionKeys.USER_ID); + log.info("Retrieving wallets for user: {}", userId); + List response = walletService.getWallets(userId); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + /** + * Unlocks an existing wallet by retrieving its key and storing it in the session. + * + * @param walletId The unique identifier of the Wallet. + * @param wallet The request containing the Wallet PIN. + * @param httpSession The HTTP session containing user details. + * @return The wallet details if unlocked successfully. + * @throws InvalidRequestException If the wallet or PIN is invalid. + */ + @Operation( + summary = "Unlock an existing Wallet", + description = "This API is secured using session-based authentication. The session ID is extracted from the Cookie header to authenticate the user. The user's ID is obtained from the session, and the provided wallet ID and PIN are used to fetch the wallet key, which is stored in the session. If successful, the wallet ID is returned; otherwise, an appropriate error response is returned.", + operationId = "unlockWallet", + security = @SecurityRequirement(name = "SessionAuth"), + parameters = {@Parameter(name = "walletId", in = ParameterIn.PATH, required = true, description = "Unique identifier of the wallet to be unlocked", schema = @Schema(type = "string"))}, + requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = @Content(mediaType = "application/json", schema = @Schema(implementation = UnlockWalletRequestDto.class))) + ) + @ApiResponse(responseCode = "200", description = "Wallet unlocked successfully", content = @Content(mediaType = "application/json", schema = @Schema(implementation = WalletResponseDto.class), examples = @ExampleObject(name = "Success response", value = "{\"walletId\": \"123e4567-e89b-12d3-a456-426614174000\"}"))) + @ApiResponse(responseCode = "400", description = "Invalid Wallet unlock request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Wallet not found", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet not found\"}"), + @ExampleObject(name = "Invalid User ID", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"User ID cannot be null or empty\"}"), + @ExampleObject(name = "Invalid Wallet Pin", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"PIN must be numeric with 6 digits\"}"), + @ExampleObject(name = "Incorrect Wallet PIN", value = "{\"errorCode\": \"invalid_pin\", \"errorMessage\": \"Invalid PIN or wallet key provided\"}") + })) + @ApiResponse(responseCode = "401", description = "Unauthorized access", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "User ID not found in session", value = "{\"errorCode\": \"unauthorized\", \"errorMessage\": \"User ID not found in session\"}"))) + @ApiResponse(responseCode = "500", description = "Internal server error - any error occurred while decrypting the Wallet key with Wallet PIN or when fetching Wallet details from the database", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"))) + @ApiResponse(responseCode = "503", description = "Service unavailable", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Database connection failure", value = "{\"errorCode\": \"database_unavailable\", \"errorMessage\": \"Failed to connect to the database\"}"))) + @PostMapping("/{walletId}/unlock") + public ResponseEntity unlockWallet( + @PathVariable("walletId") @NotBlank(message = "Wallet ID cannot be blank") String walletId, + @RequestBody @Valid UnlockWalletRequestDto wallet, + HttpSession httpSession) throws InvalidRequestException { + String userId = (String) httpSession.getAttribute(SessionKeys.USER_ID); + log.info("Unlocking wallet: {} for user: {}", walletId, userId); + WalletResponseDto response = walletService.unlockWallet(walletId, wallet.getWalletPin(), httpSession); + return ResponseEntity.status(HttpStatus.OK).body(response); + } + + /** + * Deletes a Wallet by its ID + * + * @param walletId The ID of the Wallet to delete + * @param httpSession The HTTP session containing user details + * @return ResponseEntity with HTTP status 200 if successful, 401 if unauthorized + * @throws InvalidRequestException If input parameters or session are invalid. + */ + @Operation( + summary = SwaggerLiteralConstants.WALLETS_DELETE_SUMMARY, + description = SwaggerLiteralConstants.WALLETS_DELETE_DESCRIPTION, + operationId = "deleteWallet", + security = @SecurityRequirement(name = "SessionAuth"), + parameters = {@Parameter(name = "walletId", in = ParameterIn.PATH, required = true, description = "Unique identifier of the wallet to be deleted", schema = @Schema(type = "string"))} + ) + @ApiResponse(responseCode = "200", description = "Wallet successfully deleted") + @ApiResponse(responseCode = "400", description = "Invalid Wallet deletion request", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = { + @ExampleObject(name = "Invalid User ID", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"User ID cannot be null or empty\"}"), + @ExampleObject(name = "Invalid Wallet ID", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Invalid Wallet ID. Session and request Wallet ID do not match\"}"), + @ExampleObject(name = "Wallet ID not found in session", value = "{\"errorCode\": \"wallet_locked\", \"errorMessage\": \"Wallet is locked\"}"), + @ExampleObject(name = "Wallet not found in database", value = "{\"errorCode\": \"invalid_request\", \"errorMessage\": \"Wallet not found\"}")})) + @ApiResponse(responseCode = "401", description = "Unauthorized user deleting a Wallet", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "User ID not found in session", value = "{\"errorCode\": \"unauthorized\", \"errorMessage\": \"User ID not found in session\"}"))) + @ApiResponse(responseCode = "500", description = "Internal server error", content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorDTO.class), examples = @ExampleObject(name = "Unexpected Server Error", value = "{\"errorCode\": \"internal_server_error\", \"errorMessage\": \"We are unable to process request now\"}"))) + @DeleteMapping("/{walletId}") + public ResponseEntity deleteWallet(@PathVariable("walletId") String walletId, HttpSession httpSession) { + String userId = (String) httpSession.getAttribute(SessionKeys.USER_ID); + String sessionWalletId = (String) httpSession.getAttribute(SessionKeys.WALLET_ID); + if (sessionWalletId != null) { + WalletUtil.validateWalletId(httpSession, walletId); + httpSession.removeAttribute(SessionKeys.WALLET_KEY); + httpSession.removeAttribute(SessionKeys.WALLET_ID); + } + + walletService.deleteWallet(userId, walletId); + log.info("Cleared Wallet session attributes for Wallet with Id: {}", walletId); + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/io/mosip/mimoto/core/http/ResponseWrapper.java b/src/main/java/io/mosip/mimoto/core/http/ResponseWrapper.java index 44a0241c1..0be513708 100644 --- a/src/main/java/io/mosip/mimoto/core/http/ResponseWrapper.java +++ b/src/main/java/io/mosip/mimoto/core/http/ResponseWrapper.java @@ -1,7 +1,7 @@ package io.mosip.mimoto.core.http; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import io.mosip.mimoto.dto.ErrorDTO; import lombok.Data; diff --git a/src/main/java/io/mosip/mimoto/dto/CreateWalletRequestDto.java b/src/main/java/io/mosip/mimoto/dto/CreateWalletRequestDto.java new file mode 100644 index 000000000..6b226097c --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/CreateWalletRequestDto.java @@ -0,0 +1,21 @@ +package io.mosip.mimoto.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +@Data +@ToString +@ApiModel(description = "Model representing a create user wallet request") +@Schema(description = "Create Wallet Request DTO", requiredProperties = {"walletPin", "confirmWalletPin"}) +public class CreateWalletRequestDto { + @Schema(description = "PIN used to unlock the Wallet") + String walletPin; + + @Schema(description = "Re-entered PIN used to confirm the Wallet PIN") + String confirmWalletPin; + + @Schema(description = "Name of the Wallet") + String walletName; +} diff --git a/src/main/java/io/mosip/mimoto/dto/ErrorDTO.java b/src/main/java/io/mosip/mimoto/dto/ErrorDTO.java index 4d6a3e894..9afae3488 100644 --- a/src/main/java/io/mosip/mimoto/dto/ErrorDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/ErrorDTO.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonAlias; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -26,8 +27,10 @@ public class ErrorDTO implements Serializable { private static final long serialVersionUID = 2452990684776944908L; /** The errorcode. */ + @Schema(description = "It represents the type or category of the error") private String errorCode; + @Schema(description = "A human-readable message providing more details about the error") /** The message. */ @JsonAlias("message") private String errorMessage; diff --git a/src/main/java/io/mosip/mimoto/dto/IssuerDTO.java b/src/main/java/io/mosip/mimoto/dto/IssuerDTO.java index b03b87a97..665314815 100644 --- a/src/main/java/io/mosip/mimoto/dto/IssuerDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/IssuerDTO.java @@ -38,6 +38,8 @@ public class IssuerDTO { @NotBlank @Schema(description = "Authorization Audience for retrieving Token from token endpoint") String authorization_audience; + @URL + @NotBlank @Schema(description = "Mimoto Token Endpoint Fetching the Token From Authorization Server with Client Assertion") String token_endpoint; @URL @@ -55,6 +57,7 @@ public class IssuerDTO { @NotBlank @Schema(description = "Unique Identifier of the Issuer") String credential_issuer; + @URL @NotBlank @Schema(description = "Credential Issuer Host") String credential_issuer_host; diff --git a/src/main/java/io/mosip/mimoto/dto/ProviderDataConfig.java b/src/main/java/io/mosip/mimoto/dto/ProviderDataConfig.java new file mode 100644 index 000000000..d1cc9b630 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/ProviderDataConfig.java @@ -0,0 +1,20 @@ +package io.mosip.mimoto.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * The ProviderDataConfig class represents a configuration for provider data attributes. + * It contains fields for various attributes such as username, name, email, picture, and phone number. + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ProviderDataConfig { + private String userNameAttribute; + private String nameAttribute; + private String emailAttribute; + private String pictureAttribute; + private String phoneNumberAttribute; +} diff --git a/src/main/java/io/mosip/mimoto/dto/UnlockWalletRequestDto.java b/src/main/java/io/mosip/mimoto/dto/UnlockWalletRequestDto.java new file mode 100644 index 000000000..b8f91ec95 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/UnlockWalletRequestDto.java @@ -0,0 +1,18 @@ +package io.mosip.mimoto.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.ToString; + +@Data +@ToString +@ApiModel(description = "Model representing a unlock user wallet request") +@Schema(description = "Unlock Wallet Request DTO", requiredProperties = {"walletPin"}) +public class UnlockWalletRequestDto { + @Schema(description = "PIN used to unlock the Wallet") + String walletPin; + + @Schema(description = "Name of the Wallet") + String walletName; +} diff --git a/src/main/java/io/mosip/mimoto/dto/VerifiableCredentialRequestDTO.java b/src/main/java/io/mosip/mimoto/dto/VerifiableCredentialRequestDTO.java new file mode 100644 index 000000000..ce79af54a --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/VerifiableCredentialRequestDTO.java @@ -0,0 +1,32 @@ +package io.mosip.mimoto.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +@Data +public class VerifiableCredentialRequestDTO { + @Schema(description = "The unique identifier of the issuer") + @NotBlank(message = "issuerId cannot be blank") + String issuer; + + @Schema(description = "The unique identifier of the credential type from the issuer well-known configuration") + @NotBlank(message = "credentialConfigurationId cannot be blank") + String credentialConfigurationId; + + @Schema(description = "The authorization code received from the authorization server") + @NotBlank(message = "code cannot be blank") + String code; + + @Schema(description = "The grant type for the authorization request") + @NotBlank(message = "grantType cannot be blank") + String grantType; + + @Schema(description = "The redirect URI for the authorization request") + @NotBlank(message = "redirectUri cannot be blank") + String redirectUri; + + @Schema(description = "The code verifier used for PKCE (Proof Key for Code Exchange)") + @NotBlank(message = "codeVerifier cannot be blank") + String codeVerifier; +} diff --git a/src/main/java/io/mosip/mimoto/dto/WalletResponseDto.java b/src/main/java/io/mosip/mimoto/dto/WalletResponseDto.java new file mode 100644 index 000000000..ca3491eae --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/WalletResponseDto.java @@ -0,0 +1,16 @@ +package io.mosip.mimoto.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; + +@Data +@AllArgsConstructor +@Builder +public class WalletResponseDto { + @Schema(description = "Unique identifier of the Wallet") + String walletId; + @Schema(description = "Wallet name provided by user") + String walletName; +} diff --git a/src/main/java/io/mosip/mimoto/dto/idp/TokenResponseDTO.java b/src/main/java/io/mosip/mimoto/dto/idp/TokenResponseDTO.java index 8b688decf..a63b696c0 100644 --- a/src/main/java/io/mosip/mimoto/dto/idp/TokenResponseDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/idp/TokenResponseDTO.java @@ -24,4 +24,7 @@ public class TokenResponseDTO { @JsonInclude(JsonInclude.Include.NON_NULL) private String scope; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private String c_nonce; } diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java b/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java index 9d8320f54..e86f7c5ad 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/AppOTPRequestDTO.java @@ -3,9 +3,9 @@ import java.util.List; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; @Data public class AppOTPRequestDTO { diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java index 0e981a24a..f0f1981f6 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpInnerReqDto.java @@ -3,8 +3,8 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import java.util.List; @Data diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java index c4629612b..c1a3bad19 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/BindingOtpRequestDto.java @@ -5,8 +5,8 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; @Data @AllArgsConstructor diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java index 4181e8245..6210a81fc 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDefinitionResponseDto.java @@ -25,4 +25,9 @@ public class CredentialDefinitionResponseDto { @JsonProperty("credentialSubject") @Schema(description = "Credential Subject of the VC") private Map<@NotEmpty String, @Valid CredentialDisplayResponseDto> credentialSubject; + + @SerializedName("context") + @JsonProperty("context") + @Schema(description = "context of the Credential Definition") + private List<@NotEmpty String> context; } diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java index ab7409127..942638cac 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/CredentialDownloadRequestDTO.java @@ -2,7 +2,7 @@ import lombok.Data; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotNull; @Data public class CredentialDownloadRequestDTO { diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/IssuerConfig.java b/src/main/java/io/mosip/mimoto/dto/mimoto/IssuerConfig.java new file mode 100644 index 000000000..0073a8ad4 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/IssuerConfig.java @@ -0,0 +1,11 @@ +package io.mosip.mimoto.dto.mimoto; + +import io.mosip.mimoto.dto.IssuerDTO; +import lombok.Data; + +@Data +public class IssuerConfig { + private final IssuerDTO issuerDTO; + private final CredentialIssuerWellKnownResponse wellKnownResponse; + private final CredentialsSupportedResponse credentialsSupportedResponse; +} diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/ProofTypesSupported.java b/src/main/java/io/mosip/mimoto/dto/mimoto/ProofTypesSupported.java index faa6a460c..194865827 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/ProofTypesSupported.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/ProofTypesSupported.java @@ -6,7 +6,7 @@ import jakarta.validation.constraints.NotNull; import lombok.Data; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotEmpty; import java.util.List; @Data diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/UserMetadataDTO.java b/src/main/java/io/mosip/mimoto/dto/mimoto/UserMetadataDTO.java new file mode 100644 index 000000000..1d48cbd3f --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/UserMetadataDTO.java @@ -0,0 +1,28 @@ +package io.mosip.mimoto.dto.mimoto; + +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serializable; + +@Data +@AllArgsConstructor +public class UserMetadataDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "Display name of the user provided by the Identity Provider") + private String displayName; + + @Schema(description = "Profile picture of the user provided by the Identity Provider") + private String profilePictureUrl; + + @Schema(description = "Email of the user provided by the Identity Provider") + private String email; + + @Schema(description = "Wallet id of the user in use") + @JsonInclude(JsonInclude.Include.NON_NULL) + private String walletId; +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialDefinition.java b/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialDefinition.java index a07a56ab6..747e4ad4d 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialDefinition.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialDefinition.java @@ -4,8 +4,8 @@ import lombok.Builder; import lombok.Data; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Map; diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialProperties.java b/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialProperties.java index 7c710e730..555104c11 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialProperties.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialProperties.java @@ -8,13 +8,13 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Map; @Data -@JsonPropertyOrder({"@context", "credentialSubject", "expirationDate", "id", "issuanceDate", "issuer", "proof", "type"}) +@JsonPropertyOrder({"@context", "credentialSubject", "validUntil", "id", "validFrom", "issuer", "proof", "type", "issuanceDate", "expirationDate"}) @NoArgsConstructor @AllArgsConstructor @Builder @@ -24,8 +24,15 @@ public class VCCredentialProperties { @JsonInclude(JsonInclude.Include.NON_NULL) private String id; + @JsonInclude(JsonInclude.Include.NON_NULL) + private String validFrom; + + @JsonInclude(JsonInclude.Include.NON_NULL) private String issuanceDate; + @JsonInclude(JsonInclude.Include.NON_NULL) + private String validUntil; + @JsonInclude(JsonInclude.Include.NON_NULL) private String expirationDate; @@ -38,4 +45,7 @@ public class VCCredentialProperties { @NotEmpty private List<@NotBlank String> type; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Map credentialStatus; } diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialRequest.java b/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialRequest.java index 671ca1720..d7d9dafaa 100644 --- a/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialRequest.java +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/VCCredentialRequest.java @@ -4,9 +4,9 @@ import lombok.Builder; import lombok.Data; -import javax.validation.Valid; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; @Data diff --git a/src/main/java/io/mosip/mimoto/dto/mimoto/VerifiableCredentialResponseDTO.java b/src/main/java/io/mosip/mimoto/dto/mimoto/VerifiableCredentialResponseDTO.java new file mode 100644 index 000000000..5cb4b1965 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/mimoto/VerifiableCredentialResponseDTO.java @@ -0,0 +1,68 @@ +package io.mosip.mimoto.dto.mimoto; + +import io.mosip.mimoto.dto.DisplayDTO; +import io.mosip.mimoto.dto.IssuerDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import static io.mosip.mimoto.util.LocaleUtils.getCredentialDisplayDTOBasedOnLocale; +import static io.mosip.mimoto.util.LocaleUtils.getIssuerDisplayDTOBasedOnLocale; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor + +// Holds details related to credential UUID as per stored in DB and other metadata to display in wallet +public class VerifiableCredentialResponseDTO { + @Schema(description = "Name of the issuer") + private String issuerDisplayName; + + @Schema(description = "logo of the issuer") + private String issuerLogo; + + @Schema(description = "Name of the credential type") + private String credentialTypeDisplayName; + + @Schema(description = "logo of the credential type") + private String credentialTypeLogo; + + @Schema(description = "Unique Identifier of the Credential in database") + private String credentialId; + + public static VerifiableCredentialResponseDTO fromIssuerConfig( + IssuerConfig issuerConfig, String locale, String credentialId) { + String issuerName = ""; + String issuerLogo = ""; + String credentialType = ""; + String credentialTypeLogo = ""; + DisplayDTO issuerDisplayDTO; + CredentialSupportedDisplayResponse credentialTypeDisplayDTO; + if (null != issuerConfig) { + IssuerDTO issuerDTO = issuerConfig.getIssuerDTO(); + CredentialsSupportedResponse credentialsSupportedResponse = issuerConfig.getCredentialsSupportedResponse(); + if (issuerDTO != null) { + issuerDisplayDTO = getIssuerDisplayDTOBasedOnLocale(issuerDTO.getDisplay(), locale); + issuerName = issuerDisplayDTO.getName(); + issuerLogo = issuerDisplayDTO.getLogo().getUrl(); + } + if (credentialsSupportedResponse != null) { + credentialTypeDisplayDTO = getCredentialDisplayDTOBasedOnLocale( + credentialsSupportedResponse.getDisplay(), locale); + credentialType = credentialTypeDisplayDTO.getName(); + credentialTypeLogo = credentialTypeDisplayDTO.getLogo().getUrl(); + } + } + + return VerifiableCredentialResponseDTO.builder() + .issuerDisplayName(issuerName) + .issuerLogo(issuerLogo) + .credentialTypeDisplayName(credentialType) + .credentialTypeLogo(credentialTypeLogo) + .credentialId(credentialId) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/dto/openid/datashare/DataShareResponseWrapperDTO.java b/src/main/java/io/mosip/mimoto/dto/openid/datashare/DataShareResponseWrapperDTO.java index e55c010f0..3db0037a0 100644 --- a/src/main/java/io/mosip/mimoto/dto/openid/datashare/DataShareResponseWrapperDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/openid/datashare/DataShareResponseWrapperDTO.java @@ -6,7 +6,6 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/io/mosip/mimoto/dto/openid/presentation/VerifiablePresentationDTO.java b/src/main/java/io/mosip/mimoto/dto/openid/presentation/VerifiablePresentationDTO.java index 346288d87..fc9125f1b 100644 --- a/src/main/java/io/mosip/mimoto/dto/openid/presentation/VerifiablePresentationDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/openid/presentation/VerifiablePresentationDTO.java @@ -8,8 +8,8 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; import java.util.List; @Data diff --git a/src/main/java/io/mosip/mimoto/dto/resident/CredentialRequestResponseDTO.java b/src/main/java/io/mosip/mimoto/dto/resident/CredentialRequestResponseDTO.java index 62c314ff5..d8444ef7f 100644 --- a/src/main/java/io/mosip/mimoto/dto/resident/CredentialRequestResponseDTO.java +++ b/src/main/java/io/mosip/mimoto/dto/resident/CredentialRequestResponseDTO.java @@ -3,8 +3,8 @@ import java.util.ArrayList; import java.util.List; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import io.mosip.mimoto.dto.ErrorDTO; import lombok.Data; diff --git a/src/main/java/io/mosip/mimoto/dto/resident/WalletCredentialResponseDTO.java b/src/main/java/io/mosip/mimoto/dto/resident/WalletCredentialResponseDTO.java new file mode 100644 index 000000000..ed13dd79d --- /dev/null +++ b/src/main/java/io/mosip/mimoto/dto/resident/WalletCredentialResponseDTO.java @@ -0,0 +1,24 @@ +package io.mosip.mimoto.dto.resident; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.core.io.InputStreamResource; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class WalletCredentialResponseDTO { + + @JsonProperty("file_content_stream") + @Schema(description = "This is a PDF input stream containing the requested credential data in byte format") + InputStreamResource fileContentStream; + + @JsonProperty("file_name") + @Schema(description = " Name of the PDF file") + String fileName; +} diff --git a/src/main/java/io/mosip/mimoto/exception/CorruptedEncryptedDataException.java b/src/main/java/io/mosip/mimoto/exception/CorruptedEncryptedDataException.java new file mode 100644 index 000000000..661224a26 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/CorruptedEncryptedDataException.java @@ -0,0 +1,16 @@ +package io.mosip.mimoto.exception; + +public class CorruptedEncryptedDataException extends BaseCheckedException { + private static final long serialVersionUID = 1L; + + + public CorruptedEncryptedDataException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + + } + + public CorruptedEncryptedDataException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/CredentialNotFoundException.java b/src/main/java/io/mosip/mimoto/exception/CredentialNotFoundException.java new file mode 100644 index 000000000..828a07cd1 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/CredentialNotFoundException.java @@ -0,0 +1,16 @@ +package io.mosip.mimoto.exception; + +import java.io.Serial; + +public class CredentialNotFoundException extends BaseCheckedException { + @Serial + private static final long serialVersionUID = 1L; + + public CredentialNotFoundException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + } + + public CredentialNotFoundException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/CredentialPdfGenerationException.java b/src/main/java/io/mosip/mimoto/exception/CredentialPdfGenerationException.java new file mode 100644 index 000000000..eef1f4eb5 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/CredentialPdfGenerationException.java @@ -0,0 +1,20 @@ +package io.mosip.mimoto.exception; + +public class CredentialPdfGenerationException extends BaseUncheckedException { + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + public CredentialPdfGenerationException() { + super(); + } + + public CredentialPdfGenerationException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + + } + + public CredentialPdfGenerationException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/CredentialProcessingException.java b/src/main/java/io/mosip/mimoto/exception/CredentialProcessingException.java new file mode 100644 index 000000000..45f332e32 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/CredentialProcessingException.java @@ -0,0 +1,16 @@ +package io.mosip.mimoto.exception; + +public class CredentialProcessingException extends BaseUncheckedException { + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + public CredentialProcessingException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + + } + + public CredentialProcessingException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/DecryptionException.java b/src/main/java/io/mosip/mimoto/exception/DecryptionException.java new file mode 100644 index 000000000..79dfc8398 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/DecryptionException.java @@ -0,0 +1,20 @@ +package io.mosip.mimoto.exception; + +public class DecryptionException extends BaseCheckedException { + + /** + * + */ + private static final long serialVersionUID = 1L; + + + public DecryptionException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + + } + + public DecryptionException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/EncryptionException.java b/src/main/java/io/mosip/mimoto/exception/EncryptionException.java new file mode 100644 index 000000000..967c906fc --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/EncryptionException.java @@ -0,0 +1,19 @@ +package io.mosip.mimoto.exception; + +import java.io.Serial; + +public class EncryptionException extends BaseUncheckedException { + + @Serial + private static final long serialVersionUID = 1L; + + public EncryptionException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + + } + + public EncryptionException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/ErrorConstants.java b/src/main/java/io/mosip/mimoto/exception/ErrorConstants.java index 1bf3d2b38..e474db05e 100644 --- a/src/main/java/io/mosip/mimoto/exception/ErrorConstants.java +++ b/src/main/java/io/mosip/mimoto/exception/ErrorConstants.java @@ -19,13 +19,27 @@ public enum ErrorConstants { INVALID_REDIRECT_URI("invalid_redirect_uri", "The requested redirect uri doesn’t match."), INTERNAL_SERVER_ERROR("internal_server_error", "We are unable to process request now"), - PROOF_TYPE_NOT_SUPPORTED_EXCEPTION("proof_type_not_supported", "Proof Type available in received credentials is not matching with supported proof terms" ), - SIGNATURE_VERIFICATION_EXCEPTION("signature_verification_failed", "Error while doing signature verification" ), - JSON_PARSING_EXCEPTION("json_parsing_failed", "Given data couldn't be parsed to JSON string" ), - UNKNOWN_EXCEPTION("unknown_exception", "Error while doing verification of verifiable credential" ), - PROOF_DOCUMENT_NOT_FOUND_EXCEPTION("proof_document_not_found", "Proof document is not available in the received credentials" ), - PUBLIC_KEY_NOT_FOUND_EXCEPTION("public_key_not_found", "Proof document is not available in the received credentials"); + PROOF_TYPE_NOT_SUPPORTED_EXCEPTION("proof_type_not_supported", "Proof Type available in received credentials is not matching with supported proof terms"), + SIGNATURE_VERIFICATION_EXCEPTION("signature_verification_failed", "Error while doing signature verification"), + JSON_PARSING_EXCEPTION("json_parsing_failed", "Given data couldn't be parsed to JSON string"), + UNKNOWN_EXCEPTION("unknown_exception", "Error while doing verification of verifiable credential"), + PROOF_DOCUMENT_NOT_FOUND_EXCEPTION("proof_document_not_found", "Proof document is not available in the received credentials"), + PUBLIC_KEY_NOT_FOUND_EXCEPTION("public_key_not_found", "Proof document is not available in the received credentials"), + OAUTH2_AUTHENTICATION_EXCEPTION("user_authentication_error", "Failed to authenticate user via OAuth Identity Provider during login"), + LOGIN_SESSION_INVALIDATE_EXCEPTION("user_logout_error", "Exception occurred when invalidating the session of a user"), + SESSION_EXPIRED_OR_INVALID("session_invalid_or_expired", "User session is missing or expired. Please log in again."), + DATABASE_CONNECTION_EXCEPTION("database_unavailable", "Failed to connect to the database"), + REDIS_CONNECTION_EXCEPTION("redis_unavailable", "Failed to connect to the redis"), + ENCRYPTION_FAILED("encryption_failed", "Failed to encrypt the data"), + DECRYPTION_FAILED("decryption_failed", "Failed to decrypt the data"), + SCHEMA_MISMATCH("schema_mismatch", "Failed to restored the stored data"), + INVALID_USER("invalid_user", "User does not exist in database"), + CREDENTIAL_DOWNLOAD_EXCEPTION("credential_download_error", "Failed to download and store the credential"), + CREDENTIAL_FETCH_EXCEPTION("credential_fetch_error", "Failed to fetch the credential"), + UNAUTHORIZED_ACCESS("unauthorized", "You are not authorized to access this resource"), + WALLET_LOCKED("wallet_locked", "Wallet is locked"), + INVALID_PIN("invalid_pin", "Invalid PIN or wallet key provided"); private final String errorCode; private final String errorMessage; diff --git a/src/main/java/io/mosip/mimoto/exception/ExternalServiceUnavailableException.java b/src/main/java/io/mosip/mimoto/exception/ExternalServiceUnavailableException.java new file mode 100644 index 000000000..b21a425dc --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/ExternalServiceUnavailableException.java @@ -0,0 +1,20 @@ +package io.mosip.mimoto.exception; + +public class ExternalServiceUnavailableException extends BaseCheckedException { + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + public ExternalServiceUnavailableException() { + super(); + } + + public ExternalServiceUnavailableException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + + } + + public ExternalServiceUnavailableException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/InvalidRequestException.java b/src/main/java/io/mosip/mimoto/exception/InvalidRequestException.java new file mode 100644 index 000000000..e7e3eb006 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/InvalidRequestException.java @@ -0,0 +1,17 @@ +package io.mosip.mimoto.exception; + +import java.io.Serial; + +public class InvalidRequestException extends BaseUncheckedException { + + @Serial + private static final long serialVersionUID = 1L; + + public InvalidRequestException(String errorCode, String errorMessage, Throwable cause) { + super(errorCode, errorMessage, cause); + } + + public InvalidRequestException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/KeyGenerationException.java b/src/main/java/io/mosip/mimoto/exception/KeyGenerationException.java new file mode 100644 index 000000000..a231e940b --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/KeyGenerationException.java @@ -0,0 +1,13 @@ +package io.mosip.mimoto.exception; + +public class KeyGenerationException extends BaseUncheckedException { + + public KeyGenerationException(String code, String message) { + super(code, message); + } + + public KeyGenerationException(String code, String message, Throwable rootCause) { + super(code, message, rootCause); + } + +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/exception/LoginSessionException.java b/src/main/java/io/mosip/mimoto/exception/LoginSessionException.java new file mode 100644 index 000000000..76f2125b2 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/LoginSessionException.java @@ -0,0 +1,17 @@ + +package io.mosip.mimoto.exception; + +import org.springframework.http.HttpStatus; + +public class LoginSessionException extends BaseCheckedException { + private final HttpStatus status; + + public LoginSessionException(String code, String message, HttpStatus status) { + super(code, message); + this.status = status; + } + + public HttpStatus getStatus() { + return status; + } +} diff --git a/src/main/java/io/mosip/mimoto/exception/OAuth2AuthenticationException.java b/src/main/java/io/mosip/mimoto/exception/OAuth2AuthenticationException.java new file mode 100644 index 000000000..bf9c9131e --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/OAuth2AuthenticationException.java @@ -0,0 +1,17 @@ +package io.mosip.mimoto.exception; + +import org.springframework.http.HttpStatus; + +public class OAuth2AuthenticationException extends BaseCheckedException { + private final HttpStatus status; + + public OAuth2AuthenticationException(String code, String message, HttpStatus status) { + super(code, message); + this.status=status; + } + + public HttpStatus getStatus() { + return status; + } +} + diff --git a/src/main/java/io/mosip/mimoto/exception/UnauthorizedAccessException.java b/src/main/java/io/mosip/mimoto/exception/UnauthorizedAccessException.java new file mode 100644 index 000000000..fb4771c7b --- /dev/null +++ b/src/main/java/io/mosip/mimoto/exception/UnauthorizedAccessException.java @@ -0,0 +1,13 @@ +package io.mosip.mimoto.exception; + +import java.io.Serial; + +public class UnauthorizedAccessException extends BaseUncheckedException { + + @Serial + private static final long serialVersionUID = 1L; + + public UnauthorizedAccessException(String errorCode, String errorMessage) { + super(errorCode, errorMessage); + } +} diff --git a/src/main/java/io/mosip/mimoto/model/CredentialMetadata.java b/src/main/java/io/mosip/mimoto/model/CredentialMetadata.java new file mode 100644 index 000000000..177e429cb --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/CredentialMetadata.java @@ -0,0 +1,11 @@ +package io.mosip.mimoto.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class CredentialMetadata { + private String issuerId; + private String credentialType; +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/model/KeyMetadata.java b/src/main/java/io/mosip/mimoto/model/KeyMetadata.java new file mode 100644 index 000000000..36821a73e --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/KeyMetadata.java @@ -0,0 +1,8 @@ +package io.mosip.mimoto.model; + +import lombok.Data; + +@Data +public class KeyMetadata { + private String algorithmName; +} diff --git a/src/main/java/io/mosip/mimoto/model/ProofSigningKey.java b/src/main/java/io/mosip/mimoto/model/ProofSigningKey.java new file mode 100644 index 000000000..976b5a67f --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/ProofSigningKey.java @@ -0,0 +1,49 @@ +package io.mosip.mimoto.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import java.security.PrivateKey; +import java.time.Instant; + +@Entity +@Table(name = "proof_signing_key") +@Getter +@Setter +public class ProofSigningKey { + + @Id + @Column(length = 36, updatable = false, nullable = false) + private String id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "wallet_id", nullable = false) + private Wallet wallet; // Foreign key reference to the wallet table + + @Column(nullable = false) + private String publicKey; // Public key for wallet + + @Column(name = "secret_key", nullable = false) + private String encryptedSecretKey; // Secret key, encrypted using walletKey + + @Transient + private PrivateKey secretKey; + + @JdbcTypeCode(SqlTypes.JSON) + @Column(columnDefinition = "jsonb") + private KeyMetadata keyMetadata; // Metadata about the public and private keys + + @Column(nullable = false, updatable = false) + private Instant createdAt = Instant.now(); + + @Column(nullable = false) + private Instant updatedAt = Instant.now(); + + @PreUpdate + public void preUpdate() { + this.updatedAt = Instant.now(); + } +} diff --git a/src/main/java/io/mosip/mimoto/model/UserMetadata.java b/src/main/java/io/mosip/mimoto/model/UserMetadata.java new file mode 100644 index 000000000..959dbecc3 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/UserMetadata.java @@ -0,0 +1,46 @@ +package io.mosip.mimoto.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Getter; +import lombok.Setter; + +import java.time.Instant; + +@Entity +@Table(name = "user_metadata") +@Getter +@Setter +public class UserMetadata { + + @Id + @Column(length = 36, updatable = false, nullable = false) + private String id; + + @Column(name = "provider_subject_id", nullable = false) + private String providerSubjectId; + + @Column(name = "identity_provider", nullable = false) + private String identityProvider; + + @Column(name = "display_name", nullable = false, columnDefinition = "TEXT") + private String displayName; + + @Column(name = "profile_picture_url", columnDefinition = "TEXT") + private String profilePictureUrl; + + @Column(name = "phone_number") + private String phoneNumber; + + @Column(name = "email", nullable = false, columnDefinition = "TEXT") + private String email; + + @Column(name = "created_at", updatable = false) + private Instant createdAt; + + @Column(name = "updated_at") + private Instant updatedAt; +} + diff --git a/src/main/java/io/mosip/mimoto/model/VerifiableCredential.java b/src/main/java/io/mosip/mimoto/model/VerifiableCredential.java new file mode 100644 index 000000000..896a5118d --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/VerifiableCredential.java @@ -0,0 +1,40 @@ +package io.mosip.mimoto.model; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; +import java.time.Instant; + +@Entity +@Table(name = "verifiable_credentials") +@Getter +@Setter +public class VerifiableCredential { + + @Id + @Column(length = 36, updatable = false, nullable = false) + private String id; + + @Column(name = "wallet_id", nullable = false) + private String walletId; + + @Column(nullable = false, columnDefinition = "TEXT") + private String credential; + + @JdbcTypeCode(SqlTypes.JSON) + @Column(nullable = false, columnDefinition = "jsonb") + private CredentialMetadata credentialMetadata; + + @Column(nullable = false, updatable = false) + private Instant createdAt = Instant.now(); + + @Column(nullable = false) + private Instant updatedAt = Instant.now(); + + @PreUpdate + public void preUpdate() { + this.updatedAt = Instant.now(); + } +} diff --git a/src/main/java/io/mosip/mimoto/model/Wallet.java b/src/main/java/io/mosip/mimoto/model/Wallet.java new file mode 100644 index 000000000..67002b982 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/Wallet.java @@ -0,0 +1,59 @@ +package io.mosip.mimoto.model; + +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Setter; +import org.hibernate.annotations.JdbcTypeCode; +import org.hibernate.type.SqlTypes; + +import java.time.Instant; +import java.util.List; + +@Entity +@Table(name = "wallet") +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Wallet { + + @Id + @Column(length = 36, updatable = false, nullable = false) + private String id; + + @Column(nullable = false) + private String userId; // Foreign key reference to user_metadata + + @Column(nullable = false) + private String walletKey; // Encrypted wallet key using AES256-GCM + + @JdbcTypeCode(SqlTypes.JSON) + @Column(columnDefinition = "jsonb") + private WalletMetadata walletMetadata; // Metadata about the wallet, including encryption info + + @OneToMany(mappedBy = "wallet", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List proofSigningKeys; // One wallet can have many keys + + @Column(nullable = false, updatable = false) + private Instant createdAt; + + @Column(nullable = false) + private Instant updatedAt; + + @PrePersist + public void prePersist() { + Instant now = Instant.now(); + this.createdAt = now; + this.updatedAt = now; + } + + @PreUpdate + public void preUpdate() { + this.updatedAt = Instant.now(); + } +} + diff --git a/src/main/java/io/mosip/mimoto/model/WalletMetadata.java b/src/main/java/io/mosip/mimoto/model/WalletMetadata.java new file mode 100644 index 000000000..27c6d66e8 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/model/WalletMetadata.java @@ -0,0 +1,12 @@ +package io.mosip.mimoto.model; + +import lombok.Data; + +@Data +public class WalletMetadata { + + private String encryptionAlgo; + private String encryptionType; + private String name; + +} diff --git a/src/main/java/io/mosip/mimoto/repository/ProofSigningKeyRepository.java b/src/main/java/io/mosip/mimoto/repository/ProofSigningKeyRepository.java new file mode 100644 index 000000000..394ce7f27 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/repository/ProofSigningKeyRepository.java @@ -0,0 +1,16 @@ +package io.mosip.mimoto.repository; + +import io.mosip.mimoto.model.ProofSigningKey; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository +public interface ProofSigningKeyRepository extends JpaRepository { + @Query(value = "SELECT * FROM proof_signing_key p WHERE p.wallet_id = :walletId AND p.key_metadata->>'algorithmName' = :algorithmName", nativeQuery = true) + Optional findByWalletIdAndAlgorithm(@Param("walletId") String walletId, @Param("algorithmName") String algorithmName); + + Optional findByWalletId(String walletId); +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/repository/UserMetadataRepository.java b/src/main/java/io/mosip/mimoto/repository/UserMetadataRepository.java new file mode 100644 index 000000000..08d961354 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/repository/UserMetadataRepository.java @@ -0,0 +1,11 @@ +package io.mosip.mimoto.repository; + +import io.mosip.mimoto.model.UserMetadata; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.Optional; + +@Repository +public interface UserMetadataRepository extends JpaRepository { + Optional findByProviderSubjectIdAndIdentityProvider(String providerSubjectId, String identityProvider); +} diff --git a/src/main/java/io/mosip/mimoto/repository/WalletCredentialsRepository.java b/src/main/java/io/mosip/mimoto/repository/WalletCredentialsRepository.java new file mode 100644 index 000000000..ea69f69ec --- /dev/null +++ b/src/main/java/io/mosip/mimoto/repository/WalletCredentialsRepository.java @@ -0,0 +1,19 @@ +package io.mosip.mimoto.repository; + +import io.mosip.mimoto.model.VerifiableCredential; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; + +@Repository +public interface WalletCredentialsRepository extends JpaRepository { + Optional findByIdAndWalletId(String id, String walletId); + + @Query(value = "SELECT EXISTS (SELECT 1 FROM verifiable_credentials WHERE credential_metadata->>'issuerId' = :issuerId AND credential_metadata->>'credentialType' = :credentialType AND wallet_id = :walletId)", nativeQuery = true) + boolean existsByIssuerIdAndCredentialTypeAndWalletId(@Param("issuerId") String issuerId, @Param("credentialType") String credentialType, @Param("walletId") String walletId); + + List findByWalletIdOrderByCreatedAtDesc(String walletId); +} diff --git a/src/main/java/io/mosip/mimoto/repository/WalletRepository.java b/src/main/java/io/mosip/mimoto/repository/WalletRepository.java new file mode 100644 index 000000000..c1f847671 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/repository/WalletRepository.java @@ -0,0 +1,16 @@ +package io.mosip.mimoto.repository; + +import io.mosip.mimoto.model.Wallet; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Optional; + +public interface WalletRepository extends JpaRepository { + @Query("SELECT w FROM Wallet w WHERE w.userId = :userId ORDER BY w.createdAt ASC") + List findWalletByUserId(@Param("userId") String userId); + + Optional findByUserIdAndId(String userId, String walletId); +} diff --git a/src/main/java/io/mosip/mimoto/security/oauth2/CustomOAuth2UserService.java b/src/main/java/io/mosip/mimoto/security/oauth2/CustomOAuth2UserService.java new file mode 100644 index 000000000..6f72365fb --- /dev/null +++ b/src/main/java/io/mosip/mimoto/security/oauth2/CustomOAuth2UserService.java @@ -0,0 +1,125 @@ +package io.mosip.mimoto.security.oauth2; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.config.oauth2.OAuth2ProviderProperties; +import io.mosip.mimoto.dto.ProviderDataConfig; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.service.UserMetadataService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * Custom implementation of the {@link DefaultOAuth2UserService} to handle + * user information retrieval and processing for OAuth2 authentication. + * + * This service customizes the loading of user details from an OAuth2 provider + * by enriching the user attributes, updating user metadata, and handling + * provider-specific configurations. + */ +@Service +@Slf4j +public class CustomOAuth2UserService extends DefaultOAuth2UserService { + + private final Map providers; + private final UserMetadataService userMetadataService; + + public CustomOAuth2UserService(OAuth2ProviderProperties providerProperties, + UserMetadataService userMetadataService) { + this.providers = providerProperties.getProvider(); + this.userMetadataService = userMetadataService; + } + + @Override + public OAuth2User loadUser(OAuth2UserRequest userRequest) { + OAuth2User oAuth2User = loadFromProvider(userRequest); + String registrationId = userRequest.getClientRegistration().getRegistrationId(); + ProviderDataConfig provider = getProviderConfig(registrationId); + + Map attributes = extractAttributes(oAuth2User, provider); + String userId = updateUserMetadata(attributes, provider, registrationId); + enrichAttributes(attributes, userId); + + String userNameAttribute = StringUtils.defaultIfBlank(provider.getUserNameAttribute(), "sub"); + return new DefaultOAuth2User(oAuth2User.getAuthorities(), attributes, userNameAttribute); + } + + private ProviderDataConfig getProviderConfig(String registrationId) { + ProviderDataConfig provider = providers.get(registrationId); + if (provider == null) { + log.error("Missing OAuth2 provider config for: {}", registrationId); + throw new IllegalArgumentException("Missing OAuth2 provider config for: " + registrationId); + } + return provider; + } + + private Map extractAttributes(OAuth2User oAuth2User, ProviderDataConfig provider) { + Map attributes = new HashMap<>(oAuth2User.getAttributes()); + attributes.put("name", safeGet(attributes, provider.getNameAttribute())); + attributes.put("email", safeGet(attributes, provider.getEmailAttribute())); + attributes.put("phone", safeGet(attributes, provider.getPhoneNumberAttribute())); + attributes.put("picture", extractProfilePicture(attributes, provider.getPictureAttribute())); + return attributes; + } + + private String extractProfilePicture(Map attributes, String pictureAttribute) { + if (StringUtils.isBlank(pictureAttribute) || !attributes.containsKey(pictureAttribute)) { + return null; + } + + Object picture = attributes.get(pictureAttribute); + if (picture instanceof Map picMap) { + Object data = picMap.get("data"); + if (data instanceof Map dataMap) { + Object url = dataMap.get("url"); + return url != null ? url.toString() : null; + } + } else if (picture instanceof String picUrl) { + return picUrl; + } + + return null; + } + + private String updateUserMetadata(Map attributes, ProviderDataConfig provider, String registrationId) { + String providerSubjectId = safeGet(attributes, StringUtils.defaultIfBlank(provider.getUserNameAttribute(), "sub")); + try { + return userMetadataService.updateOrCreateUserMetadata( + providerSubjectId, + registrationId, + safeGet(attributes, "name"), + safeGet(attributes, "picture"), + safeGet(attributes, "email") + ); + } catch (DecryptionException e) { + log.error("Error updating user metadata", e); + throw new RuntimeException("Metadata update failed", e); + } + } + + private void enrichAttributes(Map attributes, String userId) { + attributes.put(SessionKeys.USER_ID, userId); + } + + private String safeGet(Map attributes, String key) { + if (StringUtils.isBlank(key)) { + return null; + } + Object value = attributes.get(key); + if (value == null) { + return null; + } + return value.toString(); + } + + protected OAuth2User loadFromProvider(OAuth2UserRequest userRequest) { + return super.loadUser(userRequest); + } +} diff --git a/src/main/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationFailureHandler.java b/src/main/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationFailureHandler.java new file mode 100644 index 000000000..15cd5e61c --- /dev/null +++ b/src/main/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationFailureHandler.java @@ -0,0 +1,49 @@ +package io.mosip.mimoto.security.oauth2; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +@Component +@Slf4j +public class OAuth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { + + @Value("${mosip.inji.web.url}") + private String injiWebUrl; + + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) + throws IOException { + String errorMessage = ""; + + if (exception != null) { + if (exception instanceof OAuth2AuthenticationException) { + Throwable cause = exception.getCause(); + if (cause instanceof java.net.SocketTimeoutException) { + errorMessage = "Timeout while connecting to the IDP for authorization"; + } else if (cause instanceof java.net.ConnectException) { + errorMessage = "Could not connect to the IDP for authorization"; + } else if (exception.getMessage().contains("access_denied")) { + errorMessage = "Consent was denied to share the details with the application. Please give consent and try again"; + } else { + errorMessage = "Login is failed due to : " + exception.getMessage(); + } + } else { + errorMessage = "Login is failed due to : " + exception.getMessage(); + } + } + + String encodedErrorMessage = URLEncoder.encode(errorMessage, StandardCharsets.UTF_8); + String redirectUrl = injiWebUrl + "/?status=error&error_message=" + encodedErrorMessage; + getRedirectStrategy().sendRedirect(request, response, redirectUrl); + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationSuccessHandler.java b/src/main/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationSuccessHandler.java new file mode 100644 index 000000000..077d6358d --- /dev/null +++ b/src/main/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationSuccessHandler.java @@ -0,0 +1,52 @@ +package io.mosip.mimoto.security.oauth2; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +@Component +@Slf4j +public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + @Value("${mosip.inji.web.authentication.success.redirect.url}") + private String authenticationSuccessRedirectUrl; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + OAuth2AuthenticationToken oauth2Token = (OAuth2AuthenticationToken) authentication; + OAuth2User oAuth2User = oauth2Token.getPrincipal(); + + HttpSession session = request.getSession(false); + if (session == null) { + log.error("Session not available"); + throw new ServletException("Session not available"); + } + + String clientRegistrationId = oauth2Token.getAuthorizedClientRegistrationId(); + session.setAttribute("clientRegistrationId", clientRegistrationId); + + // Add user info to session for UI + String displayName = oAuth2User.getAttribute("name"); + String profilePictureUrl = oAuth2User.getAttribute("picture"); + String email = oAuth2User.getAttribute("email"); + + session.setAttribute(SessionKeys.USER_METADATA, new UserMetadataDTO(displayName, profilePictureUrl, email, null)); + + String userId = oAuth2User.getAttribute("userId"); + session.setAttribute(SessionKeys.USER_ID, userId); + + response.sendRedirect(authenticationSuccessRedirectUrl); + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/service/CredentialPDFGeneratorService.java b/src/main/java/io/mosip/mimoto/service/CredentialPDFGeneratorService.java new file mode 100644 index 000000000..c69d2d9a6 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/CredentialPDFGeneratorService.java @@ -0,0 +1,253 @@ +package io.mosip.mimoto.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.WriterException; +import com.google.zxing.client.j2se.MatrixToImageWriter; +import com.google.zxing.common.BitMatrix; +import com.google.zxing.qrcode.QRCodeWriter; +import com.itextpdf.html2pdf.ConverterProperties; +import com.itextpdf.html2pdf.HtmlConverter; +import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; +import com.itextpdf.kernel.pdf.PdfWriter; +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.mimoto.CredentialDisplayResponseDto; +import io.mosip.mimoto.dto.mimoto.CredentialIssuerDisplayResponse; +import io.mosip.mimoto.dto.mimoto.CredentialsSupportedResponse; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.dto.openid.presentation.PresentationDefinitionDTO; +import io.mosip.mimoto.model.QRCodeType; +import io.mosip.mimoto.service.impl.PresentationServiceImpl; +import io.mosip.mimoto.util.LocaleUtils; +import io.mosip.mimoto.util.Utilities; +import io.mosip.pixelpass.PixelPass; +import lombok.extern.slf4j.Slf4j; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class CredentialPDFGeneratorService { + + @Autowired + ObjectMapper objectMapper; + + @Value("${mosip.inji.ovp.qrdata.pattern}") + String ovpQRDataPattern; + + @Value("${mosip.inji.qr.code.height:500}") + Integer qrCodeHeight; + + @Value("${mosip.inji.qr.code.width:500}") + Integer qrCodeWidth; + + @Value("${mosip.inji.qr.data.size.limit:2000}") + Integer allowedQRDataSizeLimit; + + @Autowired + PresentationServiceImpl presentationService; + + @Autowired + private Utilities utilities; + + @Autowired + private PixelPass pixelPass; + + + public ByteArrayInputStream generatePdfForVerifiableCredentials(String credentialType, VCCredentialResponse vcCredentialResponse, IssuerDTO issuerDTO, CredentialsSupportedResponse credentialsSupportedResponse, String dataShareUrl, String credentialValidity, String locale) throws Exception { + LinkedHashMap> displayProperties = loadDisplayPropertiesFromWellknown(vcCredentialResponse, credentialsSupportedResponse, locale); + Map data = getPdfResourceFromVcProperties(displayProperties, credentialsSupportedResponse, vcCredentialResponse, issuerDTO, dataShareUrl, credentialValidity); + return renderVCInCredentialTemplate(data, issuerDTO.getIssuer_id(), credentialType); + } + + @NotNull + private static LinkedHashMap> loadDisplayPropertiesFromWellknown(VCCredentialResponse vcCredentialResponse, CredentialsSupportedResponse credentialsSupportedResponse, String userLocale) { + LinkedHashMap> displayProperties = new LinkedHashMap<>(); + Map credentialProperties = vcCredentialResponse.getCredential().getCredentialSubject(); + LinkedHashMap vcPropertiesFromWellKnown = new LinkedHashMap<>(); + Map credentialSubject = credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject(); + String locale = LocaleUtils.resolveLocaleWithFallback(credentialSubject, userLocale); + if (locale != null) { + credentialSubject.keySet().forEach(VCProperty -> { + Optional filteredResponse = credentialSubject.get(VCProperty) + .getDisplay().stream() + .filter(obj -> LocaleUtils.matchesLocale(obj.getLocale(), locale)) + .findFirst(); + + if (filteredResponse.isPresent()) { + CredentialIssuerDisplayResponse filteredValue = filteredResponse.get(); + vcPropertiesFromWellKnown.put(VCProperty, filteredValue); + } + }); + } + + List orderProperty = credentialsSupportedResponse.getOrder(); + + List fieldProperties = orderProperty == null ? new ArrayList<>(vcPropertiesFromWellKnown.keySet()) : orderProperty; + fieldProperties.forEach(vcProperty -> { + if (vcPropertiesFromWellKnown.get(vcProperty) != null && credentialProperties.get(vcProperty) != null) { + displayProperties.put(vcProperty, Map.of(vcPropertiesFromWellKnown.get(vcProperty), credentialProperties.get(vcProperty))); + } + }); + return displayProperties; + } + + private Map getPdfResourceFromVcProperties(LinkedHashMap> displayProperties, CredentialsSupportedResponse credentialsSupportedResponse, VCCredentialResponse vcCredentialResponse, IssuerDTO issuerDTO, String dataShareUrl, String credentialValidity) throws IOException, WriterException { + Map data = new HashMap<>(); + LinkedHashMap rowProperties = new LinkedHashMap<>(); + String backgroundColor = credentialsSupportedResponse.getDisplay().getFirst().getBackgroundColor(); + String backgroundImage = credentialsSupportedResponse.getDisplay().getFirst().getBackgroundImage().getUri(); + String textColor = credentialsSupportedResponse.getDisplay().getFirst().getTextColor(); + String credentialSupportedType = credentialsSupportedResponse.getDisplay().getFirst().getName(); + String face = vcCredentialResponse.getCredential().getCredentialSubject().get("face") != null ? (String) vcCredentialResponse.getCredential().getCredentialSubject().get("face") : null; + + displayProperties.entrySet().stream() + .forEachOrdered(entry -> { + String originalKey = entry.getKey(); + Map properties = entry.getValue(); + + // Process the inner map + ((Map) properties).entrySet().stream() + .forEachOrdered(innerEntry -> { + // loadDisplayPropertiesFromWellknown method returns both name and locale field values of the matching display obj in the response + CredentialIssuerDisplayResponse matchingWellknownDisplayObj = innerEntry.getKey(); + String nameFromDisplayObj = matchingWellknownDisplayObj.getName(); + String localeFromDisplayObj = matchingWellknownDisplayObj.getLocale(); + Object propertyValFromDownloadedVcResponse = innerEntry.getValue(); + String value = ""; + + if (propertyValFromDownloadedVcResponse instanceof Map) { + // If the value is a Map, handle it as a Map + value = handleMap(propertyValFromDownloadedVcResponse); + } else if (propertyValFromDownloadedVcResponse instanceof List) { + // If the value is a List, handle it as a List + value = handleList(propertyValFromDownloadedVcResponse, localeFromDisplayObj); + } else { + // Otherwise, just convert to string + value = propertyValFromDownloadedVcResponse.toString(); + } + + // Put the result into the rowProperties map + rowProperties.put(originalKey, Map.of(nameFromDisplayObj, value)); + }); + + }); + + String qrCodeImage; + if (QRCodeType.OnlineSharing.equals(issuerDTO.getQr_code_type())) { + // Login flow + if (dataShareUrl.isEmpty()) { + qrCodeImage = constructQRCodeWithVCData(vcCredentialResponse); + } else { + qrCodeImage = constructQRCodeWithAuthorizeRequest(vcCredentialResponse, dataShareUrl); + } + } else if (QRCodeType.EmbeddedVC.equals(issuerDTO.getQr_code_type())) { + qrCodeImage = constructQRCodeWithVCData(vcCredentialResponse); + } else { + qrCodeImage = ""; + } + + data.put("qrCodeImage", qrCodeImage); + data.put("credentialValidity", credentialValidity); + data.put("logoUrl", issuerDTO.getDisplay().stream().map(d -> d.getLogo().getUrl()).findFirst().orElse("")); + data.put("rowProperties", rowProperties); + data.put("textColor", textColor); + data.put("backgroundColor", backgroundColor); + data.put("backgroundImage", backgroundImage); + data.put("titleName", credentialSupportedType); + data.put("face", face); + return data; + } + + @NotNull + private ByteArrayInputStream renderVCInCredentialTemplate(Map data, String issuerId, String credentialType) { + String credentialTemplate = utilities.getCredentialSupportedTemplateString(issuerId, credentialType); + Properties props = new Properties(); + props.setProperty("resource.loader", "class"); + props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + Velocity.init(props); + VelocityContext velocityContext = new VelocityContext(data); + + // Merge the context with the template + StringWriter writer = new StringWriter(); + Velocity.evaluate(velocityContext, writer, "Credential Template", credentialTemplate); + + // Get the merged HTML string + String mergedHtml = writer.toString(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + PdfWriter pdfwriter = new PdfWriter(outputStream); + DefaultFontProvider defaultFont = new DefaultFontProvider(true, false, false); + ConverterProperties converterProperties = new ConverterProperties(); + converterProperties.setFontProvider(defaultFont); + HtmlConverter.convertToPdf(mergedHtml, pdfwriter, converterProperties); + return new ByteArrayInputStream(outputStream.toByteArray()); + } + + private String handleList(Object list, String locale) { + List castedList = (List) list; + String response = ""; + if (castedList.isEmpty()) return ""; + if (castedList.getFirst() instanceof String) { + response = castedList.stream().map(String.class::cast).collect(Collectors.joining(", ")); + } else if (castedList.getFirst() instanceof Map) { + response = ((List>) castedList).stream() + .filter(obj -> LocaleUtils.matchesLocale(obj.get("language").toString(), locale)) + .map(obj -> obj.get("value").toString()) + .findFirst() + .orElse(""); + } + return response; + } + + private String handleMap(Object map) { + if (map instanceof Map) { + return Optional.ofNullable(((Map) map).get("value")) + .map(Object::toString) + .orElse(""); + } + return ""; + } + + private String constructQRCodeWithVCData(VCCredentialResponse vcCredentialResponse) throws JsonProcessingException, WriterException { + String qrData = pixelPass.generateQRData(objectMapper.writeValueAsString(vcCredentialResponse.getCredential()), ""); + if (allowedQRDataSizeLimit > qrData.length()) { + return constructQRCode(qrData); + } + return ""; + } + + private String constructQRCodeWithAuthorizeRequest(VCCredentialResponse vcCredentialResponse, String dataShareUrl) throws WriterException, JsonProcessingException { + PresentationDefinitionDTO presentationDefinitionDTO = presentationService.constructPresentationDefinition(vcCredentialResponse); + String presentationString = objectMapper.writeValueAsString(presentationDefinitionDTO); + String qrData = String.format(ovpQRDataPattern, URLEncoder.encode(dataShareUrl, StandardCharsets.UTF_8), URLEncoder.encode(presentationString, StandardCharsets.UTF_8)); + return constructQRCode(qrData); + } + + private String constructQRCode(String qrData) throws WriterException { + QRCodeWriter qrCodeWriter = new QRCodeWriter(); + BitMatrix bitMatrix = qrCodeWriter.encode(qrData, BarcodeFormat.QR_CODE, qrCodeWidth, qrCodeHeight); + BufferedImage qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix); + return Utilities.encodeToString(qrImage, "png"); + } + + + +} + + diff --git a/src/main/java/io/mosip/mimoto/service/CredentialRequestService.java b/src/main/java/io/mosip/mimoto/service/CredentialRequestService.java new file mode 100644 index 000000000..32f53f611 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/CredentialRequestService.java @@ -0,0 +1,18 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.mimoto.CredentialIssuerWellKnownResponse; +import io.mosip.mimoto.dto.mimoto.CredentialsSupportedResponse; +import io.mosip.mimoto.dto.mimoto.VCCredentialRequest; + +public interface CredentialRequestService { + VCCredentialRequest buildRequest( + IssuerDTO issuerDTO, + CredentialIssuerWellKnownResponse wellKnownResponse, + CredentialsSupportedResponse credentialsSupportedResponse, + String cNonce, + String walletId, + String base64EncodedWalletKey, + Boolean isLoginFlow + ) throws Exception; +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/service/CredentialService.java b/src/main/java/io/mosip/mimoto/service/CredentialService.java index 5d24cc5c8..3c956e0b4 100644 --- a/src/main/java/io/mosip/mimoto/service/CredentialService.java +++ b/src/main/java/io/mosip/mimoto/service/CredentialService.java @@ -1,15 +1,13 @@ package io.mosip.mimoto.service; import io.mosip.mimoto.dto.idp.TokenResponseDTO; -import io.mosip.mimoto.exception.ApiNotAccessibleException; -import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; -import io.mosip.mimoto.exception.InvalidWellknownResponseException; +import io.mosip.mimoto.dto.mimoto.VCCredentialRequest; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.exception.InvalidCredentialResourceException; import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.util.Map; public interface CredentialService { - TokenResponseDTO getTokenResponse(Map params, String issuerId) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException; - ByteArrayInputStream downloadCredentialAsPDF(String issuerId, String credentialType, TokenResponseDTO response, String credentialValidity, String locale) throws Exception; + VCCredentialResponse downloadCredential(String credentialEndpoint, VCCredentialRequest vcCredentialRequest, String accessToken) throws InvalidCredentialResourceException; } + diff --git a/src/main/java/io/mosip/mimoto/service/CredentialVerifierService.java b/src/main/java/io/mosip/mimoto/service/CredentialVerifierService.java new file mode 100644 index 000000000..62381800b --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/CredentialVerifierService.java @@ -0,0 +1,9 @@ +package io.mosip.mimoto.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.exception.VCVerificationException; + +public interface CredentialVerifierService { + boolean verify(VCCredentialResponse credentialResponse) throws JsonProcessingException, VCVerificationException; +} diff --git a/src/main/java/io/mosip/mimoto/service/EncryptionService.java b/src/main/java/io/mosip/mimoto/service/EncryptionService.java new file mode 100644 index 000000000..bece16f57 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/EncryptionService.java @@ -0,0 +1,9 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.EncryptionException; +import io.mosip.mimoto.exception.DecryptionException; + +public interface EncryptionService { + String encrypt(String data) throws EncryptionException; + String decrypt(String data) throws DecryptionException; +} diff --git a/src/main/java/io/mosip/mimoto/service/IdpService.java b/src/main/java/io/mosip/mimoto/service/IdpService.java index 1ba04dc25..152512850 100644 --- a/src/main/java/io/mosip/mimoto/service/IdpService.java +++ b/src/main/java/io/mosip/mimoto/service/IdpService.java @@ -1,7 +1,12 @@ package io.mosip.mimoto.service; import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.VerifiableCredentialRequestDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; import io.mosip.mimoto.dto.mimoto.CredentialIssuerConfiguration; +import io.mosip.mimoto.exception.ApiNotAccessibleException; +import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; +import io.mosip.mimoto.exception.InvalidWellknownResponseException; import io.mosip.mimoto.exception.IssuerOnboardingException; import org.springframework.http.HttpEntity; import org.springframework.util.MultiValueMap; @@ -13,4 +18,8 @@ public interface IdpService { HttpEntity> constructGetTokenRequest(Map params, IssuerDTO issuerDTO, String authorizationAudience) throws IOException, IssuerOnboardingException; String getTokenEndpoint(CredentialIssuerConfiguration credentialIssuerConfiguration); + + TokenResponseDTO getTokenResponse(Map params) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException; + + TokenResponseDTO getTokenResponse(VerifiableCredentialRequestDTO verifiableCredentialRequest) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException; } diff --git a/src/main/java/io/mosip/mimoto/service/IssuersService.java b/src/main/java/io/mosip/mimoto/service/IssuersService.java index 467feeee6..647db4e49 100644 --- a/src/main/java/io/mosip/mimoto/service/IssuersService.java +++ b/src/main/java/io/mosip/mimoto/service/IssuersService.java @@ -7,15 +7,18 @@ import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; import io.mosip.mimoto.exception.InvalidIssuerIdException; import io.mosip.mimoto.exception.InvalidWellknownResponseException; +import jakarta.validation.constraints.NotBlank; import java.io.IOException; public interface IssuersService { - IssuersDTO getIssuers(String search) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException; + IssuersDTO getIssuers(String search) throws ApiNotAccessibleException, IOException; IssuerDTO getIssuerDetails(String issuerId) throws ApiNotAccessibleException, IOException, InvalidIssuerIdException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException; IssuersDTO getAllIssuers() throws ApiNotAccessibleException, AuthorizationServerWellknownResponseException, IOException, InvalidWellknownResponseException; CredentialIssuerConfiguration getIssuerConfiguration(String issuerId) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException; + + IssuerConfig getIssuerConfig(String issuerId, @NotBlank String credentialType) throws ApiNotAccessibleException, InvalidIssuerIdException; } diff --git a/src/main/java/io/mosip/mimoto/service/LogoutService.java b/src/main/java/io/mosip/mimoto/service/LogoutService.java new file mode 100644 index 000000000..88ac2092b --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/LogoutService.java @@ -0,0 +1,46 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.session.SessionRepository; +import org.springframework.stereotype.Service; + +import java.util.Base64; + +@Service +@Slf4j +public class LogoutService { + + public void handleLogout(HttpServletRequest request, HttpServletResponse response, SessionRepository sessionRepository) throws OAuth2AuthenticationException { + + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if ("SESSION".equals(cookie.getName())) { + String encodedSessionId = cookie.getValue(); + String sessionId = new String(Base64.getUrlDecoder().decode(encodedSessionId)); + if (sessionRepository.findById(sessionId) != null) { + sessionRepository.deleteById(sessionId); + } else { + throw new OAuth2AuthenticationException("NOT_FOUND", + "Logout request was sent for an invalid or expired session", + HttpStatus.NOT_FOUND); + } + } + } + } + + HttpSession session = request.getSession(false); + if (session != null) { + session.invalidate(); + } + + } + + +} diff --git a/src/main/java/io/mosip/mimoto/service/TokenDecoder.java b/src/main/java/io/mosip/mimoto/service/TokenDecoder.java new file mode 100644 index 000000000..f932a85d5 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/TokenDecoder.java @@ -0,0 +1,8 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import org.springframework.security.oauth2.jwt.Jwt; + +public interface TokenDecoder { + Jwt decode(String idToken) throws OAuth2AuthenticationException; +} diff --git a/src/main/java/io/mosip/mimoto/service/TokenService.java b/src/main/java/io/mosip/mimoto/service/TokenService.java new file mode 100644 index 000000000..e039f99eb --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/TokenService.java @@ -0,0 +1,9 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public interface TokenService { + void processToken(String idToken, String provider, HttpServletRequest request, HttpServletResponse response) throws OAuth2AuthenticationException; +} diff --git a/src/main/java/io/mosip/mimoto/service/TokenServiceFactory.java b/src/main/java/io/mosip/mimoto/service/TokenServiceFactory.java new file mode 100644 index 000000000..66e0db522 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/TokenServiceFactory.java @@ -0,0 +1,28 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class TokenServiceFactory { + private static final String UNSUPPORTED_PROVIDER_MESSAGE = "Unsupported provider: %s"; + private final Map tokenServices; + + @Autowired + public TokenServiceFactory(Map tokenServices) { + this.tokenServices = tokenServices; + } + + public TokenService getTokenService(String provider) throws OAuth2AuthenticationException { + TokenService service = tokenServices.get(provider.toLowerCase()); + if (service == null) { + throw new OAuth2AuthenticationException(ErrorConstants.INVALID_REQUEST.getErrorCode(), String.format(UNSUPPORTED_PROVIDER_MESSAGE, provider), HttpStatus.BAD_REQUEST); + } + return service; + } +} diff --git a/src/main/java/io/mosip/mimoto/service/UserMetadataService.java b/src/main/java/io/mosip/mimoto/service/UserMetadataService.java new file mode 100644 index 000000000..391300892 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/UserMetadataService.java @@ -0,0 +1,94 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.EncryptionException; +import io.mosip.mimoto.model.UserMetadata; +import io.mosip.mimoto.repository.UserMetadataRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.function.Supplier; + +@Service +public class UserMetadataService { + private final UserMetadataRepository repository; + private final EncryptionService encryptionService; + + @Autowired + public UserMetadataService(UserMetadataRepository repository, EncryptionService encryptionService) { + this.repository = repository; + this.encryptionService = encryptionService; + } + + public UserMetadata getUserMetadata(String providerSubjectId, String identityProvider) + throws DecryptionException { + return repository.findByProviderSubjectIdAndIdentityProvider(providerSubjectId, identityProvider) + .map(user -> { + try { + UserMetadata decryptedUser = new UserMetadata(); + decryptedUser.setId(user.getId()); + decryptedUser.setProviderSubjectId(user.getProviderSubjectId()); + decryptedUser.setIdentityProvider(user.getIdentityProvider()); + + decryptedUser.setDisplayName(encryptionService.decrypt(user.getDisplayName())); + decryptedUser.setProfilePictureUrl(encryptionService.decrypt(user.getProfilePictureUrl())); + decryptedUser.setEmail(encryptionService.decrypt(user.getEmail())); + + return decryptedUser; + } catch (DecryptionException e) { + throw new RuntimeException("Failed to decrypt user metadata", e); + } + }) + .orElse(null); + } + + public String updateOrCreateUserMetadata(String providerSubjectId, String identityProvider, + String displayName, String profilePictureUrl, String email) + throws EncryptionException, DecryptionException { + Instant now = Instant.now(); + UserMetadata existingUser = getUserMetadata(providerSubjectId, identityProvider); + if (null != existingUser) { + return updateUser(existingUser, displayName, profilePictureUrl, email, now); + } else { + return createUser(providerSubjectId, identityProvider, displayName, profilePictureUrl, email, now); + } + } + + private String updateUser(UserMetadata user, String displayName, String profilePictureUrl, String email, Instant now) + throws EncryptionException { + boolean updated = updateIfChanged(user::getDisplayName, user::setDisplayName, displayName); + updated = updateIfChanged(user::getProfilePictureUrl, user::setProfilePictureUrl, profilePictureUrl) || updated; + updated = updateIfChanged(user::getEmail, user::setEmail, email) || updated; + + if (updated) { + user.setUpdatedAt(now); + repository.save(user); + } + return user.getId(); + } + + private boolean updateIfChanged(Supplier getter, Consumer setter, String newValue) + throws EncryptionException { + String current = getter.get(); + setter.accept(encryptionService.encrypt(newValue)); + return !current.equals(newValue); + } + + private String createUser(String providerSubjectId, String identityProvider, String displayName, + String profilePictureUrl, String email, Instant now) + throws EncryptionException { + UserMetadata user = new UserMetadata(); + user.setId(UUID.randomUUID().toString()); + user.setProviderSubjectId(providerSubjectId); + user.setIdentityProvider(identityProvider); + user.setDisplayName(encryptionService.encrypt(displayName)); + user.setProfilePictureUrl(encryptionService.encrypt(profilePictureUrl)); + user.setEmail(encryptionService.encrypt(email)); + user.setCreatedAt(now); + user.setUpdatedAt(now); + return repository.save(user).getId(); + } +} diff --git a/src/main/java/io/mosip/mimoto/service/WalletCredentialService.java b/src/main/java/io/mosip/mimoto/service/WalletCredentialService.java new file mode 100644 index 000000000..c12ab7505 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/WalletCredentialService.java @@ -0,0 +1,59 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.VerifiableCredentialResponseDTO; +import io.mosip.mimoto.dto.resident.WalletCredentialResponseDTO; +import io.mosip.mimoto.exception.*; + +import java.util.List; + +/** + * Service interface for managing wallet credentials. + */ +public interface WalletCredentialService { + + /** + * Fetches and stores a credential for a wallet. + * + * @param issuerId The issuer ID. + * @param credentialConfigurationId The type of credential. + * @param tokenResponse The token response containing the access token. + * @param locale The locale for display purposes. + * @param walletId The wallet ID. + * @param base64Key The base64-encoded key for encryption. + * @return The stored credential response. + * @throws CredentialProcessingException If processing fails. + */ + VerifiableCredentialResponseDTO downloadVCAndStoreInDB(String issuerId, String credentialConfigurationId, + TokenResponseDTO tokenResponse, + String locale, String walletId, String base64Key) + throws CredentialProcessingException, ExternalServiceUnavailableException; + + /** + * Fetches all credentials for a wallet. + * + * @param walletId The wallet ID. + * @param base64Key The base64-encoded key for decryption. + * @param locale The locale for display purposes. + * @return List of credential responses. + */ + List fetchAllCredentialsForWallet(String walletId, String base64Key, String locale); + + /** + * Fetches a single verifiable credential by ID. + * + * @param walletId The wallet ID. + * @param credentialId The credential ID. + * @param base64Key The base64-encoded key for decryption. + * @param locale The locale for display purposes. + * @return The credential response with PDF stream. + * @throws CredentialNotFoundException If the credential is not found. + * @throws CredentialProcessingException If processing fails. + */ + WalletCredentialResponseDTO fetchVerifiableCredential(String walletId, String credentialId, String base64Key, + String locale) + throws CredentialNotFoundException, CredentialProcessingException; + + void deleteCredential(String credentialId, String walletId) throws CredentialNotFoundException; + +} diff --git a/src/main/java/io/mosip/mimoto/service/WalletService.java b/src/main/java/io/mosip/mimoto/service/WalletService.java new file mode 100644 index 000000000..4efaddece --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/WalletService.java @@ -0,0 +1,47 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.dto.WalletResponseDto; +import io.mosip.mimoto.exception.InvalidRequestException; +import jakarta.servlet.http.HttpSession; + +import java.util.List; + +/** + * Service interface for managing wallets. + */ +public interface WalletService { + + /** + * Creates a new wallet for a user. + * + * @param userId The user ID. + * @param name The wallet name. + * @param pin The wallet PIN. + * @return The created wallet ID. + * @throws InvalidRequestException If the request is invalid. + * Example: Wallet PIN and Confirm Wallet PIN received in request don't match + */ + WalletResponseDto createWallet(String userId, String name, String pin, String confirmPin) throws InvalidRequestException; + + /** + * Unlocks a wallet for a user. + * + * @param walletId + * @param pin + * @param httpSession + * @return WalletResponseDto containing wallet details. + * @throws InvalidRequestException If the wallet is not found or PIN is invalid. + */ + WalletResponseDto unlockWallet(String walletId, String pin, HttpSession httpSession) throws InvalidRequestException; + + /** + * Retrieves all wallets for a user. + * + * @param userId The user ID. + * @return List of wallet responses. + * @throws InvalidRequestException If the request fails. + */ + List getWallets(String userId); + + void deleteWallet(String userId, String walletId) throws InvalidRequestException; +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/CredentialRequestServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/CredentialRequestServiceImpl.java new file mode 100644 index 000000000..294044975 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/CredentialRequestServiceImpl.java @@ -0,0 +1,99 @@ +package io.mosip.mimoto.service.impl; + +import io.mosip.mimoto.model.ProofSigningKey; +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.mimoto.*; +import io.mosip.mimoto.constant.SigningAlgorithm; +import io.mosip.mimoto.repository.ProofSigningKeyRepository; +import io.mosip.mimoto.service.CredentialRequestService; +import io.mosip.mimoto.util.EncryptionDecryptionUtil; +import io.mosip.mimoto.util.JoseUtil; +import io.mosip.mimoto.util.JwtGeneratorUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.crypto.SecretKey; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +public class CredentialRequestServiceImpl implements CredentialRequestService { + + @Autowired + private JoseUtil joseUtil; + + @Autowired + private ProofSigningKeyRepository proofSigningKeyRepository; + + @Autowired + private EncryptionDecryptionUtil encryptionDecryptionUtil; + + @Override + public VCCredentialRequest buildRequest(IssuerDTO issuerDTO, + CredentialIssuerWellKnownResponse wellKnownResponse, + CredentialsSupportedResponse credentialsSupportedResponse, + String cNonce, + String walletId, + String base64EncodedWalletKey, + Boolean isLoginFlow) throws Exception { + + SigningAlgorithm algorithm = resolveAlgorithm(credentialsSupportedResponse); + + String jwt; + if (isLoginFlow) { + jwt = generateJwtFromDB(walletId, base64EncodedWalletKey, algorithm, wellKnownResponse, issuerDTO, cNonce); + } else { + jwt = joseUtil.generateJwt(wellKnownResponse.getCredentialIssuer(), issuerDTO.getClient_id(), cNonce); + } + + List credentialContext = credentialsSupportedResponse.getCredentialDefinition().getContext(); + if (credentialContext == null || credentialContext.isEmpty()) { + credentialContext = List.of("https://www.w3.org/2018/credentials/v1"); + } + + return VCCredentialRequest.builder() + .format(credentialsSupportedResponse.getFormat()) + .proof(VCCredentialRequestProof.builder() + .proofType(credentialsSupportedResponse.getProofTypesSupported().keySet().stream().findFirst().get()) + .jwt(jwt) + .build()) + .credentialDefinition(VCCredentialDefinition.builder() + .type(credentialsSupportedResponse.getCredentialDefinition().getType()) + .context(credentialContext) + .build()) + .build(); + } + + private SigningAlgorithm resolveAlgorithm(CredentialsSupportedResponse credentialsSupportedResponse) { + Map proofTypesSupported = credentialsSupportedResponse.getProofTypesSupported(); + if (proofTypesSupported.containsKey("jwt")) { + return SigningAlgorithm.fromString(proofTypesSupported.get("jwt").getProofSigningAlgValuesSupported().getFirst()); + } + return SigningAlgorithm.RS256; + } + + private String generateJwtFromDB(String walletId, + String base64EncodedWalletKey, + SigningAlgorithm algorithm, + CredentialIssuerWellKnownResponse wellKnownResponse, + IssuerDTO issuerDTO, + String cNonce) throws Exception { + + Optional proofSigningKey = proofSigningKeyRepository.findByWalletIdAndAlgorithm(walletId, algorithm.name()); + byte[] decodedWalletKey = Base64.getDecoder().decode(base64EncodedWalletKey); + SecretKey walletKey = EncryptionDecryptionUtil.bytesToSecretKey(decodedWalletKey); + byte[] publicKeyBytes = Base64.getDecoder().decode(proofSigningKey.get().getPublicKey()); + byte[] privateKeyInBytes = encryptionDecryptionUtil.decryptWithAES(walletKey, proofSigningKey.get().getEncryptedSecretKey()); + + return JwtGeneratorUtil.generateJwtUsingDBKeys(algorithm, + wellKnownResponse.getCredentialIssuer(), + issuerDTO.getClient_id(), + cNonce, + publicKeyBytes, + privateKeyInBytes); + } + + +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/CredentialServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/CredentialServiceImpl.java index 8483eba14..52aa71aa7 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/CredentialServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/CredentialServiceImpl.java @@ -1,71 +1,31 @@ package io.mosip.mimoto.service.impl; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.zxing.BarcodeFormat; -import com.google.zxing.WriterException; -import com.google.zxing.client.j2se.MatrixToImageWriter; -import com.google.zxing.common.BitMatrix; -import com.google.zxing.qrcode.QRCodeWriter; -import com.itextpdf.html2pdf.ConverterProperties; -import com.itextpdf.html2pdf.HtmlConverter; -import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; -import com.itextpdf.kernel.pdf.PdfWriter; import io.mosip.mimoto.dto.IssuerDTO; import io.mosip.mimoto.dto.idp.TokenResponseDTO; import io.mosip.mimoto.dto.mimoto.*; -import io.mosip.mimoto.dto.openid.presentation.PresentationDefinitionDTO; -import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.exception.InvalidCredentialResourceException; +import io.mosip.mimoto.exception.VCVerificationException; import io.mosip.mimoto.model.QRCodeType; +import io.mosip.mimoto.service.CredentialRequestService; import io.mosip.mimoto.service.CredentialService; -import io.mosip.mimoto.service.IdpService; +import io.mosip.mimoto.service.CredentialVerifierService; import io.mosip.mimoto.service.IssuersService; -import io.mosip.mimoto.util.JoseUtil; -import io.mosip.mimoto.util.LocaleUtils; +import io.mosip.mimoto.service.CredentialPDFGeneratorService; import io.mosip.mimoto.util.RestApiClient; -import io.mosip.mimoto.util.Utilities; -import io.mosip.pixelpass.PixelPass; -import io.mosip.vercred.vcverifier.CredentialsVerifier; -import io.mosip.vercred.vcverifier.constants.CredentialFormat; -import io.mosip.vercred.vcverifier.data.VerificationResult; -import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; -import org.apache.velocity.VelocityContext; -import org.apache.velocity.app.Velocity; -import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; -import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.StringWriter; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.stream.Collectors; -import static io.mosip.mimoto.exception.ErrorConstants.*; +import static io.mosip.mimoto.exception.ErrorConstants.SIGNATURE_VERIFICATION_EXCEPTION; @Slf4j @Service public class CredentialServiceImpl implements CredentialService { - @Autowired - private Utilities utilities; - - @Autowired - private JoseUtil joseUtil; - - @Autowired - private RestApiClient restApiClient; - @Autowired IssuersService issuersService; @@ -76,47 +36,17 @@ public class CredentialServiceImpl implements CredentialService { ObjectMapper objectMapper; @Autowired - IdpService idpService; + RestApiClient restApiClient; @Autowired - RestTemplate restTemplate; - - @Value("${mosip.inji.ovp.qrdata.pattern}") - String ovpQRDataPattern; - - @Value("${mosip.inji.qr.code.height:500}") - Integer qrCodeHeight; - - @Value("${mosip.inji.qr.code.width:500}") - Integer qrCodeWidth; - - @Value("${mosip.inji.qr.data.size.limit:2000}") - Integer allowedQRDataSizeLimit; + private CredentialPDFGeneratorService credentialPDFGeneratorService; @Autowired - PresentationServiceImpl presentationService; - - PixelPass pixelPass; - CredentialsVerifier credentialsVerifier; + private CredentialVerifierService credentialVerifierService; - @PostConstruct - public void init() { - pixelPass = new PixelPass(); - credentialsVerifier = new CredentialsVerifier(); - } + @Autowired + private CredentialRequestService credentialRequestService; - @Override - public TokenResponseDTO getTokenResponse(Map params, String issuerId) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { - IssuerDTO issuerDTO = issuersService.getIssuerDetails(issuerId); - CredentialIssuerConfiguration credentialIssuerConfiguration = issuersService.getIssuerConfiguration(issuerId); - String tokenEndpoint = idpService.getTokenEndpoint(credentialIssuerConfiguration); - HttpEntity> request = idpService.constructGetTokenRequest(params, issuerDTO, tokenEndpoint); - TokenResponseDTO response = restTemplate.postForObject(tokenEndpoint, request, TokenResponseDTO.class); - if (response == null) { - throw new IdpException("Exception occurred while performing the authorization"); - } - return response; - } @Override public ByteArrayInputStream downloadCredentialAsPDF(String issuerId, String credentialType, TokenResponseDTO response, String credentialValidity, String locale) throws Exception { @@ -128,213 +58,27 @@ public ByteArrayInputStream downloadCredentialAsPDF(String issuerId, String cred credentialIssuerConfiguration.getCredentialEndPoint(), credentialIssuerConfiguration.getCredentialConfigurationsSupported()); CredentialsSupportedResponse credentialsSupportedResponse = credentialIssuerWellKnownResponse.getCredentialConfigurationsSupported().get(credentialType); - VCCredentialRequest vcCredentialRequest = generateVCCredentialRequest(issuerDTO, credentialIssuerWellKnownResponse, credentialsSupportedResponse, response.getAccess_token()); + VCCredentialRequest vcCredentialRequest = credentialRequestService.buildRequest(issuerDTO, credentialIssuerWellKnownResponse, credentialsSupportedResponse, response.getC_nonce(), null, null, false + ); VCCredentialResponse vcCredentialResponse = downloadCredential(credentialIssuerWellKnownResponse.getCredentialEndPoint(), vcCredentialRequest, response.getAccess_token()); - boolean verificationStatus = issuerId.toLowerCase().contains("mock") || verifyCredential(vcCredentialResponse); + boolean verificationStatus = issuerId.toLowerCase().contains("mock") || credentialVerifierService.verify(vcCredentialResponse); if (verificationStatus) { String dataShareUrl = QRCodeType.OnlineSharing.equals(issuerDTO.getQr_code_type()) ? dataShareService.storeDataInDataShare(objectMapper.writeValueAsString(vcCredentialResponse), credentialValidity) : ""; - return generatePdfForVerifiableCredentials(credentialType, vcCredentialResponse, issuerDTO, credentialsSupportedResponse, dataShareUrl, credentialValidity, locale); + return credentialPDFGeneratorService.generatePdfForVerifiableCredentials(credentialType, vcCredentialResponse, issuerDTO, credentialsSupportedResponse, dataShareUrl, credentialValidity, locale); } throw new VCVerificationException(SIGNATURE_VERIFICATION_EXCEPTION.getErrorCode(), SIGNATURE_VERIFICATION_EXCEPTION.getErrorMessage()); } + @Override public VCCredentialResponse downloadCredential(String credentialEndpoint, VCCredentialRequest vcCredentialRequest, String accessToken) throws InvalidCredentialResourceException { VCCredentialResponse vcCredentialResponse = restApiClient.postApi(credentialEndpoint, MediaType.APPLICATION_JSON, vcCredentialRequest, VCCredentialResponse.class, accessToken); log.debug("VC Credential Response is -> " + vcCredentialResponse); - if (vcCredentialResponse == null) throw new RuntimeException("VC Credential Issue API not accessible"); + if (vcCredentialResponse == null) + throw new InvalidCredentialResourceException("VC Credential Issue API not accessible"); return vcCredentialResponse; } - public VCCredentialRequest generateVCCredentialRequest(IssuerDTO issuerDTO, CredentialIssuerWellKnownResponse credentialIssuerWellKnownResponse, CredentialsSupportedResponse credentialsSupportedResponse, String accessToken) throws Exception { - String jwt = joseUtil.generateJwt(credentialIssuerWellKnownResponse.getCredentialIssuer(), issuerDTO.getClient_id(), accessToken); - return VCCredentialRequest.builder() - .format(credentialsSupportedResponse.getFormat()) - .proof(VCCredentialRequestProof.builder() - .proofType(credentialsSupportedResponse.getProofTypesSupported().keySet().stream().findFirst().get()) - .jwt(jwt) - .build()) - .credentialDefinition(VCCredentialDefinition.builder() - .type(credentialsSupportedResponse.getCredentialDefinition().getType()) - .context(List.of("https://www.w3.org/2018/credentials/v1")) - .build()) - .build(); - } - - public ByteArrayInputStream generatePdfForVerifiableCredentials(String credentialType, VCCredentialResponse vcCredentialResponse, IssuerDTO issuerDTO, CredentialsSupportedResponse credentialsSupportedResponse, String dataShareUrl, String credentialValidity, String locale) throws Exception { - LinkedHashMap> displayProperties = loadDisplayPropertiesFromWellknown(vcCredentialResponse, credentialsSupportedResponse, locale); - Map data = getPdfResourceFromVcProperties(displayProperties, credentialsSupportedResponse, vcCredentialResponse, issuerDTO, dataShareUrl, credentialValidity); - return renderVCInCredentialTemplate(data, issuerDTO.getIssuer_id(), credentialType); - } - - public Boolean verifyCredential(VCCredentialResponse vcCredentialResponse) throws VCVerificationException, JsonProcessingException { - log.info("Initiated the VC Verification : Started"); - String credentialString = objectMapper.writeValueAsString(vcCredentialResponse.getCredential()); - VerificationResult verificationResult = credentialsVerifier.verify(credentialString, CredentialFormat.LDP_VC); - if (!verificationResult.getVerificationStatus()) { - throw new VCVerificationException(verificationResult.getVerificationErrorCode().toLowerCase(), verificationResult.getVerificationMessage()); - } - log.info("Completed the VC Verification : Completed -> result : " + verificationResult); - return true; - } - - @NotNull - private static LinkedHashMap> loadDisplayPropertiesFromWellknown(VCCredentialResponse vcCredentialResponse, CredentialsSupportedResponse credentialsSupportedResponse, String userLocale) { - LinkedHashMap> displayProperties = new LinkedHashMap<>(); - Map credentialProperties = vcCredentialResponse.getCredential().getCredentialSubject(); - LinkedHashMap vcPropertiesFromWellKnown = new LinkedHashMap<>(); - Map credentialSubject = credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject(); - String locale = LocaleUtils.resolveLocaleWithFallback(credentialSubject, userLocale); - if (locale != null) { - credentialSubject.keySet().forEach(VCProperty -> { - Optional filteredResponse = credentialSubject.get(VCProperty) - .getDisplay().stream() - .filter(obj -> LocaleUtils.matchesLocale(obj.getLocale(), locale)) - .findFirst(); - - if (filteredResponse.isPresent()) { - CredentialIssuerDisplayResponse filteredValue = filteredResponse.get(); - vcPropertiesFromWellKnown.put(VCProperty, filteredValue); - } - }); - } - - List orderProperty = credentialsSupportedResponse.getOrder(); - - List fieldProperties = orderProperty == null ? new ArrayList<>(vcPropertiesFromWellKnown.keySet()) : orderProperty; - fieldProperties.forEach(vcProperty -> { - if (vcPropertiesFromWellKnown.get(vcProperty) != null && credentialProperties.get(vcProperty) != null) { - displayProperties.put(vcProperty, Map.of(vcPropertiesFromWellKnown.get(vcProperty), credentialProperties.get(vcProperty))); - } - }); - return displayProperties; - } - - - private Map getPdfResourceFromVcProperties(LinkedHashMap> displayProperties, CredentialsSupportedResponse credentialsSupportedResponse, VCCredentialResponse vcCredentialResponse, IssuerDTO issuerDTO, String dataShareUrl, String credentialValidity) throws IOException, WriterException { - Map data = new HashMap<>(); - LinkedHashMap rowProperties = new LinkedHashMap<>(); - String backgroundColor = credentialsSupportedResponse.getDisplay().get(0).getBackgroundColor(); - String backgroundImage = credentialsSupportedResponse.getDisplay().get(0).getBackgroundImage().getUri(); - String textColor = credentialsSupportedResponse.getDisplay().get(0).getTextColor(); - String credentialSupportedType = credentialsSupportedResponse.getDisplay().get(0).getName(); - String face = vcCredentialResponse.getCredential().getCredentialSubject().get("face") != null ? (String) vcCredentialResponse.getCredential().getCredentialSubject().get("face") : null; - - displayProperties.entrySet().stream() - .forEachOrdered(entry -> { - String originalKey = entry.getKey(); - Map properties = entry.getValue(); - - // Process the inner map - ((Map) properties).entrySet().stream() - .forEachOrdered(innerEntry -> { - // loadDisplayPropertiesFromWellknown method returns both name and locale field values of the matching display obj in the response - CredentialIssuerDisplayResponse matchingWellknownDisplayObj = innerEntry.getKey(); - String nameFromDisplayObj = matchingWellknownDisplayObj.getName(); - String localeFromDisplayObj = matchingWellknownDisplayObj.getLocale(); - Object propertyValFromDownloadedVcResponse = innerEntry.getValue(); - String value = ""; - - if (propertyValFromDownloadedVcResponse instanceof Map) { - // If the value is a Map, handle it as a Map - value = handleMap(propertyValFromDownloadedVcResponse); - } else if (propertyValFromDownloadedVcResponse instanceof List) { - // If the value is a List, handle it as a List - value = handleList(propertyValFromDownloadedVcResponse, localeFromDisplayObj); - } else { - // Otherwise, just convert to string - value = propertyValFromDownloadedVcResponse.toString(); - } - - // Put the result into the rowProperties map - rowProperties.put(originalKey, Map.of(nameFromDisplayObj, value)); - }); - - }); - String qrCodeImage = QRCodeType.OnlineSharing.equals(issuerDTO.getQr_code_type()) ? constructQRCodeWithAuthorizeRequest(vcCredentialResponse, dataShareUrl) : - QRCodeType.EmbeddedVC.equals(issuerDTO.getQr_code_type()) ? constructQRCodeWithVCData(vcCredentialResponse) : ""; - data.put("qrCodeImage", qrCodeImage); - data.put("credentialValidity", credentialValidity); - data.put("logoUrl", issuerDTO.getDisplay().stream().map(d -> d.getLogo().getUrl()).findFirst().orElse("")); - data.put("rowProperties", rowProperties); - data.put("textColor", textColor); - data.put("backgroundColor", backgroundColor); - data.put("backgroundImage", backgroundImage); - data.put("titleName", credentialSupportedType); - data.put("face", face); - return data; - } - - private String handleList(Object list, String locale) { - List castedList = (List) list; - String response = ""; - if (castedList.isEmpty()) return ""; - if (castedList.get(0) instanceof String) { - response = castedList.stream().map(String.class::cast).collect(Collectors.joining(", ")); - } else if (castedList.get(0) instanceof Map) { - response = ((List>) castedList).stream() - .filter(obj -> LocaleUtils.matchesLocale(obj.get("language").toString(), locale)) - .map(obj -> obj.get("value").toString()) - .findFirst() - .orElse(""); - } - return response; - } - - private String handleMap(Object map) { - if (map instanceof Map) { - return Optional.ofNullable(((Map) map).get("value")) - .map(Object::toString) - .orElse(""); - } - return ""; - } - - @NotNull - private ByteArrayInputStream renderVCInCredentialTemplate(Map data, String issuerId, String credentialType) { - String credentialTemplate = utilities.getCredentialSupportedTemplateString(issuerId, credentialType); - Properties props = new Properties(); - props.setProperty("resource.loader", "class"); - props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); - Velocity.init(props); - VelocityContext velocityContext = new VelocityContext(data); - - // Merge the context with the template - StringWriter writer = new StringWriter(); - Velocity.evaluate(velocityContext, writer, "Credential Template", credentialTemplate); - - // Get the merged HTML string - String mergedHtml = writer.toString(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - PdfWriter pdfwriter = new PdfWriter(outputStream); - DefaultFontProvider defaultFont = new DefaultFontProvider(true, false, false); - ConverterProperties converterProperties = new ConverterProperties(); - converterProperties.setFontProvider(defaultFont); - HtmlConverter.convertToPdf(mergedHtml, pdfwriter, converterProperties); - return new ByteArrayInputStream(outputStream.toByteArray()); - } - - private String constructQRCode(String qrData) throws WriterException { - QRCodeWriter qrCodeWriter = new QRCodeWriter(); - BitMatrix bitMatrix = qrCodeWriter.encode(qrData, BarcodeFormat.QR_CODE, qrCodeWidth, qrCodeHeight); - BufferedImage qrImage = MatrixToImageWriter.toBufferedImage(bitMatrix); - return Utilities.encodeToString(qrImage, "png"); - } - private String constructQRCodeWithVCData(VCCredentialResponse vcCredentialResponse) throws JsonProcessingException, WriterException { - String qrData = pixelPass.generateQRData(objectMapper.writeValueAsString(vcCredentialResponse.getCredential()), ""); - if (allowedQRDataSizeLimit > qrData.length()) { - return constructQRCode(qrData); - } - return ""; - } - - private String constructQRCodeWithAuthorizeRequest(VCCredentialResponse vcCredentialResponse, String dataShareUrl) throws WriterException, JsonProcessingException { - PresentationDefinitionDTO presentationDefinitionDTO = presentationService.constructPresentationDefinition(vcCredentialResponse); - String presentationString = objectMapper.writeValueAsString(presentationDefinitionDTO); - String qrData = String.format(ovpQRDataPattern, URLEncoder.encode(dataShareUrl, StandardCharsets.UTF_8), URLEncoder.encode(presentationString, StandardCharsets.UTF_8)); - return constructQRCode(qrData); - } } diff --git a/src/main/java/io/mosip/mimoto/service/impl/CredentialVerifierServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/CredentialVerifierServiceImpl.java new file mode 100644 index 000000000..3f6677f52 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/CredentialVerifierServiceImpl.java @@ -0,0 +1,31 @@ +package io.mosip.mimoto.service.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.exception.VCVerificationException; +import io.mosip.mimoto.service.CredentialVerifierService; +import io.mosip.vercred.vcverifier.CredentialsVerifier; +import io.mosip.vercred.vcverifier.constants.CredentialFormat; +import io.mosip.vercred.vcverifier.data.VerificationResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CredentialVerifierServiceImpl implements CredentialVerifierService { + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private CredentialsVerifier credentialsVerifier; + + public boolean verify(VCCredentialResponse response) throws JsonProcessingException, VCVerificationException { + String credentialString = objectMapper.writeValueAsString(response.getCredential()); + VerificationResult result = credentialsVerifier.verify(credentialString, CredentialFormat.LDP_VC); + if (!result.getVerificationStatus()) { + throw new VCVerificationException(result.getVerificationErrorCode().toLowerCase(), result.getVerificationMessage()); + } + return true; + } +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/EncryptionServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/EncryptionServiceImpl.java new file mode 100644 index 000000000..e8a79d4dd --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/EncryptionServiceImpl.java @@ -0,0 +1,32 @@ +package io.mosip.mimoto.service.impl; + +import io.mosip.mimoto.exception.EncryptionException; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.service.EncryptionService; +import io.mosip.mimoto.util.EncryptionDecryptionUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class EncryptionServiceImpl implements EncryptionService { + private static final String USER_PII_KEY_REFERENCE_ID = "user_pii"; + private static final String EMPTY_AAD = ""; + private static final String EMPTY_SALT = ""; + + private final EncryptionDecryptionUtil encryptionUtil; + + @Autowired + public EncryptionServiceImpl(EncryptionDecryptionUtil encryptionUtil) { + this.encryptionUtil = encryptionUtil; + } + + @Override + public String encrypt(String data) throws EncryptionException { + return encryptionUtil.encrypt(data, USER_PII_KEY_REFERENCE_ID, EMPTY_AAD, EMPTY_SALT); + } + + @Override + public String decrypt(String data) throws DecryptionException { + return encryptionUtil.decrypt(data, USER_PII_KEY_REFERENCE_ID, EMPTY_AAD, EMPTY_SALT); + } +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/GoogleTokenDecoder.java b/src/main/java/io/mosip/mimoto/service/impl/GoogleTokenDecoder.java new file mode 100644 index 000000000..adc36beb7 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/GoogleTokenDecoder.java @@ -0,0 +1,31 @@ +package io.mosip.mimoto.service.impl; + +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import io.mosip.mimoto.service.TokenDecoder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtException; +import org.springframework.stereotype.Component; + +@Component +@Qualifier("googleJwtDecoder") +public class GoogleTokenDecoder implements TokenDecoder { + private final JwtDecoder jwtDecoder; + + @Autowired + public GoogleTokenDecoder(@Qualifier("googleJwtDecoder") JwtDecoder jwtDecoder) { + this.jwtDecoder = jwtDecoder; + } + + @Override + public Jwt decode(String idToken) throws OAuth2AuthenticationException { + try { + return jwtDecoder.decode(idToken); + } catch (JwtException e) { + throw new InvalidRequestException("invalid_token", e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/service/impl/GoogleTokenService.java b/src/main/java/io/mosip/mimoto/service/impl/GoogleTokenService.java new file mode 100644 index 000000000..b4da18615 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/GoogleTokenService.java @@ -0,0 +1,122 @@ +package io.mosip.mimoto.service.impl; + +import io.mosip.mimoto.model.UserMetadata; +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import io.mosip.mimoto.service.TokenDecoder; +import io.mosip.mimoto.service.TokenService; +import io.mosip.mimoto.service.UserMetadataService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service("google") +@Slf4j +public class GoogleTokenService implements TokenService { + private static final String MISSING_EMAIL_ERROR = "Missing email in ID token"; + private static final String INVALID_ISSUER_ERROR = "Invalid issuer"; + private static final String INVALID_AUDIENCE_ERROR = "Invalid audience"; + + private final TokenDecoder tokenDecoder; + private final UserMetadataService userMetadataService; + private final SecurityContextManager securityContextManager; + private final SessionManager sessionManager; + private final String expectedIssuer; + private final String expectedAudience; + + @Autowired + public GoogleTokenService( + @Qualifier("googleJwtDecoder") TokenDecoder tokenDecoder, + UserMetadataService userMetadataService, + SecurityContextManager securityContextManager, + SessionManager sessionManager, + @Value("${spring.security.oauth2.client.registration.google.client-id}") String clientId, + @Value("${google.issuer:https://accounts.google.com}") String issuer) { + this.tokenDecoder = tokenDecoder; + this.userMetadataService = userMetadataService; + this.securityContextManager = securityContextManager; + this.sessionManager = sessionManager; + this.expectedAudience = clientId; + this.expectedIssuer = issuer; + } + + @Override + public void processToken(String idToken, String provider, HttpServletRequest request, HttpServletResponse response) + throws OAuth2AuthenticationException { + Jwt jwt = validateToken(idToken); + String email = extractClaim(jwt, "email"); + if (email == null) { + log.error(MISSING_EMAIL_ERROR); + throw new OAuth2AuthenticationException("missing_email", MISSING_EMAIL_ERROR, HttpStatus.UNAUTHORIZED); + } + + String name = extractClaim(jwt, "name"); + String picture = extractClaim(jwt, "picture"); + String sub = extractClaim(jwt, "sub"); + + String userId; + try { + UserMetadata userMetadata = userMetadataService.getUserMetadata(sub, provider); + if (userMetadata != null) { + if (name == null || name.isBlank()) { + name = userMetadata.getDisplayName(); + } + if (picture == null || picture.isBlank()) { + picture = userMetadata.getProfilePictureUrl(); + } + } + userId = userMetadataService.updateOrCreateUserMetadata(sub, provider, name, picture, email); + } catch (DecryptionException e) { + log.error("Failed to store the user info in the database", e); + throw new RuntimeException(); + } + sessionManager.setupSession(request, provider, new UserMetadataDTO(name, picture, email,null), userId); + securityContextManager.setupSecurityContext(createOAuth2Token(provider, sub, name, picture, email), request, response); + } + + private Jwt validateToken(String idToken) throws OAuth2AuthenticationException { + Jwt jwt = tokenDecoder.decode(idToken); + if (!expectedIssuer.equals(jwt.getIssuer().toString())) { + log.error("Invalid issuer: {}", jwt.getIssuer()); + throw new OAuth2AuthenticationException("invalid_issuer", INVALID_ISSUER_ERROR, HttpStatus.UNAUTHORIZED); + } + if (!expectedAudience.equals(jwt.getAudience().stream().findFirst().orElse(null))) { + log.error("Invalid audience: {}", jwt.getAudience()); + throw new OAuth2AuthenticationException("invalid_audience", INVALID_AUDIENCE_ERROR, HttpStatus.UNAUTHORIZED); + } + return jwt; + } + + private String extractClaim(Jwt jwt, String claimName) { + return (String) jwt.getClaims().get(claimName); + } + + private OAuth2AuthenticationToken createOAuth2Token(String provider, String sub, String name, String picture, String email) { + Map attributes = new HashMap<>(); + attributes.put("sub", sub); + attributes.put("name", name); + attributes.put("picture", picture); + attributes.put("email", email); + + List authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")); + OAuth2User oAuth2User = new DefaultOAuth2User(authorities, attributes, "sub"); + return new OAuth2AuthenticationToken(oAuth2User, authorities, provider); + } +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/IdpServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/IdpServiceImpl.java index dcddfd081..befd02455 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/IdpServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/IdpServiceImpl.java @@ -1,9 +1,12 @@ package io.mosip.mimoto.service.impl; import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.VerifiableCredentialRequestDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; import io.mosip.mimoto.dto.mimoto.CredentialIssuerConfiguration; -import io.mosip.mimoto.exception.IssuerOnboardingException; +import io.mosip.mimoto.exception.*; import io.mosip.mimoto.service.IdpService; +import io.mosip.mimoto.service.IssuersService; import io.mosip.mimoto.util.JoseUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -13,8 +16,10 @@ import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; import java.io.IOException; +import java.util.HashMap; import java.util.Map; @Service @@ -35,6 +40,12 @@ public class IdpServiceImpl implements IdpService { @Autowired JoseUtil joseUtil; + @Autowired + RestTemplate restTemplate; + + @Autowired + IssuersService issuersService; + @Override public HttpEntity> constructGetTokenRequest(Map params, IssuerDTO issuerDTO, String authorizationAudience) throws IOException, IssuerOnboardingException { HttpHeaders headers = new HttpHeaders(); @@ -57,4 +68,36 @@ public HttpEntity> constructGetTokenRequest(Map params) throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { + String issuerId = params.get("issuer"); + IssuerDTO issuerDTO = issuersService.getIssuerDetails(issuerId); + CredentialIssuerConfiguration credentialIssuerConfiguration = issuersService.getIssuerConfiguration(issuerId); + String tokenEndpoint = getTokenEndpoint(credentialIssuerConfiguration); + HttpEntity> request = constructGetTokenRequest(params, issuerDTO, tokenEndpoint); + TokenResponseDTO response = restTemplate.postForObject(tokenEndpoint, request, TokenResponseDTO.class); + if (response == null) { + throw new IdpException("Exception occurred while performing the authorization"); + } + return response; + } + + + private Map convertVerifiableCredentialRequestToMap(VerifiableCredentialRequestDTO verifiableCredentialRequest) { + Map params = new HashMap<>(); + params.put("code", verifiableCredentialRequest.getCode()); + params.put("redirect_uri", verifiableCredentialRequest.getRedirectUri()); + params.put("grant_type", verifiableCredentialRequest.getGrantType()); + params.put("code_verifier", verifiableCredentialRequest.getCodeVerifier()); + params.put("issuer", verifiableCredentialRequest.getIssuer()); + + return params; + } + } diff --git a/src/main/java/io/mosip/mimoto/service/impl/IssuersServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/IssuersServiceImpl.java index 92df4d408..e6da36d1b 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/IssuersServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/IssuersServiceImpl.java @@ -4,21 +4,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.mosip.mimoto.dto.IssuerDTO; import io.mosip.mimoto.dto.IssuersDTO; -import io.mosip.mimoto.dto.mimoto.AuthorizationServerWellKnownResponse; -import io.mosip.mimoto.dto.mimoto.CredentialIssuerConfiguration; -import io.mosip.mimoto.dto.mimoto.CredentialIssuerWellKnownResponse; -import io.mosip.mimoto.exception.ApiNotAccessibleException; -import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; -import io.mosip.mimoto.exception.InvalidIssuerIdException; -import io.mosip.mimoto.exception.InvalidWellknownResponseException; +import io.mosip.mimoto.dto.mimoto.*; +import io.mosip.mimoto.exception.*; import io.mosip.mimoto.service.IssuersService; import io.mosip.mimoto.util.IssuerConfigUtil; import io.mosip.mimoto.util.Utilities; +import jakarta.validation.constraints.NotBlank; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; import java.io.IOException; import java.util.stream.Collectors; @@ -26,6 +23,7 @@ @Service @Slf4j +@Validated public class IssuersServiceImpl implements IssuersService { @Autowired @@ -39,7 +37,7 @@ public class IssuersServiceImpl implements IssuersService { @Override @Cacheable(value = "issuersConfig", key = "#p0 ?: 'allIssuersConfig'") - public IssuersDTO getIssuers(String search) throws ApiNotAccessibleException, AuthorizationServerWellknownResponseException, IOException, InvalidWellknownResponseException { + public IssuersDTO getIssuers(String search) throws ApiNotAccessibleException, IOException { IssuersDTO issuersDTO = getAllIssuers(); issuersDTO = getAllEnabledIssuers(issuersDTO); issuersDTO = getFilteredIssuers(issuersDTO, search); @@ -101,4 +99,24 @@ public CredentialIssuerConfiguration getIssuerConfiguration(String issuerId) thr authorizationServerWellKnownResponse ); } + + @Override + public IssuerConfig getIssuerConfig(String issuerId, @NotBlank String credentialType) throws ApiNotAccessibleException, InvalidIssuerIdException { + log.info("Fetching issuer config for issuerId: {}", issuerId); + try { + IssuerDTO issuerDTO = getIssuerDetails(issuerId); + CredentialIssuerWellKnownResponse wellKnownResponse = issuersConfigUtil.getIssuerWellknown(issuerDTO.getCredential_issuer_host()); + return new IssuerConfig( + issuerDTO, + wellKnownResponse, + wellKnownResponse.getCredentialConfigurationsSupported().get(credentialType) + ); + } catch (Exception e) { + log.error("Failed to fetch issuer config for issuerId: {}", issuerId, e); + if (e instanceof InvalidIssuerIdException) { + throw (InvalidIssuerIdException) e; + } + throw new ApiNotAccessibleException("Unable to fetch issuer configuration for issuerId: " + issuerId, e); + } + } } \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java index 409d86224..897bcf8bc 100644 --- a/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java +++ b/src/main/java/io/mosip/mimoto/service/impl/PresentationServiceImpl.java @@ -5,14 +5,11 @@ import io.mosip.mimoto.dto.mimoto.VCCredentialProperties; import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; import io.mosip.mimoto.dto.openid.presentation.*; -import io.mosip.mimoto.exception.ApiNotAccessibleException; import io.mosip.mimoto.exception.ErrorConstants; import io.mosip.mimoto.exception.VPNotCreatedException; import io.mosip.mimoto.service.PresentationService; import io.mosip.mimoto.util.RestApiClient; import lombok.extern.slf4j.Slf4j; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -111,7 +108,7 @@ private String constructPresentationSubmission(VerifiablePresentationDTO verifia return objectMapper.writeValueAsString(presentationSubmissionDTO); } - PresentationDefinitionDTO constructPresentationDefinition(VCCredentialResponse vcCredentialResponse){ + public PresentationDefinitionDTO constructPresentationDefinition(VCCredentialResponse vcCredentialResponse){ FilterDTO filterDTO = FilterDTO.builder().type("String").pattern(vcCredentialResponse.getCredential().getType().getLast()).build(); FieldDTO fieldDTO = FieldDTO.builder().path(new String[]{"$.type"}).filter(filterDTO).build(); ConstraintsDTO constraintsDTO = ConstraintsDTO.builder().fields(new FieldDTO[]{fieldDTO}).build(); diff --git a/src/main/java/io/mosip/mimoto/service/impl/SecurityContextManager.java b/src/main/java/io/mosip/mimoto/service/impl/SecurityContextManager.java new file mode 100644 index 000000000..6430f15fe --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/SecurityContextManager.java @@ -0,0 +1,25 @@ +package io.mosip.mimoto.service.impl; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.web.context.HttpSessionSecurityContextRepository; +import org.springframework.security.web.context.SecurityContextRepository; +import org.springframework.stereotype.Service; + +@Service +public class SecurityContextManager { + private final SecurityContextRepository securityContextRepository; + + public SecurityContextManager() { + this.securityContextRepository = new HttpSessionSecurityContextRepository(); + } + + public void setupSecurityContext(OAuth2AuthenticationToken oauth2AuthenticationToken, HttpServletRequest request, HttpServletResponse response) { + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(oauth2AuthenticationToken); + securityContextRepository.saveContext(context, request, response); + } +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/SessionManager.java b/src/main/java/io/mosip/mimoto/service/impl/SessionManager.java new file mode 100644 index 000000000..1ec335200 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/SessionManager.java @@ -0,0 +1,17 @@ +package io.mosip.mimoto.service.impl; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.springframework.stereotype.Component; + +@Component +public class SessionManager { + public void setupSession(HttpServletRequest request, String provider, UserMetadataDTO userMetadata, String userId) { + HttpSession session = request.getSession(true); + session.setAttribute("clientRegistrationId", provider); + session.setAttribute(SessionKeys.USER_METADATA, userMetadata); + session.setAttribute(SessionKeys.USER_ID, userId); + } +} diff --git a/src/main/java/io/mosip/mimoto/service/impl/WalletCredentialServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/WalletCredentialServiceImpl.java new file mode 100644 index 000000000..21cc33915 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/WalletCredentialServiceImpl.java @@ -0,0 +1,217 @@ +package io.mosip.mimoto.service.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.model.CredentialMetadata; +import io.mosip.mimoto.model.QRCodeType; +import io.mosip.mimoto.model.VerifiableCredential; +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.CredentialsSupportedResponse; +import io.mosip.mimoto.dto.mimoto.IssuerConfig; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.dto.mimoto.VerifiableCredentialResponseDTO; +import io.mosip.mimoto.dto.resident.WalletCredentialResponseDTO; +import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.repository.WalletCredentialsRepository; +import io.mosip.mimoto.service.CredentialPDFGeneratorService; +import io.mosip.mimoto.service.IssuersService; +import io.mosip.mimoto.service.WalletCredentialService; +import io.mosip.mimoto.util.CredentialProcessor; +import io.mosip.mimoto.util.EncryptionDecryptionUtil; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.InputStreamResource; +import org.springframework.stereotype.Service; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static io.mosip.mimoto.exception.ErrorConstants.*; + +/** + * Implementation of {@link WalletCredentialService} for managing wallet credentials. + */ +@Slf4j +@Service +public class WalletCredentialServiceImpl implements WalletCredentialService { + + @Value("${mosip.inji.wallet.issuersWithSingleVcLimit:Mosip}") + private String issuersWithSingleVcLimit; + + @Value("${mosip.inji.wallet.vc.validity.count:-1}") + private String credentialValidity; + + private final WalletCredentialsRepository repository; + private final IssuersService issuersService; + private final CredentialProcessor credentialProcessor; + private final ObjectMapper objectMapper; + private final EncryptionDecryptionUtil encryptionDecryptionUtil; + private final CredentialPDFGeneratorService credentialPDFGeneratorService; + private final DataShareServiceImpl dataShareService; + + @Autowired + public WalletCredentialServiceImpl(WalletCredentialsRepository repository, + IssuersService issuersService, + CredentialProcessor credentialProcessor, + ObjectMapper objectMapper, + EncryptionDecryptionUtil encryptionDecryptionUtil,CredentialPDFGeneratorService credentialPDFGeneratorService,DataShareServiceImpl dataShareService) { + this.repository = repository; + this.issuersService = issuersService; + this.credentialProcessor = credentialProcessor; + this.objectMapper = objectMapper; + this.encryptionDecryptionUtil = encryptionDecryptionUtil; + this.credentialPDFGeneratorService = credentialPDFGeneratorService; + this.dataShareService = dataShareService; + } + + @Override + public VerifiableCredentialResponseDTO downloadVCAndStoreInDB(String issuerId, String credentialConfigurationId, + TokenResponseDTO tokenResponse, + String locale, String walletId, String base64Key) + throws CredentialProcessingException, ExternalServiceUnavailableException { + log.info("Fetching and storing credential for wallet: {}, issuer: {}, type: {}", walletId, issuerId, credentialConfigurationId); + + Set issuers = Arrays.stream(issuersWithSingleVcLimit.split(",")) + .map(String::trim) + .collect(Collectors.toSet()); + if (issuers.contains(issuerId) && repository.existsByIssuerIdAndCredentialTypeAndWalletId(issuerId, credentialConfigurationId, walletId)) { + log.warn("Duplicate credential found for issuer: {}, type: {}, wallet: {}", issuerId, credentialConfigurationId, walletId); + throw new InvalidRequestException(CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), "Duplicate credential for issuer and type"); + } + + + VerifiableCredentialResponseDTO credential; + + credential = credentialProcessor.downloadCredentialAndStoreInDB( + tokenResponse, credentialConfigurationId, walletId, base64Key, issuerId, locale); + + log.debug("Credential stored successfully: {}", credential.getCredentialId()); + return credential; + + } + + @Override + public List fetchAllCredentialsForWallet(String walletId, String base64Key, String locale) { + log.info("Fetching all credentials for wallet: {}", walletId); + + List credentials = repository.findByWalletIdOrderByCreatedAtDesc(walletId); + + return credentials.stream().map(credential -> { + String issuerId = credential.getCredentialMetadata().getIssuerId(); + IssuerConfig issuerConfig = null; + try { + issuerConfig = issuersService.getIssuerConfig(issuerId, credential.getCredentialMetadata().getCredentialType()); + } catch (ApiNotAccessibleException e) { + log.error("Failed to fetch issuer details for issuerId: {}", issuerId, e); + } + return VerifiableCredentialResponseDTO.fromIssuerConfig(issuerConfig, locale, credential.getId()); + }).toList(); + + } + + @Override + public WalletCredentialResponseDTO fetchVerifiableCredential(String walletId, String credentialId, + String base64Key, String locale) + throws CredentialNotFoundException, CredentialProcessingException { + log.info("Fetching credential: {} for wallet: {}", credentialId, walletId); + VerifiableCredential credential = repository.findByIdAndWalletId(credentialId, walletId) + .orElseThrow(getCredentialNotFoundExceptionSupplier(walletId, credentialId)); + + try { + String decryptedCredential = encryptionDecryptionUtil.decryptCredential(credential.getCredential(), base64Key); + + WalletCredentialResponseDTO response = generateCredentialResponse(decryptedCredential, credential.getCredentialMetadata(), locale); + log.debug("Credential fetched successfully: {}", credentialId); + return response; + } catch (DecryptionException e) { + log.error("Decryption failed for credential: {}", credentialId, e); + throw new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Decryption failed", e); + } + } + + @Override + public void deleteCredential(String credentialId, String walletId) throws CredentialNotFoundException { + log.info("Deleting credential with ID: {} for wallet: {}", credentialId, walletId); + + repository.findByIdAndWalletId(credentialId, walletId) + .orElseThrow(getCredentialNotFoundExceptionSupplier(walletId, credentialId)); + // Delete the credential + repository.deleteById(credentialId); + log.info("Successfully deleted credential with ID: {}", credentialId); + } + + @NotNull + private static Supplier getCredentialNotFoundExceptionSupplier(String walletId, String credentialId) { + return () -> { + log.warn("Credential not found: {} for wallet: {}", credentialId, walletId); + return new CredentialNotFoundException(RESOURCE_NOT_FOUND.getErrorCode(), RESOURCE_NOT_FOUND.getErrorMessage()); + }; + } + + private WalletCredentialResponseDTO generateCredentialResponse(String decryptedCredential, CredentialMetadata credentialMetadata, String locale) throws CredentialProcessingException { + log.info("Generating credential response for issuerId: {}, credentialType: {}", credentialMetadata.getIssuerId(), credentialMetadata.getCredentialType()); + try { + // Parse decrypted credential + VCCredentialResponse vcCredentialResponse = objectMapper.readValue(decryptedCredential, VCCredentialResponse.class); + + // Fetch issuer details + IssuerDTO issuerDTO = issuersService.getIssuerDetails(credentialMetadata.getIssuerId()); + + // Fetch issuer configuration + IssuerConfig issuerConfig = issuersService.getIssuerConfig(credentialMetadata.getIssuerId(), credentialMetadata.getCredentialType()); + + if (null == issuerConfig) { + log.error("Credentials supported response not found in wellknown for credentialType: {}", credentialMetadata.getCredentialType()); + throw new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Invalid credential type configuration"); + } + + // Find credentials supported response for the credential type + CredentialsSupportedResponse credentialsSupportedResponse = issuerConfig.getCredentialsSupportedResponse(); + if (credentialsSupportedResponse == null || !credentialsSupportedResponse.getCredentialDefinition().getType().containsAll(vcCredentialResponse.getCredential().getType())) { + log.error("Credentials supported response not found for credentialType: {}", credentialMetadata.getCredentialType()); + throw new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Invalid credential type configuration"); + } + + String dataShareUrl = QRCodeType.OnlineSharing.equals(issuerDTO.getQr_code_type()) ? dataShareService.storeDataInDataShare(objectMapper.writeValueAsString(vcCredentialResponse), credentialValidity) : ""; + + + // Generate PDF + // keep the datashare url and credential validity as defaults in downloading VC as PDF as logged-in user + // This is because generatePdfForVerifiableCredentials will be used by both logged-in and non-logged-in users + ByteArrayInputStream pdfStream = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + credentialMetadata.getCredentialType(), + vcCredentialResponse, + issuerDTO, + credentialsSupportedResponse, + dataShareUrl, + credentialValidity, + locale + ); + + // Construct response + String fileName = String.format("%s_credential.pdf", credentialMetadata.getCredentialType()); + return WalletCredentialResponseDTO.builder() + .fileName(fileName) + .fileContentStream(new InputStreamResource(pdfStream)) + .build(); + } catch (JsonProcessingException e) { + log.error("Failed to parse decrypted credential for issuerId: {}, credentialType: {}", credentialMetadata.getIssuerId(), credentialMetadata.getCredentialType(), e); + throw new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Failed to parse decrypted credential"); + } catch (ApiNotAccessibleException | IOException | AuthorizationServerWellknownResponseException | + InvalidWellknownResponseException | InvalidIssuerIdException e) { + log.error("Failed to fetch issuer details or configuration for issuerId: {}", credentialMetadata.getIssuerId(), e); + throw new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Failed to fetch issuer configuration"); + } catch (Exception e) { + log.error("Failed to generate PDF for credentialType: {}", credentialMetadata.getCredentialType(), e); + throw new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Failed to generate credential PDF"); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/service/impl/WalletServiceImpl.java b/src/main/java/io/mosip/mimoto/service/impl/WalletServiceImpl.java new file mode 100644 index 000000000..96883b9c3 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/service/impl/WalletServiceImpl.java @@ -0,0 +1,103 @@ +package io.mosip.mimoto.service.impl; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.model.Wallet; +import io.mosip.mimoto.dto.WalletResponseDto; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.repository.WalletRepository; +import io.mosip.mimoto.service.WalletService; +import io.mosip.mimoto.util.WalletUtil; +import io.mosip.mimoto.util.WalletValidator; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.function.Supplier; + +/** + * Implementation of {@link WalletService} for managing wallets. + */ +@Slf4j +@Service +public class WalletServiceImpl implements WalletService { + + private final WalletRepository repository; + private final WalletUtil walletUtil; + private final WalletValidator validator; + + @Autowired + public WalletServiceImpl(WalletRepository repository, WalletUtil walletUtil, WalletValidator validator) { + this.repository = repository; + this.walletUtil = walletUtil; + this.validator = validator; + } + + @Override + public WalletResponseDto createWallet(String userId, String name, String pin, String confirmPin) throws InvalidRequestException { + log.info("Creating wallet for user: {}, name: {}", userId, name); + + validator.validateUserId(userId); + validator.validateWalletName(name); + validator.validateWalletPin(pin); + + if (!pin.equals(confirmPin)) { + log.warn("Wallet PIN and confirm PIN are not matching: {}", userId); + throw new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Entered PIN and Confirm PIN do not match"); + } + + String walletId = walletUtil.createWallet(userId, name, pin); + log.debug("Wallet created successfully: {}", walletId); + return new WalletResponseDto(walletId, name); + } + + @Override + public WalletResponseDto unlockWallet(String walletId, String pin, HttpSession httpSession) throws InvalidRequestException { + String userId = (String) httpSession.getAttribute(SessionKeys.USER_ID); + log.info("Unlocking wallet for user: {}, wallet: {}", userId, walletId); + + validator.validateUserId(userId); + validator.validateWalletPin(pin); + + return repository.findByUserIdAndId(userId, walletId).map(wallet -> { + String decryptedWalletKey = walletUtil.decryptWalletKey(wallet.getWalletKey(), pin); + httpSession.setAttribute(SessionKeys.WALLET_KEY, decryptedWalletKey); + httpSession.setAttribute(SessionKeys.WALLET_ID, walletId); + return new WalletResponseDto(walletId, wallet.getWalletMetadata().getName()); + }).orElseThrow(getWalletNotFoundExceptionSupplier(userId, walletId)); + } + + @Override + public List getWallets(String userId) { + log.debug("validating user ID provided"); + validator.validateUserId(userId); + + log.info("Retrieving wallets for user: {}", userId); + + return repository.findWalletByUserId(userId).stream().map(wallet -> WalletResponseDto.builder() + .walletId(wallet.getId()) + .walletName(wallet.getWalletMetadata().getName()) + .build()) + .toList(); + } + + @Override + public void deleteWallet(String userId, String walletId) throws InvalidRequestException { + validator.validateUserId(userId); + Wallet existingWallet = repository.findByUserIdAndId(userId, walletId) + .orElseThrow(getWalletNotFoundExceptionSupplier(userId, walletId)); + repository.delete(existingWallet); + log.info("Successfully deleted wallet with ID: {} for user: {}", walletId, userId); + } + + @NotNull + private static Supplier getWalletNotFoundExceptionSupplier(String userId, String walletId) { + return () -> { + log.warn("Wallet not found: {} for user: {}", walletId, userId); + return new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Wallet not found"); + }; + } +} diff --git a/src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java b/src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java deleted file mode 100644 index 4bf41a46c..000000000 --- a/src/main/java/io/mosip/mimoto/util/CommonExceptionHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.mosip.mimoto.util; - - -import io.mosip.mimoto.dto.ErrorDTO; -import io.mosip.mimoto.dto.resident.CredentialRequestResponseDTO; -import io.mosip.mimoto.exception.InvalidInputException; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; - -import java.util.Collections; - -@ControllerAdvice -public class CommonExceptionHandler{ - @ExceptionHandler( value = InvalidInputException.class) - public ResponseEntity handleInvalidInput(InvalidInputException ex) { - CredentialRequestResponseDTO credentialRequestResponseDTO = new CredentialRequestResponseDTO(); - ErrorDTO errors = new ErrorDTO(ex.getErrorCode(), ex.getMessage()); - credentialRequestResponseDTO.setVersion("1.0"); - credentialRequestResponseDTO.setErrors(Collections.singletonList(errors)); - return new ResponseEntity<>(credentialRequestResponseDTO, HttpStatus.BAD_REQUEST); - } - -} diff --git a/src/main/java/io/mosip/mimoto/util/CredentialProcessor.java b/src/main/java/io/mosip/mimoto/util/CredentialProcessor.java new file mode 100644 index 000000000..314e39c7f --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/CredentialProcessor.java @@ -0,0 +1,214 @@ +package io.mosip.mimoto.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.model.CredentialMetadata; +import io.mosip.mimoto.model.VerifiableCredential; + +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.IssuerConfig; +import io.mosip.mimoto.dto.mimoto.VCCredentialRequest; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.dto.mimoto.VerifiableCredentialResponseDTO; +import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.repository.WalletCredentialsRepository; +import io.mosip.mimoto.service.CredentialRequestService; +import io.mosip.mimoto.service.CredentialService; +import io.mosip.mimoto.service.CredentialVerifierService; +import io.mosip.mimoto.service.IssuersService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +import static io.mosip.mimoto.exception.ErrorConstants.*; + +/** + * Utility class for processing and storing credentials. + */ +@Slf4j +@Component +public class CredentialProcessor { + + private final ObjectMapper objectMapper; + private final EncryptionDecryptionUtil encryptionDecryptionUtil; + private final WalletCredentialsRepository walletCredentialsRepository; + private final IssuersService issuersService; + private final CredentialVerifierService credentialVerifierService; + private final CredentialRequestService credentialRequestService; + private final CredentialService credentialService; + + @Autowired + public CredentialProcessor( + ObjectMapper objectMapper, + EncryptionDecryptionUtil encryptionDecryptionUtil, + WalletCredentialsRepository walletCredentialsRepository, + IssuersService issuersService, + CredentialVerifierService credentialVerifierService, + CredentialRequestService credentialRequestService, + CredentialService credentialService) { + + this.objectMapper = objectMapper; + this.encryptionDecryptionUtil = encryptionDecryptionUtil; + this.walletCredentialsRepository = walletCredentialsRepository; + this.issuersService = issuersService; + this.credentialVerifierService = credentialVerifierService; + this.credentialRequestService = credentialRequestService; + this.credentialService = credentialService; + } + + /** + * Download credential and stores a credential using the provided token and parameters. + * + * @param tokenResponse The token response containing the access token. + * @param credentialConfigurationId The type of the credential. + * @param walletId The ID of the wallet. + * @param base64Key The Base64-encoded wallet key. + * @param issuerId The ID of the issuer. + * @return The stored VerifiableCredential. + * @throws InvalidRequestException If input parameters are invalid. + * @throws CredentialProcessingException If processing fails. + * @throws ExternalServiceUnavailableException If an external service is unavailable. + * @throws VCVerificationException If credential verification fails. + */ + public VerifiableCredentialResponseDTO downloadCredentialAndStoreInDB( + TokenResponseDTO tokenResponse, String credentialConfigurationId, String walletId, + String base64Key, String issuerId, String locale) + throws InvalidRequestException, CredentialProcessingException, ExternalServiceUnavailableException, VCVerificationException { + // Validate inputs + if (tokenResponse == null || StringUtils.isBlank(tokenResponse.getAccess_token())) { + log.error("Invalid token response: null or missing access token"); + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Token response or access token cannot be null"); + } + if (StringUtils.isBlank(credentialConfigurationId )) { + log.error("Invalid credential type: null or blank"); + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Credential type cannot be null or blank"); + } + if (StringUtils.isBlank(walletId)) { + log.error("Invalid wallet ID: null or blank"); + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Wallet ID cannot be null or blank"); + } + if (StringUtils.isBlank(base64Key)) { + log.error("Invalid wallet key: null or blank"); + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Wallet key cannot be null or blank"); + } + if (StringUtils.isBlank(issuerId)) { + log.error("Invalid issuer ID: null or blank"); + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Issuer ID cannot be null or blank"); + } + + // Fetch issuer configuration + IssuerConfig issuerConfig; + try { + issuerConfig = issuersService.getIssuerConfig(issuerId, credentialConfigurationId); + } catch (Exception e) { + log.error("Failed to fetch issuer config for issuerId: {}", issuerId, e); + throw new CredentialProcessingException( + CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), + "Unable to fetch issuer configuration", e); + } + + // Generate credential request + VCCredentialRequest vcCredentialRequest; + try { + vcCredentialRequest = credentialRequestService.buildRequest( + issuerConfig.getIssuerDTO(), issuerConfig.getWellKnownResponse(), + issuerConfig.getCredentialsSupportedResponse(), tokenResponse.getC_nonce(), + walletId, base64Key, true); + } catch (Exception e) { + log.error("Failed to generate VC credential request for issuerId: {}, credentialConfigurationId: {}", issuerId, credentialConfigurationId, e); + throw new CredentialProcessingException( + CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), + "Unable to generate credential request", e); + } + + // Download credential + VCCredentialResponse vcCredentialResponse; + try { + vcCredentialResponse = credentialService.downloadCredential( + issuerConfig.getWellKnownResponse().getCredentialEndPoint(), + vcCredentialRequest, tokenResponse.getAccess_token()); + } catch (Exception e) { + log.error("Failed to download credential for issuerId: {}, credentialConfigurationId: {}", issuerId, credentialConfigurationId, e); + throw new ExternalServiceUnavailableException( + SERVER_UNAVAILABLE.getErrorCode(), + "Unable to download credential from issuer", e); + } + + // Verify credential + boolean verificationStatus; + try { + verificationStatus = issuerId.toLowerCase().contains("mock") || + credentialVerifierService.verify(vcCredentialResponse); + } catch (VCVerificationException | JsonProcessingException e) { + log.error("Credential verification failed for issuerId: {}, credentialConfigurationId: {}", issuerId, credentialConfigurationId, e); + throw new VCVerificationException( + SIGNATURE_VERIFICATION_EXCEPTION.getErrorCode(), + "Credential verification failed"); + } + + if (!verificationStatus) { + log.error("Signature verification failed for issuerId: {}, credentialConfigurationId: {}", issuerId, credentialConfigurationId); + throw new VCVerificationException( + SIGNATURE_VERIFICATION_EXCEPTION.getErrorCode(), + SIGNATURE_VERIFICATION_EXCEPTION.getErrorMessage()); + } + + // Serialize and store credential + String vcResponseAsJsonString; + try { + vcResponseAsJsonString = objectMapper.writeValueAsString(vcCredentialResponse); + } catch (JsonProcessingException e) { + log.error("Failed to serialize credential response for issuerId: {}, credentialConfigurationId: {}", issuerId, credentialConfigurationId, e); + throw new CredentialProcessingException( + CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), + "Unable to serialize credential response", e); + } + + String encryptedCredentialData; + try { + encryptedCredentialData = encryptionDecryptionUtil.encryptCredential(vcResponseAsJsonString, base64Key); + } catch (Exception e) { + log.error("Failed to encrypt credential for issuerId: {}, credentialConfigurationId: {}", issuerId, credentialConfigurationId, e); + throw new CredentialProcessingException( + CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), + "Unable to encrypt credential data", e); + } + + VerifiableCredential savedCredential = saveCredential(walletId, encryptedCredentialData, issuerId, credentialConfigurationId); + return VerifiableCredentialResponseDTO.fromIssuerConfig(issuerConfig, locale, savedCredential.getId()); + } + + /** + * Saves the credential to the repository. + * + * @param walletId The wallet ID. + * @param encryptedCredential The encrypted credential data. + * @param issuerId The issuer ID. + * @param credentialType The credential type. + * @return The stored VerifiableCredential. + */ + private VerifiableCredential saveCredential(String walletId, String encryptedCredential, String issuerId, + String credentialType) { + CredentialMetadata credentialMetadata = new CredentialMetadata(); + credentialMetadata.setIssuerId(issuerId); + credentialMetadata.setCredentialType(credentialType); + + VerifiableCredential verifiableCredential = new VerifiableCredential(); + verifiableCredential.setId(UUID.randomUUID().toString()); + verifiableCredential.setWalletId(walletId); + verifiableCredential.setCredential(encryptedCredential); + verifiableCredential.setCredentialMetadata(credentialMetadata); + + try { + return walletCredentialsRepository.save(verifiableCredential); + } catch (Exception e) { + log.error("Failed to save credential for walletId: {}, issuerId: {}, credentialType: {}", walletId, issuerId, credentialType, e); + throw new CredentialProcessingException( + CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), + "Unable to save credential to database", e); + } + } +} diff --git a/src/main/java/io/mosip/mimoto/util/CryptoCoreUtil.java b/src/main/java/io/mosip/mimoto/util/CryptoCoreUtil.java index 11fe4ad2e..70c4893ce 100644 --- a/src/main/java/io/mosip/mimoto/util/CryptoCoreUtil.java +++ b/src/main/java/io/mosip/mimoto/util/CryptoCoreUtil.java @@ -216,7 +216,7 @@ private static byte[] asymmetricDecrypt(PrivateKey privateKey, BigInteger keyMod private static byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] aad) { byte[] output = null; try { - Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding"); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); byte[] randomIV = Arrays.copyOfRange(data, data.length - cipher.getBlockSize(), data.length); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES"); GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, randomIV); @@ -237,7 +237,7 @@ public byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] nonce, byte[] byte[] output = null; Cipher cipher; try { - cipher = Cipher.getInstance("AES/GCM/PKCS5Padding"); + cipher = Cipher.getInstance("AES/GCM/NoPadding"); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES"); GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, nonce); cipher.init(Cipher.DECRYPT_MODE, keySpec, gcmParameterSpec); diff --git a/src/main/java/io/mosip/mimoto/util/CryptoUtil.java b/src/main/java/io/mosip/mimoto/util/CryptoUtil.java index e4cee3eb4..0c6ba30fc 100644 --- a/src/main/java/io/mosip/mimoto/util/CryptoUtil.java +++ b/src/main/java/io/mosip/mimoto/util/CryptoUtil.java @@ -71,7 +71,7 @@ private byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] iv, byte[] aa } Cipher cipher = null; try { - cipher = Cipher.getInstance("AES/GCM/PKCS5Padding"); + cipher = Cipher.getInstance("AES/GCM/NoPadding"); } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { throw new CryptoManagerException(PlatformErrorMessages.MIMOTO_INVALID_KEY_EXCEPTION.getCode(), PlatformErrorMessages.MIMOTO_INVALID_KEY_EXCEPTION.getMessage(), e); @@ -89,7 +89,7 @@ private byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] iv, byte[] aa private static byte[] symmetricDecrypt(SecretKey key, byte[] data, byte[] aad) { byte[] output = null; try { - Cipher cipher = Cipher.getInstance("AES/GCM/PKCS5Padding"); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); byte[] randomIV = Arrays.copyOfRange(data, data.length - cipher.getBlockSize(), data.length); SecretKeySpec keySpec = new SecretKeySpec(key.getEncoded(), "AES"); GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, randomIV); diff --git a/src/main/java/io/mosip/mimoto/util/EncryptionDecryptionUtil.java b/src/main/java/io/mosip/mimoto/util/EncryptionDecryptionUtil.java new file mode 100644 index 000000000..56afb1c0a --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/EncryptionDecryptionUtil.java @@ -0,0 +1,382 @@ +package io.mosip.mimoto.util; + +import io.mosip.kernel.core.util.CryptoUtil; +import io.mosip.kernel.cryptomanager.dto.CryptoWithPinRequestDto; +import io.mosip.kernel.cryptomanager.dto.CryptoWithPinResponseDto; +import io.mosip.kernel.cryptomanager.dto.CryptomanagerRequestDto; +import io.mosip.kernel.cryptomanager.dto.CryptomanagerResponseDto; +import io.mosip.kernel.cryptomanager.service.CryptomanagerService; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.EncryptionException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +import static io.mosip.mimoto.exception.ErrorConstants.DECRYPTION_FAILED; +import static io.mosip.mimoto.exception.ErrorConstants.ENCRYPTION_FAILED; + +/** + * Utility class for encryption and decryption operations. + */ +@Slf4j +@Component +public class EncryptionDecryptionUtil { + private static final String AES_ALGORITHM = "AES/GCM/NoPadding"; + private static final int NONCE_LENGTH = 12; // Recommended nonce length for GCM + private static final int TAG_LENGTH = 128; // Authentication tag length in bits (16 bytes) + public static final String USER_PII_KEY_REFERENCE_ID = "user_pii"; + + private final CryptomanagerService cryptomanagerService; + + @Value("${mosip.inji.app.id:MIMOTO}") + private String appId; + + @Autowired + public EncryptionDecryptionUtil(CryptomanagerService cryptomanagerService) { + this.cryptomanagerService = cryptomanagerService; + } + + /** + * Encrypts data using the provided reference ID, AAD, and salt. + * + * @param data The data to encrypt. + * @param refId The reference ID. + * @param aad The additional authentication data. + * @param salt The salt for encryption. + * @return The encrypted data. + */ + public String encrypt(String data, String refId, String aad, String salt) { + if (data == null) { + log.warn("Encryption skipped: Input data is null"); + return null; + } + try { + CryptomanagerRequestDto request = new CryptomanagerRequestDto(); + request.setApplicationId(appId); + request.setReferenceId(refId); + request.setData(CryptoUtil.encodeToURLSafeBase64(data.getBytes(StandardCharsets.UTF_8))); + request.setAad(aad); + request.setSalt(salt); + CryptomanagerResponseDto response = cryptomanagerService.encrypt(request); + log.debug("Data encrypted successfully for refId: {}", refId); + return response.getData(); + } catch (Exception e) { + log.error("Encryption failed for refId: {}", refId, e); + throw new RuntimeException("Failed to encrypt data", e); + } + } + + /** + * Decrypts data using the provided reference ID, AAD, and salt. + * + * @param data The data to decrypt. + * @param refId The reference ID. + * @param aad The additional authentication data. + * @param salt The salt for decryption. + * @return The decrypted data. + */ + public String decrypt(String data, String refId, String aad, String salt) { + if (data == null) { + log.warn("Decryption skipped: Input data is null"); + return null; + } + try { + CryptomanagerRequestDto request = new CryptomanagerRequestDto(); + request.setApplicationId(appId); + request.setReferenceId(refId); + request.setData(data); + request.setAad(aad); + request.setSalt(salt); + CryptomanagerResponseDto response = cryptomanagerService.decrypt(request); + String decryptedData = new String(CryptoUtil.decodeURLSafeBase64(response.getData()), StandardCharsets.UTF_8); + log.debug("Data decrypted successfully for refId: {}", refId); + return decryptedData; + } catch (Exception e) { + log.error("Decryption failed for refId: {}", refId, e); + throw new RuntimeException("Failed to decrypt data", e); + } + } + + /** + * Encrypts data with AES/GCM/NoPadding using the provided key. + * + * @param key The AES key. + * @param data The data to encrypt. + * @return The encrypted data, including the nonce prepended, encoded in Base64. + */ + public String encryptWithAES(SecretKey key, byte[] data) { + if (data == null || data.length == 0) { + log.warn("AES encryption skipped: Input data is null or empty"); + return null; + } + try { + // Generate a random nonce + byte[] nonce = new byte[NONCE_LENGTH]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(nonce); + GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH, nonce); + + // Initialize cipher + Cipher cipher = Cipher.getInstance(AES_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, key, gcmSpec); + + // Encrypt data + byte[] encryptedData = cipher.doFinal(data); + + // Prepend nonce to encrypted data (nonce + ciphertext + tag) + byte[] combined = new byte[nonce.length + encryptedData.length]; + System.arraycopy(nonce, 0, combined, 0, nonce.length); + System.arraycopy(encryptedData, 0, combined, nonce.length, encryptedData.length); + + // Encode to Base64 + String result = Base64.getEncoder().encodeToString(combined); + log.debug("AES/GCM encryption successful, data length: {}", data.length); + return result; + } catch (Exception e) { + log.error("AES/GCM encryption failed", e); + throw new RuntimeException("Failed to encrypt with AES/GCM", e); + } + } + + /** + * Decrypts data with AES/GCM/NoPadding using the provided key. + * + * @param key The AES key. + * @param data The encrypted data (Base64-encoded, with nonce prepended). + * @return The decrypted data. + */ + public byte[] decryptWithAES(SecretKey key, String data) { + if (data == null || data.isEmpty()) { + log.warn("AES decryption skipped: Input data is null or empty"); + return null; + } + try { + // Decode Base64 data + byte[] decodedData = Base64.getDecoder().decode(data); + + // Extract nonce and encrypted data + if (decodedData.length < NONCE_LENGTH) { + log.error("Invalid encrypted data: too short"); + throw new IllegalArgumentException("Invalid encrypted data length"); + } + byte[] nonce = new byte[NONCE_LENGTH]; + byte[] encryptedData = new byte[decodedData.length - NONCE_LENGTH]; + System.arraycopy(decodedData, 0, nonce, 0, NONCE_LENGTH); + System.arraycopy(decodedData, NONCE_LENGTH, encryptedData, 0, encryptedData.length); + + // Initialize cipher + GCMParameterSpec gcmSpec = new GCMParameterSpec(TAG_LENGTH, nonce); + Cipher cipher = Cipher.getInstance(AES_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, key, gcmSpec); + + // Decrypt data + byte[] decryptedData = cipher.doFinal(encryptedData); + log.debug("AES/GCM decryption successful, decrypted length: {}", decryptedData.length); + return decryptedData; + } catch (Exception e) { + log.error("AES/GCM decryption failed", e); + throw new RuntimeException("Failed to decrypt with AES/GCM", e); + } + } + + /** + * Converts a byte array to a SecretKey for AES. + * + * @param keyBytes The byte array representing the key. + * @return The SecretKey. + */ + public static SecretKey bytesToSecretKey(byte[] keyBytes) { + if (keyBytes == null || keyBytes.length == 0) { + log.error("Invalid key bytes: null or empty"); + throw new IllegalArgumentException("Key bytes cannot be null or empty"); + } + try { + return new javax.crypto.spec.SecretKeySpec(keyBytes, "AES"); + } catch (Exception e) { + log.error("Failed to convert bytes to SecretKey", e); + throw new RuntimeException("Failed to create SecretKey", e); + } + } + + /** + * Converts a byte array to a PrivateKey for the specified algorithm. + * + * @param privateKeyBytes The byte array representing the private key. + * @param algorithmName The algorithm name (e.g., RSA, EC). + * @return The PrivateKey. + * @throws Exception If key conversion fails. + */ + public static PrivateKey bytesToPrivateKey(byte[] privateKeyBytes, String algorithmName) throws Exception { + if (privateKeyBytes == null || privateKeyBytes.length == 0) { + log.error("Invalid private key bytes: null or empty"); + throw new IllegalArgumentException("Private key bytes cannot be null or empty"); + } + if (algorithmName == null || algorithmName.isEmpty()) { + log.error("Invalid algorithm name: null or empty"); + throw new IllegalArgumentException("Algorithm name cannot be null or empty"); + } + try { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(algorithmName); + return keyFactory.generatePrivate(keySpec); + } catch (Exception e) { + log.error("Failed to convert bytes to PrivateKey for algorithm: {}", algorithmName, e); + throw new Exception("Failed to create PrivateKey", e); + } + } + + /** + * Encrypts a SecretKey with a user PIN. + * + * @param encryptionKey The SecretKey to encrypt. + * @param pin The user PIN. + * @return The encrypted key data. + */ + public String encryptKeyWithPin(SecretKey encryptionKey, String pin) { + if (encryptionKey == null) { + log.error("Encryption key is null"); + throw new IllegalArgumentException("Encryption key cannot be null"); + } + if (pin == null || pin.isEmpty()) { + log.error("PIN is null or empty"); + throw new IllegalArgumentException("PIN cannot be null or empty"); + } + try { + CryptoWithPinRequestDto requestDto = new CryptoWithPinRequestDto(); + requestDto.setUserPin(pin); + String dataAsString = Base64.getEncoder().encodeToString(encryptionKey.getEncoded()); + requestDto.setData(dataAsString); + CryptoWithPinResponseDto responseDto = cryptomanagerService.encryptWithPin(requestDto); + log.debug("Key encrypted with PIN successfully"); + return responseDto.getData(); + } catch (Exception e) { + log.error("Failed to encrypt key with PIN", e); + throw new RuntimeException("Failed to encrypt key with PIN", e); + } + } + + /** + * Decrypts data using a user PIN. + * + * @param encryptedString The encrypted data. + * @param pin The user PIN. + * @return The decrypted data. + */ + public String decryptWithPin(String encryptedString, String pin) { + if (encryptedString == null || encryptedString.isEmpty()) { + log.error("Encrypted string is null or empty"); + throw new IllegalArgumentException("Encrypted string cannot be null or empty"); + } + if (pin == null || pin.isEmpty()) { + log.error("PIN is null or empty"); + throw new IllegalArgumentException("PIN cannot be null or empty"); + } + try { + CryptoWithPinRequestDto requestDto = new CryptoWithPinRequestDto(); + requestDto.setUserPin(pin); + requestDto.setData(encryptedString); + CryptoWithPinResponseDto responseDto = cryptomanagerService.decryptWithPin(requestDto); + log.debug("Data decrypted with PIN successfully"); + return responseDto.getData(); + } catch (Exception e) { + log.error("Failed to decrypt with PIN", e); + throw new RuntimeException("Failed to decrypt with PIN", e); + } + } + + /** + * Encrypts credential data using a wallet key. + * + * @param credentialData The credential data to encrypt. + * @param base64EncodedWalletKey The Base64-encoded wallet key. + * @return The encrypted credential data. + * @throws EncryptionException If encryption fails. + */ + public String encryptCredential(String credentialData, String base64EncodedWalletKey) throws EncryptionException { + if (credentialData == null || credentialData.isEmpty()) { + log.error("Credential data is null or empty"); + throw new EncryptionException(ENCRYPTION_FAILED.getErrorCode(), "Credential data cannot be null or empty"); + } + if (base64EncodedWalletKey == null || base64EncodedWalletKey.isEmpty()) { + log.error("Wallet key is null or empty"); + throw new EncryptionException(ENCRYPTION_FAILED.getErrorCode(), "Wallet key cannot be null or empty"); + } + try { + byte[] decodedWalletKey = Base64.getDecoder().decode(base64EncodedWalletKey); + SecretKey walletKey = bytesToSecretKey(decodedWalletKey); + String encryptedData = encryptWithAES(walletKey, stringToBytes(credentialData)); + log.debug("Credential encrypted successfully"); + return encryptedData; + } catch (Exception e) { + log.error("Failed to encrypt credential", e); + throw new EncryptionException(ENCRYPTION_FAILED.getErrorCode(), "Failed to encrypt credential", e); + } + } + + /** + * Decrypts credential data using a wallet key. + * + * @param encryptedCredentialData The encrypted credential data. + * @param base64EncodedWalletKey The Base64-encoded wallet key. + * @return The decrypted credential data. + * @throws DecryptionException If decryption fails. + */ + public String decryptCredential(String encryptedCredentialData, String base64EncodedWalletKey) throws DecryptionException { + if (encryptedCredentialData == null || encryptedCredentialData.isEmpty()) { + log.error("Encrypted credential data is null or empty"); + throw new DecryptionException(DECRYPTION_FAILED.getErrorCode() , "Encrypted credential data cannot be null or empty"); + } + if (base64EncodedWalletKey == null || base64EncodedWalletKey.isEmpty()) { + log.error("Wallet key is null or empty"); + throw new DecryptionException(DECRYPTION_FAILED.getErrorCode(), "Wallet key cannot be null or empty"); + } + try { + byte[] decodedWalletKey = Base64.getDecoder().decode(base64EncodedWalletKey); + SecretKey walletKey = bytesToSecretKey(decodedWalletKey); + byte[] decryptedData = decryptWithAES(walletKey, encryptedCredentialData); + String result = bytesToString(decryptedData); + log.debug("Credential decrypted successfully"); + return result; + } catch (Exception e) { + log.error("Failed to decrypt credential", e); + throw new DecryptionException(DECRYPTION_FAILED.getErrorCode(), "Failed to decrypt credential", e); + } + } + + /** + * Converts a string to a byte array using UTF-8 encoding. + * + * @param data The input string. + * @return The byte array. + */ + private static byte[] stringToBytes(String data) { + if (data == null) { + return null; + } + return data.getBytes(StandardCharsets.UTF_8); + } + + /** + * Converts a byte array to a string using UTF-8 encoding. + * + * @param data The byte array. + * @return The string. + */ + private static String bytesToString(byte[] data) { + if (data == null) { + return null; + } + return new String(data, StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/util/GlobalExceptionHandler.java b/src/main/java/io/mosip/mimoto/util/GlobalExceptionHandler.java new file mode 100644 index 000000000..75e08bbde --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/GlobalExceptionHandler.java @@ -0,0 +1,110 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.dto.resident.CredentialRequestResponseDTO; +import io.mosip.mimoto.exception.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.method.annotation.HandlerMethodValidationException; +import org.springframework.web.servlet.NoHandlerFoundException; + +import java.util.Collections; +import java.util.stream.Collectors; + +@ControllerAdvice +@ResponseBody +@Slf4j +public class GlobalExceptionHandler { + @ExceptionHandler(Throwable.class) // Catch-all for unexpected exceptions + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorDTO handleGenericException(Exception ex) { + log.error("An unexpected error occurred: ", ex); + return new ErrorDTO(ErrorConstants.INTERNAL_SERVER_ERROR.getErrorCode(), ErrorConstants.INTERNAL_SERVER_ERROR.getErrorMessage()); + } + + @ExceptionHandler( value = InvalidInputException.class) + public ResponseEntity handleInvalidInput(InvalidInputException ex) { + CredentialRequestResponseDTO credentialRequestResponseDTO = new CredentialRequestResponseDTO(); + ErrorDTO errors = new ErrorDTO(ex.getErrorCode(), ex.getMessage()); + credentialRequestResponseDTO.setVersion("1.0"); + credentialRequestResponseDTO.setErrors(Collections.singletonList(errors)); + return new ResponseEntity<>(credentialRequestResponseDTO, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(DataAccessResourceFailureException.class) + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) + public ErrorDTO handleDatabaseException(Exception ex) { + log.error("Database error occurred:", ex); + return new ErrorDTO(ErrorConstants.DATABASE_CONNECTION_EXCEPTION.getErrorCode(), ErrorConstants.DATABASE_CONNECTION_EXCEPTION.getErrorMessage()); + } + + @ExceptionHandler(ExternalServiceUnavailableException.class) + @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE) + public ErrorDTO handleExternalServiceUnavailable(ExternalServiceUnavailableException ex) { + log.error("Connection to external service failed: ", ex); + return new ErrorDTO(ex.getErrorCode(), ex.getMessage()); + } + + @ExceptionHandler(UnauthorizedAccessException.class) + @ResponseStatus(HttpStatus.UNAUTHORIZED) + public ErrorDTO handleUnAuthorizedAccess(UnauthorizedAccessException ex) { + log.error("UnAuthorized access detected: ", ex); + return new ErrorDTO(ex.getErrorCode(), ex.getErrorText()); + } + + @ExceptionHandler(InvalidRequestException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorDTO handleInvalidRequest(InvalidRequestException ex) { + log.error("Invalid request parameters: ", ex); + return new ErrorDTO(ex.getErrorCode(), ex.getErrorText()); + } + + @ExceptionHandler(HttpMessageNotReadableException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorDTO handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) { + log.error("Invalid request body: {}", ex.getMessage(), ex); + String errorCode = ErrorConstants.INVALID_REQUEST.getErrorCode(); + String errorMessage; + + if (ex.getCause() instanceof com.fasterxml.jackson.core.JsonParseException) { + errorMessage = "Invalid JSON format in request body"; + } else if (ex.getCause() instanceof com.fasterxml.jackson.databind.JsonMappingException) { + errorMessage = "Request body does not match expected structure"; + } else if (ex.getMessage().contains("Required request body is missing")) { + errorMessage = "Required request body is missing"; + } else if (ex.getMessage().contains("Content type")) { + errorMessage = "Unsupported content type in request"; + } else { + errorMessage = "Unable to process request body"; + } + + return new ErrorDTO(errorCode, errorMessage); + } + + @ExceptionHandler(NoHandlerFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorDTO handleNoHandlerFoundException(NoHandlerFoundException ex) { + log.error("No handler found for request: {} {}", ex.getHttpMethod(), ex.getRequestURL()); + return new ErrorDTO(ErrorConstants.RESOURCE_NOT_FOUND.getErrorCode(), ErrorConstants.RESOURCE_NOT_FOUND.getErrorMessage()); + } + + @ExceptionHandler(HandlerMethodValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorDTO handleHandlerMethodValidationException(HandlerMethodValidationException ex) { + log.error("Validation error in handler method: {}", ex.getMessage()); + String errorMessage = ex.getAllValidationResults() + .stream() + .flatMap(r -> r.getResolvableErrors().stream()) + .map(MessageSourceResolvable::getDefaultMessage) + .collect(Collectors.joining(", ")); + return new ErrorDTO(ErrorConstants.INVALID_REQUEST.getErrorCode(), errorMessage); + } +} diff --git a/src/main/java/io/mosip/mimoto/util/JoseUtil.java b/src/main/java/io/mosip/mimoto/util/JoseUtil.java index 1abee52c1..3491bb589 100644 --- a/src/main/java/io/mosip/mimoto/util/JoseUtil.java +++ b/src/main/java/io/mosip/mimoto/util/JoseUtil.java @@ -156,10 +156,11 @@ public String getJWT(String clientId, String keyStorePath, String fileName, Stri .withAudience(audience) .withExpiresAt(expiresAt) .withIssuedAt(issuedAt) + .withClaim("jti", UUID.randomUUID().toString()) .sign(Algorithm.RSA256(null, privateKey)); } - public String generateJwt(String audience, String clientId, String accessToken) throws Exception { + public String generateJwt(String audience, String clientId, String cNonce) throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); kpg.initialize(2048); @@ -177,9 +178,6 @@ public String generateJwt(String audience, String clientId, String accessToken) Date issuedAt = Date.from(Instant.now()); Date expiresAt = Date.from(Instant.now().plusMillis(120000000)); RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate(); - SignedJWT jwt = (SignedJWT) JWTParser.parse(accessToken); - Map jsonObject = jwt.getPayload().toJSONObject(); - String cNonce = (String) jsonObject.get("c_nonce"); Map payload = new HashMap<>(); payload.put("sub", clientId); @@ -188,6 +186,7 @@ public String generateJwt(String audience, String clientId, String accessToken) payload.put("iss", clientId); payload.put("exp", expiresAt.toInstant().getEpochSecond()); payload.put("iat", issuedAt.toInstant().getEpochSecond()); + payload.put("jti", UUID.randomUUID().toString()); return Jwts.builder() .setHeader(header) diff --git a/src/main/java/io/mosip/mimoto/util/JwtGeneratorUtil.java b/src/main/java/io/mosip/mimoto/util/JwtGeneratorUtil.java new file mode 100644 index 000000000..0379f87b0 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/JwtGeneratorUtil.java @@ -0,0 +1,148 @@ +package io.mosip.mimoto.util; + +import com.nimbusds.jose.*; +import com.nimbusds.jose.crypto.*; +import com.nimbusds.jose.jca.JCAContext; +import com.nimbusds.jose.jwk.*; +import com.nimbusds.jose.jwk.ECKey; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.*; +import com.nimbusds.jose.util.Base64URL; +import io.mosip.mimoto.constant.SigningAlgorithm; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.security.*; +import java.security.interfaces.*; +import java.util.*; +import java.util.Arrays; + + +@Slf4j +public class JwtGeneratorUtil { + + private static final Provider BC_PROVIDER = new BouncyCastleProvider(); + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(BC_PROVIDER); + } + } + + public static String generateJwtUsingDBKeys(SigningAlgorithm algorithm, String audience, String clientId, String cNonce, byte[] publicKeyBytes, byte[] privateKeyBytes) throws Exception { + KeyPair keyPair = KeyGenerationUtil.getKeyPairFromDBStoredKeys(algorithm, publicKeyBytes, privateKeyBytes); + JWK jwk = generateJwk(algorithm, keyPair); + JWSSigner signer = createSigner(algorithm, jwk); + + JWTClaimsSet claimsSet = createClaims(clientId, audience, cNonce); + JWSHeader header = new JWSHeader.Builder(algorithm.getJWSAlgorithm()) + .type(new JOSEObjectType("openid4vci-proof+jwt")) + .jwk(jwk.toPublicJWK()) + .build(); + + SignedJWT signedJWT = new SignedJWT(header, claimsSet); + signedJWT.sign(signer); + return signedJWT.serialize(); + } + + private static JWTClaimsSet createClaims(String clientId, String audience, String cNonce) throws java.text.ParseException { + long nowSeconds = System.currentTimeMillis() / 1000; + Date issuedAt = new Date(nowSeconds * 1000); + Date expiresAt = new Date((nowSeconds + 18000) * 1000); + + return new JWTClaimsSet.Builder() + .subject(clientId) + .audience(audience) + .issuer(clientId) + .issueTime(issuedAt) + .expirationTime(expiresAt) + .claim("nonce", cNonce) + .build(); + } + + + private static JWK generateJwk(SigningAlgorithm algorithm, KeyPair keyPair) { + PublicKey publicKey = keyPair.getPublic(); + PrivateKey privateKey = keyPair.getPrivate(); + + return switch (algorithm) { + case RS256 -> new RSAKey.Builder((RSAPublicKey) publicKey) + .privateKey((RSAPrivateKey) privateKey) + .algorithm(JWSAlgorithm.RS256) + .keyUse(KeyUse.SIGNATURE) + .build(); + + case ES256 -> new ECKey.Builder(Curve.P_256, (ECPublicKey) publicKey) + .privateKey((ECPrivateKey) privateKey) + .algorithm(JWSAlgorithm.ES256) + .keyUse(KeyUse.SIGNATURE) + .build(); + + case ES256K -> new ECKey.Builder(Curve.SECP256K1, (ECPublicKey) publicKey) + .privateKey((ECPrivateKey) privateKey) + .algorithm(JWSAlgorithm.ES256K) + .keyUse(KeyUse.SIGNATURE) + .build(); + + case ED25519 -> { + EdECPublicKey edECPublicKey = (EdECPublicKey) publicKey; + EdECPrivateKey edECPrivateKey = (EdECPrivateKey) privateKey; + + byte[] x = Arrays.copyOfRange(edECPublicKey.getEncoded(), edECPublicKey.getEncoded().length - 32, edECPublicKey.getEncoded().length); + byte[] d = Arrays.copyOfRange(edECPrivateKey.getEncoded(), edECPrivateKey.getEncoded().length - 32, edECPrivateKey.getEncoded().length); + + yield new OctetKeyPair.Builder(Curve.Ed25519, Base64URL.encode(x)) + .d(Base64URL.encode(d)) + .algorithm(JWSAlgorithm.EdDSA) + .keyUse(KeyUse.SIGNATURE) + .build(); + } + }; + } + + private static JWSSigner createSigner(SigningAlgorithm algorithm, JWK jwk) throws JOSEException { + return switch (algorithm) { + case RS256 -> { + RSASSASigner signer = new RSASSASigner((RSAKey) jwk); + signer.getJCAContext().setProvider(BC_PROVIDER); + yield signer; + } + case ES256, ES256K -> { + ECDSASigner signer = new ECDSASigner((ECKey) jwk); + signer.getJCAContext().setProvider(BC_PROVIDER); + yield signer; + } + case ED25519 -> createEd25519Signer((OctetKeyPair) jwk); + }; + } + + private static JWSSigner createEd25519Signer(OctetKeyPair jwk) { + byte[] privateKeyBytes = jwk.getD().decode(); + return new JWSSigner() { + private final JCAContext jcaContext = new JCAContext(); + + @Override + public Base64URL sign(JWSHeader header, byte[] input) throws JOSEException { + try { + var signer = new org.bouncycastle.crypto.signers.Ed25519Signer(); + signer.init(true, new org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters(privateKeyBytes, 0)); + signer.update(input, 0, input.length); + return Base64URL.encode(signer.generateSignature()); + } catch (Exception e) { + throw new JOSEException("Ed25519 signing failed", e); + } + } + + @Override + public Set supportedJWSAlgorithms() { + return Collections.singleton(JWSAlgorithm.EdDSA); + } + + @Override + public JCAContext getJCAContext() { + return jcaContext; + } + }; + } +} + diff --git a/src/main/java/io/mosip/mimoto/util/KeyGenerationUtil.java b/src/main/java/io/mosip/mimoto/util/KeyGenerationUtil.java new file mode 100644 index 000000000..115411b9d --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/KeyGenerationUtil.java @@ -0,0 +1,94 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.exception.KeyGenerationException; +import io.mosip.mimoto.constant.SigningAlgorithm; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import java.security.*; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import static io.mosip.mimoto.exception.ErrorConstants.ENCRYPTION_FAILED; + +public class KeyGenerationUtil { + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + /** + * Generates a key pair for the given algorithm. + * + * @param algorithm The algorithm to use (e.g., "RS256", "ES256", "ES256K", "Ed25519"). + * @return KeyPair object. + * @throws NoSuchAlgorithmException + * @throws InvalidAlgorithmParameterException + * @throws NoSuchProviderException + */ + public static KeyPair generateKeyPair(SigningAlgorithm algorithm) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException { + KeyPairGenerator keyPairGenerator = null; + + switch (algorithm) { + case RS256: + keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(2048); + break; + case ES256: + keyPairGenerator = KeyPairGenerator.getInstance("EC"); + ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("secp256r1"); + keyPairGenerator.initialize(ecGenParameterSpec); + break; + case ES256K: + keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC"); + ECGenParameterSpec ecGenParameterSpecK = new ECGenParameterSpec("secp256k1"); + keyPairGenerator.initialize(ecGenParameterSpecK); + break; + case ED25519: + keyPairGenerator = KeyPairGenerator.getInstance("Ed25519"); + break; + } + + if (keyPairGenerator == null) { + throw new IllegalArgumentException("Unsupported algorithm: " + algorithm); + } + + return keyPairGenerator.generateKeyPair(); + } + + + public static KeyPair getKeyPairFromDBStoredKeys(SigningAlgorithm algorithm, byte[] publicKeyBytes, byte[] privateKeyBytes) throws Exception { + KeyFactory keyFactory = switch (algorithm) { + case ES256K -> KeyFactory.getInstance(algorithm.getKeyFactoryAlgorithm(), "BC"); + default -> KeyFactory.getInstance(algorithm.getKeyFactoryAlgorithm()); + }; + + PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyBytes)); + PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyBytes)); + + return new KeyPair(publicKey, privateKey); + } + + + /** + * Generates a secret key for the given algorithm and key size. + * + * @param algorithm The algorithm to use (e.g., "AES"). + * @param keysize The key size in bits. + * @return SecretKey object. + */ + public static SecretKey generateEncryptionKey(String algorithm, int keysize) { + KeyGenerator keyGenerator = null; + try { + keyGenerator = KeyGenerator.getInstance(algorithm); + } catch (NoSuchAlgorithmException e) { + throw new KeyGenerationException(ENCRYPTION_FAILED.getErrorCode(), ENCRYPTION_FAILED.getErrorMessage(), e); + } + keyGenerator.init(keysize); + return keyGenerator.generateKey(); + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/util/LocaleUtils.java b/src/main/java/io/mosip/mimoto/util/LocaleUtils.java index cb960360b..0c8ceff54 100644 --- a/src/main/java/io/mosip/mimoto/util/LocaleUtils.java +++ b/src/main/java/io/mosip/mimoto/util/LocaleUtils.java @@ -1,7 +1,9 @@ package io.mosip.mimoto.util; +import io.mosip.mimoto.dto.DisplayDTO; import io.mosip.mimoto.dto.mimoto.CredentialDisplayResponseDto; import io.mosip.mimoto.dto.mimoto.CredentialIssuerDisplayResponse; +import io.mosip.mimoto.dto.mimoto.CredentialSupportedDisplayResponse; import java.util.List; import java.util.Locale; @@ -44,4 +46,26 @@ public static String resolveLocaleWithFallback(Map displayDTOList, String locale) { + if (displayDTOList == null || displayDTOList.isEmpty()) { + return null; + } + + return displayDTOList.stream() + .filter(obj -> matchesLocale(obj.getLanguage(), locale)) + .findFirst() + .orElse(displayDTOList.get(0)); // Return first display object if no match is found for the received locale + } + + public static CredentialSupportedDisplayResponse getCredentialDisplayDTOBasedOnLocale(List displayDTOList, String locale) { + if (displayDTOList == null || displayDTOList.isEmpty()) { + return null; + } + + return displayDTOList.stream() + .filter(obj -> matchesLocale(obj.getLocale(), locale)) + .findFirst() + .orElse(displayDTOList.get(0)); // Return first display object if no match is found for the received locale + } } diff --git a/src/main/java/io/mosip/mimoto/util/ProofSigningKeyFactory.java b/src/main/java/io/mosip/mimoto/util/ProofSigningKeyFactory.java new file mode 100644 index 000000000..afddfe964 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/ProofSigningKeyFactory.java @@ -0,0 +1,43 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.model.KeyMetadata; +import io.mosip.mimoto.model.ProofSigningKey; +import io.mosip.mimoto.exception.KeyGenerationException; +import io.mosip.mimoto.constant.SigningAlgorithm; +import lombok.extern.slf4j.Slf4j; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.time.Instant; +import java.util.Base64; +import java.util.UUID; + +import static io.mosip.mimoto.exception.ErrorConstants.ENCRYPTION_FAILED; + +@Slf4j +public class ProofSigningKeyFactory { + + public static ProofSigningKey createProofSigningKey(SigningAlgorithm algorithm) { + try { + KeyPair keyPair = KeyGenerationUtil.generateKeyPair(algorithm); + + KeyMetadata keyMetadata = new KeyMetadata(); + keyMetadata.setAlgorithmName(algorithm.name()); + + ProofSigningKey proofSigningKey = new ProofSigningKey(); + proofSigningKey.setId(UUID.randomUUID().toString()); + proofSigningKey.setPublicKey(Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded())); + proofSigningKey.setSecretKey(keyPair.getPrivate()); + proofSigningKey.setKeyMetadata(keyMetadata); + proofSigningKey.setCreatedAt(Instant.now()); + proofSigningKey.setUpdatedAt(Instant.now()); + + return proofSigningKey; + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchProviderException e) { + log.error("Error while generating key",e); + throw new KeyGenerationException(ENCRYPTION_FAILED.getErrorCode(), "Failed to generate proof signing key for algorithm: " + algorithm); + } + } +} diff --git a/src/main/java/io/mosip/mimoto/util/Utilities.java b/src/main/java/io/mosip/mimoto/util/Utilities.java index dba961834..327582e91 100644 --- a/src/main/java/io/mosip/mimoto/util/Utilities.java +++ b/src/main/java/io/mosip/mimoto/util/Utilities.java @@ -3,11 +3,10 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.Lists; -import io.mosip.kernel.core.logger.spi.Logger; import io.mosip.mimoto.core.http.ResponseWrapper; import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.exception.ErrorConstants; import io.mosip.mimoto.exception.ExceptionUtils; -import io.mosip.mimoto.exception.PlatformErrorMessages; import io.mosip.mimoto.service.impl.CredentialShareServiceImpl; import jakarta.annotation.PostConstruct; import lombok.Data; @@ -18,6 +17,9 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import javax.imageio.ImageIO; @@ -86,7 +88,7 @@ public class Utilities { @PostConstruct public void setUp() throws IOException { - if(activeProfile.equals("local")) { + if(activeProfile.contains("local")) { Resource resource = new ClassPathResource(issuersConfigPath); Resource trustedVerifiersResource = new ClassPathResource(trustedVerifiersPath); Resource credentialTemplateResource = new ClassPathResource("templates/"+ credentialTemplatePath); @@ -185,7 +187,7 @@ public String getTrustedVerifiersJsonValue() { } public String getCredentialSupportedTemplateString(String issuerId, String credentialType) { String templateFileName = String.format("%s-%s-template.html", issuerId, credentialType); - if(activeProfile.equals("local")) { + if(activeProfile.contains("local")) { Path basePath = Paths.get("templates").toAbsolutePath().normalize(); Path resolvedPath = basePath.resolve(templateFileName).normalize(); @@ -204,17 +206,75 @@ public String getCredentialSupportedTemplateString(String issuerId, String crede String specificCredentialPDFTemplate = getJson("", templateFileName); return !StringUtils.isEmpty(specificCredentialPDFTemplate)? specificCredentialPDFTemplate : getJson("", credentialTemplatePath); } + public static String[] handleExceptionWithErrorCode(Exception exception, String flowErrorCode) { String errorMessage = exception.getMessage(); String errorCode = flowErrorCode; - if(errorMessage.contains(DELIMITER)){ + if (errorMessage.contains(DELIMITER)) { String[] errorSections = errorMessage.split(DELIMITER); errorCode = errorSections[0]; errorMessage = errorSections[1]; } return new String[]{errorCode, errorMessage}; } + public static ResponseEntity> handleErrorResponse( + Exception exception, String flowErrorCode, HttpStatus status, MediaType contentType) { + String errorMessage = exception.getMessage(); + String errorCode = flowErrorCode; + + if (errorMessage.contains(DELIMITER)) { + String[] errorSections = errorMessage.split(DELIMITER); + errorCode = errorSections[0]; + errorMessage = errorSections[1]; + } + + ResponseWrapper responseWrapper = new ResponseWrapper<>(); + responseWrapper.setResponse(null); + responseWrapper.setErrors(Utilities.getErrors(errorCode, errorMessage)); + ResponseEntity.BodyBuilder responseEntity = ResponseEntity.status(status); + if (contentType != null) { + responseEntity.contentType(contentType); + } + return responseEntity.body(responseWrapper); + } + + public static ResponseEntity getErrorResponseEntityWithoutWrapper( + Exception exception, String flowErrorCode, HttpStatus status, MediaType contentType) { + String errorMessage = exception.getMessage(); + String errorCode = flowErrorCode; + + if (errorMessage.contains(DELIMITER)) { + String[] errorSections = errorMessage.split(DELIMITER); + errorCode = errorSections[0]; + errorMessage = errorSections[1]; + } + + ResponseEntity.BodyBuilder responseEntity = ResponseEntity.status(status); + if (contentType != null) { + responseEntity.contentType(contentType); + } + return (ResponseEntity) responseEntity.body(new ErrorDTO(errorCode, errorMessage)); + } + + public static ResponseEntity getErrorResponseEntityFromPlatformErrorMessage( + ErrorConstants message, HttpStatus status, MediaType contentType) { + String errorMessage = message.getErrorMessage(); + String errorCode = message.getErrorCode(); + + ResponseEntity.BodyBuilder responseEntity = ResponseEntity.status(status); + if (contentType != null) { + responseEntity.contentType(contentType); + } + return (ResponseEntity) responseEntity.body(new ErrorDTO(errorCode, errorMessage)); + } + + public static ResponseEntity buildErrorResponse(HttpStatus status, String errorCode, String message) { + return ResponseEntity + .status(status) + .contentType(MediaType.APPLICATION_JSON) + .body(new ErrorDTO(errorCode, message)); + } public static List getErrors(String errorCode, String errorMessage) { ErrorDTO errorDTO = new ErrorDTO(errorCode, errorMessage); diff --git a/src/main/java/io/mosip/mimoto/util/WalletUtil.java b/src/main/java/io/mosip/mimoto/util/WalletUtil.java new file mode 100644 index 000000000..056804dfc --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/WalletUtil.java @@ -0,0 +1,102 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.model.ProofSigningKey; +import io.mosip.mimoto.model.Wallet; +import io.mosip.mimoto.model.WalletMetadata; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.constant.SigningAlgorithm; +import io.mosip.mimoto.repository.WalletRepository; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.crypto.SecretKey; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static io.mosip.mimoto.exception.ErrorConstants.*; + +@Component +@Slf4j +public class WalletUtil { + + @Autowired + private WalletRepository walletRepository; + + @Autowired + private EncryptionDecryptionUtil encryptionDecryptionUtil; + + public String decryptWalletKey(String encryptedWalletKey, String pin) { + try { + return encryptionDecryptionUtil.decryptWithPin(encryptedWalletKey, pin); + } catch (Exception e) { + throw new InvalidRequestException(INVALID_PIN.getErrorCode(), INVALID_PIN.getErrorMessage() + " " + e); + } + } + + public String createWallet(String userId, String walletName, String pin) { + SecretKey encryptionKey = KeyGenerationUtil.generateEncryptionKey("AES", 256); + return saveWallet(userId, walletName, pin, encryptionKey, "AES", "encryptWithPin"); + } + + public String saveWallet(String userId, String walletName, String walletPin, SecretKey encryptionKey, String encryptionAlgorithm, String encryptionType) { + + String walletId = UUID.randomUUID().toString(); + WalletMetadata walletMetadata = createWalletMetadata(walletName, encryptionAlgorithm, encryptionType); + String walletKey = encryptionDecryptionUtil.encryptKeyWithPin(encryptionKey, walletPin); + Wallet newWallet = Wallet.builder() + .id(walletId) + .userId(userId) + .walletMetadata(walletMetadata) + .walletKey(walletKey) + .build(); + + List proofSigningKeys = createProofSigningKeys(encryptionKey, newWallet); + newWallet.setProofSigningKeys(proofSigningKeys); + + walletRepository.save(newWallet); + return walletId; + } + + private WalletMetadata createWalletMetadata(String walletName, String encryptionAlgorithm, String encryptionType) { + WalletMetadata walletMetadata = new WalletMetadata(); + walletMetadata.setEncryptionAlgo(encryptionAlgorithm); + walletMetadata.setEncryptionType(encryptionType); + walletMetadata.setName(walletName); + return walletMetadata; + } + + private List createProofSigningKeys(SecretKey encryptionKey, Wallet wallet) { + List proofSigningKeys = new ArrayList<>(); + List algorithms = List.of(SigningAlgorithm.RS256, SigningAlgorithm.ES256, SigningAlgorithm.ES256K, SigningAlgorithm.ED25519); + for (SigningAlgorithm algorithm : algorithms) { + ProofSigningKey signingKey = ProofSigningKeyFactory.createProofSigningKey(algorithm); + signingKey.setWallet(wallet); + signingKey.setEncryptedSecretKey(encryptionDecryptionUtil.encryptWithAES(encryptionKey, + signingKey.getSecretKey().getEncoded())); + proofSigningKeys.add(signingKey); + } + return proofSigningKeys; + } + + public static String getSessionWalletKey(HttpSession session) { + Object key = session.getAttribute(SessionKeys.WALLET_KEY); + if (key == null) + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Wallet key not found in session"); + return key.toString(); + } + + public static void validateWalletId(HttpSession session, String walletIdFromRequest) { + Object sessionWalletId = session.getAttribute(SessionKeys.WALLET_ID); + if (sessionWalletId == null) + throw new InvalidRequestException(WALLET_LOCKED.getErrorCode(), WALLET_LOCKED.getErrorMessage()); + + String walletIdInSession = sessionWalletId.toString(); + if (!walletIdInSession.equals(walletIdFromRequest)) { + throw new InvalidRequestException(INVALID_REQUEST.getErrorCode(), "Invalid Wallet ID. Session and request Wallet ID do not match"); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/mosip/mimoto/util/WalletValidator.java b/src/main/java/io/mosip/mimoto/util/WalletValidator.java new file mode 100644 index 000000000..46e9e8691 --- /dev/null +++ b/src/main/java/io/mosip/mimoto/util/WalletValidator.java @@ -0,0 +1,69 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.UnauthorizedAccessException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * Utility class for validating wallet-related requests. + */ +@Slf4j +@Component +public class WalletValidator { + + @Value("${mosip.inji.user.wallet.pin.validation.regex:^\\d{6}$}") + private String pinRegex; + + @Value("${mosip.inji.user.wallet.name.validation.regex:^[A-Za-z0-9 _.-]{0,50}$}") + private String nameRegex; + + /** + * Validates the user ID. + * + * @param userId The user ID. + * @throws InvalidRequestException If the User ID is invalid. + */ + public void validateUserId(String userId) throws InvalidRequestException { + log.debug("Validating User: {}", userId); + if(userId == null){ + log.warn("User ID is not available in the session"); + throw new UnauthorizedAccessException(ErrorConstants.UNAUTHORIZED_ACCESS.getErrorCode(), "User ID not found in session"); + } + if (StringUtils.isBlank(userId)) { + log.warn("Invalid user ID: null or empty"); + throw new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "User ID cannot be null or empty"); + } + } + + /** + * Validates the Wallet name. + * + * @param name The Wallet name. + * @throws InvalidRequestException If the Wallet name is invalid. + */ + public void validateWalletName(String name) throws InvalidRequestException { + log.debug("Validating Wallet name: {}", name); + if (name != null && !name.matches(nameRegex)) { + log.warn("Invalid Wallet name: {}", name); + throw new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Wallet name must be alphanumeric with allowed special characters"); + } + } + + /** + * Validates the Wallet PIN. + * + * @param pin The Wallet PIN. + * @throws InvalidRequestException If the Wallet PIN is invalid. + */ + public void validateWalletPin(String pin) throws InvalidRequestException { + log.debug("Validating Wallet PIN: {}", pin); + if (pin == null || !pin.matches(pinRegex)) { + log.warn("Invalid PIN: {}", pin); + throw new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "PIN must be numeric with 6 digits"); + } + } +} diff --git a/src/main/resources/application-default.properties b/src/main/resources/application-default.properties new file mode 100644 index 000000000..76df27cb5 --- /dev/null +++ b/src/main/resources/application-default.properties @@ -0,0 +1,69 @@ +#Google OAuth2 configuration +spring.security.oauth2.client.registration.google.client-id=${mosip.injiweb.google.client.id} +spring.security.oauth2.client.registration.google.client-secret=${mosip.injiweb.google.client.secret} +spring.security.oauth2.client.registration.google.scope=profile,email +spring.security.oauth2.client.registration.google.redirect-uri={baseUrl}/oauth2/callback/{registrationId} +spring.security.oauth2.client.registration.google.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.google.client-name=Google +spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/auth?prompt=select_account +spring.security.oauth2.client.provider.google.token-uri=https://oauth2.googleapis.com/token +spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo +spring.security.oauth2.client.provider.google.jwk-set-uri=https://www.googleapis.com/oauth2/v3/certs +spring.security.oauth2.client.provider.google.userNameAttribute=sub +spring.security.oauth2.client.provider.google.nameAttribute=name +spring.security.oauth2.client.provider.google.emailAttribute=email +spring.security.oauth2.client.provider.google.pictureAttribute=picture +spring.security.oauth2.client.provider.google.phoneNumberAttribute=phone_number + +#Inji Web Config +mosip.inji.web.authentication.success.redirect.url=${mosip.inji.web.url}/user/passcode +#Lists issuer IDs limited to one VC in storage +mosip.inji.wallet.issuersWithSingleVcLimit=Mosip +#Specifies how many times the downloaded VC can be verified; -1 means unlimited. +mosip.inji.wallet.vc.validity.count=-1 + +#Database config +spring.datasource.url=jdbc:postgresql://${mosip.mimoto.database.hostname}:${mosip.mimoto.database.port}/inji_mimoto +spring.datasource.username=mimotouser +spring.datasource.password=${db.dbuser.password} +spring.jpa.hibernate.ddl-auto=none +spring.jpa.show-sql=true +spring.datasource.driver-class-name=org.postgresql.Driver +hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +hibernate.hbm2ddl.auto=none +hibernate.jdbc.lob.non_contextual_creation=true +hibernate.show_sql=false +hibernate.format_sql=false +hibernate.connection.charSet=utf8 +hibernate.cache.use_second_level_cache=false +hibernate.cache.use_query_cache=false +hibernate.cache.use_structured_entries=false +hibernate.generate_statistics=false +hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext + +# keymanager database properties +keymanager.persistence.jdbc.driver=org.postgresql.Driver +keymanager_database_url = jdbc:postgresql://${mosip.mimoto.database.hostname}:${mosip.mimoto.database.port}/inji_mimoto +keymanager_database_password=${db.dbuser.password} +keymanager_database_username= mimotouser +keymanager.persistence.jdbc.schema=mimoto + +#Redis configuration +spring.session.store-type=redis +spring.data.redis.host=${redis.host} +spring.data.redis.port=${redis.port} +spring.data.redis.password=${redis.password} +server.servlet.session.timeout=30m +spring.session.redis.namespace=injiweb:session: +spring.data.redis.timeout=10s +spring.data.redis.connect-timeout=10s + +mosip.security.ignore-auth-urls=/safetynet/**,/actuator/**,/swagger-ui/**,/v3/api-docs/**,\ + /allProperties,/credentials/**,/credentialshare/**,/binding-otp,/wallet-binding,/get-token/**,\ + /issuers,/issuers/**,/authorize,/req/otp,/vid,/req/auth/**,/req/individualId/otp,/aid/get-individual-id,\ + /verifiers, /auth/*/token-login + +mosip.inji.app.id=MIMOTO +mosip.inji.encryption.algorithm=AES +mosip.inji.encryption.key.size=256 +mosip.inji.jwt.default.algorithm=RS256 \ No newline at end of file diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties index b6b54c8a5..a97c41190 100644 --- a/src/main/resources/application-local.properties +++ b/src/main/resources/application-local.properties @@ -1,8 +1,8 @@ # MOSIP -mosipbox.public.url=https://api-internal.dev1.mosip.net -public.internet.url=https://api-internal.dev1.mosip.net -public.url=https://api.dev1.mosip.net -keycloak.internal.url=https://iam.dev1.mosip.net/ +mosipbox.public.url=https://api-internal.dev2.mosip.net +public.internet.url=https://api-internal.dev2.mosip.net +public.url=https://api.dev2.mosip.net +keycloak.internal.url=https://iam.dev2.mosip.net/ # Resident service mosip.resident.base.url=${mosipbox.public.url}/resident/v1 @@ -133,6 +133,40 @@ mosip.kernel.pin.length=6 #length of the token id mosip.kernel.tokenid.length=36 +#------------------------------------ Key-manager specific properties -------------------------------------------------- + +#Crypto asymmetric algorithm name +mosip.kernel.crypto.asymmetric-algorithm-name=RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING +#Crypto symmetric algorithm name +mosip.kernel.crypto.symmetric-algorithm-name=AES/GCM/NoPadding +#Encrypted data and encrypted symmetric key separator +mosip.kernel.data-key-splitter=#KEY_SPLITTER# +#GCM tag length +mosip.kernel.crypto.gcm-tag-length=128 +#Hash algo name +mosip.kernel.crypto.hash-algorithm-name=PBKDF2WithHmacSHA512 +#Symmtric key length used in hash +mosip.kernel.crypto.hash-symmetric-key-length=256 +#No of iterations in hash +mosip.kernel.crypto.hash-iteration=10000 +mosip.kernel.keygenerator.symmetric-algorithm-name=AES +mosip.kernel.keygenerator.symmetric-key-length=256 +#Asymmetric algorithm key length +mosip.kernel.keygenerator.asymmetric-key-length=2048 +#Keygenerator asymmetric algorithm name +mosip.kernel.keygenerator.asymmetric-algorithm-name=RSA +mosip.kernel.keymanager.hsm.config-path=/Users/gurpreet.kaur/MOSIP/code/mimoto/certs/oidckeystore.p12 +mosip.kernel.keymanager.hsm.keystore-type=PKCS12 +mosip.kernel.keymanager.hsm.keystore-pass=mosip@123 + + +mosip.kernel.keymanager.certificate.default.common-name=www.example.com +mosip.kernel.keymanager.certificate.default.organizational-unit=EXAMPLE-CENTER +mosip.kernel.keymanager.certificate.default.organization=IIITB +mosip.kernel.keymanager.certificate.default.location=BANGALORE +mosip.kernel.keymanager.certificate.default.state=KA +mosip.kernel.keymanager.certificate.default.country=IN + # log level logging.level.root=WARN logging.level.io.mosip=INFO @@ -205,33 +239,38 @@ mosip.openid.verifiers=mimoto-trusted-verifiers.json mosip.openid.htmlTemplate=credential-template.html mosip.oidc.client.assertion.type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer mosip.oidc.p12.filename=oidckeystore.p12 -mosip.oidc.p12.password=password +mosip.oidc.p12.password=mosip@123 mosip.oidc.p12.path=certs/ #OpenId4VP related Configuration START #Inji Web Config -mosip.inji.web.url=https://injiweb.dev1.mosip.net +mosip.inji.web.url=http://localhost:3004 mosip.inji.web.redirect.url=${mosip.inji.web.url}/authorize mosip.inji.qr.data.size.limit=2000 mosip.inji.qr.code.height=650 mosip.inji.qr.code.width=650 - +mosip.inji.user.wallet.pin.validation.regex=^\\d{4,6}$ +mosip.inji.user.wallet.name.validation.regex=^[A-Za-z0-9 _.-]{1,50}$ #OVP Config mosip.inji.ovp.qrdata.pattern=INJI_OVP://http://localhost:${server.port}/v1/mimoto/authorize?response_type=vp_token&resource=%s&presentation_definition=%s mosip.inji.ovp.redirect.url.pattern=%s#vp_token=%s&presentation_submission=%s mosip.inji.ovp.error.redirect.url.pattern=%s?error=%s&error_description=%s #DataShare Config -mosip.data.share.url=https://datashare-inji.dev1.mosip.net +mosip.data.share.url=http://localhost:8097 mosip.data.share.create.url=${mosip.data.share.url}/v1/datashare/create/static-policyid/static-subscriberid mosip.data.share.create.retry.count=3 -mosip.data.share.get.url.pattern=http://datashare.datashare/v1/datashare/get/static-policyid/static-subscriberid/* +mosip.data.share.get.url.pattern=${mosip.data.share.url}/v1/datashare/get/static-policyid/static-subscriberid/* mosip.security.cors-enable=true #comma separated allowed origins mosip.security.origins=http://localhost:3004 +mosip.security.ignore-auth-urls=/safetynet/**,/actuator/**,/swagger-ui/**,/v3/api-docs/**,\ + /allProperties,/credentials/**,/credentialshare/**,/binding-otp,/wallet-binding,/get-token/**,\ + /issuers,/issuers/**,/authorize,/req/otp,/vid,/req/auth/**,/req/individualId/otp,/aid/get-individual-id,\ + /verifiers, /auth/*/token-login #OpenId4VP related Configuration END @@ -244,3 +283,32 @@ cache.issuers-config.expiry-time-in-min = 60 cache.credential-issuer.authserver-wellknown.expiry-time-in-min = 60 # Default cache expiry time in minutes for others cache types cache.default.expiry-time-in-min = 60 + +spring.security.oauth2.client.registration.google.client-id= +spring.security.oauth2.client.registration.google.client-secret= +spring.security.oauth2.client.registration.google.scope=profile,email +spring.security.oauth2.client.registration.google.redirect-uri={baseUrl}/oauth2/callback/{registrationId} +spring.security.oauth2.client.registration.google.authorization-grant-type=authorization_code +spring.security.oauth2.client.registration.google.client-name=Google +spring.security.oauth2.client.provider.google.authorization-uri=https://accounts.google.com/o/oauth2/auth?prompt=select_account +spring.security.oauth2.client.provider.google.token-uri=https://oauth2.googleapis.com/token +spring.security.oauth2.client.provider.google.user-info-uri=https://www.googleapis.com/oauth2/v3/userinfo +spring.security.oauth2.client.provider.google.jwk-set-uri=https://www.googleapis.com/oauth2/v3/certs + + +spring.datasource.url=jdbc:postgresql://localhost:5432/inji_mimoto +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.jpa.hibernate.ddl-auto=none +spring.jpa.show-sql=true +spring.datasource.driver-class-name=org.postgresql.Driver + +#Redis configuration +spring.session.store-type=redis +spring.data.redis.host=localhost +spring.data.redis.port=6379 +spring.data.redis.password= +spring.data.redis.connect-timeout=10s +server.servlet.session.timeout=30m +spring.session.redis.namespace=injiweb:session: +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file diff --git a/src/main/resources/bootstrap.properties b/src/main/resources/bootstrap.properties index 0c72fa3da..d5834548f 100644 --- a/src/main/resources/bootstrap.properties +++ b/src/main/resources/bootstrap.properties @@ -1,6 +1,6 @@ spring.cloud.config.uri=localhost spring.cloud.config.label=develop -spring.profiles.active=local +spring.profiles.active=default,local spring.cloud.config.name=mimoto,inji spring.application.name=mimoto config.server.file.storage.uri=${spring.cloud.config.uri}/print/${spring.profiles.active}/${spring.cloud.config.label}/ @@ -8,7 +8,7 @@ config.server.file.storage.uri=${spring.cloud.config.uri}/print/${spring.profile management.endpoint.health.show-details=always management.endpoints.web.exposure.include=info,health,refresh -server.port=8088 +server.port=8099 server.servlet.context-path=/v1/mimoto server.tomcat.max-http-response-header-size=65536 health.config.enabled=false diff --git a/src/test/java/io/mosip/mimoto/OAuthLoginTestBootApplication.java b/src/test/java/io/mosip/mimoto/OAuthLoginTestBootApplication.java new file mode 100644 index 000000000..8ca5c56d3 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/OAuthLoginTestBootApplication.java @@ -0,0 +1,2 @@ +package io.mosip.mimoto;public class OAuthLoginTestBootApplication { +} diff --git a/src/test/java/io/mosip/mimoto/config/IssuersValidationConfigTest.java b/src/test/java/io/mosip/mimoto/config/IssuersValidationConfigTest.java new file mode 100644 index 000000000..fb9adce03 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/config/IssuersValidationConfigTest.java @@ -0,0 +1,75 @@ +package io.mosip.mimoto.config; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.dto.IssuersDTO; +import io.mosip.mimoto.service.impl.IssuersServiceImpl; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; +import java.util.List; + +import static io.mosip.mimoto.util.TestUtilities.*; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {IssuersValidationConfig.class, LocalValidatorFactoryBean.class, ObjectMapper.class}) +public class IssuersValidationConfigTest { + + @Autowired + private IssuersValidationConfig issuersValidationConfig; + + @MockBean + private IssuersServiceImpl issuersService; + + IssuersDTO issuers = new IssuersDTO(); + + @Test + public void shouldNotThrowAnyExceptionForValidIssuersConfig() { + try { + issuers.setIssuers(List.of(getIssuerConfigDTO("Issuer8"), getIssuerConfigDTO("Issuer7"))); + when(issuersService.getAllIssuers()).thenReturn(issuers); + + issuersValidationConfig.run(mock(ApplicationArguments.class)); + } catch (Exception e) { + Assert.fail("Exception message: " + e.getMessage()); + } + } + + @Test + public void shouldThrowExceptionIfTheFieldValuesOfIssuerAreNotSatisfyingNotBlankAnnotation() { + try { + issuers.setIssuers(List.of(getIssuerConfigDTOWithInvalidFieldValues("Issuer1", true, false), getIssuerConfigDTOWithInvalidFieldValues("Issuer2",true,false))); + when(issuersService.getAllIssuers()).thenReturn(issuers); + + issuersValidationConfig.run(mock(ApplicationArguments.class)); + } catch (Exception exception) { + String expectedErrorMsg = "\n\nValidation failed in Mimoto-issuers-config.json:\nErrors for issuer at index: 0 with issuerId - \n- authorization_audience must not be blank\n- client_alias must not be blank\n- client_id must not be blank\n- credential_issuer must not be blank\n- credential_issuer_host must not be blank\n- display[0].description must not be blank\n- display[0].language must not be blank\n- display[0].logo.url must be a valid URL\n- display[0].name must not be blank\n- display[0].title must not be blank\n- enabled must not be blank\n- issuer_id must not be blank\n- protocol must not be blank\n- proxy_token_endpoint must not be blank\n- redirect_uri must not be blank\n- token_endpoint must not be blank\n- wellknown_endpoint must not be blank\nErrors for issuer at index: 1 with issuerId - \n- authorization_audience must not be blank\n- client_alias must not be blank\n- client_id must not be blank\n- credential_issuer must not be blank\n- credential_issuer_host must not be blank\n- display[0].description must not be blank\n- display[0].language must not be blank\n- display[0].logo.url must be a valid URL\n- display[0].name must not be blank\n- display[0].title must not be blank\n- enabled must not be blank\n- issuer_id must not be blank\n- protocol must not be blank\n- proxy_token_endpoint must not be blank\n- redirect_uri must not be blank\n- token_endpoint must not be blank\n- wellknown_endpoint must not be blank\n- Duplicate value found for the issuerId. More than one issuer is having the same issuerId\n"; + String actualErrorMsg = exception.getMessage(); + + assertEquals(expectedErrorMsg, actualErrorMsg); + } + } + + @Test + public void shouldThrowExceptionIfTheFieldValuesOfIssuerAreNotSatisfyingUrlAnnotation() { + try { + issuers.setIssuers(List.of(getIssuerConfigDTOWithInvalidFieldValues("Issuer1", false, true), getIssuerConfigDTOWithInvalidFieldValues("Issuer2",false,true))); + when(issuersService.getAllIssuers()).thenReturn(issuers); + + issuersValidationConfig.run(mock(ApplicationArguments.class)); + } catch (Exception exception) { + String expectedErrorMsg = "\n\nValidation failed in Mimoto-issuers-config.json:\nErrors for issuer at index: 0 with issuerId - Issuer1id\n- credential_issuer_host must be a valid URL\n- proxy_token_endpoint must be a valid URL\n- token_endpoint must be a valid URL\n- wellknown_endpoint must be a valid URL\n- TokenEndpoint does not match with the credential issuerId\nErrors for issuer at index: 1 with issuerId - Issuer2id\n- credential_issuer_host must be a valid URL\n- proxy_token_endpoint must be a valid URL\n- token_endpoint must be a valid URL\n- wellknown_endpoint must be a valid URL\n- TokenEndpoint does not match with the credential issuerId\n"; + String actualErrorMsg = exception.getMessage(); + + assertEquals(expectedErrorMsg, actualErrorMsg); + } + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/controller/CredentialsControllerTest.java b/src/test/java/io/mosip/mimoto/controller/CredentialsControllerTest.java index 5710b0ae2..8ee0b89c2 100644 --- a/src/test/java/io/mosip/mimoto/controller/CredentialsControllerTest.java +++ b/src/test/java/io/mosip/mimoto/controller/CredentialsControllerTest.java @@ -2,6 +2,7 @@ import io.mosip.mimoto.dto.idp.TokenResponseDTO; import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.service.IdpService; import io.mosip.mimoto.service.impl.CredentialServiceImpl; import io.mosip.mimoto.util.TestUtilities; import org.apache.http.client.entity.UrlEncodedFormEntity; @@ -39,13 +40,16 @@ public class CredentialsControllerTest { @MockBean private CredentialServiceImpl credentialService; + @MockBean + private IdpService idpService; + private String locale = "test-local", issuer = "test-issuer", credential = "test-credential", requestContent; private TokenResponseDTO tokenResponseDTO; @Before public void setUp() throws Exception { tokenResponseDTO = TestUtilities.getTokenResponseDTO(); - Mockito.when(credentialService.getTokenResponse(Mockito.anyMap(), Mockito.eq(issuer))).thenReturn(tokenResponseDTO); + Mockito.when(idpService.getTokenResponse(Mockito.anyMap())).thenReturn(tokenResponseDTO); requestContent = EntityUtils.toString(new UrlEncodedFormEntity(List.of( new BasicNameValuePair("grant_type", "authorization_code"), new BasicNameValuePair("code", "test-code"), @@ -72,7 +76,7 @@ public void downloadPDFSuccessfully() throws Exception { @Test public void throwExceptionOnFetchingTokenResponseFailure() throws Exception { - Mockito.when(credentialService.getTokenResponse(Mockito.anyMap(), Mockito.eq(issuer))) + Mockito.when(idpService.getTokenResponse(Mockito.anyMap())) .thenThrow(new IdpException("Exception occurred while performing the authorization")); mockMvc.perform(post("/credentials/download") @@ -86,7 +90,7 @@ public void throwExceptionOnFetchingTokenResponseFailure() throws Exception { @Test public void throwExceptionOnFetchingIssuerOrAuthServerWellknownFailureDuringTokenGeneration() throws Exception { - Mockito.when(credentialService.getTokenResponse(Mockito.anyMap(), Mockito.eq(issuer))) + Mockito.when(idpService.getTokenResponse(Mockito.anyMap())) .thenThrow(new ApiNotAccessibleException()); mockMvc.perform(post("/credentials/download") diff --git a/src/test/java/io/mosip/mimoto/controller/IdpControllerTest.java b/src/test/java/io/mosip/mimoto/controller/IdpControllerTest.java index efc949361..233d90153 100644 --- a/src/test/java/io/mosip/mimoto/controller/IdpControllerTest.java +++ b/src/test/java/io/mosip/mimoto/controller/IdpControllerTest.java @@ -4,15 +4,12 @@ import io.mosip.kernel.core.util.JsonUtils; import io.mosip.mimoto.core.http.ResponseWrapper; import io.mosip.mimoto.dto.mimoto.*; -import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.exception.BaseUncheckedException; +import io.mosip.mimoto.exception.IdpException; import io.mosip.mimoto.service.RestClientService; -import io.mosip.mimoto.service.impl.CredentialServiceImpl; import io.mosip.mimoto.service.impl.IdpServiceImpl; import io.mosip.mimoto.service.impl.IssuersServiceImpl; -import io.mosip.mimoto.util.DateUtils; -import io.mosip.mimoto.util.JoseUtil; -import io.mosip.mimoto.util.RequestValidator; -import io.mosip.mimoto.util.RestApiClient; +import io.mosip.mimoto.util.*; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; @@ -28,10 +25,9 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import static io.mosip.mimoto.util.TestUtilities.*; - import java.util.Arrays; +import static io.mosip.mimoto.util.TestUtilities.getTokenResponseDTO; 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; @@ -58,15 +54,9 @@ public class IdpControllerTest { @MockBean public RestClientService restClientService; - @MockBean - private IssuersServiceImpl issuersService; - @MockBean private IdpServiceImpl idpService; - @MockBean - private CredentialServiceImpl credentialService; - @Test public void otpRequestTest() throws Exception { BindingOtpInnerReqDto innerReqDto = new BindingOtpInnerReqDto(); @@ -128,7 +118,7 @@ public void walletBindingTest() throws Exception { @Test public void shouldReturnTokenResponseForValidIssuerAndParams() throws Exception { String issuer = "test-issuer"; - Mockito.when(credentialService.getTokenResponse(Mockito.anyMap(), Mockito.eq(issuer))).thenReturn(getTokenResponseDTO()); + Mockito.when(idpService.getTokenResponse(Mockito.anyMap())).thenReturn(getTokenResponseDTO()); mockMvc.perform(post("/get-token/{issuer}", issuer) .contentType(MediaType.APPLICATION_FORM_URLENCODED) @@ -153,7 +143,7 @@ public void shouldReturnTokenResponseForValidIssuerAndParams() throws Exception @Test public void shouldReturnBadRequestWithErrorIfTokenResponseIsNull() throws Exception { String issuer = "test-issuer"; - Mockito.when(credentialService.getTokenResponse(Mockito.anyMap(), Mockito.eq(issuer))) + Mockito.when(idpService.getTokenResponse(Mockito.anyMap())) .thenThrow(new IdpException("Exception occurred while performing the authorization")); mockMvc.perform(post("/get-token/{issuer}", issuer) diff --git a/src/test/java/io/mosip/mimoto/controller/TokenAuthControllerTest.java b/src/test/java/io/mosip/mimoto/controller/TokenAuthControllerTest.java new file mode 100644 index 000000000..cfa843202 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/controller/TokenAuthControllerTest.java @@ -0,0 +1,142 @@ +package io.mosip.mimoto.controller; + +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import io.mosip.mimoto.service.TokenService; +import io.mosip.mimoto.service.TokenServiceFactory; +import io.mosip.mimoto.util.GlobalExceptionHandler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {TokenAuthController.class, GlobalExceptionHandler.class}) +@AutoConfigureMockMvc(addFilters = false) +@EnableWebMvc +public class TokenAuthControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private TokenServiceFactory tokenServiceFactory; + + @MockBean + private TokenService tokenService; + + private String provider; + private String idToken; + private String authorizationHeader; + + @Before + public void setUp() { + provider = "google"; + idToken = "dummyIdToken"; + authorizationHeader = "Bearer " + idToken; + } + + @Test + public void shouldReturnSuccessForValidTokenAndProvider() throws Exception { + when(tokenServiceFactory.getTokenService(eq(provider))).thenReturn(tokenService); + doNothing().when(tokenService).processToken(eq(idToken), eq(provider), any(), any()); + + mockMvc.perform(post("/auth/{provider}/token-login", provider) + .header("Authorization", authorizationHeader) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(SecurityMockMvcRequestPostProcessors.user("user123").roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$", is("Session created."))); + + verify(tokenServiceFactory).getTokenService(eq(provider)); + verify(tokenService).processToken(eq(idToken), eq(provider), any(), any()); + } + + @Test + public void shouldReturnBadRequestForUnsupportedProvider() throws Exception { + + OAuth2AuthenticationException exception = new OAuth2AuthenticationException( + "INVALID_REQUEST", + String.format("Unsupported provider: %s", provider), + HttpStatus.BAD_REQUEST + ); + + when(tokenServiceFactory.getTokenService(eq(provider))).thenThrow(exception); + + + mockMvc.perform(post("/auth/{provider}/token-login", provider) + .header("Authorization", authorizationHeader) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(SecurityMockMvcRequestPostProcessors.user("user123").roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isUnauthorized()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorMessage", is(String.format("Unsupported provider: %s", provider)))); + + verifyNoInteractions(tokenService); + } + + @Test + public void shouldReturnBadRequestForMissingAuthorizationHeader() throws Exception { + mockMvc.perform(post("/auth/google/token-login", provider) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(SecurityMockMvcRequestPostProcessors.user("user123").roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()); + + verifyNoInteractions(tokenService); + } + + @Test + public void shouldReturnBadRequestForInvalidAuthorizationHeader() throws Exception { + String invalidAuthorizationHeader = "InvalidToken"; + + mockMvc.perform(post("/auth/google/token-login", provider) + .header("Authorization", invalidAuthorizationHeader) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(SecurityMockMvcRequestPostProcessors.user("user123").roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorMessage", is("Bearer ID token required."))); + } + + @Test + public void shouldReturnUnauthorizedForInvalidToken() throws Exception { + when(tokenServiceFactory.getTokenService(eq(provider))).thenReturn(tokenService); + OAuth2AuthenticationException exception = new OAuth2AuthenticationException("invalid_token", "Invalid token format", HttpStatus.BAD_REQUEST); + doThrow(exception).when(tokenService).processToken(eq(idToken), eq(provider), any(), any()); + + mockMvc.perform(post("/auth/{provider}/token-login", provider) + .header("Authorization", authorizationHeader) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .with(SecurityMockMvcRequestPostProcessors.user("user123").roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isUnauthorized()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.errorCode", is("invalid_token"))) + .andExpect(jsonPath("$.errorMessage", is("Invalid token format"))); + verify(tokenServiceFactory).getTokenService(eq(provider)); + verify(tokenService).processToken(eq(idToken), eq(provider), any(), any()); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/controller/UsersControllerTest.java b/src/test/java/io/mosip/mimoto/controller/UsersControllerTest.java new file mode 100644 index 000000000..ae4541c11 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/controller/UsersControllerTest.java @@ -0,0 +1,168 @@ +package io.mosip.mimoto.controller; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.model.UserMetadata; +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.repository.UserMetadataRepository; +import io.mosip.mimoto.service.EncryptionService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.when; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = UsersController.class) +@AutoConfigureMockMvc(addFilters = false) +@EnableWebMvc +class UsersControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockBean + private UserMetadataRepository userMetadataRepository; + + @MockBean + private EncryptionService encryptionService; + + @Autowired + private WebApplicationContext context; + + private MockHttpSession mockHttpSession; + + private static final String PROVIDER_SUBJECT_ID = "test-user"; + private static final String IDENTITY_PROVIDER = "test-provider"; + private static final String ENCRYPTED_DISPLAY_NAME = "encrypted-name"; + private static final String ENCRYPTED_PROFILE_PIC = "encrypted-pic"; + private static final String ENCRYPTED_EMAIL = "encrypted-email"; + private static final String DECRYPTED_DISPLAY_NAME = "Test User"; + private static final String DECRYPTED_PROFILE_PIC = "https://test.com/pic.jpg"; + private static final String DECRYPTED_EMAIL = "test@example.com"; + private static final String WALLET_ID = "test-wallet-id"; + + @BeforeEach + void setUp() { + mockMvc = MockMvcBuilders.webAppContextSetup(context) + .apply(springSecurity()) + .build(); + + MockitoAnnotations.openMocks(this); + mockHttpSession = new MockHttpSession(); + + } + + @Test + void getUserProfileInfoFromCacheSuccess() throws Exception { + UserMetadataDTO userMetadataDTO = new UserMetadataDTO(DECRYPTED_DISPLAY_NAME, DECRYPTED_PROFILE_PIC, DECRYPTED_EMAIL, WALLET_ID); + mockHttpSession.setAttribute(SessionKeys.USER_METADATA, userMetadataDTO); + + mockMvc.perform(get("/users/me") + .with(SecurityMockMvcRequestPostProcessors.user(DECRYPTED_DISPLAY_NAME).roles("USER")) + .session(mockHttpSession)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.displayName").value(DECRYPTED_DISPLAY_NAME)) + .andExpect(jsonPath("$.profilePictureUrl").value(DECRYPTED_PROFILE_PIC)) + .andExpect(jsonPath("$.email").value(DECRYPTED_EMAIL)) + .andExpect(jsonPath("$.walletId").value("test-wallet-id")); + } + + @Test + void getUserProfileInfoFromDatabaseSuccess() throws Exception { + UserMetadata userMetadata = new UserMetadata(); + userMetadata.setDisplayName(ENCRYPTED_DISPLAY_NAME); + userMetadata.setProfilePictureUrl(ENCRYPTED_PROFILE_PIC); + userMetadata.setEmail(ENCRYPTED_EMAIL); + mockHttpSession.setAttribute(SessionKeys.USER_METADATA, null); + mockHttpSession.setAttribute("clientRegistrationId", IDENTITY_PROVIDER); + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(PROVIDER_SUBJECT_ID, IDENTITY_PROVIDER)) + .thenReturn(Optional.of(userMetadata)); + when(encryptionService.decrypt(ENCRYPTED_DISPLAY_NAME)).thenReturn(DECRYPTED_DISPLAY_NAME); + when(encryptionService.decrypt(ENCRYPTED_PROFILE_PIC)).thenReturn(DECRYPTED_PROFILE_PIC); + when(encryptionService.decrypt(ENCRYPTED_EMAIL)).thenReturn(DECRYPTED_EMAIL); + + mockMvc.perform(get("/users/me") + .with(SecurityMockMvcRequestPostProcessors.user(PROVIDER_SUBJECT_ID).roles("USER")) + .session(mockHttpSession)) + .andExpect(status().isOk()) + .andExpect(result -> result.getResponse().getContentAsString()) + .andExpect(jsonPath("$.displayName").value(DECRYPTED_DISPLAY_NAME)) + .andExpect(jsonPath("$.profilePictureUrl").value(DECRYPTED_PROFILE_PIC)) + .andExpect(jsonPath("$.email").value(DECRYPTED_EMAIL)); + // assert that the user metadata is stored in the session + UserMetadataDTO userMetadataDTO = (UserMetadataDTO) mockHttpSession.getAttribute(SessionKeys.USER_METADATA); + assertEquals(DECRYPTED_DISPLAY_NAME, userMetadataDTO.getDisplayName()); + assertEquals(DECRYPTED_PROFILE_PIC, userMetadataDTO.getProfilePictureUrl()); + assertEquals(DECRYPTED_EMAIL, userMetadataDTO.getEmail()); + assertNull(userMetadataDTO.getWalletId()); + } + + @Test + void getUserProfileInfoWhenUserNotFoundInDB() throws Exception { + mockHttpSession.setAttribute(SessionKeys.USER_METADATA, null); + mockHttpSession.setAttribute("clientRegistrationId", IDENTITY_PROVIDER); + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(PROVIDER_SUBJECT_ID, IDENTITY_PROVIDER)) + .thenReturn(Optional.empty()); + + mockMvc.perform(get("/users/me") + .with(SecurityMockMvcRequestPostProcessors.user(DECRYPTED_DISPLAY_NAME).roles("USER")) + .session(mockHttpSession)) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.errorCode").value("unauthorized")) + .andExpect(jsonPath("$.errorMessage").value("User not found. Please check your credentials or login again")); + } + + @Test + void getUserProfileInfoWhenDecryptionExceptionOccursWhileProcessingDataFromDB() throws Exception { + UserMetadata userMetadata = new UserMetadata(); + userMetadata.setDisplayName(ENCRYPTED_DISPLAY_NAME); + userMetadata.setProfilePictureUrl(ENCRYPTED_PROFILE_PIC); + userMetadata.setEmail(ENCRYPTED_EMAIL); + mockHttpSession.setAttribute(SessionKeys.USER_METADATA, null); + mockHttpSession.setAttribute("clientRegistrationId", IDENTITY_PROVIDER); + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(PROVIDER_SUBJECT_ID, IDENTITY_PROVIDER)) + .thenReturn(Optional.of(userMetadata)); + when(encryptionService.decrypt(ENCRYPTED_DISPLAY_NAME)).thenThrow(new DecryptionException("DECRYPTION_ERROR", "Decryption failed")); + + mockMvc.perform(get("/users/me") + .with(SecurityMockMvcRequestPostProcessors.user(PROVIDER_SUBJECT_ID).roles("USER")) + .session(mockHttpSession)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value(ErrorConstants.INTERNAL_SERVER_ERROR.getErrorCode())) + .andExpect(jsonPath("$.errorMessage").value("Failed to process user data")); + } + + @Test + void getUserProfileInfoWhenIdentityProviderIsMissing() throws Exception { + mockHttpSession.setAttribute(SessionKeys.USER_METADATA, null); + mockHttpSession.setAttribute("clientRegistrationId", null); + + mockMvc.perform(get("/users/me") + .with(SecurityMockMvcRequestPostProcessors.user(DECRYPTED_DISPLAY_NAME).roles("USER")) + .session(mockHttpSession)) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.errorCode").value("unauthorized")) + .andExpect(jsonPath("$.errorMessage").value("User not found. Please check your credentials or login again")); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/controller/WalletCredentialsControllerTest.java b/src/test/java/io/mosip/mimoto/controller/WalletCredentialsControllerTest.java new file mode 100644 index 000000000..e5784d3a0 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/controller/WalletCredentialsControllerTest.java @@ -0,0 +1,634 @@ +package io.mosip.mimoto.controller; + +import com.jayway.jsonpath.JsonPath; +import io.mosip.mimoto.dto.VerifiableCredentialRequestDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.VerifiableCredentialResponseDTO; +import io.mosip.mimoto.dto.resident.WalletCredentialResponseDTO; +import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.service.IdpService; +import io.mosip.mimoto.service.WalletCredentialService; +import io.mosip.mimoto.util.GlobalExceptionHandler; +import jakarta.servlet.http.HttpSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.core.io.InputStreamResource; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import java.io.ByteArrayInputStream; +import java.util.Collections; +import java.util.List; + +import static io.mosip.mimoto.exception.ErrorConstants.*; +import static io.mosip.mimoto.util.TestUtilities.createRequestBody; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {WalletCredentialsController.class, GlobalExceptionHandler.class}) +@AutoConfigureMockMvc(addFilters = false) +@EnableWebMvc +public class WalletCredentialsControllerTest { + + @MockBean + private WalletCredentialService walletCredentialService; + + @MockBean + private IdpService idpService; + + @Mock + private HttpSession httpSession; + + @Autowired + private MockMvc mockMvc; + + private VerifiableCredentialResponseDTO verifiableCredentialResponseDTO; + private WalletCredentialResponseDTO walletCredentialResponseDTO; + private final String walletId = "wallet123"; + private final String credentialId = "cred456"; + private final String walletKey = "encodedKey"; + private final String issuer = "issuer1"; + private final String credentialConfigurationId = "type1"; + private final String code = "code"; + private final String grantType = "authorization-code"; + private final String redirectUri = "https://.../redirect"; + private final String codeVerifier = "code-verifier"; + private final String locale = "en"; + VerifiableCredentialRequestDTO verifiableCredentialRequest; + + @Before + public void setup() { + MockitoAnnotations.openMocks(this); + verifiableCredentialRequest = new VerifiableCredentialRequestDTO(); + + verifiableCredentialResponseDTO = VerifiableCredentialResponseDTO.builder() + .issuerDisplayName("issuerName123") + .issuerLogo("issuerLogo") + .credentialTypeDisplayName("credentialType123") + .credentialTypeLogo("credentialTypeLogo") + .credentialId("credentialId123") + .build(); + + walletCredentialResponseDTO = new WalletCredentialResponseDTO(); + walletCredentialResponseDTO.setFileName("credential.pdf"); + walletCredentialResponseDTO.setFileContentStream(new InputStreamResource(new ByteArrayInputStream("test-pdf".getBytes()))); + + when(httpSession.getAttribute("wallet_id")).thenReturn(walletId); + when(httpSession.getAttribute("wallet_key")).thenReturn(walletKey); + } + + // Tests for downloadCredential + @Test + public void shouldDownloadCredentialSuccessfully() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + when(idpService.getTokenResponse(verifiableCredentialRequest)).thenReturn(new TokenResponseDTO()); + when(walletCredentialService.downloadVCAndStoreInDB(eq(issuer), eq(credentialConfigurationId), any(), eq(locale), eq(walletId), eq(walletKey))) + .thenReturn(verifiableCredentialResponseDTO); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", locale) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.issuerDisplayName").value("issuerName123")) + .andExpect(jsonPath("$.issuerLogo").value("issuerLogo")) + .andExpect(jsonPath("$.credentialTypeDisplayName").value("credentialType123")) + .andExpect(jsonPath("$.credentialTypeLogo").value("credentialTypeLogo")) + .andExpect(jsonPath("$.credentialId").value("credentialId123")); + } + + @Test + public void shouldReturnErroResponseWhenRequestedCredentialIsAlreadyAvailableInWallet() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + when(idpService.getTokenResponse(verifiableCredentialRequest)).thenReturn(new TokenResponseDTO()); + when(walletCredentialService.downloadVCAndStoreInDB(eq(issuer), eq(credentialConfigurationId), any(), eq(locale), eq(walletId), eq(walletKey))) + .thenThrow(new InvalidRequestException(CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), "Duplicate credential for issuer and type")); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", locale) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("credential_download_error")) + .andExpect(jsonPath("$.errorMessage").value("Duplicate credential for issuer and type")); + } + + @Test + public void shouldReturnErrorResponseWhenSessionDoesNotHaveWalletIdAndKey() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", locale) + .content(createRequestBody(verifiableCredentialRequest)) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("wallet_locked")) + .andExpect(jsonPath("$.errorMessage").value("Wallet is locked")); + } + + @Test + public void shouldCallServiceWithCorrectParameters() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", "fr") + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)); + + verify(idpService).getTokenResponse(verifiableCredentialRequest); + verify(walletCredentialService + ).downloadVCAndStoreInDB(eq(issuer), eq(credentialConfigurationId), any(), eq("fr"), eq(walletId), eq(walletKey)); + } + + @Test + public void shouldSetDefaultAndProceedWhenOptionalRequestParametersAreNotPassed() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)); + + // Default value for vcStorageExpiryLimit is -1 and locale is "en" + verify(walletCredentialService).downloadVCAndStoreInDB(any(), any(), any(), eq("en"), eq(walletId), eq(walletKey)); + } + + @Test + public void shouldThrowExceptionWhenInvalidLocaleIsPassed() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", "eng") // Three letter language code passed which is not valid + .header("Accept-Language", "invalid") + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Locale must be a 2-letter code")); + } + + @Test + public void shouldThrowInvalidRequestForWalletIdMismatch() throws Exception { + when(httpSession.getAttribute("wallet_id")).thenReturn("differentWalletId"); + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", "differentWalletId") + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Invalid Wallet ID. Session and request Wallet ID do not match")); + } + + @Test + public void shouldThrowInvalidRequestForMissingWalletKey() throws Exception { + when(httpSession.getAttribute("wallet_key")).thenReturn(null); + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Wallet key not found in session")); + } + + @Test + public void shouldThrowInvalidRequestForMissingIssuerAndCredentialConfigurationIdInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(null, null, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header("Accept-Language", "fr") + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(result -> { + String errorMessage = JsonPath.read(result.getResponse().getContentAsString(), "$.errorMessage"); + String[] messages = errorMessage.split(",\\s*"); + + assertEquals(2, messages.length); + assertThat(messages).anySatisfy(msg -> + assertThat(msg.trim()).isEqualTo("issuerId cannot be blank")); + assertThat(messages).anySatisfy(msg -> + assertThat(msg.trim()).isEqualTo("credentialConfigurationId cannot be blank")); + }); + } + + @Test + public void shouldThrowInvalidRequestForMissingIssuerInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(null, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .header("Accept-Language", "fr") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("issuerId cannot be blank")); + } + + @Test + public void shouldThrowInvalidRequestForMissingCredentialConfigurationIdInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(issuer, null, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .header("Accept-Language", "fr") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("credentialConfigurationId cannot be blank")); + } + + @Test + public void shouldThrowInvalidRequestForMissingCodeInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, null, grantType, redirectUri, codeVerifier); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .header("Accept-Language", "fr") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("code cannot be blank")); + } + + @Test + public void shouldThrowInvalidRequestForMissingGrantTypeInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, null, redirectUri, codeVerifier); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .header("Accept-Language", "fr") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("grantType cannot be blank")); + } + + @Test + public void shouldThrowInvalidRequestForMissingRedirectUriInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, null, codeVerifier); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .header("Accept-Language", "fr") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("redirectUri cannot be blank")); + } + + @Test + public void shouldThrowInvalidRequestForMissingCodeVerifierInDownloadCredentialApi() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, null); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .header("Accept-Language", "fr") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("codeVerifier cannot be blank")); + } + + @Test + public void shouldThrowServiceUnavailableForTokenResponseFailure() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + when(idpService.getTokenResponse(verifiableCredentialRequest)) + .thenThrow(new ApiNotAccessibleException("API not accessible")); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isServiceUnavailable()) + .andExpect(jsonPath("$.errorCode").value("credential_download_error")); + } + + @Test + public void shouldThrowServiceUnavailableForExternalServiceFailure() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + when(idpService.getTokenResponse(anyMap())).thenReturn(new TokenResponseDTO()); + when(walletCredentialService.downloadVCAndStoreInDB(anyString(), anyString(), any(), anyString(), anyString(), anyString())) + .thenThrow(new ExternalServiceUnavailableException("Service unavailable", "Service unavailable")); + + mockMvc.perform(post("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .content(createRequestBody(verifiableCredentialRequest)) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isServiceUnavailable()) + .andExpect(jsonPath("$.errorCode").value("Service unavailable")) + .andExpect(jsonPath("$.errorMessage").value("Service unavailable")); + } + + // Tests for fetchAllCredentialsForGivenWallet + + @Test + public void shouldFetchAllCredentialsSuccessfully() throws Exception { + List credentials = Collections.singletonList(verifiableCredentialResponseDTO); + when(walletCredentialService.fetchAllCredentialsForWallet(walletId, walletKey, locale)).thenReturn(credentials); + + mockMvc.perform(get("/wallets/{walletId}/credentials", walletId) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", locale) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].issuerDisplayName").value("issuerName123")) + .andExpect(jsonPath("$[0].issuerLogo").value("issuerLogo")) + .andExpect(jsonPath("$[0].credentialTypeDisplayName").value("credentialType123")) + .andExpect(jsonPath("$[0].credentialId").value("credentialId123")) + .andExpect(jsonPath("$[0].credentialTypeLogo").value("credentialTypeLogo")); + } + + @Test + public void shouldThrowInvalidRequestForInvalidLocale() throws Exception { + mockMvc.perform(get("/wallets/{walletId}/credentials", walletId) + .header("Accept-Language", "invalid") + .param("issuer", issuer) + .param("credentialConfigurationId", credentialConfigurationId) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()); + } + + @Test + public void shouldReturnErrorResponseOnFetchAllCredentialsForGivenWalletWhenSessionDoesNotHaveWalletId() throws Exception { + buildVerifiableCredentialRequest(issuer, credentialConfigurationId, code, grantType, redirectUri, codeVerifier); + mockMvc.perform(get("/wallets/{walletId}/credentials", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", locale) + .sessionAttr("wallet_key", walletKey) + .content(createRequestBody(verifiableCredentialRequest)) + ) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("wallet_locked")) + .andExpect(jsonPath("$.errorMessage").value("Wallet is locked")); + } + + @Test + public void shouldThrowInvalidRequestForMissingWalletKeyInFetchAll() throws Exception { + when(httpSession.getAttribute("wallet_key")).thenReturn(null); + + mockMvc.perform(get("/wallets/{walletId}/credentials", walletId) + .header("Accept-Language", locale) + .accept(MediaType.APPLICATION_JSON) + .sessionAttr("wallet_id", walletId)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Wallet key not found in session")); + } + + @Test + public void shouldThrowInvalidRequestForWalletIdMismatchInFetchAll() throws Exception { + when(httpSession.getAttribute("wallet_id")).thenReturn("differentWalletId"); + + mockMvc.perform(get("/wallets/{walletId}/credentials", walletId) + .header("Accept-Language", locale) + .accept(MediaType.APPLICATION_JSON) + .sessionAttr("wallet_id", "differentWalletId") + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Invalid Wallet ID. Session and request Wallet ID do not match")); + } + + // Tests for getVerifiableCredential + @Test + public void shouldFetchVerifiableCredentialAsPdfSuccessfullyInline() throws Exception { + when(walletCredentialService.fetchVerifiableCredential(walletId, credentialId, walletKey, locale)) + .thenReturn(walletCredentialResponseDTO); + + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .accept(MediaType.APPLICATION_PDF) + .header("Accept-Language", locale) + .param("action", "inline") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isOk()) + .andExpect(header().string("Content-Disposition", "inline; filename=\"credential.pdf\"")) + .andExpect(content().contentType(MediaType.APPLICATION_PDF)); + } + + @Test + public void shouldFetchVerifiableCredentialAsDownload() throws Exception { + when(walletCredentialService.fetchVerifiableCredential(walletId, credentialId, walletKey, locale)) + .thenReturn(walletCredentialResponseDTO); + + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .accept(MediaType.APPLICATION_PDF) + .header("Accept-Language", locale) + .param("action", "download") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isOk()) + .andExpect(header().string("Content-Disposition", "attachment; filename=\"credential.pdf\"")) + .andExpect(content().contentType(MediaType.APPLICATION_PDF)); + } + + @Test + public void shouldThrowInvalidRequestForInvalidAcceptHeader() throws Exception { + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .header("Accept-Language", locale) + .accept(MediaType.APPLICATION_JSON) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Accept header must be application/pdf")); + } + + @Test + public void shouldReturnErrorResponseOnGetVerifiableCredentialWhenSessionDoesNotHaveWalletId() throws Exception { + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .header("Accept-Language", locale) + .accept(MediaType.APPLICATION_PDF) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("wallet_locked")) + .andExpect(jsonPath("$.errorMessage").value("Wallet is locked")); + } + + @Test + public void shouldReturnErrorResponseOnGetVerifiableCredentialWhenSessionDoesNotHaveWalletKey() throws Exception { + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .header("Accept-Language", locale) + .accept(MediaType.APPLICATION_PDF) + .sessionAttr("wallet_id", walletId)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Wallet key not found in session")); + } + + @Test + public void shouldThrowInvalidRequestForInvalidAction() throws Exception { + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .accept(MediaType.APPLICATION_JSON) + .header("Accept-Language", locale) + .param("action", "invalid") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()); + } + + @Test + public void shouldThrowCredentialNotFoundException() throws Exception { + when(walletCredentialService.fetchVerifiableCredential(walletId, credentialId, walletKey, locale)) + .thenThrow(new CredentialNotFoundException(RESOURCE_NOT_FOUND.getErrorCode(), RESOURCE_NOT_FOUND.getErrorMessage())); + + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .accept(MediaType.APPLICATION_PDF) + .header("Accept-Language", locale) + .param("action", "inline") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.errorCode").value("resource_not_found")) + .andExpect(jsonPath("$.errorMessage").value("The requested resource doesn’t exist.")); + } + + @Test + public void shouldThrowInvalidRequestForMissingCredentialId() throws Exception { + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, "")) + .andExpect(status().isNotFound()); + } + + @Test + public void shouldThrowCredentialProcessingExceptionForDecryptionError() throws Exception { + when(walletCredentialService.fetchVerifiableCredential(walletId, credentialId, walletKey, locale)) + .thenThrow(new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "Decryption failed")); + + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .accept(MediaType.APPLICATION_PDF) + .header("Accept-Language", locale) + .param("action", "inline") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value("credential_fetch_error")) + .andExpect(jsonPath("$.errorMessage").value("Decryption failed")); + } + + @Test + public void shouldThrowCredentialProcessingExceptionForCorruptedData() throws Exception { + when(walletCredentialService.fetchVerifiableCredential(walletId, credentialId, walletKey, locale)) + .thenThrow(new CredentialProcessingException(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), "CORRUPTED_DATA")); + + mockMvc.perform(get("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .accept(MediaType.APPLICATION_PDF) + .header("Accept-Language", locale) + .param("action", "inline") + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value(CREDENTIAL_FETCH_EXCEPTION.getErrorCode())) + .andExpect(jsonPath("$.errorMessage").value("CORRUPTED_DATA")); + } + + // Tests for deleteCredential + @Test + public void shouldDeleteCredentialSuccessfully() throws Exception { + mockMvc.perform(delete("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isOk()); + } + + @Test + public void shouldReturnErrorResponseOnDeleteCredentialWhenSessionDoesNotHaveWalletId() throws Exception { + mockMvc.perform(delete("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("wallet_locked")) + .andExpect(jsonPath("$.errorMessage").value("Wallet is locked")); + } + + @Test + public void shouldThrowCredentialNotFoundExceptionWhenDeletingNonExistentCredential() throws Exception { + doThrow(new CredentialNotFoundException(RESOURCE_NOT_FOUND.getErrorCode(), RESOURCE_NOT_FOUND.getErrorMessage())) + .when(walletCredentialService).deleteCredential(credentialId, walletId); + + mockMvc.perform(delete("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .sessionAttr("wallet_id", walletId) + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.errorCode").value("resource_not_found")) + .andExpect(jsonPath("$.errorMessage").value("The requested resource doesn’t exist.")); + } + + @Test + public void shouldThrowInvalidRequestForWalletIdMismatchInDeleteCredential() throws Exception { + when(httpSession.getAttribute("wallet_id")).thenReturn("differentWalletId"); + + mockMvc.perform(delete("/wallets/{walletId}/credentials/{credentialId}", walletId, credentialId) + .sessionAttr("wallet_id", "differentWalletId") + .sessionAttr("wallet_key", walletKey)) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Invalid Wallet ID. Session and request Wallet ID do not match")); + } + + private void buildVerifiableCredentialRequest(String issuer, String credentialConfigurationId, String code, String grantType, String redirectUri, String codeVerifier) { + verifiableCredentialRequest.setIssuer(issuer); + verifiableCredentialRequest.setCredentialConfigurationId(credentialConfigurationId); + verifiableCredentialRequest.setCode(code); + verifiableCredentialRequest.setGrantType(grantType); + verifiableCredentialRequest.setRedirectUri(redirectUri); + verifiableCredentialRequest.setCodeVerifier(codeVerifier); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/controller/WalletsControllerTest.java b/src/test/java/io/mosip/mimoto/controller/WalletsControllerTest.java new file mode 100644 index 000000000..6051bb089 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/controller/WalletsControllerTest.java @@ -0,0 +1,370 @@ +package io.mosip.mimoto.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.dto.CreateWalletRequestDto; +import io.mosip.mimoto.dto.UnlockWalletRequestDto; +import io.mosip.mimoto.dto.WalletResponseDto; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.UnauthorizedAccessException; +import io.mosip.mimoto.service.WalletService; +import io.mosip.mimoto.util.GlobalExceptionHandler; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; + +import java.util.List; + +import static io.mosip.mimoto.util.TestUtilities.createRequestBody; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = {WalletsController.class, GlobalExceptionHandler.class}) +@AutoConfigureMockMvc +@EnableWebMvc +@EnableWebSecurity +public class WalletsControllerTest { + @Autowired + private MockMvc mockMvc; + + @MockBean + private WalletService walletService; + + CreateWalletRequestDto createWalletRequestDto; + + MockHttpSession mockSession; + + String userId, walletName, walletPin, confirmWalletPin, walletId; + + @Before + public void setUp() { + walletName = "My Wallet"; + walletPin = "1234"; + confirmWalletPin = "1234"; + walletId = "walletId123"; + createWalletRequestDto = new CreateWalletRequestDto(); + createWalletRequestDto.setWalletName(walletName); + createWalletRequestDto.setWalletPin(walletPin); + createWalletRequestDto.setConfirmWalletPin(confirmWalletPin); + mockSession = new MockHttpSession(); + mockSession.setAttribute("clientRegistrationId", "google"); + mockSession.setAttribute(SessionKeys.USER_ID, "user123"); + userId = (String) mockSession.getAttribute(SessionKeys.USER_ID); + } + + // Test for creating a wallet + + @Test + public void shouldReturnWalletIdForSuccessfulWalletCreation() throws Exception { + when(walletService.createWallet(userId, walletName, walletPin, confirmWalletPin)).thenReturn(new WalletResponseDto(walletId, walletName)); + + mockMvc.perform(post("/wallets") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(createWalletRequestDto)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isOk()) + .andExpect(content().json("{\"walletId\": \"walletId123\",\"walletName\": \"My Wallet\"}", true)); + } + + @Test + public void shouldThrowExceptionIfAnyErrorOccurredWhenCreatingWallet() throws Exception { + when(walletService.createWallet(userId, walletName, walletPin, confirmWalletPin)) + .thenThrow(new RuntimeException("Exception occurred when creating Wallet for given userId")); + + mockMvc.perform(post("/wallets") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(createWalletRequestDto)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value("internal_server_error")) + .andExpect(jsonPath("$.errorMessage").value("We are unable to process request now")); + } + + @Test + public void shouldThrowExceptionIfReceivedPINAndConfirmPINDoNotMatch() throws Exception { + createWalletRequestDto.setConfirmWalletPin("2345"); + when(walletService.createWallet(userId, walletName, walletPin, "2345")) + .thenThrow(new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Entered PIN and Confirm PIN do not match")); + + mockMvc.perform(post("/wallets") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(createWalletRequestDto)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Entered PIN and Confirm PIN do not match")); + } + + @Test + public void shouldReturnUnauthorizedWhenUserIdIsMissingForCreateWallet() throws Exception { + mockSession.clearAttributes(); + MockHttpSession sessionWithoutUserId = mockSession; + doThrow(new UnauthorizedAccessException(ErrorConstants.UNAUTHORIZED_ACCESS.getErrorCode(), "User ID not found in session")).when(walletService).createWallet(null, walletName, walletPin, confirmWalletPin); + + mockMvc.perform(post("/wallets") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(createWalletRequestDto)) + .session(sessionWithoutUserId) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.errorCode").value("unauthorized")) + .andExpect(jsonPath("$.errorMessage").value("User ID not found in session")); + } + + // Test for fetching wallets + + @Test + public void shouldReturnListOfWalletIDsForValidUserId() throws Exception { + List wallets = List.of(new WalletResponseDto("wallet1", "Wallet1"), + new WalletResponseDto("wallet2", "Wallet2")); + when(walletService.getWallets(userId)).thenReturn(wallets); + + mockMvc.perform(get("/wallets") + .accept(MediaType.APPLICATION_JSON) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isOk()) + .andExpect(content().string("[{\"walletId\":\"wallet1\",\"walletName\":\"Wallet1\"},{\"walletId\":\"wallet2\",\"walletName\":\"Wallet2\"}]")); + } + + @Test + public void shouldThrowExceptionIfAnyErrorOccurredWhileFetchingWalletsForGivenUserId() throws Exception { + when(walletService.getWallets(userId)).thenThrow(new RuntimeException("Exception occurred when fetching the wallets for given userId")); + + mockMvc.perform(get("/wallets") + .session(mockSession) + .accept(MediaType.APPLICATION_JSON) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value("internal_server_error")) + .andExpect(jsonPath("$.errorMessage").value("We are unable to process request now")); + } + + // Test for deleting a wallet + + @Test + public void shouldDeleteWalletSuccessfully() throws Exception { + mockMvc.perform(delete(String.format("/wallets/%s", walletId)) + .accept(MediaType.APPLICATION_JSON) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isOk()); + } + + @Test + public void shouldClearSessionAttributesWhenDeletingCurrentWallet() throws Exception { + mockSession.setAttribute(SessionKeys.WALLET_ID, walletId); + mockSession.setAttribute(SessionKeys.WALLET_KEY, "walletKey123"); + + mockMvc.perform(delete(String.format("/wallets/%s", walletId)) + .accept(MediaType.APPLICATION_JSON) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isOk()); + + // Session attributes should be cleared + assertEquals(null, mockSession.getAttribute(SessionKeys.WALLET_ID)); + assertEquals(null, mockSession.getAttribute(SessionKeys.WALLET_KEY)); + } + + @Test + public void shouldReturnUnauthorizedWhenUserIdIsMissingForDeleteWallet() throws Exception { + mockSession.clearAttributes(); + MockHttpSession sessionWithoutUserId = mockSession; + doThrow(new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "User ID cannot be null or empty")).when(walletService).deleteWallet(null, walletId); + + mockMvc.perform(delete(String.format("/wallets/%s", walletId)) + .accept(MediaType.APPLICATION_JSON) + .session(sessionWithoutUserId) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("User ID cannot be null or empty")); + } + + @Test + public void shouldReturnBadRequestWhenInvalidRequestExceptionIsThrownByService() throws Exception { + doThrow(new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Wallet not found")).when(walletService).deleteWallet(userId, walletId); + + mockMvc.perform(delete("/wallets/" + walletId) + .accept(MediaType.APPLICATION_JSON) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user("user123").roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Wallet not found")); + } + + // Unlock wallet tests + + @Test + public void shouldUnlockWalletSuccessfully() throws Exception { + UnlockWalletRequestDto unlockRequest = new UnlockWalletRequestDto(); + unlockRequest.setWalletPin(walletPin); + + when(walletService.unlockWallet(walletId, walletPin, mockSession)).thenReturn(new WalletResponseDto(walletId, walletName)); + + mockMvc.perform(post("/wallets/{walletId}/unlock", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(unlockRequest)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.walletId").value(walletId)) + .andReturn(); + } + + @Test + public void shouldThrowExceptionWhenUserIdIsMissingForUnlockWallet() throws Exception { + UnlockWalletRequestDto unlockRequest = new UnlockWalletRequestDto(); + unlockRequest.setWalletPin(walletPin); + when(walletService.unlockWallet(walletId, walletPin, mockSession)) + .thenThrow(new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "User ID cannot be null or empty")); + mockSession.clearAttributes(); + MockHttpSession sessionWithoutUserId = mockSession; + sessionWithoutUserId.setAttribute("clientRegistrationId", "google"); + + mockMvc.perform(post("/wallets/{walletId}/unlock", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(unlockRequest)) + .session(sessionWithoutUserId) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("User ID cannot be null or empty")); + } + + @Test + public void shouldThrowExceptionWhenWalletNotFoundForUnlock() throws Exception { + String nonExistentWalletId = "nonExistentWalletId"; + UnlockWalletRequestDto unlockRequest = new UnlockWalletRequestDto(); + unlockRequest.setWalletPin(walletPin); + + when(walletService.unlockWallet(nonExistentWalletId, walletPin, mockSession)) + .thenThrow(new InvalidRequestException(ErrorConstants.INVALID_REQUEST.getErrorCode(), "Wallet not found")); + + mockMvc.perform(post("/wallets/{walletId}/unlock", nonExistentWalletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(unlockRequest)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_request")) + .andExpect(jsonPath("$.errorMessage").value("Wallet not found")); + } + + @Test + public void shouldThrowExceptionWhenInvalidPinProvidedForUnlock() throws Exception { + String invalidPin = "invalidPin"; + UnlockWalletRequestDto unlockRequest = new UnlockWalletRequestDto(); + unlockRequest.setWalletPin(invalidPin); + + when(walletService.unlockWallet(walletId, invalidPin, mockSession)) + .thenThrow(new InvalidRequestException("invalid_pin", "Invalid PIN or wallet key provided")); + + mockMvc.perform(post("/wallets/{walletId}/unlock", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(unlockRequest)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("invalid_pin")) + .andExpect(jsonPath("$.errorMessage").value("Invalid PIN or wallet key provided")); + } + + @Test + public void shouldThrowExceptionForServerErrorDuringUnlock() throws Exception { + UnlockWalletRequestDto unlockRequest = new UnlockWalletRequestDto(); + unlockRequest.setWalletPin(walletPin); + + when(walletService.unlockWallet(walletId, walletPin, mockSession)) + .thenThrow(new RuntimeException("Error decrypting wallet key")); + + mockMvc.perform(post("/wallets/{walletId}/unlock", walletId) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(unlockRequest)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value("internal_server_error")) + .andExpect(jsonPath("$.errorMessage").value("We are unable to process request now")); + } + + @Test + public void shouldThrowExceptionIfAnyErrorOccurredWhileFetchingWalletDataForGivenUserIdAndWalletId() throws Exception { + when(walletService.unlockWallet(walletId, walletPin, mockSession)).thenThrow(new RuntimeException("Exception occurred when fetching the wallet data for given walletId and userId")); + + mockMvc.perform(post(String.format("/wallets/%s/unlock", walletId)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(new ObjectMapper().writeValueAsString(createWalletRequestDto)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isInternalServerError()) + .andExpect(jsonPath("$.errorCode").value("internal_server_error")) + .andExpect(jsonPath("$.errorMessage").value("We are unable to process request now")); + } + + @Test + public void shouldReturnWalletLockedErrorDetailsWhenWalletIDIsMissingInSession() throws Exception { + mockSession.removeAttribute(SessionKeys.WALLET_ID); + when(walletService.unlockWallet(walletId, walletPin, mockSession)). + thenThrow(new InvalidRequestException("wallet_locked", "Wallet is locked")); + + mockMvc.perform(post(String.format("/wallets/%s/unlock", walletId)) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + .content(createRequestBody(createWalletRequestDto)) + .session(mockSession) + .with(SecurityMockMvcRequestPostProcessors.user(userId).roles("USER")) + .with(SecurityMockMvcRequestPostProcessors.csrf())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.errorCode").value("wallet_locked")) + .andExpect(jsonPath("$.errorMessage").value("Wallet is locked")); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/security/oauth2/CustomOAuth2UserServiceTest.java b/src/test/java/io/mosip/mimoto/security/oauth2/CustomOAuth2UserServiceTest.java new file mode 100644 index 000000000..e0fbe4e52 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/security/oauth2/CustomOAuth2UserServiceTest.java @@ -0,0 +1,253 @@ +package io.mosip.mimoto.security.oauth2; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.config.oauth2.OAuth2ProviderProperties; +import io.mosip.mimoto.dto.ProviderDataConfig; +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.service.UserMetadataService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.time.Instant; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class CustomOAuth2UserServiceTest { + + @Mock + private OAuth2ProviderProperties providerProperties; + + @Mock + private UserMetadataService userMetadataService; + + private CustomOAuth2UserService customOAuth2UserService; + + private ClientRegistration clientRegistration; + private OAuth2AccessToken accessToken; + private OAuth2UserRequest oAuth2UserRequest; + private Map providersConfig; + + private static class TestableOAuth2UserService extends CustomOAuth2UserService { + private final OAuth2User testUser; + + public TestableOAuth2UserService(OAuth2ProviderProperties properties, UserMetadataService service, OAuth2User testUser) { + super(properties, service); + this.testUser = testUser; + } + + @Override + protected OAuth2User loadFromProvider(OAuth2UserRequest userRequest) { + return testUser; + } + } + + @BeforeEach + void setUp() { + clientRegistration = ClientRegistration.withRegistrationId("test-provider") + .clientId("test-client-id") + .clientSecret("test-client-secret") + .authorizationUri("https://example.com/oauth/authorize") + .tokenUri("https://example.com/oauth/token") + .userInfoUri("https://example.com/oauth/userinfo") + .userNameAttributeName("sub") + .clientName("Test Provider") + .redirectUri("{baseUrl}/oauth2/callback/{registrationId}") + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .build(); + + accessToken = new OAuth2AccessToken( + OAuth2AccessToken.TokenType.BEARER, + "test-token", + Instant.now(), + Instant.now().plusSeconds(3600) + ); + + oAuth2UserRequest = new OAuth2UserRequest(clientRegistration, accessToken); + providersConfig = new HashMap<>(); + } + + @Test + void testLoadUserSuccessfulFlow() throws DecryptionException { + // Arrange + Map userAttributes = new HashMap<>(); + userAttributes.put("sub", "test-subject"); + userAttributes.put("name", "Test User"); + userAttributes.put("email", "test@example.com"); + userAttributes.put("phone", "123-456-7890"); + userAttributes.put("picture", "https://example.com/avatar.jpg"); + + OAuth2User defaultUser = new DefaultOAuth2User( + Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")), + userAttributes, + "sub" + ); + + ProviderDataConfig providerConfig = new ProviderDataConfig(); + providerConfig.setUserNameAttribute("sub"); + providerConfig.setNameAttribute("name"); + providerConfig.setEmailAttribute("email"); + providerConfig.setPhoneNumberAttribute("phone"); + providerConfig.setPictureAttribute("picture"); + + providersConfig.put("test-provider", providerConfig); + when(providerProperties.getProvider()).thenReturn(providersConfig); + when(userMetadataService.updateOrCreateUserMetadata(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn("test-user-id"); + + customOAuth2UserService = new TestableOAuth2UserService(providerProperties, userMetadataService, defaultUser); + + // Act + OAuth2User actualUser = customOAuth2UserService.loadUser(oAuth2UserRequest); + + // Assert + assertNotNull(actualUser); + assertEquals("Test User", actualUser.getAttributes().get("name")); + assertEquals("test@example.com", actualUser.getAttributes().get("email")); + assertEquals("123-456-7890", actualUser.getAttributes().get("phone")); + assertEquals("https://example.com/avatar.jpg", actualUser.getAttributes().get("picture")); + assertEquals("test-user-id", actualUser.getAttributes().get(SessionKeys.USER_ID)); + + verify(userMetadataService).updateOrCreateUserMetadata( + "test-subject", + "test-provider", + "Test User", + "https://example.com/avatar.jpg", + "test@example.com" + ); + } + + @Test + void testLoadUserWithNestedPictureData() throws DecryptionException { + // Arrange + Map pictureMap = new HashMap<>(); + Map dataMap = new HashMap<>(); + dataMap.put("url", "https://example.com/nested.jpg"); + pictureMap.put("data", dataMap); + + Map userAttributes = new HashMap<>(); + userAttributes.put("sub", "nested-subject"); + userAttributes.put("name", "Nested User"); + userAttributes.put("email", "nested@example.com"); + userAttributes.put("phone", "9999999999"); + userAttributes.put("picture", pictureMap); + + OAuth2User nestedPictureUser = new DefaultOAuth2User( + Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")), + userAttributes, + "sub" + ); + + ProviderDataConfig providerConfig = new ProviderDataConfig(); + providerConfig.setUserNameAttribute("sub"); + providerConfig.setNameAttribute("name"); + providerConfig.setEmailAttribute("email"); + providerConfig.setPhoneNumberAttribute("phone"); + providerConfig.setPictureAttribute("picture"); + + providersConfig.put("test-provider", providerConfig); + when(providerProperties.getProvider()).thenReturn(providersConfig); + when(userMetadataService.updateOrCreateUserMetadata(anyString(), anyString(), anyString(), anyString(), anyString())) + .thenReturn("nested-user-id"); + + customOAuth2UserService = new TestableOAuth2UserService(providerProperties, userMetadataService, nestedPictureUser); + + // Act + OAuth2User actualUser = customOAuth2UserService.loadUser(oAuth2UserRequest); + + // Assert + assertEquals("https://example.com/nested.jpg", actualUser.getAttributes().get("picture")); + assertEquals("nested-user-id", actualUser.getAttributes().get(SessionKeys.USER_ID)); + } + + @Test + void testLoadUserThrowsRuntimeExceptionOnDecryptionFailure() throws DecryptionException { + // Arrange + Map userAttributes = new HashMap<>(); + userAttributes.put("sub", "error-subject"); + userAttributes.put("name", "Error User"); + userAttributes.put("email", "error@example.com"); + userAttributes.put("phone", "0000000000"); + userAttributes.put("picture", "https://example.com/error.jpg"); + + OAuth2User errorUser = new DefaultOAuth2User( + Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")), + userAttributes, + "sub" + ); + + ProviderDataConfig providerConfig = new ProviderDataConfig(); + providerConfig.setUserNameAttribute("sub"); + providerConfig.setNameAttribute("name"); + providerConfig.setEmailAttribute("email"); + providerConfig.setPhoneNumberAttribute("phone"); + providerConfig.setPictureAttribute("picture"); + + providersConfig.put("test-provider", providerConfig); + when(providerProperties.getProvider()).thenReturn(providersConfig); + when(userMetadataService.updateOrCreateUserMetadata(any(), any(), any(), any(), any())) + .thenThrow(new DecryptionException(ErrorConstants.DECRYPTION_FAILED.getErrorCode(), "Decryption failed")); + + customOAuth2UserService = new TestableOAuth2UserService(providerProperties, userMetadataService, errorUser); + + // Act & Assert + RuntimeException exception = assertThrows(RuntimeException.class, + () -> customOAuth2UserService.loadUser(oAuth2UserRequest)); + + assertTrue(exception.getCause() instanceof DecryptionException); + assertEquals("Metadata update failed", exception.getMessage()); + } + + @Test + void testLoadUserWithMissingOptionalAttributes() throws DecryptionException { + // Arrange + Map userAttributes = new HashMap<>(); + userAttributes.put("sub", "minimal-subject"); + userAttributes.put("name", "Minimal User"); + + OAuth2User minimalUser = new DefaultOAuth2User( + Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")), + userAttributes, + "sub" + ); + + ProviderDataConfig providerConfig = new ProviderDataConfig(); + providerConfig.setUserNameAttribute("sub"); + providerConfig.setNameAttribute("name"); + providerConfig.setEmailAttribute("email"); + providerConfig.setPhoneNumberAttribute("phone"); + providerConfig.setPictureAttribute("picture"); + + providersConfig.put("test-provider", providerConfig); + when(providerProperties.getProvider()).thenReturn(providersConfig); + when(userMetadataService.updateOrCreateUserMetadata(any(), any(), any(), any(), any())) + .thenReturn("minimal-user-id"); + + customOAuth2UserService = new TestableOAuth2UserService(providerProperties, userMetadataService, minimalUser); + + // Act + OAuth2User actualUser = customOAuth2UserService.loadUser(oAuth2UserRequest); + + // Assert + assertEquals("Minimal User", actualUser.getAttributes().get("name")); + assertNull(actualUser.getAttributes().get("email")); + assertNull(actualUser.getAttributes().get("phone")); + assertNull(actualUser.getAttributes().get("picture")); + } +} diff --git a/src/test/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationSuccessHandlerTest.java b/src/test/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationSuccessHandlerTest.java new file mode 100644 index 000000000..05a10693b --- /dev/null +++ b/src/test/java/io/mosip/mimoto/security/oauth2/OAuth2AuthenticationSuccessHandlerTest.java @@ -0,0 +1,121 @@ +package io.mosip.mimoto.security.oauth2; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.io.IOException; +import java.lang.reflect.Field; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class OAuth2AuthenticationSuccessHandlerTest { + + @InjectMocks + private OAuth2AuthenticationSuccessHandler successHandler; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private HttpSession session; + + @Mock + private OAuth2AuthenticationToken oauth2Token; + + @Mock + private OAuth2User oauth2User; + + private static final String AUTH_SUCCESS_REDIRECT_URL = "https://example.com/passcode"; + private static final String CLIENT_REGISTRATION_ID = "google"; + private static final String DISPLAY_NAME = "John Doe"; + private static final String PROFILE_PICTURE_URL = "https://example.com/profile.jpg"; + private static final String EMAIL = "john.doe@example.com"; + private static final String USER_ID = "user456"; + + @Before + public void setUp() throws Exception { + Field injiWebUrlField = OAuth2AuthenticationSuccessHandler.class.getDeclaredField("authenticationSuccessRedirectUrl"); + injiWebUrlField.setAccessible(true); + injiWebUrlField.set(successHandler, AUTH_SUCCESS_REDIRECT_URL); + + when(request.getSession(false)).thenReturn(session); + } + + @Test + public void testOnAuthenticationSuccessSetsAttributesAndRedirects() throws Exception { + when(oauth2Token.getAuthorizedClientRegistrationId()).thenReturn(CLIENT_REGISTRATION_ID); + when(oauth2Token.getPrincipal()).thenReturn(oauth2User); + when(oauth2User.getAttribute("name")).thenReturn(DISPLAY_NAME); + when(oauth2User.getAttribute("picture")).thenReturn(PROFILE_PICTURE_URL); + when(oauth2User.getAttribute("email")).thenReturn(EMAIL); + when(oauth2User.getAttribute("userId")).thenReturn(USER_ID); + + successHandler.onAuthenticationSuccess(request, response, oauth2Token); + + verify(session).setAttribute("clientRegistrationId", CLIENT_REGISTRATION_ID); + verify(session).setAttribute(SessionKeys.USER_ID, USER_ID); + verify(response).sendRedirect(AUTH_SUCCESS_REDIRECT_URL); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserMetadataDTO.class); + verify(session).setAttribute(eq(SessionKeys.USER_METADATA), captor.capture()); + + UserMetadataDTO dto = captor.getValue(); + assertEquals(DISPLAY_NAME, dto.getDisplayName()); + assertEquals(PROFILE_PICTURE_URL, dto.getProfilePictureUrl()); + assertEquals(EMAIL, dto.getEmail()); + } + + @Test + public void testOnAuthenticationSuccessWithNullAttributes() throws Exception { + when(oauth2Token.getAuthorizedClientRegistrationId()).thenReturn(CLIENT_REGISTRATION_ID); + when(oauth2Token.getPrincipal()).thenReturn(oauth2User); + when(oauth2User.getAttribute("name")).thenReturn(null); + when(oauth2User.getAttribute("picture")).thenReturn(null); + when(oauth2User.getAttribute("email")).thenReturn(null); + when(oauth2User.getAttribute("userId")).thenReturn(USER_ID); + + successHandler.onAuthenticationSuccess(request, response, oauth2Token); + + verify(session).setAttribute("clientRegistrationId", CLIENT_REGISTRATION_ID); + verify(session).setAttribute(SessionKeys.USER_ID, USER_ID); + verify(response).sendRedirect(AUTH_SUCCESS_REDIRECT_URL); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserMetadataDTO.class); + verify(session).setAttribute(eq(SessionKeys.USER_METADATA), captor.capture()); + + UserMetadataDTO dto = captor.getValue(); + assertNull(dto.getDisplayName()); + assertNull(dto.getProfilePictureUrl()); + assertNull(dto.getEmail()); + } + + @Test + public void testOnAuthenticationSuccessWithNullSessionThrows() throws IOException { + when(request.getSession(false)).thenReturn(null); + + ServletException thrown = assertThrows(ServletException.class, () -> + successHandler.onAuthenticationSuccess(request, response, oauth2Token) + ); + + assertEquals("Session not available", thrown.getMessage()); + verify(response, never()).sendRedirect(anyString()); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/CredentialPDFGeneratorServiceTest.java b/src/test/java/io/mosip/mimoto/service/CredentialPDFGeneratorServiceTest.java new file mode 100644 index 000000000..d173aa604 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/CredentialPDFGeneratorServiceTest.java @@ -0,0 +1,281 @@ +package io.mosip.mimoto.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.dto.BackgroundImageDTO; +import io.mosip.mimoto.dto.DisplayDTO; +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.LogoDTO; +import io.mosip.mimoto.dto.mimoto.*; +import io.mosip.mimoto.dto.openid.presentation.PresentationDefinitionDTO; +import io.mosip.mimoto.model.QRCodeType; +import io.mosip.mimoto.service.impl.PresentationServiceImpl; +import io.mosip.mimoto.util.Utilities; +import io.mosip.pixelpass.PixelPass; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class CredentialPDFGeneratorServiceTest { + + @Mock private ObjectMapper objectMapper; + @Mock private PresentationServiceImpl presentationService; + @Mock private Utilities utilities; + @Mock private PixelPass pixelPass; + + @InjectMocks + private CredentialPDFGeneratorService credentialPDFGeneratorService; + + private VCCredentialResponse vcCredentialResponse; + private IssuerDTO issuerDTO; + private CredentialsSupportedResponse credentialsSupportedResponse; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(credentialPDFGeneratorService, "ovpQRDataPattern", "test-pattern-%s-%s"); + ReflectionTestUtils.setField(credentialPDFGeneratorService, "qrCodeHeight", 500); + ReflectionTestUtils.setField(credentialPDFGeneratorService, "qrCodeWidth", 500); + ReflectionTestUtils.setField(credentialPDFGeneratorService, "allowedQRDataSizeLimit", 2000); + ReflectionTestUtils.setField(credentialPDFGeneratorService, "pixelPass", pixelPass); + + setupTestData(); + } + + private void setupTestData() { + Map subjectData = new HashMap<>(); + subjectData.put("name", "John Doe"); + subjectData.put("dateOfBirth", "1990-01-01"); + subjectData.put("face", "base64-encoded-image"); + + VCCredentialProperties vcProperties = VCCredentialProperties.builder() + .credentialSubject(subjectData) + .type(List.of("VerifiableCredential")) + .build(); + + vcCredentialResponse = VCCredentialResponse.builder() + .format("ldp_vc") + .credential(vcProperties) + .build(); + + issuerDTO = new IssuerDTO(); + issuerDTO.setIssuer_id("test-issuer"); + issuerDTO.setQr_code_type(QRCodeType.OnlineSharing); + + DisplayDTO display = new DisplayDTO(); + display.setName("Issuer Display Name"); + display.setTitle("Issuer Title"); + display.setDescription("Issuer Description"); + display.setLanguage("en"); + + LogoDTO logo = new LogoDTO(); + logo.setUrl("https://example.com/logo.png"); + display.setLogo(logo); + + issuerDTO.setDisplay(List.of(display)); + + credentialsSupportedResponse = new CredentialsSupportedResponse(); + + Map credentialSubjectMap = new HashMap<>(); + credentialSubjectMap.put("name", createDisplay("Full Name")); + credentialSubjectMap.put("dateOfBirth", createDisplay("Date of Birth")); + + CredentialDefinitionResponseDto definition = new CredentialDefinitionResponseDto(); + definition.setCredentialSubject(credentialSubjectMap); + credentialsSupportedResponse.setCredentialDefinition(definition); + credentialsSupportedResponse.setOrder(new ArrayList<>(List.of("name", "dateOfBirth"))); + + CredentialSupportedDisplayResponse credDisplay = new CredentialSupportedDisplayResponse(); + credDisplay.setBackgroundColor("#FFFFFF"); + credDisplay.setTextColor("#000000"); + credDisplay.setName("Test Credential"); + BackgroundImageDTO bgImage = new BackgroundImageDTO(); + bgImage.setUri("https://example.com/bg.png"); + credDisplay.setBackgroundImage(bgImage); + + credentialsSupportedResponse.setDisplay(List.of(credDisplay)); + } + + private CredentialDisplayResponseDto createDisplay(String name) { + CredentialDisplayResponseDto dto = new CredentialDisplayResponseDto(); + CredentialIssuerDisplayResponse display = new CredentialIssuerDisplayResponse(); + display.setName(name); + display.setLocale("en"); + dto.setDisplay(List.of(display)); + return dto; + } + + @Test + void testGeneratePdfForVerifiableCredentials() throws Exception { + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + PresentationDefinitionDTO presentationDef = new PresentationDefinitionDTO(); + when(presentationService.constructPresentationDefinition(any())) + .thenReturn(presentationDef); + when(objectMapper.writeValueAsString(presentationDef)) + .thenReturn("{\"presentation\":\"definition\"}"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "https://example.com/share", "2025-12-31", "en"); + + assertNotNull(result); + } + + @Test + void testGeneratePdfForEmbeddedVCQR() throws Exception { + issuerDTO.setQr_code_type(QRCodeType.EmbeddedVC); + when(objectMapper.writeValueAsString(any())).thenReturn("{\"credential\":\"data\"}"); + when(pixelPass.generateQRData(anyString(), anyString())).thenReturn("generated-qr-data"); + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + + + try (MockedStatic mocked = mockStatic(Utilities.class)) { + mocked.when(() -> Utilities.encodeToString(any(), anyString())) + .thenReturn("base64-encoded-qr"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "", "", "en"); + + verify(pixelPass).generateQRData(anyString(), anyString()); + verify(presentationService, never()).constructPresentationDefinition(any()); + assertNotNull(result); + } + } + + @Test + void testGeneratePdfShouldGeneratePresentationDefinitionForOnlineSharingQrTypeWithNonEmptyDataShareUrl() throws Exception { + issuerDTO.setQr_code_type(QRCodeType.OnlineSharing); + when(objectMapper.writeValueAsString(any())).thenReturn("{\"credential\":\"data\"}"); + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + + + try (MockedStatic mocked = mockStatic(Utilities.class)) { + mocked.when(() -> Utilities.encodeToString(any(), anyString())) + .thenReturn("base64-encoded-qr"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "http://datashare.datashare/v1/datashare/get/static-policyid/static-subscriberid/test", "", "en"); + + verify(presentationService).constructPresentationDefinition(any()); + verify(pixelPass, never()).generateQRData(anyString(), anyString()); + assertNotNull(result); + } + } + + @Test + void testGeneratePdfShouldGenerateEmbeddedVCForOnlineSharingQrTypeWithEmptyDataShareUrl() throws Exception { + issuerDTO.setQr_code_type(QRCodeType.OnlineSharing); + when(objectMapper.writeValueAsString(any())).thenReturn("{\"credential\":\"data\"}"); + when(pixelPass.generateQRData(anyString(), anyString())).thenReturn("generated-qr-data"); + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + + + try (MockedStatic mocked = mockStatic(Utilities.class)) { + mocked.when(() -> Utilities.encodeToString(any(), anyString())) + .thenReturn("base64-encoded-qr"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "", "", "en"); + + verify(pixelPass).generateQRData(anyString(), anyString()); + verify(presentationService, never()).constructPresentationDefinition(any()); + assertNotNull(result); + } + } + + @Test + void testHandleMapWithListValue() throws Exception { + Map skills = new HashMap<>(); + skills.put("skills", List.of("Java", "Spring")); + vcCredentialResponse.getCredential().setCredentialSubject(skills); + credentialsSupportedResponse.getCredentialDefinition().getCredentialSubject() + .put("skills", createDisplay("Skills")); + credentialsSupportedResponse.setOrder(List.of("skills")); + + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + PresentationDefinitionDTO presentationDef = new PresentationDefinitionDTO(); + when(presentationService.constructPresentationDefinition(any())) + .thenReturn(presentationDef); + when(objectMapper.writeValueAsString(presentationDef)) + .thenReturn("{\"presentation\":\"definition\"}"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "https://example.com/share", "", "en"); + + assertNotNull(result); + } + + @Test + void testNullFaceImageHandling() throws Exception { + Map mutableSubject = new HashMap<>(vcCredentialResponse.getCredential().getCredentialSubject()); + mutableSubject.remove("face"); + vcCredentialResponse.getCredential().setCredentialSubject(mutableSubject); + + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + PresentationDefinitionDTO presentationDef = new PresentationDefinitionDTO(); + when(presentationService.constructPresentationDefinition(any())) + .thenReturn(presentationDef); + when(objectMapper.writeValueAsString(presentationDef)) + .thenReturn("{\"presentation\":\"definition\"}"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "https://example.com/share", "", "en"); + + assertNotNull(result); + } + + @Test + void testGeneratePdfWithNullOrder() throws Exception { + credentialsSupportedResponse.setOrder(null); + when(utilities.getCredentialSupportedTemplateString(anyString(), anyString())) + .thenReturn("Test"); + + PresentationDefinitionDTO presentationDef = new PresentationDefinitionDTO(); + when(presentationService.constructPresentationDefinition(any())) + .thenReturn(presentationDef); + when(objectMapper.writeValueAsString(presentationDef)) + .thenReturn("{\"presentation\":\"definition\"}"); + + try (MockedStatic mocked = mockStatic(Utilities.class)) { + mocked.when(() -> Utilities.encodeToString(any(), anyString())) + .thenReturn("base64-encoded-qr"); + + ByteArrayInputStream result = credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + "TestCredential", vcCredentialResponse, issuerDTO, credentialsSupportedResponse, + "https://example.com/share", "", "en"); + + assertNotNull(result); + } + } +} diff --git a/src/test/java/io/mosip/mimoto/service/CredentialRequestServiceTest.java b/src/test/java/io/mosip/mimoto/service/CredentialRequestServiceTest.java new file mode 100644 index 000000000..da4f5bc58 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/CredentialRequestServiceTest.java @@ -0,0 +1,114 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.mimoto.*; +import io.mosip.mimoto.service.impl.CredentialRequestServiceImpl; +import io.mosip.mimoto.util.*; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.List; +import static io.mosip.mimoto.util.TestUtilities.*; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; + +@RunWith(MockitoJUnitRunner.class) +public class CredentialRequestServiceTest { + @InjectMocks + CredentialRequestServiceImpl credentialRequestBuilder; + + @Mock + JoseUtil joseUtil; + + IssuerDTO issuerDTO; + String issuerId; + + @Before + public void setUp() throws Exception { + issuerId = "issuer1"; + issuerDTO = getIssuerConfigDTO(issuerId); + } + + @Test + public void shouldHandleNullContextInCredentialSupportedResponse() throws Exception { + CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); + credentialsSupportedResponse.getCredentialDefinition().setContext(null); + + CredentialIssuerWellKnownResponse issuerWellKnownResponse = new CredentialIssuerWellKnownResponse(); + issuerWellKnownResponse.setCredentialIssuer("https://example-issuer.com"); + + Mockito.when(joseUtil.generateJwt(any(), any(), any())).thenReturn("jwt"); + + VCCredentialRequest result = credentialRequestBuilder.buildRequest( + issuerDTO, + issuerWellKnownResponse, + credentialsSupportedResponse, + "test-cnonce", + "walletId", + "walletKey", + false + ); + + assertNotNull(result.getCredentialDefinition().getContext()); + assertEquals("https://www.w3.org/2018/credentials/v1", result.getCredentialDefinition().getContext().getFirst()); + } + + @Test + public void shouldHandleEmptyContextInCredentialSupportedResponse() throws Exception { + CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); + + // Set an empty list as context + credentialsSupportedResponse.getCredentialDefinition().setContext(List.of()); + + CredentialIssuerWellKnownResponse issuerWellKnownResponse = new CredentialIssuerWellKnownResponse(); + issuerWellKnownResponse.setCredentialIssuer("https://example-issuer.com"); + + Mockito.when(joseUtil.generateJwt(any(), any(), any())).thenReturn("jwt"); + + VCCredentialRequest result = credentialRequestBuilder.buildRequest( + issuerDTO, + issuerWellKnownResponse, + credentialsSupportedResponse, + "test-cnonce", + "walletId", + "walletKey", + false + ); + + assertNotNull(result.getCredentialDefinition().getContext()); + assertEquals("https://www.w3.org/2018/credentials/v1", result.getCredentialDefinition().getContext().getFirst()); + } + + + @Test + public void shouldHandleExistingContextWithSpecificValue() throws Exception { + CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); + + // Set context with a specific value + credentialsSupportedResponse.getCredentialDefinition().setContext(List.of("https://www.w3.org/ns/credentials/v2")); + + CredentialIssuerWellKnownResponse issuerWellKnownResponse = new CredentialIssuerWellKnownResponse(); + issuerWellKnownResponse.setCredentialIssuer("https://example-issuer.com"); + + Mockito.when(joseUtil.generateJwt(any(), any(), any())).thenReturn("jwt"); + + VCCredentialRequest result = credentialRequestBuilder.buildRequest( + issuerDTO, + issuerWellKnownResponse, + credentialsSupportedResponse, + "test-cnonce", + "walletId", + "walletKey", + false + ); + + assertNotNull(result.getCredentialDefinition().getContext()); + assertEquals("https://www.w3.org/ns/credentials/v2", result.getCredentialDefinition().getContext().getFirst()); + } + +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/CredentialServiceTest.java b/src/test/java/io/mosip/mimoto/service/CredentialServiceTest.java index 8786f60d2..c903b5984 100644 --- a/src/test/java/io/mosip/mimoto/service/CredentialServiceTest.java +++ b/src/test/java/io/mosip/mimoto/service/CredentialServiceTest.java @@ -1,23 +1,13 @@ package io.mosip.mimoto.service; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import io.mosip.mimoto.dto.IssuerDTO; import io.mosip.mimoto.dto.idp.TokenResponseDTO; import io.mosip.mimoto.dto.mimoto.*; -import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.exception.VCVerificationException; import io.mosip.mimoto.model.QRCodeType; import io.mosip.mimoto.service.impl.CredentialServiceImpl; -import io.mosip.mimoto.service.impl.IdpServiceImpl; import io.mosip.mimoto.service.impl.IssuersServiceImpl; -import io.mosip.mimoto.util.JoseUtil; import io.mosip.mimoto.util.RestApiClient; -import io.mosip.mimoto.util.TestUtilities; -import io.mosip.mimoto.util.Utilities; -import io.mosip.vercred.vcverifier.CredentialsVerifier; -import io.mosip.vercred.vcverifier.constants.CredentialFormat; -import io.mosip.vercred.vcverifier.data.VerificationResult; -import lombok.extern.slf4j.Slf4j; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.Velocity; import org.junit.Before; @@ -26,14 +16,12 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.Spy; import org.mockito.junit.MockitoJUnitRunner; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpEntity; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestTemplate; import java.io.ByteArrayInputStream; import java.io.StringWriter; @@ -43,47 +31,34 @@ import static io.mosip.mimoto.util.TestUtilities.*; import static org.junit.Assert.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.doReturn; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @SpringBootTest -@Slf4j public class CredentialServiceTest { @Mock - CredentialsVerifier credentialsVerifier; - @Mock - ObjectMapper objectMapper; - @Spy - @InjectMocks - CredentialServiceImpl credentialService = new CredentialServiceImpl(); - - @Mock - IssuersServiceImpl issuersService; - - @Mock - RestTemplate restTemplate; + CredentialRequestService credentialRequestService; @Mock - IdpServiceImpl idpService; + CredentialVerifierService credentialVerifierService; @Mock RestApiClient restApiClient; - @Mock - JoseUtil joseUtil; + @InjectMocks + CredentialServiceImpl credentialService; @Mock - Utilities utilities; + CredentialPDFGeneratorService credentialUtilService; - private Map tokenRequestParams = Map.of( - "grant_type", "client_credentials", - "client_id", "test-client" - ); + @Mock + IssuersServiceImpl issuersService; TokenResponseDTO expectedTokenResponse; - String tokenEndpoint, issuerId, expectedExceptionMsg; + String tokenEndpoint, issuerId; IssuerDTO issuerDTO; HttpEntity> mockRequest; CredentialIssuerConfiguration issuerConfig; @@ -103,13 +78,6 @@ public void setUp() throws Exception { "client_id", List.of("test-client") ))); expectedTokenResponse = getTokenResponseDTO(); - - Mockito.when(idpService.constructGetTokenRequest(tokenRequestParams, issuerDTO, tokenEndpoint)) - .thenReturn(mockRequest); - Mockito.when(idpService.getTokenEndpoint(issuerConfig)) - .thenReturn(tokenEndpoint); - Mockito.when(restTemplate.postForObject(tokenEndpoint, mockRequest, TokenResponseDTO.class)) - .thenReturn(expectedTokenResponse); } @Test @@ -125,124 +93,23 @@ public void shouldParseHtmlStringToDocument() { assertTrue(mergedHtml.contains("PDF")); } - - @Test - public void shouldReturnTrueIfAValidCredentialIsPassedForVerification() throws VCVerificationException, JsonProcessingException { - VCCredentialResponse vc = TestUtilities.getVCCredentialResponseDTO("ed25519Signature2020"); - VerificationResult verificationResult = new VerificationResult(true, "", ""); - Mockito.when(credentialsVerifier.verify(any(String.class), eq(CredentialFormat.LDP_VC))).thenReturn(verificationResult); - Mockito.when(objectMapper.writeValueAsString(vc.getCredential())).thenReturn("vc"); - Boolean verificationStatus = credentialService.verifyCredential(vc); - - assertTrue(verificationStatus); - } - - @Test - public void shouldThrowExceptionIfInvalidCredentialIsPassedForVerification() throws VCVerificationException, JsonProcessingException { - VCCredentialResponse vc = TestUtilities.getVCCredentialResponseDTO("ed25519Signature2020"); - VerificationResult verificationResult = new VerificationResult(false, "Verification failed for the provided credentials", "Verification Failed!"); - Mockito.when(credentialsVerifier.verify(any(String.class), eq(CredentialFormat.LDP_VC))).thenReturn(verificationResult); - Mockito.when(objectMapper.writeValueAsString(vc.getCredential())).thenReturn("vc"); - expectedExceptionMsg = "verification failed! --> Verification failed for the provided credentials"; - - VCVerificationException actualException = assertThrows(VCVerificationException.class, () -> - credentialService.verifyCredential(vc) - ); - - assertEquals(expectedExceptionMsg, actualException.getMessage()); - } - - @Test - public void shouldReturnTokenResponseForValidTokenEndpoint() throws Exception { - - TokenResponseDTO actualTokenResponse = credentialService.getTokenResponse(tokenRequestParams, "issuer1"); - - assertEquals(expectedTokenResponse, actualTokenResponse); - } - - @Test - public void shouldThrowExceptionIfResponseIsNullWhenFetchingTokenResponse() throws Exception { - Mockito.when(restTemplate.postForObject(tokenEndpoint, mockRequest, TokenResponseDTO.class)) - .thenReturn(null); - - IdpException actualException = assertThrows(IdpException.class, () -> { - credentialService.getTokenResponse(tokenRequestParams, "issuer1"); - }); - - assertEquals("RESIDENT-APP-034 --> Exception occurred while performing the authorization", actualException.getMessage()); - } - - @Test - public void shouldThrowExceptionOnFetchingCredentialFromCredentialEndpointFailure() { - CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); - CredentialIssuerWellKnownResponse issuerWellKnownResponse = getCredentialIssuerWellKnownResponseDto(issuerId, Map.of("CredentialType1", credentialsSupportedResponse)); - VCCredentialRequest vcCredentialRequest = getVCCredentialRequestDTO(); - Mockito.when(restApiClient.postApi(issuerWellKnownResponse.getCredentialEndPoint(), MediaType.APPLICATION_JSON, vcCredentialRequest, VCCredentialResponse.class, "test-access-token")).thenReturn(null); - String credentialEndpoint = issuerWellKnownResponse.getCredentialEndPoint(); - expectedExceptionMsg = "VC Credential Issue API not accessible"; - - - RuntimeException actualException = assertThrows(RuntimeException.class, () -> { - credentialService.downloadCredential(credentialEndpoint, vcCredentialRequest, "test-access-token"); - }); - - assertEquals(expectedExceptionMsg, actualException.getMessage()); - } - - @Test - public void shouldReturnVCCredentialWhenCallingCredentialEndpointWithCredentialRequest() { - CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); - CredentialIssuerWellKnownResponse issuerWellKnownResponse = getCredentialIssuerWellKnownResponseDto(issuerId, Map.of("CredentialType1", credentialsSupportedResponse)); - VCCredentialRequest vcCredentialRequest = getVCCredentialRequestDTO(); - VCCredentialResponse expectedCredentialResponse = getVCCredentialResponseDTO("RSASignature2020"); - Mockito.when(restApiClient.postApi(issuerWellKnownResponse.getCredentialEndPoint(), MediaType.APPLICATION_JSON, vcCredentialRequest, VCCredentialResponse.class, "test-access-token")).thenReturn(expectedCredentialResponse); - - VCCredentialResponse actualCredentialResponse = credentialService.downloadCredential(issuerWellKnownResponse.getCredentialEndPoint(), vcCredentialRequest, "test-access-token"); - - assertEquals(expectedCredentialResponse, actualCredentialResponse); - } - - @Test - public void shouldGenerateVCCredentialRequestForProvidedIssuerAndCredentialType() throws Exception { - CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); - CredentialIssuerWellKnownResponse issuerWellKnownResponse = getCredentialIssuerWellKnownResponseDto(issuerId, Map.of("CredentialType1", credentialsSupportedResponse)); - VCCredentialRequest expectedVCCredentialRequest = getVCCredentialRequestDTO(); - Mockito.when(joseUtil.generateJwt(any(String.class), any(String.class), any(String.class))).thenReturn("jwt"); - - VCCredentialRequest actualVCCredentialRequest = credentialService.generateVCCredentialRequest(issuerDTO, issuerWellKnownResponse, credentialsSupportedResponse, "test-access-token"); - - assertEquals(expectedVCCredentialRequest, actualVCCredentialRequest); - } - - @Test - public void shouldThrowExceptionWhenInvalidAlgoIsProvidedForGeneratingJWTDuringCredentialRequestGeneration() throws Exception { - CredentialsSupportedResponse credentialsSupportedResponse = getCredentialSupportedResponse("CredentialType1"); - CredentialIssuerWellKnownResponse issuerWellKnownResponse = getCredentialIssuerWellKnownResponseDto(issuerId, Map.of("CredentialType1", credentialsSupportedResponse)); - Mockito.when(joseUtil.generateJwt(any(String.class), any(String.class), any(String.class))).thenThrow(new AssertionError("Unexpected algorithm type: dfs")); - - AssertionError actualError = assertThrows(AssertionError.class, () -> { - credentialService.generateVCCredentialRequest(issuerDTO, issuerWellKnownResponse, credentialsSupportedResponse, "test-access-token"); - }); - - assertEquals("Unexpected algorithm type: dfs", actualError.getMessage()); - } - @Test public void shouldThrowExceptionIfDownloadedVCSignatureVerificationFailed() throws Exception { Mockito.when(issuersService.getIssuerDetails(issuerId)).thenReturn(issuerDTO); Mockito.when(issuersService.getIssuerConfiguration(issuerId)).thenReturn(issuerConfig); - doReturn(getVCCredentialRequestDTO()).when(credentialService).generateVCCredentialRequest( - any(IssuerDTO.class), + when(credentialRequestService.buildRequest(any(IssuerDTO.class), any(CredentialIssuerWellKnownResponse.class), any(CredentialsSupportedResponse.class), - any(String.class) - ); + any(String.class), any(), any(), eq(false))).thenReturn(getVCCredentialRequestDTO()); VCCredentialResponse vcCredentialResponse = getVCCredentialResponseDTO("CredentialType1"); - doReturn(vcCredentialResponse).when(credentialService).downloadCredential(any(String.class), + when(restApiClient.postApi( + any(String.class), + any(MediaType.class), any(VCCredentialRequest.class), - any(String.class)); - doReturn(false).when(credentialService).verifyCredential(vcCredentialResponse); - + eq(VCCredentialResponse.class), + any(String.class) + )).thenReturn(vcCredentialResponse); + when(credentialVerifierService.verify(vcCredentialResponse)).thenReturn(false); VCVerificationException actualException = assertThrows(VCVerificationException.class, () -> credentialService.downloadCredentialAsPDF(issuerId, "CredentialType1", expectedTokenResponse, "once", "en")); @@ -253,27 +120,27 @@ public void shouldThrowExceptionIfDownloadedVCSignatureVerificationFailed() thro public void shouldReturnDownloadedVCAsPDFIfSignatureVerificationIsSuccessful() throws Exception { Mockito.when(issuersService.getIssuerDetails(issuerId)).thenReturn(issuerDTO); Mockito.when(issuersService.getIssuerConfiguration(issuerId)).thenReturn(issuerConfig); - doReturn(getVCCredentialRequestDTO()).when(credentialService).generateVCCredentialRequest( - any(IssuerDTO.class), + when(credentialRequestService.buildRequest(any(IssuerDTO.class), any(CredentialIssuerWellKnownResponse.class), any(CredentialsSupportedResponse.class), - any(String.class) - ); + any(String.class), any(), any(), eq(false))).thenReturn(getVCCredentialRequestDTO()); VCCredentialResponse vcCredentialResponse = getVCCredentialResponseDTO("CredentialType1"); - doReturn(vcCredentialResponse).when(credentialService).downloadCredential(any(String.class), + when(restApiClient.postApi( + any(String.class), + any(MediaType.class), any(VCCredentialRequest.class), - any(String.class)); - doReturn(true).when(credentialService).verifyCredential(vcCredentialResponse); + eq(VCCredentialResponse.class), + any(String.class) + )).thenReturn(vcCredentialResponse); + when(credentialVerifierService.verify(vcCredentialResponse)).thenReturn(true); issuerDTO.setQr_code_type(QRCodeType.None); - Mockito.when(utilities.getCredentialSupportedTemplateString(issuerDTO.getIssuer_id(), "CredentialType1")).thenReturn("

PDF

"); + ByteArrayInputStream expectedPDFByteArray = generatePdfFromHTML(); + Mockito.when(credentialUtilService.generatePdfForVerifiableCredentials("CredentialType1", vcCredentialResponse, issuerDTO, issuerConfig.getCredentialConfigurationsSupported().get("CredentialType1"), "", "once", "en")).thenReturn(expectedPDFByteArray); ByteArrayInputStream actualPDFByteArray = credentialService.downloadCredentialAsPDF(issuerId, "CredentialType1", expectedTokenResponse, "once", "en"); - String expectedText = extractTextFromPdf(expectedPDFByteArray); - String actualText = extractTextFromPdf(actualPDFByteArray); - - assertEquals(expectedText, actualText); + assertEquals(expectedPDFByteArray, actualPDFByteArray); } } diff --git a/src/test/java/io/mosip/mimoto/service/CredentialVerifierServiceTest.java b/src/test/java/io/mosip/mimoto/service/CredentialVerifierServiceTest.java new file mode 100644 index 000000000..3ce3ff681 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/CredentialVerifierServiceTest.java @@ -0,0 +1,108 @@ +package io.mosip.mimoto.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.dto.mimoto.VCCredentialProperties; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponse; +import io.mosip.mimoto.dto.mimoto.VCCredentialResponseProof; +import io.mosip.mimoto.exception.VCVerificationException; +import io.mosip.mimoto.service.impl.CredentialVerifierServiceImpl; +import io.mosip.vercred.vcverifier.CredentialsVerifier; +import io.mosip.vercred.vcverifier.constants.CredentialFormat; +import io.mosip.vercred.vcverifier.data.VerificationResult; + +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 java.util.Arrays; +import java.util.Map; + +import static org.mockito.Mockito.*; +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class CredentialVerifierServiceTest { + + @InjectMocks + private CredentialVerifierServiceImpl credentialVerifier; + + @Mock + private ObjectMapper objectMapper; + + @Mock + private CredentialsVerifier credentialsVerifier; + + @Mock + private VerificationResult verificationResult; + + private VCCredentialResponse vcCredentialResponse; + + private final String credentialJson = "{\"credential\":\"test\"}"; + + @Before + public void setUp() throws Exception { + VCCredentialProperties credentialProperties = VCCredentialProperties.builder() + .type(Arrays.asList("VerifiableCredential", "test")) + .credentialSubject(Map.of("name", "John Doe")) + .proof(new VCCredentialResponseProof()) + .build(); + + vcCredentialResponse = VCCredentialResponse.builder() + .format("LDP_VC") + .credential(credentialProperties) + .build(); + + when(objectMapper.writeValueAsString(any())).thenReturn(credentialJson); + } + + @Test + public void testVerifySuccess() throws Exception { + // Arrange + when(credentialsVerifier.verify(eq(credentialJson), eq(CredentialFormat.LDP_VC))).thenReturn(verificationResult); + when(verificationResult.getVerificationStatus()).thenReturn(true); + + // Act + boolean result = credentialVerifier.verify(vcCredentialResponse); + + // Assert + assertTrue(result); + verify(credentialsVerifier).verify(credentialJson, CredentialFormat.LDP_VC); + } + + @Test(expected = VCVerificationException.class) + public void testVerifyFailureThrowsException() throws Exception { + // Arrange + when(credentialsVerifier.verify(eq(credentialJson), eq(CredentialFormat.LDP_VC))).thenReturn(verificationResult); + when(verificationResult.getVerificationStatus()).thenReturn(false); + when(verificationResult.getVerificationErrorCode()).thenReturn("ERR_CODE"); + when(verificationResult.getVerificationMessage()).thenReturn("Verification failed"); + + // Act + credentialVerifier.verify(vcCredentialResponse); + } + + @Test(expected = JsonProcessingException.class) + public void testVerifyJsonProcessingException() throws Exception { + // Arrange + when(objectMapper.writeValueAsString(any())).thenThrow(JsonProcessingException.class); + + // Act + credentialVerifier.verify(vcCredentialResponse); + } + + @Test(expected = NullPointerException.class) + public void testVerifyWithNullCredential() throws Exception { + // Arrange + VCCredentialResponse nullCredentialResponse = VCCredentialResponse.builder() + .format("LDP_VC") + .credential(null) + .build(); + + // Act + credentialVerifier.verify(nullCredentialResponse); + } +} diff --git a/src/test/java/io/mosip/mimoto/service/GoogleTokenServiceTest.java b/src/test/java/io/mosip/mimoto/service/GoogleTokenServiceTest.java new file mode 100644 index 000000000..1255801a7 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/GoogleTokenServiceTest.java @@ -0,0 +1,205 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.dto.mimoto.UserMetadataDTO; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import io.mosip.mimoto.service.impl.GoogleTokenService; +import io.mosip.mimoto.service.impl.SecurityContextManager; +import io.mosip.mimoto.service.impl.SessionManager; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.jwt.Jwt; + +import java.time.Instant; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static io.mosip.mimoto.exception.ErrorConstants.INVALID_REQUEST; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class GoogleTokenServiceTest { + + @Mock + private TokenDecoder tokenDecoder; + + @Mock + private UserMetadataService userMetadataService; + + @Mock + private SecurityContextManager securityContextManager; + + @Mock + private SessionManager sessionManager; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @InjectMocks + private GoogleTokenService googleTokenService; + + @Captor + private ArgumentCaptor userMetadataDTOArgumentCaptor; + + @Captor + private ArgumentCaptor oauth2TokenCaptor; + + private String idToken; + private String provider; + private Jwt validJwt; + private Map claims; + + @BeforeEach + void setUp() { + idToken = "dummyIdToken"; + provider = "google"; + + // Set expectedIssuer and expectedAudience via constructor injection simulation + googleTokenService = new GoogleTokenService(tokenDecoder, userMetadataService, securityContextManager, sessionManager, "google-client-id", "https://accounts.google.com"); + + claims = new HashMap<>(); + claims.put("email", "test@example.com"); + claims.put("name", "Test User"); + claims.put("picture", "http://example.com/picture.jpg"); + claims.put("sub", "google-subject-id"); + claims.put("iss", "https://accounts.google.com"); + claims.put("aud", "google-client-id"); + claims.put("exp", Instant.now().plusSeconds(3600).getEpochSecond()); + + validJwt = Jwt.withTokenValue(idToken) + .header("alg", "RS256") + .claims(claimsMap -> claimsMap.putAll(claims)) + .issuer("https://accounts.google.com") + .audience(Collections.singletonList("google-client-id")) + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(3600)) + .build(); + } + + @Test + void processTokenValidTokenShouldCallDependenciesAndSetSessionAndSecurityContext() throws Exception { + when(tokenDecoder.decode(idToken)).thenReturn(validJwt); + when(userMetadataService.updateOrCreateUserMetadata("google-subject-id", provider, "Test User", "http://example.com/picture.jpg", "test@example.com")).thenReturn("test-user-id"); + + googleTokenService.processToken(idToken, provider, request, response); + + verify(tokenDecoder).decode(idToken); + verify(userMetadataService).updateOrCreateUserMetadata("google-subject-id", provider, "Test User", "http://example.com/picture.jpg", "test@example.com"); + verify(sessionManager).setupSession(eq(request), eq(provider), userMetadataDTOArgumentCaptor.capture(), eq("test-user-id")); + verify(securityContextManager).setupSecurityContext(oauth2TokenCaptor.capture(), eq(request), eq(response)); + + UserMetadataDTO capturedUserMetadata = userMetadataDTOArgumentCaptor.getValue(); + assertEquals("Test User", capturedUserMetadata.getDisplayName()); + assertEquals("http://example.com/picture.jpg", capturedUserMetadata.getProfilePictureUrl()); + assertEquals("test@example.com", capturedUserMetadata.getEmail()); + + OAuth2AuthenticationToken capturedToken = oauth2TokenCaptor.getValue(); + assertEquals(provider, capturedToken.getAuthorizedClientRegistrationId()); + assertEquals("google-subject-id", capturedToken.getPrincipal().getAttribute("sub")); + } + + @Test + void processTokenInvalidIssuerShouldThrowException() throws OAuth2AuthenticationException { + Jwt invalidIssuerJwt = Jwt.withTokenValue(idToken) + .header("alg", "RS256") + .claims(claimsMap -> claimsMap.putAll(claims)) + .issuer("https://invalid.com") + .audience(Collections.singletonList("google-client-id")) + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(3600)) + .build(); + + when(tokenDecoder.decode(idToken)).thenReturn(invalidIssuerJwt); + + OAuth2AuthenticationException exception = assertThrows(OAuth2AuthenticationException.class, () -> + googleTokenService.processToken(idToken, provider, request, response)); + + assertEquals("invalid_issuer", exception.getErrorCode()); + verify(tokenDecoder).decode(idToken); + verifyNoInteractions(userMetadataService, sessionManager, securityContextManager); + } + + @Test + void processTokenInvalidAudienceShouldThrowException() throws OAuth2AuthenticationException { + Jwt invalidAudienceJwt = Jwt.withTokenValue(idToken) + .header("alg", "RS256") + .claims(claimsMap -> claimsMap.putAll(claims)) + .issuer("https://accounts.google.com") + .audience(Collections.singletonList("invalid-client-id")) + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(3600)) + .build(); + + when(tokenDecoder.decode(idToken)).thenReturn(invalidAudienceJwt); + + OAuth2AuthenticationException exception = assertThrows(OAuth2AuthenticationException.class, () -> + googleTokenService.processToken(idToken, provider, request, response)); + + assertEquals("invalid_audience", exception.getErrorCode()); + verify(tokenDecoder).decode(idToken); + verifyNoInteractions(userMetadataService, sessionManager, securityContextManager); + } + + @Test + void processTokenMissingEmailClaimShouldThrowException() throws OAuth2AuthenticationException { + Map claimsWithoutEmail = new HashMap<>(claims); + claimsWithoutEmail.remove("email"); + Jwt jwtWithoutEmail = Jwt.withTokenValue(idToken) + .header("alg", "RS256") + .claims(claimsMap -> claimsMap.putAll(claimsWithoutEmail)) + .issuer("https://accounts.google.com") + .audience(Collections.singletonList("google-client-id")) + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(3600)) + .build(); + + when(tokenDecoder.decode(idToken)).thenReturn(jwtWithoutEmail); + + OAuth2AuthenticationException exception = assertThrows(OAuth2AuthenticationException.class, () -> + googleTokenService.processToken(idToken, provider, request, response)); + + assertEquals("missing_email", exception.getErrorCode()); + verify(tokenDecoder).decode(idToken); + verifyNoInteractions(userMetadataService, sessionManager, securityContextManager); + } + + @Test + void processTokenEmptyTokenShouldThrowException() throws OAuth2AuthenticationException { + // Stub tokenDecoder.decode to throw a JwtException for an empty token + when(tokenDecoder.decode("")).thenThrow(new InvalidRequestException("invalid_request","Empty token")); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + googleTokenService.processToken("", provider, request, response)); + + assertEquals(INVALID_REQUEST.getErrorCode(), exception.getErrorCode()); + verifyNoInteractions(userMetadataService, sessionManager, securityContextManager); + verify(tokenDecoder).decode(""); + } + + @Test + void processTokenInvalidTokenFormatShouldThrowException() throws OAuth2AuthenticationException { + when(tokenDecoder.decode(idToken)).thenThrow(new InvalidRequestException("invalid_token", "Invalid token format")); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + googleTokenService.processToken(idToken, provider, request, response)); + + assertEquals("invalid_token", exception.getErrorCode()); + verify(tokenDecoder).decode(idToken); + verifyNoInteractions(userMetadataService, sessionManager, securityContextManager); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/IdpServiceTest.java b/src/test/java/io/mosip/mimoto/service/IdpServiceTest.java index f7a85e5fb..c656af005 100644 --- a/src/test/java/io/mosip/mimoto/service/IdpServiceTest.java +++ b/src/test/java/io/mosip/mimoto/service/IdpServiceTest.java @@ -1,7 +1,11 @@ package io.mosip.mimoto.service; import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.VerifiableCredentialRequestDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.AuthorizationServerWellKnownResponse; import io.mosip.mimoto.dto.mimoto.CredentialIssuerConfiguration; +import io.mosip.mimoto.exception.IdpException; import io.mosip.mimoto.exception.IssuerOnboardingException; import io.mosip.mimoto.service.impl.IdpServiceImpl; import io.mosip.mimoto.util.JoseUtil; @@ -15,15 +19,16 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; +import static io.mosip.mimoto.util.TestUtilities.getCredentialIssuerConfigurationResponseDto; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; -import static io.mosip.mimoto.util.TestUtilities.*; @RunWith(MockitoJUnitRunner.class) public class IdpServiceTest { @@ -33,10 +38,27 @@ public class IdpServiceTest { @Mock JoseUtil joseUtil; + @Mock + private RestTemplate restTemplate; + + @Mock + private IssuersService issuersService; + + @Mock + private CredentialIssuerConfiguration credentialIssuerConfiguration; + + @Mock + private AuthorizationServerWellKnownResponse authorizationServerWellKnownResponse; + + @Mock + private TokenResponseDTO tokenResponseDTO; + private IssuerDTO issuerDTO; private Map params; private final String authorizationAudience = "https://example.com/auth"; + + @Before public void setUp() throws IOException { issuerDTO = new IssuerDTO(); @@ -94,4 +116,89 @@ public void shouldReturnTokenEndpointFromCredentialIssuerConfigurationResponse() assertEquals(expectedTokenEndpoint, actualTokenEndpoint); } + + @Test + public void shouldThrowExceptionIfResponseIsNullWhenFetchingTokenResponse() throws Exception { + params.put("issuer", "issuer123"); + + IssuerDTO mockIssuer = new IssuerDTO(); + mockIssuer.setClient_id("client123"); + mockIssuer.setClient_alias("clientAlias"); + + when(issuersService.getIssuerDetails("issuer123")).thenReturn(mockIssuer); + when(issuersService.getIssuerConfiguration("issuer123")).thenReturn(credentialIssuerConfiguration); + + when(credentialIssuerConfiguration.getAuthorizationServerWellKnownResponse()) + .thenReturn(authorizationServerWellKnownResponse); + when(authorizationServerWellKnownResponse.getTokenEndpoint()) + .thenReturn("https://example.com/token"); + + when(joseUtil.getJWT(eq("client123"), any(), any(), eq("clientAlias"), any(), eq("https://example.com/token"))) + .thenReturn("jwt-token"); + + when(restTemplate.postForObject(eq("https://example.com/token"), any(HttpEntity.class), eq(TokenResponseDTO.class))) + .thenReturn(null); + + IdpException ex = assertThrows(IdpException.class, () -> + idpService.getTokenResponse(params)); + + assertEquals("RESIDENT-APP-034 --> Exception occurred while performing the authorization", ex.getMessage()); + } + + @Test + public void shouldReturnTokenResponseForValidTokenEndpoint() throws Exception { + params.put("issuer", "issuer123"); + + IssuerDTO mockIssuer = new IssuerDTO(); + mockIssuer.setClient_id("client123"); + mockIssuer.setClient_alias("clientAlias"); + + when(issuersService.getIssuerDetails("issuer123")).thenReturn(mockIssuer); + when(issuersService.getIssuerConfiguration("issuer123")).thenReturn(credentialIssuerConfiguration); + when(credentialIssuerConfiguration.getAuthorizationServerWellKnownResponse()) + .thenReturn(authorizationServerWellKnownResponse); + when(authorizationServerWellKnownResponse.getTokenEndpoint()) + .thenReturn("https://example.com/token"); + + when(joseUtil.getJWT(eq("client123"), any(), any(), eq("clientAlias"), any(), eq("https://example.com/token"))) + .thenReturn("jwt-token"); + + when(restTemplate.postForObject(eq("https://example.com/token"), any(HttpEntity.class), eq(TokenResponseDTO.class))) + .thenReturn(tokenResponseDTO); + + TokenResponseDTO response = idpService.getTokenResponse(params); + + assertNotNull(response); + assertEquals(tokenResponseDTO, response); + } + + @Test + public void shouldReturnTokenResponseForVerifiableCredentialRequestDTO() throws Exception { + VerifiableCredentialRequestDTO requestDTO = new VerifiableCredentialRequestDTO(); + requestDTO.setCode("sampleCode"); + requestDTO.setRedirectUri("https://myapp.com/callback"); + requestDTO.setGrantType("authorization_code"); + requestDTO.setCodeVerifier("verifier123"); + requestDTO.setIssuer("issuer123"); + + IssuerDTO mockIssuer = new IssuerDTO(); + mockIssuer.setClient_id("client123"); + mockIssuer.setClient_alias("clientAlias"); + + when(issuersService.getIssuerDetails("issuer123")).thenReturn(mockIssuer); + when(issuersService.getIssuerConfiguration("issuer123")).thenReturn(credentialIssuerConfiguration); + when(credentialIssuerConfiguration.getAuthorizationServerWellKnownResponse()) + .thenReturn(authorizationServerWellKnownResponse); + when(authorizationServerWellKnownResponse.getTokenEndpoint()) + .thenReturn("https://example.com/token"); + when(joseUtil.getJWT(eq("client123"), any(), any(), eq("clientAlias"), any(), eq("https://example.com/token"))) + .thenReturn("jwt-token"); + when(restTemplate.postForObject(eq("https://example.com/token"), any(HttpEntity.class), eq(TokenResponseDTO.class))) + .thenReturn(tokenResponseDTO); + + TokenResponseDTO response = idpService.getTokenResponse(requestDTO); + + assertNotNull(response); + assertEquals(tokenResponseDTO, response); + } } \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/IssuersServiceTest.java b/src/test/java/io/mosip/mimoto/service/IssuersServiceTest.java index 6cab5e88a..282f0de20 100644 --- a/src/test/java/io/mosip/mimoto/service/IssuersServiceTest.java +++ b/src/test/java/io/mosip/mimoto/service/IssuersServiceTest.java @@ -6,11 +6,11 @@ import io.mosip.mimoto.dto.IssuersDTO; import io.mosip.mimoto.dto.mimoto.CredentialIssuerConfiguration; import io.mosip.mimoto.dto.mimoto.CredentialIssuerWellKnownResponse; +import io.mosip.mimoto.dto.mimoto.IssuerConfig; import io.mosip.mimoto.exception.ApiNotAccessibleException; import io.mosip.mimoto.exception.AuthorizationServerWellknownResponseException; import io.mosip.mimoto.exception.InvalidIssuerIdException; import io.mosip.mimoto.exception.InvalidWellknownResponseException; -import io.mosip.mimoto.service.impl.CredentialServiceImpl; import io.mosip.mimoto.service.impl.IssuersServiceImpl; import io.mosip.mimoto.util.IssuerConfigUtil; import io.mosip.mimoto.util.Utilities; @@ -29,20 +29,15 @@ import java.util.Map; import static io.mosip.mimoto.util.TestUtilities.*; -import static io.mosip.mimoto.util.TestUtilities.getCredentialSupportedResponse; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) public class IssuersServiceTest { @InjectMocks - IssuersServiceImpl issuersService = new IssuersServiceImpl(); - - @InjectMocks - CredentialServiceImpl credentialService = new CredentialServiceImpl(); + IssuersServiceImpl issuersService; @Mock Utilities utilities; @@ -53,8 +48,6 @@ public class IssuersServiceTest { @Spy ObjectMapper objectMapper; - List issuerConfigRelatedFields = List.of("additional_headers", "authorization_endpoint", "authorization_audience", "credential_endpoint", "credential_audience"); - String issuerWellKnownUrl, issuerId, credentialIssuerHostUrl, authServerWellknownUrl, issuersConfigJsonValue; CredentialIssuerConfiguration expectedCredentialIssuerConfiguration; IssuersDTO issuers = new IssuersDTO(); @@ -83,7 +76,7 @@ public void setUp() throws Exception { } @Test - public void shouldReturnAllIssuersWhenSearchValueIsNull() throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { + public void shouldReturnAllIssuersWhenSearchValueIsNull() throws ApiNotAccessibleException, IOException { issuers.setIssuers(List.of(getIssuerConfigDTO("Issuer1"), getIssuerConfigDTO("Issuer2"))); issuersConfigJsonValue = new Gson().toJson(issuers); Mockito.when(utilities.getIssuersConfigJsonValue()).thenReturn(issuersConfigJsonValue); @@ -98,7 +91,7 @@ public void shouldReturnAllIssuersWhenSearchValueIsNull() throws ApiNotAccessibl } @Test - public void shouldReturnMatchingIssuersWhenSearchValuePatternMatchesWithIssuerName() throws ApiNotAccessibleException, IOException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { + public void shouldReturnMatchingIssuersWhenSearchValuePatternMatchesWithIssuerName() throws ApiNotAccessibleException, IOException { issuers.setIssuers(List.of(getIssuerConfigDTO("Issuer1"), getIssuerConfigDTO("Issuer2"))); issuersConfigJsonValue = new Gson().toJson(issuers); Mockito.when(utilities.getIssuersConfigJsonValue()).thenReturn(issuersConfigJsonValue); @@ -113,7 +106,7 @@ public void shouldReturnMatchingIssuersWhenSearchValuePatternMatchesWithIssuerNa } @Test(expected = ApiNotAccessibleException.class) - public void shouldThrowApiNotAccessibleExceptionWhenIssuersJsonStringIsNullForGettingAllIssuers() throws IOException, ApiNotAccessibleException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { + public void shouldThrowApiNotAccessibleExceptionWhenIssuersJsonStringIsNullForGettingAllIssuers() throws IOException, ApiNotAccessibleException { Mockito.when(utilities.getIssuersConfigJsonValue()).thenReturn(null); issuersService.getIssuers(null); @@ -152,7 +145,7 @@ public void shouldThrowApiNotAccessibleExceptionWhenIssuersJsonStringIsNullForGe } @Test - public void shouldReturnOnlyEnabledIssuers() throws IOException, ApiNotAccessibleException, AuthorizationServerWellknownResponseException, InvalidWellknownResponseException { + public void shouldReturnOnlyEnabledIssuers() throws IOException, ApiNotAccessibleException { IssuersDTO issuers = new IssuersDTO(); IssuerDTO enabledIssuer = getIssuerConfigDTO("Issuer1"); IssuerDTO disabledIssuer = getIssuerConfigDTO("Issuer2"); @@ -167,8 +160,8 @@ public void shouldReturnOnlyEnabledIssuers() throws IOException, ApiNotAccessibl IssuersDTO actualIssuersDTO = issuersService.getIssuers(""); assertEquals(expectedIssuersDTO, actualIssuersDTO); - assertEquals(actualIssuersDTO.getIssuers().get(0).getEnabled(), "true"); - assertEquals(actualIssuersDTO.getIssuers().size(), 1); + assertEquals("true", actualIssuersDTO.getIssuers().getFirst().getEnabled()); + assertEquals(1, actualIssuersDTO.getIssuers().size()); } @Test @@ -182,9 +175,7 @@ public void shouldReturnProperCredentialConfigurationsForTheRequestedIssuer() th public void issuersConfigShouldThrowExceptionIfAnyErrorOccurredWhileFetchingIssuersWellknown() { Mockito.when(utilities.getIssuersConfigJsonValue()).thenReturn(null); - ApiNotAccessibleException actualException = assertThrows(ApiNotAccessibleException.class, () -> { - issuersService.getIssuerConfiguration(issuerId); - }); + ApiNotAccessibleException actualException = assertThrows(ApiNotAccessibleException.class, () -> issuersService.getIssuerConfiguration(issuerId)); assertEquals("RESIDENT-APP-026 --> Api not accessible failure", actualException.getMessage()); verify(utilities, times(1)).getIssuersConfigJsonValue(); @@ -192,15 +183,112 @@ public void issuersConfigShouldThrowExceptionIfAnyErrorOccurredWhileFetchingIssu @Test - public void issuersConfigShouldThrowExceptionIfAnyErrorOccurredWhileFetchingIssuersAuthorizationServerWellknown() throws IOException, AuthorizationServerWellknownResponseException { + public void issuersConfigShouldThrowExceptionIfAnyErrorOccurredWhileFetchingIssuersAuthorizationServerWellknown() throws AuthorizationServerWellknownResponseException { Mockito.when(issuersConfigUtil.getAuthServerWellknown(authServerWellknownUrl)).thenThrow(new AuthorizationServerWellknownResponseException("well-known api is not accessible")); - AuthorizationServerWellknownResponseException actualException = assertThrows(AuthorizationServerWellknownResponseException.class, () -> { - issuersService.getIssuerConfiguration("Issuer3id"); - }); + AuthorizationServerWellknownResponseException actualException = assertThrows(AuthorizationServerWellknownResponseException.class, () -> issuersService.getIssuerConfiguration("Issuer3id")); assertEquals("RESIDENT-APP-042 --> Invalid Authorization Server well-known from server:\n" + "well-known api is not accessible", actualException.getMessage()); verify(issuersConfigUtil, times(1)).getAuthServerWellknown(authServerWellknownUrl); } + + // Existing imports and class setup remain unchanged +// Add these test cases to the existing IssuersServiceTest class + + @Test + public void shouldReturnIssuerConfigForValidIssuerIdAndCredentialType() throws ApiNotAccessibleException, IOException, InvalidIssuerIdException, InvalidWellknownResponseException { + // Arrange + String issuerId = "Issuer3id"; + String credentialType = "CredentialType1"; + IssuerDTO expectedIssuerDTO = getIssuerConfigDTO("Issuer3"); + CredentialIssuerWellKnownResponse wellKnownResponse = getCredentialIssuerWellKnownResponseDto( + issuerId, Map.of(credentialType, getCredentialSupportedResponse(credentialType))); + IssuerConfig expectedIssuerConfig = new IssuerConfig( + expectedIssuerDTO, + wellKnownResponse, + wellKnownResponse.getCredentialConfigurationsSupported().get(credentialType) + ); + + // Act + IssuerConfig actualIssuerConfig = issuersService.getIssuerConfig(issuerId, credentialType); + + // Assert + assertEquals(expectedIssuerConfig, actualIssuerConfig); + assertEquals(expectedIssuerDTO, actualIssuerConfig.getIssuerDTO()); + assertEquals(wellKnownResponse, actualIssuerConfig.getWellKnownResponse()); + assertEquals(wellKnownResponse.getCredentialConfigurationsSupported().get(credentialType), + actualIssuerConfig.getCredentialsSupportedResponse()); + verify(issuersConfigUtil, times(1)).getIssuerWellknown(credentialIssuerHostUrl); + verify(utilities, times(1)).getIssuersConfigJsonValue(); + } + + @Test + public void shouldThrowInvalidIssuerIdExceptionForNonExistentIssuerId() throws ApiNotAccessibleException, IOException, InvalidWellknownResponseException { + // Arrange + String issuerId = "InvalidIssuerId"; + String credentialType = "CredentialType1"; + + // Act & Assert + InvalidIssuerIdException exception = assertThrows(InvalidIssuerIdException.class, + () -> issuersService.getIssuerConfig(issuerId, credentialType)); + + assertEquals("RESIDENT-APP-035 --> Invalid issuer ID", exception.getMessage()); + verify(utilities, times(1)).getIssuersConfigJsonValue(); + verify(issuersConfigUtil, never()).getIssuerWellknown(anyString()); + } + + @Test + public void shouldThrowApiNotAccessibleExceptionWhenIssuersConfigJsonIsNull() throws ApiNotAccessibleException, IOException, InvalidWellknownResponseException { + // Arrange + String issuerId = "Issuer3id"; + String credentialType = "CredentialType1"; + Mockito.when(utilities.getIssuersConfigJsonValue()).thenReturn(null); + + // Act & Assert + ApiNotAccessibleException exception = assertThrows(ApiNotAccessibleException.class, + () -> issuersService.getIssuerConfig(issuerId, credentialType)); + + assertEquals("RESIDENT-APP-026 --> Unable to fetch issuer configuration for issuerId: Issuer3id; \n" + + "nested exception is io.mosip.mimoto.exception.ApiNotAccessibleException: RESIDENT-APP-026 --> Api not accessible failure", exception.getMessage()); + verify(utilities, times(1)).getIssuersConfigJsonValue(); + verify(issuersConfigUtil, never()).getIssuerWellknown(anyString()); + } + + @Test + public void shouldThrowApiNotAccessibleExceptionWhenGetIssuerWellknownFails() throws IOException, InvalidWellknownResponseException, ApiNotAccessibleException { + // Arrange + String issuerId = "Issuer3id"; + String credentialType = "CredentialType1"; + Mockito.when(issuersConfigUtil.getIssuerWellknown(credentialIssuerHostUrl)) + .thenThrow(new ApiNotAccessibleException("Well-known endpoint inaccessible")); + + // Act & Assert + ApiNotAccessibleException exception = assertThrows(ApiNotAccessibleException.class, + () -> issuersService.getIssuerConfig(issuerId, credentialType)); + + assertEquals("RESIDENT-APP-026 --> Unable to fetch issuer configuration for issuerId: Issuer3id; \n" + + "nested exception is io.mosip.mimoto.exception.ApiNotAccessibleException: RESIDENT-APP-026 --> Well-known endpoint inaccessible", exception.getMessage()); + verify(utilities, times(1)).getIssuersConfigJsonValue(); + verify(issuersConfigUtil, times(1)).getIssuerWellknown(credentialIssuerHostUrl); + } + + @Test + public void shouldLogErrorWhenApiNotAccessibleExceptionOccurs() throws IOException, InvalidWellknownResponseException, ApiNotAccessibleException { + // Arrange + String issuerId = "Issuer3id"; + String credentialType = "CredentialType1"; + ApiNotAccessibleException apiException = new ApiNotAccessibleException("Well-known endpoint inaccessible"); + Mockito.when(issuersConfigUtil.getIssuerWellknown(credentialIssuerHostUrl)).thenThrow(apiException); + + // Act & Assert + ApiNotAccessibleException exception = assertThrows(ApiNotAccessibleException.class, + () -> issuersService.getIssuerConfig(issuerId, credentialType)); + + assertEquals("RESIDENT-APP-026 --> Unable to fetch issuer configuration for issuerId: Issuer3id; \n" + + "nested exception is io.mosip.mimoto.exception.ApiNotAccessibleException: RESIDENT-APP-026 --> Well-known endpoint inaccessible", exception.getMessage()); + verify(utilities, times(1)).getIssuersConfigJsonValue(); + verify(issuersConfigUtil, times(1)).getIssuerWellknown(credentialIssuerHostUrl); + // Note: Logging verification requires a logging framework setup (e.g., Logback with ListAppender) + } } \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/LogoutServiceTest.java b/src/test/java/io/mosip/mimoto/service/LogoutServiceTest.java new file mode 100644 index 000000000..12c80cada --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/LogoutServiceTest.java @@ -0,0 +1,189 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.HttpStatus; +import org.springframework.session.Session; +import org.springframework.session.SessionRepository; + +import java.util.Base64; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class LogoutServiceTest { + + @InjectMocks + private LogoutService logoutService; + + @Mock + private HttpServletRequest request; + + @Mock + private HttpServletResponse response; + + @Mock + private SessionRepository sessionRepository; + + @Mock + private HttpSession session; + + @Mock + private Session springSession; + + private String sessionId = "test-session-id"; + private String encodedSessionId; + + @BeforeEach + public void setup() { + encodedSessionId = Base64.getUrlEncoder().encodeToString(sessionId.getBytes()); + } + + @Test + void shouldSuccessfullyHandleLogoutWithValidSessionCookieAndSession() throws OAuth2AuthenticationException { + // Arrange + Cookie sessionCookie = new Cookie("SESSION", encodedSessionId); + when(request.getCookies()).thenReturn(new Cookie[]{sessionCookie}); + when(sessionRepository.findById(sessionId)).thenReturn(springSession); + when(request.getSession(false)).thenReturn(session); + + // Act + logoutService.handleLogout(request, response, sessionRepository); + + // Assert + verify(sessionRepository).findById(sessionId); + verify(sessionRepository).deleteById(sessionId); + verify(session).invalidate(); + verifyNoInteractions(response); + } + + @Test + void shouldThrowOAuth2AuthenticationExceptionForInvalidSessionId() { + // Arrange + Cookie sessionCookie = new Cookie("SESSION", encodedSessionId); + when(request.getCookies()).thenReturn(new Cookie[]{sessionCookie}); + when(sessionRepository.findById(sessionId)).thenReturn(null); + + // Act & Assert + OAuth2AuthenticationException exception = assertThrows(OAuth2AuthenticationException.class, + () -> logoutService.handleLogout(request, response, sessionRepository)); + + assertEquals("NOT_FOUND", exception.getErrorCode()); + assertEquals("NOT_FOUND --> Logout request was sent for an invalid or expired session", exception.getMessage()); + assertEquals(HttpStatus.NOT_FOUND, exception.getStatus()); + verify(sessionRepository).findById(sessionId); + verify(sessionRepository, never()).deleteById(anyString()); + verify(request, never()).getSession(false); + verifyNoInteractions(response, session); + } + + @Test + void shouldHandleLogoutWithNoSessionCookie() throws OAuth2AuthenticationException { + // Arrange + when(request.getCookies()).thenReturn(new Cookie[]{new Cookie("OTHER_COOKIE", "value")}); + when(request.getSession(false)).thenReturn(session); + + // Act + logoutService.handleLogout(request, response, sessionRepository); + + // Assert + verify(sessionRepository, never()).findById(anyString()); + verify(sessionRepository, never()).deleteById(anyString()); + verify(session).invalidate(); + verifyNoInteractions(response); + } + + @Test + void shouldHandleLogoutWithNullCookies() throws OAuth2AuthenticationException { + // Arrange + when(request.getCookies()).thenReturn(null); + when(request.getSession(false)).thenReturn(session); + + // Act + logoutService.handleLogout(request, response, sessionRepository); + + // Assert + verify(sessionRepository, never()).findById(anyString()); + verify(sessionRepository, never()).deleteById(anyString()); + verify(session).invalidate(); + verifyNoInteractions(response); + } + + @Test + void shouldHandleLogoutWithNoHttpSession() throws OAuth2AuthenticationException { + // Arrange + Cookie sessionCookie = new Cookie("SESSION", encodedSessionId); + when(request.getCookies()).thenReturn(new Cookie[]{sessionCookie}); + when(sessionRepository.findById(sessionId)).thenReturn(springSession); + when(request.getSession(false)).thenReturn(null); + + // Act + logoutService.handleLogout(request, response, sessionRepository); + + // Assert + verify(sessionRepository).findById(sessionId); + verify(sessionRepository).deleteById(sessionId); + verify(request).getSession(false); + verifyNoInteractions(session, response); + } + + @Test + void shouldHandleLogoutWithEmptyCookiesArray() throws OAuth2AuthenticationException { + // Arrange + when(request.getCookies()).thenReturn(new Cookie[]{}); + when(request.getSession(false)).thenReturn(session); + + // Act + logoutService.handleLogout(request, response, sessionRepository); + + // Assert + verify(sessionRepository, never()).findById(anyString()); + verify(sessionRepository, never()).deleteById(anyString()); + verify(session).invalidate(); + verifyNoInteractions(response); + } + + @Test + void shouldThrowOAuth2AuthenticationExceptionForInvalidBase64SessionId() { + // Arrange + Cookie sessionCookie = new Cookie("SESSION", "invalid-base64"); + when(request.getCookies()).thenReturn(new Cookie[]{sessionCookie}); + + // Act & Assert + assertThrows(OAuth2AuthenticationException.class, + () -> logoutService.handleLogout(request, response, sessionRepository)); + + verify(sessionRepository, never()).deleteById(anyString()); + verify(request, never()).getSession(false); + verifyNoInteractions(response, session); + } + + @Test + void shouldHandleMultipleCookiesWithValidSessionCookie() throws OAuth2AuthenticationException { + // Arrange + Cookie sessionCookie = new Cookie("SESSION", encodedSessionId); + Cookie otherCookie = new Cookie("OTHER_COOKIE", "value"); + when(request.getCookies()).thenReturn(new Cookie[]{otherCookie, sessionCookie}); + when(sessionRepository.findById(sessionId)).thenReturn(springSession); + when(request.getSession(false)).thenReturn(session); + + // Act + logoutService.handleLogout(request, response, sessionRepository); + + // Assert + verify(sessionRepository).findById(sessionId); + verify(sessionRepository).deleteById(sessionId); + verify(session).invalidate(); + verifyNoInteractions(response); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/TokenServiceFactoryTest.java b/src/test/java/io/mosip/mimoto/service/TokenServiceFactoryTest.java new file mode 100644 index 000000000..db18ac7d9 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/TokenServiceFactoryTest.java @@ -0,0 +1,68 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.OAuth2AuthenticationException; +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.http.HttpStatus; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class TokenServiceFactoryTest { + + @InjectMocks + private TokenServiceFactory tokenServiceFactory; + + @Mock + private TokenService googleTokenService; + + @Before + public void setUp() { + Map tokenServices = new HashMap<>(); + tokenServices.put("google", googleTokenService); + tokenServiceFactory = new TokenServiceFactory(tokenServices); + } + + @Test + public void testGetTokenService_ValidProvider_ReturnsService() throws OAuth2AuthenticationException { + // Act + TokenService result = tokenServiceFactory.getTokenService("google"); + + // Assert + assertNotNull(result); + assertEquals(googleTokenService, result); + } + + @Test + public void testGetTokenService_CaseInsensitiveProvider_ReturnsService() throws OAuth2AuthenticationException { + // Act + TokenService result = tokenServiceFactory.getTokenService("GOOGLE"); + + // Assert + assertNotNull(result); + assertEquals(googleTokenService, result); + } + + @Test + public void testGetTokenService_UnsupportedProvider_ThrowsException() { + // Arrange + String invalidProvider = "facebook"; + + // Act & Assert + OAuth2AuthenticationException exception = assertThrows(OAuth2AuthenticationException.class, + () -> tokenServiceFactory.getTokenService(invalidProvider)); + + assertEquals(ErrorConstants.INVALID_REQUEST.getErrorCode(), exception.getErrorCode()); + assertEquals("invalid_request --> Unsupported provider: " + invalidProvider, exception.getMessage()); + assertEquals(HttpStatus.BAD_REQUEST, exception.getStatus()); + } + +} diff --git a/src/test/java/io/mosip/mimoto/service/UserMetadataServiceTest.java b/src/test/java/io/mosip/mimoto/service/UserMetadataServiceTest.java new file mode 100644 index 000000000..1f04f43d7 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/UserMetadataServiceTest.java @@ -0,0 +1,170 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.exception.DecryptionException; +import io.mosip.mimoto.exception.EncryptionException; +import io.mosip.mimoto.model.UserMetadata; +import io.mosip.mimoto.repository.UserMetadataRepository; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.time.Instant; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class UserMetadataServiceTest { + + @Mock + private UserMetadataRepository userMetadataRepository; + + @Mock + private EncryptionService encryptionService; + + @InjectMocks + private UserMetadataService userMetadataService; + + private String providerSubjectId, identityProvider, displayName, profilePictureUrl, email, userId; + private Instant now; + private UserMetadata userMetadata; + + @Before + public void setUp() { + providerSubjectId = "provider123"; + identityProvider = "google"; + displayName = "Name 123"; + profilePictureUrl = "http://profile.pic"; + email = "name.123@example.com"; + now = Instant.now(); + userId = UUID.randomUUID().toString(); + + userMetadata = new UserMetadata(); + userMetadata.setId(userId); + userMetadata.setProviderSubjectId(providerSubjectId); + userMetadata.setIdentityProvider(identityProvider); + userMetadata.setDisplayName("encryptedDisplayName"); + userMetadata.setProfilePictureUrl("encryptedProfilePictureUrl"); + userMetadata.setEmail("encryptedEmail"); + userMetadata.setCreatedAt(now); + userMetadata.setUpdatedAt(now); + } + + @Test + public void shouldUpdateUserMetadataForSameProviderSubjectIdAndSameIdentityProviderButWithDifferentDisplayName() throws DecryptionException, EncryptionException { + String updatedDisplayName = "Name 124"; + String encryptedUpdatedDisplayName = "encryptedUpdatedDisplayName"; + + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(providerSubjectId, identityProvider)).thenReturn(Optional.of(userMetadata)); + when(encryptionService.decrypt("encryptedDisplayName")).thenReturn(displayName); + when(encryptionService.encrypt(updatedDisplayName)).thenReturn(encryptedUpdatedDisplayName); + when(encryptionService.decrypt("encryptedProfilePictureUrl")).thenReturn(profilePictureUrl); + when(encryptionService.encrypt(profilePictureUrl)).thenReturn("encryptedProfilePictureUrl"); + when(encryptionService.decrypt("encryptedEmail")).thenReturn(email); + when(encryptionService.encrypt(email)).thenReturn("encryptedEmail"); + when(userMetadataRepository.save(any(UserMetadata.class))).thenReturn(userMetadata); + + String storedUserId = userMetadataService.updateOrCreateUserMetadata(providerSubjectId, identityProvider, updatedDisplayName, profilePictureUrl, email); + + assertEquals(userId, storedUserId); + ArgumentCaptor userMetadataCaptor = ArgumentCaptor.forClass(UserMetadata.class); + verify(userMetadataRepository).save(userMetadataCaptor.capture()); + UserMetadata savedUserMetadata = userMetadataCaptor.getValue(); + assertEquals(encryptedUpdatedDisplayName, savedUserMetadata.getDisplayName()); + assertEquals("encryptedProfilePictureUrl", savedUserMetadata.getProfilePictureUrl()); + assertEquals("encryptedEmail", savedUserMetadata.getEmail()); + verify(encryptionService).decrypt("encryptedDisplayName"); + verify(encryptionService).encrypt(updatedDisplayName); + } + + @Test + public void shouldCreateNewUserMetadataForSameProviderSubjectIdAndDifferentIdentityProvider() throws DecryptionException, EncryptionException { + String newIdentityProvider = "facebook"; + String newUserId = UUID.randomUUID().toString(); + UserMetadata newUserMetadata = new UserMetadata(); + newUserMetadata.setId(newUserId); + newUserMetadata.setProviderSubjectId(providerSubjectId); + newUserMetadata.setIdentityProvider(newIdentityProvider); + newUserMetadata.setDisplayName("encryptedDisplayName"); + newUserMetadata.setProfilePictureUrl("encryptedProfilePictureUrl"); + newUserMetadata.setEmail("encryptedEmail"); + newUserMetadata.setCreatedAt(now); + newUserMetadata.setUpdatedAt(now); + + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(providerSubjectId, newIdentityProvider)).thenReturn(Optional.empty()); + when(encryptionService.encrypt(displayName)).thenReturn("encryptedDisplayName"); + when(encryptionService.encrypt(profilePictureUrl)).thenReturn("encryptedProfilePictureUrl"); + when(encryptionService.encrypt(email)).thenReturn("encryptedEmail"); + when(userMetadataRepository.save(any(UserMetadata.class))).thenReturn(newUserMetadata); + + String storedUserId = userMetadataService.updateOrCreateUserMetadata(providerSubjectId, newIdentityProvider, displayName, profilePictureUrl, email); + + assertEquals(newUserId, storedUserId); + ArgumentCaptor userMetadataCaptor = ArgumentCaptor.forClass(UserMetadata.class); + verify(userMetadataRepository).save(userMetadataCaptor.capture()); + UserMetadata capturedUserMetadata = userMetadataCaptor.getValue(); + assertEquals(providerSubjectId, capturedUserMetadata.getProviderSubjectId()); + assertEquals(newIdentityProvider, capturedUserMetadata.getIdentityProvider()); + assertEquals("encryptedDisplayName", capturedUserMetadata.getDisplayName()); + assertEquals("encryptedProfilePictureUrl", capturedUserMetadata.getProfilePictureUrl()); + assertEquals("encryptedEmail", capturedUserMetadata.getEmail()); + verify(encryptionService, times(3)).encrypt(anyString()); + } + + @Test + public void shouldCreateNewUserMetadataForDifferentProviderSubjectIdAndSameIdentityProvider() throws DecryptionException, EncryptionException { + String newProviderSubjectId = "provider124"; + String newUserId = UUID.randomUUID().toString(); + UserMetadata newUserMetadata = new UserMetadata(); + newUserMetadata.setId(newUserId); + newUserMetadata.setProviderSubjectId(newProviderSubjectId); + newUserMetadata.setIdentityProvider(identityProvider); + newUserMetadata.setDisplayName("encryptedDisplayName"); + newUserMetadata.setProfilePictureUrl("encryptedProfilePictureUrl"); + newUserMetadata.setEmail("encryptedEmail"); + newUserMetadata.setCreatedAt(now); + newUserMetadata.setUpdatedAt(now); + + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(newProviderSubjectId, identityProvider)).thenReturn(Optional.empty()); + when(encryptionService.encrypt(displayName)).thenReturn("encryptedDisplayName"); + when(encryptionService.encrypt(profilePictureUrl)).thenReturn("encryptedProfilePictureUrl"); + when(encryptionService.encrypt(email)).thenReturn("encryptedEmail"); + when(userMetadataRepository.save(any(UserMetadata.class))).thenReturn(newUserMetadata); + + String storedUserId = userMetadataService.updateOrCreateUserMetadata(newProviderSubjectId, identityProvider, displayName, profilePictureUrl, email); + + assertEquals(newUserId, storedUserId); + ArgumentCaptor userMetadataCaptor = ArgumentCaptor.forClass(UserMetadata.class); + verify(userMetadataRepository).save(userMetadataCaptor.capture()); + UserMetadata capturedUserMetadata = userMetadataCaptor.getValue(); + assertEquals(newProviderSubjectId, capturedUserMetadata.getProviderSubjectId()); + assertEquals(identityProvider, capturedUserMetadata.getIdentityProvider()); + assertEquals("encryptedDisplayName", capturedUserMetadata.getDisplayName()); + assertEquals("encryptedProfilePictureUrl", capturedUserMetadata.getProfilePictureUrl()); + assertEquals("encryptedEmail", capturedUserMetadata.getEmail()); + verify(encryptionService, times(3)).encrypt(anyString()); + } + + @Test + public void shouldNotCreateNewUserMetadataForSameProviderSubjectIdAndSameIdentityProvider() throws DecryptionException, EncryptionException { + when(userMetadataRepository.findByProviderSubjectIdAndIdentityProvider(providerSubjectId, identityProvider)).thenReturn(Optional.of(userMetadata)); + when(encryptionService.decrypt(userMetadata.getDisplayName())).thenReturn(displayName); + when(encryptionService.decrypt(userMetadata.getProfilePictureUrl())).thenReturn(profilePictureUrl); + when(encryptionService.decrypt(userMetadata.getEmail())).thenReturn(email); + + String storedUserId = userMetadataService.updateOrCreateUserMetadata(providerSubjectId, identityProvider, displayName, profilePictureUrl, email); + + assertEquals(userId, storedUserId); + verify(userMetadataRepository, times(0)).save(any(UserMetadata.class)); + verify(encryptionService).decrypt(userMetadata.getDisplayName()); + verify(encryptionService).decrypt(userMetadata.getProfilePictureUrl()); + verify(encryptionService).decrypt(userMetadata.getEmail()); + verify(encryptionService, times(3)).encrypt(anyString()); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/WalletCredentialServiceTest.java b/src/test/java/io/mosip/mimoto/service/WalletCredentialServiceTest.java new file mode 100644 index 000000000..50e7c85a4 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/WalletCredentialServiceTest.java @@ -0,0 +1,429 @@ +package io.mosip.mimoto.service; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.mimoto.model.CredentialMetadata; +import io.mosip.mimoto.model.VerifiableCredential; +import io.mosip.mimoto.dto.IssuerDTO; +import io.mosip.mimoto.dto.idp.TokenResponseDTO; +import io.mosip.mimoto.dto.mimoto.*; +import io.mosip.mimoto.dto.resident.WalletCredentialResponseDTO; +import io.mosip.mimoto.exception.*; +import io.mosip.mimoto.repository.WalletCredentialsRepository; +import io.mosip.mimoto.service.impl.WalletCredentialServiceImpl; +import io.mosip.mimoto.util.CredentialProcessor; +import io.mosip.mimoto.util.EncryptionDecryptionUtil; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.ByteArrayInputStream; +import java.lang.reflect.Field; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import static io.mosip.mimoto.exception.ErrorConstants.*; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class WalletCredentialServiceTest { + + @InjectMocks + private WalletCredentialServiceImpl walletCredentialService; + + @Mock + private WalletCredentialsRepository walletCredentialsRepository; + + @Mock + private IssuersService issuersService; + + @Mock + private ObjectMapper objectMapper; + + @Mock + private CredentialProcessor credentialProcessor; + + @Mock + private CredentialPDFGeneratorService credentialPDFGeneratorService; + + @Mock + private EncryptionDecryptionUtil encryptionDecryptionUtil; + + private final String walletId = "wallet123"; + private final String issuerId = "issuer123"; + private final String credentialType = "CredentialType1"; + private final String credentialId = "cred123"; + private final String base64Key = "ZHVtbXlrZXkxMjM0NTY3OA=="; // Base64 of "dummykey12345678" + private final String locale = "en"; + + private TokenResponseDTO tokenResponse; + private VerifiableCredential verifiableCredential; + private IssuerConfig issuerConfig; + + @Before + public void setUp() throws Exception { + tokenResponse = new TokenResponseDTO(); + tokenResponse.setAccess_token("accessToken"); + + verifiableCredential = new VerifiableCredential(); + verifiableCredential.setId(credentialId); + verifiableCredential.setWalletId(walletId); + verifiableCredential.setCredential("encryptedCredential"); + CredentialMetadata metadata = new CredentialMetadata(); + metadata.setIssuerId(issuerId); + metadata.setCredentialType(credentialType); + verifiableCredential.setCredentialMetadata(metadata); + verifiableCredential.setCreatedAt(Instant.now()); + verifiableCredential.setUpdatedAt(Instant.now()); + + IssuerDTO issuerDTO = new IssuerDTO(); + issuerDTO.setIssuer_id(issuerId); + CredentialIssuerWellKnownResponse wellKnownResponse = new CredentialIssuerWellKnownResponse(); + CredentialsSupportedResponse credentialsSupportedResponse = new CredentialsSupportedResponse(); + issuerConfig = new IssuerConfig(issuerDTO, wellKnownResponse, credentialsSupportedResponse); + + Field field = WalletCredentialServiceImpl.class.getDeclaredField("issuersWithSingleVcLimit"); + field.setAccessible(true); + field.set(walletCredentialService, "Mosip"); + } + + @Test + public void shouldDownloadVCAndStoreInDBSuccessfully() throws Exception { + String mosipIssuerId = "Mosip"; + VerifiableCredentialResponseDTO expectedResponse = new VerifiableCredentialResponseDTO(); + expectedResponse.setCredentialId(credentialId); + + when(walletCredentialsRepository.existsByIssuerIdAndCredentialTypeAndWalletId(mosipIssuerId, credentialType, walletId)).thenReturn(false); + when(credentialProcessor.downloadCredentialAndStoreInDB(tokenResponse, credentialType, walletId, base64Key, mosipIssuerId, locale)) + .thenReturn(expectedResponse); + + VerifiableCredentialResponseDTO actualResponse = walletCredentialService.downloadVCAndStoreInDB( + mosipIssuerId, credentialType, tokenResponse, locale, walletId, base64Key); + + assertEquals(expectedResponse, actualResponse); + verify(walletCredentialsRepository).existsByIssuerIdAndCredentialTypeAndWalletId(mosipIssuerId, credentialType, walletId); + verify(credentialProcessor).downloadCredentialAndStoreInDB(tokenResponse, credentialType, walletId, base64Key, mosipIssuerId, locale); + } + + @Test + public void shouldThrowDuplicateCredentialExceptionForMosipIssuer() { + when(walletCredentialsRepository.existsByIssuerIdAndCredentialTypeAndWalletId("Mosip", credentialType, walletId)).thenReturn(true); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + walletCredentialService.downloadVCAndStoreInDB("Mosip", credentialType, tokenResponse, locale, walletId, base64Key)); + + assertEquals(CREDENTIAL_DOWNLOAD_EXCEPTION.getErrorCode(), exception.getErrorCode()); + assertEquals("credential_download_error --> Duplicate credential for issuer and type", exception.getMessage()); + verify(walletCredentialsRepository).existsByIssuerIdAndCredentialTypeAndWalletId("Mosip", credentialType, walletId); + verifyNoInteractions(credentialProcessor); + } + + @Test + public void shouldFetchAndStoreNonMosipIssuerCredential() throws Exception { + VerifiableCredentialResponseDTO expectedResponse = new VerifiableCredentialResponseDTO(); + expectedResponse.setCredentialId(credentialId); + + when(credentialProcessor.downloadCredentialAndStoreInDB(tokenResponse, credentialType, walletId, base64Key, issuerId, locale)) + .thenReturn(expectedResponse); + + VerifiableCredentialResponseDTO actualResponse = walletCredentialService.downloadVCAndStoreInDB( + issuerId, credentialType, tokenResponse, locale, walletId, base64Key); + + assertEquals(expectedResponse, actualResponse); + verify(credentialProcessor).downloadCredentialAndStoreInDB(tokenResponse, credentialType, walletId, base64Key, issuerId, locale); + } + + @Test + public void shouldThrowExternalServiceUnavailableException() throws Exception { + String mosipIssuerId = "Mosip"; // Use Mosip to trigger repository check + + when(walletCredentialsRepository.existsByIssuerIdAndCredentialTypeAndWalletId(mosipIssuerId, credentialType, walletId)).thenReturn(false); + when(credentialProcessor.downloadCredentialAndStoreInDB(any(), anyString(), anyString(), anyString(), anyString(), anyString())) + .thenThrow(new ExternalServiceUnavailableException("SERVICE_UNAVAILABLE", "Service unavailable")); + + ExternalServiceUnavailableException exception = assertThrows(ExternalServiceUnavailableException.class, () -> + walletCredentialService.downloadVCAndStoreInDB(mosipIssuerId, credentialType, tokenResponse, locale, walletId, base64Key)); + + assertEquals("SERVICE_UNAVAILABLE", exception.getErrorCode()); + assertEquals("SERVICE_UNAVAILABLE --> Service unavailable", exception.getMessage()); + verify(walletCredentialsRepository).existsByIssuerIdAndCredentialTypeAndWalletId(mosipIssuerId, credentialType, walletId); + verify(credentialProcessor).downloadCredentialAndStoreInDB(tokenResponse, credentialType, walletId, base64Key, mosipIssuerId, locale); + } + + @Test + public void shouldFetchAllCredentialsForWalletSuccessfully() throws Exception { + VerifiableCredentialResponseDTO responseDTO = new VerifiableCredentialResponseDTO(); + responseDTO.setCredentialId(credentialId); + + when(walletCredentialsRepository.findByWalletIdOrderByCreatedAtDesc(walletId)).thenReturn(List.of(verifiableCredential)); + when(issuersService.getIssuerConfig(issuerId, credentialType)).thenReturn(issuerConfig); + + try (MockedStatic factoryMock = mockStatic(VerifiableCredentialResponseDTO.class)) { + factoryMock.when(() -> VerifiableCredentialResponseDTO.fromIssuerConfig(issuerConfig, locale, credentialId)) + .thenReturn(responseDTO); + + List actualCredentials = walletCredentialService.fetchAllCredentialsForWallet(walletId, base64Key, locale); + + assertEquals(1, actualCredentials.size()); + assertEquals(responseDTO, actualCredentials.getFirst()); + verify(walletCredentialsRepository).findByWalletIdOrderByCreatedAtDesc(walletId); + verify(issuersService).getIssuerConfig(issuerId, credentialType); + factoryMock.verify(() -> VerifiableCredentialResponseDTO.fromIssuerConfig(issuerConfig, locale, credentialId)); + } + } + + @Test + public void shouldHandleIssuerConfigFetchFailure() throws Exception { + VerifiableCredentialResponseDTO responseDTO = new VerifiableCredentialResponseDTO(); + responseDTO.setCredentialId(credentialId); + + when(walletCredentialsRepository.findByWalletIdOrderByCreatedAtDesc(walletId)).thenReturn(List.of(verifiableCredential)); + when(issuersService.getIssuerConfig(issuerId, credentialType)).thenThrow(new ApiNotAccessibleException("API error")); + + try (MockedStatic factoryMock = mockStatic(VerifiableCredentialResponseDTO.class)) { + factoryMock.when(() -> VerifiableCredentialResponseDTO.fromIssuerConfig(null, locale, credentialId)) + .thenReturn(responseDTO); + + List actualCredentials = walletCredentialService.fetchAllCredentialsForWallet(walletId, base64Key, locale); + + assertEquals(1, actualCredentials.size()); + assertEquals(responseDTO, actualCredentials.getFirst()); + verify(walletCredentialsRepository).findByWalletIdOrderByCreatedAtDesc(walletId); + verify(issuersService).getIssuerConfig(issuerId, credentialType); + factoryMock.verify(() -> VerifiableCredentialResponseDTO.fromIssuerConfig(null, locale, credentialId)); + } + } + + @Test + public void shouldFetchVerifiableCredentialSuccessfully() throws Exception { + // Mocked VC JSON as String + VCCredentialResponse vcResponse = VCCredentialResponse.builder() + .format("ldp_vc") + .credential(VCCredentialProperties.builder() + .type(List.of(credentialType)) + .issuer("issuer123") + .issuanceDate("2024-01-01T00:00:00Z") + .credentialSubject(Map.of("name", "John Doe")) + .build()) + .build(); + + String decryptedCredentialJson = new ObjectMapper().writeValueAsString(vcResponse); + + // Setup repository + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)) + .thenReturn(Optional.of(verifiableCredential)); + + // Setup decryption + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)) + .thenReturn(decryptedCredentialJson); + when(objectMapper.readValue(decryptedCredentialJson, VCCredentialResponse.class)) + .thenReturn(vcResponse); + // IssuerDTO + IssuerDTO issuerDTO = new IssuerDTO(); + issuerDTO.setIssuer_id(issuerId); + when(issuersService.getIssuerDetails(issuerId)).thenReturn(issuerDTO); + + // Credential Definition that matches VC type + CredentialDefinitionResponseDto credentialDefinition = new CredentialDefinitionResponseDto(); + credentialDefinition.setType(List.of(credentialType)); + credentialDefinition.setCredentialSubject(Map.of()); // safe stub + credentialDefinition.setContext(List.of("https://www.w3.org/2018/credentials/v1")); + + // Full IssuerConfig + CredentialsSupportedResponse supportedResponse = new CredentialsSupportedResponse(); + supportedResponse.setCredentialDefinition(credentialDefinition); + supportedResponse.setProofTypesSupported(Map.of("ldp_vc", new ProofTypesSupported())); + supportedResponse.setDisplay(List.of()); + supportedResponse.setFormat("ldp_vc"); + supportedResponse.setScope("scope"); + + IssuerConfig issuerConfig = new IssuerConfig(issuerDTO, new CredentialIssuerWellKnownResponse(), supportedResponse); + + when(issuersService.getIssuerConfig(issuerId, credentialType)).thenReturn(issuerConfig); + + // Setup PDF stream + ByteArrayInputStream pdfContent = new ByteArrayInputStream("PDF Content".getBytes()); + when(credentialPDFGeneratorService.generatePdfForVerifiableCredentials( + eq(credentialType), + any(VCCredentialResponse.class), + eq(issuerDTO), + eq(supportedResponse), + eq(""), + eq(null), + eq(locale) + )).thenReturn(pdfContent); + + // Run + WalletCredentialResponseDTO actualResponse = walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale); + + // Assert + assertNotNull(actualResponse); + assertEquals(String.format("%s_credential.pdf", credentialType), actualResponse.getFileName()); + assertNotNull(actualResponse.getFileContentStream()); + + verify(walletCredentialsRepository).findByIdAndWalletId(credentialId, walletId); + verify(encryptionDecryptionUtil).decryptCredential("encryptedCredential", base64Key); + verify(issuersService).getIssuerDetails(issuerId); + verify(issuersService).getIssuerConfig(issuerId, credentialType); + verify(credentialPDFGeneratorService).generatePdfForVerifiableCredentials( + eq(credentialType), + any(VCCredentialResponse.class), + eq(issuerDTO), + eq(supportedResponse), + eq(""), + eq(null), + eq(locale) + ); + } + + + + @Test + public void shouldThrowCredentialNotFoundException() { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.empty()); + + CredentialNotFoundException exception = assertThrows(CredentialNotFoundException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(RESOURCE_NOT_FOUND.getErrorCode(), exception.getErrorCode()); + assertEquals(RESOURCE_NOT_FOUND.getErrorCode() +" --> "+RESOURCE_NOT_FOUND.getErrorMessage(), exception.getMessage()); + verify(walletCredentialsRepository).findByIdAndWalletId(credentialId, walletId); + verifyNoInteractions(encryptionDecryptionUtil, credentialPDFGeneratorService); + } + + @Test + public void shouldThrowCredentialProcessingExceptionOnDecryptionFailure() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)) + .thenThrow(new DecryptionException("DECRYPTION_ERROR", "Decryption failed")); + + CredentialProcessingException exception = assertThrows(CredentialProcessingException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), exception.getErrorCode()); + verify(walletCredentialsRepository).findByIdAndWalletId(credentialId, walletId); + verify(encryptionDecryptionUtil).decryptCredential("encryptedCredential", base64Key); + verifyNoInteractions(credentialPDFGeneratorService); + } + + @Test + public void shouldDeleteCredentialSuccessfully() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + + walletCredentialService.deleteCredential(credentialId, walletId); + + verify(walletCredentialsRepository).findByIdAndWalletId(credentialId, walletId); + verify(walletCredentialsRepository).deleteById(credentialId); + } + + @Test + public void shouldThrowCredentialNotFoundExceptionWhenDeletingNonExistentCredential() { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.empty()); + + CredentialNotFoundException exception = assertThrows(CredentialNotFoundException.class, () -> + walletCredentialService.deleteCredential(credentialId, walletId)); + + assertEquals(RESOURCE_NOT_FOUND.getErrorCode(), exception.getErrorCode()); + assertEquals(RESOURCE_NOT_FOUND.getErrorCode() + " --> " + RESOURCE_NOT_FOUND.getErrorMessage(), exception.getMessage()); + verify(walletCredentialsRepository).findByIdAndWalletId(credentialId, walletId); + verify(walletCredentialsRepository, never()).deleteById(anyString()); + } + + @Test + public void shouldThrowCredentialProcessingExceptionOnJsonProcessingError() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)).thenReturn("bad-json"); + when(objectMapper.readValue("bad-json", VCCredentialResponse.class)).thenThrow(new com.fasterxml.jackson.core.JsonProcessingException("error") {}); + + CredentialProcessingException exception = assertThrows(CredentialProcessingException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), exception.getErrorCode()); + verify(walletCredentialsRepository).findByIdAndWalletId(credentialId, walletId); + verify(encryptionDecryptionUtil).decryptCredential("encryptedCredential", base64Key); + verify(objectMapper).readValue("bad-json", VCCredentialResponse.class); + } + + @Test + public void shouldThrowCredentialProcessingExceptionOnNullIssuerConfig() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)).thenReturn("{}"); + VCCredentialResponse vcResponse = VCCredentialResponse.builder().credential(VCCredentialProperties.builder().type(List.of(credentialType)).build()).build(); + when(objectMapper.readValue(anyString(), eq(VCCredentialResponse.class))).thenReturn(vcResponse); + when(issuersService.getIssuerDetails(issuerId)).thenReturn(new IssuerDTO()); + when(issuersService.getIssuerConfig(issuerId, credentialType)).thenReturn(null); + + CredentialProcessingException exception = assertThrows(CredentialProcessingException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), exception.getErrorCode()); + } + + @Test + public void shouldThrowCredentialProcessingExceptionOnCredentialTypeMismatch() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)).thenReturn("{}"); + VCCredentialResponse vcResponse = VCCredentialResponse.builder().credential(VCCredentialProperties.builder().type(List.of("OtherType")).build()).build(); + when(objectMapper.readValue(anyString(), eq(VCCredentialResponse.class))).thenReturn(vcResponse); + when(issuersService.getIssuerDetails(issuerId)).thenReturn(new IssuerDTO()); + + CredentialDefinitionResponseDto credentialDefinition = new CredentialDefinitionResponseDto(); + credentialDefinition.setType(List.of(credentialType)); + CredentialsSupportedResponse supportedResponse = new CredentialsSupportedResponse(); + supportedResponse.setCredentialDefinition(credentialDefinition); + + IssuerConfig issuerConfig = new IssuerConfig(new IssuerDTO(), new CredentialIssuerWellKnownResponse(), supportedResponse); + when(issuersService.getIssuerConfig(issuerId, credentialType)).thenReturn(issuerConfig); + + CredentialProcessingException exception = assertThrows(CredentialProcessingException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), exception.getErrorCode()); + } + + @Test + public void shouldThrowCredentialProcessingExceptionOnIssuerServiceException() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)).thenReturn("{}"); + VCCredentialResponse vcResponse = VCCredentialResponse.builder().credential(VCCredentialProperties.builder().type(List.of(credentialType)).build()).build(); + when(objectMapper.readValue(anyString(), eq(VCCredentialResponse.class))).thenReturn(vcResponse); + when(issuersService.getIssuerDetails(issuerId)).thenThrow(new ApiNotAccessibleException("API error")); + + CredentialProcessingException exception = assertThrows(CredentialProcessingException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), exception.getErrorCode()); + } + + @Test + public void shouldThrowCredentialProcessingExceptionOnPdfGenerationException() throws Exception { + when(walletCredentialsRepository.findByIdAndWalletId(credentialId, walletId)).thenReturn(Optional.of(verifiableCredential)); + when(encryptionDecryptionUtil.decryptCredential("encryptedCredential", base64Key)).thenReturn("{}"); + VCCredentialResponse vcResponse = VCCredentialResponse.builder().credential(VCCredentialProperties.builder().type(List.of(credentialType)).build()).build(); + when(objectMapper.readValue(anyString(), eq(VCCredentialResponse.class))).thenReturn(vcResponse); + when(issuersService.getIssuerDetails(issuerId)).thenReturn(new IssuerDTO()); + + CredentialDefinitionResponseDto credentialDefinition = new CredentialDefinitionResponseDto(); + credentialDefinition.setType(List.of(credentialType)); + CredentialsSupportedResponse supportedResponse = new CredentialsSupportedResponse(); + supportedResponse.setCredentialDefinition(credentialDefinition); + + IssuerConfig issuerConfig = new IssuerConfig(new IssuerDTO(), new CredentialIssuerWellKnownResponse(), supportedResponse); + when(issuersService.getIssuerConfig(issuerId, credentialType)).thenReturn(issuerConfig); + + when(credentialPDFGeneratorService.generatePdfForVerifiableCredentials(any(), any(), any(), any(), any(), any(), any())) + .thenThrow(new RuntimeException("PDF error")); + + CredentialProcessingException exception = assertThrows(CredentialProcessingException.class, () -> + walletCredentialService.fetchVerifiableCredential(walletId, credentialId, base64Key, locale)); + + assertEquals(CREDENTIAL_FETCH_EXCEPTION.getErrorCode(), exception.getErrorCode()); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/service/WalletServiceTest.java b/src/test/java/io/mosip/mimoto/service/WalletServiceTest.java new file mode 100644 index 000000000..0a4a80026 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/service/WalletServiceTest.java @@ -0,0 +1,246 @@ +package io.mosip.mimoto.service; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.model.Wallet; +import io.mosip.mimoto.model.WalletMetadata; +import io.mosip.mimoto.dto.WalletResponseDto; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.UnauthorizedAccessException; +import io.mosip.mimoto.repository.WalletRepository; +import io.mosip.mimoto.service.impl.WalletServiceImpl; +import io.mosip.mimoto.util.WalletUtil; +import io.mosip.mimoto.util.WalletValidator; +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.mock.web.MockHttpSession; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class WalletServiceTest { + + @Mock + private WalletRepository walletRepository; + + @Mock + private WalletUtil walletHelper; + + @Mock + private WalletValidator walletValidator; + + @InjectMocks + private WalletServiceImpl walletService; + + MockHttpSession mockSession; + + private String userId, name, walletId, walletPin, walletConfirmPin, encryptedWalletKey, decryptedWalletKey; + private Wallet wallet; + + @Before + public void setUp() { + userId = UUID.randomUUID().toString(); + walletId = UUID.randomUUID().toString(); + walletPin = "1234"; + walletConfirmPin = "1234"; + name = "default"; + encryptedWalletKey = "encryptedKey"; + decryptedWalletKey = "decryptedKey"; + + mockSession = new MockHttpSession(); + mockSession.setAttribute("clientRegistrationId", "google"); + mockSession.setAttribute(SessionKeys.USER_ID, userId); + + wallet = new Wallet(); + wallet.setId(walletId); + wallet.setUserId(userId); + wallet.setWalletKey(encryptedWalletKey); + WalletMetadata walletMetadata = new WalletMetadata(); + walletMetadata.setEncryptionAlgo("AES"); + walletMetadata.setEncryptionType("symmetric"); + walletMetadata.setName("default"); + wallet.setWalletMetadata(walletMetadata); + } + + @Test + public void shouldCreateWalletSuccessfully() { + String newWalletId = UUID.randomUUID().toString(); + when(walletHelper.createWallet(userId, name, walletPin)).thenReturn(newWalletId); + + WalletResponseDto result = walletService.createWallet(userId, name, walletPin, walletConfirmPin); + + assertEquals(newWalletId, result.getWalletId()); + assertEquals("default", result.getWalletName()); + verify(walletValidator).validateUserId(userId); + verify(walletValidator).validateWalletName(name); + verify(walletValidator).validateWalletPin(walletPin); + verify(walletHelper).createWallet(userId, name, walletPin); + } + + + @Test + public void shouldThrowUnAuthorizedAccessExceptionOnCreatingWalletIfUserIdNotFoundInSession() { + // In case of userId (or any key) not available in session, the default value will be null. + doThrow(new UnauthorizedAccessException("unauthorized", "User ID not found in session")).when(walletValidator).validateUserId(null); + + UnauthorizedAccessException exception = assertThrows(UnauthorizedAccessException.class, () -> + walletService.createWallet(null, name, walletPin, walletConfirmPin)); + + assertEquals("unauthorized", exception.getErrorCode()); + assertEquals("unauthorized --> User ID not found in session", exception.getMessage()); + } + + @Test + public void shouldCreateWalletSuccessfullyIfWalletNameIsNotProvided() { + String newWalletId = UUID.randomUUID().toString(); + when(walletHelper.createWallet(userId, null, walletPin)).thenReturn(newWalletId); + + WalletResponseDto result = walletService.createWallet(userId, null, walletPin, walletConfirmPin); + + assertEquals(newWalletId, result.getWalletId()); + assertNull(result.getWalletName()); + verify(walletValidator).validateUserId(userId); + verify(walletValidator).validateWalletName(null); + verify(walletValidator).validateWalletPin(walletPin); + + verify(walletHelper).createWallet(userId, null, walletPin); + } + + @Test + public void shouldUnlockWalletSuccessfully() { + when(walletRepository.findByUserIdAndId(userId, walletId)).thenReturn(Optional.of(wallet)); + when(walletHelper.decryptWalletKey(encryptedWalletKey, walletPin)).thenReturn(decryptedWalletKey); + + WalletResponseDto result = walletService.unlockWallet(walletId, walletPin, mockSession); + + verify(walletRepository).findByUserIdAndId(userId, walletId); + verify(walletHelper).decryptWalletKey(encryptedWalletKey, walletPin); + assertEquals(walletId, result.getWalletId()); + assertEquals("default", result.getWalletName()); + assertEquals(decryptedWalletKey, mockSession.getAttribute(SessionKeys.WALLET_KEY)); + assertEquals(walletId, mockSession.getAttribute(SessionKeys.WALLET_ID)); + } + + @Test + public void shouldThrowInvalidRequestExceptionIfWalletNotFoundForGivenUserIdAndWalletId() { + when(walletRepository.findByUserIdAndId(userId, walletId)).thenReturn(Optional.empty()); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + walletService.unlockWallet(walletId, walletPin, mockSession)); + + assertEquals("invalid_request", exception.getErrorCode()); + assertEquals("invalid_request --> Wallet not found", exception.getMessage()); + verify(walletRepository).findByUserIdAndId(userId, walletId); + verifyNoInteractions(walletHelper); + } + + @Test + public void shouldThrowInvalidPinErrorCodeExceptionWhenDecryptingWalletKeyWithInvalidPin() { + String invalidPin = "wrongPin"; + when(walletRepository.findByUserIdAndId(userId, walletId)).thenReturn(Optional.of(wallet)); + when(walletHelper.decryptWalletKey(encryptedWalletKey, invalidPin)).thenThrow(new InvalidRequestException("invalid_pin", "Invalid PIN or wallet key provided")); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + walletService.unlockWallet(walletId, invalidPin, mockSession)); + + verify(walletRepository).findByUserIdAndId(userId, walletId); + assertEquals("invalid_pin", exception.getErrorCode()); + assertEquals("invalid_pin --> Invalid PIN or wallet key provided", exception.getMessage()); + } + + @Test + public void shouldReturnListOfWalletResponseDtosForGivenUserId() { + String walletId1 = UUID.randomUUID().toString(); + String walletId2 = UUID.randomUUID().toString(); + WalletMetadata walletMetadata = new WalletMetadata(); + walletMetadata.setEncryptionAlgo("AES"); + walletMetadata.setEncryptionType("symmetric"); + walletMetadata.setName("Test Wallet"); + List walletIds = Arrays.asList(walletId1, walletId2); + List walletNames = Arrays.asList(null, "Test Wallet"); + Wallet wallet1 = new Wallet( + walletId1, "mock-user-id", "mock-encrypted-key", new WalletMetadata(), List.of(), Instant.now(), Instant.now() + ); + Wallet wallet2 = new Wallet( + walletId2, "mock-user-id-2", "mock-encrypted-key", walletMetadata, List.of(), Instant.now(), Instant.now() + ); + when(walletRepository.findWalletByUserId(userId)).thenReturn(List.of(wallet1, wallet2)); + + List result = walletService.getWallets(userId); + + assertEquals(walletIds.size(), result.size()); + for (int i = 0; i < walletIds.size(); i++) { + assertEquals(walletIds.get(i), result.get(i).getWalletId()); + assertEquals(walletNames.get(i), result.get(i).getWalletName()); + } + verify(walletRepository).findWalletByUserId(userId); + } + + @Test + public void shouldReturnEmptyListIfNoWalletsFoundForGivenUserId() { + when(walletRepository.findWalletByUserId(userId)).thenReturn(List.of()); + + List result = walletService.getWallets(userId); + + assertTrue(result.isEmpty()); + verify(walletRepository).findWalletByUserId(userId); + } + + @Test + public void shouldThrowInvalidRequestExceptionIfAnyErrorOccurredWhileCreatingWallet() { + when(walletHelper.createWallet(userId, name, walletPin)).thenThrow(new InvalidRequestException("INVALID_REQUEST", "Test Exception")); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + walletService.createWallet(userId, name, walletPin, walletConfirmPin)); + + assertEquals("INVALID_REQUEST", exception.getErrorCode()); + assertEquals("INVALID_REQUEST --> Test Exception", exception.getMessage()); + verify(walletHelper).createWallet(userId, name, walletPin); + } + + @Test + public void shouldThrowInvalidRequestExceptionIfReceivedPINAndConfirmPINDoNotMatch() { + walletConfirmPin = "2345"; + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + walletService.createWallet(userId, name, walletPin, walletConfirmPin)); + + assertEquals("invalid_request", exception.getErrorCode()); + assertEquals("invalid_request --> Entered PIN and Confirm PIN do not match", exception.getMessage()); + } + + @Test + public void shouldDeleteWalletSuccessfully() { + when(walletRepository.findByUserIdAndId(userId, walletId)).thenReturn(Optional.of(wallet)); + + walletService.deleteWallet(userId, walletId); + + verify(walletValidator).validateUserId(userId); + verify(walletRepository).findByUserIdAndId(userId, walletId); + verify(walletRepository).delete(wallet); + } + + @Test + public void shouldThrowInvalidRequestExceptionWhenDeletingNonExistentWallet() { + when(walletRepository.findByUserIdAndId(userId, walletId)).thenReturn(Optional.empty()); + + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> + walletService.deleteWallet(userId, walletId)); + + assertEquals("invalid_request", exception.getErrorCode()); + assertEquals("invalid_request --> Wallet not found", exception.getMessage()); + verify(walletValidator).validateUserId(userId); + verify(walletRepository).findByUserIdAndId(userId, walletId); + verify(walletRepository, never()).delete(any(Wallet.class)); + } +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java b/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java index 21c69f7bf..dee6e4973 100644 --- a/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java +++ b/src/test/java/io/mosip/mimoto/util/CredentialIssuerWellknownResponseValidatorTest.java @@ -5,7 +5,6 @@ import io.mosip.mimoto.dto.mimoto.CredentialIssuerWellKnownResponse; import io.mosip.mimoto.dto.mimoto.CredentialSupportedDisplayResponse; import io.mosip.mimoto.dto.mimoto.CredentialsSupportedResponse; -import io.mosip.mimoto.exception.ApiNotAccessibleException; import io.mosip.mimoto.exception.InvalidWellknownResponseException; import jakarta.validation.Validation; import jakarta.validation.Validator; @@ -16,13 +15,10 @@ import org.springframework.beans.factory.annotation.Autowired; import java.util.*; -import java.util.stream.Collectors; import static io.mosip.mimoto.util.TestUtilities.getCredentialIssuerWellKnownResponseDto; import static io.mosip.mimoto.util.TestUtilities.getCredentialSupportedResponse; -import static org.junit.Assert.*; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.*; public class CredentialIssuerWellknownResponseValidatorTest { @@ -40,7 +36,7 @@ void setUp() { } @Test - public void shouldThrowExceptionWhenCredentialIssuerIsMissingInCredentialIssuerWellknownResponse() throws ApiNotAccessibleException { + public void shouldThrowExceptionWhenCredentialIssuerIsMissingInCredentialIssuerWellknownResponse() { response.setCredentialIssuer(""); response.setCredentialEndPoint("http://example.com/credential"); @@ -156,7 +152,7 @@ public void shouldDetectMissingMandatoryFields() { InvalidWellknownResponseException invalidWellknownResponseException = assertThrows(InvalidWellknownResponseException.class, () -> credentialIssuerWellknownResponseValidator.validate(response, validator)); assertEquals("RESIDENT-APP-041", invalidWellknownResponseException.getErrorCode()); - assertTrue(Arrays.stream(invalidWellknownResponseException.getMessage().split("\n")).collect(Collectors.toList()).containsAll(List.of("RESIDENT-APP-041 --> Invalid Wellknown from Issuer", + assertTrue(Arrays.stream(invalidWellknownResponseException.getMessage().split("\n")).toList().containsAll(List.of("RESIDENT-APP-041 --> Invalid Wellknown from Issuer", "Validation failed:", "credentialIssuer: must not be blank", "credentialConfigurationsSupported: must not be empty", @@ -192,10 +188,11 @@ public void shouldDetectMissingProofAlgorithmsSupported() { InvalidWellknownResponseException invalidWellknownResponseException = assertThrows(InvalidWellknownResponseException.class, () -> credentialIssuerWellknownResponseValidator.validate(response, validator)); - assertEquals(""" - RESIDENT-APP-041 --> Invalid Wellknown from Issuer - Validation failed: - credentialConfigurationsSupported[CredentialType1].proofTypesSupported[jwt].proofSigningAlgValuesSupported: must not be null""", invalidWellknownResponseException.getMessage()); + String message = invalidWellknownResponseException.getMessage(); + assertTrue(message.contains("RESIDENT-APP-041 --> Invalid Wellknown from Issuer")); + assertTrue(message.contains("credentialConfigurationsSupported[CredentialType1].proofTypesSupported[jwt].proofSigningAlgValuesSupported: must not be null")); + assertTrue(message.contains("credentialConfigurationsSupported[CredentialType1].proofTypesSupported[jwt].proofSigningAlgValuesSupported: must not be empty")); + } @Test @@ -216,7 +213,7 @@ public void shouldDetectMissingMandatoryFieldsOfCredentialSupportedDisplayRespon InvalidWellknownResponseException invalidWellknownResponseException = assertThrows(InvalidWellknownResponseException.class, () -> credentialIssuerWellknownResponseValidator.validate(response, validator) ); - assertTrue(Arrays.stream(invalidWellknownResponseException.getMessage().split("\n")).collect(Collectors.toList()).containsAll(Arrays.stream(""" + assertTrue(Arrays.stream(invalidWellknownResponseException.getMessage().split("\n")).toList().containsAll(Arrays.stream(""" RESIDENT-APP-041 --> Invalid Wellknown from Issuer Validation failed: credentialConfigurationsSupported[CredentialType1].display[0].backgroundColor: must not be blank diff --git a/src/test/java/io/mosip/mimoto/util/EncryptionDecryptionUtilTest.java b/src/test/java/io/mosip/mimoto/util/EncryptionDecryptionUtilTest.java new file mode 100644 index 000000000..a92afb111 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/util/EncryptionDecryptionUtilTest.java @@ -0,0 +1,123 @@ +package io.mosip.mimoto.util; + +import io.mosip.kernel.cryptomanager.dto.CryptomanagerRequestDto; +import io.mosip.kernel.cryptomanager.dto.CryptomanagerResponseDto; +import io.mosip.kernel.cryptomanager.service.CryptomanagerService; +import io.mosip.kernel.core.util.CryptoUtil; +import io.mosip.mimoto.constant.SigningAlgorithm; +import org.apache.commons.lang3.StringUtils; +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.test.util.ReflectionTestUtils; + +import javax.crypto.SecretKey; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.util.Arrays; +import java.util.Base64; + +import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class EncryptionDecryptionUtilTest { + + @Mock + private CryptomanagerService cryptomanagerService; + + @InjectMocks + private EncryptionDecryptionUtil encryptionDecryptionUtil; + + private final String refId = "ref123"; + private final String aad = "aad123"; + private final String salt = "salt123"; + private final String encryptedData = "encryptedData"; + private KeyPair keyPair; + + private SecretKey encryptionKey; + + + @Before + public void setUp() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException { + String appId = "MIMOTO"; + ReflectionTestUtils.setField(encryptionDecryptionUtil, "appId", appId); + encryptionKey = KeyGenerationUtil.generateEncryptionKey("AES", 256); + keyPair = KeyGenerationUtil.generateKeyPair(SigningAlgorithm.ED25519); + } + + @Test + public void shouldEncryptDataSuccessfully() { + CryptomanagerResponseDto responseDto = new CryptomanagerResponseDto(); + responseDto.setData(encryptedData); + when(cryptomanagerService.encrypt(any(CryptomanagerRequestDto.class))).thenReturn(responseDto); + + String data = "testData"; + String result = encryptionDecryptionUtil.encrypt(data, refId, aad, salt); + + assertEquals(encryptedData, result); + } + + @Test + public void shouldReturnNullIfDataToEncryptIsNull() { + String result = encryptionDecryptionUtil.encrypt(null, refId, aad, salt); + + assertNull(result); + } + + @Test + public void shouldDecryptDataSuccessfully() { + CryptomanagerResponseDto responseDto = new CryptomanagerResponseDto(); + String decryptedData = "testData"; + responseDto.setData(CryptoUtil.encodeToURLSafeBase64(decryptedData.getBytes(StandardCharsets.UTF_8))); + when(cryptomanagerService.decrypt(any(CryptomanagerRequestDto.class))).thenReturn(responseDto); + + String result = encryptionDecryptionUtil.decrypt(encryptedData, refId, aad, salt); + + assertEquals(decryptedData, result); + } + + @Test + public void shouldReturnNullIfDataToDecryptIsNull() { + String result = encryptionDecryptionUtil.decrypt(null, refId, aad, salt); + assertNull(result); + } + + + @Test + public void shouldEncryptPrivateKeyWithAESSuccessfully() throws Exception { + SecretKey aesKey = KeyGenerationUtil.generateEncryptionKey("AES", 256); + KeyPair keyPair = KeyGenerationUtil.generateKeyPair(SigningAlgorithm.ED25519); + + String encryptedPrivateKey = encryptionDecryptionUtil.encryptWithAES(aesKey, keyPair.getPrivate().getEncoded()); + + assertNotNull(encryptedPrivateKey); + assertFalse(StringUtils.isBlank(encryptedPrivateKey)); + } + + @Test + public void testIVChangesButCiphertextRemainsSameForSameEncryptionKeyAndSecretKey() throws Exception { + String encryptedPrivateKey1 = encryptionDecryptionUtil.encryptWithAES(encryptionKey, keyPair.getPrivate().getEncoded()); + String encryptedPrivateKey2 = encryptionDecryptionUtil.encryptWithAES(encryptionKey, keyPair.getPrivate().getEncoded()); + + byte[] encryptedBytes1 = Base64.getDecoder().decode(encryptedPrivateKey1); + byte[] encryptedBytes2 = Base64.getDecoder().decode(encryptedPrivateKey2); + + byte[] iv1 = Arrays.copyOfRange(encryptedBytes1, 0, 12); + byte[] iv2 = Arrays.copyOfRange(encryptedBytes2, 0, 12); + + byte[] decryptedPrivateKey1Bytes = encryptionDecryptionUtil.decryptWithAES(encryptionKey, encryptedPrivateKey1); + byte[] decryptedPrivateKey2Bytes = encryptionDecryptionUtil.decryptWithAES(encryptionKey, encryptedPrivateKey2); + PrivateKey decryptedPrivateKey1 = EncryptionDecryptionUtil.bytesToPrivateKey(decryptedPrivateKey1Bytes, "ed25519"); + PrivateKey decryptedPrivateKey2 = EncryptionDecryptionUtil.bytesToPrivateKey(decryptedPrivateKey2Bytes,"ed25519"); + + assertFalse(Arrays.equals(iv1, iv2), "IVs should be different"); + assertEquals(decryptedPrivateKey1, decryptedPrivateKey2); + } +} diff --git a/src/test/java/io/mosip/mimoto/util/GlobalExceptionHandlerTest.java b/src/test/java/io/mosip/mimoto/util/GlobalExceptionHandlerTest.java new file mode 100644 index 000000000..33fd24b94 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/util/GlobalExceptionHandlerTest.java @@ -0,0 +1,138 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.dto.ErrorDTO; +import io.mosip.mimoto.exception.ErrorConstants; +import io.mosip.mimoto.exception.ExternalServiceUnavailableException; +import io.mosip.mimoto.exception.InvalidRequestException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.dao.DataAccessResourceFailureException; +import org.springframework.http.HttpStatus; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.web.bind.annotation.ResponseStatus; + +import java.lang.reflect.Method; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(MockitoJUnitRunner.class) +public class GlobalExceptionHandlerTest { + + @InjectMocks + private GlobalExceptionHandler globalExceptionHandler; + + @Before + public void setUp() { + // Initialize any mocks if needed + } + + @Test + public void handleGenericExceptionReturnsInternalServerError() throws Exception { + // Arrange + Exception ex = new RuntimeException("Unexpected error"); + String expectedErrorCode = ErrorConstants.INTERNAL_SERVER_ERROR.getErrorCode(); + String expectedErrorMessage = ErrorConstants.INTERNAL_SERVER_ERROR.getErrorMessage(); + + // Act + ErrorDTO result = globalExceptionHandler.handleGenericException(ex); + + // Assert + assertEquals(expectedErrorCode, result.getErrorCode()); + assertEquals(expectedErrorMessage, result.getErrorMessage()); + + // Verify ResponseStatus annotation + Method method = GlobalExceptionHandler.class.getMethod("handleGenericException", Exception.class); + ResponseStatus responseStatus = method.getAnnotation(ResponseStatus.class); + assertNotNull(responseStatus); + assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, responseStatus.value()); + } + + @Test + public void handleDatabaseExceptionReturnsServiceUnavailable() throws Exception { + // Arrange + DataAccessResourceFailureException ex = new DataAccessResourceFailureException("Database connection failed"); + String expectedErrorCode = ErrorConstants.DATABASE_CONNECTION_EXCEPTION.getErrorCode(); + String expectedErrorMessage = ErrorConstants.DATABASE_CONNECTION_EXCEPTION.getErrorMessage(); + + // Act + ErrorDTO result = globalExceptionHandler.handleDatabaseException(ex); + + // Assert + assertEquals(expectedErrorCode, result.getErrorCode()); + assertEquals(expectedErrorMessage, result.getErrorMessage()); + + // Verify ResponseStatus annotation + Method method = GlobalExceptionHandler.class.getMethod("handleDatabaseException", Exception.class); + ResponseStatus responseStatus = method.getAnnotation(ResponseStatus.class); + assertNotNull(responseStatus); + assertEquals(HttpStatus.SERVICE_UNAVAILABLE, responseStatus.value()); + } + + @Test + public void handleExternalServiceUnavailableReturnsServiceUnavailable() throws Exception { + // Arrange + String errorCode = "EXT_SERVICE_ERROR"; + String errorMessage = "EXT_SERVICE_ERROR --> External service unavailable"; + ExternalServiceUnavailableException ex = new ExternalServiceUnavailableException(errorCode, errorMessage); + + // Act + ErrorDTO result = globalExceptionHandler.handleExternalServiceUnavailable(ex); + + // Assert + assertEquals(errorCode, result.getErrorCode()); + assertEquals(errorCode + " --> " + errorMessage, result.getErrorMessage()); + + // Verify ResponseStatus annotation + Method method = GlobalExceptionHandler.class.getMethod("handleExternalServiceUnavailable", ExternalServiceUnavailableException.class); + ResponseStatus responseStatus = method.getAnnotation(ResponseStatus.class); + assertNotNull(responseStatus); + assertEquals(HttpStatus.SERVICE_UNAVAILABLE, responseStatus.value()); + } + + @Test + public void handleInvalidRequestReturnsBadRequest() throws Exception { + // Arrange + String errorCode = "INVALID_REQUEST"; + String errorMessage = "Invalid input provided"; + InvalidRequestException ex = new InvalidRequestException(errorCode, errorMessage); + + // Act + ErrorDTO result = globalExceptionHandler.handleInvalidRequest(ex); + + // Assert + assertEquals(errorCode, result.getErrorCode()); + assertEquals(errorMessage, result.getErrorMessage()); + + // Verify ResponseStatus annotation + Method method = GlobalExceptionHandler.class.getMethod("handleInvalidRequest", InvalidRequestException.class); + ResponseStatus responseStatus = method.getAnnotation(ResponseStatus.class); + assertNotNull(responseStatus); + assertEquals(HttpStatus.BAD_REQUEST, responseStatus.value()); + } + + @Test + public void handleHttpMessageNotReadableExceptionReturnsBadRequest() throws Exception { + // Arrange + HttpMessageNotReadableException ex = new HttpMessageNotReadableException("Invalid JSON format"); + String expectedErrorCode = ErrorConstants.INVALID_REQUEST.getErrorCode(); + String expectedErrorMessage = "Unable to process request body"; + + // Act + ErrorDTO result = globalExceptionHandler.handleHttpMessageNotReadableException(ex); + + // Assert + assertEquals(expectedErrorCode, result.getErrorCode()); + assertEquals(expectedErrorMessage, result.getErrorMessage()); + + // Verify ResponseStatus annotation + Method method = GlobalExceptionHandler.class.getMethod("handleHttpMessageNotReadableException", HttpMessageNotReadableException.class); + ResponseStatus responseStatus = method.getAnnotation(ResponseStatus.class); + assertNotNull(responseStatus); + assertEquals(HttpStatus.BAD_REQUEST, responseStatus.value()); + } + +} \ No newline at end of file diff --git a/src/test/java/io/mosip/mimoto/util/JwtGeneratorUtilTest.java b/src/test/java/io/mosip/mimoto/util/JwtGeneratorUtilTest.java new file mode 100644 index 000000000..0ec5c2763 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/util/JwtGeneratorUtilTest.java @@ -0,0 +1,138 @@ +package io.mosip.mimoto.util; + +import com.nimbusds.jwt.SignedJWT; +import io.mosip.mimoto.constant.SigningAlgorithm; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.BeforeClass; +import org.junit.Test; +import java.security.*; +import java.security.spec.ECGenParameterSpec; +import java.util.Arrays; +import java.util.Base64; +import java.util.Date; +import java.util.List; +import static org.junit.Assert.*; + +@Slf4j +public class JwtGeneratorUtilTest { + + @BeforeClass + public static void setupProvider() { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } + + @Test + public void shouldGenerateJWTForRS256Successfully() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); + generator.initialize(2048); + KeyPair keyPair = generator.generateKeyPair(); + List expectedClaims = Arrays.asList("client-id", "audience", "nonce-rs256"); + + String jwt = JwtGeneratorUtil.generateJwtUsingDBKeys( + SigningAlgorithm.RS256, + "audience", + "client-id", + "nonce-rs256", + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded() + ); + + SignedJWT signedJWT = SignedJWT.parse(jwt); + List actualClaims = Arrays.asList( + signedJWT.getJWTClaimsSet().getSubject(), + signedJWT.getJWTClaimsSet().getAudience().getFirst(), + signedJWT.getJWTClaimsSet().getStringClaim("nonce") + ); + + assertEquals(expectedClaims, actualClaims); + assertEquals(18000, diffInSeconds(signedJWT.getJWTClaimsSet().getIssueTime(), signedJWT.getJWTClaimsSet().getExpirationTime())); + } + + @Test + public void shouldGenerateJWTForES256Successfully() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC"); + generator.initialize(new ECGenParameterSpec("secp256r1")); + KeyPair keyPair = generator.generateKeyPair(); + List expectedClaims = Arrays.asList("client-id", "audience", "nonce-es256"); + + String jwt = JwtGeneratorUtil.generateJwtUsingDBKeys( + SigningAlgorithm.ES256, + "audience", + "client-id", + "nonce-es256", + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded() + ); + + SignedJWT signedJWT = SignedJWT.parse(jwt); + List actualClaims = Arrays.asList( + signedJWT.getJWTClaimsSet().getSubject(), + signedJWT.getJWTClaimsSet().getAudience().getFirst(), + signedJWT.getJWTClaimsSet().getStringClaim("nonce") + ); + + assertEquals(expectedClaims, actualClaims); + assertEquals(18000, diffInSeconds(signedJWT.getJWTClaimsSet().getIssueTime(), signedJWT.getJWTClaimsSet().getExpirationTime())); + } + + @Test + public void shouldGenerateJWTForES256kSuccessfully() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "BC"); + generator.initialize(new ECGenParameterSpec("secp256k1")); + KeyPair keyPair = generator.generateKeyPair(); + List expectedClaims = Arrays.asList("client-id", "audience", "nonce-es256k"); + + String jwt = JwtGeneratorUtil.generateJwtUsingDBKeys( + SigningAlgorithm.ES256K, + "audience", + "client-id", + "nonce-es256k", + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded() + ); + + SignedJWT signedJWT = SignedJWT.parse(jwt); + List actualClaims = Arrays.asList( + signedJWT.getJWTClaimsSet().getSubject(), + signedJWT.getJWTClaimsSet().getAudience().getFirst(), + signedJWT.getJWTClaimsSet().getStringClaim("nonce") + ); + + assertEquals(expectedClaims, actualClaims); + assertEquals(18000, diffInSeconds(signedJWT.getJWTClaimsSet().getIssueTime(), signedJWT.getJWTClaimsSet().getExpirationTime())); + } + + @Test + public void shouldGenerateJWTForED25519Successfully() throws Exception { + KeyPairGenerator generator = KeyPairGenerator.getInstance("Ed25519", "BC"); + KeyPair keyPair = generator.generateKeyPair(); + List expectedClaims = Arrays.asList("client-id", "audience", "nonce-ed25519"); + + String jwt = JwtGeneratorUtil.generateJwtUsingDBKeys( + SigningAlgorithm.ED25519, + "audience", + "client-id", + "nonce-ed25519", + keyPair.getPublic().getEncoded(), + keyPair.getPrivate().getEncoded() + ); + + SignedJWT signedJWT = SignedJWT.parse(jwt); + List actualClaims = Arrays.asList( + signedJWT.getJWTClaimsSet().getSubject(), + signedJWT.getJWTClaimsSet().getAudience().getFirst(), + signedJWT.getJWTClaimsSet().getStringClaim("nonce") + ); + + assertEquals(expectedClaims, actualClaims); + assertEquals(18000, diffInSeconds(signedJWT.getJWTClaimsSet().getIssueTime(), signedJWT.getJWTClaimsSet().getExpirationTime())); + } + + + public long diffInSeconds(Date issuedAt, Date expiresAt) { + return (expiresAt.getTime() - issuedAt.getTime()) / 1000; + } +} diff --git a/src/test/java/io/mosip/mimoto/util/KeyGenerationUtilTest.java b/src/test/java/io/mosip/mimoto/util/KeyGenerationUtilTest.java new file mode 100644 index 000000000..5c8351cf4 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/util/KeyGenerationUtilTest.java @@ -0,0 +1,64 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.exception.KeyGenerationException; +import io.mosip.mimoto.constant.SigningAlgorithm; +import org.junit.Test; + +import javax.crypto.SecretKey; +import java.security.KeyPair; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class KeyGenerationUtilTest { + + @Test + public void shouldGenerateEncryptionKeySuccessfully() { + SecretKey key = KeyGenerationUtil.generateEncryptionKey("AES", 256); + + assertNotNull(key); + assertEquals("AES", key.getAlgorithm()); + } + + @Test + public void shouldGenerateKeyPairSuccessfully() throws Exception { + KeyPair keyPair = KeyGenerationUtil.generateKeyPair(SigningAlgorithm.ED25519); + + assertNotNull(keyPair); + assertEquals("EdDSA", keyPair.getPublic().getAlgorithm()); + } + + @Test + public void shouldGenerateRSAKeyPairSuccessfully() throws Exception { + KeyPair keyPair = KeyGenerationUtil.generateKeyPair(SigningAlgorithm.RS256); + + assertNotNull(keyPair); + assertEquals("RSA", keyPair.getPublic().getAlgorithm()); + } + + @Test + public void shouldGenerateECKeyPairWithP256Successfully() throws Exception { + KeyPair keyPair = KeyGenerationUtil.generateKeyPair(SigningAlgorithm.ES256); + + assertNotNull(keyPair); + assertEquals("EC", keyPair.getPublic().getAlgorithm()); + } + + @Test + public void shouldGenerateECKeyPairWithSecp256k1Successfully() throws Exception { + KeyPair keyPair = KeyGenerationUtil.generateKeyPair(SigningAlgorithm.ES256K); + + assertNotNull(keyPair); + assertEquals("EC", keyPair.getPublic().getAlgorithm()); + } + + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionForUnsupportedAlgorithm() throws Exception { + KeyGenerationUtil.generateKeyPair(SigningAlgorithm.fromString("unsupportedAlgorithm")); + } + + @Test(expected = KeyGenerationException.class) + public void shouldThrowExceptionForUnsupportedEncryptionAlgorithm() { + KeyGenerationUtil.generateEncryptionKey("unsupportedAlgorithm", 256); + } +} diff --git a/src/test/java/io/mosip/mimoto/util/TestUtilities.java b/src/test/java/io/mosip/mimoto/util/TestUtilities.java index c99324498..245db10bb 100644 --- a/src/test/java/io/mosip/mimoto/util/TestUtilities.java +++ b/src/test/java/io/mosip/mimoto/util/TestUtilities.java @@ -8,6 +8,8 @@ import com.itextpdf.kernel.pdf.PdfWriter; import com.itextpdf.kernel.pdf.PdfDocument; import com.itextpdf.kernel.pdf.PdfReader; +import io.mosip.mimoto.model.CredentialMetadata; +import io.mosip.mimoto.model.VerifiableCredential; import io.mosip.mimoto.dto.*; import io.mosip.mimoto.dto.mimoto.*; import io.mosip.mimoto.dto.openid.VerifierDTO; @@ -186,7 +188,7 @@ public static IssuerDTO getIssuerDTO(String issuerName) { public static IssuerDTO getIssuerConfigDTO(String issuerName) { LogoDTO logo = new LogoDTO(); - logo.setUrl("/logo"); + logo.setUrl("https://logo"); logo.setAlt_text("logo-url"); DisplayDTO display = new DisplayDTO(); display.setName(issuerName); @@ -199,12 +201,47 @@ public static IssuerDTO getIssuerConfigDTO(String issuerName) { issuer.setCredential_issuer(issuerName + "id"); issuer.setDisplay(Collections.singletonList(display)); issuer.setClient_id("123"); + issuer.setClient_alias("test-client-alias"); + issuer.setRedirect_uri("https://oauthredirect"); issuer.setEnabled("true"); issuer.setProtocol("OpenId4VCI"); issuer.setWellknown_endpoint("https://issuer.env.net/.well-known/openid-credential-issuer"); issuer.setCredential_issuer_host("https://issuer.env.net"); - issuer.setAuthorization_audience("https://dev/token"); - issuer.setProxy_token_endpoint("https://dev/token"); + issuer.setToken_endpoint("https://dev/" + issuerName + "id"); + issuer.setAuthorization_audience("https://dev/auth-server/token"); + issuer.setProxy_token_endpoint("https://dev/auth-server/token"); + return issuer; + } + + public static IssuerDTO getIssuerConfigDTOWithInvalidFieldValues(String issuerName, boolean emptyValues, boolean invalidUrls) { + LogoDTO logo = new LogoDTO(); + logo.setUrl(emptyValues ? "/logo" : "https://logo"); + logo.setAlt_text("logo-url"); + + DisplayDTO display = new DisplayDTO(); + display.setName(emptyValues ? "" : issuerName); + display.setTitle(emptyValues ? "" : "Download via " + issuerName); + display.setDescription(emptyValues ? "" : issuerName + " description"); + display.setLanguage(emptyValues ? "" : "en"); + display.setLogo(logo); + + IssuerDTO issuer = new IssuerDTO(); + issuer.setIssuer_id(emptyValues ? "" : issuerName + "id"); + issuer.setCredential_issuer(emptyValues ? "" : issuerName + "id"); + issuer.setDisplay(Collections.singletonList(display)); + issuer.setClient_id(emptyValues ? "" : "123"); + issuer.setClient_alias(emptyValues ? "" : "test-client-alias"); + issuer.setRedirect_uri(emptyValues ? "" : "https://oauthredirect"); + issuer.setEnabled(emptyValues ? "" : "true"); + issuer.setProtocol(emptyValues ? "" : "OpenId4VCI"); + + // Handle valid and invalid URLs + issuer.setWellknown_endpoint(emptyValues ? "" : (invalidUrls ? "ht//issuer.env.net/.well-known/openid-credential-issuer" : "https://issuer.env.net/.well-known/openid-credential-issuer")); + issuer.setCredential_issuer_host(emptyValues ? "" : (invalidUrls ? "https//issuer.env.net" : "https://issuer.env.net")); + issuer.setToken_endpoint(emptyValues ? "" : (invalidUrls ? "h://dev/token" : "https://dev/token")); + issuer.setAuthorization_audience(emptyValues ? "" : (invalidUrls ? "htt://dev/auth-server/token" : "https://dev/auth-server/token")); + issuer.setProxy_token_endpoint(emptyValues ? "" : (invalidUrls ? "htp://dev/auth-server/token" : "https://dev/auth-server/token")); + return issuer; } @@ -319,6 +356,7 @@ public static io.mosip.mimoto.dto.idp.TokenResponseDTO getTokenResponseDTO() { .expires_in(12345) .scope("test-scope") .token_type("test-token-type") + .c_nonce("test-cnonce") .build(); } @@ -379,4 +417,31 @@ public static DataShareResponseWrapperDTO getDataShareResponseWrapperDTO() { .dataShare(dataShareResponseDTO) .errors(Collections.singletonList(errorDTO)).build(); } + + public static VerifiableCredential getVerifiableCredential(String id, String walletId, String credential, String issuerId, String credentialType) { + VerifiableCredential verifiableCredential = new VerifiableCredential(); + verifiableCredential.setId(id); + verifiableCredential.setWalletId(walletId); + verifiableCredential.setCredential(credential); + + CredentialMetadata metadata = new CredentialMetadata(); + metadata.setIssuerId(issuerId); + metadata.setCredentialType(credentialType); + verifiableCredential.setCredentialMetadata(metadata); + return verifiableCredential; + } + + public static VerifiableCredentialResponseDTO getVerifiableCredentialResponseDTO(String issuerName, String issuerLogo, String credentialType, String credentialTypeLogo, String credentialId) { + return VerifiableCredentialResponseDTO.builder() + .issuerDisplayName(issuerName) + .issuerLogo(issuerLogo) + .credentialTypeDisplayName(credentialType) + .credentialTypeLogo(credentialTypeLogo) + .credentialId(credentialId) + .build(); + } + + public static String createRequestBody(Object request) throws JsonProcessingException { + return new ObjectMapper().writeValueAsString(request); + } } diff --git a/src/test/java/io/mosip/mimoto/util/WalletUtilTest.java b/src/test/java/io/mosip/mimoto/util/WalletUtilTest.java new file mode 100644 index 000000000..97477a712 --- /dev/null +++ b/src/test/java/io/mosip/mimoto/util/WalletUtilTest.java @@ -0,0 +1,156 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.constant.SessionKeys; +import io.mosip.mimoto.model.Wallet; +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.repository.WalletRepository; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.crypto.SecretKey; +import java.util.Base64; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@Slf4j +class WalletUtilTest { + + @Mock + private WalletRepository walletRepository; + + @Mock + private EncryptionDecryptionUtil encryptionDecryptionUtil; + + @InjectMocks + private WalletUtil walletUtil; + + private String pin; + private String name; + private String userId; + private SecretKey encryptionKey; + private String encryptedPrivateKey; + private String encryptedWalletKey; + private String decryptedWalletKey; + private String encryptionAlgorithm; + private String encryptionType; + + @BeforeEach + void setUp() { + pin = "1234"; + name = "default"; + userId = UUID.randomUUID().toString(); + encryptionKey = KeyGenerationUtil.generateEncryptionKey("AES", 256); + encryptedPrivateKey = "encryptedPrivateKey"; + encryptedWalletKey = "encryptedWalletKey"; + decryptedWalletKey = Base64.getEncoder().encodeToString(encryptionKey.getEncoded()); + encryptionAlgorithm = "AES"; + encryptionType = "encryptWithPin"; + } + + @Test + void shouldDecryptWalletKeySuccessfully() { + when(encryptionDecryptionUtil.decryptWithPin(encryptedWalletKey, pin)).thenReturn(decryptedWalletKey); + + String decrypted = walletUtil.decryptWalletKey(encryptedWalletKey, pin); + + assertEquals(decryptedWalletKey, decrypted); + } + + @Test + void shouldThrowErrorWhenDecryptionOfWalletKeyFails() { + when(encryptionDecryptionUtil.decryptWithPin(encryptedWalletKey, pin)).thenThrow(new RuntimeException("Failed to decrypt with PIN")); + + InvalidRequestException ex = assertThrows(InvalidRequestException.class, + () -> walletUtil.decryptWalletKey(encryptedWalletKey, pin)); + assertEquals("invalid_pin", ex.getErrorCode()); + assertEquals("invalid_pin --> Invalid PIN or wallet key provided java.lang.RuntimeException: Failed to decrypt with PIN", ex.getMessage()); + } + + + @Test + void shouldCreateNewWalletSuccessfully() { + when(encryptionDecryptionUtil.encryptKeyWithPin(any(SecretKey.class), any(String.class))).thenReturn(encryptedWalletKey); + when(encryptionDecryptionUtil.encryptWithAES(any(SecretKey.class), any(byte[].class))).thenReturn(encryptedPrivateKey); + + String walletId = walletUtil.saveWallet(userId, name, pin, encryptionKey, encryptionAlgorithm, encryptionType); + + assertNotNull(walletId); + } + + @Test + void shouldCreateEd25519WalletSuccessfully() { + when(encryptionDecryptionUtil.encryptKeyWithPin(any(SecretKey.class), any(String.class))).thenReturn(encryptedWalletKey); + when(encryptionDecryptionUtil.encryptWithAES(any(SecretKey.class), any(byte[].class))).thenReturn(encryptedPrivateKey); + + String walletId = walletUtil.createWallet(userId, name, pin); + + assertNotNull(walletId); + } + + @Test + void shouldVerifyWalletObjectOnCreateNewWallet() { + when(encryptionDecryptionUtil.encryptKeyWithPin(any(SecretKey.class), any(String.class))).thenReturn(encryptedWalletKey); + when(encryptionDecryptionUtil.encryptWithAES(any(SecretKey.class), any(byte[].class))).thenReturn(encryptedPrivateKey); + + String walletId = walletUtil.saveWallet(userId, name, pin, encryptionKey, encryptionAlgorithm, encryptionType); + + ArgumentCaptor walletCaptor = ArgumentCaptor.forClass(Wallet.class); + verify(walletRepository).save(walletCaptor.capture()); + + Wallet savedWallet = walletCaptor.getValue(); + + assertEquals(walletId, savedWallet.getId()); + assertEquals(userId, savedWallet.getUserId()); + assertEquals(name, savedWallet.getWalletMetadata().getName()); + assertEquals(encryptedWalletKey, savedWallet.getWalletKey()); + assertEquals(encryptionAlgorithm, savedWallet.getWalletMetadata().getEncryptionAlgo()); + assertEquals(encryptionType, savedWallet.getWalletMetadata().getEncryptionType()); + assertNotNull(savedWallet.getProofSigningKeys()); + assertEquals(4, savedWallet.getProofSigningKeys().size()); + } + + @Test + void testValidWalletId() { + HttpSession session = mock(HttpSession.class); + when(session.getAttribute(SessionKeys.WALLET_ID)).thenReturn("wallet-123"); + assertDoesNotThrow(() -> WalletUtil.validateWalletId(session, "wallet-123")); + } + + @Test + void testMissingWalletIdInSession() { + HttpSession session = mock(HttpSession.class); + when(session.getAttribute(SessionKeys.WALLET_ID)).thenReturn(null); + InvalidRequestException ex = assertThrows(InvalidRequestException.class, + () -> WalletUtil.validateWalletId(session, "wallet-123")); + assertEquals("wallet_locked", ex.getErrorCode()); + assertEquals("wallet_locked --> Wallet is locked", ex.getMessage()); + } + + @Test + void testMismatchedWalletId() { + HttpSession session = mock(HttpSession.class); + when(session.getAttribute(SessionKeys.WALLET_ID)).thenReturn("wallet-abc"); + InvalidRequestException ex = assertThrows(InvalidRequestException.class, + () -> WalletUtil.validateWalletId(session, "wallet-123")); + assertEquals("invalid_request", ex.getErrorCode()); + assertEquals("invalid_request --> Invalid Wallet ID. Session and request Wallet ID do not match", ex.getMessage()); + } + + @Test + void testNonStringWalletIdInSession() { + HttpSession session = mock(HttpSession.class); + when(session.getAttribute(SessionKeys.WALLET_ID)).thenReturn(12345); + assertDoesNotThrow(() -> WalletUtil.validateWalletId(session, "12345")); + } +} diff --git a/src/test/java/io/mosip/mimoto/util/WalletValidatorTest.java b/src/test/java/io/mosip/mimoto/util/WalletValidatorTest.java new file mode 100644 index 000000000..4c884839a --- /dev/null +++ b/src/test/java/io/mosip/mimoto/util/WalletValidatorTest.java @@ -0,0 +1,67 @@ +package io.mosip.mimoto.util; + +import io.mosip.mimoto.exception.InvalidRequestException; +import io.mosip.mimoto.exception.UnauthorizedAccessException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = WalletValidator.class) +@TestPropertySource(locations = "classpath:application-test.properties") +public class WalletValidatorTest { + + @Autowired + private WalletValidator walletValidator; + + @Test + void testValidateWalletRequest_validData() { + walletValidator.validateUserId("user1"); + walletValidator.validateWalletName("wallet1"); + walletValidator.validateWalletPin("123456"); + } + + @Test + void testValidatePin_invalidPin() { + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + walletValidator.validateUserId("user1"); + walletValidator.validateWalletName("My Wallet"); + walletValidator.validateWalletPin("12"); + }); + + assertEquals("invalid_request --> PIN must be numeric with 6 digits", exception.getMessage()); + } + + @Test + void testValidateWalletName_invalidName() { + InvalidRequestException exception = assertThrows(InvalidRequestException.class, () -> { + walletValidator.validateUserId("user1"); + walletValidator.validateWalletName("My Wallet!@"); + walletValidator.validateWalletPin("123456"); + }); + + assertEquals("invalid_request --> Wallet name must be alphanumeric with allowed special characters", exception.getMessage()); + } + + @Test + void testValidateWalletName_validName() { + walletValidator.validateUserId("user1"); + walletValidator.validateWalletName("wallet1"); + walletValidator.validateWalletPin("123456"); + } + + @Test + void testValidateUserId_nullUserId() { + UnauthorizedAccessException exception = assertThrows(UnauthorizedAccessException.class, () -> { + walletValidator.validateUserId(null); + }); + + assertEquals("unauthorized --> User ID not found in session", exception.getMessage()); + } +} diff --git a/src/test/resources/application-test.properties b/src/test/resources/application-test.properties index 2d49a6b00..58b7367bc 100644 --- a/src/test/resources/application-test.properties +++ b/src/test/resources/application-test.properties @@ -11,6 +11,8 @@ mosip.inji.vcDownloadMaxRetry=10 mosip.inji.vcDownloadPoolInterval=6000 mosip.inji.issuer=residentapp mosip.inji.openId4VCIDownloadVCTimeout=30000 +mosip.inji.user.wallet.pin.validation.regex=^\\d{6}$ +mosip.inji.user.wallet.name.validation.regex=^[A-Za-z0-9 _.-]{0,50}$ mosipbox.public.url=https://api.dev1.mosip.net public.url=https://api.dev1.mosip.net @@ -42,3 +44,5 @@ mosip.data.share.get.url.pattern=http://datashare.datashare/v1/datashare/get/sta mosip.data.share.create.retry.count=3 #OpenId4VP related Configuration END + +spring.security.oauth2.client.provider.google.jwk-set-uri=https://www.googleapis.com/oauth2/v3/certs