diff --git a/README.md b/README.md index 02ec9233..87d33e18 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,30 @@ # eSignet Signup -signup-service is part of the esignet, but has a separate Helm chart to install and manage it in a completely independent namespace. +## Overview + +This repository contains a signup UI and signup service to handle signup operations by the end user. This module can be +used to fast-track the availability of any digital service to end users via eSignet. eSignet has built-in support for the +integration with the signup module. The signup service is flexible to connect to any ID registry system via a well-defined plugin interface. + +Currently, signup supports below features: +1. Register User +2. Reset password +3. Online video based identity verification workflow integration via plugin + +## Build (for developers) +The project requires JDK 11. +1. Build: + ``` + $ mvn clean install -Dgpg.skip=true -Dmaven.gitcommitid.skip=true + ``` ## Installing in k8s cluster using helm + +signup-service is part of the esignet, but has a separate Helm chart to install and manage it in a completely independent namespace. + ### Pre-requisites 1. Set the kube config file of the Esignet k8 cluster having esignet services is set correctly in PC. -1. Below are the dependent services required for signup service integrated with MOSIP IDA: +1. Below are the dependent services required for signup service integrated with [Mock Identity System](https://github.com/mosip/esignet-mock-services/tree/master/mock-identity-system) | Chart | Chart version | |---|---| |[Keycloak](https://github.com/mosip/mosip-infra/tree/v1.2.0.1-B3/deployment/v3/external/iam) | 7.1.18 | @@ -36,11 +55,35 @@ cd deploy cd deploy ./restart-signup.sh ``` +### Additional services required +To complete the signup portal deployment below MOSIP kernel services are required to be deployed. +* otpmanager +* authmanager +* auditmanager +* notifier + +* Initialize the db script to create mosip_kernel and mosip_audit databases make sure to update the existing db-common-secret in init_values.yaml if postgres-initialization already done + * copy db-common-secret from existing postgres deployment secret if its already created + * run the postgres-init.sh + ``` + cd deploy + ./postgres-init.sh + ``` +#### Prerequisites for MOSIP kernel services: +1. msg-gateway +2. config-server +3. artifactory +4. mock-smtp +5. kernel +``` + cd deploy (follow the above sequence and run the install.sh for each module installation) + +``` ## Partner onboarding * Perform Partner onboarding for esignet Signup OIDC client using [steps](partner-onboarder/README.md) only if mosip-identity plugin is used. ## APIs -API documentation is available [here](https://mosip.stoplight.io/docs/identity-provider/branches/signupV1/t9tvfbteqqokf-e-signet-signup-portal-ap-is). +API documentation is available [here](docs/esignet-signup-openapi.yaml). ## License This project is licensed under the terms of [Mozilla Public License 2.0](LICENSE). diff --git a/api-test/.gitignore b/api-test/.gitignore index defd0502..97e685f4 100644 --- a/api-test/.gitignore +++ b/api-test/.gitignore @@ -38,3 +38,4 @@ test-output/ testng-report/ /reg ./reg +.temp* diff --git a/api-test/pom.xml b/api-test/pom.xml index 13dd8d05..c887faf4 100644 --- a/api-test/pom.xml +++ b/api-test/pom.xml @@ -265,4 +265,4 @@ - \ No newline at end of file + diff --git a/deploy/artifactory/README.md b/deploy/artifactory/README.md new file mode 100644 index 00000000..bff9e01f --- /dev/null +++ b/deploy/artifactory/README.md @@ -0,0 +1,6 @@ +# Artifactory + +## Install +```sh +./install.sh +``` diff --git a/deploy/artifactory/delete.sh b/deploy/artifactory/delete.sh new file mode 100755 index 00000000..ffd65f8c --- /dev/null +++ b/deploy/artifactory/delete.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Uninstalls artifactory +# Usage: ./delete.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function deleting_artifactory() { + NS=artifactory + while true; do + read -p "Are you sure you want to delete artifactory helm chart?(Y/n) " yn + if [ $yn = "Y" ] + then + helm -n $NS delete artifactory + break + else + break + fi + done + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +deleting_artifactory # calling function diff --git a/deploy/artifactory/install.sh b/deploy/artifactory/install.sh new file mode 100755 index 00000000..9c41154a --- /dev/null +++ b/deploy/artifactory/install.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# Installs artifactory +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +NS=artifactory +CHART_VERSION=0.0.1-develop + +echo Create $NS namespace +kubectl create ns $NS + +function installing_artifactory() { + echo Istio label + kubectl label ns $NS istio-injection=enabled --overwrite + helm repo update + + echo Installing artifactory + helm -n $NS install artifactory mosip/artifactory --version $CHART_VERSION + + kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status + + echo Installed artifactory service + 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_artifactory # calling function diff --git a/deploy/artifactory/restart.sh b/deploy/artifactory/restart.sh new file mode 100755 index 00000000..b82a3d03 --- /dev/null +++ b/deploy/artifactory/restart.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Restart the artifactory service +## Usage: ./restart.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function Restarting_artifactory() { + NS=artifactory + kubectl -n $NS rollout restart deploy + + kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status + + echo Restarted Artifactory 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 +Restarting_artifactory # calling function diff --git a/deploy/config-server/README.md b/deploy/config-server/README.md new file mode 100755 index 00000000..387f46be --- /dev/null +++ b/deploy/config-server/README.md @@ -0,0 +1,11 @@ +# Config server + +## Introduction +Config server serves all properties required by MOSIP modules. This must be installed before any other MOSIP 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/delete.sh b/deploy/config-server/delete.sh new file mode 100755 index 00000000..ee52b0af --- /dev/null +++ b/deploy/config-server/delete.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Uninstalls config server +## Usage: ./delete.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function config_server() { + NS=config-server + while true; do + read -p "Are you sure you want to delete config-server helm charts?(Y/n) " yn + if [ $yn = "Y" ] + then + kubectl -n $NS delete configmap keycloak-host + kubectl -n $NS delete secret keycloak keycloak-client-secrets + helm -n $NS delete config-server + break + else + break + fi + done + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +config_server # calling function diff --git a/deploy/config-server/install.sh b/deploy/config-server/install.sh new file mode 100755 index 00000000..f0d8b017 --- /dev/null +++ b/deploy/config-server/install.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Installs config-server +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +# 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 + +NS=config-server +CHART_VERSION=0.0.1-develop + + echo Create $NS namespace + kubectl create ns $NS || true + + echo Istio label + kubectl label ns $NS istio-injection=enabled --overwrite + helm repo update + + COPY_UTIL=../copy_cm_func.sh + $COPY_UTIL configmap keycloak-host keycloak $NS + $COPY_UTIL configmap esignet-global esignet $NS + $COPY_UTIL configmap msg-gateway msg-gateways $NS + + $COPY_UTIL secret keycloak keycloak $NS + $COPY_UTIL secret db-common-secrets postgres $NS + $COPY_UTIL secret keycloak-client-secrets keycloak $NS + $COPY_UTIL secret msg-gateway msg-gateways $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 new file mode 100755 index 00000000..cb7009ab --- /dev/null +++ b/deploy/config-server/restart.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Restart the config-server service +## Usage: ./restart.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function config_server() { + NS=esignet + kubectl -n $NS rollout restart deploy esignet-config-server + + 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 diff --git a/deploy/config-server/values.yaml b/deploy/config-server/values.yaml new file mode 100755 index 00000000..463b0863 --- /dev/null +++ b/deploy/config-server/values.yaml @@ -0,0 +1,164 @@ +gitRepo: + uri: https://github.com/mosip/mosip-config + version: es-qa + ## 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_DB_DBUSER_PASSWORD + valueFrom: + secretKeyRef: + name: db-common-secrets + key: db-dbuser-password + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_API_PUBLIC_HOST + valueFrom: + configMapKeyRef: + name: esignet-global + key: mosip-api-host + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_API_INTERNAL_HOST + valueFrom: + configMapKeyRef: + name: esignet-global + key: mosip-api-internal-host + 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_KEYCLOAK_ADMIN_PASSWORD + valueFrom: + secretKeyRef: + name: keycloak + key: admin-password + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MPARTNER_DEFAULT_AUTH_SECRET + valueFrom: + secretKeyRef: + name: keycloak-client-secrets + key: mpartner_default_auth_secret + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_PMS_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: mosip_pms_client_secret + name: keycloak-client-secrets + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_ADMIN_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: mosip_admin_client_secret + name: keycloak-client-secrets + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_PREREG_CLIENT_SECRET + valueFrom: + secretKeyRef: + key: mosip_prereg_client_secret + name: keycloak-client-secrets + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMTP_HOST + valueFrom: + configMapKeyRef: + name: msg-gateway + key: smtp-host + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMS_HOST + valueFrom: + configMapKeyRef: + name: msg-gateway + key: sms-host + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMTP_PORT + valueFrom: + configMapKeyRef: + name: msg-gateway + key: smtp-port + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMS_PORT + valueFrom: + configMapKeyRef: + name: msg-gateway + key: sms-port + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMTP_USERNAME + valueFrom: + configMapKeyRef: + name: msg-gateway + key: smtp-username + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMS_USERNAME + valueFrom: + configMapKeyRef: + name: msg-gateway + key: sms-username + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMTP_SECRET + valueFrom: + secretKeyRef: + name: msg-gateway + key: smtp-secret + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMS_AUTHKEY + valueFrom: + secretKeyRef: + name: msg-gateway + key: sms-authkey + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_SMS_SECRET + valueFrom: + secretKeyRef: + name: msg-gateway + key: sms-secret + enabled: true + + - name: SPRING_CLOUD_CONFIG_SERVER_OVERRIDES_MOSIP_SIGNUP_HOST + valueFrom: + configMapKeyRef: + key: mosip-signup-host + name: esignet-global + enabled: true \ No newline at end of file diff --git a/deploy/init_values.yaml b/deploy/init_values.yaml new file mode 100644 index 00000000..bc1886c1 --- /dev/null +++ b/deploy/init_values.yaml @@ -0,0 +1,39 @@ +dbUserPasswords: + dbuserPassword: "" +databases: + mosip_audit: + enabled: true + host: "esignet-postgres..mosip.net" + port: 5432 + su: + user: postgres + secret: + name: postgres-postgresql + key: postgres-password + dml: 0 + repoUrl: https://github.com/mosip/audit-manager.git + branch: develop + mosip_kernel: + enabled: true + host: "esignet-postgres..mosip.net" + port: 5432 + su: + user: postgres + secret: + name: postgres-postgresql + key: postgres-password + dml: 0 + repoUrl: https://github.com/mosip/commons.git + branch: develop + mosip_otp: + enabled: true + host: "esignet-postgres..mosip.net" + port: 5432 + su: + user: postgres + secret: + name: postgres-postgresql + key: postgres-password + dml: 0 + repoUrl: https://github.com/mosip/otp-manager.git + branch: develop diff --git a/deploy/kernel/README.md b/deploy/kernel/README.md new file mode 100644 index 00000000..f8527342 --- /dev/null +++ b/deploy/kernel/README.md @@ -0,0 +1,21 @@ +# Kernel + +## Overview +Refer [Commons](https://docs.mosip.io/1.2.0/modules/commons). + +## Install +``` +./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. + +## Masterdata seeding +For one time seeding of master data follow the procedure given [here](masterdata/README.md). + +**CAUTION**: If you run the script again, it will erase entire masterdata and seed it fresh. + + + diff --git a/deploy/kernel/delete.sh b/deploy/kernel/delete.sh new file mode 100755 index 00000000..e1018243 --- /dev/null +++ b/deploy/kernel/delete.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Uninstalls all kernel helm charts +## Usage: ./delete.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function deleting_kernel() { + NS=kernel + while true; do + read -p "Are you sure you want to delete all kernel helm charts?(Y/n) " yn + if [ $yn = "Y" ] + then + helm -n $NS delete auditmanager + helm -n $NS delete authmanager + helm -n $NS delete otpmanager + helm -n $NS delete notifier + break + else + break + fi + done + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +deleting_kernel # calling function diff --git a/deploy/kernel/install.sh b/deploy/kernel/install.sh new file mode 100755 index 00000000..6eebf18d --- /dev/null +++ b/deploy/kernel/install.sh @@ -0,0 +1,78 @@ +#!/bin/bash +# Installs all kernel helm charts +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +NS=kernel +CHART_VERSION=12.0.1 + +echo Create $NS namespace +kubectl create ns $NS + +function installing_kernel() { + echo Istio label + kubectl label ns $NS istio-injection=enabled --overwrite + helm repo update + + COPY_UTIL=../copy_cm_func.sh + $COPY_UTIL configmap artifactory-share artifactory $NS + $COPY_UTIL configmap config-server-share config-server $NS + $COPY_UTIL configmap esignet-global esignet $NS + + echo "Do you have public domain & valid SSL? (Y/n) " + echo "Y: if you have public domain & valid ssl certificate" + echo "n: If you don't have a public domain and a valid SSL certificate. Note: It is recommended to use this option only in development environments." + read -p "" flag + + if [ -z "$flag" ]; then + echo "'flag' was provided; EXITING;" + exit 1; + fi + ENABLE_INSECURE='' + if [ "$flag" = "n" ]; then + ENABLE_INSECURE='--set enable_insecure=true'; + fi + + echo Installing authmanager + helm -n $NS install authmanager mosip/authmanager --version $CHART_VERSION $ENABLE_INSECURE + + echo Installing auditmanager + helm -n $NS install auditmanager mosip/auditmanager --version $CHART_VERSION $ENABLE_INSECURE + + echo Installing otpmanager + helm -n $NS install otpmanager mosip/otpmanager --version $CHART_VERSION + + echo Installing notifier + helm -n $NS install notifier mosip/notifier --version $CHART_VERSION + + # Array of deployment names + DEPLOYMENTS=("authmanager" "auditmanager" "otpmanager" "notifier") + + # Patch all deployments to use esignet-global as configMapRef + for DEPLOYMENT in "${DEPLOYMENTS[@]}"; do + echo Patching $DEPLOYMENT to use esignet-global ConfigMap + kubectl -n $NS patch deployment $DEPLOYMENT \ + --type=json \ + -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/envFrom/0/configMapRef/name", "value": "esignet-global"}]' + done + + kubectl -n $NS set env deployment/notifier \ + MOSIP_KERNEL_SMS_NUMBER_MIN_LENGTH=7 \ + MOSIP_KERNEL_SMS_NUMBER_MAX_LENGTH=10 + + kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status + + echo Installed kernel 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 +installing_kernel # calling function diff --git a/deploy/kernel/restart.sh b/deploy/kernel/restart.sh new file mode 100755 index 00000000..b286217c --- /dev/null +++ b/deploy/kernel/restart.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Restart the kernel services + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function Restarting_kernel() { + NS=kernel + kubectl -n $NS rollout restart deploy + + kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status + + echo Restarted kernel 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 +Restarting_kernel # calling function diff --git a/deploy/keycloak/keycloak-init.sh b/deploy/keycloak/keycloak-init.sh index 1a8c021f..eeb427d8 100755 --- a/deploy/keycloak/keycloak-init.sh +++ b/deploy/keycloak/keycloak-init.sh @@ -1,18 +1,17 @@ -#!/bin/sh -# Initialises signup keycloak-init +#!/bin/bash +# Initialises signup keycloak-init and manages secrets in keycloak-client-secrets ## Usage: ./keycloak-init.sh [kubeconfig] -if [ $# -ge 1 ] ; then +if [ $# -ge 1 ]; then export KUBECONFIG=$1 fi -# set commands for error handling. +# 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 - +set -o errexit ## Exit the script if any statement returns a non-true return value +set -o nounset ## Exit the script if you try to use an uninitialized variable +set -o errtrace # Trace ERR through 'time command' and other functions +set -o pipefail # Trace ERR through pipes NS=signup CHART_VERSION=0.0.1-develop @@ -23,18 +22,17 @@ helm repo update kubectl create ns $NS || true -echo "checking if mosip-pms-client, mosip-ida-client & mpartner_default_auth client is created already" -IAMHOST_URL=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-iam-external-host}) +echo "mosip-signup-client secret is created already" SIGNUP_CLIENT_SECRET_KEY='mosip_signup_client_secret' SIGNUP_CLIENT_SECRET_VALUE=$(kubectl -n keycloak get secrets keycloak-client-secrets -o jsonpath={.data.$SIGNUP_CLIENT_SECRET_KEY} | base64 -d) + echo "Copying keycloak configmaps and secret" $COPY_UTIL configmap keycloak-host keycloak $NS $COPY_UTIL configmap keycloak-env-vars keycloak $NS $COPY_UTIL secret keycloak keycloak $NS -echo "creating and adding roles to keycloak pms & mpartner_default_auth clients for ESIGNET" -kubectl -n $NS delete secret --ignore-not-found=true keycloak-client-secrets -helm -n $NS delete signup-keycloak-init +echo "Creating and adding roles to mosip-signup-client for SIGNUP" +helm -n $NS delete signup-keycloak-init || true helm -n $NS install signup-keycloak-init mosip/keycloak-init \ -f keycloak-init-values.yaml \ --set clientSecrets[0].name="$SIGNUP_CLIENT_SECRET_KEY" \ @@ -52,3 +50,23 @@ else echo "Secret 'keycloak-client-secrets' does not exist. Copying the secret to the keycloak namespace." $COPY_UTIL secret keycloak-client-secrets $NS keycloak fi + +# Process remaining secrets for Kernel +SECRETS=( + "mosip-prereg-client-secret" + "mosip-auth-client-secret" + "mosip-ida-client-secret" + "mosip-admin-client-secret" +) + +for SECRET in "${SECRETS[@]}"; do + read -p "Enter value for $SECRET (leave empty to create an empty key): " SECRET_VALUE + if [[ -z "$SECRET_VALUE" ]]; then + echo "No value entered for $SECRET. Creating it with an empty value." + SECRET_VALUE="" + kubectl patch secret keycloak-client-secrets --namespace=$NS --type=json -p='[{"op": "add", "path": "/data/'$SECRET'", "value": "'$SECRET_VALUE'"}]' + $COPY_UTIL secret keycloak-client-secrets $NS keycloak + fi +done + +echo "All specified secrets have been updated in keycloak-client-secrets." diff --git a/deploy/mock-smtp/README.md b/deploy/mock-smtp/README.md new file mode 100644 index 00000000..c210d5f8 --- /dev/null +++ b/deploy/mock-smtp/README.md @@ -0,0 +1,32 @@ +# Mock SMTP and Mock SMS + +## Introduction +The chart here installs a Mock SMTP and Mock SMS accessed over an https URL. + +## Install +* The url must point to your internal loadbalancer as `https://smtp.sandbox.xyz.net/` will typically not be open to public. +* For more details refer [mock-smtp-repo](https://github.com/mosip/mock-smtp) +* Make sure to update the below properties in the config's `kernel-default.properties` file. + ``` + #kernel-default.properties + #Email properties + spring.mail.properties.mail.smtp.starttls.required=false + spring.mail.properties.mail.smtp.starttls.enable=false + spring.mail.properties.mail.smtp.auth=true + mosip.kernel.mail.proxy-mail=false + + #SMS properties + mosip.kernel.sms.enabled:true + mosip.kernel.sms.country.code: +91 + mosip.kernel.sms.number.length: 10 + mosip.kernel.sms.api:http://mock-smtp.mock-smtp:8080/sendsms + mosip.kernel.sms.sender:AD-MOSIP + mosip.kernel.sms.password:dummy + mosip.kernel.sms.route:mock + mosip.kernel.sms.authkey:dummy + + ``` +* Install +```sh +./install.sh +``` diff --git a/deploy/mock-smtp/delete.sh b/deploy/mock-smtp/delete.sh new file mode 100755 index 00000000..9a05c7a5 --- /dev/null +++ b/deploy/mock-smtp/delete.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Uninstalls mock smtp + +function mock_smtp() { + NS=mock-smtp + while true; do + read -p "Are you sure you want to delete mock smtp helm chart?(Y/n) " yn + if [ $yn = "Y" ] + then + helm -n $NS delete mock-smtp + break + else + break + fi + done + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +mock_smtp # calling function diff --git a/deploy/mock-smtp/install.sh b/deploy/mock-smtp/install.sh new file mode 100755 index 00000000..c66bf44e --- /dev/null +++ b/deploy/mock-smtp/install.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Installs mock-mv +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +NS=mock-smtp +CHART_VERSION=12.0.x-develop + +echo Create $NS namespace +kubectl create ns $NS + +function mock_smtp() { + echo Istio label + kubectl label ns $NS istio-injection=enabled --overwrite + helm repo update + + COPY_UTIL=../copy_cm_func.sh + $COPY_UTIL configmap esignet-global esignet $NS + + SMTP_HOST=$(kubectl get cm esignet-global -n esignet -o jsonpath={.data.mosip-smtp-host}) + + echo Installing mock-smtp + helm -n $NS install mock-smtp mosip/mock-smtp --set istio.hosts\[0\]=$SMTP_HOST --version $CHART_VERSION + + kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status + + echo Installed mock-smtp 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 +mock_smtp # calling function diff --git a/deploy/mock-smtp/restart.sh b/deploy/mock-smtp/restart.sh new file mode 100755 index 00000000..1334aee6 --- /dev/null +++ b/deploy/mock-smtp/restart.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Restarts the mock-smtp +## Usage: ./restart.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function Restarting_smtp() { + NS=mock-smtp + kubectl -n $NS rollout restart deploy + + kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status + + echo Restarted mock-smtp 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 +Restarting_smtp # calling function diff --git a/deploy/msg-gateway/README.md b/deploy/msg-gateway/README.md new file mode 100644 index 00000000..8429ec6c --- /dev/null +++ b/deploy/msg-gateway/README.md @@ -0,0 +1,14 @@ +# SMTP and SMS gateways + +The information of your SMTP and SMS gateways is created here. Create these configmaps and secrets before installing [Config Server](../../mosip/config_server). Even if you are not using any Email/SMS service, you still need to create these (with dummy values). + +If you would like to use Gmail SMTP. You can follow the procedure from [here](../../docs/create-gmail-app-password.md) + +If you would like to use mock-smtp. You can follow the procedure form [here](../../mosip/mock-smtp/README.md). + +If you would like to use Gmail SMTP. You can follow the procedure from [here](../../docs/create-gmail-app-password.md) + +Run +```sh +./install.sh +``` diff --git a/deploy/msg-gateway/delete.sh b/deploy/msg-gateway/delete.sh new file mode 100755 index 00000000..82561720 --- /dev/null +++ b/deploy/msg-gateway/delete.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# To remove msg-gateway configmap and secret +## Usage: ./delete.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +function deleting_msg-gateway() { + NS=msg-gateways + while true; do + read -p "Are you sure you want to delete msg-gateways configmaps and secrets?(Y/n) " yn + if [ $yn = "Y" ] + then + kubectl -n $NS delete --ignore-not-found=true configmap msg-gateway + kubectl -n $NS delete --ignore-not-found=true secret msg-gateway + break + else + break + fi + done + return 0 +} + +# set commands for error handling. +set -e +set -o errexit ## set -e : exit the script if any statement returns a non-true return value +set -o nounset ## set -u : exit the script if you try to use an uninitialised variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes +deleting_msg-gateway # calling function diff --git a/deploy/msg-gateway/install.sh b/deploy/msg-gateway/install.sh new file mode 100755 index 00000000..2f1241d7 --- /dev/null +++ b/deploy/msg-gateway/install.sh @@ -0,0 +1,65 @@ +#!/bin/bash +# Creates configmap and secrets for SMTP and SMS +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +NS=msg-gateways + +echo Create $NS namespace +kubectl create ns $NS + +function msg_gateway() { + echo Istio label + kubectl label ns $NS istio-injection=enabled --overwrite + + SMTP_HOST=mock-smtp.mock-smtp + SMS_HOST=mock-smtp.mock-smtp + SMTP_PORT=8025 + SMS_PORT=8080 + SMTP_USER= + SMS_USER= + SMTP_SECRET="''" + SMS_SECRET="''" + SMS_AUTHKEY="authkey" + + read -p "Would you like to use mock-smtp (Y/N) [ Default: Y ] : " yn + # Set yn to N if user input is null + if [ -z $yn ]; then + yn=Y; + fi + if [ $yn != "Y" ]; then + read -p "Please enter the SMTP host " SMTP_HOST + read -p "Please enter the SMTP host port " SMTP_PORT + read -p "Please enter the SMTP user " SMTP_USER + read -p "Please enter the SMTP secret key " SMTP_SECRET + fi + unset yn + read -p "Would you like to use mock-sms (Y/N) [ Default: Y ] : " yn + if [ -z $yn ]; then + yn=Y; + fi + if [ $yn != "Y" ]; then + read -p "Please enter the SMS host " SMS_HOST + read -p "Please enter the SMS host port " SMS_PORT + read -p "Please enter the SMS user " SMS_USER + read -p "Please enter the SMS secret key " SMS_SECRET + read -p "Please enter the SMS auth key " SMS_AUTHKEY + fi + kubectl -n $NS delete --ignore-not-found=true configmap msg-gateway + kubectl -n $NS create configmap msg-gateway --from-literal="smtp-host=$SMTP_HOST" --from-literal="sms-host=$SMS_HOST" --from-literal="smtp-port=$SMTP_PORT" --from-literal="sms-port=$SMS_PORT" --from-literal="smtp-username=$SMTP_USER" --from-literal="sms-username=$SMS_USER" + kubectl -n $NS delete --ignore-not-found=true secret msg-gateway + kubectl -n $NS create secret generic msg-gateway --from-literal="smtp-secret=$SMTP_SECRET" --from-literal="sms-secret=$SMS_SECRET" --from-literal="sms-authkey=$SMS_AUTHKEY" --dry-run=client -o yaml | kubectl apply -f - + + echo smtp and sms related configurations set. + 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 +msg_gateway # calling function diff --git a/deploy/postgres-init.sh b/deploy/postgres-init.sh new file mode 100755 index 00000000..36a67269 --- /dev/null +++ b/deploy/postgres-init.sh @@ -0,0 +1,71 @@ +# Script to initialize the DB. +## Usage: ./init_db.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +# Function to initialize the DB +function initialize_db() { + NS=signup + CHART_VERSION=1.1.0-develop + helm repo update + + # Confirm if the user wants to initialize DB scripts + while true; do + read -p "Are the modules of the MOSIP platform already deployed? (Y/n): " yn + if [[ "$yn" == "Y" || "$yn" == "y" ]]; then + echo "Exiting as MOSIP platform modules are already deployed.No need to initialize db again" + exit 0 + elif [[ "$yn" == "N" || "$yn" == "n" ]]; then + echo "Initializing DB scripts for MOSIP_KERNEL and MOSIP_AUDIT, because mosip platform modules are not deployed yet" + break + else + echo "Invalid input. Please enter Y for Yes or N for No." + fi + done + + while true; do + read -p "Please confirm with "Y" once init-values.yaml is updated correctly with tag, postgres host details else "N" to exit installation: " ans + if [ "$ans" = "Y" ] || [ "$ans" = "y" ]; then + break + elif [ "$ans" = "N" ] || [ "$ans" = "n" ]; then + exit 1 + else + echo "Please provide a correct option (Y or N)" + fi + done + + # Prompt for dbuserPassword + echo "Please provide the dbuserPassword" + read -s dbuserPassword + if [ -z "$dbuserPassword" ]; then + echo "ERROR: dbuserPassword not specified; EXITING." + exit 1 + fi + + # Initialize DB + echo "Removing any existing installation..." + helm -n $NS delete postgres-init || true + kubectl -n $NS delete secret db-common-secrets || true + ./copy_cm_func.sh secret postgres-postgresql postgres $NS + + echo "Initializing DB..." + helm -n $NS install postgres-init mosip/postgres-init -f init_values.yaml \ + --version $CHART_VERSION \ + --set dbUserPasswords.dbuserPassword="$dbuserPassword" \ + --wait --wait-for-jobs + + echo "Database initialization complete." + return 0 +} + +# Set commands for error handling +set -e +set -o errexit ## exit the script if any statement returns a non-true return value +set -o nounset ## exit the script if you try to use an uninitialized variable +set -o errtrace # trace ERR through 'time command' and other functions +set -o pipefail # trace ERR through pipes + +# Call the function +initialize_db diff --git a/deploy/prereq.sh b/deploy/prereq.sh index 77d423d4..d4eaa9d2 100755 --- a/deploy/prereq.sh +++ b/deploy/prereq.sh @@ -50,7 +50,7 @@ function installing_prereq() { kubectl -n $NS create secret generic signup-captcha --from-literal=signup-captcha-site-key=$SSITE_KEY --from-literal=signup-captcha-secret-key=$SSECRET_KEY --dry-run=client -o yaml | kubectl apply -f - echo "Captcha secrets for esignet configured sucessfully" - ./copy_cm_func.sh secret signup-captcha $NS captcha + ../copy_cm_func.sh secret signup-captcha $NS captcha # Check if the first environment variable exists ENV_VAR_EXISTS=$(kubectl -n captcha get deployment captcha -o jsonpath="{.spec.template.spec.containers[0].env[?(@.name=='MOSIP_CAPTCHA_SECRET_SIGNUP')].name}") @@ -64,9 +64,10 @@ function installing_prereq() { echo "Environment variable 'MOSIP_CAPTCHA_SECRET_SIGNUP' exists. Updating it..." kubectl patch deployment -n captcha captcha --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/env[?(@.name==\"MOSIP_CAPTCHA_SECRET_SIGNUP\")]", "value": {"name": "MOSIP_CAPTCHA_SECRET_SIGNUP", "valueFrom": {"secretKeyRef": {"name": "signup-captcha", "key": "signup-captcha-secret-key"}}}}]' fi - + break elif [ "$ans" = "N" ] || [ "$ans" = "n" ]; then - exit 1 + echo "Exiting captcha configuration." + break # Exit the loop else echo "Please provide a correct option (Y or N)" fi diff --git a/docker-compose/README.md b/docker-compose/README.md index bd146f7e..436ffc6e 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -2,9 +2,38 @@ This is the docker-compose setup to run eSignet Signup service with mock identity system. This is not for production use. +## Run signup service in local with all its dependencies +1. Run `docker compose --file dependent-docker-compose.yml up` to start all the dependent services. +2. Go to command line for the project root directory and run `mvn clean install -Dgpg.skip=true -DskipTests=true` +3. Add [esignet-mock-plugin.jar](../signup-service/target/signup-plugins/esignet-mock-plugin.jar) to signup-service classpath in your IDE. +4. Add [kernel-auth-adapter-lite.jar](../signup-service/target/signup-plugins/kernel-auth-adapter-lite.jar) to signup-service classpath in your IDE. +5. Update below properties in [application-local.properties](../signup-service/src/main/resources/application-local.properties) with valid values: + mosip.internal.domain.url=https://api-internal.dev.mosip.net + keycloak.external.url=https://iam.dev.mosip.net + mosip.signup.client.secret=secret +6. Start the [SignUpServiceApplication.java](../signup-service/src/main/java/io/mosip/signup/SignUpServiceApplication.java) from your IDE. +7. Access the service swagger with this URL - http://localhost:8089/v1/signup/swagger-ui.html +8. Import files under [postman-collection](../postman-collection) folder into your postman to test/validate registration flow. + +## Prerequisite to run Identity verification flow from postman collection + +1. Onboard signup-service as a OIDC client in esignet-service: + +Execute [create-signup-oidc-keystore.sh](../docs/create-signup-oidc-keystore.sh) to generate a keypair. This script after +successful execution creates 2 files in the project root directory: + +* oidckeystore.p12 +* public_key.jwk + +As esignet only supports confidential OIDC clients, we should generate a RSA keypair to onboard signup-service. RSA private key is +stored in the oidckeystore.p12 file and the corresponding public key is written to public_key.jwk file. + +Copy the public key in public_key.jwk file and update the same in the `Register Signup OIDC/Create Signup OIDC client` request body. + +Run `Register Signup OIDC/Create Signup OIDC client` in postman before starting the identity verification flow. \ No newline at end of file diff --git a/docker-compose/dependent-docker-compose.yml b/docker-compose/dependent-docker-compose.yml index e013f6b2..2e5558b0 100644 --- a/docker-compose/dependent-docker-compose.yml +++ b/docker-compose/dependent-docker-compose.yml @@ -17,10 +17,13 @@ services: - 8082:8082 environment: - container_user=mosip - - active_profile_env=local + - active_profile_env=default,local - SPRING_DATASOURCE_URL=jdbc:postgresql://database:5432/mosip_mockidentitysystem?currentSchema=mockidentitysystem - SPRING_DATASOURCE_USERNAME=postgres - SPRING_DATASOURCE_PASSWORD=postgres + - MOSIP_MOCK_IDA_KYC_TRANSACTION_TIMEOUT_SECS=1200 + - MOSIP_MOCK_IDENTITY_CREATE_REQUIRED_FIELDS=individualId,fullName,phone,password + - MOSIP_MOCK_IDENTITY_UPDATE_REQUIRED_FIELDS=individualId depends_on: - database @@ -57,14 +60,31 @@ services: - 8088:8088 environment: - container_user=mosip - - active_profile_env=local + - active_profile_env=default,local - plugin_name_env=esignet-mock-plugin.jar - KAFKA_ENABLED=false + - SPRING_CACHE_TYPE=redis - SPRING_REDIS_HOST=redis + - SPRING_REDIS_PASSWORD= - SPRING_AUTOCONFIGURE_EXCLUDE=org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration - SPRING_DATASOURCE_URL=jdbc:postgresql://database:5432/mosip_esignet?currentSchema=esignet - MOSIP_ESIGNET_MOCK_DOMAIN_URL=http://mock-identity-system:8082 + - MOSIP_ESIGNET_INTEGRATION_KEY_BINDER=MockKeyBindingWrapperService depends_on: - database - redis - - mock-identity-system \ No newline at end of file + - mock-identity-system + + esignet-ui: + image: 'mosipdev/oidc-ui:release-1.5.x' + user: root + ports: + - 3000:3000 + environment: + - container_user=mosip + - DEFAULT_WELLKNOWN=%5B%7B%22name%22%3A%22OpenID%20Configuration%22%2C%22value%22%3A%22%2F.well-known%2Fopenid-configuration%22%7D%2C%7B%22name%22%3A%22Jwks%20Json%22%2C%22value%22%3A%22%2F.well-known%2Fjwks.json%22%7D%2C%7B%22name%22%3A%22Authorization%20Server%22%2C%22value%22%3A%22%2F.well-known%2Foauth-authorization-server%22%7D%5D + - SIGN_IN_WITH_ESIGNET_PLUGIN_URL=https://raw.githubusercontent.com/mosip/artifactory-ref-impl/master/artifacts/src/mosip-plugins/sign-in-with-esignet/sign-in-with-esignet.zip + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + depends_on: + - esignet diff --git a/docs/create-signup-oidc-keystore.sh b/docs/create-signup-oidc-keystore.sh index ddecea25..84fc62d5 100755 --- a/docs/create-signup-oidc-keystore.sh +++ b/docs/create-signup-oidc-keystore.sh @@ -44,7 +44,7 @@ cat > $JWK_FILE < connected -> subscribe to slotId -> send/publish to /ws/process-frames destination + + Message is received on the subscribed slotId + requestBody: + content: + application/json: + schema: + type: object + required: + - slotId + - stepCode + - frames + properties: + slotId: + type: string + x-stoplight: + id: n45hj9gc30s42 + description: Valid slotId as returned by the fetch slotID endpoint. + stepCode: + type: string + x-stoplight: + id: ww928zat6csik + description: | + Step Code of the current step in video identity verification. + Two default step code for any identity verifier is "START" and "END". + frames: + type: array + x-stoplight: + id: dovez6i2fekdd + description: 'Captured frames, in the "START" step frames will be empty array.' + items: + x-stoplight: + id: 3jfn0g164rc08 + type: object + required: + - frame + - order + properties: + frame: + type: string + x-stoplight: + id: 0eo56iryvt1df + description: Encoded captured image. + order: + type: integer + x-stoplight: + id: r0767pp3awa31 + description: Order of the captured frame. + /identity-verification/status: + get: + tags: + - UI + summary: Get Identity Verification Status Endpoint + description: |- + Endpoint to get the latest identity verification status. + + 1. Validate the IDV_SLOT_ALLOTTED id in the cookie. + 2. Check status of verified data update in the integrated registry. + 3. Return back the final status in the response. + operationId: get-identity-verification-status + parameters: + - name: X-XSRF-TOKEN + in: header + description: CSRF token as set in cookie key 'XSRF-TOKEN' + required: false + schema: + type: string + - name: IDV_SLOT_ALLOTTED + in: cookie + description: Cookie set after the allotted slot ID verification + required: true + schema: + type: string + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + responseTime: + type: string + description: Current date and time when the request is sent + format: date-time + response: + type: object + properties: + status: + type: string + enum: + - UPDATE_PENDING + - COMPLETED + - FAILED + - STARTED + description: Status of registration + errors: + type: array + description: List of Errors in case of request validation / processing failure in the server. + items: + type: object + properties: + errorCode: + type: string + enum: + - invalid_transaction + - unknown_error + errorMessage: + type: string + enum: + - invalid_transaction + - unknown_error + examples: + Example 1: + value: + responseTime: '2023-11-03T11:03:49.770Z' + response: + status: COMPLETED + errors: [] + servers: + - url: 'https://signup.collab.mosip.net/v1/signup' + x-stoplight: + id: dub1wcgovwhwl + parameters: [] tags: - name: UI description: UI related API. @@ -701,6 +1304,8 @@ components: otp.blocked: type: integer description: 'Number of seconds, a mobile number will not be allowed to register.' + x-stoplight: + id: qsg1ykzn89elz ChallengeInfo: type: object title: ChallengeInfo @@ -724,6 +1329,8 @@ components: - challenge - format - type + x-stoplight: + id: vupi50p6qhgul UserInfoMap: type: object title: UserInfoMap @@ -739,6 +1346,8 @@ components: required: - fullName - phone + x-stoplight: + id: 7gzdo83lsa98j LanguageTaggedValue: type: object title: LanguageTaggedValue @@ -747,3 +1356,69 @@ components: type: string value: type: string + x-stoplight: + id: fjvqsxzx2yzln + ProviderDetail: + title: IdentityVerifierDetail + x-stoplight: + id: b6hnskxc5isg1 + type: object + description: Identity Verifier details to be used by the UI to display. And every verifier is identified with a unique `id` + required: + - id + - displayName + - logoUrl + - processType + - active + properties: + id: + type: string + x-stoplight: + id: osonjar9p3hng + description: A unique identifier for the identity verifier. + description: + type: object + description: A map containing localized descriptions for the identity verifier. + x-stoplight: + id: mdywcu1t5p7f7 + additionalProperties: + x-stoplight: + id: p5s44gsq5418c + type: string + displayName: + type: object + description: A map containing localized display names for the identity verifier. + x-stoplight: + id: d7gji8pii33ao + additionalProperties: + x-stoplight: + id: iiis9477hhde8 + type: string + logoUrl: + type: string + description: The URL to the logo image representing the identity verifier. + x-stoplight: + id: p53z2gnd7g7t8 + processType: + type: string + description: The type of process associated with the identity verification. + x-stoplight: + id: c1izbfvk09g2e + retryOnFailure: + type: boolean + description: A flag indicating if the process should automatically retry on failure. + x-stoplight: + id: cv452a1uhh6v4 + resumeOnSuccess: + type: boolean + description: A flag indicating if the process can resume from the last successful state. + x-stoplight: + id: ewzxm6x6p3pg0 + active: + type: boolean + description: A flag indicating whether the identity verifier is currently active. + x-stoplight: + id: tp0ta9gots7qw + processDuration: + type: number + description: The duration (in seconds) for which the identity verification process typically runs. diff --git a/helm/signup-service/values.yaml b/helm/signup-service/values.yaml index e544e60b..787b3cbf 100644 --- a/helm/signup-service/values.yaml +++ b/helm/signup-service/values.yaml @@ -287,6 +287,16 @@ extraEnvVars: | secretKeyRef: name: keycloak-client-secrets key: mosip_signup_client_secret + - name: MOSIP_SIGNUP_MOCK_MANDATORY_ATTRIBUTES_CREATE + value: fullName,phone,password,preferredLang + - name: MOSIP_SIGNUP_MOCK_USERNAME_FIELD + value: phone + - name: MOSIP_SIGNUP_SUPPORTED_LANGUAGES + value: '{''eng'',''khm''}' + - name: MOSIP_SIGNUP_FULLNAME_PATTERN + value: ^[\u1780-\u17FF\u19E0-\u19FF\u1A00-\u1A9F\u0020]{1,30}$ + - name: MOSIP_SIGNUP_UI_CONFIG_KEY_VALUES_FULLNAME_ALLOWED_CHARACTERS + value: ^[\u1780-\u17FF\u19E0-\u19FF\u1A00-\u1A9F\u0020] ## ConfigMap with extra environment variables that used ## diff --git a/partner-onboarder/README.md b/partner-onboarder/README.md index 7e627aec..010b88f2 100644 --- a/partner-onboarder/README.md +++ b/partner-onboarder/README.md @@ -3,20 +3,34 @@ ## Overview Creates and onboards eSignet signup OIDC client. 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`. ``` ./install.sh ``` -# Troubleshootings +* When install.sh runs,it uses https://github.com/mosip/mosip-onboarding/blob/release-1.3.x/certs/create-signing-certs.sh to generate appropriate keypair and keystore.p12 file for the partner. + +* After generating the same it uses the public key in JWK format in the OIDC client creation process. +* The keystore file(.p12) ,which contains the private-key is then stored as a secret in the k8s cluster,from where it is mounted on to the esignet-signup pod in the esignet namespace. +# Troubleshooting: * Once onboarder job is completed, detailed `html report` is prepared and stored at provided S3 bucket / NFS directory. -* Once onboarder helm installation is complted, please check the reports to confirm sucessfull onboarding. +* Once onboarder helm installation is complted, please check the reports to confirm successful onboarding. ### Commonly found issues 1. KER-ATH-401: Authentication Failed Resolution: You need to provide correct secretkey for mosip-deployment-client. 1. Certificate dates are not valid - Resolution: Check with admin regarding adding grace period in configuration. -1. Upload of certificate will not be allowed to update other domain certificate - Resolution: This is expected when you try to upload `ida-cred` certificate twice. It should only run once and if you see this error while uploading a second time it can be ignored as the cert is already present. + Resolution: Check with admin regarding certificate renewal through re-onboarding. diff --git a/partner-onboarder/install.sh b/partner-onboarder/install.sh index dd04673b..519d2411 100755 --- a/partner-onboarder/install.sh +++ b/partner-onboarder/install.sh @@ -111,6 +111,7 @@ function installing_onboarder() { --set extraEnvVarsCM[2]=keycloak-host \ $ENABLE_INSECURE \ -f values.yaml \ + --set image.repository=mosipdev/partner-onboarder --set image.tag=MOSIP-35987 \ --version $CHART_VERSION \ --wait --wait-for-jobs echo "Partner onboarder executed and reports are moved to S3 or NFS please check the same to make sure partner was onboarded sucessfully." diff --git a/postman-collection/README.md b/postman-collection/README.md new file mode 100644 index 00000000..4ba267cf --- /dev/null +++ b/postman-collection/README.md @@ -0,0 +1,85 @@ +## Signup service postman collection + +Collection contains 3 folder, each containing requests for 3 different operations: + +1. Registration - contains sequence of requests to register a new user in the plugged in ID registry +2. Reset password - contains sequence of requests to reset password for an already created user. +3. Identity(eKYC) verification - Sequence of requests to initiate identity verification process. Priori to this +request a valid authorization code should be generated. Using eSignet collection/verified claims folder one +can obtain the authorization code and the id-token-hint. + +In the identity verification folder once the slot-id is returned in the "slot" endpoint response. One should run the ws_client.py to +carry out the video identity(eKYC) verification process using a WebSocket connection. + +**Note:** Mock plugin does not validate the frames in the current version. + +## Usage of [ws_client.py](ws_client.py) + +eKYC verification process is carried out through WebSocket connection and as postman currently does not support export of WS +collections, we have created [ws_client.py](ws_client.py) script. + +This script is a simple Python WebSocket client that connects to a specified WebSocket server, subscribes to a topic using +the STOMP protocol, and allows the user to send messages to that topic. + +## Overview of the Script +User Input: + +The script starts by asking the user for the WebSocket server URL, slot ID, and cookie value. + +If you are running eSignet signup service in local, then the url will be "ws://localhost:8089/v1/signup/ws" +Slot ID and IDV_SLOT_ALOTTED cookie value should be taken from 'http://localhost:8088/v1/signup/identity-verification/slot' endpoint response. + +## WebSocket Callbacks: + +Several callback functions handle different events during the WebSocket connection lifecycle: +on_message: Called when a message is received from the server. +on_error: Called when an error occurs during the WebSocket operation. +on_close: Called when the WebSocket connection is closed. +on_open: Called when the WebSocket connection is successfully established. + +## STOMP Protocol Frames: + +The script uses the STOMP protocol to communicate with the WebSocket server, sending: +A CONNECT frame to initiate the STOMP connection. +A SUBSCRIBE frame to listen for messages on a specified topic (based on the user-provided slot_id). +A SEND frame to send messages from the user input to the specified destination. + +## Threading for User Input: + +The send_user_input function runs in a separate thread, allowing the main thread to continue processing incoming messages while waiting for user input. +The user can enter messages to send to the server or type "exit" to close the connection. + +## WebSocket Connection Management: + +The start_ws_client function sets up the WebSocket connection using the websocket-client library, specifying the URI and headers (including cookies). +The connection is established with ws.run_forever(), which keeps the connection alive and processes incoming messages. + + +## How to use the script? +1. Install Required Library: Ensure you have the websocket-client library installed. You can install it using: + +`pip install websocket-client` + +2. Run the Script: Execute the script in your terminal or command prompt: + +`python ` + +3. Provide Input: When prompted, enter the base URL (WebSocket server address), slot ID, and cookie value. + +4. Sending Messages: When prompted, to enter message to send, type the message as below, there are 3 different messages + +START step message -> `{"slotId":"","stepCode":"START","frames":[{"frame":"","order":"0"}]}` + +Other step messages -> `{"slotId":"","stepCode":"","frames":[{"frame":"","order":"0"}]}` + +END step message -> `{"slotId":"","stepCode":"END","frames":[{"frame":"","order":"0"}]}` + +5. Receiving Messages: Any messages sent from the server to the subscribed topic will be printed to the console as they are received. + + +## Example interaction + + +![img.png](interaction_1.png) + +![img_1.png](interaction_2.png) \ No newline at end of file diff --git a/postman-collection/eSignet Signup.postman_collection.json b/postman-collection/eSignet Signup.postman_collection.json index fe19a352..8f0f8ad8 100644 --- a/postman-collection/eSignet Signup.postman_collection.json +++ b/postman-collection/eSignet Signup.postman_collection.json @@ -38,7 +38,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"clientId\": \"mosip-signup-oauth-client\",\n \"clientName\": \"mosip-signup-oauth-client\",\n \"publicKey\": {\n \"kty\": \"RSA\",\n \"n\": \"lfq9e7C3aHIXeMdVV5LnuiC8WRI6jvRdXqNeFwUh6t2jASN4Ku3ENlPGdsIY3VIoR5YXoogfKUkR3suld3pDGzyeuGp8v1gkRJ0IZtPioXyBLa4PET3-9CsdsH5H7MV0Vz6606h1ZfN95vGWjsWyqjbZbzeWc8xbcVQ2-GVMVGVZNrCP-iJ0v7m7f89FXtZH4kzvzKwFt_KJy1_7xD3op2Xu5NtR2-PENDJLwh501slBuJczcagplfZTm4Hz7v9coCx3QHuMOv4iwMhFnpcpymiY7YT1geUDoUzR3MY-1OT-SKzWWnyLC8p8s0EkfvsXXHSr9zVTtqhGqjlZjQP9pQ\",\n \"e\": \"AQAB\",\n \"alg\": \"RS256\",\n \"use\": \"sig\"\n },\n \"relyingPartyId\": \"mosip-signup-oauth-client\",\n \"userClaims\": [],\n \"authContextRefs\": [\n \"mosip:idp:acr:id-token\"\n ],\n \"logoUri\": \"{{$randomImageUrl}}\",\n \"redirectUris\": [\n \"http://localhost:8089/identity-verification\"\n ],\n \"grantTypes\": [\n \"authorization_code\"\n ],\n \"clientAuthMethods\": [\n \"private_key_jwt\"\n ]\n }\n}", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"clientId\": \"mosip-signup-oauth-client\",\n \"clientName\": \"mosip-signup-oauth-client\",\n \"publicKey\":{\n \"kty\": \"RSA\",\n \"n\": \"om6aewZ1ls3bwIt0BdAJcxw8N1GjHN4S0pIqZRlxcUtoZCHSi5bTli4kIb0tb9VSrv4FxPbVOL8sJVDv2T5O4prKZUHMfghlgjqOSR_h9vfxqKH8PyBS-AMrkjeNSNf5wIrZQGBcjw45v24GpseHu1O-VAqJQkmGDAIdnj5Cpmoo-_rI45_hTNWPIp-8tLIPB7SqdNd104PdbmDePpVVRQhe3iquGC6x41ATgd2LblVji8_BMm4lP5L9-17AV20yGcO8w_9IciPQIwz0_7c8ApS-mLH1IJ4LuItu-y_Dl4x7_Q-glfobc2JTDPEhpHYEJxIlszGJIwN3A9AAmhqXyQ\",\n \"e\": \"AQAB\",\n \"alg\": \"RS256\",\n \"use\": \"sig\"\n },\n \"relyingPartyId\": \"mosip-signup-oauth-client\",\n \"userClaims\": [],\n \"authContextRefs\": [\n \"mosip:idp:acr:id-token\"\n ],\n \"logoUri\": \"{{$randomImageUrl}}\",\n \"redirectUris\": [\n \"http://localhost:8089/identity-verification\"\n ],\n \"grantTypes\": [\n \"authorization_code\"\n ],\n \"clientAuthMethods\": [\n \"private_key_jwt\"\n ]\n }\n}", "options": { "raw": { "language": "json" @@ -711,12 +711,21 @@ { "name": "Initiate", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript", + "packages": {} + } + }, { "listen": "prerequest", "script": { "exec": [ - "code = pm.environment.get(\"idt_auth_code\");", - "pm.environment.set(\"code\", code);" + "" ], "type": "text/javascript", "packages": {} @@ -734,7 +743,7 @@ ], "body": { "mode": "raw", - "raw": "{\r\n \"requestTime\": \"{{$isoTimestamp}}\",\r\n \"request\": {\r\n \"authorizationCode\": \"{{code}}\",\r\n \"state\": \"urlInfo1724138417665\"\r\n }\r\n}", + "raw": "{\r\n \"requestTime\": \"{{$isoTimestamp}}\",\r\n \"request\": {\r\n \"authorizationCode\": \"{{idt_auth_code}}\",\r\n \"state\": \"urlInfo1724138417665\"\r\n }\r\n}", "options": { "raw": { "language": "json" @@ -791,7 +800,27 @@ "script": { "exec": [ "var jsonData = pm.response.json();\r", - "pm.environment.set(\"slot-id\", jsonData.response.slotId);" + "pm.globals.set(\"slot_id\", jsonData.response.slotId);\r", + "\r", + "// Get all Set-Cookie headers\r", + "let setCookieHeaders = pm.response.headers.filter(function(header) {\r", + " return header.key.toLowerCase() === \"set-cookie\";\r", + "});\r", + "\r", + "// Loop through each Set-Cookie header and extract the values\r", + "setCookieHeaders.forEach(function(cookieHeader, index) {\r", + " console.log(\"Set-Cookie Header #\" + (index + 1) + \": \" + cookieHeader.value);\r", + "\r", + " // You can split the cookie into name=value and other attributes\r", + " let cookieParts = cookieHeader.value.split(';'); // Split by ';' to separate name=value from attributes\r", + " let cookieNameValue = cookieParts[0]; // The first part is the cookie name=value\r", + " \r", + " if(cookieNameValue.split('=')[0].trim() === \"IDV_SLOT_ALLOTTED\") {\r", + " console.log(\"IDV_SLOT_ALLOTTED matched\");\r", + " pm.globals.set(\"idv_slot_allotted\", cookieNameValue.split('=')[1].trim());\r", + " }\r", + "});\r", + "" ], "type": "text/javascript", "packages": {} @@ -840,6 +869,11 @@ "key": "X-XSRF-TOKEN", "value": "{{csrf_token}}", "type": "text" + }, + { + "key": "Cookie", + "value": "IDV_SLOT_ALLOTTED={{idv_slot_allotted}}", + "type": "text" } ], "url": { diff --git a/postman-collection/interaction_1.png b/postman-collection/interaction_1.png new file mode 100644 index 00000000..f241b94b Binary files /dev/null and b/postman-collection/interaction_1.png differ diff --git a/postman-collection/interaction_2.png b/postman-collection/interaction_2.png new file mode 100644 index 00000000..7799369b Binary files /dev/null and b/postman-collection/interaction_2.png differ diff --git a/postman-collection/ws_client.py b/postman-collection/ws_client.py new file mode 100644 index 00000000..19fe4d28 --- /dev/null +++ b/postman-collection/ws_client.py @@ -0,0 +1,70 @@ +import websocket +import threading +import sys + +base_url=input("Enter the base URL (ws://localhost:8089/v1/signup/ws): ") +slot_id=input("Enter the slotId: ") +cookie=input("Enter the cookie value: ") + +def on_message(ws, message): + print("===================") + print(f"Received {message}") + print("===================") + +def on_error(ws, error): + print("Error:", error) + +def on_close(ws, close_status_code, close_msg): + print("Connection closed:", close_status_code, close_msg) + +def on_open(ws): + # Send STOMP CONNECT frame + connect_frame = "CONNECT\naccept-version:1.2\n\n\x00" + ws.send(connect_frame) + print(f"{connect_frame}") + + # Subscribe to the /topic/slotId destination + subscribe_frame = f"SUBSCRIBE\nid:sub-0\ndestination:/topic/{slot_id}\n\n\x00" + ws.send(subscribe_frame) + print(f"{subscribe_frame}") + + # Start a new thread to take user input and send messages to the WebSocket + threading.Thread(target=send_user_input, args=(ws,)).start() + +def send_user_input(ws): + try: + while True: + user_input = input("Enter a message to send: ") + if user_input.lower() == "exit": + print("Closing connection...") + ws.close() + break + + # Send user input as a message to the WebSocket + send_frame = f"SEND\ndestination:/v1/signup/ws/process-frame\ncontent-type:application/json\n\n{user_input}\x00" + ws.send(send_frame) + print(f"{send_frame}") + + except Exception as e: + print("Error sending message:", e) + +# WebSocket connection +def start_ws_client(): + uri = f"{base_url}?slotId={slot_id}" # Replace with your WebSocket server's URI + headers = {"Cookie": f"IDV_SLOT_ALLOTTED={cookie}"} # Replace with any necessary headers + + ws = websocket.WebSocketApp( + uri, + header=headers, + on_open=on_open, + on_message=on_message, + on_error=on_error, + on_close=on_close + ) + + # Run the WebSocket with a blocking loop + ws.run_forever() + +# Run the subscribe function +if __name__ == "__main__": + start_ws_client() diff --git a/signup-integration-api/README.md b/signup-integration-api/README.md index 64e72528..fc2a1b51 100644 --- a/signup-integration-api/README.md +++ b/signup-integration-api/README.md @@ -1,9 +1,4 @@ -## Dependency -Add this dependency in your pom.xml file. -``` - - io.mosip.signup - signup-integration-api - 1.0.0-SNAPSHOT - -``` +## Signup integration interfaces + +This module contains all the interface defined for external integrations with signup-service. Kindly find the default implementations +for the plugins in [esignet-plugins](https://github.com/mosip/esignet-plugins) repository. diff --git a/signup-integration-api/src/main/java/io/mosip/signup/api/impl/NoOpIdentityVerifierPluginImpl.java b/signup-integration-api/src/main/java/io/mosip/signup/api/impl/NoOpIdentityVerifierPluginImpl.java index a60bc417..b228898b 100644 --- a/signup-integration-api/src/main/java/io/mosip/signup/api/impl/NoOpIdentityVerifierPluginImpl.java +++ b/signup-integration-api/src/main/java/io/mosip/signup/api/impl/NoOpIdentityVerifierPluginImpl.java @@ -27,16 +27,17 @@ public List getSupportedProcessTypes() { @Override public void initialize(String transactionId, IdentityVerificationInitDto identityVerificationInitDto) { - + log.warn("NoOpVerifier initialize invoked!!"); } @Override public void verify(String transactionId, IdentityVerificationDto identityVerificationDto) throws IdentityVerifierException { - + log.warn("NoOpVerifier verify invoked!!"); } @Override public VerificationResult getVerificationResult(String transactionId) throws IdentityVerifierException { + log.warn("NoOpVerifier getVerificationResult invoked!!"); return null; } } diff --git a/signup-service/README.md b/signup-service/README.md index 7cafcedf..be735504 100644 --- a/signup-service/README.md +++ b/signup-service/README.md @@ -9,16 +9,17 @@ Signup service is a spring boot application with endpoints to 5. Reset the password of the registered user 6. Websocket handler and endpoints to support video identity verification process. -Signup service connects to MOSIP IDRepo Identity service to register the verified user as an identity record. -ID Repo identity service publishes the registered identity to MOSIP IDA. This enables authentication with the registered -username and password with eSignet. +In this version, signup-service require below MOSIP kernel modules and its dependents: +1. kernel otpmanager +2. kernel auditmanager +3. kernel authmanager +4. kernel notifier -Publishing registered/updated identity to MOSIP IDA is an async process. Hence, status endpoint is configured to check -the latest status from server after every configured interval from signup UI. +**Note:** To run signup-service locally `mosip.internal.domain.url` property should be set with a valid base URL of any MOSIP(LTS) environment. ### Signup service uses spring cache to store the transaction details. -Registration flow: +#### Registration flow: | Endpoint | Cache | Evict | |-------------------|----------------------------------------------------------------------|----------------------------------------------------------------------| @@ -27,7 +28,7 @@ Registration flow: | register | status_check (k: verified-transactionId, v: SignupTransaction) | challenge_verified (k: verified-transactionId, v: SignupTransaction) | | status | status_check (k: verified-transactionId, v: SignupTransaction) | | -Reset Password flow: +#### Reset Password flow: | Endpoint | Cache | Evict | |-------------------|----------------------------------------------------------------------|----------------------------------------------------------------------| @@ -37,7 +38,7 @@ Reset Password flow: | status | status_check (k: verified-transactionId, v: SignupTransaction) | | -Identity Verification flow: +#### Identity Verification flow: | Endpoint | Cache | Evict | |---------------------------------|--------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| @@ -63,31 +64,31 @@ Identity Verification flow: > Note: The connection-id is concatenation of transactionId and slotId with a separator. -## Build & run (for developers) +## Build & Run (for developers) The project requires JDK 11. 1. Build and install: ``` - $ mvn clean install -Dgpg.skip=true + $ mvn clean install -Dgpg.skip=true -Dmaven.gitcommitid.skip=true ``` -2. Build Docker for a service: - ``` - $ docker build -f Dockerfile - ``` - 3. Run with IntelliJ IDEA +2. Run with IntelliJ IDE + + 3.1 Right click on signup-service and click on "Open module settings". + + 3.2 Click on "+" to add jars from external directory. + + 3.3 Choose below jars under signup-service/target/signup-plugins and click on "Apply" and "Ok" button. + + -> [kernel-auth-adapter-lite.jar](target/signup-plugins/kernel-auth-adapter-lite.jar) - 3.1 Right click on parent POM file (pom.xml) and click on button "Add as Maven Project". + -> [esignet-mock-plugin.jar](target/signup-plugins/esignet-mock-plugin.jar) + + 3.4 Update below properties in [application-local.properties](src/main/resources/application-local.properties) to point to right MOSIP environment. - 3.2 Add below dependency in the signup-service pom.xml + -> `mosip.internal.domain.url=https://api-internal.dev.mosip.net` - ``` - - io.mosip.kernel - kernel-auth-adapter-lite - 1.2.0.1-B4 - - ``` + -> `keycloak.external.url=https://iam.dev.mosip.net` - 3.3 Add that file to "signup-service" in Project Structure settings of IntelliJ, and Apply. + -> `mosip.signup.client.secret=actual-secret` - 3.4 Open signup-service/src/main/java/io/mosip/signup/SignUpServiceApplication.java and click on Run + 3.4 Go to [SignUpServiceApplication.java](src/main/java/io/mosip/signup/SignUpServiceApplication.java) and run from the main class. diff --git a/signup-service/configure_start.sh b/signup-service/configure_start.sh index 511ebcac..21ceb477 100644 --- a/signup-service/configure_start.sh +++ b/signup-service/configure_start.sh @@ -30,5 +30,12 @@ else exit 1 fi +## set active profile if not set +if [[ -z "$active_profile_env" ]]; then + echo "Alert: active_profile_env is not set. setting to default" + active_profile_env="default" + export active_profile_env +fi + cd $work_dir exec "$@" diff --git a/signup-service/src/main/java/io/mosip/signup/config/Config.java b/signup-service/src/main/java/io/mosip/signup/config/Config.java index 4b42c579..e41b0c11 100644 --- a/signup-service/src/main/java/io/mosip/signup/config/Config.java +++ b/signup-service/src/main/java/io/mosip/signup/config/Config.java @@ -31,7 +31,7 @@ public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(taskCorePoolSize); executor.setMaxPoolSize(taskMaxPoolSize); - executor.setThreadNamePrefix("MOSIP-SIGNUP-Async-Thread-"); + executor.setThreadNamePrefix("ES-SIGNUP-Async-Thread-"); executor.initialize(); return executor; } diff --git a/signup-service/src/main/java/io/mosip/signup/config/WebSocketConfig.java b/signup-service/src/main/java/io/mosip/signup/config/WebSocketConfig.java index 4f35ed33..fed2c88f 100644 --- a/signup-service/src/main/java/io/mosip/signup/config/WebSocketConfig.java +++ b/signup-service/src/main/java/io/mosip/signup/config/WebSocketConfig.java @@ -22,13 +22,14 @@ public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/v1/signup/ws"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //By default, only same origin requests are allowed, should take the origin from properties - registry.addEndpoint("/ws").setAllowedOrigins("*").setHandshakeHandler(webSocketHandshakeHandler); + registry.addEndpoint("/ws") + .setAllowedOrigins("*") + .setHandshakeHandler(webSocketHandshakeHandler); } } diff --git a/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java b/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java index f9ec6537..0025da12 100644 --- a/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java +++ b/signup-service/src/main/java/io/mosip/signup/controllers/RegistrationController.java @@ -45,7 +45,7 @@ public class RegistrationController { protected void initBinder(WebDataBinder binder) { if(binder.getTarget() != null && RequestWrapper.class.equals(binder.getTarget().getClass())) { RequestWrapper dto = (RequestWrapper) binder.getTarget(); - if(RegisterRequest.class.equals(dto.getRequest().getClass())) { + if(dto.getRequest() != null && RegisterRequest.class.equals(dto.getRequest().getClass())) { RegisterRequest registerRequest = (RegisterRequest) dto.getRequest(); //TODO remove this logic after changes in the UI is done to pass password inside userinfo registerRequest.setUserInfo( diff --git a/signup-service/src/main/java/io/mosip/signup/controllers/WebSocketController.java b/signup-service/src/main/java/io/mosip/signup/controllers/WebSocketController.java index d2b63448..f29e5eb6 100644 --- a/signup-service/src/main/java/io/mosip/signup/controllers/WebSocketController.java +++ b/signup-service/src/main/java/io/mosip/signup/controllers/WebSocketController.java @@ -8,11 +8,12 @@ import io.mosip.signup.api.dto.*; import io.mosip.signup.api.exception.IdentityVerifierException; import io.mosip.signup.api.spi.IdentityVerifierPlugin; -import io.mosip.signup.api.util.VerificationStatus; import io.mosip.signup.dto.IdentityVerificationRequest; -import io.mosip.signup.dto.IdentityVerificationTransaction; +import io.mosip.signup.helper.AuditHelper; import io.mosip.signup.services.CacheUtilService; import io.mosip.signup.services.WebSocketHandler; +import io.mosip.signup.util.AuditEvent; +import io.mosip.signup.util.AuditEventType; import io.mosip.signup.util.ErrorConstants; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -22,7 +23,6 @@ import org.springframework.messaging.handler.annotation.Payload; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; -import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.messaging.SessionConnectedEvent; import org.springframework.web.socket.messaging.SessionDisconnectEvent; @@ -41,6 +41,9 @@ public class WebSocketController { @Autowired private CacheUtilService cacheUtilService; + @Autowired + AuditHelper auditHelper; + @MessageMapping("/process-frame") public void processFrames(final @Payload IdentityVerificationRequest identityVerificationRequest) { @@ -52,12 +55,14 @@ public void processFrames(final @Payload IdentityVerificationRequest identityVer throw new IdentityVerifierException(ErrorConstants.INVALID_STEP_CODE); webSocketHandler.processFrames(identityVerificationRequest); + auditHelper.sendAuditTransaction(AuditEvent.PROCESS_FRAMES, AuditEventType.SUCCESS, identityVerificationRequest.getSlotId(),null); } - @KafkaListener(id = "step-status-consumer", autoStartup = "true", + @KafkaListener(id = "${kafka.consumer.group-id}", autoStartup = "true", topics = IdentityVerifierPlugin.RESULT_TOPIC) public void consumeStepResult(final IdentityVerificationResult identityVerificationResult) { webSocketHandler.processVerificationResult(identityVerificationResult); + auditHelper.sendAuditTransaction(AuditEvent.CONSUME_STEP_RESULT,AuditEventType.SUCCESS,identityVerificationResult.getId(),null); } @@ -66,23 +71,16 @@ public void onConnected(SessionConnectedEvent connectedEvent) { final String username = Objects.requireNonNull(connectedEvent.getUser()).getName(); log.info("WebSocket Connected >>>>>> {}", username); webSocketHandler.updateProcessDuration(username); + auditHelper.sendAuditTransaction(AuditEvent.ON_CONNECTED,AuditEventType.SUCCESS,username.split(VALUE_SEPARATOR)[0],null); } @EventListener public void onDisconnected(SessionDisconnectEvent disconnectEvent) { String username = Objects.requireNonNull(disconnectEvent.getUser()).getName(); - String transactionId = username.split(VALUE_SEPARATOR)[0]; - String slotId = username.split(VALUE_SEPARATOR)[1]; - log.info("WebSocket Disconnected >>>>>> {}", username); - log.info("WebSocket Disconnected Status>>>>>> {}", disconnectEvent.getCloseStatus()); - if(!CloseStatus.NORMAL.equals(disconnectEvent.getCloseStatus())){ - IdentityVerificationTransaction transaction = - cacheUtilService.getVerifiedSlotTransaction(slotId); - transaction.setStatus(VerificationStatus.FAILED); - cacheUtilService.updateVerifiedSlotTransaction(slotId, transaction); - } cacheUtilService.removeFromSlotConnected(username); - cacheUtilService.evictSlotAllottedTransaction(transactionId,slotId); + cacheUtilService.evictSlotAllottedTransaction(username.split(VALUE_SEPARATOR)[0], + username.split(VALUE_SEPARATOR)[1]); + auditHelper.sendAuditTransaction(AuditEvent.ON_DISCONNECTED,AuditEventType.SUCCESS,username.split(VALUE_SEPARATOR)[0],null); } } diff --git a/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java b/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java index b87ab7d8..0bc0d609 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java +++ b/signup-service/src/main/java/io/mosip/signup/services/CacheUtilService.java @@ -48,8 +48,15 @@ public class CacheUtilService { private RedisConnectionFactory redisConnectionFactory; - - private static final String CLEANUP_SCRIPT = "local hash_name = KEYS[1]\n" + + private static final String CLEANUP_SCRIPT = "local function binary_to_long(binary_str)\n" + + " local result = 0\n" + + " for i = 1, #binary_str do\n" + + " result = result * 256 + binary_str:byte(i)\n" + + " end\n" + + " return result\n" + + "end" + + "\n" + + "local hash_name = KEYS[1]\n" + "local time = redis.call(\"TIME\")\n" + "local current_time = ( tonumber(time[1]) * 1000) + math.floor( tonumber(time[2]) / 1000)\n" + "local verified_slot_cache_keys = {}\n" + @@ -97,6 +104,13 @@ public class CacheUtilService { "return add_to_hset(KEYS[1], ARGV[1], ARGV[2], ARGV[3])\n"; private String addSlotScriptHash = null; + private static final String UPDATE_SLOT_EXPIRE_DT_SCRIPT = "local function update_slot_expire_dt(key, field, value)\n" + + "redis.call('HSET', key, field, value)\n" + + "end\n" + + "\n" + + "return update_slot_expire_dt(KEYS[1], ARGV[1], ARGV[2], ARGV[3])\n"; + private String updateSlotExpireDtScriptHash = null; + //---Setter--- @@ -238,21 +252,10 @@ public void updateVerificationStatus(String haltedTransactionId, String status, } } - public void addToSlotConnected(String value, long slotExpireEpochInMillis) { - redisConnectionFactory.getConnection().hSet(SLOTS_CONNECTED.getBytes(), value.getBytes(), - Longs.toByteArray(slotExpireEpochInMillis)); - } - public void removeFromSlotConnected(String value) { redisConnectionFactory.getConnection().hDel(SLOTS_CONNECTED.getBytes(), value.getBytes()); } - public long getCurrentSlotCount() { - Long count = redisConnectionFactory.getConnection().hLen(SLOTS_CONNECTED.getBytes()); - log.info("Current allotted slot count : {}", count); - return count == null ? 0 : count; - } - //Cleanup assigned slot details on WS connection disconnect @Caching(evict = { @CacheEvict(value = SignUpConstants.SLOT_ALLOTTED, key = "#transactionId") @@ -307,4 +310,22 @@ public Long getSetSlotCount(String field, long expireTimeInMillis, Integer maxCo return -1L; } + public void updateSlotExpireTime(String field, long expireTimeInMillis) { + if (redisConnectionFactory.getConnection() != null) { + if (updateSlotExpireDtScriptHash == null) { + updateSlotExpireDtScriptHash = redisConnectionFactory.getConnection().scriptingCommands().scriptLoad(UPDATE_SLOT_EXPIRE_DT_SCRIPT.getBytes()); + } + log.info("Running UPDATE_SLOT_EXPIRE_DT_SCRIPT script: {} {} {}", updateSlotExpireDtScriptHash, SLOTS_CONNECTED, field); + + redisConnectionFactory.getConnection().scriptingCommands().evalSha( + updateSlotExpireDtScriptHash, + ReturnType.INTEGER, + 1, // Number of keys (SLOTS_CONNECTED is the key here) + SLOTS_CONNECTED.getBytes(StandardCharsets.UTF_8), // key (first argument in Lua script) + field.getBytes(StandardCharsets.UTF_8), // field (second argument in Lua script) + String.valueOf(expireTimeInMillis).getBytes(StandardCharsets.UTF_8) // value + ); + } + } + } diff --git a/signup-service/src/main/java/io/mosip/signup/services/IdentityVerificationService.java b/signup-service/src/main/java/io/mosip/signup/services/IdentityVerificationService.java index 6e33bd6f..5e11d312 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/IdentityVerificationService.java +++ b/signup-service/src/main/java/io/mosip/signup/services/IdentityVerificationService.java @@ -74,6 +74,12 @@ public class IdentityVerificationService { @Value("${mosip.signup.identity-verification.txn.timeout}") private int identityVerificationTransactionTimeout; + @Value("${mosip.signup.slot-allotted.timeout:1000}") + private int slotAllottedTimeout; + + @Value("${mosip.signup.verified-slot.timeout:1000}") + private int verifiedSlotTimeout; + @Value("${mosip.signup.oauth.client-id}") private String oauthClientId; @@ -276,7 +282,8 @@ private void addSlotAllottedCookie(String value, IdentityVerifierDetail identity response.addCookie(unsetCookie); Cookie cookie = new Cookie(SignUpConstants.IDV_SLOT_ALLOTTED, value); - cookie.setMaxAge(identityVerifierDetail.getProcessDuration() > 0 ? identityVerifierDetail.getProcessDuration() : identityVerificationTransactionTimeout); + int maxAge = identityVerifierDetail.getProcessDuration() > 0 ? identityVerifierDetail.getProcessDuration() : verifiedSlotTimeout; + cookie.setMaxAge(slotAllottedTimeout+maxAge); cookie.setHttpOnly(true); cookie.setSecure(true); cookie.setPath("/"); diff --git a/signup-service/src/main/java/io/mosip/signup/services/IdentityVerifierFactory.java b/signup-service/src/main/java/io/mosip/signup/services/IdentityVerifierFactory.java index b8cc48ee..7c678a41 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/IdentityVerifierFactory.java +++ b/signup-service/src/main/java/io/mosip/signup/services/IdentityVerifierFactory.java @@ -26,14 +26,11 @@ public class IdentityVerifierFactory { public IdentityVerifierPlugin getIdentityVerifier(String id) { - log.info("Request to fetch identity verifier with id : {}", id); - log.info("List of identity verifiers found : {}", identityVerifiers); + log.debug("Request to fetch identity verifier with id : {} in the available list of verifiers: {}", id, identityVerifiers); Optional result = identityVerifiers.stream() .filter(idv -> idv.getVerifierId().equals(id) ) .findFirst(); - log.info("Identity verifiers result : {}", result); - if(result.isEmpty()) throw new IdentityVerifierException(PLUGIN_NOT_FOUND); diff --git a/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java b/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java index bf8001c3..268147d9 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java +++ b/signup-service/src/main/java/io/mosip/signup/services/RegistrationService.java @@ -19,6 +19,7 @@ import io.mosip.signup.exception.SignUpException; import io.mosip.signup.helper.CryptoHelper; import io.mosip.signup.util.*; +import io.mosip.signup.exception.CaptchaException; import io.mosip.signup.exception.GenerateChallengeException; import io.mosip.signup.helper.NotificationHelper; import lombok.extern.slf4j.Slf4j; @@ -95,8 +96,10 @@ public class RegistrationService { * @throws SignUpException */ public GenerateChallengeResponse generateChallenge(GenerateChallengeRequest generateChallengeRequest, String transactionId) throws SignUpException { - if (captchaRequired) - captchaHelper.validateCaptcha(generateChallengeRequest.getCaptchaToken()); + if (captchaRequired && !captchaHelper.validateCaptcha(generateChallengeRequest.getCaptchaToken())) { + log.error("generate-challenge failed: invalid captcha"); + throw new CaptchaException(ErrorConstants.INVALID_CAPTCHA); + } String identifier = generateChallengeRequest.getIdentifier(); RegistrationTransaction transaction = null; @@ -335,9 +338,9 @@ private void validateTransaction(RegistrationTransaction transaction, String ide } if(transaction.getChallengeRetryAttempts() > resendAttempts) { + log.error("generate-challenge failed: too many attempts, blocking the identifier"); //Resend attempts exhausted, block the identifier for configured time. cacheUtilService.blockIdentifier(transactionId, transaction.getIdentifier(), "blocked"); - log.error("generate-challenge failed: too many attempts"); throw new GenerateChallengeException(ErrorConstants.TOO_MANY_ATTEMPTS); } diff --git a/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandler.java b/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandler.java index 11d27518..b175f5ab 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandler.java +++ b/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandler.java @@ -13,14 +13,15 @@ import io.mosip.signup.api.exception.ProfileException; import io.mosip.signup.api.spi.IdentityVerifierPlugin; import io.mosip.signup.api.spi.ProfileRegistryPlugin; -import io.mosip.signup.api.util.ProcessFeedbackType; import io.mosip.signup.api.util.VerificationStatus; import io.mosip.signup.dto.IdentityVerificationRequest; import io.mosip.signup.dto.IdentityVerificationTransaction; import io.mosip.signup.dto.IdentityVerifierDetail; import io.mosip.signup.exception.InvalidTransactionException; import io.mosip.signup.exception.SignUpException; -import io.mosip.signup.util.ErrorConstants; +import io.mosip.signup.helper.AuditHelper; +import io.mosip.signup.util.AuditEvent; +import io.mosip.signup.util.AuditEventType; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -28,7 +29,10 @@ import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import static io.mosip.signup.api.util.ErrorConstants.IDENTITY_VERIFICATION_FAILED; import static io.mosip.signup.api.util.ErrorConstants.PLUGIN_NOT_FOUND; @@ -57,42 +61,30 @@ public class WebSocketHandler { @Autowired private SimpMessagingTemplate simpMessagingTemplate; + @Autowired + AuditHelper auditHelper; - public void processFrames(IdentityVerificationRequest identityVerificationRequest) { - String errorCode = null; - try { - validate(identityVerificationRequest); - IdentityVerificationTransaction transaction = cacheUtilService.getVerifiedSlotTransaction(identityVerificationRequest.getSlotId()); - if(transaction == null) { - log.error("Ignoring identity verification request received for unknown/expired transaction!"); - throw new InvalidTransactionException(); - } - - IdentityVerifierPlugin plugin = identityVerifierFactory.getIdentityVerifier(transaction.getVerifierId()); - if(plugin == null) { - log.error("Ignoring identity verification request received for unknown {} IDV plugin!", identityVerificationRequest.getSlotId()); - throw new SignUpException(PLUGIN_NOT_FOUND); - } - - if(plugin.isStartStep(identityVerificationRequest.getStepCode())) { - IdentityVerificationInitDto identityVerificationInitDto = new IdentityVerificationInitDto(); - identityVerificationInitDto.setIndividualId(transaction.getIndividualId()); - identityVerificationInitDto.setDisabilityType(transaction.getDisabilityType()); - plugin.initialize(identityVerificationRequest.getSlotId(), identityVerificationInitDto); - } - IdentityVerificationDto dto = new IdentityVerificationDto(); - dto.setStepCode(identityVerificationRequest.getStepCode()); - dto.setFrames(identityVerificationRequest.getFrames()); - plugin.verify(identityVerificationRequest.getSlotId(), dto); - } catch (SignUpException e) { - errorCode = e.getErrorCode(); - log.error("An error occurred while processing frames", e); - } finally { - if (errorCode != null) { - sendErrorFeedback(identityVerificationRequest.getSlotId(), errorCode); - } + public void processFrames(IdentityVerificationRequest identityVerificationRequest) { + IdentityVerificationTransaction transaction = cacheUtilService.getVerifiedSlotTransaction(identityVerificationRequest.getSlotId()); + if(transaction == null) + throw new InvalidTransactionException(); + + IdentityVerifierPlugin plugin = identityVerifierFactory.getIdentityVerifier(transaction.getVerifierId()); + if(plugin == null) + throw new SignUpException(PLUGIN_NOT_FOUND); + + if(plugin.isStartStep(identityVerificationRequest.getStepCode())) { + IdentityVerificationInitDto identityVerificationInitDto = new IdentityVerificationInitDto(); + identityVerificationInitDto.setIndividualId(transaction.getIndividualId()); + identityVerificationInitDto.setDisabilityType(transaction.getDisabilityType()); + plugin.initialize(identityVerificationRequest.getSlotId(), identityVerificationInitDto); } + + IdentityVerificationDto dto = new IdentityVerificationDto(); + dto.setStepCode(identityVerificationRequest.getStepCode()); + dto.setFrames(identityVerificationRequest.getFrames()); + plugin.verify(identityVerificationRequest.getSlotId(), dto); } public void processVerificationResult(IdentityVerificationResult identityVerificationResult) { @@ -137,7 +129,7 @@ public void updateProcessDuration(String username) { .filter(idv -> idv.isActive() && idv.getId().equals(transaction.getVerifierId())) .findFirst(); - result.ifPresent(identityVerifierDetail -> cacheUtilService.addToSlotConnected(username, getVerificationProcessExpireTimeInMillis(identityVerifierDetail))); + result.ifPresent(identityVerifierDetail -> cacheUtilService.updateSlotExpireTime(username, getVerificationProcessExpireTimeInMillis(identityVerifierDetail))); } private void handleVerificationResult(IdentityVerifierPlugin plugin, IdentityVerificationResult identityVerificationResult, @@ -169,14 +161,13 @@ private void handleVerificationResult(IdentityVerifierPlugin plugin, IdentityVer break; } - } catch (IdentityVerifierException e) { - log.error("Failed to fetch verified result from the plugin", e); - transaction.setStatus(VerificationStatus.FAILED); - transaction.setErrorCode(e.getErrorCode()); - } catch (ProfileException e) { + } catch (IdentityVerifierException | ProfileException e) { log.error("Failed to update profile", e); transaction.setStatus(VerificationStatus.FAILED); - transaction.setErrorCode(e.getErrorCode()); + transaction.setErrorCode(e instanceof IdentityVerifierException ? + ((IdentityVerifierException) e).getErrorCode() : + ((ProfileException) e).getErrorCode()); + auditHelper.sendAuditTransaction(AuditEvent.PROCESS_FRAMES, AuditEventType.ERROR,transaction.getSlotId(), null); } cacheUtilService.updateVerifiedSlotTransaction(identityVerificationResult.getId(), transaction); cacheUtilService.updateVerificationStatus(transaction.getAccessTokenSubject(), transaction.getStatus().toString(), @@ -187,33 +178,4 @@ private long getVerificationProcessExpireTimeInMillis(IdentityVerifierDetail ide int processDurationInSeconds = identityVerifierDetail.getProcessDuration() <= 0 ? slotExpireInSeconds : identityVerifierDetail.getProcessDuration(); return System.currentTimeMillis() + ( processDurationInSeconds * 1000L ); } - - private void sendErrorFeedback(String slotId, String errorCode) { - IDVProcessFeedback idvProcessFeedback = new IDVProcessFeedback(); - idvProcessFeedback.setType(ProcessFeedbackType.ERROR); - idvProcessFeedback.setCode(errorCode); - - IdentityVerificationResult identityVerificationResult = new IdentityVerificationResult(); - identityVerificationResult.setFeedback(idvProcessFeedback); - - simpMessagingTemplate.convertAndSend("/topic/" + slotId, identityVerificationResult); - } - - private void validate(IdentityVerificationRequest request) { - if (request.getStepCode() == null || request.getStepCode().isBlank()) { - throw new SignUpException(ErrorConstants.INVALID_STEP_CODE); - } - - List frames = request.getFrames(); - if (frames != null && !frames.isEmpty()) { - for (FrameDetail frame : frames) { - if (frame.getFrame() == null || frame.getFrame().isBlank()) { - throw new SignUpException(ErrorConstants.INVALID_FRAME); - } - if (frame.getOrder() < 0) { - throw new SignUpException(ErrorConstants.INVALID_ORDER); - } - } - } - } } diff --git a/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandshakeHandler.java b/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandshakeHandler.java index 7207aa86..e4835265 100644 --- a/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandshakeHandler.java +++ b/signup-service/src/main/java/io/mosip/signup/services/WebSocketHandshakeHandler.java @@ -6,6 +6,9 @@ package io.mosip.signup.services; import io.mosip.signup.dto.IdentityVerificationTransaction; +import io.mosip.signup.helper.AuditHelper; +import io.mosip.signup.util.AuditEvent; +import io.mosip.signup.util.AuditEventType; import io.mosip.signup.util.ErrorConstants; import io.mosip.signup.util.SignUpConstants; import lombok.extern.slf4j.Slf4j; @@ -32,6 +35,9 @@ public class WebSocketHandshakeHandler extends DefaultHandshakeHandler { @Autowired CacheUtilService cacheUtilService; + @Autowired + AuditHelper auditHelper; + private static final String SLOTID_QUERY_PARAM = "slotId="; private static final String SLOT_COOKIE_NAME = SignUpConstants.IDV_SLOT_ALLOTTED+"="; @@ -66,11 +72,13 @@ protected Principal determineUser(ServerHttpRequest request, WebSocketHandler ws !transaction.getSlotId().equals(queryParam.split(SLOTID_QUERY_PARAM)[1]) || !transaction.getSlotId().equals(cookieValue.split(VALUE_SEPARATOR)[1])) { log.error("SlotId in the handshake url doesn't match the slotId in the transaction"); + auditHelper.sendAuditTransaction(AuditEvent.HANDSHAKE_FAILED, AuditEventType.ERROR, transactionId,null); throw new HandshakeFailureException(ErrorConstants.INVALID_TRANSACTION); } final String username = transactionId.concat(VALUE_SEPARATOR).concat(transaction.getSlotId()); cacheUtilService.setVerifiedSlotTransaction(transactionId, transaction.getSlotId(), transaction); + auditHelper.sendAuditTransaction(AuditEvent.HANDSHAKE_SUCCESS, AuditEventType.SUCCESS, transactionId,null); return new Principal() { @Override diff --git a/signup-service/src/main/java/io/mosip/signup/util/AuditEvent.java b/signup-service/src/main/java/io/mosip/signup/util/AuditEvent.java index cbe9855a..4aa432bf 100644 --- a/signup-service/src/main/java/io/mosip/signup/util/AuditEvent.java +++ b/signup-service/src/main/java/io/mosip/signup/util/AuditEvent.java @@ -14,5 +14,11 @@ public enum AuditEvent { RESET_PASSWORD, INITIATE_IDENTITY_VERIFICATION, IDENTITY_VERIFICATION_SLOT, - IDENTITY_VERIFIER; + IDENTITY_VERIFIER, + PROCESS_FRAMES, + CONSUME_STEP_RESULT, + ON_CONNECTED, + ON_DISCONNECTED, + HANDSHAKE_SUCCESS, + HANDSHAKE_FAILED; } diff --git a/signup-service/src/main/resources/application-default.properties b/signup-service/src/main/resources/application-default.properties index 844c8038..5d565359 100644 --- a/signup-service/src/main/resources/application-default.properties +++ b/signup-service/src/main/resources/application-default.properties @@ -3,24 +3,24 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. #---------------------------------------------------------------------------------------------------------------------------- ## challenge.timeout, resend-delay are count as seconds -mosip.signup.identifier.regex=^\\+855[1-9]\\d{7,8}$ -mosip.signup.identifier.prefix=+855 -mosip.signup.supported-languages={'khm','eng'} -mosip.signup.default-language=khm +mosip.signup.identifier.regex=^\\+91[1-9]\\d{7,8}$ +mosip.signup.identifier.prefix=+91 +mosip.signup.supported-languages={'eng'} +mosip.signup.default-language=eng mosip.signup.password.pattern=^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\\x5F\\W])(?=.{8,20})[a-zA-Z0-9\\x5F\\W]{8,20}$ mosip.signup.password.max-length=20 mosip.signup.generate-challenge.blocked.timeout=300 mosip.signup.challenge.timeout=60 mosip.signup.audit.description.max-length=2048 mosip.signup.password.min-length=8 -mosip.signup.fullname.pattern=^[\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]{1,30}$ +mosip.signup.fullname.pattern=.* ## Time given to generate and verify the challenge in seconds. ## Default resend delay is 60 seconds, with 3 attempts, so 60*3=180 seconds. ## Adding 60 seconds for the default generate challenge 180+60=240 -## Adding 10 seconds buffer to default 240 seconds = 250 seconds. -## so 250 seconds is the Generate and verify cookie max age. -mosip.signup.unauthenticated.txn.timeout=250 +## Adding 60 seconds buffer to default 240 seconds = 300 seconds. +## so 300 seconds is the Generate and verify cookie max age. +mosip.signup.unauthenticated.txn.timeout=300 mosip.signup.challenge.resend-attempt=3 mosip.signup.challenge.verification-attempt=3 mosip.signup.challenge.resend-delay=${mosip.signup.challenge.timeout} @@ -44,7 +44,6 @@ mosip.esignet.domain.url=https://${mosip.esignet.host} ## Idenity verification configurations mosip.signup.config-server-url=classpath: -mosip.signup.identity-verification.txn.timeout=180 mosip.signup.oauth.client-id=mosip-signup-oauth-client mosip.signup.oauth.redirect-uri=${mosip.signup.domain.url}/identity-verification mosip.signup.oauth.issuer-uri=${mosip.esignet.domain.url} @@ -55,12 +54,30 @@ mosip.signup.oauth.audience=${mosip.esignet.domain.url}/v1/esignet/oauth/v2/toke mosip.signup.oauth.token-uri=http://esignet.esignet/v1/esignet/oauth/v2/token mosip.signup.oauth.userinfo-uri=http://esignet.esignet/v1/esignet/oidc/userinfo +## Max allowed number of slots mosip.signup.slot.max-count=50 + +## Number of timees UI can poll for free slot, and the delay between the polling requests mosip.signup.slot.request.delay=20 mosip.signup.slot.request.limit=10 -mosip.signup.slot.expire-in-seconds=3600 + +## Number of seconds a slot is allowed to be in connected status. If processDuration is defined by the identity verifier, +# defined duration is considered. +mosip.signup.slot.expire-in-seconds=1200 + +## cron trigger to run the expired slots cleanup script from the "slots_connected" cache. As the slot expire is set to 20 +# minutes configuring the cleanup script to run every 20 minutes once mosip.signup.slot.cleanup-cron=0/20 * * * * * +## Time(in seconds) allowed to get slot after the authorization code exchange. +mosip.signup.identity-verification.txn.timeout=180 + +## Time(in seconds) allowed start with identity(eKYC) verification process after slot is allotted. +mosip.signup.slot-allotted.timeout=300 + +## Defines the lifetime of a verified slot cache, An allotted slot is considered verified only after successful WebSocket handshake. +mosip.signup.verified-slot.timeout=${mosip.signup.slot.expire-in-seconds} + ## ------------------------------------- challenge configuration ------------------------------------------------------- mosip.signup.supported.generate-challenge-type=OTP @@ -81,6 +98,7 @@ management.health.redis.enabled=false mosip.esignet.cache.names=challenge_generated,challenge_verified,status_check,blocked_identifier,keystore,key_alias,request_ids,identity_verification,identity_verifiers,idv_metadata,slot_allotted,verified_slot,slots_connected +## cache size is applicable only with 'simple' cache type mosip.esignet.cache.size={'challenge_generated': 200, \ 'challenge_verified': 200,\ 'status_check': 200,\ @@ -92,7 +110,6 @@ mosip.esignet.cache.size={'challenge_generated': 200, \ 'identity_verifiers' : 20, \ 'idv_metadata' : 30,\ 'slot_allotted' : 200, \ - 'slots_connected': 200,\ 'verified_slot' : 200 } ## Note: keystore TTL should be more than the key_alias cache TTL. @@ -107,9 +124,8 @@ mosip.esignet.cache.expire-in-seconds={'challenge_generated': ${mosip.signup.una 'identity_verification' : ${mosip.signup.identity-verification.txn.timeout},\ 'identity_verifiers' : 800, \ 'idv_metadata' : 500,\ - 'slot_allotted' : 1000, \ - 'slots_connected': 1000,\ - 'verified_slot' : 1000 } + 'slot_allotted' : ${mosip.signup.slot-allotted.timeout}, \ + 'verified_slot' : ${mosip.signup.verified-slot.timeout} } ## ------------------------------------- Auth adapter ------------------------------------------------------------------ auth.server.validate.url=http://authmanager.kernel/v1/authmanager/authorize/admin/validateToken diff --git a/signup-service/src/main/resources/application-local.properties b/signup-service/src/main/resources/application-local.properties index 22c70cfc..a878707b 100644 --- a/signup-service/src/main/resources/application-local.properties +++ b/signup-service/src/main/resources/application-local.properties @@ -1,216 +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/. +#----------------------------------------------------------------------------------------------------------------------- +## Environment specific domain names mosip.signup.domain.url=http://localhost:8089 mosip.esignet.domain.url=http://localhost:8088 mosip.esignet.mock.domain.url=http://localhost:8082 mosip.internal.domain.url=https://api-internal.dev.mosip.net keycloak.external.url=https://iam.dev.mosip.net -mosip.signup.client.secret=secret-to-be-copied +mosip.signup.client.secret=secret -#---------------------------------------------------------------------------------------------------------------------------- -## challenge.timeout, resend-delay are count as seconds -mosip.signup.identifier.regex=^\\+855[1-9]\\d{7,8}$ -mosip.signup.identifier.prefix=+855 -mosip.signup.supported-languages={'khm','eng'} -mosip.signup.default-language=khm -mosip.signup.password.pattern=^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[\\x5F\\W])(?=.{8,20})[a-zA-Z0-9\\x5F\\W]{8,20}$ -mosip.signup.password.max-length=20 -mosip.signup.generate-challenge.blocked.timeout=300 -mosip.signup.challenge.timeout=60 -mosip.signup.audit.description.max-length=2048 -mosip.signup.password.min-length=8 -mosip.signup.fullname.pattern=^[\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]{1,30}$ - -## Time given to generate and verify the challenge in seconds. -## Default resend delay is 60 seconds, with 3 attempts, so 60*3=180 seconds. -## Adding 60 seconds for the default generate challenge 180+60=240 -## Adding 10 seconds buffer to default 240 seconds = 250 seconds. -## so 250 seconds is the Generate and verify cookie max age. -mosip.signup.unauthenticated.txn.timeout=250 -mosip.signup.challenge.resend-attempt=3 -mosip.signup.challenge.verification-attempt=3 -mosip.signup.challenge.resend-delay=${mosip.signup.challenge.timeout} - -## Time given to complete registration and get back the status of the registration in seconds. -## Considering 5 minutes(300 seconds) to complete registration form and submit. -## Default status request limit is 10 with 20 seconds request delay, 10*20=200 seconds -## so 300+200=500 seconds is the authentication cookie max age. -mosip.signup.verified.txn.timeout=300 -mosip.signup.status-check.txn.timeout=200 -mosip.signup.status.request.delay=20 -mosip.signup.status.request.limit=10 -mosip.signup.status.request.retry-error-codes=unknown_error - -## Thread pool size -mosip.signup.task.core.pool.size=2 -mosip.signup.task.max.pool.size=4 - -## Idenity verification configurations -mosip.signup.config-server-url=classpath: -mosip.signup.identity-verification.txn.timeout=180 -mosip.signup.oauth.client-id=mosip-signup-oauth-client -mosip.signup.oauth.redirect-uri=${mosip.signup.domain.url}/identity-verification -mosip.signup.oauth.issuer-uri=${mosip.esignet.domain.url} -mosip.signup.oauth.keystore-path=oidckeystore.p12 -mosip.signup.oauth.keystore-password=signup-oidc-password -mosip.signup.oauth.key-alias=${mosip.signup.oauth.client-id} -mosip.signup.oauth.audience=${mosip.esignet.domain.url}/v1/esignet/oauth/v2/token +mosip.signup.generate-challenge.endpoint=${mosip.internal.domain.url}/v1/otpmanager/otp/generate +mosip.signup.send-notification.endpoint=${mosip.internal.domain.url}/v1/notifier/sms/send +mosip.signup.audit-endpoint=${mosip.internal.domain.url}/v1/auditmanager/audits mosip.signup.oauth.token-uri=${mosip.esignet.domain.url}/v1/esignet/oauth/v2/token mosip.signup.oauth.userinfo-uri=${mosip.esignet.domain.url}/v1/esignet/oidc/userinfo -mosip.signup.slot.max-count=50 -mosip.signup.slot.request.delay=20 -mosip.signup.slot.request.limit=10 -mosip.signup.slot.expire-in-seconds=3600 -mosip.signup.slot.cleanup-cron=0 0 * * * * - -## ------------------------------------- challenge configuration ------------------------------------------------------- - -mosip.signup.supported.generate-challenge-type=OTP -mosip.signup.supported.challenge-format-types={'alpha-numeric', 'base64url-encoded-json'} -mosip.signup.supported.challenge-types={'OTP', 'KBI'} -mosip.signup.supported.challenge.otp.length=6 - -## ------------------------------------- Cache configuration ----------------------------------------------------------- -mosip.signup.cache.symmetric-algorithm-name=AES/CFB/PKCS5Padding - -# Use 'simple' for in-memory cache in non-production env -spring.cache.type=redis -spring.cache.cache-names=${mosip.esignet.cache.names} +## Redis connection configuration spring.redis.host=localhost spring.redis.port=6379 -management.health.redis.enabled=false - -mosip.esignet.cache.names=challenge_generated,challenge_verified,status_check,blocked_identifier,keystore,key_alias,request_ids,identity_verification,identity_verifiers,idv_metadata,slot_allotted,verified_slot,slots_connected - -mosip.esignet.cache.size={'challenge_generated': 200, \ - 'challenge_verified': 200,\ - 'status_check': 200,\ - 'blocked_identifier':2000,\ - 'keystore' : 10, \ - 'key_alias' : 2,\ - 'request_ids' : 300,\ - 'identity_verification': 200,\ - 'identity_verifiers' : 20, \ - 'idv_metadata' : 30,\ - 'slot_allotted' : 200, \ - 'slots_connected': 200,\ - 'verified_slot' : 200 } +spring.redis.password= -## Note: keystore TTL should be more than the key_alias cache TTL. -## So that key rotation happens before the actual key is removed from the keystore cache. -mosip.esignet.cache.expire-in-seconds={'challenge_generated': ${mosip.signup.unauthenticated.txn.timeout},\ - 'challenge_verified': ${mosip.signup.verified.txn.timeout},\ - 'status_check': ${mosip.signup.status-check.txn.timeout}, \ - 'blocked_identifier': ${mosip.signup.generate-challenge.blocked.timeout},\ - 'keystore' : 600, \ - 'key_alias' : 300,\ - 'request_ids' : ${mosip.signup.status-check.txn.timeout},\ - 'identity_verification' : ${mosip.signup.identity-verification.txn.timeout},\ - 'identity_verifiers' : 800, \ - 'idv_metadata' : 500,\ - 'slot_allotted' : 1000, \ - 'slots_connected': 1000,\ - 'verified_slot' : 1000 } +## Kafka configuration +kafka.bootstrap-servers=kafka:9092 -## ------------------------------------- Auth adapter ------------------------------------------------------------------ -auth.server.validate.url=http://authmanager.kernel/v1/authmanager/authorize/admin/validateToken -auth.server.admin.issuer.uri=${keycloak.external.url}/auth/realms/ -auth-token-generator.rest.issuerUrl=${keycloak.external.url}/auth/realms/mosip -mosip.keycloak.issuerUrl=${keycloak.external.url}/auth/realms/mosip - -mosip.iam.adapter.clientid=mosip-signup-client -mosip.iam.adapter.clientsecret=${mosip.signup.client.secret} -mosip.iam.adapter.appid=signup - -mosip.iam.adapter.issuerURL=${keycloak.external.url}/auth/realms/mosip -mosip.authmanager.client-token-endpoint=${mosip.internal.domain.url}/v1/authmanager/authenticate/clientidsecretkey - -mosip.kernel.auth.adapter.ssl-bypass=true -mosip.kernel.auth.appid-realm-map={admin:'mosip',crereq:'mosip',creser:'mosip',idrepo:'mosip', signup:'mosip'} -mosip.kernel.auth.appids.realm.map={admin:'mosip',crereq:'mosip',creser:'mosip',idrepo:'mosip','regproc':'mosip', signup:'mosip'} -mosip.iam.adapter.validate-expiry-check-rate=15 -mosip.iam.adapter.renewal-before-expiry-interval=15 -mosip.iam.adapter.self-token-renewal-enable=true -mosip.service-context=${server.servlet.context-path} -mosip.service.end-points=/**/* -mosip.service.exclude.auth.allowed.method=GET,POST -mosip.security.csrf-enable=true -mosip.security.cors-enable=true - -## -------------------------- External endpoints ----------------------------------------------------------------------- - -mosip.signup.generate-challenge.endpoint=${mosip.internal.domain.url}/v1/otpmanager/otp/generate -mosip.signup.send-notification.endpoint=${mosip.internal.domain.url}/v1/notifier/sms/send -mosip.signup.audit-endpoint=${mosip.internal.domain.url}/v1/auditmanager/audits - -## --------------------------------- captcha validator------------------------------------------------------------------ +## Captcha configuration mosip.signup.send-challenge.captcha-required=false -mosip.esignet.captcha.module-name=signup -mosip.esignet.captcha.validator-url=http://captcha.captcha/v1/captcha/validatecaptcha mosip.signup.captcha.site-key=signup.captcha.site.key -## ----------------------------- UI-Config ----------------------------------------------------------------------------- - -mosip.signup.minimum-browser-version={ \ - 'chrome': '118.0.6423.142', \ - 'firefox': '126.1.1', \ - 'edge': '118.0.2535.93', \ - 'safari': '16.1' } - -# Only after current challenge timeout we should enable resend in the UI. -# In this case timeout and resend-delay should be same always. -mosip.signup.ui.config.key-values={\ -'identifier.pattern': '${mosip.signup.identifier.regex}', \ -'identifier.prefix': '${mosip.signup.identifier.prefix}', \ -'captcha.site.key': '${mosip.signup.captcha.site-key}', \ -'otp.length': ${mosip.signup.supported.challenge.otp.length}, \ -'password.pattern': '${mosip.signup.password.pattern}', \ -'password.length.max': ${mosip.signup.password.max-length}, \ -'password.length.min': ${mosip.signup.password.min-length}, \ -'challenge.timeout': ${mosip.signup.challenge.resend-delay}, \ -'resend.attempts': ${mosip.signup.challenge.resend-attempt}, \ -'resend.delay': ${mosip.signup.challenge.resend-delay}, \ -'fullname.pattern': '${mosip.signup.fullname.pattern}', \ -'status.request.delay': ${mosip.signup.status.request.delay}, \ -'status.request.limit': ${mosip.signup.status.request.limit}, \ -'status.request.retry.error.codes': '${mosip.signup.status.request.retry-error-codes}', \ -'slot.request.delay': ${mosip.signup.slot.request.delay}, \ -'slot.request.limit': ${mosip.signup.slot.request.limit}, \ -'popup.timeout': 10, \ -'signin.redirect-url': '${mosip.esignet.domain.url}/authorize', \ -'identifier.allowed.characters': '^[0-9]+', \ -'identifier.length.min': 8, \ -'identifier.length.max': 9, \ -'fullname.allowed.characters': '[^\\u1780-\\u17FF\\u19E0-\\u19FF\\u1A00-\\u1A9F\\u0020]', \ -'fullname.length.min': 1, \ -'fullname.length.max': 30, \ -'otp.blocked' : ${mosip.signup.generate-challenge.blocked.timeout}, \ -'send-challenge.captcha.required': ${mosip.signup.send-challenge.captcha-required}, \ -'signup.oauth-client-id': '${mosip.signup.oauth.client-id}', \ -'identity-verification.redirect-url': '${mosip.signup.oauth.redirect-uri}', \ -'broswer.minimum-version': ${mosip.signup.minimum-browser-version}, \ -'esignet-consent.redirect-url': '${mosip.esignet.domain.url}/consent' } - -## ----------------------------- Notification templates ----------------------------------------------------------------------------- +## Run create-signup-oidc-keystore.sh to generate oidckeystore.p12 file +mosip.signup.oauth.keystore-path=oidckeystore.p12 +mosip.signup.oauth.keystore-password=signup-oidc-password -# Default charset encoding ISO-8859-1 does not support khmer language characters, so templates in khm language are base64 encoded. -mosip.signup.sms-notification-template.encoded-langcodes={'khm'} -mosip.signup.sms-notification-template.send-otp.khm=4Z6U4Z+S4Z6a4Z6+IHtjaGFsbGVuZ2V9IOGeiuGevuGemOGfkuGelOGeuOGeleGfkuGekeGfgOGehOGeleGfkuGekeGetuGej+Gfi+GeguGejuGek+GeuCBLaElEIOGemuGelOGen+Gfi+GeouGfkuGek+GegOGflA== -mosip.signup.sms-notification-template.send-otp.eng=Use {challenge} to verify your KhID account. -mosip.signup.sms-notification-template.registration.khm=4Z6i4Z+S4Z6T4Z6A4Z6U4Z624Z6T4Z6F4Z674Z+H4Z6I4Z+S4Z6Y4Z+E4Z+H4Z6C4Z6O4Z6T4Z64IEtoSUQg4Z6K4Z+E4Z6Z4Z6H4Z+E4Z6C4Z6H4Z+Q4Z6Z4Z+U -mosip.signup.sms-notification-template.registration.eng=You successfully registered to KhID account. -mosip.signup.sms-notification-template.forgot-password.khm=4Z6i4Z+S4Z6T4Z6A4Z6U4Z624Z6T4Z6V4Z+S4Z6b4Z624Z6f4Z+L4Z6U4Z+S4Z6K4Z684Z6a4Z6W4Z624Z6A4Z+S4Z6Z4Z6f4Z6Y4Z+S4Z6E4Z624Z6P4Z+LIEtoSUQg4Z6K4Z+E4Z6Z4Z6H4Z+E4Z6C4Z6H4Z+Q4Z6Z4Z+U -mosip.signup.sms-notification-template.forgot-password.eng=You successfully changed KhID password. +## Validators w.r.t MOCK plugin +mosip.signup.identifier.regex=^\\+855[1-9]\\d{7,8}$ +mosip.signup.identifier.prefix=+855 +mosip.signup.supported-languages={'eng','khm'} -## Kafka configurations -kafka.bootstrap-servers=localhost:9092 -kafka.consumer.group-id=signup-idv-kafka -kafka.consumer.enable-auto-commit=true +## Required fields for registration based on the current signup UI +mosip.signup.mock.mandatory-attributes.CREATE=fullName,phone,password,preferredLang +mosip.signup.mock.mandatory-attributes.UPDATE= +mosip.signup.mock.lang-based-attributes=fullName +mosip.signup.mock.username.field=phone -#------------------------------------------ Others --------------------------------------------------------------------- -logging.level.io.mosip.signup=INFO -#logging.level.org.springframework.web.client.RestTemplate=INFO diff --git a/signup-service/src/main/resources/bootstrap.properties b/signup-service/src/main/resources/bootstrap.properties index 9e9d3a59..49fd1f0e 100644 --- a/signup-service/src/main/resources/bootstrap.properties +++ b/signup-service/src/main/resources/bootstrap.properties @@ -3,7 +3,7 @@ # file, You can obtain one at https://mozilla.org/MPL/2.0/. ## Application Name spring.application.name=signup -spring.profiles.active=local +spring.profiles.active=default,local server.port=8089 server.servlet.path=/ diff --git a/signup-service/src/main/resources/signup-idv_mock-identity-verifier.json b/signup-service/src/main/resources/signup-idv_mock-identity-verifier.json index ab8b9c6f..dd507943 100644 --- a/signup-service/src/main/resources/signup-idv_mock-identity-verifier.json +++ b/signup-service/src/main/resources/signup-idv_mock-identity-verifier.json @@ -3,29 +3,77 @@ "eng": "I understand that the data collected about me during registration by the said authority includes different parameters.

Lorem Ipsum is simply dummy text of the printing and type setting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry’s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries.

It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages.", "khm": "ខ្ញុំយល់ថាទិន្នន័យដែលប្រមូលបានអំពីខ្ញុំក្នុងអំឡុងពេលចុះឈ្មោះដោយអាជ្ញាធរបាននិយាយថាមានប៉ារ៉ាម៉ែត្រផ្សេងៗគ្នា។

Lorem Ipsum គឺជាអត្ថបទមិនពិតនៃឧស្សាហកម្មការកំណត់ប្រភេទបោះពុម្ព។ Lorem Ipsum គឺជាអត្ថបទអត់ចេះសោះស្តង់ដាររបស់ឧស្សាហកម្មនេះ ចាប់តាំងពីទសវត្សរ៍ឆ្នាំ 1500 នៅពេលដែលម៉ាស៊ីនបោះពុម្ពមិនស្គាល់មួយបានយកប្រអប់ប្រភេទមួយ ហើយលាយវាដើម្បីបង្កើតសៀវភៅគំរូប្រភេទមួយ។ វាបានរស់រានមានជីវិតមិនត្រឹមតែប្រាំសតវត្សប៉ុណ្ណោះទេ ប៉ុន្តែវាក៏ជាការលោតផ្លោះចូលទៅក្នុងការវាយអក្សរអេឡិចត្រូនិចផងដែរ ដែលនៅតែមិនផ្លាស់ប្តូរ។
Lorem Ipsum គឺជាអត្ថបទដ៏សាមញ្ញនៃឧស្សាហកម្មបោះពុម្ព និងវាយអក្សរ។ Lorem Ipsum គឺជាអត្ថបទអត់ចេះសោះស្តង់ដាររបស់ឧស្សាហកម្មនេះ ចាប់តាំងពីទសវត្សរ៍ឆ្នាំ 1500 នៅពេលដែលម៉ាស៊ីនបោះពុម្ពមិនស្គាល់មួយបានយកប្រអប់ប្រភេទមួយ ហើយលាយវាដើម្បីបង្កើតសៀវភៅគំរូប្រភេទមួយ។ វាបានរស់រានមានជីវិតមិនត្រឹមតែប្រាំសតវត្សប៉ុណ្ណោះទេ។

វាត្រូវបានពេញនិយមនៅក្នុងទសវត្សរ៍ឆ្នាំ 1960 ជាមួយនឹងការចេញផ្សាយសន្លឹក Letraset ដែលមានអត្ថបទ Lorem Ipsum ។" }, - "previewInfo" : { - "step_1": { "eng" : "Verify the functionality of your camera using the video preview on the right" }, - "step_2": { "eng" : "Ensure you are positioned in a well-lit area to facilitate clear video capture"}, - "step_3": { "eng" : "Position your face within the oval frame, ensuring your face is clearly visible"}, - "step_4": { "eng" : "Remove any accessories or items that could obstruct your face, such as hats or sunglasses."}, - "step_5": { "eng" : "Maintain a stable posture throughout the video recording to prevent blurring"}, - "step_6": { "eng" : "Be prepared to follow instructions provided on screen during the eKYC process, such as blinking or turning your head as directed."}, - "step_7": { "eng" : "Have your ID readily accessible for the verification purposes."} + "previewInfo": { + "step_1": { + "eng": "Verify the functionality of your camera using the video preview on the right", + "khm": "ផ្ទៀងផ្ទាត់មុខងាររបស់កាមេរ៉ារបស់អ្នកដោយប្រើការមើលវីដេអូជាមុននៅខាងស្តាំ" + }, + "step_2": { + "eng": "Ensure you are positioned in a well-lit area to facilitate clear video capture", + "khm": "ត្រូវប្រាកដថាអ្នកត្រូវបានដាក់នៅកន្លែងដែលមានពន្លឺល្អ ដើម្បីជួយសម្រួលដល់ការថតវីដេអូច្បាស់" + }, + "step_3": { + "eng": "Position your face within the oval frame, ensuring your face is clearly visible", + "khm": "ដាក់មុខរបស់អ្នកក្នុងរង្វង់រាងពងក្រពើ ធានាថាមុខរបស់អ្នកអាចមើលឃើញយ៉ាងច្បាស់" + }, + "step_4": { + "eng": "Remove any accessories or items that could obstruct your face, such as hats or sunglasses.", + "khm": "ដកគ្រឿងបន្ថែម ឬរបស់របរដែលអាចរារាំងមុខរបស់អ្នក ដូចជាមួក ឬវ៉ែនតាជាដើម។" + }, + "step_5": { + "eng": "Maintain a stable posture throughout the video recording to prevent blurring", + "khm": "រក្សា​ជំហរ​ឲ្យ​មាន​ស្ថិរភាព​ពេញ​មួយ​ការ​ថត​វីដេអូ ដើម្បី​ការពារ​ការ​ព្រិល" + }, + "step_6": { + "eng": "Be prepared to follow instructions provided on screen during the eKYC process, such as blinking or turning your head as directed.", + "khm": "ត្រូវបានរៀបចំដើម្បីធ្វើតាមការណែនាំដែលមាននៅលើអេក្រង់ក្នុងអំឡុងពេលដំណើរការ eKYC ដូចជាការភ្លឹបភ្លែតៗ ឬបង្វែរក្បាលរបស់អ្នកតាមការណែនាំ។" + }, + "step_7": { + "eng": "Have your ID readily accessible for the verification purposes.", + "khm": "មានអត្តសញ្ញាណប័ណ្ណរបស់អ្នកអាចចូលប្រើបានយ៉ាងងាយស្រួលសម្រាប់គោលបំណងផ្ទៀងផ្ទាត់។" + } }, - "stepCodes" : { - "liveness_check" : { "eng": "Liveness check" }, - "id_verification" : { "eng": "ID card verification" } + "stepCodes": { + "liveness_check": { + "eng": "Liveness check", + "khm": "ពិនិត្យភាពរស់រវើក" + }, + "id_verification": { + "eng": "ID card verification", + "khm": "ការផ្ទៀងផ្ទាត់អត្តសញ្ញាណប័ណ្ណ" + } }, "errors": { - "low_light": { "eng" : "Low light, consider facing the sun or switching on the lights" }, - "id_card_too_far": { "eng" : "Unable to read card as its too far" } + "low_light": { + "eng": "Low light, consider facing the sun or switching on the lights", + "khm": "ពន្លឺទាប ពិចារណាបែរមុខទៅព្រះអាទិត្យ ឬបើកភ្លើង" + }, + "id_card_too_far": { + "eng": "Unable to read card as its too far", + "khm": "មិនអាចអានកាតបានទេ ព្រោះនៅឆ្ងាយពេក" + } }, "messages": { - "turn_left": { "eng" : "Turn your head to left" }, - "turn_right": { "eng" : "Turn your head to right" }, - "sit_straight": {"eng" : "Keep good posture while facing the camera" }, - "success_check": { "eng" : "Liveness check successful" }, - "id_verified": { "eng" : "ID card verification successful" }, + "turn_left": { + "eng": "Turn your head to Left", + "khm": "បង្វែរក្បាលរបស់អ្នកទៅខាងឆ្វេង" + }, + "turn_right": { + "eng": "Turn your head to Right", + "khm": "បង្វែរក្បាលរបស់អ្នកទៅស្តាំ" + }, + "sit_straight": { + "eng": "Keep good posture while facing the camera", + "khm": "រក្សា​ឥរិយាបថ​ឱ្យ​បានល្អ​ពេល​កំពុង​ប្រឈមមុខ​នឹង​កាមេរ៉ា" + }, + "success_check": { + "eng": "Liveness check successful", + "khm": "ការត្រួតពិនិត្យភាពរស់រវើកបានជោគជ័យ" + }, + "id_verified": { + "eng": "ID card verification successful", + "khm": "ការផ្ទៀងផ្ទាត់អត្តសញ្ញាណប័ណ្ណបានជោគជ័យ" + }, "facingcamera": { "eng": "Keep good posture while facing the camera,do follow all the instructions as informed", "khm": "រក្សា​ឥរិយាបថ​ឱ្យ​បានល្អ​ពេល​កំពុង​ប្រឈមមុខ​នឹង​កាមេរ៉ា សូម​ធ្វើ​តាម​ការណែនាំ​ទាំងអស់​ដូច​ដែល​បាន​ជូនដំណឹង" @@ -39,4 +87,4 @@ "khm": "សូមធ្វើតាមការណែនាំ ដើម្បីអនុវត្តដំណើរការ eKYC ដោយជោគជ័យ រក្សាអ៊ីនធឺណិតរបស់អ្នកបានភ្ជាប់ពេញដំណើរការ" } } -} \ No newline at end of file +} diff --git a/signup-service/src/main/resources/static/app.js b/signup-service/src/main/resources/static/app.js deleted file mode 100644 index ea934b02..00000000 --- a/signup-service/src/main/resources/static/app.js +++ /dev/null @@ -1,74 +0,0 @@ -// Get the current URL -const currentUrl = window.location.href; - -// Create a URL object -const url = new URL(currentUrl); - -// Get the search parameters (query params) -const searchParams = new URLSearchParams(url.search); - -// Access specific query parameters -const slotId = searchParams.get('slotId'); -const uri = 'ws://localhost:8089' - -const stompClient = new StompJs.Client({ - brokerURL: uri+'/v1/signup/ws?slotId='+slotId -}); - -stompClient.onConnect = (frame) => { - setConnected(true); - console.log('Connected: ' + frame); - stompClient.subscribe('/topic/'+slotId, (resp) => { - showGreeting(resp.body); - }); -}; - -stompClient.onWebSocketError = (error) => { - console.error('Error with websocket', error); -}; - -stompClient.onStompError = (frame) => { - console.error('Broker reported error: ' + frame.headers['message']); - console.error('Additional details: ' + frame.body); -}; - -function setConnected(connected) { - $("#connect").prop("disabled", connected); - $("#disconnect").prop("disabled", !connected); - if (connected) { - $("#conversation").show(); - } - else { - $("#conversation").hide(); - } - $("#greetings").html(""); -} - -function connect() { - stompClient.activate(); -} - -function disconnect() { - stompClient.deactivate(); - setConnected(false); - console.log("Disconnected"); -} - -function sendName() { - console.log('Sending message to server ', $("#step-code").val(), $("#frame-order").val()); - stompClient.publish({ - destination: "/v1/signup/ws/process-frame", - body: JSON.stringify({'slotId': slotId, 'stepCode': $("#step-code").val(), "frames": [{"frame": "", "order": $("#frame-order").val()}]}) - }); -} - -function showGreeting(message) { - $("#greetings").append("" + message + ""); -} - -$(function () { - $("form").on('submit', (e) => e.preventDefault()); - $( "#connect" ).click(() => connect()); - $( "#disconnect" ).click(() => disconnect()); - $( "#send" ).click(() => sendName()); -}); \ No newline at end of file diff --git a/signup-service/src/main/resources/static/index.html b/signup-service/src/main/resources/static/index.html deleted file mode 100644 index b8d0b5ea..00000000 --- a/signup-service/src/main/resources/static/index.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - Hello WebSocket - - - - - - - - -
-
-
-
-
- - - -
-
-
-
-
-
- - -
-
- - -
- -
-
-
-
-
- - - - - - - - -
Greetings
-
-
-
- - \ No newline at end of file diff --git a/signup-service/src/main/resources/static/main.css b/signup-service/src/main/resources/static/main.css deleted file mode 100644 index 8643b769..00000000 --- a/signup-service/src/main/resources/static/main.css +++ /dev/null @@ -1,14 +0,0 @@ -body { - background-color: #f5f5f5; -} - -#main-content { - max-width: 940px; - padding: 2em 3em; - margin: 0 auto 20px; - background-color: #fff; - border: 1px solid #e5e5e5; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - border-radius: 5px; -} \ No newline at end of file diff --git a/signup-service/src/test/java/io/mosip/signup/controllers/WebSocketControllerTest.java b/signup-service/src/test/java/io/mosip/signup/controllers/WebSocketControllerTest.java index 4ceae3c3..68def318 100644 --- a/signup-service/src/test/java/io/mosip/signup/controllers/WebSocketControllerTest.java +++ b/signup-service/src/test/java/io/mosip/signup/controllers/WebSocketControllerTest.java @@ -2,11 +2,12 @@ import io.mosip.signup.api.dto.IdentityVerificationResult; import io.mosip.signup.api.exception.IdentityVerifierException; -import io.mosip.signup.api.util.VerificationStatus; import io.mosip.signup.dto.IdentityVerificationRequest; -import io.mosip.signup.dto.IdentityVerificationTransaction; +import io.mosip.signup.helper.AuditHelper; import io.mosip.signup.services.CacheUtilService; import io.mosip.signup.services.WebSocketHandler; +import io.mosip.signup.util.AuditEvent; +import io.mosip.signup.util.AuditEventType; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -20,7 +21,6 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.client.RestTemplate; -import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.messaging.SessionConnectedEvent; import org.springframework.web.socket.messaging.SessionDisconnectEvent; @@ -46,10 +46,14 @@ public class WebSocketControllerTest { @MockBean private RestTemplate restTemplate; + @MockBean + private AuditHelper auditHelper; + @Before public void setup() { ReflectionTestUtils.setField(webSocketController, "webSocketHandler", webSocketHandler); ReflectionTestUtils.setField(webSocketController, "cacheUtilService", cacheUtilService); + ReflectionTestUtils.setField(webSocketController,"auditHelper",auditHelper); } @Test @@ -81,12 +85,17 @@ public void processFrames_withValidInput_thenPass() { identityVerificationRequest.setStepCode("START"); identityVerificationRequest.setSlotId("slot-id"); webSocketController.processFrames(identityVerificationRequest); + Mockito.verify(auditHelper, Mockito.times(1)) + .sendAuditTransaction(AuditEvent.PROCESS_FRAMES, AuditEventType.SUCCESS, "slot-id", null); } @Test public void consumeStepResult_test() { IdentityVerificationResult identityVerificationResult = new IdentityVerificationResult(); + identityVerificationResult.setId("id"); webSocketController.consumeStepResult(identityVerificationResult); + Mockito.verify(auditHelper, Mockito.times(1)) + .sendAuditTransaction(AuditEvent.CONSUME_STEP_RESULT, AuditEventType.SUCCESS, "id", null); } @Test @@ -100,6 +109,8 @@ public String getName() { }); webSocketController.onConnected(sessionConnectedEvent); Mockito.verify(webSocketHandler, Mockito.times(1)).updateProcessDuration(""); + Mockito.verify(auditHelper, Mockito.times(1)) + .sendAuditTransaction(AuditEvent.ON_CONNECTED, AuditEventType.SUCCESS,"" , null); } @Test @@ -111,32 +122,10 @@ public String getName() { return "TID"+VALUE_SEPARATOR+"SID"; } }); - IdentityVerificationTransaction transaction = new IdentityVerificationTransaction(); - transaction.setStatus(VerificationStatus.FAILED); - Mockito.when(cacheUtilService.getVerifiedSlotTransaction(Mockito.anyString())).thenReturn(transaction); webSocketController.onDisconnected(sessionDisconnectEvent); Mockito.verify(cacheUtilService, Mockito.times(1)).removeFromSlotConnected(Mockito.anyString()); Mockito.verify(cacheUtilService, Mockito.times(1)).evictSlotAllottedTransaction(Mockito.anyString(),Mockito.anyString()); - } - - @Test - public void onDisconnected_WithAbnormalClosedState_test() { - SessionDisconnectEvent sessionDisconnectEvent = Mockito.mock(SessionDisconnectEvent.class); - Mockito.when(sessionDisconnectEvent.getUser()).thenReturn(new java.security.Principal() { - @Override - public String getName() { - return "TID"+VALUE_SEPARATOR+"SID"; - } - }); - Mockito.when(sessionDisconnectEvent.getCloseStatus()).thenReturn(CloseStatus.SERVER_ERROR); - - IdentityVerificationTransaction transaction = new IdentityVerificationTransaction(); - transaction.setStatus(VerificationStatus.FAILED); - Mockito.when(cacheUtilService.getVerifiedSlotTransaction(Mockito.anyString())).thenReturn(transaction); - webSocketController.onDisconnected(sessionDisconnectEvent); - - Mockito.verify(cacheUtilService, Mockito.times(1)).updateVerifiedSlotTransaction(Mockito.anyString(), Mockito.any()); - Mockito.verify(cacheUtilService, Mockito.times(1)).removeFromSlotConnected(Mockito.anyString()); - Mockito.verify(cacheUtilService, Mockito.times(1)).evictSlotAllottedTransaction(Mockito.anyString(),Mockito.anyString()); + Mockito.verify(auditHelper, Mockito.times(1)) + .sendAuditTransaction(AuditEvent.ON_DISCONNECTED, AuditEventType.SUCCESS, "TID", null); } } diff --git a/signup-service/src/test/java/io/mosip/signup/helper/CryptoHelperTest.java b/signup-service/src/test/java/io/mosip/signup/helper/CryptoHelperTest.java new file mode 100644 index 00000000..2404ed6e --- /dev/null +++ b/signup-service/src/test/java/io/mosip/signup/helper/CryptoHelperTest.java @@ -0,0 +1,77 @@ +package io.mosip.signup.helper; + +import io.mosip.esignet.core.util.IdentityProviderUtil; +import io.mosip.signup.services.CacheUtilService; +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 org.springframework.test.util.ReflectionTestUtils; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import java.nio.charset.StandardCharsets; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class CryptoHelperTest { + + @InjectMocks + private CryptoHelper cryptoHelper; + + @Mock + private CacheUtilService cacheUtilService; + + private static String symmetricAlgorithm = "AES/CFB/PKCS5Padding"; + private static String symmetricKeyAlgorithm = "AES"; + private static int symmetricKeySize = 256; + + String keyAlias = "aced6829-63bb-5b28-8898-64efd90a70fa"; + private static String secretKey; + + static { + KeyGenerator keyGenerator = null; + try { + keyGenerator = KeyGenerator.getInstance(symmetricKeyAlgorithm); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + keyGenerator.init(symmetricKeySize); + secretKey = IdentityProviderUtil.b64Encode(keyGenerator.generateKey().getEncoded()); + } + + @Before + public void setUp() { + when(cacheUtilService.getSecretKey(Mockito.anyString())).thenReturn(secretKey).thenReturn(secretKey); + + ReflectionTestUtils.setField(cryptoHelper, "symmetricAlgorithm", symmetricAlgorithm); + ReflectionTestUtils.setField(cryptoHelper, "symmetricKeyAlgorithm", symmetricKeyAlgorithm); + ReflectionTestUtils.setField(cryptoHelper, "symmetricKeySize", symmetricKeySize); + } + + @Test + public void symmetricEncrypt_withValidInput_thenPass() { + String data = "test data test fatata"; + String encryptedData = cryptoHelper.symmetricEncrypt(data); + + assertNotNull(encryptedData); + verify(cacheUtilService, times(1)).getActiveKeyAlias(); + verify(cacheUtilService, times(1)).getSecretKey(keyAlias); + + String decryptedData = cryptoHelper.symmetricDecrypt(encryptedData); + assertNotNull(decryptedData); + assertEquals(data, decryptedData); + verify(cacheUtilService, times(1)).getActiveKeyAlias(); + verify(cacheUtilService, times(2)).getSecretKey(keyAlias); + } +} + diff --git a/signup-service/src/test/java/io/mosip/signup/helper/NotificationHelperTest.java b/signup-service/src/test/java/io/mosip/signup/helper/NotificationHelperTest.java new file mode 100644 index 00000000..9af18c09 --- /dev/null +++ b/signup-service/src/test/java/io/mosip/signup/helper/NotificationHelperTest.java @@ -0,0 +1,147 @@ +package io.mosip.signup.helper; + +import io.mosip.signup.dto.NotificationResponse; +import io.mosip.signup.dto.RestResponseWrapper; +import io.mosip.signup.exception.SignUpException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.*; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +@RunWith(MockitoJUnitRunner.class) +public class NotificationHelperTest { + + @InjectMocks + private NotificationHelper notificationHelper; + + @Mock + private RestTemplate selfTokenRestTemplate; + + @Mock + private Environment environment; + + private String sendNotificationEndpoint = "http://test.endpoint.com/send-notification"; + private String defaultLanguage = "en"; + private List encodedLangCodes = List.of("es"); + + @Before + public void setUp() { + ReflectionTestUtils.setField(notificationHelper, "sendNotificationEndpoint", sendNotificationEndpoint); + ReflectionTestUtils.setField(notificationHelper, "defaultLanguage", defaultLanguage); + ReflectionTestUtils.setField(notificationHelper, "encodedLangCodes", encodedLangCodes); + } + + @Test + public void testSendSMSNotification_withValidInput_thenPass() { + String locale = "eng"; + String templateKey = "mosip.signup.sms-notification-template.send-otp"; + String message = "Hello, {{name}}!"; + + when(environment.getProperty(templateKey + "." + locale)).thenReturn(Base64.getEncoder().encodeToString(message.getBytes())); + Map params = new HashMap<>(); + params.put("{{name}}", "John"); + + RestResponseWrapper responseWrapper = new RestResponseWrapper<>(); + ResponseEntity> responseEntity = mock(ResponseEntity.class); + when(responseEntity.getBody()).thenReturn(responseWrapper); + when(selfTokenRestTemplate.exchange( + eq(sendNotificationEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenReturn(responseEntity); + + notificationHelper.sendSMSNotification("1234567890", locale, templateKey, params); + + verify(selfTokenRestTemplate, times(1)).exchange( + eq(sendNotificationEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class)); + } + + @Test(expected = SignUpException.class) + public void testSendSMSNotification_onRestException_thenFail() { + String locale = "eng"; + String templateKey = "mosip.signup.sms-notification-template.send-otp"; + String message = "Hello, {{name}}!"; + + when(environment.getProperty(templateKey + "." + locale)).thenReturn(message); + + when(selfTokenRestTemplate.exchange( + eq(sendNotificationEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenThrow(new RestClientException("Error in RestTemplate")); + + notificationHelper.sendSMSNotification("1234567890", locale, templateKey, null); + } + + @Test + public void testSendSMSNotification_withNullLocale_thenPass() { //fallback to default language + String locale = null; + String templateKey = "mosip.signup.sms-notification-template.send-otp"; + String message = "Hello, {{name}}!"; + + when(environment.getProperty(templateKey + "." + defaultLanguage)).thenReturn(Base64.getEncoder().encodeToString(message.getBytes())); + + Map params = new HashMap<>(); + params.put("{{name}}", "John"); + + RestResponseWrapper responseWrapper = new RestResponseWrapper<>(); + ResponseEntity> responseEntity = mock(ResponseEntity.class); + when(responseEntity.getBody()).thenReturn(responseWrapper); + when(selfTokenRestTemplate.exchange( + eq(sendNotificationEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenReturn(responseEntity); + + notificationHelper.sendSMSNotification("1234567890", locale, templateKey, params); + + verify(selfTokenRestTemplate, times(1)).exchange( + eq(sendNotificationEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class)); + } + + @Test + public void testSendSMSNotificationAsync() { + // Verify that the async method simply delegates to the sync method + NotificationHelper spyNotificationHelper = spy(notificationHelper); + + RestResponseWrapper responseWrapper = new RestResponseWrapper<>(); + ResponseEntity> responseEntity = mock(ResponseEntity.class); + when(responseEntity.getBody()).thenReturn(responseWrapper); + when(selfTokenRestTemplate.exchange( + eq(sendNotificationEndpoint), + eq(HttpMethod.POST), + any(HttpEntity.class), + any(ParameterizedTypeReference.class))) + .thenReturn(responseEntity); + + spyNotificationHelper.sendSMSNotificationAsync("1234567890", "en", "sms.templateKey", null); + verify(spyNotificationHelper, times(1)).sendSMSNotification("1234567890", "en", "sms.templateKey", null); + } +} + diff --git a/signup-service/src/test/java/io/mosip/signup/services/WebSocketHandshakeHandlerTest.java b/signup-service/src/test/java/io/mosip/signup/services/WebSocketHandshakeHandlerTest.java index 6c04fc17..0478f4e3 100644 --- a/signup-service/src/test/java/io/mosip/signup/services/WebSocketHandshakeHandlerTest.java +++ b/signup-service/src/test/java/io/mosip/signup/services/WebSocketHandshakeHandlerTest.java @@ -7,6 +7,9 @@ import io.mosip.signup.dto.IdentityVerificationTransaction; +import io.mosip.signup.helper.AuditHelper; +import io.mosip.signup.util.AuditEvent; +import io.mosip.signup.util.AuditEventType; import io.mosip.signup.util.ErrorConstants; import org.junit.Assert; import org.junit.Test; @@ -36,6 +39,9 @@ public class WebSocketHandshakeHandlerTest { @Mock private CacheUtilService cacheUtilService; + @Mock + private AuditHelper auditHelper; + private Map attributes; @BeforeEach @@ -61,6 +67,7 @@ public void determineUser_withValidDetails_thenPass() throws Exception { Assert.assertNotNull(principal); Assert.assertEquals("Slot###123", principal.getName()); + Mockito.verify(auditHelper).sendAuditTransaction(AuditEvent.HANDSHAKE_SUCCESS, AuditEventType.SUCCESS, "Slot", null); } @Test @@ -102,6 +109,7 @@ public void determineUser_withInValidSlotId_thenFail() throws Exception { }catch (HandshakeFailureException e){ Assert.assertEquals(ErrorConstants.INVALID_TRANSACTION, e.getMessage()); } + Mockito.verify(auditHelper).sendAuditTransaction(AuditEvent.HANDSHAKE_FAILED, AuditEventType.ERROR, "Slot", null); } } diff --git a/signup-service/src/test/java/io/mosip/signup/services/WebsocketHandlerTest.java b/signup-service/src/test/java/io/mosip/signup/services/WebsocketHandlerTest.java index 0989f63b..d18cf1c3 100644 --- a/signup-service/src/test/java/io/mosip/signup/services/WebsocketHandlerTest.java +++ b/signup-service/src/test/java/io/mosip/signup/services/WebsocketHandlerTest.java @@ -12,7 +12,9 @@ import io.mosip.signup.dto.IdentityVerificationTransaction; import io.mosip.signup.exception.InvalidTransactionException; import io.mosip.signup.exception.SignUpException; -import io.mosip.signup.util.ErrorConstants; +import io.mosip.signup.helper.AuditHelper; +import io.mosip.signup.util.AuditEvent; +import io.mosip.signup.util.AuditEventType; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -51,6 +53,9 @@ public class WebsocketHandlerTest { @Mock private CacheUtilService cacheUtilService; + @Mock + private AuditHelper auditHelper; + private ObjectMapper objectMapper = new ObjectMapper(); @Before @@ -100,26 +105,29 @@ public void processFrames_invalidTransaction_thenFail() { identityVerificationRequest.setSlotId("test"); identityVerificationRequest.setStepCode("stepCode"); Mockito.when(cacheUtilService.getVerifiedSlotTransaction(identityVerificationRequest.getSlotId())).thenReturn(null); - webSocketHandler.processFrames(identityVerificationRequest); - Mockito.verify(cacheUtilService, Mockito.times(1)).getVerifiedSlotTransaction(identityVerificationRequest.getSlotId()); - Mockito.verify(simpMessagingTemplate, Mockito.times(1)) - .convertAndSend(Mockito.eq("/topic/" + identityVerificationRequest.getSlotId()), Mockito.any(IdentityVerificationResult.class)); + try { + webSocketHandler.processFrames(identityVerificationRequest); + Assert.fail(); + } catch (InvalidTransactionException e) { + Assert.assertNotNull(e.getErrorCode()); + } } @Test public void processFrames_invalidVerifierId_thenFail() { IdentityVerificationRequest identityVerificationRequest = new IdentityVerificationRequest(); identityVerificationRequest.setSlotId("test"); - identityVerificationRequest.setStepCode("stepCode"); + IdentityVerificationTransaction identityVerificationTransaction = new IdentityVerificationTransaction(); identityVerificationTransaction.setVerifierId("verifier-id"); Mockito.when(cacheUtilService.getVerifiedSlotTransaction(identityVerificationRequest.getSlotId())).thenReturn(identityVerificationTransaction); Mockito.when(identityVerifierFactory.getIdentityVerifier("verifier-id")).thenReturn(null); - webSocketHandler.processFrames(identityVerificationRequest); - Mockito.verify(cacheUtilService, Mockito.times(1)).getVerifiedSlotTransaction(identityVerificationRequest.getSlotId()); - Mockito.verify(identityVerifierFactory, Mockito.times(1)).getIdentityVerifier("verifier-id"); - Mockito.verify(simpMessagingTemplate, Mockito.times(1)) - .convertAndSend(Mockito.eq("/topic/" + identityVerificationRequest.getSlotId()), Mockito.any(IdentityVerificationResult.class)); + try { + webSocketHandler.processFrames(identityVerificationRequest); + Assert.fail(); + } catch (SignUpException e) { + Assert.assertEquals(PLUGIN_NOT_FOUND, e.getErrorCode()); + } } @Test @@ -263,6 +271,7 @@ public void processVerificationResult_OnExceptionFromGetVerificationResult_thenF IdentityVerificationTransaction transaction = new IdentityVerificationTransaction(); transaction.setVerifierId("verifier-id"); transaction.setApplicationId("application-id"); + transaction.setSlotId("slotId"); Mockito.when(cacheUtilService.getVerifiedSlotTransaction(identityVerificationResult.getId())).thenReturn(transaction); IdentityVerifierPlugin identityVerifierPlugin = Mockito.mock(IdentityVerifierPlugin.class); Mockito.when(identityVerifierFactory.getIdentityVerifier("verifier-id")).thenReturn(identityVerifierPlugin); @@ -270,6 +279,8 @@ public void processVerificationResult_OnExceptionFromGetVerificationResult_thenF webSocketHandler.processVerificationResult(identityVerificationResult); Mockito.verify(profileRegistryPlugin, Mockito.times(0)).updateProfile(Mockito.anyString(), Mockito.any()); + Mockito.verify(auditHelper, Mockito.times(1)) + .sendAuditTransaction(AuditEvent.PROCESS_FRAMES, AuditEventType.ERROR, "slotId", null); Assert.assertEquals(VerificationStatus.FAILED, transaction.getStatus()); Assert.assertEquals("verification_failed", transaction.getErrorCode()); } diff --git a/signup-ui/README.md b/signup-ui/README.md index ae37ac04..99b48c42 100644 --- a/signup-ui/README.md +++ b/signup-ui/README.md @@ -1,8 +1,9 @@ -# E-Signet Sign Up Web Application +# Signup UI -## Description +## Overview -The E-Signet Sign Up web application is a platform allowing users to register their identities. +Signup UI has provision to verify the user's phone number with OTP and on successful verification user is allowed to +register in the integrated ID registry.Both register and reset password requires OTP verification. ## Local Development diff --git a/signup-ui/public/locales/en.json b/signup-ui/public/locales/en.json index 901fe131..ae785efa 100644 --- a/signup-ui/public/locales/en.json +++ b/signup-ui/public/locales/en.json @@ -294,4 +294,4 @@ "IDR-CFJ-001": "Credential Feeder job failed", "grant_exchange_failed": "Unable to Authorize. Please try again." } -} +} \ No newline at end of file diff --git a/signup-ui/public/locales/km.json b/signup-ui/public/locales/km.json index 4c12014c..2b32e86a 100644 --- a/signup-ui/public/locales/km.json +++ b/signup-ui/public/locales/km.json @@ -294,4 +294,4 @@ "IDR-CFJ-001": "ការបញ្ជាក់អត្តសញ្ញាណបានបរាជ័យ", "grant_exchange_failed": "មិនអាចអនុញ្ញាត។ សូមព្យាយាមម្តងទៀត។" } -} +} \ No newline at end of file