From 25d4b6363618c19cbfd2c76a454c6db6bad9b0fa Mon Sep 17 00:00:00 2001 From: Nandhukumar Date: Mon, 18 Nov 2024 18:43:11 +0530 Subject: [PATCH 01/40] MOSIP-37486 Signed-off-by: Nandhukumar --- .../AuthenticateUserNegTC.hbs | 15 - .../AuthenticateUserNegTC.yml | 34 - .../AuthenticateUserNegTCResult.hbs | 1 - .../ClaimDetailsNegTC/ClaimDetailsNegTC.hbs | 4 - .../ClaimDetailsNegTC/ClaimDetailsNegTC.yml | 245 ---- .../ClaimDetailsResultNegTC.hbs | 1 - .../GetIdVerifierNegTC/GetIdVerifierNegTC.hbs | 4 - .../GetIdVerifierNegTC/GetIdVerifierNegTC.yml | 132 --- .../GetIdVerifierResultNegTC.hbs | 1 - .../esignet/GetSlotNegTC/GetSlotNegTC.hbs | 8 - .../esignet/GetSlotNegTC/GetSlotNegTC.yml | 505 --------- .../GetSlotNegTC/GetSlotResultNegTC.hbs | 1 - .../IDTAuthenticationNegTC.hbs | 18 - .../IDTAuthenticationNegTC.yml | 609 ---------- .../IDTAuthenticationNegTCResult.hbs | 1 - .../InitiateIdVerificationNegTC.hbs | 7 - .../InitiateIdVerificationNegTC.yml | 386 ------- .../InitiateIdVerificationResultNegTC.hbs | 1 - .../LinkedAuthenticationWla.yml | 2 +- .../LinkedAuthorizationConsent.yml | 9 +- .../OAuthDetailsRequestNegResultTC.hbs | 1 - .../OAuthDetailsRequestNegTC.hbs | 39 - .../OAuthDetailsRequestNegTC.yml | 1007 ----------------- ...OAuthDetailsWithClaimValueRequestNegTC.hbs | 62 - .../PrepareSignupRedirectNegTC.hbs | 8 - .../PrepareSignupRedirectNegTC.yml | 414 ------- .../PrepareSignupRedirectNegTCResult.hbs | 1 - .../SignupAuthorizeCodeNegTC.hbs | 16 - .../SignupAuthorizeCodeNegTC.yml | 271 ----- .../SignupAuthorizeCodeNegTCResult.hbs | 1 - .../SignupAuthorizeNegTC.hbs | 15 - .../SignupAuthorizeNegTC.yml | 914 --------------- .../SignupAuthorizeNegTCResult.hbs | 1 - .../esignet/WalletBinding/WalletBinding.yml | 6 +- api-test/testNgXmlFiles/esignetSuite.xml | 80 +- 35 files changed, 7 insertions(+), 4813 deletions(-) delete mode 100644 api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTCResult.hbs delete mode 100644 api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsResultNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierResultNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotResultNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTCResult.hbs delete mode 100644 api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationResultNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegResultTC.hbs delete mode 100644 api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsWithClaimValueRequestNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTCResult.hbs delete mode 100644 api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTCResult.hbs delete mode 100644 api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.hbs delete mode 100644 api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.yml delete mode 100644 api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTCResult.hbs diff --git a/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.hbs b/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.hbs deleted file mode 100644 index 43ef2d127..000000000 --- a/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.hbs +++ /dev/null @@ -1,15 +0,0 @@ -{ - "encodedHash": "{{encodedHash}}", - "requestTime": "{{requestTime}}", - "request": { - "transactionId": "{{transactionId}}", - "individualId": "{{individualId}}", - "challengeList" : [ - { - "authFactorType" : "{{authFactorType}}", - "challenge" : "{{challenge}}", - "format": "alpha-numeric" - } - ] - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.yml b/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.yml deleted file mode 100644 index 1f9cc3ada..000000000 --- a/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC.yml +++ /dev/null @@ -1,34 +0,0 @@ -AuthenticateUserV3NegTC: - ESignet_AuthenticateUserNegTC_V3_AuthToken_Xsrf__uin_Otp_Valid_Smoke: - endPoint: /v1/esignet/authorization/v3/authenticate - description: To Authenticate user with all valid data - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTC - outputTemplate: esignet/AuthenticateUserV3/AuthenticateUserResult - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "individualId": "$ID:AddIdentity_L2_Valid_Parameters_smoke_Neg_Pos_UIN$", - "authFactorType" : "OTP", - "challenge" : "$ID:AddIdentity_L2_Valid_Parameters_smoke_Neg_Pos_EMAIL$", - "sendOtp":{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "individualId": "$ID:AddIdentity_L2_Valid_Parameters_smoke_Neg_Pos_UIN$", - "otpChannels": [{channel: "email"},{channel: "phone"}], - "sendOtpReqTemplate": "esignet/SendOtp/SendOtp", - "sendOtpEndPoint": "/v1/esignet/authorization/send-otp" - } - }' - output: '{ - "sendOtpResp":{ - "maskedMobile": "$IGNORE$", - "sendOtpResTemplate":"esignet/SendOtp/SendOtpResult", - "maskedEmail": "$IGNORE$" - } -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTCResult.hbs b/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTCResult.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/AuthenticateUserV3NegTC/AuthenticateUserNegTCResult.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.hbs b/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.hbs deleted file mode 100644 index ead77da28..000000000 --- a/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.hbs +++ /dev/null @@ -1,4 +0,0 @@ -{ - "encodedHash": "{{encodedHash}}", - "transactionId": "{{transactionId}}" -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.yml b/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.yml deleted file mode 100644 index 65630977d..000000000 --- a/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsNegTC.yml +++ /dev/null @@ -1,245 +0,0 @@ -ClaimDetailsNegTC: - ESignet_ClaimDetailsNegTC_Invalid_EncodedHash_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_01 - description: To get ClaimDetails with invalid EncodedHash and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "ggfhjkhgyydiytf", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Null_EncodedHash_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_02 - description: To get ClaimDetails with null value in EncodedHash and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": null, - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Empty_EncodedHash_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_03 - description: To get ClaimDetails with an empty EncodedHash and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Space_Val_EncodedHash_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_04 - description: To get ClaimDetails with space value in EncodedHash and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": " ", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Missing_EncodedHash_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_05 - description: To get ClaimDetails with missing EncodedHash and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$REMOVE$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_invalid_TransactionId_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_06 - description: To get ClaimDetails with invalid TransactionId and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": "aojoidncpoailf" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Null_Val_TransactionId_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_07 - description: To get ClaimDetails with null value in TransactionId and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": null - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Empty_TransactionId_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_08 - description: To get ClaimDetails with an empty TransactionId and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": "" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Space_Val_TransactionId_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_09 - description: To get ClaimDetails with space value in TransactionId and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": " " - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - - ESignet_ClaimDetailsNegTC_Missing_TransactionId_Neg: - endPoint: /v1/esignet/authorization/claim-details - uniqueIdentifier: TC_ESignet_ClaimDetailsNegTC_V3_10 - description: To get ClaimDetails with missing TransactionId and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": "$REMOVE$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_ClaimDetailsNegTC_AuthToken_Xsrf_uin_Valid_Smoke: - endPoint: /v1/esignet/authorization/claim-details - description: To get ClaimDetails with and all valid data - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsNegTC - outputTemplate: esignet/ClaimDetailsNegTC/ClaimDetailsResultNegTC - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$" - }' - output: '{ -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsResultNegTC.hbs b/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsResultNegTC.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/ClaimDetailsNegTC/ClaimDetailsResultNegTC.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.hbs b/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.hbs deleted file mode 100644 index 5ad3d59f8..000000000 --- a/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.hbs +++ /dev/null @@ -1,4 +0,0 @@ -{ - "idvTransactionID": "{{idvTransactionID}}", - "verifierId": "{{verifierId}}" -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.yml b/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.yml deleted file mode 100644 index 6dece253a..000000000 --- a/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierNegTC.yml +++ /dev/null @@ -1,132 +0,0 @@ -GetIdVerifierNegTC: - ESignet_GetIdVerifierNegTC_with_invalidVal_in_verifierId_Neg: - endPoint: /v1/signup/identity-verification/identity-verifier/{verifierId} - uniqueIdentifier: TC_ESignet_GetIdVerifierNegTC_01 - description: To get the details of specific identity verifier with invalid value in verifierId - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetIdVerifierNegTC/GetIdVerifierNegTC - outputTemplate: esignet/error - input: '{ - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "7672836gvfbsvbna" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetIdVerifierNegTC_with_space_in_verifierId_Neg: - endPoint: /v1/signup/identity-verification/identity-verifier/{verifierId} - uniqueIdentifier: TC_ESignet_GetIdVerifierNegTC_02 - description: To get the details of specific identity verifier with only space in verifierId - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetIdVerifierNegTC/GetIdVerifierNegTC - outputTemplate: esignet/error - input: '{ - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": " " -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetIdVerifierNegTC_with_space_in_idvTransactionID_Neg: - endPoint: /v1/signup/identity-verification/identity-verifier/{verifierId} - uniqueIdentifier: TC_ESignet_GetIdVerifierNegTC_03 - description: To get the details of specific identity verifier with only space in verifierId - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetIdVerifierNegTC/GetIdVerifierNegTC - outputTemplate: esignet/error - input: '{ - "idvTransactionID": " ", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetIdVerifierNegTC_with_null_in_idvTransactionID_Neg: - endPoint: /v1/signup/identity-verification/identity-verifier/{verifierId} - uniqueIdentifier: TC_ESignet_GetIdVerifierNegTC_04 - description: To get the details of specific identity verifier with null in idvTransactionID - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetIdVerifierNegTC/GetIdVerifierNegTC - outputTemplate: esignet/error - input: '{ - "idvTransactionID": null, - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetIdVerifierNegTC_with_EmptyVal_in_idvTransactionID_Neg: - endPoint: /v1/signup/identity-verification/identity-verifier/{verifierId} - uniqueIdentifier: TC_ESignet_GetIdVerifierNegTC_05 - description: To get the details of specific identity verifier with empty value in idvTransactionID - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetIdVerifierNegTC/GetIdVerifierNegTC - outputTemplate: esignet/error - input: '{ - "idvTransactionID": "", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetIdVerifierNegTC_with_missing_idvTransactionID_Neg: - endPoint: /v1/signup/identity-verification/identity-verifier/{verifierId} - uniqueIdentifier: TC_ESignet_GetIdVerifierNegTC_06 - description: To get the details of specific identity verifier without idvTransactionID - role: resident - restMethod: get - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetIdVerifierNegTC/GetIdVerifierNegTC - outputTemplate: esignet/error - input: '{ - "idvTransactionID": "$REMOVE$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierResultNegTC.hbs b/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierResultNegTC.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/GetIdVerifierNegTC/GetIdVerifierResultNegTC.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.hbs b/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.hbs deleted file mode 100644 index 2ab9b73a1..000000000 --- a/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.hbs +++ /dev/null @@ -1,8 +0,0 @@ -{ - "requestTime": "{{requestTime}}", - "idvTransactionID": "{{idvTransactionID}}", - "request": { - "verifierId": "{{verifierId}}", - "consent": "{{consent}}" - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.yml b/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.yml deleted file mode 100644 index 280ffcb4d..000000000 --- a/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotNegTC.yml +++ /dev/null @@ -1,505 +0,0 @@ -GetSlotNegTC: - ESignet_GetSlot_without_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_01 - description: To get the slot ID to begin the video identity verification process without requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$REMOVE$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_EmptyVal_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_02 - description: To get the slot ID to begin the video identity verification process with empty value in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_space_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_03 - description: To get the slot ID to begin the video identity verification process with only space in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": " ", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_invalidVal_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_04 - description: To get the slot ID to begin the video identity verification process with invalid value in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "ggasv541265 ", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_numericVal_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_05 - description: To get the slot ID to begin the video identity verification process with numeric value in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "67567541265", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_futureDate_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_06 - description: To get the slot ID to begin the video identity verification process with futureDate in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "67567541265", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_invalidFormat_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_07 - description: To get the slot ID to begin the video identity verification process with invalidFormat in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "2024-10-03 04:10:22.360", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_Empty_verifierId_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_08 - description: To get the slot ID to begin the video identity verification process with empty value in verifierId, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_without_verifierId_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_09 - description: To get the slot ID to begin the video identity verification process without verifierId, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$REMOVE$", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_space_in_verifierId_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_10 - description: To get the slot ID to begin the video identity verification process with only space in verifierId, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": " ", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_numVal_in_verifierId_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_11 - description: To get the slot ID to begin the video identity verification process with numeric value in verifierId, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "545642516717", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_splChar_in_verifierId_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_12 - description: To get the slot ID to begin the video identity verification process with special characters in verifierId, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "^%&^%&^%", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_alphaNumVal_in_verifierId_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_13 - description: To get the slot ID to begin the video identity verification process with alpha numeric value in verifierId, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "abc67816", - "consent": "AGREE" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_identity_verifier_id", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_invalidVal_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_14 - description: To get the slot ID to begin the video identity verification process with invalid value in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "User agreed to consent12" -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_null_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_15 - description: To get the slot ID to begin the video identity verification process with null in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": null -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_without_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_16 - description: To get the slot ID to begin the video identity verification process without consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "$REMOVE$" -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_space_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_17 - description: To get the slot ID to begin the video identity verification process with only space in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": " " -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_numVal_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_18 - description: To get the slot ID to begin the video identity verification process with numeric value in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "6757625" -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_splChar_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_19 - description: To get the slot ID to begin the video identity verification process with special characters in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "%$%^^&%^&" -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_alphaNumVal_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_20 - description: To get the slot ID to begin the video identity verification process with alpha numeric value in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "652fdFF" -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - - ESignet_GetSlot_with_ZeroVal_in_consent_Neg: - endPoint: /v1/signup/identity-verification/slot - uniqueIdentifier: TC_ESignet_GetSlotNegTC_21 - description: To get the slot ID to begin the video identity verification process with zero's in consent, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/GetSlotNegTC/GetSlotNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "idvTransactionID": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_idvTransactionID$", - "verifierId": "$ID:InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg_id$", - "consent": "00000000" -}' - output: '{ - "errors": [ - { - "errorCode": "consent_required", - "errorMessage": "$IGNORE$" - } - ] -}' - \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotResultNegTC.hbs b/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotResultNegTC.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/GetSlotNegTC/GetSlotResultNegTC.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.hbs b/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.hbs deleted file mode 100644 index f48997ac5..000000000 --- a/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.hbs +++ /dev/null @@ -1,18 +0,0 @@ -{ - "encodedHash": "{{encodedHash}}", - "requestTime": "{{requestTime}}", - "pathFragmentCookie": "{{pathFragmentCookie}}", - "pathFragmentCookieTransactionId": "{{pathFragmentCookieTransactionId}}", - "idtToken": "{{idtToken}}", - "request": { - "transactionId": "{{transactionId}}", - "individualId": "{{individualId}}", - "challengeList" : [ - { - "authFactorType" : "{{authFactorType}}", - "challenge" : "{{challenge}}", - "format": "base64url-encoded-json" - } - ] - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.yml b/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.yml deleted file mode 100644 index abdcbb2ae..000000000 --- a/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC.yml +++ /dev/null @@ -1,609 +0,0 @@ -IDTAuthenticationNegTC: - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_empty_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_01 - description: Create transaction Id and consentAction with empty transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_emptyString_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_02 - description: Create transaction Id and consentAction with emptyString transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": " ", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_withoutTransactionId_request_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_03 - description: Create transaction Id and consentAction without transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$REMOVE$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_transactionIDReceivedFromOtherAPIOAuth_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_04 - description: Create transaction Id and consentAction with transaction id from other API - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_alreadyCompleted_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_05 - description: Create transaction Id and consentAction with already completed transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "wGSAtOrEoQ_x_AFvS_uwjIkruvVXVO5hOYhc7izVDyc", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_withoutAuthFromSendOTP_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_06 - description: Create transaction Id and consentAction withoutAuthFromSendOTP transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_missing_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_07 - description: Create transaction Id and consentAction with missing transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$REMOVE$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_invalid_transactionId_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_08 - description: Create transaction Id and consentAction with invalid transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "abcdef", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_invalid_authFactorType_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_09 - description: Create transaction Id and consentAction with invalid auth factor type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "abcd", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_auth_factor_type" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_empty_authFactorType_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_10 - description: Create transaction Id and consentAction with empty auth factor type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_auth_factor_type" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_missing_authFactorType_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_11 - description: Create transaction Id and consentAction with missing auth factor type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "$REMOVE$", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_auth_factor_type" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_emptyString_authFactorType_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_12 - description: Create transaction Id and consentAction with emptyString auth factor type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : " ", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_auth_factor_type" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_Empty_challenge_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_13 - description: Create transaction Id and consentAction with empty challenge - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "" -}' - output: '{ - "errors": [ - { - "errorCode": "unknown_error" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_invalid_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_14 - description: Create transaction Id and consentAction with invalid requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "2022-11-08T06:35:53", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_invalid_diffFormat_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_15 - description: Create transaction Id and consentAction with diff format requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "2022-11-07T06:51:30.195Z", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_missing_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_16 - description: Create transaction Id and consentAction with missing requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$REMOVE$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_empty_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_17 - description: Create transaction Id and consentAction with empty requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_emptyString_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_18 - description: Create transaction Id and consentAction with emptyString requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": " ", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_invalidValue_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_19 - description: Create transaction Id and consentAction with invalid Value as requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "requestinvalid", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_numericString_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_20 - description: Create transaction Id and consentAction with numericString as requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "123456789", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_without_request_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_21 - description: Create transaction Id and consentAction without request - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_withOnlyIndividualID_request_Neg: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_22 - description: Create transaction Id and consentAction with only individualId - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$REMOVE$", - "requestTime": "$REMOVE$", - "pathFragmentCookie": "$REMOVE$", - "pathFragmentCookieTransactionId": "$REMOVE$", - "idtToken": "$REMOVE$", - "transactionId": "$REMOVE$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "$REMOVE$", - "challenge" : "$REMOVE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] -}' - ESignet_IDTAuthenticationNegTC_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke: - endPoint: /v1/esignet/authorization/v3/authenticate - uniqueIdentifier: TC_ESignet_IDTAuthenticationNegTC_23 - description: Create transaction Id and consentAction with valid details - Success - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTC - outputTemplate: esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTCResult - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "idtToken": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "individualId": "$IDTINDIVIUALID$", - "authFactorType" : "IDT", - "challenge" : "$IDTCHALLENGE$" -}' - output: '{ - -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTCResult.hbs b/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTCResult.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/IDTAuthenticationNegTC/IDTAuthenticationNegTCResult.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.hbs b/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.hbs deleted file mode 100644 index 7951f53bd..000000000 --- a/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.hbs +++ /dev/null @@ -1,7 +0,0 @@ -{ - "requestTime": "{{requestTime}}", - "request": { - "authorizationCode": "{{authorizationCode}}", - "state": "{{state}}" - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.yml b/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.yml deleted file mode 100644 index 05b92209e..000000000 --- a/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC.yml +++ /dev/null @@ -1,386 +0,0 @@ -InitiateIdVerificationNegTC: - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_missing_authorizationCode_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_01 - description: To start with Identity verification process with out authorization code, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "$REMOVE$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_authorization_code", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_space_in_authorizationCode_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_02 - description: To start with Identity verification process with only space in authorization code, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": " ", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_authorization_code", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_null_in_authorizationCode_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_03 - description: To start with Identity verification process with null in authorization code, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": null, - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_authorization_code", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_invalidVal_in_authorizationCode_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_04 - description: To start with Identity verification process with invalid value in authorization code, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "jkhuewrfkeuw8frhe876754@#", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "grant_exchange_failed", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_NumVal_in_authorizationCode_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_05 - description: To start with Identity verification process with numeric value in authorization code, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "5645767564534565677", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "grant_exchange_failed", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_EmptyVal_in_authorizationCode_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_06 - description: To start with Identity verification process with empty value in authorization code, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_authorization_code", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_spaceVal_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_07 - description: To start with Identity verification process with only space in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": " ", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_NULL_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_08 - description: To start with Identity verification process with NULL in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": NULL, - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_EmptyVal_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_09 - description: To start with Identity verification process with empty value in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_null_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_10 - description: To start with Identity verification process with null in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": null, - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_invalidFormat_in_requestTime_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_11 - description: To start with Identity verification process with invalidFormat in requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "2023/11/03 11:03:29.296Z", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_missing_state_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_12 - description: To start with Identity verification process with out state parameter, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "$REMOVE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_state", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_spaceVal_in_state_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_13 - description: To start with Identity verification process with only space in state parameter, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": " " -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_state", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_EmptyVal_in_state_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_14 - description: To start with Identity verification process with empty value in state parameter, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_state", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_null_in_state_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_15 - description: To start with Identity verification process with null value in state parameter, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": null -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_state", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_with_missing_requestTime_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_16 - description: To start with Identity verification process with out requestTime, Expecting error code in the response - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$REMOVE$", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] - }' - - ESignet_InitiateIdVerification_STransId_AuthToken_Xsrf_NegTC_Valid_Smoke_Sid_Neg: - endPoint: /v1/signup/identity-verification/initiate - uniqueIdentifier: TC_ESignet_InitiateIdVerificationNegTC_17 - description: To start with Identity verification process with all valid details, Expecting success response by setting the cookie header as IDV_TRANSACTION_ID in the response with identity verification transaction ID - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationNegTC - outputTemplate: esignet/InitiateIdVerificationNegTC/InitiateIdVerificationResultNegTC - input: '{ - "requestTime": "$TIMESTAMP$", - "authorizationCode": "$ID:SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_code$", - "state": "urlInfo1724138417665" -}' - output: '{ - -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationResultNegTC.hbs b/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationResultNegTC.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/InitiateIdVerificationNegTC/InitiateIdVerificationResultNegTC.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/LinkedAuthenticationWla/LinkedAuthenticationWla.yml b/api-test/src/main/resources/esignet/LinkedAuthenticationWla/LinkedAuthenticationWla.yml index 53b93a3ba..ae7be5d5c 100644 --- a/api-test/src/main/resources/esignet/LinkedAuthenticationWla/LinkedAuthenticationWla.yml +++ b/api-test/src/main/resources/esignet/LinkedAuthenticationWla/LinkedAuthenticationWla.yml @@ -144,7 +144,7 @@ LinkedAuthenticationWla: } }' output: '{ - "consentAction": "NOCAPTURE", + "consentAction": "CAPTURE", "sendOtpResp":{ "maskedMobile": "$IGNORE$", "sendOtpResTemplate":"esignet/SendBindingOtp/SendBindingOtpResult", diff --git a/api-test/src/main/resources/esignet/LinkedAuthorizationConsent/LinkedAuthorizationConsent.yml b/api-test/src/main/resources/esignet/LinkedAuthorizationConsent/LinkedAuthorizationConsent.yml index 21ba8b1b3..e289def93 100644 --- a/api-test/src/main/resources/esignet/LinkedAuthorizationConsent/LinkedAuthorizationConsent.yml +++ b/api-test/src/main/resources/esignet/LinkedAuthorizationConsent/LinkedAuthorizationConsent.yml @@ -37,7 +37,7 @@ LinkedAuthorizationConsent: }' - ESignet_LinkedAuthorizationConsent_Consent_uin_User2_Neg: + ESignet_LinkedAuthorizationConsent_Consent_uin_User2: endPoint: /v1/esignet/linked-authorization/v2/consent description: Linked authorization consent with User2 UIN role: resident @@ -45,7 +45,7 @@ LinkedAuthorizationConsent: restMethod: post validityCheckRequired: true inputTemplate: esignet/LinkedConsent/LinkAuthorizationConsent/LinkAuthorizationConsent - outputTemplate: esignet/error + outputTemplate: esignet/LinkedAuthorizationConsent/LinkedAuthorizationConsentResult input: '{ "requestTime": "$TIMESTAMP$", "linkedTransactionId": "$ID:LinkTransaction_Consent_uin_User2_all_Valid_Smoke_sid_linkTransactionId$", @@ -53,11 +53,6 @@ LinkedAuthorizationConsent: "signature": "$CONSENTDETACHEDSIGNATUREUSER2$" }' output: '{ - "errors": [ - { - "errorCode": "invalid_transaction" - } - ] }' ESignet_LinkedAuthorizationConsent_Consent_Vid_User2_Neg: diff --git a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegResultTC.hbs b/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegResultTC.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegResultTC.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.hbs b/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.hbs deleted file mode 100644 index 18abb612e..000000000 --- a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.hbs +++ /dev/null @@ -1,39 +0,0 @@ -{ - "requestTime": "{{requestTime}}", - "request": { - "clientId": "{{clientId}}", - "scope": "{{scope}}", - "responseType": "{{responseType}}", - "redirectUri": "{{redirectUri}}", - "display": "{{display}}", - "prompt": "{{prompt}}", - "acrValues": "{{acrValues}}", - "claims": { - "userinfo": { - "name": { - "essential": false - }, - "phone_number": { - "essential": true - }, - "verified_claims": [ - { - "verification": { - "trust_framework": {"value": "de_aml"} - }, - "claims": { - "email": { - "essential": true - } - } - } - ] - }, - "id_token": {} - }, - "nonce" : "{{nonce}}", - "state" : "{{state}}", - "claimsLocales" : "{{claimsLocales}}", - "uiLocales" : "{{uiLocales}}" - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.yml b/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.yml deleted file mode 100644 index 612a68806..000000000 --- a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC.yml +++ /dev/null @@ -1,1007 +0,0 @@ -OAuthDetailsRequestV3Neg: - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_RequestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid RequestTime and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "apknktokd", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US", - "trust_framework": "jp_aml" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "requestTime: invalid_request" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Null_value_RequestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with null value RequestTime and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": null, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "requestTime: invalid_request" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_empty_RequestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with empty RequestTime and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "requestTime: invalid_request" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Space_value_RequestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with space value in RequestTime and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": " ", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "requestTime: invalid_request" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_missing_RequestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with missing RequestTime and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$REMOVE$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "requestTime: invalid_request" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_ClientID_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid ClientID and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "jkjdangjfkdjnlkfkgjdsfnlkfjdkf", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "invalid_client_id" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Null_ClientID_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with null ClientID and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": null, - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "request.clientId: invalid_client_id" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_empty_ClientID_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with an empty ClientID and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "request.clientId: invalid_client_id" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Space_value_ClientID_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with space value in ClientID and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": " ", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "request.clientId: invalid_client_id" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_missing_ClientID_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with missing ClientID and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "$REMOVE$", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "request.clientId: invalid_client_id" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_Scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid scope and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "aegaegegadd", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "request.scope: invalid_scope" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Null_Scope_value_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with null scope value and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": null, - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "request.scope: invalid_scope" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_empty_Scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with an empty scope and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "request.scope: invalid_scope" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Space_value_Scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with space value in scope and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": " ", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "request.scope: invalid_scope" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_missing_Scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with missing scope and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "$REMOVE$", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "request.scope: invalid_scope" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_ResponseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid ResponseType and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "tfyjii", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "request.responseType: invalid_response_type" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Null_ResponseType_value_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with null ResponseType value and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": null, - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "request.responseType: invalid_response_type" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_empty_ResponseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with an empty ResponseType and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "request.responseType: invalid_response_type" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Space_value_ResponseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with space value in ResponseType and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": " ", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "request.responseType: invalid_response_type" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_missing_ResponseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with missing ResponseType and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "$REMOVE$", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "request.responseType: invalid_response_type" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_RedirectURI_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid RedirectURI and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "adaklfjfods", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "request.redirectUri: invalid_redirect_uri" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Null_RedirectURI_value_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with null value in RedirectURI and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": null, - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "request.redirectUri: invalid_redirect_uri" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_empty_RedirectURI_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with an empty RedirectURI and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": $TIMESTAMP$, - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "request.redirectUri: invalid_redirect_uri" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_Space_value_RedirectURI_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with space value in RedirectURI and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": " ", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "request.redirectUri: invalid_redirect_uri" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_missing_RedirectURI_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with missing RedirectURI and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$REMOVE$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "request.redirectUri: invalid_redirect_uri" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_Display_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid Display and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "ajodinvalidoaoi", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_display", - "errorMessage": "request.display: invalid_display" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_Prompt_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid Prompt and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "oaidnoadi", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_prompt", - "errorMessage": "request.prompt: invalid_prompt" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_acrValues_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid acrValues and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "malslinakjol", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_acr", - "errorMessage": "request.acr: invalid_acr" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_nonce_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid nonce and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "aod;ofald", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_nonce", - "errorMessage": "request.nonce: invalid_nonce" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_state_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid state and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "aodfjlf", - "claimsLocales": "en", - "uiLocales": "en-US" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_state", - "errorMessage": "request.state: invalid_state" - } - ] -}' - - ESignet_OAuthDetailsRequestNegTC_V3_with_invalid_uiLocales_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with invalid uiLocales and all valid data - role: resident - restMethod: post - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "adfa", -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_ui_locales", - "errorMessage": "request.ui_locales: invalid_ui_locales" - } - ] -}' - - ESignet_OAuthDetailsRequest_V3_WithClaimValues_AuthToken_with_invalid_essential_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_OAuthDetailsRequestNegTC_V3_01 - description: To get OAuthDetails with invalid essential value in verified claims and all valid data - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsWithClaimValueRequestNegTC - outputTemplate: esignet/error - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US", - "trust_framework": "de_aml", - "max_age": 63113852, - "given_name": ["automation", "test"], - "purpose": "Name given by user", - "name_value": "automation", - "name_values": ["ab","cd"], - "phone_number": "1234567890", - "essential": 10 -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_essential", - "errorMessage": "request.invalid_essential: invalid_essential" - } - ] -}' - - ESignet_OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid: - endPoint: /v1/esignet/authorization/v3/oauth-details - description: To get OAuthDetails with all valid data - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/OAuthDetailsRequestV3/OAuthDetailsWithClaimValueRequest - input: '{ - "requestTime": "$TIMESTAMP$", - "clientId": "0brnxh_duaXqCa-HW4kSeljeMp0qGOmqh4tm_zFKVgY", - "scope": "openid profile", - "responseType": "code", - "redirectUri": "$IDPREDIRECTURI$", - "display": "popup", - "prompt": "login", - "acrValues": "mosip:idp:acr:generated-code", - "nonce": "973eieljzng", - "state": "urlInfo1724138417665", - "claimsLocales": "en", - "uiLocales": "en-US", - "trust_framework": ["de_aml","jp_aml"], - "assurance_level": "high", - "max_age": 63113852, - "evidence_type": "document", - "evidence_method": "test", - "given_name": ["automation", "test"], - "purpose": "Name given by user", - "name_value": "automation", - "name_values": ["ab","cd"], - "phone_number": "1234567890", - "email_essential": true -}' - output: '{ - -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsWithClaimValueRequestNegTC.hbs b/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsWithClaimValueRequestNegTC.hbs deleted file mode 100644 index eeafba6f8..000000000 --- a/api-test/src/main/resources/esignet/OAuthDetailsRequestV3NegTC/OAuthDetailsWithClaimValueRequestNegTC.hbs +++ /dev/null @@ -1,62 +0,0 @@ -{ - "requestTime": "{{requestTime}}", - "request": { - "clientId": "{{clientId}}", - "scope": "{{scope}}", - "responseType": "{{responseType}}", - "redirectUri": "{{redirectUri}}", - "display": "{{display}}", - "prompt": "{{prompt}}", - "acrValues": "{{acrValues}}", - "claims": { - "userinfo": { - "name": { - "essential": false - }, - "phone_number": { - "essential": true - }, - "verified_claims": [ - { - "verification": { - "trust_framework": { - "values": "{{trust_framework}}" - }, - "assurance-level": { - "value": "high" - }, - "time": { - "max_age": "{{max_age}}" - }, - "evidence": [ - {"type": { "value": "document"}}, - {"method": { "value": "test" }} - ] - }, - "claims": { - "given_name": { - "values":"manual", - "purpose":"{{purpose}}", - "essential": true - }, - "name": { - "value": "{{name_value}}" - }, - "phone_number": { - "value": "{{phone_number}}" - }, - "email": { - "essential": {{essential}} - } - } - } - ] - }, - "id_token": {} - }, - "nonce" : "{{nonce}}", - "state" : "{{state}}", - "claimsLocales" : "{{claimsLocales}}", - "uiLocales" : "{{uiLocales}}" - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.hbs b/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.hbs deleted file mode 100644 index 042e820a0..000000000 --- a/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.hbs +++ /dev/null @@ -1,8 +0,0 @@ -{ - "encodedHash": "{{encodedHash}}", - "requestTime": "{{requestTime}}", - "request": { - "transactionId": "{{transactionId}}", - "pathFragment": "{{pathFragment}}" - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.yml b/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.yml deleted file mode 100644 index f3b5091ec..000000000 --- a/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC.yml +++ /dev/null @@ -1,414 +0,0 @@ -PrepareSignupRedirectNegTC: - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_without_requestTime_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_01 - description: Create ID token for the signup service without requestTime - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "invalid_request" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_empty_requestTime_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_02 - description: Create ID token for the signup service empty requestTime - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "invalid_request" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_emptyString_requestTime_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_03 - description: Create ID token for the signup service emptyString requestTime - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": " ", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "invalid_request" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_invalid_requestTime_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_04 - description: Create ID token for the signup service invalid requestTime - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "invalidRequest", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "invalid_request" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_numeric_As_requestTime_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_05 - description: Create ID token for the signup service numeric as requestTime - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "12345", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "invalid_request" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_invalidTimeFormat_requestTime_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_06 - description: Create ID token for the signup service invalidTimeFormat as requestTime - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "Jun 09 2023 15:28:14", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "invalid_request" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_empty_requestObject_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_07 - description: Create ID token for the signup service empty as request object - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "", - "transactionId": "", - "pathFragment": "" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_invalid_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_08 - description: Create ID token for the signup service transaction id as invalid - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "1234567890", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_without_transactionId_parameter_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_09 - description: Create ID token for the signup service without transaction id - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_unAuthenticated_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_10 - description: Create ID token for the signup service with an unauthenticated transaction id - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "aTcWUCX2K4Rlx43UEm0bgkB0r0el9_2jWPclsfjebBc", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_alreadyAuthenticated_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_11 - description: Create ID token for the signup service with an already authenticated transaction id - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "aTcWUCX2K4Rlx43UEm0bgkB0r0el9_2jWPclsfjebRc", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_null_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_12 - description: Create ID token for the signup service with transaction id as null - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": null, - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_transactionIDReceivedOtherAPI_thatOAuthDetail_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_13 - description: Create ID token for the signup service with transaction id received in other API - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "bHcqVsIp11X2GBWu2PrKFePQKYYHIuo6eafYnRmHey0", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_expired_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_14 - description: Create ID token for the signup service with expired transaction id - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "Lb3c_7al_WOAqbbDyU4aSxxofOcEHLC9Y3qSgSCBB7M", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_empty_transactionId_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_15 - description: Create ID token for the signup service with empty transaction id - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": " ", - "pathFragment": "path-fragment" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "invalid_transaction" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_without_pathFragment_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_16 - description: Create ID token for the signup service withuot path Fragment - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "$REMOVE$" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_path_fragment", - "errorMessage": "invalid_path_fragment" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_null_pathFragment_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_17 - description: Create ID token for the signup service with path Fragment as null - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": null - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_path_fragment", - "errorMessage": "invalid_path_fragment" - } - ] -}' - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_empty_pathFragment_Neg_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_18 - description: Create ID token for the signup service with path Fragment as empty - Failure - role: resident - restMethod: post - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "" - }' - output: '{ - "errors": [ - { - "errorCode": "invalid_path_fragment", - "errorMessage": "invalid_path_fragment" - } - ] -}' - - ESignet_PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid: - endPoint: /v1/esignet/authorization/prepare-signup-redirect - uniqueIdentifier: TC_ESignet_PrepareSignupRedirectNegTC_19 - description: Create ID token for the signup service with valid data - Success - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - validityCheckRequired: true - inputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTC - outputTemplate: esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTCResult - input: '{ - "encodedHash": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "transactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "pathFragment": "path-fragment" - }' - output: '{ -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTCResult.hbs b/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTCResult.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/PrepareSignupRedirectNegTC/PrepareSignupRedirectNegTCResult.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.hbs b/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.hbs deleted file mode 100644 index e3704b3ec..000000000 --- a/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.hbs +++ /dev/null @@ -1,16 +0,0 @@ -{ - "encodedHash": "{{encodedHash}}", - "requestTime": "{{requestTime}}", - "pathFragmentCookie": "{{pathFragmentCookie}}", - "pathFragmentCookieTransactionId": "{{pathFragmentCookieTransactionId}}", - "request": { - "transactionId": "{{transactionId}}", - "permittedAuthorizeScopes": [], - "acceptedClaims" : [ - {{#each acceptedClaims}} - "{{channel}}" - {{#unless @last}},{{/unless}} - {{/each}} - ] - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.yml b/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.yml deleted file mode 100644 index 7ec64f457..000000000 --- a/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC.yml +++ /dev/null @@ -1,271 +0,0 @@ -SignupAuthorizeCodeNegTC: - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_empty_acceptedClaims_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_01 - description: Create authorization code with empty acceptedClaims - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": [{channel: ""},{channel: ""}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_accepted_claim", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_missing_acceptedClaims_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_02 - description: Create authorization code with missing acceptedClaims - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": "$REMOVE$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_accepted_claim", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_invalid_transactionId_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_03 - description: Create authorization code with invalid transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "invalidTransactionID", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_without_transactionId_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_04 - description: Create authorization code without transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_alreadyCompleted_transactionId_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_05 - description: Create authorization code with already completed transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "Rtf-lPuIJfXUg9jaqJdOeuospISzuDJ7_PxV6fEvX7g", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_emptyString_transactionId_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_06 - description: Create authorization code with empty string transaction id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": " ", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_transaction", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_empty_requestTime_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_07 - description: Create authorization code with empty requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_diff_requestTime_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_08 - description: Create authorization code with different requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "2022-11-07T06:51:30.195Z", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_future_requestTime_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_09 - description: Create authorization code with future requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "2050-11-07T06:51:30.195Z", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_AuthToken_Xsrf_uin_invalid_requestTime_Neg: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_10 - description: Create authorization code with invalid requestTime - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/error - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "2022-11-08T06:35:53", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeCodeNegTC_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid: - endPoint: /v1/esignet/authorization/auth-code - uniqueIdentifier: TC_ESignet_SignupAuthorizeCodeNegTC_11 - description: Create authorization code with valid details - Success - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTC - outputTemplate: esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTCResult - input: '{ - "encodedHash": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_encodedResp$", - "requestTime": "$TIMESTAMP$", - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "transactionId": "$ID:SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid_transactionId$", - "acceptedClaims": [{channel: "email"},{channel: "phone_number"}] -}' - output: '{ - -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTCResult.hbs b/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTCResult.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/SignupAuthorizeCodeNegTC/SignupAuthorizeCodeNegTCResult.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.hbs b/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.hbs deleted file mode 100644 index 95c858e2b..000000000 --- a/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.hbs +++ /dev/null @@ -1,15 +0,0 @@ -{ - "requestTime": "{{requestTime}}", - "pathFragmentCookieTransactionId": "{{pathFragmentCookieTransactionId}}", - "pathFragmentCookie": "{{pathFragmentCookie}}", - "request": { - "clientId": "{{clientId}}", - "scope": "{{scope}}", - "responseType": "{{responseType}}", - "redirectUri": "{{redirectUri}}", - "acrValues": "{{acrValues}}", - "state" : "{{state}}", - "uiLocales" : "{{uiLocales}}", - "idTokenHint" : "{{idTokenHint}}" - } -} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.yml b/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.yml deleted file mode 100644 index 1538e453a..000000000 --- a/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC.yml +++ /dev/null @@ -1,914 +0,0 @@ -SignupAuthorizeNegTC: - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_clientId_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_01 - description: Create consent details and unique transactionID with invalid client id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "reateOIDCClient_all_Valid_Smoke_sid_cl", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_clientId_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_02 - description: Create consent details and unique transactionID with empty client id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_clientId_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_03 - description: Create consent details and unique transactionID with missing client id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "$REMOVE$", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_null_clientId_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_04 - description: Create consent details and unique transactionID with null client id - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "null", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_client_id", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_redirectUri_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_05 - description: Create consent details and unique transactionID with empty redirect URI - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_redirectUri_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_06 - description: Create consent details and unique transactionID with invalid redirect URI - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "https://health-services.com", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_redirectUri_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_07 - description: Create consent details and unique transactionID with missing redirect URI - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$REMOVE$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_redirect_uri", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_responseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_08 - description: Create consent details and unique transactionID with empty response type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_responseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_09 - description: Create consent details and unique transactionID with invalid response type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "decode", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_responseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_10 - description: Create consent details and unique transactionID with missing response type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "$REMOVE$", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_null_responseType_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_11 - description: Create consent details and unique transactionID with null response type - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "null", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_response_type", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_12 - description: Create consent details and unique transactionID with invalid scope - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "email", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_withoutSpace_scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_13 - description: Create consent details and unique transactionID without space in scope - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openidemailaddressphone", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_multipleSpacesInBetween_scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_14 - description: Create consent details and unique transactionID with multiple spaces in between scope - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": " openid email address phone ", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_15 - description: Create consent details and unique transactionID with missing scope - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "$REMOVE$", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_scope_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_16 - description: Create consent details and unique transactionID with empty scope - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_scope", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_acrValues_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_17 - description: Create consent details and unique transactionID with invalid acr values - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:static", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_acrValues_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_18 - description: Create consent details and unique transactionID with empty acr values - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_acrValues_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_19 - description: Create consent details and unique transactionID with missing acr values - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "$REMOVE$", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalidFormat_acrValues_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_20 - description: Create consent details and unique transactionID with invalidformat acr values - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "abcd", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_uiLocales_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_21 - description: Create consent details and unique transactionID with invalid uilocales - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "abcd", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_uiLocales_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_22 - description: Create consent details and unique transactionID with empty uilocales - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_uiLocales_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_23 - description: Create consent details and unique transactionID with missing uilocales - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "$REMOVE$", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_otherLanguage_uiLocales_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_24 - description: Create consent details and unique transactionID with other language uilocales - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "kn", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_state_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_25 - description: Create consent details and unique transactionID with invalid state - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "$$@#fdhfdkl", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_state_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_26 - description: Create consent details and unique transactionID with missing state - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "$REMOVE$", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_exceedingRange_state_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_27 - description: Create consent details and unique transactionID with exceeding range state - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "aa1hmnkz7ee0c1te988gw9bkos5pb4mi830uf6wrxj6hs80z07rmtwpzvjr9qjhgw8aw2rf9iljprvvxxh4y94je13gl4vr9vwaq358tnov5bh3likbef6elkqnqvnn4gu9fh1pzui2b459m0xfwjdsjlinll1s6kayk29d01i3vx1uagewcoc39ssxm4feyci06zoimd6ot3c0w3w8genv1d0hk4ni9ym1mjkwclffj5tfiglipy9sn5fyjqway9c6y", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_empty_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_28 - description: Create consent details and unique transactionID with empty request time - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_diffFormat_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_29 - description: Create consent details and unique transactionID with different format request time - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "2022-11-07T06:51:30.195Z", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_invalid_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_30 - description: Create consent details and unique transactionID with invalid request time - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "2022-11-08T06:35:53", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_missing_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_31 - description: Create consent details and unique transactionID with missing request time - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$REMOVE$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_emptyString_requestTime_Neg: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_32 - description: Create consent details and unique transactionID with empty String as request time - Failure - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/error - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": " ", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - "errors": [ - { - "errorCode": "invalid_request", - "errorMessage": "$IGNORE$" - } - ] -}' - ESignet_SignupAuthorizeNegTC_OAuthDetailsRequest_AuthToken_Xsrf_uin_all_Valid_PosNeg_Smoke_sid: - endPoint: /v1/esignet/authorization/v3/oauth-details - uniqueIdentifier: TC_ESignet_SignupAuthorizeNegTC_33 - description: Create consent details and unique transactionID with valid details - Success - role: resident - restMethod: post - checkErrorsOnlyInResponse: true - inputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTC - outputTemplate: esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTCResult - input: '{ - "pathFragmentCookie": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_pathFragmentCookie$", - "pathFragmentCookieTransactionId": "$ID:OAuthDetailsRequest_V3_Neg_AuthToken_Xsrf_uin_all_Valid_Smoke_sid_transactionId$", - "requestTime": "$TIMESTAMP$", - "clientId": "mosip-signup-oauth-client", - "scope": "openid", - "responseType": "code", - "redirectUri": "$SIGNUPREDIRECTURI$", - "acrValues": "mosip:idp:acr:id-token", - "state": "urlInfo1724138417665", - "uiLocales": "en", - "idTokenHint": "$ID:PrepareSignupRedirectNegTC_AuthToken_Xsrf_STransId_uin_Otp_Valid_posNeg_Smoke_sid_idToken$" -}' - output: '{ - -}' \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTCResult.hbs b/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTCResult.hbs deleted file mode 100644 index 9e26dfeeb..000000000 --- a/api-test/src/main/resources/esignet/SignupAuthorizeNegTC/SignupAuthorizeNegTCResult.hbs +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml b/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml index 3597ba381..1031d40d9 100644 --- a/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml +++ b/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml @@ -1854,7 +1854,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length" + "errorCode": "invalid_auth_factor_type" } ], "sendOtpResp": { @@ -2031,7 +2031,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length" + "errorCode": "invalid_challenge" } ], "sendOtpResp": { @@ -2209,7 +2209,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "invalid_auth_factor_type_format" + "errorCode": "invalid_challenge_format" } ], "sendOtpResp": { diff --git a/api-test/testNgXmlFiles/esignetSuite.xml b/api-test/testNgXmlFiles/esignetSuite.xml index df3245132..dbaacbb11 100644 --- a/api-test/testNgXmlFiles/esignetSuite.xml +++ b/api-test/testNgXmlFiles/esignetSuite.xml @@ -654,83 +654,5 @@ --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file From ef7306c6462b8e030c9c68de2403016405515ca2 Mon Sep 17 00:00:00 2001 From: Nandhukumar Date: Tue, 19 Nov 2024 16:24:38 +0530 Subject: [PATCH 02/40] MOSIP-36485 Signed-off-by: Nandhukumar --- .../src/main/resources/esignet/OIDCClientV2/OIDCClientV2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-test/src/main/resources/esignet/OIDCClientV2/OIDCClientV2.yml b/api-test/src/main/resources/esignet/OIDCClientV2/OIDCClientV2.yml index ab21d84ab..d09126466 100644 --- a/api-test/src/main/resources/esignet/OIDCClientV2/OIDCClientV2.yml +++ b/api-test/src/main/resources/esignet/OIDCClientV2/OIDCClientV2.yml @@ -50,7 +50,7 @@ OIDCClientV2: "userClaims3": "gender", "userClaims4": "phone_number", "userClaims5": "birthdate", - "authContextRefs": [{acrValues: "mosip:idp:acr:static-code"},{acrValues: "mosip:idp:acr:generated-code"},{acrValues: "mosip:idp:acr:biometrics"}], + "authContextRefs": [{acrValues: "mosip:idp:acr:static-code"},{acrValues: "mosip:idp:acr:generated-code"},{acrValues: "mosip:idp:acr:biometrics"},{acrValues: "mosip:idp:acr:password"},{acrValues: "mosip:idp:acr:linked-wallet"},{acrValues: "mosip:idp:acr:knowledge"},{acrValues: "mosip:idp:acr:id-token"}], "grantTypes": "authorization_code", "clientAuthMethods": "private_key_jwt", "keyLang1": "$1STLANG$", From cd61d6126c2f4583ba559751114617e186586b3f Mon Sep 17 00:00:00 2001 From: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com> Date: Thu, 28 Nov 2024 09:40:20 +0530 Subject: [PATCH 03/40] MOSIP-37793 - Updated the Readme file Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com> --- api-test/README.md | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/api-test/README.md b/api-test/README.md index b0f1c11cd..735dcb15b 100644 --- a/api-test/README.md +++ b/api-test/README.md @@ -4,15 +4,21 @@ The **eSignet API Test Rig** is designed for the execution of module-wise automation API tests for the eSignet service. This test rig utilizes **Java REST Assured** and **TestNG** frameworks to automate testing of the eSignet API functionalities. The key focus is to validate the authentication, signature generation, and related functionalities provided by the eSignet module. +--- + ## Test Categories - **Smoke**: Contains only positive test scenarios for quick verification. - **Regression**: Includes all test scenarios, covering both positive and negative cases. +--- + ## Coverage This test rig covers only **external API endpoints** exposed by the eSignet module. +--- + ## Pre-requisites Before running the automation tests, ensure the following software is installed on the machine: @@ -20,6 +26,8 @@ Before running the automation tests, ensure the following software is installed - **Java 21** (or a compatible version) - **Maven 3.9.6** (or higher) - **Lombok** (Refer to [Lombok Project](https://projectlombok.org/)) +- **setting.xml** ([download here](https://github.com/mosip/mosip-functional-tests/blob/master/settings.xml)) +- **apitest-commons** library should be cloned and the JAR should be built. Refer to ([README](https://github.com/mosip/mosip-functional-tests/blob/release-1.3.0/apitest-commons/README.md)) ### For Windows @@ -32,6 +40,8 @@ Before running the automation tests, ensure the following software is installed - In the regular Maven configuration folder (`/conf`) - Under `/usr/local/maven/conf/` +--- + ## Access Test Automation Code You can access the test automation code using either of the following methods: @@ -51,6 +61,8 @@ You can access the test automation code using either of the following methods: git clone https://github.com/mosip/esignet ``` +--- + ## Build Test Automation Code Once the repository is cloned or downloaded, follow these steps to build and install the test automation code: @@ -67,6 +79,8 @@ Once the repository is cloned or downloaded, follow these steps to build and ins This will download the required dependencies and prepare the test suite for execution. +--- + ## Execute Test Automation Suite You can execute the test automation code using either of the following methods: @@ -130,23 +144,28 @@ To execute the tests using Eclipse IDE, use the following steps: ## 6. **View Test Results** - After the tests are executed, you can view the detailed results in the `api-test\testng-report` directory. + - Two reports will gets generated + - First report is for pre-requisite testcases + - Second report is for core testcases - - -### Details of Arguments Used +--- + +## Details of Arguments Used - **env.user**: Replace `` with the appropriate environment name (e.g., `dev`, `qa`, etc.). - **env.endpoint**: The environment where the application under test is deployed. Replace `` with the correct base URL for the environment (e.g., `https://api-internal..mosip.net`). - **env.testLevel**: Set this to `smoke` to run only smoke test cases, or `smokeAndRegression` to run both smoke and regression tests. - **jar**: Specify the name of the JAR file to execute. The version will change according to the development code version. For example, the current version may look like `apitest-esignet-1.2.1-SNAPSHOT-jar-with-dependencies.jar`. -## Build and Run +### Build and Run Info To run the tests for both **Smoke** and **Regression**: 1. Ensure the correct environment and test level parameters are set. 2. Execute the tests as shown in the command above to validate eSignet's API functionalities. +--- + ## License -This project is licensed under the terms of the [Mozilla Public License 2.0] \ No newline at end of file +This project is licensed under the terms of the [Mozilla Public License 2.0](https://github.com/mosip/mosip-platform/blob/master/LICENSE) From cbc39641823064457add7b0e1c639154884b6a8c Mon Sep 17 00:00:00 2001 From: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com> Date: Fri, 29 Nov 2024 11:22:26 +0530 Subject: [PATCH 04/40] [MOSIP-37815] Added Unique Config as User input (#1017) * [MOSIP-37815] Added Unique Config as Userinput Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Fixed Chart lint Failure Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Updated as per requirement Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Updated values.yaml Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Tested Working as expected Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Signed-off-by: VSIVAKALYAN --------- Signed-off-by: VSIVAKALYAN --- deploy/esignet/install.sh | 4 ++-- helm/esignet/templates/deployment.yaml | 2 ++ helm/esignet/values.yaml | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/deploy/esignet/install.sh b/deploy/esignet/install.sh index 2dc71cf6b..c17d41f92 100755 --- a/deploy/esignet/install.sh +++ b/deploy/esignet/install.sh @@ -83,9 +83,9 @@ function installing_esignet() { fi done - echo Installing esignet - helm -n $NS install esignet mosip/esignet --version $CHART_VERSION $ENABLE_INSECURE $plugin_option --set metrics.serviceMonitor.enabled=$servicemonitorflag -f values.yaml --wait + helm -n $NS install esignet mosip/esignet --version $CHART_VERSION $ENABLE_INSECURE $plugin_option \ + --set metrics.serviceMonitor.enabled=$servicemonitorflag -f values.yaml --wait kubectl -n $NS get deploy -o name | xargs -n1 -t kubectl -n $NS rollout status diff --git a/helm/esignet/templates/deployment.yaml b/helm/esignet/templates/deployment.yaml index 6abaa3cd8..90a30eb53 100644 --- a/helm/esignet/templates/deployment.yaml +++ b/helm/esignet/templates/deployment.yaml @@ -95,6 +95,8 @@ spec: - name: container_user value: {{ .Values.containerSecurityContext.runAsUser }} {{- if .Values.additionalResources.javaOpts }} + - name: SPRING_KAFKA_CONSUMER_GROUP_ID + value: esignet-consumer-{{ .Release.Namespace }} # Using the namespace dynamically - name: JDK_JAVA_OPTIONS value: {{ .Values.additionalResources.javaOpts }} {{- end }} diff --git a/helm/esignet/values.yaml b/helm/esignet/values.yaml index 24fed18f2..f394b1e75 100644 --- a/helm/esignet/values.yaml +++ b/helm/esignet/values.yaml @@ -240,6 +240,7 @@ updateStrategy: ## - name: FOO ## value: "bar" ## + extraEnvVars: | - name: KEYCLOAK_EXTERNAL_URL valueFrom: From 7fdc96d1492f449d985337f6501ef29a825c2ded Mon Sep 17 00:00:00 2001 From: Nandhukumar Date: Fri, 29 Nov 2024 13:15:53 +0530 Subject: [PATCH 05/40] MOSIP-37942 Signed-off-by: Nandhukumar --- .../testrig/apirig/esignet/testrunner/MosipTestRunner.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java index 255c95f5a..74d3e0fe7 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java @@ -159,13 +159,13 @@ public static void suiteSetup(String runType) { BaseTestCase.certsForModule = GlobalConstants.ESIGNET; DBManager.executeDBQueries(EsignetConfigManager.getKMDbUrl(), EsignetConfigManager.getKMDbUser(), EsignetConfigManager.getKMDbPass(), EsignetConfigManager.getKMDbSchema(), - getGlobalResourcePath() + "/" + "config/keyManagerDataDeleteQueriesForEsignet.txt"); + getGlobalResourcePath() + "/" + "config/keyManagerCertDataDeleteQueries.txt"); DBManager.executeDBQueries(EsignetConfigManager.getIdaDbUrl(), EsignetConfigManager.getIdaDbUser(), EsignetConfigManager.getPMSDbPass(), EsignetConfigManager.getIdaDbSchema(), - getGlobalResourcePath() + "/" + "config/idaDeleteQueriesForEsignet.txt"); + getGlobalResourcePath() + "/" + "config/idaCertDataDeleteQueries.txt"); DBManager.executeDBQueries(EsignetConfigManager.getMASTERDbUrl(), EsignetConfigManager.getMasterDbUser(), EsignetConfigManager.getMasterDbPass(), EsignetConfigManager.getMasterDbSchema(), - getGlobalResourcePath() + "/" + "config/masterDataDeleteQueriesForEsignet.txt"); + getGlobalResourcePath() + "/" + "config/masterDataCertDataDeleteQueries.txt"); AdminTestUtil.initiateesignetTest(); BaseTestCase.otpListener = new OTPListener(); BaseTestCase.otpListener.run(); From 474344a4c8322b962361bce2010768688b0f10fe Mon Sep 17 00:00:00 2001 From: Nandhukumar Date: Fri, 29 Nov 2024 22:36:23 +0530 Subject: [PATCH 06/40] MOSIP-37942 Signed-off-by: Nandhukumar --- api-test/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api-test/pom.xml b/api-test/pom.xml index 0f909aa6f..e6fa76c01 100644 --- a/api-test/pom.xml +++ b/api-test/pom.xml @@ -54,9 +54,9 @@ - io.mosip.testrig.apirig.apitest.commons + io.mosip.testrig.apitest.commons apitest-commons - 1.2.2-SNAPSHOT + 1.3.0-SNAPSHOT From 1db8aaeea2f326d43667dcebaae04b013e6fa402 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:44:29 +0530 Subject: [PATCH 07/40] [ES-1908] [ES-1909] New UI: Configurable multiple prefix and single postfix type for the individual ID field in the eSignet login. (#1024) Signed-off-by: GurukiranP --- .../resources/application-default.properties | 11 +- oidc-ui/package-lock.json | 370 ++++++++++++++++- oidc-ui/package.json | 2 + oidc-ui/public/images/asterisk_icon.svg | 3 + oidc-ui/public/images/bio_icon.svg | 2 +- oidc-ui/public/images/email_icon.svg | 4 + oidc-ui/public/images/error_icon.svg | 3 + oidc-ui/public/images/kbi_icon.svg | 9 + oidc-ui/public/images/left_arrow_icon.svg | 11 + oidc-ui/public/images/mobile_icon.svg | 4 + oidc-ui/public/images/nrc_id_icon.svg | 7 + oidc-ui/public/images/otp_icon.svg | 18 +- oidc-ui/public/images/password_hide.svg | 5 +- oidc-ui/public/images/password_show.svg | 4 +- oidc-ui/public/images/pwd_icon.svg | 5 + ...n_in_with_kba.png => sign_in_with_kbi.png} | Bin oidc-ui/public/images/up_down_arrow_icon.svg | 3 + oidc-ui/public/images/vid_icon.svg | 9 + oidc-ui/public/locales/ar.json | 80 +++- oidc-ui/public/locales/en.json | 92 ++++- oidc-ui/public/locales/hi.json | 80 +++- oidc-ui/public/locales/km.json | 80 +++- oidc-ui/public/locales/kn.json | 80 +++- oidc-ui/public/locales/ta.json | 80 +++- oidc-ui/public/theme/variables.css | 245 ++++++----- oidc-ui/src/App.css | 49 ++- oidc-ui/src/common/ErrorBanner.js | 2 +- oidc-ui/src/components/Background.js | 75 ++-- oidc-ui/src/components/Form.js | 77 ++-- oidc-ui/src/components/FormAction.js | 13 +- oidc-ui/src/components/InputWithImage.js | 193 +++++++-- oidc-ui/src/components/InputWithPrefix.js | 273 ++++++++++++ oidc-ui/src/components/L1Biometrics.js | 280 +++++++++---- oidc-ui/src/components/LoginIDOptions.js | 196 +++++++++ oidc-ui/src/components/LoginQRCode.js | 192 +++++---- oidc-ui/src/components/NavHeader.js | 2 +- oidc-ui/src/components/Otp.js | 65 ++- oidc-ui/src/components/OtpGet.js | 298 ++++++++++---- oidc-ui/src/components/OtpVerify.js | 214 +++++----- oidc-ui/src/components/Password.js | 387 +++++++++++++----- oidc-ui/src/components/Pin.js | 352 ++++++++++++---- oidc-ui/src/components/SignInOptions.js | 169 ++++++-- oidc-ui/src/constants/clientConstants.js | 7 +- oidc-ui/src/pages/Login.js | 111 ++--- 44 files changed, 3237 insertions(+), 925 deletions(-) create mode 100644 oidc-ui/public/images/asterisk_icon.svg create mode 100644 oidc-ui/public/images/email_icon.svg create mode 100644 oidc-ui/public/images/error_icon.svg create mode 100644 oidc-ui/public/images/kbi_icon.svg create mode 100644 oidc-ui/public/images/left_arrow_icon.svg create mode 100644 oidc-ui/public/images/mobile_icon.svg create mode 100644 oidc-ui/public/images/nrc_id_icon.svg create mode 100644 oidc-ui/public/images/pwd_icon.svg rename oidc-ui/public/images/{sign_in_with_kba.png => sign_in_with_kbi.png} (100%) create mode 100644 oidc-ui/public/images/up_down_arrow_icon.svg create mode 100644 oidc-ui/public/images/vid_icon.svg create mode 100644 oidc-ui/src/components/InputWithPrefix.js create mode 100644 oidc-ui/src/components/LoginIDOptions.js diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index fc210a48d..6c27012e2 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -350,7 +350,7 @@ mosip.kernel.keymgr.hsm.health.check.encrypt=true # 2. A new Qrcode will be autogenerated before the expiry of current qr-code, and the time difference in seconds for the same is defined in wallet.qr-code-buffer-in-secs property # 3. If esignet is deployed with MOSIP IDA, then 'resend.otp.delay.secs' must be the same as 'mosip.kernel.otp.expiry-time' -mosip.esignet.ui.wallet.config={{'wallet.name': 'walletName', 'wallet.logo-url': '/images/qr_code.png', 'wallet.download-uri': '#', \ +mosip.esignet.ui.wallet.config={{'wallet.name': 'Inji', 'wallet.logo-url': '/images/qr_code.png', 'wallet.download-uri': '#', \ 'wallet.deep-link-uri': 'inji://landing-page-name?linkCode=LINK_CODE&linkExpireDateTime=LINK_EXPIRE_DT' }} mosip.signup.domain.url=https://${mosip.signup.host} @@ -374,6 +374,12 @@ mosip.esignet.ui.config.username.prefix= mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text +mosip.esignet.ui.config.login-id.options={ \ + { "id": "mobile", "svg": "mobile_icon", "prefixes": [{"label": "IND", "value": "+91", "maxLength": "", "regex": ""}, {"label": "KHM", "value": "+855"}, {"label": "USA", "value": "+1"}], "postfix": "@phone", "maxLength": "", "regex": "" }, \ + { "id": "nrc", "svg": "nrc_id_icon", "prefixes": "", "postfix": "@NRC", "maxLength": "", "regex": "" }, \ + { "id": "vid", "svg": "vid_icon", "prefixes": "", "postfix": "@ID", "maxLength": "", "regex": "" }, \ + { "id": "email", "svg": "email_icon", "prefixes": "", "postfix": "@email", "maxLength": "", "regex": "" } \ +} mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ @@ -397,7 +403,8 @@ mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.id 'eKYC-steps.config': '${mosip.esignet.ui.eKYC-steps.url}', \ 'error.banner.close-timer': 10,\ 'auth.factor.kbi.individual-id-field' : '${mosip.esignet.authenticator.default.auth-factor.kbi.individual-id-field}',\ - 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}} + 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}, \ + 'login-id.options': ${mosip.esignet.ui.config.login-id.options}} ##-------------------------------------------- Default Integrations ----------------------------------------------------- diff --git a/oidc-ui/package-lock.json b/oidc-ui/package-lock.json index ce63ddfce..c1d8a0cdc 100644 --- a/oidc-ui/package-lock.json +++ b/oidc-ui/package-lock.json @@ -13,20 +13,22 @@ "@radix-ui/react-popover": "^1.0.7", "@tailwindcss/line-clamp": "^0.4.2", "@tanstack/react-query": "^5.8.4", - "axios": "^1.4.0", + "axios": "^1.7.3", "buffer": "^6.0.3", "cra-template": "1.1.3", "crypto-js": "^4.1.1", "i18next": "^21.10.0", "i18next-browser-languagedetector": "^7.0.0", "i18next-http-backend": "^2.0.1", + "iso-3166-1": "^2.1.1", "jose": "^4.9.3", "qrcode": "^1.5.1", "react": "^18.2.0", + "react-country-flag": "^3.1.0", "react-dom": "^18.2.0", "react-google-recaptcha": "^2.1.0", "react-i18next": "^11.18.6", - "react-pin-input": "^1.3.0", + "react-pin-input": "^1.3.1", "react-router-dom": "^6.3.0", "react-scripts": "5.0.0", "react-select": "^5.4.0", @@ -34,12 +36,20 @@ "secure-biometric-interface-integrator": "^0.9.1" }, "devDependencies": { + "@testing-library/jest-dom": "^6.4.5", + "@testing-library/react": "^16.0.0", "autoprefixer": "^10.4.8", "postcss": "^8.4.16", "tailwindcss": "^3.1.8", "tailwindcss-dir": "^4.0.0" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.1.tgz", + "integrity": "sha512-12WGKBQzjUAI4ayyF4IAtfw2QR/IDoqk6jTddXDhtYTJF9ASmoE1zst7cVtP0aL/F1jUJL5r+JxKXKEgHNbEUQ==", + "dev": true + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -4200,6 +4210,241 @@ } } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "peer": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -4216,6 +4461,13 @@ "node": ">=10.13.0" } }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "peer": true + }, "node_modules/@types/babel__core": { "version": "7.1.19", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", @@ -5325,11 +5577,11 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.8.tgz", + "integrity": "sha512-Uu0wb7KNqK2t5K+YQyVCLM76prD5sRFjKHbJYCP1J7JFGEQ6nN7HWn9+04LAeiJ3ji54lgS/gZCH1oxyrf1SPw==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -6634,6 +6886,12 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, "node_modules/cssdb": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", @@ -6909,6 +7167,16 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -7042,6 +7310,13 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "peer": true + }, "node_modules/dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -8443,9 +8718,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -9400,6 +9675,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -9755,6 +10039,11 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/iso-3166-1": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/iso-3166-1/-/iso-3166-1-2.1.1.tgz", + "integrity": "sha512-RZxXf8cw5Y8LyHZIwIRvKw8sWTIHh2/txBT+ehO0QroesVfnz3JNFFX4i/OC/Yuv2bDIVYrHna5PMvjtpefq5w==" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", @@ -12200,6 +12489,16 @@ "node": ">=10" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -12335,6 +12634,15 @@ "node": ">=6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", @@ -14733,6 +15041,17 @@ "react": ">=16.4.1" } }, + "node_modules/react-country-flag": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-country-flag/-/react-country-flag-3.1.0.tgz", + "integrity": "sha512-JWQFw1efdv9sTC+TGQvTKXQg1NKbDU2mBiAiRWcKM9F1sK+/zjhP2yGmm8YDddWyZdXVkR8Md47rPMJmo4YO5g==", + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": ">=16" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -14895,9 +15214,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-pin-input": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/react-pin-input/-/react-pin-input-1.3.0.tgz", - "integrity": "sha512-9OEQIF4Wx3yDQzSoGq7HIiun9ViMK5jL5hxOKM7oBz9WSCYB/Q5ceurN4h+xnq05euK7s3bU9FHPaPRbMoOi+w==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/react-pin-input/-/react-pin-input-1.3.1.tgz", + "integrity": "sha512-/xlrm2GDryrZfZe2DMA8EadxXeOMzkx7lcvQaHFPWbw/v2f9mMbM+tYPgeBsqex1omKMtjZbT3XCqDpBrummpw==", "peerDependencies": { "prop-types": "^15.6.0", "react": "^0.14.0 || ^15.4.2 || ^16.2.0 || ^17.0.0 || ^18.0.0", @@ -15184,6 +15503,19 @@ "node": ">=6.0.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/reduce-css-calc": { "version": "2.1.8", "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", @@ -16248,6 +16580,18 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -18265,4 +18609,4 @@ } } } -} \ No newline at end of file +} diff --git a/oidc-ui/package.json b/oidc-ui/package.json index c57777050..3cbd834c9 100644 --- a/oidc-ui/package.json +++ b/oidc-ui/package.json @@ -15,9 +15,11 @@ "i18next": "^21.10.0", "i18next-browser-languagedetector": "^7.0.0", "i18next-http-backend": "^2.0.1", + "iso-3166-1": "^2.1.1", "jose": "^4.9.3", "qrcode": "^1.5.1", "react": "^18.2.0", + "react-country-flag": "^3.1.0", "react-dom": "^18.2.0", "react-google-recaptcha": "^2.1.0", "react-i18next": "^11.18.6", diff --git a/oidc-ui/public/images/asterisk_icon.svg b/oidc-ui/public/images/asterisk_icon.svg new file mode 100644 index 000000000..ca008482f --- /dev/null +++ b/oidc-ui/public/images/asterisk_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/oidc-ui/public/images/bio_icon.svg b/oidc-ui/public/images/bio_icon.svg index e1073885a..1bcd50bab 100644 --- a/oidc-ui/public/images/bio_icon.svg +++ b/oidc-ui/public/images/bio_icon.svg @@ -1,4 +1,4 @@ - + diff --git a/oidc-ui/public/images/email_icon.svg b/oidc-ui/public/images/email_icon.svg new file mode 100644 index 000000000..0fa769557 --- /dev/null +++ b/oidc-ui/public/images/email_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/oidc-ui/public/images/error_icon.svg b/oidc-ui/public/images/error_icon.svg new file mode 100644 index 000000000..bc2aa6b80 --- /dev/null +++ b/oidc-ui/public/images/error_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/oidc-ui/public/images/kbi_icon.svg b/oidc-ui/public/images/kbi_icon.svg new file mode 100644 index 000000000..50cb38c84 --- /dev/null +++ b/oidc-ui/public/images/kbi_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/oidc-ui/public/images/left_arrow_icon.svg b/oidc-ui/public/images/left_arrow_icon.svg new file mode 100644 index 000000000..055f41dc8 --- /dev/null +++ b/oidc-ui/public/images/left_arrow_icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/oidc-ui/public/images/mobile_icon.svg b/oidc-ui/public/images/mobile_icon.svg new file mode 100644 index 000000000..4f988090f --- /dev/null +++ b/oidc-ui/public/images/mobile_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/oidc-ui/public/images/nrc_id_icon.svg b/oidc-ui/public/images/nrc_id_icon.svg new file mode 100644 index 000000000..fc1742009 --- /dev/null +++ b/oidc-ui/public/images/nrc_id_icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/oidc-ui/public/images/otp_icon.svg b/oidc-ui/public/images/otp_icon.svg index 2469af443..4c0aa4ed9 100644 --- a/oidc-ui/public/images/otp_icon.svg +++ b/oidc-ui/public/images/otp_icon.svg @@ -1,13 +1,7 @@ - - - - - - - - - - - - + + + + + + diff --git a/oidc-ui/public/images/password_hide.svg b/oidc-ui/public/images/password_hide.svg index b30710edb..7f7f6d289 100644 --- a/oidc-ui/public/images/password_hide.svg +++ b/oidc-ui/public/images/password_hide.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/oidc-ui/public/images/password_show.svg b/oidc-ui/public/images/password_show.svg index f9ec36f3c..3360fd9b7 100644 --- a/oidc-ui/public/images/password_show.svg +++ b/oidc-ui/public/images/password_show.svg @@ -1,3 +1,3 @@ - - + + diff --git a/oidc-ui/public/images/pwd_icon.svg b/oidc-ui/public/images/pwd_icon.svg new file mode 100644 index 000000000..1feca4771 --- /dev/null +++ b/oidc-ui/public/images/pwd_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/oidc-ui/public/images/sign_in_with_kba.png b/oidc-ui/public/images/sign_in_with_kbi.png similarity index 100% rename from oidc-ui/public/images/sign_in_with_kba.png rename to oidc-ui/public/images/sign_in_with_kbi.png diff --git a/oidc-ui/public/images/up_down_arrow_icon.svg b/oidc-ui/public/images/up_down_arrow_icon.svg new file mode 100644 index 000000000..49d028e98 --- /dev/null +++ b/oidc-ui/public/images/up_down_arrow_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/oidc-ui/public/images/vid_icon.svg b/oidc-ui/public/images/vid_icon.svg new file mode 100644 index 000000000..2b3474f92 --- /dev/null +++ b/oidc-ui/public/images/vid_icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/oidc-ui/public/locales/ar.json b/oidc-ui/public/locales/ar.json index d9b5bbaa8..86247e58b 100644 --- a/oidc-ui/public/locales/ar.json +++ b/oidc-ui/public/locales/ar.json @@ -61,7 +61,14 @@ "Face": "وجه", "Finger": "إصبع", "Iris": "قزحية", - "select_a_device": "حدد جهازًا" + "select_a_device": "حدد جهازًا", + "invalid_input": "الرجاء إدخال {{id}} صالح", + "mobile": "رقم الهاتف المحمول", + "nrc": "معرف المجلس النرويجي للاجئين", + "vid": "فيد", + "email": "بريد إلكتروني", + "login_with_id": "تسجيل الدخول باستخدام {{currentID}}", + "multiple_login_ids": "حدد خيار معرف تسجيل الدخول" }, "pin": { "sign_in_with_pin": "تسجيل الدخول باستخدام رقم التعريف الشخصي", @@ -70,7 +77,14 @@ "uin_label_text": "UIN/VID", "pin_label_text": "دبوس", "uin_placeholder": "أدخل رقم UIN أو VID الخاص بك", - "pin_placeholder": "أدخل رقم التعريف الشخصي" + "pin_placeholder": "أدخل رقم التعريف الشخصي", + "invalid_input": "الرجاء إدخال {{id}} صالح", + "mobile": "رقم الهاتف المحمول", + "nrc": "معرف المجلس النرويجي للاجئين", + "vid": "فيد", + "email": "بريد إلكتروني", + "login_with_id": "تسجيل الدخول باستخدام {{currentID}}", + "multiple_login_ids": "حدد خيار معرف تسجيل الدخول" }, "password": { "sign_in_with_password": "تسجيل الدخول بكلمة المرور", @@ -81,7 +95,14 @@ "uin_placeholder": "أدخل رقم UIN أو VID الخاص بك", "password_placeholder": "أدخل كلمة المرور", "forgot_password": "هل نسيت كلمة السر؟", - "reset_password": "إعادة تعيين كلمة المرور" + "reset_password": "إعادة تعيين كلمة المرور", + "invalid_input": "الرجاء إدخال {{id}} صالح", + "mobile": "رقم الهاتف المحمول", + "nrc": "معرف المجلس النرويجي للاجئين", + "vid": "فيد", + "email": "بريد إلكتروني", + "login_with_id": "تسجيل الدخول باستخدام {{currentID}}", + "multiple_login_ids": "حدد خيار معرف تسجيل الدخول" }, "Form": { "sign_in_with_details": "تسجيل الدخول بالتفاصيل", @@ -91,7 +112,8 @@ "dob_label_text": "أدخل تاريخ الميلاد", "policyNumber_placeholder": "رقم السياسة", "fullName_placeholder": "الاسم الكامل", - "dob_placeholder": "تاريخ الميلاد" + "dob_placeholder": "تاريخ الميلاد", + "login_with_kbi": "تسجيل الدخول مع KBI" }, "LoginQRCode": { "scan_with_wallet": "مسح مع {{walletName}} لتسجيل الدخول", @@ -100,7 +122,8 @@ "link_code_status": "حالة رمز الارتباط", "failed": "غير قادر على تسجيل الدخول. ", "refresh": "تحديث رمز الاستجابة السريعة", - "open_wallet_app": "يفتح {{walletName}} برنامج" + "open_wallet_app": "يفتح {{walletName}} برنامج", + "login_with_id": "تسجيل الدخول باستخدام {{currentID}}" }, "otp": { "sign_in_with_otp": "تسجيل الدخول باستخدام OTP", @@ -114,15 +137,24 @@ "otp_placeholder": "1234", "mobile_number": "رقم الهاتف المحمول", "email_address": "عنوان البريد الإلكتروني", - "otp_sent_msg": "تم إرسال OTP إلى المسجل الخاص بك {{otpChannels}}", - "resend_otp_counter": "إعادة إرسال OTP في {{min}}:{{sec}}", + "otp_sent_msg": "الرجاء إدخال {{otpLength}} المكون من رقم OTP المرسل إليه", + "resend_otp_counter": "يمكنك إعادة إرسال كلمة المرور لمرة واحدة (OTP) في {{min}}:{{sec}}", "mobile_placeholder": "رقم الهاتف المحمول: {{mobileNumber}}", "email_placeholder": "عنوان البريد الإلكتروني: {{emailAddress}}", - "mobile_email_placeholder": "رقم الهاتف المحمول: {{mobileNumber}} وعنوان البريد الإلكتروني: {{emailAddress}}" + "mobile_email_placeholder": "رقم الهاتف المحمول: {{mobileNumber}} وعنوان البريد الإلكتروني: {{emailAddress}}", + "invalid_input": "الرجاء إدخال {{id}} صالح", + "mobile": "رقم الهاتف المحمول", + "nrc": "معرف المجلس النرويجي للاجئين", + "vid": "فيد", + "email": "بريد إلكتروني", + "login_with_id": "تسجيل الدخول باستخدام {{currentID}}", + "multiple_login_ids": "حدد خيار معرف تسجيل الدخول", + "enter_otp": "أدخل كلمة المرور لمرة واحدة", + "and": "و" }, "signInOption": { "login_option_heading": "اختر الطريقة التي تريد تسجيل الدخول بها:", - "login_with": "تسجيل الدخول مع {{option}}", + "login_with_id": "تسجيل الدخول باستخدام {{option}}", "PIN": "دبوس", "Biometrics": "القياسات الحيوية", "BIO": "القياسات الحيوية", @@ -143,7 +175,7 @@ "wellknown_api": ".wellknown API" }, "header": { - "login_heading": "تسجيل الدخول", + "login_heading": "تسجيل الدخول باستخدام eSignet", "login_subheading": "الرجاء إدخال اسم المستخدم وكلمة المرور المسجلين لديك", "login_linkName": "التسجيل المسبق", "more_ways_to_sign_in": "المزيد من الطرق لتسجيل الدخول", @@ -154,6 +186,7 @@ "language": "لغة", "noAccount": "ليس لديك حساب؟", "signup_for_unified_login": "قم بالتسجيل باستخدام تسجيل الدخول الموحد", + "subheading": "يطلب {{clientName}} معلوماتك", "subheader_text": { "all_login_options": "الرجاء اختيار خيار تسجيل الدخول", "otp_login": "الرجاء إدخال UIN/VID الخاص بك", @@ -187,7 +220,8 @@ "otp_info": "أدخل UIN/VID الذي تم إنشاؤه", "bio_info": "أدخل UIN/VID الذي تم إنشاؤه", "pin_info": "أدخل UIN/VID الذي تم إنشاؤه", - "username_info": "أدخل UIN/VID الذي تم إنشاؤه" + "username_info": "أدخل UIN/VID الذي تم إنشاؤه", + "caps_on": "مفتاح Caps Lock قيد التشغيل" }, "consentDetails": { "essential_claims": "المطالبات الأساسية", @@ -229,6 +263,28 @@ "popup_body": "{{clientName}} يتطلب الوصول من أجل المضي قدمًا، هل مازلت ترغب في التوقف؟", "consent_details_rejected": "آسفون! " }, + "loginOptions": { + "buttons": { + "mobile": "رقم الهاتف المحمول", + "nrc": "معرف المجلس النرويجي للاجئين", + "vid": "فيد", + "email": "بريد إلكتروني" + }, + "input": { + "label": { + "mobile": "رقم الهاتف المحمول", + "nrc": "معرف المجلس النرويجي للاجئين", + "vid": "فيد", + "email": "بريد إلكتروني" + }, + "placeholder": { + "mobile": "أدخل رقم الجوال", + "nrc": "أدخل معرف المجلس النرويجي للاجئين", + "vid": "أدخل فيد", + "email": "أدخل البريد الإلكتروني هنا" + } + } + }, "errors": { "0": "نجاح", "100": "لم يتم تسجيل الجهاز البيومتري. ", @@ -594,4 +650,4 @@ "511": "يحتاج العميل إلى المصادقة للوصول إلى الشبكة" } } -} \ No newline at end of file +} diff --git a/oidc-ui/public/locales/en.json b/oidc-ui/public/locales/en.json index d5334fa80..3032e966b 100644 --- a/oidc-ui/public/locales/en.json +++ b/oidc-ui/public/locales/en.json @@ -61,7 +61,14 @@ "Face": "Face", "Finger": "Finger", "Iris": "Iris", - "select_a_device": "Select a Device" + "select_a_device": "Select a Device", + "invalid_input": "Please Enter Valid {{id}}", + "mobile": "Mobile Number", + "nrc": "NRC ID", + "vid": "VID", + "email": "Email", + "login_with_id": "Login with {{currentID}}", + "multiple_login_ids": "Select a Login ID Option" }, "pin": { "sign_in_with_pin": "Login with PIN", @@ -70,7 +77,14 @@ "uin_label_text": "UIN/VID", "pin_label_text": "PIN", "uin_placeholder": "Enter Your UIN or VID", - "pin_placeholder": "Enter PIN" + "pin_placeholder": "Enter PIN", + "invalid_input": "Please Enter Valid {{id}}", + "mobile": "Mobile Number", + "nrc": "NRC ID", + "vid": "VID", + "email": "Email", + "login_with_id": "Login with {{currentID}}", + "multiple_login_ids": "Select a Login ID Option" }, "password": { "sign_in_with_password": "Login with Password", @@ -79,9 +93,16 @@ "uin_label_text": "UIN/VID", "password_label_text": "Password", "uin_placeholder": "Enter Your UIN or VID", - "password_placeholder": "Enter password", + "password_placeholder": "Enter Password", "forgot_password": "Forgot Password?", - "reset_password": "Reset Password" + "reset_password": "Reset Password", + "invalid_input": "Please Enter Valid {{id}}", + "mobile": "Mobile Number", + "nrc": "NRC ID", + "vid": "VID", + "email": "Email", + "login_with_id": "Login with {{currentID}}", + "multiple_login_ids": "Select a Login ID Option" }, "Form": { "sign_in_with_details": "Login with Details", @@ -91,7 +112,8 @@ "dob_label_text": "Enter DOB", "policyNumber_placeholder": "Policy Number", "fullName_placeholder": "Full Name", - "dob_placeholder": "DOB" + "dob_placeholder": "DOB", + "login_with_kbi": "Login with KBI" }, "LoginQRCode": { "scan_with_wallet": "Scan with {{walletName}} to login", @@ -100,7 +122,8 @@ "link_code_status": "Link Code Status", "failed": "Unable to login. Please scan QR code and try again.", "refresh": "Refresh QR Code", - "open_wallet_app": "Open {{walletName}} App" + "open_wallet_app": "Open {{walletName}} App", + "login_with_id": "Login with {{currentID}}" }, "otp": { "sign_in_with_otp": "Login with OTP", @@ -114,15 +137,24 @@ "otp_placeholder": "1234", "mobile_number": "Mobile Number", "email_address": "Email Address", - "otp_sent_msg": "OTP has been sent to your registered {{otpChannels}}", - "resend_otp_counter": "Resend OTP in {{min}}:{{sec}}", + "otp_sent_msg": "Please enter the {{otpLength}}-digit OTP sent to ", + "resend_otp_counter": "You can resend the OTP in {{min}}:{{sec}}", "mobile_placeholder": "Mobile Number: {{mobileNumber}}", "email_placeholder": "Email Address: {{emailAddress}}", - "mobile_email_placeholder": "Mobile Number: {{mobileNumber}} and Email Address: {{emailAddress}}" + "mobile_email_placeholder": "Mobile Number: {{mobileNumber}} and Email Address: {{emailAddress}}", + "invalid_input": "Please Enter Valid {{id}}", + "mobile": "Mobile Number", + "nrc": "NRC ID", + "vid": "VID", + "email": "Email", + "login_with_id": "Login with {{currentID}}", + "multiple_login_ids": "Select a Login ID Option", + "enter_otp": "Enter OTP", + "and": "and" }, "signInOption": { "login_option_heading": "Choose How You Want to Login:", - "login_with": "Login with {{option}}", + "login_with_id": "Login with {{option}}", "PIN": "PIN", "Biometrics": "Biometrics", "BIO": "Biometrics", @@ -134,7 +166,7 @@ "more_ways_to_sign_in": "More ways to sign in", "or": "OR", "sign_in_with": "Verify using {{idProviderName}}", - "verify_using_national_id":"Verify using other National IDs", + "verify_using_national_id": "Verify using other National IDs", "register": "Register" }, "esignetDetails": { @@ -143,7 +175,7 @@ "wellknown_api": ".wellknown API" }, "header": { - "login_heading": "Login", + "login_heading": "Login with eSignet", "login_subheading": "Please enter your registered username and password", "login_linkName": "Preregister", "more_ways_to_sign_in": "More Ways to Login", @@ -154,6 +186,7 @@ "language": "Language", "noAccount": "Don't have an account?", "signup_for_unified_login": "Sign Up with Unified Login", + "subheading": "{{clientName}} is asking for your Information", "subheader_text": { "all_login_options": "Please choose the login option", "otp_login": "Please enter your UIN/VID", @@ -187,7 +220,8 @@ "otp_info": "Enter your generated UIN/VID", "bio_info": "Enter your generated UIN/VID", "pin_info": "Enter your generated UIN/VID", - "username_info": "Enter your generated UIN/VID" + "username_info": "Enter your generated UIN/VID", + "caps_on": "Caps lock is on" }, "consentDetails": { "essential_claims": "Essential Claims", @@ -229,6 +263,28 @@ "popup_body": "{{clientName}} requires access in order to proceed further, do you still wish to discontinue?", "consent_details_rejected": "We’re sorry! Your login was unsuccessful as consent was not shared." }, + "loginOptions": { + "buttons": { + "mobile": "Mobile Number", + "nrc": "NRC ID", + "vid": "VID", + "email": "Email" + }, + "input": { + "label": { + "mobile": "Mobile Number", + "nrc": "NRC ID", + "vid": "VID", + "email": "Email" + }, + "placeholder": { + "mobile": "Enter Mobile Number", + "nrc": "Enter NRC ID", + "vid": "Enter VID", + "email": "Enter Email Here" + } + } + }, "errors": { "invalid_policyNumber": "Please Enter Valid Policy Number.", "invalid_fullName": "Please Enter Valid Name.", @@ -237,8 +293,8 @@ "page_not_exist": "Oops! The page you are looking for does not exist.", "something_went_wrong": "Something went wrong!", "experts_working": "Our team is working to resolve the issue.", - "username_not_valid": "Please Enter Valid Username.", - "password_not_valid": "Please Enter Valid Password.", + "username_not_valid": "Please Enter Valid Username", + "password_not_valid": "Please Enter Valid Password", "no_response_msg": "No response from Server. Please try again.", "no_devices_found_msg": "Device not found. Please check your connectivity and try again.", "link_code_status_failed": "Failed to Load", @@ -342,8 +398,8 @@ "captcha_validator_not_found": "Unable to validate captcha. Please try again.", "invalid_captcha": "The captcha you entered is incorrect. Please try again.", "invalid_auth_factor_type_or_challenge_format": "Invalid auth factor type or challenge format.", - "invalid_auth_factor_type_format" : "Invalid auth factor type or challenge format.", - "invalid_challenge_length" : "Invalid challenge, Min or Max length constraint violated.", + "invalid_auth_factor_type_format": "Invalid auth factor type or challenge format.", + "invalid_challenge_length": "Invalid challenge, Min or Max length constraint violated.", "unsupported_pkce_challenge_method": "Unsupported proof key challenge exchange method.", "invalid_pkce_challenge": "Invalid Proof key challenge.", "invalid_pkce_code_verifier": "Invalid proof key code verifier.", @@ -363,7 +419,7 @@ "proof_invalid_iat": "Invalid value in proof payload iat claim.", "proof_invalid_nonce": "Invalid value in proof payload nonce claim.", "no_attempts_left": "The limit for authentication attempts has been reached. Please try again later.", - "individual_id_blocked": "The user has been temporarily blocked. Please contact support for further assistance.", + "individual_id_blocked": "The user has been temporarily blocked. Please contact support for further assistance.", "invalid_state_response": "Invalid state", "authCode_missing": "AuthCode Missing", "failed_to_validate_captcha": "Unable to validate captcha. Please try again.", diff --git a/oidc-ui/public/locales/hi.json b/oidc-ui/public/locales/hi.json index 70ca0ab49..ef6af6d53 100644 --- a/oidc-ui/public/locales/hi.json +++ b/oidc-ui/public/locales/hi.json @@ -61,7 +61,14 @@ "Face": "चेहरा", "Finger": "उँगलिया", "Iris": "आईरिस", - "select_a_device": "एक डिवाइस चुनें" + "select_a_device": "एक डिवाइस चुनें", + "invalid_input": "कृपया वैध {{id}} दर्ज करें", + "mobile": "मोबाइल नंबर", + "nrc": "एनआरसी आईडी", + "vid": "वी.आई.डी", + "email": "ईमेल", + "login_with_id": "{{currentID}} से लॉगिन करें", + "multiple_login_ids": "एक लॉगिन आईडी विकल्प चुनें" }, "pin": { "sign_in_with_pin": "पिन से लॉगिन करें", @@ -70,7 +77,14 @@ "uin_label_text": "यूआईएन/वीआईडी", "pin_label_text": "नत्थी करना", "uin_placeholder": "अपना यूआईएन या वीआईडी ​​दर्ज करें", - "pin_placeholder": "पिन दर्ज करें" + "pin_placeholder": "पिन दर्ज करें", + "invalid_input": "कृपया वैध {{id}} दर्ज करें", + "mobile": "मोबाइल नंबर", + "nrc": "एनआरसी आईडी", + "vid": "वी.आई.डी", + "email": "ईमेल", + "login_with_id": "{{currentID}} से लॉगिन करें", + "multiple_login_ids": "एक लॉगिन आईडी विकल्प चुनें" }, "password": { "sign_in_with_password": "पासवर्ड से लॉगिन करें", @@ -81,7 +95,14 @@ "uin_placeholder": "अपना यूआईएन या वीआईडी ​​दर्ज करें", "password_placeholder": "पास वर्ड दर्ज करें", "forgot_password": "पासवर्ड भूल गए?", - "reset_password": "पासवर्ड रीसेट" + "reset_password": "पासवर्ड रीसेट", + "invalid_input": "कृपया वैध {{id}} दर्ज करें", + "mobile": "मोबाइल नंबर", + "nrc": "एनआरसी आईडी", + "vid": "वी.आई.डी", + "email": "ईमेल", + "login_with_id": "{{currentID}} से लॉगिन करें", + "multiple_login_ids": "एक लॉगिन आईडी विकल्प चुनें" }, "Form": { "sign_in_with_details": "विवरण के साथ लॉगिन करें", @@ -91,7 +112,8 @@ "dob_label_text": "जन्मतिथि दर्ज करें", "policyNumber_placeholder": "नीति संख्या", "fullName_placeholder": "पूरा नाम", - "dob_placeholder": "जन्म तिथि" + "dob_placeholder": "जन्म तिथि", + "login_with_kbi": "KBI के साथ लॉगिन करें" }, "LoginQRCode": { "scan_with_wallet": "के साथ स्कैन करें {{walletName}} लॉगिन करने के लिए", @@ -100,7 +122,8 @@ "link_code_status": "लिंक कोड स्थिति", "failed": "लॉगिन करने में असमर्थ। ", "refresh": "क्यूआर कोड रिफ्रेश करें", - "open_wallet_app": "खुला {{walletName}} अनुप्रयोग" + "open_wallet_app": "खुला {{walletName}} अनुप्रयोग", + "login_with_id": "{{currentID}} से लॉगिन करें" }, "otp": { "sign_in_with_otp": "ओटीपी से लॉगइन करें", @@ -114,15 +137,24 @@ "otp_placeholder": "1234", "mobile_number": "मोबाइल नंबर", "email_address": "मेल पता", - "otp_sent_msg": "आपके पंजीकृत पर ओटीपी भेज दिया गया है {{otpChannels}}", - "resend_otp_counter": "ओटीपी पुनः भेजें {{min}}:{{sec}}", + "otp_sent_msg": "कृपया भेजा गया {{otpLength}}-अंकीय ओटीपी दर्ज करें", + "resend_otp_counter": "आप ओटीपी को दोबारा भेज सकते हैं {{min}}:{{sec}}", "mobile_placeholder": "मोबाइल नंबर: {{mobileNumber}}", "email_placeholder": "मेल पता: {{emailAddress}}", - "mobile_email_placeholder": "मोबाइल नंबर: {{mobileNumber}} और ईमेल पता: {{emailAddress}}" + "mobile_email_placeholder": "मोबाइल नंबर: {{mobileNumber}} और ईमेल पता: {{emailAddress}}", + "invalid_input": "कृपया वैध {{id}} दर्ज करें", + "mobile": "मोबाइल नंबर", + "nrc": "एनआरसी आईडी", + "vid": "वी.आई.डी", + "email": "ईमेल", + "login_with_id": "{{currentID}} से लॉगिन करें", + "multiple_login_ids": "एक लॉगिन आईडी विकल्प चुनें", + "enter_otp": "ओटीपी दर्ज करें", + "and": "और" }, "signInOption": { "login_option_heading": "चुनें कि आप कैसे लॉगिन करना चाहते हैं:", - "login_with": "साथ प्रवेश करना {{option}}", + "login_with_id": "{{option}} से लॉगिन करें", "PIN": "नत्थी करना", "Biometrics": "बॉयोमेट्रिक्स", "BIO": "बॉयोमेट्रिक्स", @@ -143,7 +175,7 @@ "wellknown_api": ".सुप्रसिद्ध एपीआई" }, "header": { - "login_heading": "लॉग इन करें", + "login_heading": "ई-सिग्नेट से लॉगिन करें", "login_subheading": "कृपया अपना पंजीकृत उपयोगकर्ता नाम और पासवर्ड दर्ज करें", "login_linkName": "पूर्वपंजीकरण", "more_ways_to_sign_in": "लॉगिन करने के और भी तरीके", @@ -154,6 +186,7 @@ "language": "भाषा", "noAccount": "कोई खाता नहीं है?", "signup_for_unified_login": "एकीकृत लॉगिन के साथ साइन अप करें", + "subheading": "{{clientName}} आपकी जानकारी मांग रहा है", "subheader_text": { "all_login_options": "कृपया लॉगिन विकल्प चुनें", "otp_login": "कृपया अपना यूआईएन/वीआईडी ​​दर्ज करें", @@ -187,7 +220,8 @@ "otp_info": "अपना जनरेट किया गया यूआईएन/वीआईडी ​​दर्ज करें", "bio_info": "अपना जनरेट किया गया यूआईएन/वीआईडी ​​दर्ज करें", "pin_info": "अपना जनरेट किया गया यूआईएन/वीआईडी ​​दर्ज करें", - "username_info": "अपना जनरेट किया गया यूआईएन/वीआईडी ​​दर्ज करें" + "username_info": "अपना जनरेट किया गया यूआईएन/वीआईडी ​​दर्ज करें", + "caps_on": "कैप्स लॉक ऑन है" }, "consentDetails": { "essential_claims": "आवश्यक दावे", @@ -229,6 +263,28 @@ "popup_body": "{{clientName}} आगे बढ़ने के लिए पहुंच की आवश्यकता है, क्या आप अब भी इसे बंद करना चाहते हैं?", "consent_details_rejected": "हम क्षमा चाहते हैं! " }, + "loginOptions": { + "buttons": { + "mobile": "मोबाइल नंबर", + "nrc": "एनआरसी आईडी", + "vid": "वी.आई.डी", + "email": "ईमेल" + }, + "input": { + "label": { + "mobile": "मोबाइल नंबर", + "nrc": "एनआरसी आईडी", + "vid": "वी.आई.डी", + "email": "ईमेल" + }, + "placeholder": { + "mobile": "मोबाइल नंबर दर्ज करें", + "nrc": "एनआरसी आईडी दर्ज करें", + "vid": "वीआईडी ​​दर्ज करें", + "email": "यहां ईमेल दर्ज करें" + } + } + }, "errors": { "0": "सफलता", "100": "बायोमेट्रिक डिवाइस पंजीकृत नहीं है. ", @@ -594,4 +650,4 @@ "511": "नेटवर्क पहुंच प्राप्त करने के लिए क्लाइंट को प्रमाणित करने की आवश्यकता है" } } -} \ No newline at end of file +} diff --git a/oidc-ui/public/locales/km.json b/oidc-ui/public/locales/km.json index edbfb7fe9..5a509fa0a 100644 --- a/oidc-ui/public/locales/km.json +++ b/oidc-ui/public/locales/km.json @@ -61,7 +61,14 @@ "Face": "មុខ", "Finger": "ម្រាមដៃ", "Iris": "អាយរីស", - "select_a_device": "ជ្រើសរើសឧបករណ៍" + "select_a_device": "ជ្រើសរើសឧបករណ៍", + "invalid_input": "សូមបញ្ចូលសុពលភាព {{id}}", + "mobile": "លេខទូរសព្ទ", + "nrc": "លេខសម្គាល់ NRC", + "vid": "វីឌី", + "email": "អ៊ីមែល", + "login_with_id": "ចូលដោយប្រើ {{currentID}}", + "multiple_login_ids": "ជ្រើសរើសជម្រើសលេខសម្គាល់ចូល" }, "pin": { "sign_in_with_pin": "ចូលដោយប្រើកូដ PIN", @@ -70,7 +77,14 @@ "uin_label_text": "UIN/VID", "pin_label_text": "លេខសម្ងាត់", "uin_placeholder": "បញ្ចូល UIN ឬ VID របស់អ្នក។", - "pin_placeholder": "បញ្ចូលកូដ PIN" + "pin_placeholder": "បញ្ចូលកូដ PIN", + "invalid_input": "សូមបញ្ចូលសុពលភាព {{id}}", + "mobile": "លេខទូរសព្ទ", + "nrc": "លេខសម្គាល់ NRC", + "vid": "វីឌី", + "email": "អ៊ីមែល", + "login_with_id": "ចូលដោយប្រើ {{currentID}}", + "multiple_login_ids": "ជ្រើសរើសជម្រើសលេខសម្គាល់ចូល" }, "password": { "sign_in_with_password": "ចូលដោយប្រើពាក្យសម្ងាត់", @@ -81,7 +95,14 @@ "uin_placeholder": "បញ្ចូល UIN ឬ VID របស់អ្នក។", "password_placeholder": "បញ្ចូលពាក្យសម្ងាត់", "forgot_password": "ភ្លេចលេខសម្ងាត់?", - "reset_password": "កំណត់ពាក្យសម្ងាត់ឡើងវិញ" + "reset_password": "កំណត់ពាក្យសម្ងាត់ឡើងវិញ", + "invalid_input": "សូមបញ្ចូលសុពលភាព {{id}}", + "mobile": "លេខទូរសព្ទ", + "nrc": "លេខសម្គាល់ NRC", + "vid": "វីឌី", + "email": "អ៊ីមែល", + "login_with_id": "ចូលដោយប្រើ {{currentID}}", + "multiple_login_ids": "ជ្រើសរើសជម្រើសលេខសម្គាល់ចូល" }, "Form": { "sign_in_with_details": "ចូលជាមួយព័ត៌មានលម្អិត", @@ -91,7 +112,8 @@ "dob_label_text": "បញ្ចូល DOB", "policyNumber_placeholder": "លេខគោលនយោបាយ", "fullName_placeholder": "ឈ្មោះពេញ", - "dob_placeholder": "DOB" + "dob_placeholder": "DOB", + "login_with_kbi": "ចូលជាមួយ KBI" }, "LoginQRCode": { "scan_with_wallet": "ស្កេនជាមួយ {{walletName}} ដើម្បីចូល", @@ -100,7 +122,8 @@ "link_code_status": "ស្ថានភាពលេខកូដភ្ជាប់", "failed": "មិនអាចចូលបានទេ។ ", "refresh": "ផ្ទុកកូដ QR ឡើងវិញ", - "open_wallet_app": "បើក {{walletName}} កម្មវិធី" + "open_wallet_app": "បើក {{walletName}} កម្មវិធី", + "login_with_id": "ចូលដោយប្រើ {{currentID}}" }, "otp": { "sign_in_with_otp": "ចូលជាមួយ OTP", @@ -114,15 +137,24 @@ "otp_placeholder": "១២៣៤", "mobile_number": "លេខទូរសព្ទ", "email_address": "អាសយដ្ឋានអ៊ីមែល", - "otp_sent_msg": "OTP ត្រូវបានផ្ញើទៅកាន់អ្នកដែលបានចុះឈ្មោះ {{otpChannels}}", - "resend_otp_counter": "ផ្ញើ OTP ឡើងវិញ {{min}}:{{sec}}", + "otp_sent_msg": "សូមបញ្ចូលលេខ {{otpLength}}-ខ្ទង់ OTP ដែលបានផ្ញើទៅ", + "resend_otp_counter": "អ្នកអាចផ្ញើ OTP ឡើងវិញ {{min}}:{{sec}}", "mobile_placeholder": "លេខទូរសព្ទ៖ {{mobileNumber}}", "email_placeholder": "អាសយដ្ឋានអ៊ីមែល៖ {{emailAddress}}", - "mobile_email_placeholder": "លេខទូរសព្ទ៖ {{mobileNumber}} និងអាសយដ្ឋានអ៊ីមែល៖ {{emailAddress}}" + "mobile_email_placeholder": "លេខទូរសព្ទ៖ {{mobileNumber}} និងអាសយដ្ឋានអ៊ីមែល៖ {{emailAddress}}", + "invalid_input": "សូមបញ្ចូលសុពលភាព {{id}}", + "mobile": "លេខទូរសព្ទ", + "nrc": "លេខសម្គាល់ NRC", + "vid": "វីឌី", + "email": "អ៊ីមែល", + "login_with_id": "ចូលដោយប្រើ {{currentID}}", + "multiple_login_ids": "ជ្រើសរើសជម្រើសលេខសម្គាល់ចូល", + "enter_otp": "បញ្ចូល OTP", + "and": "និង" }, "signInOption": { "login_option_heading": "ជ្រើសរើសរបៀបដែលអ្នកចង់ចូល៖", - "login_with": "ចូលជាមួយ {{option}}", + "login_with_id": "ចូលដោយប្រើ {{option}}", "PIN": "លេខសម្ងាត់", "Biometrics": "ជីវមាត្រ", "BIO": "ជីវមាត្រ", @@ -143,7 +175,7 @@ "wellknown_api": ".ល្បី API" }, "header": { - "login_heading": "ចូល", + "login_heading": "ចូលជាមួយ eSignet", "login_subheading": "សូមបញ្ចូលឈ្មោះអ្នកប្រើប្រាស់ និងពាក្យសម្ងាត់ដែលបានចុះឈ្មោះរបស់អ្នក។", "login_linkName": "ចុះឈ្មោះជាមុន", "more_ways_to_sign_in": "វិធីច្រើនទៀតដើម្បីចូល", @@ -154,6 +186,7 @@ "language": "ភាសា", "noAccount": "មិនមានគណនីមែនទេ?", "signup_for_unified_login": "ចុះឈ្មោះជាមួយការចូលបង្រួបបង្រួម", + "subheading": "{{clientName}} កំពុងស្នើសុំព័ត៌មានរបស់អ្នក។", "subheader_text": { "all_login_options": "សូមជ្រើសរើសជម្រើសចូល", "otp_login": "សូមបញ្ចូល UIN/VID របស់អ្នក។", @@ -187,7 +220,8 @@ "otp_info": "បញ្ចូល UIN/VID ដែលបានបង្កើតរបស់អ្នក។", "bio_info": "បញ្ចូល UIN/VID ដែលបានបង្កើតរបស់អ្នក។", "pin_info": "បញ្ចូល UIN/VID ដែលបានបង្កើតរបស់អ្នក។", - "username_info": "បញ្ចូល UIN/VID ដែលបានបង្កើតរបស់អ្នក។" + "username_info": "បញ្ចូល UIN/VID ដែលបានបង្កើតរបស់អ្នក។", + "caps_on": "Caps Lock ត្រូវបានបើក" }, "consentDetails": { "essential_claims": "ការទាមទារសំខាន់ៗ", @@ -229,6 +263,28 @@ "popup_body": "{{clientName}} ទាមទារ​ការ​ចូល​ដំណើរ​ការ​ដើម្បី​បន្ត​ទៀត តើ​អ្នក​នៅ​តែ​ចង់​ឈប់​បន្ត​ឬ?", "consent_details_rejected": "យើង​សុំទោស! " }, + "loginOptions": { + "buttons": { + "mobile": "លេខទូរសព្ទ", + "nrc": "លេខសម្គាល់ NRC", + "vid": "វីឌី", + "email": "អ៊ីមែល" + }, + "input": { + "label": { + "mobile": "លេខទូរសព្ទ", + "nrc": "លេខសម្គាល់ NRC", + "vid": "វីឌី", + "email": "អ៊ីមែល" + }, + "placeholder": { + "mobile": "បញ្ចូលលេខទូរស័ព្ទ", + "nrc": "បញ្ចូលលេខសម្គាល់ NRC", + "vid": "បញ្ចូល VID", + "email": "បញ្ចូលអ៊ីមែលនៅទីនេះ" + } + } + }, "errors": { "0": "ជោគជ័យ", "100": "ឧបករណ៍ជីវមាត្រមិនត្រូវបានចុះឈ្មោះទេ។ ", @@ -594,4 +650,4 @@ "511": "អតិថិជនត្រូវការផ្ទៀងផ្ទាត់ ដើម្បីទទួលបានការចូលប្រើបណ្តាញ" } } -} \ No newline at end of file +} diff --git a/oidc-ui/public/locales/kn.json b/oidc-ui/public/locales/kn.json index 4c48f8403..ba5639ccf 100644 --- a/oidc-ui/public/locales/kn.json +++ b/oidc-ui/public/locales/kn.json @@ -61,7 +61,14 @@ "Face": "ಮುಖ", "Finger": "ಬೆರಳು", "Iris": "ಐರಿಸ್", - "select_a_device": "ಸಾಧನವನ್ನು ಆಯ್ಕೆಮಾಡಿ" + "select_a_device": "ಸಾಧನವನ್ನು ಆಯ್ಕೆಮಾಡಿ", + "invalid_input": "ದಯವಿಟ್ಟು ಮಾನ್ಯ {{id}} ನಮೂದಿಸಿ", + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ", + "vid": "ವಿಐಡಿ", + "email": "ಇಮೇಲ್", + "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", + "multiple_login_ids": "ಲಾಗಿನ್ ಐಡಿ ಆಯ್ಕೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ" }, "pin": { "sign_in_with_pin": "PIN ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", @@ -70,7 +77,14 @@ "uin_label_text": "UIN/VID", "pin_label_text": "ಪಿನ್", "uin_placeholder": "ನಿಮ್ಮ UIN ಅಥವಾ VID ನಮೂದಿಸಿ", - "pin_placeholder": "ಪಿನ್ ನಮೂದಿಸಿ" + "pin_placeholder": "ಪಿನ್ ನಮೂದಿಸಿ", + "invalid_input": "ದಯವಿಟ್ಟು ಮಾನ್ಯ {{id}} ನಮೂದಿಸಿ", + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ", + "vid": "ವಿಐಡಿ", + "email": "ಇಮೇಲ್", + "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", + "multiple_login_ids": "ಲಾಗಿನ್ ಐಡಿ ಆಯ್ಕೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ" }, "password": { "sign_in_with_password": "ಪಾಸ್ವರ್ಡ್ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", @@ -81,7 +95,14 @@ "uin_placeholder": "ನಿಮ್ಮ UIN ಅಥವಾ VID ನಮೂದಿಸಿ", "password_placeholder": "ಪಾಸ್ವರ್ಡ್ ನಮೂದಿಸಿ", "forgot_password": "ಪಾಸ್ವರ್ಡ್ ಮರೆತಿರುವಿರಾ?", - "reset_password": "ಪಾಸ್ವರ್ಡ್ ಮರುಹೊಂದಿಸಿ" + "reset_password": "ಪಾಸ್ವರ್ಡ್ ಮರುಹೊಂದಿಸಿ", + "invalid_input": "ದಯವಿಟ್ಟು ಮಾನ್ಯ {{id}} ನಮೂದಿಸಿ", + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ", + "vid": "ವಿಐಡಿ", + "email": "ಇಮೇಲ್", + "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", + "multiple_login_ids": "ಲಾಗಿನ್ ಐಡಿ ಆಯ್ಕೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ" }, "Form": { "sign_in_with_details": "ವಿವರಗಳೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", @@ -91,7 +112,8 @@ "dob_label_text": "DOB ಅನ್ನು ನಮೂದಿಸಿ", "policyNumber_placeholder": "ನೀತಿ ಸಂಖ್ಯೆ", "fullName_placeholder": "ಪೂರ್ಣ ಹೆಸರು", - "dob_placeholder": "DOB" + "dob_placeholder": "DOB", + "login_with_kbi": "KBI ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ" }, "LoginQRCode": { "scan_with_wallet": "ಇದರೊಂದಿಗೆ ಸ್ಕ್ಯಾನ್ ಮಾಡಿ {{walletName}} ಲಾಗಿನ್ ಮಾಡಲು", @@ -100,7 +122,8 @@ "link_code_status": "ಲಿಂಕ್ ಕೋಡ್ ಸ್ಥಿತಿ", "failed": "ಲಾಗಿನ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ", "refresh": "QR ಕೋಡ್ ಅನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಿ", - "open_wallet_app": "ತೆರೆಯಿರಿ {{walletName}} ಅಪ್ಲಿಕೇಶನ್" + "open_wallet_app": "ತೆರೆಯಿರಿ {{walletName}} ಅಪ್ಲಿಕೇಶನ್", + "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ" }, "otp": { "sign_in_with_otp": "OTP ಯೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", @@ -114,15 +137,24 @@ "otp_placeholder": "1234", "mobile_number": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", "email_address": "ಇಮೇಲ್ ವಿಳಾಸ", - "otp_sent_msg": "ನಿಮ್ಮ ನೋಂದಾಯಿತರಿಗೆ OTP ಕಳುಹಿಸಲಾಗಿದೆ {{otpChannels}}", - "resend_otp_counter": "OTP ಅನ್ನು ಮರುಕಳುಹಿಸಿ {{min}}:{{sec}}", + "otp_sent_msg": "ದಯವಿಟ್ಟು ಕಳುಹಿಸಲಾದ {{otpLength}}-ಅಂಕಿಯ OTP ಅನ್ನು ನಮೂದಿಸಿ", + "resend_otp_counter": "ನೀವು OTP ಅನ್ನು ಮತ್ತೆ ಕಳುಹಿಸಬಹುದು {{min}}:{{sec}}", "mobile_placeholder": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ: {{mobileNumber}}", "email_placeholder": "ಇಮೇಲ್ ವಿಳಾಸ: {{emailAddress}}", - "mobile_email_placeholder": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ: {{mobileNumber}} ಮತ್ತು ಇಮೇಲ್ ವಿಳಾಸ: {{emailAddress}}" + "mobile_email_placeholder": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ: {{mobileNumber}} ಮತ್ತು ಇಮೇಲ್ ವಿಳಾಸ: {{emailAddress}}", + "invalid_input": "ದಯವಿಟ್ಟು ಮಾನ್ಯ {{id}} ನಮೂದಿಸಿ", + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ", + "vid": "ವಿಐಡಿ", + "email": "ಇಮೇಲ್", + "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", + "multiple_login_ids": "ಲಾಗಿನ್ ಐಡಿ ಆಯ್ಕೆಯನ್ನು ಆಯ್ಕೆಮಾಡಿ", + "enter_otp": "OTP ನಮೂದಿಸಿ", + "and": "ಮತ್ತು" }, "signInOption": { "login_option_heading": "ನೀವು ಹೇಗೆ ಲಾಗಿನ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಿ ಎಂಬುದನ್ನು ಆರಿಸಿ:", - "login_with": "ಇದರೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ {{option}}", + "login_with_id": "{{option}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", "PIN": "ಪಿನ್", "Biometrics": "ಬಯೋಮೆಟ್ರಿಕ್ಸ್", "BIO": "ಬಯೋಮೆಟ್ರಿಕ್ಸ್", @@ -143,7 +175,7 @@ "wellknown_api": "ಪ್ರಸಿದ್ಧ API" }, "header": { - "login_heading": "ಲಾಗಿನ್ ಮಾಡಿ", + "login_heading": "eSignet ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", "login_subheading": "ದಯವಿಟ್ಟು ನಿಮ್ಮ ನೋಂದಾಯಿತ ಬಳಕೆದಾರಹೆಸರು ಮತ್ತು ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ನಮೂದಿಸಿ", "login_linkName": "ಮುಂಚಿತವಾಗಿ ನೋಂದಾಯಿಸಿ", "more_ways_to_sign_in": "ಲಾಗಿನ್ ಮಾಡಲು ಹೆಚ್ಚಿನ ಮಾರ್ಗಗಳು", @@ -154,6 +186,7 @@ "language": "ಭಾಷೆ", "noAccount": "ಖಾತೆ ಇಲ್ಲವೇ?", "signup_for_unified_login": "ಏಕೀಕೃತ ಲಾಗಿನ್‌ನೊಂದಿಗೆ ಸೈನ್ ಅಪ್ ಮಾಡಿ", + "subheading": "{{clientName}} ನಿಮ್ಮ ಮಾಹಿತಿಯನ್ನು ಕೇಳುತ್ತಿದ್ದಾರೆ", "subheader_text": { "all_login_options": "ದಯವಿಟ್ಟು ಲಾಗಿನ್ ಆಯ್ಕೆಯನ್ನು ಆರಿಸಿ", "otp_login": "ದಯವಿಟ್ಟು ನಿಮ್ಮ UIN/VID ಅನ್ನು ನಮೂದಿಸಿ", @@ -187,7 +220,8 @@ "otp_info": "ನೀವು ರಚಿಸಿದ UIN/VID ಅನ್ನು ನಮೂದಿಸಿ", "bio_info": "ನೀವು ರಚಿಸಿದ UIN/VID ಅನ್ನು ನಮೂದಿಸಿ", "pin_info": "ನೀವು ರಚಿಸಿದ UIN/VID ಅನ್ನು ನಮೂದಿಸಿ", - "username_info": "ನೀವು ರಚಿಸಿದ UIN/VID ಅನ್ನು ನಮೂದಿಸಿ" + "username_info": "ನೀವು ರಚಿಸಿದ UIN/VID ಅನ್ನು ನಮೂದಿಸಿ", + "caps_on": "ಕ್ಯಾಪ್ಸ್ ಲಾಕ್ ಆನ್ ಆಗಿದೆ" }, "consentDetails": { "essential_claims": "ಅಗತ್ಯ ಹಕ್ಕುಗಳು", @@ -229,6 +263,28 @@ "popup_body": "{{clientName}} ಮತ್ತಷ್ಟು ಮುಂದುವರಿಯಲು ಪ್ರವೇಶದ ಅಗತ್ಯವಿದೆ, ನೀವು ಇನ್ನೂ ನಿಲ್ಲಿಸಲು ಬಯಸುವಿರಾ?", "consent_details_rejected": "ನಮ್ಮನ್ನು ಕ್ಷಮಿಸಿ! " }, + "loginOptions": { + "buttons": { + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ", + "vid": "ವಿಐಡಿ", + "email": "ಇಮೇಲ್" + }, + "input": { + "label": { + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ", + "vid": "ವಿಐಡಿ", + "email": "ಇಮೇಲ್" + }, + "placeholder": { + "mobile": "ಮೊಬೈಲ್ ಸಂಖ್ಯೆಯನ್ನು ನಮೂದಿಸಿ", + "nrc": "ಎನ್ಆರ್ಸಿ ಐಡಿ ನಮೂದಿಸಿ", + "vid": "ವಿಐಡಿ ನಮೂದಿಸಿ", + "email": "ಇಲ್ಲಿ ಇಮೇಲ್ ನಮೂದಿಸಿ" + } + } + }, "errors": { "0": "ಯಶಸ್ಸು", "100": "ಬಯೋಮೆಟ್ರಿಕ್ ಸಾಧನವನ್ನು ನೋಂದಾಯಿಸಲಾಗಿಲ್ಲ. ", @@ -594,4 +650,4 @@ "511": "ನೆಟ್‌ವರ್ಕ್ ಪ್ರವೇಶವನ್ನು ಪಡೆಯಲು ಗ್ರಾಹಕರು ದೃಢೀಕರಿಸುವ ಅಗತ್ಯವಿದೆ" } } -} \ No newline at end of file +} diff --git a/oidc-ui/public/locales/ta.json b/oidc-ui/public/locales/ta.json index dcc53d1e8..a55ee9654 100644 --- a/oidc-ui/public/locales/ta.json +++ b/oidc-ui/public/locales/ta.json @@ -61,7 +61,14 @@ "Face": "முகம்", "Finger": "விரல்", "Iris": "கருவிழி", - "select_a_device": "ஒரு சாதனத்தைத் தேர்ந்தெடுக்கவும்" + "select_a_device": "ஒரு சாதனத்தைத் தேர்ந்தெடுக்கவும்", + "invalid_input": "செல்லுபடியாகும் {{id}} ஐ உள்ளிடவும்", + "mobile": "மொபைல் எண்", + "nrc": "NRC ஐடி", + "vid": "விஐடி", + "email": "மின்னஞ்சல்", + "login_with_id": "{{currentID}} மூலம் உள்நுழைக", + "multiple_login_ids": "உள்நுழைவு ஐடி விருப்பத்தைத் தேர்ந்தெடுக்கவும்" }, "pin": { "sign_in_with_pin": "பின் மூலம் உள்நுழையவும்", @@ -70,7 +77,14 @@ "uin_label_text": "UIN/VID", "pin_label_text": "பின்", "uin_placeholder": "உங்கள் UIN அல்லது VID ஐ உள்ளிடவும்", - "pin_placeholder": "பின்னை உள்ளிடவும்" + "pin_placeholder": "பின்னை உள்ளிடவும்", + "invalid_input": "செல்லுபடியாகும் {{id}} ஐ உள்ளிடவும்", + "mobile": "மொபைல் எண்", + "nrc": "NRC ஐடி", + "vid": "விஐடி", + "email": "மின்னஞ்சல்", + "login_with_id": "{{currentID}} மூலம் உள்நுழைக", + "multiple_login_ids": "உள்நுழைவு ஐடி விருப்பத்தைத் தேர்ந்தெடுக்கவும்" }, "password": { "sign_in_with_password": "கடவுச்சொல் மூலம் உள்நுழைக", @@ -81,7 +95,14 @@ "uin_placeholder": "உங்கள் UIN அல்லது VID ஐ உள்ளிடவும்", "password_placeholder": "கடவுச்சொல்லை உள்ளிடவும்", "forgot_password": "கடவுச்சொல் மறந்துவிட்டதா?", - "reset_password": "கடவுச்சொல்லை மீட்டமைக்கவும்" + "reset_password": "கடவுச்சொல்லை மீட்டமைக்கவும்", + "invalid_input": "செல்லுபடியாகும் {{id}} ஐ உள்ளிடவும்", + "mobile": "மொபைல் எண்", + "nrc": "NRC ஐடி", + "vid": "விஐடி", + "email": "மின்னஞ்சல்", + "login_with_id": "{{currentID}} மூலம் உள்நுழைக", + "multiple_login_ids": "உள்நுழைவு ஐடி விருப்பத்தைத் தேர்ந்தெடுக்கவும்" }, "Form": { "sign_in_with_details": "விவரங்களுடன் உள்நுழைக", @@ -91,7 +112,8 @@ "dob_label_text": "DOB ஐ உள்ளிடவும்", "policyNumber_placeholder": "கொள்கை எண்", "fullName_placeholder": "முழுப் பெயர்", - "dob_placeholder": "DOB" + "dob_placeholder": "DOB", + "login_with_kbi": "KBI உடன் உள்நுழைக" }, "LoginQRCode": { "scan_with_wallet": "உடன் ஸ்கேன் செய்யவும் {{walletName}} உள்நுழைய", @@ -100,7 +122,8 @@ "link_code_status": "இணைப்பு குறியீடு நிலை", "failed": "உள்நுழைய முடியவில்லை. ", "refresh": "QR குறியீட்டைப் புதுப்பிக்கவும்", - "open_wallet_app": "திற {{walletName}} ஆப்" + "open_wallet_app": "திற {{walletName}} ஆப்", + "login_with_id": "{{currentID}} மூலம் உள்நுழைக" }, "otp": { "sign_in_with_otp": "OTP மூலம் உள்நுழையவும்", @@ -114,15 +137,24 @@ "otp_placeholder": "1234", "mobile_number": "மொபைல் எண்", "email_address": "மின்னஞ்சல் முகவரி", - "otp_sent_msg": "நீங்கள் பதிவு செய்தவருக்கு OTP அனுப்பப்பட்டுள்ளது {{otpChannels}}", - "resend_otp_counter": "OTP ஐ மீண்டும் அனுப்பவும் {{min}}:{{sec}}", + "otp_sent_msg": "அனுப்பப்பட்ட {{otpLength}} இலக்க OTP ஐ உள்ளிடவும்", + "resend_otp_counter": "நீங்கள் OTP ஐ மீண்டும் அனுப்பலாம் {{min}}:{{sec}}", "mobile_placeholder": "மொபைல் எண்: {{mobileNumber}}", "email_placeholder": "மின்னஞ்சல் முகவரி: {{emailAddress}}", - "mobile_email_placeholder": "மொபைல் எண்: {{mobileNumber}} மற்றும் மின்னஞ்சல் முகவரி: {{emailAddress}}" + "mobile_email_placeholder": "மொபைல் எண்: {{mobileNumber}} மற்றும் மின்னஞ்சல் முகவரி: {{emailAddress}}", + "invalid_input": "செல்லுபடியாகும் {{id}} ஐ உள்ளிடவும்", + "mobile": "மொபைல் எண்", + "nrc": "NRC ஐடி", + "vid": "விஐடி", + "email": "மின்னஞ்சல்", + "login_with_id": "{{currentID}} மூலம் உள்நுழைக", + "multiple_login_ids": "உள்நுழைவு ஐடி விருப்பத்தைத் தேர்ந்தெடுக்கவும்", + "enter_otp": "OTP ஐ உள்ளிடவும்", + "and": "மற்றும்" }, "signInOption": { "login_option_heading": "நீங்கள் எப்படி உள்நுழைய விரும்புகிறீர்கள் என்பதைத் தேர்ந்தெடுக்கவும்:", - "login_with": "உடன் உள்நுழைக {{option}}", + "login_with_id": "{{option}} மூலம் உள்நுழைக", "PIN": "பின்", "Biometrics": "பயோமெட்ரிக்ஸ்", "BIO": "பயோமெட்ரிக்ஸ்", @@ -143,7 +175,7 @@ "wellknown_api": "நன்கு அறியப்பட்ட API" }, "header": { - "login_heading": "உள்நுழைக", + "login_heading": "eSignet மூலம் உள்நுழைக", "login_subheading": "உங்கள் பதிவு செய்யப்பட்ட பயனர்பெயர் மற்றும் கடவுச்சொல்லை உள்ளிடவும்", "login_linkName": "முன்பதிவு", "more_ways_to_sign_in": "உள்நுழைவதற்கான கூடுதல் வழிகள்", @@ -154,6 +186,7 @@ "language": "மொழி", "noAccount": "கணக்கு இல்லையா?", "signup_for_unified_login": "ஒருங்கிணைந்த உள்நுழைவுடன் பதிவு செய்யவும்", + "subheading": "{{clientName}} உங்கள் தகவலைக் கேட்கிறார்", "subheader_text": { "all_login_options": "உள்நுழைவு விருப்பத்தைத் தேர்ந்தெடுக்கவும்", "otp_login": "உங்கள் UIN/VIDஐ உள்ளிடவும்", @@ -187,7 +220,8 @@ "otp_info": "நீங்கள் உருவாக்கிய UIN/VIDஐ உள்ளிடவும்", "bio_info": "நீங்கள் உருவாக்கிய UIN/VIDஐ உள்ளிடவும்", "pin_info": "நீங்கள் உருவாக்கிய UIN/VIDஐ உள்ளிடவும்", - "username_info": "நீங்கள் உருவாக்கிய UIN/VIDஐ உள்ளிடவும்" + "username_info": "நீங்கள் உருவாக்கிய UIN/VIDஐ உள்ளிடவும்", + "caps_on": "கேப்ஸ் லாக் ஆன் செய்யப்பட்டுள்ளது" }, "consentDetails": { "essential_claims": "அத்தியாவசிய உரிமைகோரல்கள்", @@ -229,6 +263,28 @@ "popup_body": "{{clientName}} மேலும் தொடர அணுகல் தேவை, நீங்கள் இன்னும் நிறுத்த விரும்புகிறீர்களா?", "consent_details_rejected": "மன்னிக்கவும்! " }, + "loginOptions": { + "buttons": { + "mobile": "மொபைல் எண்", + "nrc": "NRC ஐடி", + "vid": "விஐடி", + "email": "மின்னஞ்சல்" + }, + "input": { + "label": { + "mobile": "மொபைல் எண்", + "nrc": "NRC ஐடி", + "vid": "விஐடி", + "email": "மின்னஞ்சல்" + }, + "placeholder": { + "mobile": "மொபைல் எண்ணை உள்ளிடவும்", + "nrc": "NRC ஐடியை உள்ளிடவும்", + "vid": "VID ஐ உள்ளிடவும்", + "email": "மின்னஞ்சலை இங்கே உள்ளிடவும்" + } + } + }, "errors": { "0": "வெற்றி", "100": "பயோமெட்ரிக் சாதனம் பதிவு செய்யப்படவில்லை. ", @@ -594,4 +650,4 @@ "511": "நெட்வொர்க் அணுகலைப் பெற வாடிக்கையாளர் அங்கீகரிக்க வேண்டும்" } } -} \ No newline at end of file +} diff --git a/oidc-ui/public/theme/variables.css b/oidc-ui/public/theme/variables.css index 9b09b0ba5..f05234fab 100644 --- a/oidc-ui/public/theme/variables.css +++ b/oidc-ui/public/theme/variables.css @@ -1,18 +1,19 @@ :root { + --primary-color: #1262C9; --login-card-box-color: #BCC0C7; - --login-card-box-hover-color: #1263CA; - --login-card-box-focus-color: #1263CA; + --login-card-box-hover-color: var(--primary-color); + --login-card-box-focus-color: var(--primary-color); --login-background: #FCFCFF; --language-icon-color: #FFFFFF; - --language-icon-bg-color: #1262C9; + --language-icon-bg-color: var(--primary-color); --login-background-min-height: calc(100vh - 104.6px) !important; --brand-only-logo-url: url("/logo.png"); --brand-logo-url: url("/images/brand_logo.png"); --background-logo-url: url("/images/illustration_one.png"); --footer-brand-logo-url: url("/images/footer_logo.png"); --primary-button-color: #FFFFFF; - --primary-button-bg-color: #0953FA; - --primary-button-border-color: #0953FA; + --primary-button-bg-color: var(--primary-color); + --primary-button-border-color: var(--primary-color); --primary-button-hover-color: #FFFFFF; --primary-button-hover-bg-color: #0549b6; --primary-button-hover-border-color: #0549b6; @@ -31,25 +32,25 @@ --loading-indicator-ring-color: #3182CE; --loading-indicator-bg-color: transparent; --loading-indicator-font-color: #000000; - --qrcode-border-color: #1263CA; + --qrcode-border-color: var(--primary-color); --info-icon-color: #0686F0; --toggle-button-inactive-bg-color: #FFFFFF; --toggle-button-inactive-border-color: #D8D8D8; --toggle-button-active-bg-color: #FFFFFF; - --toggle-button-active-border-color: #1262C9; + --toggle-button-active-border-color: var(--primary-color); --toggle-button-inactive-ball-color: #A7A8A9; --toggle-button-inactive-ball-border-color: #A7A8A9; - --toggle-button-active-ball-color: #1262C9; - --toggle-button-active-ball-border-color: #1262C9; - --toggle-button-ring-color: #1262C9; + --toggle-button-active-ball-color: var(--primary-color); + --toggle-button-active-ball-border-color: var(--primary-color); + --toggle-button-ring-color: var(--primary-color); --header-height: 55px; --footer-height: 47px; - --signup-banner-background-color: #FFF6F2; + --signup-banner-background-color: #eaf5ff; --signup-banner-bottom-radius: 20px; --signup-banner-padding-top: 20px; --signup-banner-padding-bottom: 20px; --signup-banner-margin-top: 16px; - --signup-banner-signup-url-color: #1262C9; + --signup-banner-signup-url-color: var(--primary-color); --signup-banner-signup-url-color-hover: #2F8EA3; --signup-banner-signup-url-color-focus: #2F8EA3; --signup-banner-font: normal normal normal 15px/19px Roboto; @@ -59,17 +60,17 @@ --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); --bottom-left-bg-logo-display: none; --top-right-bg-logo-display: none; - --login-client-logo-image-height: 3.5rem; - --login-client-logo-image-width: 3.5rem; + --login-client-logo-image-height: 3.25rem; + --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21CB"; --login-alternate-arrow-size: 3rem; - --login-text-color: #4B5563; + --login-text-color: #000000; --login-card-separator: linear-gradient(0deg, rgb(0,0,0,0.2) 0%, rgb(0,0,0,0.2) 100%); --login-card-separator-height: 1px; --footer-text-color: #898989; --footer-text-size: 14px; - --language-check-icon-color: #1262C9; - --forgot-password-color: #1262C9; + --language-check-icon-color: var(--primary-color); + --forgot-password-color: var(--primary-color); --forgot-password-font-size: small; --forgot-password-width: max-content; --forgot-password-margin-top: 1rem; @@ -77,80 +78,93 @@ --navbar-position: relative; --navbar-zIndex: 1; --navbar-box-shadow: 0px 2px 5px #0000001A; - --back-button-color: #1262C9; - --more-signin-options-color: #1262C9; - --arrow-down-color: #1262C9; + --back-button-color: var(--primary-color); + --more-signin-options-color: var(--primary-color); + --arrow-down-color: var(--primary-color); --primary-button-border-radius: 5px; --consent-details-header-font: normal normal 600 24px/29px Roboto; --consent-details-header-color: #2b3840; --consent-details-subheader-font: normal normal bold 16px/21px Roboto; --consent-details-subheader-color: #2c2c2c; - --consent-details-infoIcon-fill: #1262C9; - --consent-details-infoIcon-stroke: #1262C9; + --consent-details-infoIcon-fill: var(--primary-color); + --consent-details-infoIcon-stroke: var(--primary-color); --consent-details-available-font: normal normal medium 14px/17px Helvetica Neue; --consent-details-available-color: #76ac6d; --consent-details-unavailable-font: normal normal medium 14px/16px Helvetica Neue; --consent-details-unavailable-color: #707070; - --consent-details-message-color: #1262C9; + --consent-details-message-color: var(--primary-color); --consent-details-message-background: #fff9f0 0% 0% no-repeat padding-box; --otp-pin-input-width: 40px; --otp-pin-input-height: 40px; --otp-pin-input-margin: 0 5px; - --otp-pin-input-color: #1262C9; - --otp-pin-input-border-bottom: 2px solid #1262C9; - --checkbox-color: #1262C9; - --wallet-download-text-color: #1262C9; + --otp-pin-input-color: var(--primary-color); + --otp-pin-input-border-bottom: 2px solid var(--primary-color); + --checkbox-color: var(--primary-color); + --wallet-download-text-color: var(--primary-color); --no-record-banner-background: #fff9f0 0% 0% no-repeat padding-box; --no-record-banner-border-radius: 10px; --no-record-banner-font: normal normal normal 13px/18px Roboto; - --no-record-banner-color: #1262c9; + --no-record-banner-color: var(--primary-color); --no-record-banner-padding: 1em 1.5em; + --selected_login_id_border_color: var(--primary-color); + --selected_login_id_color: var(--primary-color); + --login_id_hover_border_color: var(--primary-color); + --login_id_hover_color: var(--primary-color); + --selected_login_id_focus_color: var(--primary-color); + --selected_login_id_focus_outline_color: var(--primary-color); + --qr_code_title_color: #696969; + --resend_otp_button_color: var(--primary-color); + --login-client-logo-image-shadow: 0px 4px 15px 2px #EB6F2D4D; + --caps-lock-text-color: #2D86BA; + --login-header-text-color: rgb(0, 0, 0); + --login-subheader-text-color: #838383; } -[class="orange_theme"] { +[class="orange"] { + --primary-color: #eb6f2d; --login-card-box-color: #bcc0c7; - --login-card-box-hover-color: #eb6f2d; - --login-card-box-focus-color: #eb6f2d; + --login-card-box-hover-color: var(--primary-color); + --login-card-box-focus-color: var(--primary-color); --login-background: #f7f9fd; --language-icon-color: #ffffff; - --language-icon-bg-color: #eb6f2d; + --language-icon-bg-color: var(--primary-color); --login-background-min-height: calc(100vh - 104.6px) !important; --brand-only-logo-url: url("/logo.png"); --brand-logo-url: url("/images/brand_logo.png"); --background-logo-url: url("/images/illustration_one.png"); --footer-brand-logo-url: url("/images/footer_logo.png"); --primary-button-color: #ffffff; - --primary-button-bg-color: #eb6f2d; - --primary-button-border-color: #eb6f2d; + --primary-button-bg-color: var(--primary-color); + --primary-button-border-color: var(--primary-color); --primary-button-hover-color: #ffffff; - --primary-button-hover-bg-color: #eb6f2d; - --primary-button-hover-border-color: #eb6f2d; + --primary-button-hover-bg-color: var(--primary-color); + --primary-button-hover-border-color: var(--primary-color); --primary-button-disable-color: #ffffff; --primary-button-disable-bg-color: #cbcbcb; --primary-button-disable-border-color: #cbcbcb; - --secondary-button-color: #eb6f2d; + --secondary-button-color: var(--primary-color); --secondary-button-bg-color: #ffffff; --secondary-button-border-color: #ffffff; - --secondary-button-hover-color: #eb6f2d; + --secondary-button-hover-color: var(--primary-color); --secondary-button-hover-bg-color: #ffffff; --secondary-button-hover-border-color: #ffffff; --secondary-button-disable-color: #ffffff; --secondary-button-disable-bg-color: #cbcbcb; --secondary-button-disable-border-color: #cbcbcb; - --loading-indicator-ring-color: #eb6f2d; + --loading-indicator-ring-color: var(--primary-color); --loading-indicator-bg-color: transparent; --loading-indicator-font-color: #000000; - --qrcode-border-color: #eb6f2d; - --info-icon-color: #eb6f2d; + --qrcode-border-color: var(--primary-color); + --info-icon-color: var(--primary-color); --toggle-button-inactive-bg-color: #ffffff; --toggle-button-inactive-border-color: #d8d8d8; --toggle-button-active-bg-color: #ffdecc; --toggle-button-active-border-color: #ffffff; --toggle-button-inactive-ball-color: #cbcbcb; --toggle-button-inactive-ball-border-color: #cbcbcb; - --toggle-button-active-ball-color: #eb6f2d; - --toggle-button-active-ball-border-color: #eb6f2d; - --toggle-button-ring-color: #eb6f2d; + --toggle-button-active-ball-color: var(--primary-color); + --toggle-button-active-ball-border-color: var(--primary-color); + --toggle-button-ring-color: var(--primary-color); --header-height: 55px; --footer-height: 47px; --signup-banner-background-color: #fff6f2; @@ -158,9 +172,9 @@ --signup-banner-padding-top: 20px; --signup-banner-padding-bottom: 20px; --signup-banner-margin-top: 16px; - --signup-banner-signup-url-color: #eb6f2d; - --signup-banner-signup-url-color-hover: #eb6f2d; - --signup-banner-signup-url-color-focus: #eb6f2d; + --signup-banner-signup-url-color: var(--primary-color); + --signup-banner-signup-url-color-hover: var(--primary-color); + --signup-banner-signup-url-color-focus: var(--primary-color); --signup-banner-font: normal normal normal 15px/19px Roboto; --multipurpose-login-card-border-radius: 10px; --multipurpose-login-card-background-color: #ffffff; @@ -168,17 +182,17 @@ --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); --bottom-left-bg-logo-display: none; --top-right-bg-logo-display: none; - --login-client-logo-image-height: 3.5rem; - --login-client-logo-image-width: 3.5rem; + --login-client-logo-image-height: 3.25rem; + --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21C4"; --login-alternate-arrow-size: 1.5rem; - --login-text-color: #333333; - --login-card-separator: linear-gradient(0deg, rgb(0,0,0,0.2) 0%, rgb(0,0,0,0.2) 100%); + --login-text-color: #000000; + --login-card-separator: linear-gradient(0deg, #FFFBF7 0%, #FFFFFF 100%); --login-card-separator-height: 1px; --footer-text-color: #898989; --footer-text-size: 14px; - --language-check-icon-color: #eb6f2d; - --forgot-password-color: #eb6f2d; + --language-check-icon-color: var(--primary-color); + --forgot-password-color: var(--primary-color); --forgot-password-font-size: small; --forgot-password-width: max-content; --forgot-password-margin-top: 1rem; @@ -186,16 +200,16 @@ --navbar-position: relative; --navbar-zIndex: 1; --navbar-box-shadow: 0px 2px 5px #0000001a; - --back-button-color: #eb6f2d; - --more-signin-options-color: #eb6f2d; - --arrow-down-color: #eb6f2d; + --back-button-color: var(--primary-color); + --more-signin-options-color: var(--primary-color); + --arrow-down-color: var(--primary-color); --primary-button-border-radius: 8px; --consent-details-header-font: normal normal 600 24px/29px Roboto; --consent-details-header-color: #2b3840; --consent-details-subheader-font: normal normal bold 16px/21px Roboto; --consent-details-subheader-color: #2c2c2c; - --consent-details-infoIcon-fill: #eb6f2d; - --consent-details-infoIcon-stroke: #eb6f2d; + --consent-details-infoIcon-fill: var(--primary-color); + --consent-details-infoIcon-stroke: var(--primary-color); --consent-details-available-font: normal normal medium 14px/17px Helvetica Neue; --consent-details-available-color: #76ac6d; --consent-details-unavailable-font: normal normal medium 14px/16px Helvetica Neue; @@ -205,73 +219,86 @@ --otp-pin-input-width: 40px; --otp-pin-input-height: 40px; --otp-pin-input-margin: 0 5px; - --otp-pin-input-color: #eb6f2d; - --otp-pin-input-border-bottom: 2px solid #eb6f2d; - --checkbox-color: #eb6f2d; - --wallet-download-text-color: #eb6f2d; + --otp-pin-input-color: var(--primary-color); + --otp-pin-input-border-bottom: 2px solid var(--primary-color); + --checkbox-color: var(--primary-color); + --wallet-download-text-color: var(--primary-color); --no-record-banner-background: #fff9f0 0% 0% no-repeat padding-box; --no-record-banner-border-radius: 10px; --no-record-banner-font: normal normal normal 13px/18px Roboto; --no-record-banner-color: #de7a24; --no-record-banner-padding: 1em 1.5em; + --selected_login_id_border_color: var(--primary-color); + --selected_login_id_color: var(--primary-color); + --login_id_hover_border_color: var(--primary-color); + --login_id_hover_color: var(--primary-color); + --selected_login_id_focus_color: var(--primary-color); + --selected_login_id_focus_outline_color: var(--primary-color); + --qr_code_title_color: #696969; + --resend_otp_button_color: var(--primary-color); + --login-client-logo-image-shadow: 0px 4px 15px 2px #EB6F2D4D; + --caps-lock-text-color: #2D86BA; + --login-header-text-color: rgb(0, 0, 0); + --login-subheader-text-color: #838383; } -[class="blue"] { +[class="green"] { + --primary-color: #4CBB17; --font-family: "Kantumruy Pro", sans-serif; --login-card-box-color: #bcc0c7; - --login-card-box-hover-color: #1263ca; - --login-card-box-focus-color: #1263ca; + --login-card-box-hover-color: var(--primary-color); + --login-card-box-focus-color: var(--primary-color); --login-background: #fcfcff; --login-background-image: url("/images/section-bg.png"); --login-background-min-height: calc(100vh - 157.6px) !important; --language-icon-color: #ffffff; - --language-icon-bg-color: #004282; + --language-icon-bg-color: var(--primary-color); --brand-only-logo-url: url("/logo.png"); --brand-logo-url: url("/images/brand_logo.png"); --background-logo-url: url("/images/illustration_one.png"); --footer-brand-logo-url: url("/images/footer_logo.png"); --primary-button-color: #ffffff; - --primary-button-bg-color: #004282; - --primary-button-border-color: #004282; + --primary-button-bg-color: var(--primary-color); + --primary-button-border-color: var(--primary-color); --primary-button-hover-color: #ffffff; - --primary-button-hover-bg-color: #0549b6; - --primary-button-hover-border-color: #0549b6; + --primary-button-hover-bg-color: var(--primary-color); + --primary-button-hover-border-color: var(--primary-color); --primary-button-disable-color: #94a3b8; --primary-button-disable-bg-color: #ffffff; --primary-button-disable-border-color: #94a3b8; - --secondary-button-color: #111827; + --secondary-button-color: green; --secondary-button-bg-color: #ffffff; --secondary-button-border-color: #ffffff; - --secondary-button-hover-color: #111827; + --secondary-button-hover-color: green; --secondary-button-hover-bg-color: #ffffff; --secondary-button-hover-border-color: #ffffff; --secondary-button-disable-color: #ffffff; --secondary-button-disable-bg-color: #cbcbcb; --secondary-button-disable-border-color: #cbcbcb; - --loading-indicator-ring-color: #004282; + --loading-indicator-ring-color: var(--primary-color); --loading-indicator-bg-color: transparent; --loading-indicator-font-color: #000000; - --qrcode-border-color: #004282; - --info-icon-color: #004282; + --qrcode-border-color: var(--primary-color); + --info-icon-color: var(--primary-color); --toggle-button-inactive-bg-color: #ffffff; --toggle-button-inactive-border-color: #d8d8d8; --toggle-button-active-bg-color: #ffffff; - --toggle-button-active-border-color: #004282; + --toggle-button-active-border-color: var(--primary-color); --toggle-button-inactive-ball-color: #a7a8a9; --toggle-button-inactive-ball-border-color: #a7a8a9; - --toggle-button-active-ball-color: #004282; - --toggle-button-active-ball-border-color: #004282; - --toggle-button-ring-color: #004282; + --toggle-button-active-ball-color: var(--primary-color); + --toggle-button-active-ball-border-color: var(--primary-color); + --toggle-button-ring-color: var(--primary-color); --header-height: 70px; --footer-height: 56px; - --signup-banner-background-color: #eaf5ff; + --signup-banner-background-color: #90EE90; --signup-banner-bottom-radius: 20px; --signup-banner-padding-top: 20px; --signup-banner-padding-bottom: 20px; --signup-banner-margin-top: 16px; - --signup-banner-signup-url-color: #004282; - --signup-banner-signup-url-color-hover: #2f8ea3; - --signup-banner-signup-url-color-focus: #2f8ea3; + --signup-banner-signup-url-color: green; + --signup-banner-signup-url-color-hover: var(--primary-color); + --signup-banner-signup-url-color-focus: var(--primary-color); --signup-banner-font: normal normal normal 15px/19px "Kantumruy Pro"; --multipurpose-login-card-border-radius: 20px; --multipurpose-login-card-background-color: #ffffff; @@ -279,23 +306,23 @@ --top-left-bg-logo-url: url("/images/top_left_bg_logo.svg"); --top-left-bg-logo-display: none; --bottom-left-bg-logo-url: url("/images/bg_bottom_left.png"); - --bottom-left-bg-logo-display: block; + --bottom-left-bg-logo-display: none; --top-right-bg-logo-url: url("/images/top_left_bg_logo.svg"); --top-right-bg-logo-display: none; --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); --bottom-right-bg-logo-display: none; - --login-client-logo-image-height: 5rem; - --login-client-logo-image-width: 8rem; + --login-client-logo-image-height: 3.25rem; + --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21CB"; --login-alternate-arrow-size: 3rem; - --login-text-color: #4b5563; + --login-text-color: #000000; --login-card-separator: linear-gradient(0deg, #ffffff 0%, #f7fcff 100%); --login-card-separator-height: 20px; --footer-text-color: #ffffff; --footer-text-size: 14px; - --footer-bg-color: #004282; - --language-check-icon-color: #004282; - --forgot-password-color: #004282; + --footer-bg-color: var(--primary-color); + --language-check-icon-color: var(--primary-color); + --forgot-password-color: var(--primary-color); --forgot-password-font-size: small; --forgot-password-width: max-content; --forgot-password-margin-top: 1rem; @@ -303,32 +330,44 @@ --navbar-position: relative; --navbar-zIndex: 1; --navbar-box-shadow: 0px 2px 5px #0000001a; - --back-button-color: #004282; - --more-signin-options-color: #004282; - --arrow-down-color: #004282; + --back-button-color: var(--primary-color); + --more-signin-options-color: var(--primary-color); + --arrow-down-color: var(--primary-color); --primary-button-border-radius: 8px; --consent-details-header-font: normal normal 600 24px/29px Roboto; - --consent-details-header-color: #2b3840; + --consent-details-header-color: var(--primary-color); --consent-details-subheader-font: normal normal bold 16px/21px Roboto; - --consent-details-subheader-color: #2c2c2c; - --consent-details-infoIcon-fill: #004282; - --consent-details-infoIcon-stroke: #004282; + --consent-details-subheader-color: var(--primary-color); + --consent-details-infoIcon-fill: var(--primary-color); + --consent-details-infoIcon-stroke: var(--primary-color); --consent-details-available-font: normal normal medium 14px/17px Helvetica Neue; --consent-details-available-color: #76ac6d; --consent-details-unavailable-font: normal normal medium 14px/16px Helvetica Neue; --consent-details-unavailable-color: #707070; - --consent-details-message-color: #004282; + --consent-details-message-color: var(--primary-color); --consent-details-message-background: #fff9f0 0% 0% no-repeat padding-box; --otp-pin-input-width: 40px; --otp-pin-input-height: 40px; --otp-pin-input-margin: 0 5px; - --otp-pin-input-color: #004282; - --otp-pin-input-border-bottom: 2px solid #004282; - --checkbox-color: #004282; - --wallet-download-text-color: #004282; + --otp-pin-input-color: var(--primary-color); + --otp-pin-input-border-bottom: 2px solid var(--primary-color); + --checkbox-color: var(--primary-color); + --wallet-download-text-color: var(--primary-color); --no-record-banner-background: #fff9f0 0% 0% no-repeat padding-box; --no-record-banner-border-radius: 10px; --no-record-banner-font: normal normal normal 13px/18px Roboto; - --no-record-banner-color: #004282; + --no-record-banner-color: var(--primary-color); --no-record-banner-padding: 1em 1.5em; + --selected_login_id_border_color: var(--primary-color); + --selected_login_id_color: var(--primary-color); + --login_id_hover_border_color: var(--primary-color); + --login_id_hover_color: var(--primary-color); + --selected_login_id_focus_color: var(--primary-color); + --selected_login_id_focus_outline_color: var(--primary-color); + --qr_code_title_color: #696969; + --resend_otp_button_color: var(--primary-color); + --login-client-logo-image-shadow: 0px 4px 15px 2px #EB6F2D4D; + --caps-lock-text-color: #2D86BA; + --login-header-text-color: rgb(0, 0, 0); + --login-subheader-text-color: #838383; } diff --git a/oidc-ui/src/App.css b/oidc-ui/src/App.css index 515f615ec..fe7ffae5a 100644 --- a/oidc-ui/src/App.css +++ b/oidc-ui/src/App.css @@ -392,6 +392,10 @@ input[type="checkbox"]:hover { width: var(--login-client-logo-image-width); } +.client-logo-shadow { + box-shadow: var(--login-client-logo-image-shadow); +} + .alternate-arrow { font-size: var(--login-alternate-arrow-size); } @@ -412,7 +416,6 @@ input[type="checkbox"]:hover { .error-banner { color: #d52929; background-color: #faefef; - height: 40px; } .error-banner-text { @@ -430,7 +433,7 @@ input[type="checkbox"]:hover { display: flex; align-items: center; border: 1px solid #e5e7eb; - border-radius: 4px; + border-radius: 0.5rem; overflow: hidden; } @@ -527,6 +530,48 @@ input[type="number"] { z-index: 50 !important; } +.selected_login_id { + border-color: var(--selected_login_id_border_color); + color: var(--selected_login_id_color); +} + +.login_id:hover { + border-color: var(--login_id_hover_border_color); + color: var(--login_id_hover_color); +} + +.login_id:focus { + color: var(--selected_login_id_focus_color); + outline-color: var(--selected_login_id_focus_outline_color); +} + +.qr-title { + color: var(--qr_code_title_color); +} + +.resend_otp { + color: var(--resend_otp_button_color); + + &:hover, + &:focus { + color: var(--resend_otp_button_color) !important; + } +} + +#login-header { + font-family: "Roboto"; + letter-spacing: 0.25px; + color: var(--login-header-text-color); +} + +#login-subheader { + color: var(--login-subheader-text-color); +} + +.caps_lock { + color: var(--caps-lock-text-color); +} + @media screen and (max-width: 767px) { .multipurpose-login-card { border-radius: 0; diff --git a/oidc-ui/src/common/ErrorBanner.js b/oidc-ui/src/common/ErrorBanner.js index 6a9c02001..05da39c7c 100644 --- a/oidc-ui/src/common/ErrorBanner.js +++ b/oidc-ui/src/common/ErrorBanner.js @@ -16,7 +16,7 @@ const ErrorBanner = ({ return showBanner && (
{ - if(signupConfig?.[configurationKeys.signupBanner]) { + if (signupConfig?.[configurationKeys.signupBanner]) { setSignupBanner(true); - setSignupURL(signupConfig[configurationKeys.signupURL] + "#" + authService.getAuthorizeQueryParam()); + setSignupURL( + signupConfig[configurationKeys.signupURL] + + "#" + + authService.getAuthorizeQueryParam() + ); } + // document.getElementById("language_dropdown")?.style?.display="none" }, [i18n.language]); // check signup banner is present or not, @@ -33,9 +40,8 @@ export default function Background({ const conditionalPadding = signupBanner ? "pt-4" : "py-4"; const handleSignup = () => { - window.onbeforeunload = null - } - + window.onbeforeunload = null; + }; return (
-
-

+
+

{heading}

-

{subheading}

+ {clientName && ( +

+ {t("subheading", { + clientName: subheading["@none"], + })} +

+ )}
- {clientName} + {clientLogoPath && ( + {clientName} + )} {t("logo_alt")}
-
+
{component}

{/* Enable the signup banner when it is true in the signup.config of oauth-details */} - {signupBanner && - } + {signupBanner && ( + + )}
); } diff --git a/oidc-ui/src/components/Form.js b/oidc-ui/src/components/Form.js index 58e066137..49cd39869 100644 --- a/oidc-ui/src/components/Form.js +++ b/oidc-ui/src/components/Form.js @@ -2,10 +2,7 @@ import { useEffect, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import LoadingIndicator from "../common/LoadingIndicator"; -import { - buttonTypes, - configurationKeys, -} from "../constants/clientConstants"; +import { buttonTypes, configurationKeys } from "../constants/clientConstants"; import { LoadingStates as states } from "../constants/states"; import FormAction from "./FormAction"; import InputWithImage from "./InputWithImage"; @@ -23,9 +20,8 @@ export default function Form({ openIDConnectService, backButtonDiv, i18nKeyPrefix1 = "Form", - i18nKeyPrefix2 = "errors" + i18nKeyPrefix2 = "errors", }) { - const { t: t1, i18n } = useTranslation("translation", { keyPrefix: i18nKeyPrefix1, }); @@ -33,7 +29,7 @@ export default function Form({ const { t: t2 } = useTranslation("translation", { keyPrefix: i18nKeyPrefix2, }); - + const inputCustomClass = "h-10 border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[hsla(0, 0%, 51%)] focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-muted-light-gray shadow-none"; @@ -49,8 +45,7 @@ export default function Form({ const [status, setStatus] = useState(states.LOADED); const [invalidState, setInvalidState] = useState(true); - useEffect(() => { - }, []); + useEffect(() => {}, []); const navigate = useNavigate(); @@ -95,17 +90,28 @@ export default function Form({ const resetCaptcha = () => { _reCaptchaRef.current.reset(); setCaptchaToken(null); - } + }; //Handle Login API Integration here const authenticateUser = async () => { try { let transactionId = openIDConnectService.getTransactionId(); - let uin = loginState["_form_" + openIDConnectService.getEsignetConfiguration(configurationKeys.authFactorKnowledgeIndividualIdField) ?? ""]; + let uin = + loginState[ + "_form_" + + openIDConnectService.getEsignetConfiguration( + configurationKeys.authFactorKnowledgeIndividualIdField + ) ?? "" + ]; let challengeManipulate = {}; fields.forEach(function (field) { - if (field.id !== openIDConnectService.getEsignetConfiguration(configurationKeys.authFactorKnowledgeIndividualIdField)) { - challengeManipulate[field.id] = loginState["_form_" + field.id] + if ( + field.id !== + openIDConnectService.getEsignetConfiguration( + configurationKeys.authFactorKnowledgeIndividualIdField + ) + ) { + challengeManipulate[field.id] = loginState["_form_" + field.id]; } }); let challenge = btoa(JSON.stringify(challengeManipulate)); @@ -132,27 +138,27 @@ export default function Form({ const { response, errors } = authenticateResponse; if (errors != null && errors.length > 0) { - let errorCodeCondition = langConfig.errors.otp[errors[0].errorCode] !== undefined && langConfig.errors.kbi[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.otp[errors[0].errorCode] !== undefined && + langConfig.errors.kbi[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `kbi.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } if (showCaptcha) { resetCaptcha(); - } + } return; } else { setError(null); @@ -174,13 +180,13 @@ export default function Form({ } catch (error) { setErrorBanner({ errorCode: "kbi.auth_failed", - show: true + show: true, }); setStatus(states.ERROR); if (showCaptcha) { resetCaptcha(); - } + } } }; @@ -206,11 +212,11 @@ export default function Form({ const onBlurChange = (e, errors) => { let id = e.target.id; - let tempError = inputErrorBanner.map(_ => _); + let tempError = inputErrorBanner.map((_) => _); if (errors.length > 0) { - tempError.push(id) + tempError.push(id); } else { - let errorIndex = tempError.findIndex(_ => _ === id); + let errorIndex = tempError.findIndex((_) => _ === id); if (errorIndex !== -1) { tempError.splice(errorIndex, 1); } @@ -220,16 +226,21 @@ export default function Form({ return ( <> -
- {(backButtonDiv)} +
+ {backButtonDiv} +
+ {`${t1("login_with_kbi")}`} +
{errorBanner !== null && ( - +
+ +
)}
diff --git a/oidc-ui/src/components/FormAction.js b/oidc-ui/src/components/FormAction.js index 4082a642b..a46aa808d 100644 --- a/oidc-ui/src/components/FormAction.js +++ b/oidc-ui/src/components/FormAction.js @@ -6,9 +6,10 @@ export default function FormAction({ text, disabled = false, id, + customClassName }) { const className = - "flex justify-center w-full font-medium rounded-lg text-sm px-5 py-2 text-center border border-2 "; + "flex justify-center w-full font-semibold rounded-lg px-5 py-3 text-center border border-2 "; return ( <> @@ -16,7 +17,7 @@ export default function FormAction({ +
+ + {isDropdownOpen && + props.currentLoginID?.prefixes?.length > 1 && + typeof props.currentLoginID?.prefixes === "object" && ( +
+
+ {props.currentLoginID?.prefixes.map((option) => ( + + ))} +
+
+ )} + 0 && "border-[#FE6B6B]" + }`} + onChange={handleChange} + onBlur={handleBlur} + id={props.login + "_" + selectedCountry.label} + name={props.login + "_" + selectedCountry.label} + value={individualId ?? ""} + /> +
+ {!isValid && individualId?.length > 0 && ( + + + error_icon + + + {`${t(`${props.i18nPrefix}.invalid_input`, { + id: `${t(`${props.i18nPrefix}.${props.currentLoginID.id}`)}`, + })}`} + + + )} + + ) + ); +}; + +export default InputWithPrefix; diff --git a/oidc-ui/src/components/L1Biometrics.js b/oidc-ui/src/components/L1Biometrics.js index 92a11ce30..325567c50 100644 --- a/oidc-ui/src/components/L1Biometrics.js +++ b/oidc-ui/src/components/L1Biometrics.js @@ -14,9 +14,11 @@ import ErrorBanner from "../common/ErrorBanner"; import langConfigService from "../services/langConfigService"; import redirectOnError from "../helpers/redirectOnError"; import ReCAPTCHA from "react-google-recaptcha"; +import LoginIDOptions from "./LoginIDOptions"; +import InputWithPrefix from "./InputWithPrefix"; let fieldsState = {}; -const langConfig = await langConfigService.getEnLocaleConfiguration(); +const langConfig = await langConfigService.getEnLocaleConfiguration(); export default function L1Biometrics({ param, @@ -24,9 +26,8 @@ export default function L1Biometrics({ openIDConnectService, backButtonDiv, i18nKeyPrefix1 = "l1Biometrics", - i18nKeyPrefix2 = "errors" + i18nKeyPrefix2 = "errors", }) { - const { t: t1, i18n } = useTranslation("translation", { keyPrefix: i18nKeyPrefix1, }); @@ -46,7 +47,7 @@ export default function L1Biometrics({ const { post_AuthenticateUser, buildRedirectParams } = authService; inputFields.forEach((field) => (fieldsState["sbi_" + field.id] = "")); - const [loginState, setLoginState] = useState(fieldsState); + const [status, setStatus] = useState({ state: states.LOADED, msg: "", @@ -58,6 +59,21 @@ export default function L1Biometrics({ const [captchaToken, setCaptchaToken] = useState(null); const _reCaptchaRef = useRef(null); + const [currentLoginID, setCurrentLoginID] = useState(null); + const [countryCode, setCountryCode] = useState(null); + const [individualId, setIndividualId] = useState(null); + const [selectedCountry, setSelectedCountry] = useState(null); + const [isValid, setIsValid] = useState(false); + const [isBtnDisabled, setIsBtnDisabled] = useState(true); + const [prevLanguage, setPrevLanguage] = useState(i18n.language); + + const iso = require("iso-3166-1"); + const countries = iso.all(); + + var loginIDs = openIDConnectService.getEsignetConfiguration( + configurationKeys.loginIdOptions + ); + const captchaEnableComponents = openIDConnectService.getEsignetConfiguration( configurationKeys.captchaEnableComponents @@ -83,10 +99,87 @@ export default function L1Biometrics({ const authTxnIdLength = parseInt(authTxnIdLengthValue); - const handleInputChange = (e) => { - setLoginState({ ...loginState, [e.target.id]: e.target.value }); + function getPropertiesForLoginID(loginID, label) { + const { prefixes, maxLength: outerMaxLength, regex: outerRegex } = loginID; + + if (Array.isArray(prefixes) && prefixes.length > 0) { + const prefix = prefixes.find((prefix) => prefix.label === label); + if (prefix) { + return { + maxLength: prefix.maxLength || outerMaxLength || null, + regex: prefix.regex || outerRegex || null, + }; + } + } + + return { + maxLength: outerMaxLength || null, + regex: outerRegex || null, + }; + } + + const handleChange = (e) => { + setIsValid(true); + onCloseHandle(); + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + const trimmedValue = e.target.value.trim(); + + let newValue = regex + ? trimmedValue + .split("") + .filter((char) => regex.test(char)) + .join("") + : trimmedValue; + + setIndividualId(newValue); // Update state with the visible valid value + + setIsBtnDisabled( + !( + ( + (!maxLength && !regex) || // Case 1: No maxLength, no regex + (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only + (maxLength && + regex && + newValue.length === parseInt(maxLength) && + regex.test(newValue)) + ) // Case 4: Both maxLength and regex + ) + ); + }; + + const handleBlur = (e) => { + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + setIsValid( + (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!regex || regex.test(e.target.value.trim())) + ); }; + useEffect(() => { + if (i18n.language === prevLanguage) { + setIndividualId(null); + setIsValid(false); + setIsBtnDisabled(true); + onCloseHandle(); + if (currentLoginID && currentLoginID.prefixes) { + setSelectedCountry(currentLoginID.prefixes[0]); + } + } else { + setPrevLanguage(i18n.language); + } + }, [currentLoginID]); + /* authenticate method after removing startCapture * which have capturing & authenticate as well */ @@ -95,18 +188,27 @@ export default function L1Biometrics({ setStatus({ state: states.LOADED, msg: "" }); const { errorCode } = validateBiometricResponse(biometricResponse); - const vid = inputFields[0].prefix + loginState["sbi_mosip-vid"] + inputFields[0].postfix; + let prefix = currentLoginID.prefixes + ? typeof currentLoginID.prefixes === "object" + ? countryCode + : currentLoginID.prefixes + : ""; + let id = individualId; + let postfix = currentLoginID.postfix ? currentLoginID.postfix : ""; + + let ID = prefix + id + postfix; + if (errorCode === null) { try { await Authenticate( transactionId, - vid, + ID, openIDConnectService.encodeBase64(biometricResponse["biometrics"]) ); } catch (error) { setErrorBanner({ errorCode: "authentication_failed_msg", - show: true + show: true, }); } } @@ -179,7 +281,7 @@ export default function L1Biometrics({ setCaptchaToken(null); }; - const Authenticate = async (transactionId, uin, bioValue) => { + const Authenticate = async (transactionId, id, bioValue) => { const challengeList = [ { authFactorType: challengeTypes.bio, @@ -195,7 +297,7 @@ export default function L1Biometrics({ const authenticateResponse = await post_AuthenticateUser( transactionId, - uin, + id, challengeList, captchaToken ); @@ -205,22 +307,21 @@ export default function L1Biometrics({ const { response, errors } = authenticateResponse; if (errors != null && errors.length > 0) { - - let errorCodeCondition = langConfig.errors.biometrics[errors[0].errorCode] !== undefined && langConfig.errors.biometrics[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.biometrics[errors[0].errorCode] !== undefined && + langConfig.errors.biometrics[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `biometrics.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } if (showCaptcha) { @@ -246,7 +347,7 @@ export default function L1Biometrics({ const getEsignetConfiguration = (key) => { return openIDConnectService.getEsignetConfiguration(configurationKeys[key]); - } + }; useEffect(() => { let mosipProp = { @@ -284,81 +385,116 @@ export default function L1Biometrics({ return; } propChange({ - disable: !loginState["sbi_mosip-vid"].length || inputError || - (showCaptcha && captchaToken === null), + disable: + !individualId?.trim() || + isBtnDisabled || + (showCaptcha && captchaToken === null), onCapture: (e) => authenticateBiometricResponse(e), }); - }, [loginState, inputError, captchaToken]); + }, [individualId, isBtnDisabled, captchaToken, countryCode]); const onCloseHandle = () => { setErrorBanner(null); }; - const onBlurChange = (e, errors) => { - setInputError(errors.length === 0 ? null : errors); - } - return ( <> -
+
{backButtonDiv} + {currentLoginID && ( +
+ {loginIDs && loginIDs.length > 1 + ? t1("multiple_login_ids") + : `${t1("login_with_id", { + currentID: `${t1(currentLoginID.id)}` + })}`} +
+ )}
{errorBanner !== null && ( - - )} - -
- {inputFields.map((field) => ( - - ))} +
+
- - {showCaptcha && ( -
- -
+ )} + { + setCurrentLoginID(value); + }} + /> + + {currentLoginID && ( + <> +
+ {currentLoginID?.prefixes?.length > 0 ? ( + { + setCountryCode(val); + }} + selectedCountry={(val) => { + setSelectedCountry(val); + }} + individualId={(val) => { + setIndividualId(val); + }} + isBtnDisabled={(val) => { + setIsBtnDisabled(val); + }} + i18nPrefix={i18nKeyPrefix1} + /> + ) : ( + inputFields.map((field) => ( + + )) + )} +
+ + {showCaptcha && ( +
+ +
+ )} + )} {status.state === states.LOADING && errorBanner === null && ( -
+
)} -
+
{status.state === states.AUTHENTICATING && errorBanner === null && (
-
+
{ + const [selectedOption, setSelectedOption] = useState(); + const { t, i18n } = useTranslation("translation", { + keyPrefix: "loginOptions", + }); + + // Parsing the current URL into a URL object + const urlObj = new URL(window.location.href); + + // Extracting the value of the 'state' and 'nonce' parameter from the URL + const state = urlObj.searchParams.get("state"); + const nonce = urlObj.searchParams.get("nonce"); + + // Extracting the hash part of the URL (excluding the # character) + const code = urlObj.hash.substring(1); + + // Decoding the Base64-encoded string (excluding the first character) + const decodedBase64 = atob(code); + + // Creating an instance of the openIDConnectService with decodedBase64, nonce, and state parameters + const oidcService = new openIDConnectService( + JSON.parse(decodedBase64), + nonce, + state + ); + + let loginIDs = oidcService.getEsignetConfiguration( + configurationKeys.loginIdOptions + ); + + if (!loginIDs || loginIDs.length === 0) { + loginIDs = [ + { + id: "vid", + svg: "vid_icon", + prefixes: "", + postfix: "@ID", + maxLength: "", + regex: "", + }, + ]; + } + + // const loginIDs = [ + // { + // id: "mobile", + // svg: "mobile_icon", + // prefixes: [ + // { + // label: "IND", + // value: "+91", + // maxLength: "", + // regex: "", + // }, + // { + // label: "KHM", + // value: "+855", + // regex: "^[a-zA-Z]+$" + // }, + // { + // label: "USA", + // value: "+1", + // maxLength: "5" + // }, + // ], + // postfix: "@phone", + // maxLength: 10, + // regex: "^\\d*$", + // }, + // { + // id: "nrc", + // svg: "nrc_id_icon", + // prefixes: "", + // postfix: "@NRC", + // maxLength: 5, + // regex: "^\\d*$", + // }, + // { + // id: "vid", + // svg: "vid_icon", + // prefixes: "", + // postfix: "@ID", + // maxLength: "", + // regex: "^\\d*$", + // }, + // { + // id: "email", + // svg: "email_icon", + // prefixes: "", + // postfix: "@email", + // maxLength: 10, + // regex: "", + // }, + // ]; + + const [iconsMap, setIconsMap] = useState({}); // To store preloaded SVGs + const fetchSvg = async (path) => { + try { + const response = await fetch(`/images/${path}.svg`); + if (!response.ok) { + throw new Error("Failed to fetch SVG"); + } + let svgText = await response.text(); + + // Ensure that all stroke attributes are set to "currentColor" + svgText = svgText.replace(/stroke="[^"]*"/g, 'stroke="currentColor"'); + + return svgText; + } catch (error) { + return ""; // Return empty string if error occurs + } + }; + + useEffect(() => { + const preloadIcons = async () => { + const svgPromises = loginIDs.map(async (option) => { + const svgContent = await fetchSvg(option.svg); + return { id: option.id, svgContent }; + }); + + const svgResults = await Promise.all(svgPromises); + const svgMap = svgResults.reduce((acc, { id, svgContent }) => { + acc[id] = svgContent; + return acc; + }, {}); + + setIconsMap(svgMap); // Store preloaded SVGs + setSelectedOption(options[0]); + }; + + preloadIcons(); + }, []); + + // Transform the array into the desired structure + const options = loginIDs.map((option) => ({ + ...option, // Spread existing properties like id, prefix, postfix, etc. + svg: ( +
+ ), // Modify the svg + input_label: t(`input.label.${option.id}`), // Add input_label + input_placeholder: t(`input.placeholder.${option.id}`), // Add input_placeholder + })); + + useEffect(() => { + setSelectedOption((prevState) => { + if (!prevState) return null; // No selection to update + + return { + ...prevState, + input_label: t(`input.label.${prevState.id}`), + input_placeholder: t(`input.placeholder.${prevState.id}`), + }; + }); + }, [i18n.language]); + + props.currentLoginID(selectedOption); + + return ( + selectedOption && ( +
+
+ {options.map((option, index) => ( + + ))} +
+
+ ) + ); +}; + +export default LoginIDOptions; diff --git a/oidc-ui/src/components/LoginQRCode.js b/oidc-ui/src/components/LoginQRCode.js index 3781861fd..f68d876de 100644 --- a/oidc-ui/src/components/LoginQRCode.js +++ b/oidc-ui/src/components/LoginQRCode.js @@ -14,7 +14,7 @@ import langConfigService from "../services/langConfigService"; import redirectOnError from "../helpers/redirectOnError"; var linkAuthTriggered = false; -const langConfig = await langConfigService.getEnLocaleConfiguration(); +const langConfig = await langConfigService.getEnLocaleConfiguration(); export default function LoginQRCode({ walletDetail, @@ -22,14 +22,18 @@ export default function LoginQRCode({ openIDConnectService, backButtonDiv, i18nKeyPrefix1 = "LoginQRCode", - i18nKeyPrefix2 = "errors" + i18nKeyPrefix2 = "errors", }) { const post_GenerateLinkCode = linkAuthService.post_GenerateLinkCode; const post_LinkStatus = linkAuthService.post_LinkStatus; const post_AuthorizationCode = linkAuthService.post_AuthorizationCode; - const { t: t1 } = useTranslation("translation", { keyPrefix: i18nKeyPrefix1 }); - const { t: t2 } = useTranslation("translation", { keyPrefix: i18nKeyPrefix2 }); + const { t: t1 } = useTranslation("translation", { + keyPrefix: i18nKeyPrefix1, + }); + const { t: t2 } = useTranslation("translation", { + keyPrefix: i18nKeyPrefix2, + }); const [qr, setQr] = useState(""); const [status, setStatus] = useState({ state: states.LOADED, msg: "" }); @@ -37,7 +41,7 @@ export default function LoginQRCode({ const [qrCodeTimeOut, setQrCodeTimeout] = useState(); const [errorBanner, setErrorBanner] = useState(null); const [qrRedirectUrl, setQrRedirectUrl] = useState(""); - + const linkedTransactionExpireInSec = openIDConnectService.getEsignetConfiguration( configurationKeys.linkedTransactionExpireInSecs @@ -81,7 +85,7 @@ export default function LoginQRCode({ ); setQrRedirectUrl(text); - + const canvas = document.createElement("canvas"); QRCode.toCanvas( canvas, @@ -97,7 +101,7 @@ export default function LoginQRCode({ if (err) { setErrorBanner({ errorCode: "wallet.link_code_refresh_failed", - show: true + show: true, }); return; } @@ -152,7 +156,7 @@ export default function LoginQRCode({ if (qrCodeRefreshCount >= walletQrCodeAutoRefreshLimit) { setErrorBanner({ errorCode: "wallet.qr_code_expired", - show: true + show: true, }); return; } @@ -168,22 +172,21 @@ export default function LoginQRCode({ ); if (errors != null && errors.length > 0) { - - let errorCodeCondition = langConfig.errors.wallet[errors[0].errorCode] !== undefined && langConfig.errors.wallet[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.wallet[errors[0].errorCode] !== undefined && + langConfig.errors.wallet[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `wallet.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } } else { @@ -197,7 +200,7 @@ export default function LoginQRCode({ */ setErrorBanner({ errorCode: "wallet.invalid_qrcode_config", - show: true + show: true, }); return; } @@ -227,7 +230,7 @@ export default function LoginQRCode({ } catch (error) { setErrorBanner({ errorCode: "wallet.link_code_refresh_failed", - show: true + show: true, }); } }; @@ -252,21 +255,25 @@ export default function LoginQRCode({ //return if invalid transactionId; if (linkStatusResponse?.errors[0] === "invalid_transaction") { + let errorCodeCondition = + langConfig.errors.password[ + linkStatusResponse.errors[0].errorCode + ] !== undefined && + langConfig.errors.password[ + linkStatusResponse.errors[0].errorCode + ] !== null; - let errorCodeCondition = langConfig.errors.password[linkStatusResponse.errors[0].errorCode] !== undefined && langConfig.errors.password[linkStatusResponse.errors[0].errorCode] !== null; - - if (errorCodeCondition) { - setErrorBanner({ - errorCode: `password.${linkStatusResponse.errors[0].errorCode}`, - show: true - }); - } - else { - setErrorBanner({ - errorCode: `${linkStatusResponse.errors[0].errorCode}`, - show: true - }); - } + if (errorCodeCondition) { + setErrorBanner({ + errorCode: `password.${linkStatusResponse.errors[0].errorCode}`, + show: true, + }); + } else { + setErrorBanner({ + errorCode: `${linkStatusResponse.errors[0].errorCode}`, + show: true, + }); + } return; } @@ -284,22 +291,28 @@ export default function LoginQRCode({ linkStatusResponse?.errors != null && linkStatusResponse?.length > 0 ) { - - let errorCodeCondition = langConfig.errors.password[linkStatusResponse.errors[0].errorCode] !== undefined && langConfig.errors.password[linkStatusResponse.errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.password[linkStatusResponse.errors[0].errorCode] !== + undefined && + langConfig.errors.password[linkStatusResponse.errors[0].errorCode] !== + null; if (errorCodeCondition) { setErrorBanner({ errorCode: `password.${linkStatusResponse.errors[0].errorCode}`, - show: true + show: true, }); - } - else if (linkStatusResponse.errors[0].errorCode === "invalid_transaction") { - redirectOnError(linkStatusResponse.errors[0].errorCode, t2(`${linkStatusResponse.errors[0].errorCode}`)); - } - else { + } else if ( + linkStatusResponse.errors[0].errorCode === "invalid_transaction" + ) { + redirectOnError( + linkStatusResponse.errors[0].errorCode, + t2(`${linkStatusResponse.errors[0].errorCode}`) + ); + } else { setErrorBanner({ errorCode: `${linkStatusResponse.errors[0].errorCode}`, - show: true + show: true, }); } } else if (linkStatusResponse?.response) { @@ -307,7 +320,7 @@ export default function LoginQRCode({ if (response.linkStatus != "LINKED") { setErrorBanner({ errorCode: "wallet.failed_to_link", - show: true + show: true, }); } else { setErrorBanner(null); @@ -323,7 +336,7 @@ export default function LoginQRCode({ } catch (error) { setErrorBanner({ errorCode: "wallet.link_code_refresh_failed", - show: true + show: true, }); } }; @@ -349,19 +362,21 @@ export default function LoginQRCode({ //return if invalid transactionId; if (linkAuthResponse?.errors[0] === "invalid_transaction") { - - let errorCodeCondition = langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== undefined && langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== + undefined && + langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== + null; if (errorCodeCondition) { setErrorBanner({ errorCode: `password.${linkAuthResponse.errors[0].errorCode}`, - show: true + show: true, }); - } - else { + } else { setErrorBanner({ errorCode: `${linkAuthResponse.errors[0].errorCode}`, - show: true + show: true, }); } return; @@ -379,7 +394,7 @@ export default function LoginQRCode({ if (!linkAuthResponse || !linkAuthResponse?.response) { setErrorBanner({ errorCode: "authorization_failed", - show: true + show: true, }); return; } @@ -388,21 +403,28 @@ export default function LoginQRCode({ linkAuthResponse?.errors != null && linkAuthResponse?.errors.length > 0 ) { - let errorCodeCondition = langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== undefined && langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== + undefined && + langConfig.errors.password[linkAuthResponse.errors[0].errorCode] !== + null; if (errorCodeCondition) { setErrorBanner({ errorCode: `password.${linkAuthResponse.errors[0].errorCode}`, - show: true + show: true, }); - } - else if (linkAuthResponse.errors[0].errorCode === "invalid_transaction") { - redirectOnError(linkAuthResponse.errors[0].errorCode, t2(`${linkAuthResponse.errors[0].errorCode}`)); - } - else { + } else if ( + linkAuthResponse.errors[0].errorCode === "invalid_transaction" + ) { + redirectOnError( + linkAuthResponse.errors[0].errorCode, + t2(`${linkAuthResponse.errors[0].errorCode}`) + ); + } else { setErrorBanner({ errorCode: `${linkAuthResponse.errors[0].errorCode}`, - show: true + show: true, }); } } else { @@ -430,7 +452,7 @@ export default function LoginQRCode({ } catch (error) { setErrorBanner({ errorCode: "wallet.link_code_status_failed", - show: true + show: true, }); } }; @@ -443,22 +465,29 @@ export default function LoginQRCode({ window.onbeforeunload = null; window.open(qrRedirectUrl); }; - + return ( <> -
+
{backButtonDiv} +
+ {`${t1("login_with_id", { + currentID: walletDetail[walletConfigKeys.walletName], + })}`} +
{errorBanner !== null && ( - +
+ +
)} -
+
{error && (
@@ -481,12 +510,29 @@ export default function LoginQRCode({
)} + {qr && ( -
- -
+ <> +

+ Scan the QR code with {walletDetail[walletConfigKeys.walletName]}{" "} + app +

+
+ +
+ )} {status.state === states.LOADING && error === null && (
@@ -502,7 +548,7 @@ export default function LoginQRCode({
{/**footer */} -
+

{t1("dont_have_wallet", { diff --git a/oidc-ui/src/components/NavHeader.js b/oidc-ui/src/components/NavHeader.js index 38d92ab27..48ad9740f 100644 --- a/oidc-ui/src/components/NavHeader.js +++ b/oidc-ui/src/components/NavHeader.js @@ -182,7 +182,7 @@ export default function NavHeader({ langOptions, i18nKeyPrefix = "header" }) { diff --git a/oidc-ui/src/components/Otp.js b/oidc-ui/src/components/Otp.js index 248fbe242..41b72d585 100644 --- a/oidc-ui/src/components/Otp.js +++ b/oidc-ui/src/components/Otp.js @@ -2,6 +2,7 @@ import { useState } from "react"; import { useTranslation } from "react-i18next"; import OtpGet from "./OtpGet"; import OtpVerify from "./OtpVerify"; +import { configurationKeys } from "../constants/clientConstants"; const OTPStatusEnum = { getOtp: "GETOTP", @@ -19,30 +20,78 @@ export default function Otp({ const [otpStatus, setOtpStatus] = useState(OTPStatusEnum.getOtp); const [otpResponse, setOtpResponse] = useState(""); - const [vid, setVid] = useState(""); + const [ID, setId] = useState(""); + const [currentLoginID, setCurrentLoginID] = useState(""); + const [selectedCountryOption, setSelectedCountryOption] = useState(""); - const onOtpSent = async (vid, response) => { + const onOtpSent = async (ID, response, loginID, selectedCountry) => { + setId(ID); setOtpResponse(response); - setVid(vid); setOtpStatus(OTPStatusEnum.verifyOtp); + setCurrentLoginID(loginID); + setSelectedCountryOption(selectedCountry); }; + var loginIDs = openIDConnectService.getEsignetConfiguration( + configurationKeys.loginIdOptions + ); + + if (!loginIDs || loginIDs.length === 0) { + loginIDs = [{ id: "vid" }]; + } + return ( <> -

+
{otpStatus === OTPStatusEnum.verifyOtp ? ( -
+
+
+ {t("enter_otp")} +
) : ( backButtonDiv )} + {otpStatus === OTPStatusEnum.getOtp && ( +
+ {loginIDs && loginIDs.length > 1 + ? t("multiple_login_ids") + : `${t("login_with_id", { + currentID: `${t(loginIDs[0].id)}` + })}`} +
+ )}
{otpStatus === OTPStatusEnum.getOtp && ( @@ -58,7 +107,9 @@ export default function Otp({ diff --git a/oidc-ui/src/components/OtpGet.js b/oidc-ui/src/components/OtpGet.js index caf2ac0d5..d2d9125ad 100644 --- a/oidc-ui/src/components/OtpGet.js +++ b/oidc-ui/src/components/OtpGet.js @@ -9,6 +9,8 @@ import ReCAPTCHA from "react-google-recaptcha"; import ErrorBanner from "../common/ErrorBanner"; import langConfigService from "../services/langConfigService"; import redirectOnError from "../helpers/redirectOnError"; +import LoginIDOptions from "./LoginIDOptions"; +import InputWithPrefix from "./InputWithPrefix"; const langConfig = await langConfigService.getEnLocaleConfiguration(); @@ -19,9 +21,8 @@ export default function OtpGet({ onOtpSent, i18nKeyPrefix1 = "otp", i18nKeyPrefix2 = "errors", - getCaptchaToken + getCaptchaToken, }) { - const { t: t1, i18n } = useTranslation("translation", { keyPrefix: i18nKeyPrefix1, }); @@ -29,7 +30,7 @@ export default function OtpGet({ const { t: t2 } = useTranslation("translation", { keyPrefix: i18nKeyPrefix2, }); - + const inputCustomClass = "h-10 border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[hsla(0, 0%, 51%)] focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-muted-light-gray shadow-none"; @@ -40,12 +41,14 @@ export default function OtpGet({ const post_SendOtp = authService.post_SendOtp; const commaSeparatedChannels = - openIDConnectService.getEsignetConfiguration(configurationKeys.sendOtpChannels) ?? - process.env.REACT_APP_SEND_OTP_CHANNELS; + openIDConnectService.getEsignetConfiguration( + configurationKeys.sendOtpChannels + ) ?? process.env.REACT_APP_SEND_OTP_CHANNELS; const captchaEnableComponents = - openIDConnectService.getEsignetConfiguration(configurationKeys.captchaEnableComponents) ?? - process.env.REACT_APP_CAPTCHA_ENABLE; + openIDConnectService.getEsignetConfiguration( + configurationKeys.captchaEnableComponents + ) ?? process.env.REACT_APP_CAPTCHA_ENABLE; const captchaEnableComponentsList = captchaEnableComponents .split(",") @@ -56,17 +59,24 @@ export default function OtpGet({ ); const captchaSiteKey = - openIDConnectService.getEsignetConfiguration(configurationKeys.captchaSiteKey) ?? - process.env.REACT_APP_CAPTCHA_SITE_KEY; + openIDConnectService.getEsignetConfiguration( + configurationKeys.captchaSiteKey + ) ?? process.env.REACT_APP_CAPTCHA_SITE_KEY; - const [loginState, setLoginState] = useState(fieldsState); const [status, setStatus] = useState({ state: states.LOADED, msg: "" }); const [errorBanner, setErrorBanner] = useState(null); - const [inputError, setInputError] = useState(null); const [captchaToken, setCaptchaToken] = useState(null); const _reCaptchaRef = useRef(null); + const [currentLoginID, setCurrentLoginID] = useState(null); + const [countryCode, setCountryCode] = useState(null); + const [individualId, setIndividualId] = useState(null); + const [selectedCountry, setSelectedCountry] = useState(null); + const [isValid, setIsValid] = useState(false); + const [isBtnDisabled, setIsBtnDisabled] = useState(true); + const [prevLanguage, setPrevLanguage] = useState(i18n.language); + useEffect(() => { let loadComponent = async () => { i18n.on("languageChanged", function (lng) { @@ -88,10 +98,87 @@ export default function OtpGet({ getCaptchaToken(value); }; + function getPropertiesForLoginID(loginID, label) { + const { prefixes, maxLength: outerMaxLength, regex: outerRegex } = loginID; + + if (Array.isArray(prefixes) && prefixes.length > 0) { + const prefix = prefixes.find((prefix) => prefix.label === label); + if (prefix) { + return { + maxLength: prefix.maxLength || outerMaxLength || null, + regex: prefix.regex || outerRegex || null, + }; + } + } + + return { + maxLength: outerMaxLength || null, + regex: outerRegex || null, + }; + } + const handleChange = (e) => { - setLoginState({ ...loginState, [e.target.id]: e.target.value }); + setIsValid(true); + onCloseHandle(); + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + const trimmedValue = e.target.value.trim(); + + let newValue = regex + ? trimmedValue + .split("") + .filter((char) => regex.test(char)) + .join("") + : trimmedValue; + + setIndividualId(newValue); // Update state with the visible valid value + + setIsBtnDisabled( + !( + ( + (!maxLength && !regex) || // Case 1: No maxLength, no regex + (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only + (maxLength && + regex && + newValue.length === parseInt(maxLength) && + regex.test(newValue)) + ) // Case 4: Both maxLength and regex + ) + ); + }; + + const handleBlur = (e) => { + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + setIsValid( + (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!regex || regex.test(e.target.value.trim())) + ); }; - + + useEffect(() => { + if (i18n.language === prevLanguage) { + setIndividualId(null); + setIsValid(false); + setIsBtnDisabled(true); + onCloseHandle(); + if (currentLoginID && currentLoginID.prefixes) { + setSelectedCountry(currentLoginID.prefixes[0]); + } + } else { + setPrevLanguage(i18n.language); + } + }, [currentLoginID]); + /** * Reset the captcha widget * & its token value @@ -100,20 +187,28 @@ export default function OtpGet({ _reCaptchaRef.current.reset(); setCaptchaToken(null); getCaptchaToken(null); - } + }; const sendOTP = async () => { try { - let transactionId = openIDConnectService.getTransactionId(); - let vid = fields[0].prefix + loginState["Otp_mosip-vid"] + fields[0].postfix; + let prefix = currentLoginID.prefixes + ? typeof currentLoginID.prefixes === "object" + ? countryCode + : currentLoginID.prefixes + : ""; + let id = individualId; + let postfix = currentLoginID.postfix ? currentLoginID.postfix : ""; + + let ID = prefix + id + postfix; + // let ID = id; let otpChannels = commaSeparatedChannels.split(",").map((x) => x.trim()); setStatus({ state: states.LOADING, msg: "sending_otp_msg" }); const sendOtpResponse = await post_SendOtp( transactionId, - vid, + ID, otpChannels, captchaToken ); @@ -122,22 +217,21 @@ export default function OtpGet({ const { response, errors } = sendOtpResponse; if (errors != null && errors.length > 0) { - - let errorCodeCondition = langConfig.errors.otp[errors[0].errorCode] !== undefined && langConfig.errors.otp[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.otp[errors[0].errorCode] !== undefined && + langConfig.errors.otp[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `otp.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } if (showCaptcha) { @@ -145,13 +239,18 @@ export default function OtpGet({ } return; } else { - onOtpSent(loginState["Otp_mosip-vid"], response); + onOtpSent( + { prefix: prefix, id: id, postfix: postfix }, + response, + currentLoginID, + selectedCountry + ); setErrorBanner(null); } } catch (error) { setErrorBanner({ errorCode: "otp.send_otp_failed_msg", - show: true + show: true, }); setStatus({ state: states.ERROR, msg: "" }); if (showCaptcha) { @@ -164,70 +263,101 @@ export default function OtpGet({ setErrorBanner(null); }; - const onBlurChange = (e, errors) => { - setInputError(errors.length === 0 ? null : errors); - } - return ( <> {errorBanner !== null && ( - - )} - -
- {fields.map((field) => ( - + - ))} - - {showCaptcha && ( -
- + )} + { + setCurrentLoginID(value); + }} + /> + {currentLoginID ? ( +
+ {currentLoginID?.prefixes?.length > 0 ? ( + { + setCountryCode(val); + }} + selectedCountry={(val) => { + setSelectedCountry(val); + }} + individualId={(val) => { + setIndividualId(val); + }} + isBtnDisabled={(val) => { + setIsBtnDisabled(val); + }} + i18nPrefix={i18nKeyPrefix1} + /> + ) : ( + <> + {fields.map((field) => ( + + ))} + + )} + + {showCaptcha && ( +
+ +
+ )} + +
+
- )} - -
- -
- {status.state === states.LOADING && ( - - )} -
+ {status.state === states.LOADING && ( + + )} +
+ ) : ( +
+ +
+ )} ); } diff --git a/oidc-ui/src/components/OtpVerify.js b/oidc-ui/src/components/OtpVerify.js index ee81dd33a..0bdb5b773 100644 --- a/oidc-ui/src/components/OtpVerify.js +++ b/oidc-ui/src/components/OtpVerify.js @@ -10,7 +10,6 @@ import { configurationKeys, } from "../constants/clientConstants"; import { useTranslation } from "react-i18next"; -import InputWithImage from "./InputWithImage"; import PinInput from "react-pin-input"; import ErrorBanner from "../common/ErrorBanner"; import langConfigService from "../services/langConfigService"; @@ -22,18 +21,18 @@ const langConfig = await langConfigService.getEnLocaleConfiguration(); export default function OtpVerify({ param, otpResponse, - vid, + ID, authService, openIDConnectService, i18nKeyPrefix1 = "otp", i18nKeyPrefix2 = "errors", }) { - - const { t: t1, i18n } = useTranslation("translation", { keyPrefix: i18nKeyPrefix1 }); - const { t: t2 } = useTranslation("translation", { keyPrefix: i18nKeyPrefix2 }); - - const inputCustomClass = - "h-10 border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[hsla(0, 0%, 51%)] focus-visible:outline-none disabled:cursor-not-allowed disabled:bg-muted-light-gray shadow-none text-gray-400"; + const { t: t1, i18n } = useTranslation("translation", { + keyPrefix: i18nKeyPrefix1, + }); + const { t: t2 } = useTranslation("translation", { + keyPrefix: i18nKeyPrefix2, + }); const fields = param; let fieldsState = {}; @@ -44,17 +43,18 @@ export default function OtpVerify({ const buildRedirectParams = authService.buildRedirectParams; const resendOtpTimeout = - openIDConnectService.getEsignetConfiguration(configurationKeys.resendOtpTimeout) ?? - process.env.REACT_APP_RESEND_OTP_TIMEOUT_IN_SEC; + openIDConnectService.getEsignetConfiguration( + configurationKeys.resendOtpTimeout + ) ?? process.env.REACT_APP_RESEND_OTP_TIMEOUT_IN_SEC; const commaSeparatedChannels = - openIDConnectService.getEsignetConfiguration(configurationKeys.sendOtpChannels) ?? - process.env.REACT_APP_SEND_OTP_CHANNELS; + openIDConnectService.getEsignetConfiguration( + configurationKeys.sendOtpChannels + ) ?? process.env.REACT_APP_SEND_OTP_CHANNELS; const otpLengthValue = openIDConnectService.getEsignetConfiguration(configurationKeys.otpLength) ?? process.env.REACT_APP_OTP_LENGTH; const otpLength = parseInt(otpLengthValue); - const [loginState, setLoginState] = useState(fieldsState); const [status, setStatus] = useState({ state: states.LOADED, msg: "" }); const [resendOtpCountDown, setResendOtpCountDown] = useState(); const [showResendOtp, setShowResendOtp] = useState(false); @@ -65,13 +65,15 @@ export default function OtpVerify({ const [otpSentMobile, setOtpSentMobile] = useState(""); const [errorBanner, setErrorBanner] = useState(null); - const captchaSiteKey = - (openIDConnectService.getEsignetConfiguration(configurationKeys.captchaSiteKey) ?? - process.env.REACT_APP_CAPTCHA_SITE_KEY); + const captchaSiteKey = + openIDConnectService.getEsignetConfiguration( + configurationKeys.captchaSiteKey + ) ?? process.env.REACT_APP_CAPTCHA_SITE_KEY; const captchaEnableComponents = - openIDConnectService.getEsignetConfiguration(configurationKeys.captchaEnableComponents) ?? - process.env.REACT_APP_CAPTCHA_ENABLE; + openIDConnectService.getEsignetConfiguration( + configurationKeys.captchaEnableComponents + ) ?? process.env.REACT_APP_CAPTCHA_ENABLE; const captchaEnableComponentsList = captchaEnableComponents .split(",") @@ -87,10 +89,6 @@ export default function OtpVerify({ const navigate = useNavigate(); - const handleChange = (e) => { - setLoginState({ ...loginState, [e.target.id]: e.target.value }); - }; - const handleSubmit = (e) => { e.preventDefault(); authenticateUser(); @@ -102,13 +100,13 @@ export default function OtpVerify({ }; /** - * Reset the captcha widget - * & its token value - */ + * Reset the captcha widget + * & its token value + */ const resetCaptcha = () => { _reCaptchaRef.current?.reset(); setCaptchaToken(null); - } + }; const handleCaptchaChange = (value) => { setCaptchaToken(value); @@ -123,14 +121,14 @@ export default function OtpVerify({ let transactionId = openIDConnectService.getTransactionId(); let otpChannels = commaSeparatedChannels.split(",").map((x) => x.trim()); - let idvid = fields[0].prefix + vid + fields[0].postfix; + let id = ID.prefix + ID.id + ID.postfix; let tempCaptchaToken = captchaToken; setCaptchaToken(null); setStatus({ state: states.LOADING, msg: "sending_otp_msg" }); const sendOtpResponse = await post_SendOtp( transactionId, - idvid, + id, otpChannels, tempCaptchaToken ); @@ -139,22 +137,21 @@ export default function OtpVerify({ const { response, errors } = sendOtpResponse; if (errors != null && errors.length > 0) { - - let errorCodeCondition = langConfig.errors.otp[errors[0].errorCode] !== undefined && langConfig.errors.otp[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.otp[errors[0].errorCode] !== undefined && + langConfig.errors.otp[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `otp.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } return; @@ -167,7 +164,7 @@ export default function OtpVerify({ } catch (error) { setErrorBanner({ errorCode: "otp.send_otp_failed_msg", - show: true + show: true, }); setStatus({ state: states.ERROR, msg: "" }); } @@ -232,6 +229,7 @@ export default function OtpVerify({ return { min: min, sec: sec }; }; + //Handle Login API Integration here const authenticateUser = async () => { try { @@ -245,16 +243,16 @@ export default function OtpVerify({ { authFactorType: challengeType, challenge: challenge, - format: challengeFormat + format: challengeFormat, }, ]; - let idvid = fields[0].prefix + vid + fields[0].postfix; + let id = ID.prefix + ID.id + ID.postfix; setStatus({ state: states.LOADING, msg: "authenticating_msg" }); const authenticateResponse = await post_AuthenticateUser( transactionId, - idvid, + id, challengeList ); setStatus({ state: states.LOADED, msg: "" }); @@ -262,22 +260,21 @@ export default function OtpVerify({ const { response, errors } = authenticateResponse; if (errors != null && errors.length > 0) { - - let errorCodeCondition = langConfig.errors.otp[errors[0].errorCode] !== undefined && langConfig.errors.otp[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.otp[errors[0].errorCode] !== undefined && + langConfig.errors.otp[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `otp.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } return; @@ -286,7 +283,12 @@ export default function OtpVerify({ let nonce = openIDConnectService.getNonce(); let state = openIDConnectService.getState(); - let params = buildRedirectParams(nonce, state, openIDConnectService.getOAuthDetails(), response.consentAction); + let params = buildRedirectParams( + nonce, + state, + openIDConnectService.getOAuthDetails(), + response.consentAction + ); navigate(process.env.PUBLIC_URL + "/claim-details" + params, { replace: true, @@ -295,7 +297,7 @@ export default function OtpVerify({ } catch (error) { setErrorBanner({ errorCode: "authentication_failed_msg", - show: true + show: true, }); setStatus({ state: states.ERROR, msg: "" }); } @@ -307,15 +309,17 @@ export default function OtpVerify({ margin: "0 5px", border: "", borderBottom: "2px solid #0284c7", - color: "#0284c7" + color: "#0284c7", }; if (window.screen.availWidth <= 375) { - styles = { ...styles, width: "2em" } + styles = { ...styles, width: "2em" }; } const onCloseHandle = () => { setErrorBanner(null); + setOtpSentMobile(""); + setOtpSentEmail(""); }; return ( @@ -328,38 +332,56 @@ export default function OtpVerify({ /> )} - -
- {fields.map((field) => ( - - ))} + +
+ {status.state !== states.LOADING && !errorBanner && ( +
+ {otpSentMobile && otpSentEmail ? ( + <> + {t1("otp_sent_msg", { + otpLength: otpLength, + })} +
+ {otpSentMobile} + {t1("and")} + {otpSentEmail} +
+ + ) : otpSentMobile ? ( + <> + {t1("otp_sent_msg", { + otpLength: otpLength, + })} +
{otpSentMobile}
+ + ) : ( + <> + {t1("otp_sent_msg", { + otpLength: otpLength, + })} +
{otpSentEmail}
+ + )} +
+ )} + + {status.state === states.LOADING && ( + + )}
-
+
{ setOtpValue(value); }} + secret + secretDelay={1} type="numeric" inputMode="number" style={{ padding: "5px 0px" }} @@ -374,35 +396,6 @@ export default function OtpVerify({ />
-
- {status.state !== states.LOADING && !errorBanner && ( - - {otpSentEmail && otpSentMobile - ? t1("otp_sent_msg", { - otpChannels: t1("mobile_email_placeholder", { - mobileNumber: otpSentMobile, - emailAddress: otpSentEmail, - }), - }) - : otpSentEmail - ? t1("otp_sent_msg", { - otpChannels: t1("email_placeholder", { - emailAddress: otpSentEmail, - }), - }) - : t1("otp_sent_msg", { - otpChannels: t1("mobile_placeholder", { - mobileNumber: otpSentMobile, - }), - })} - - )} - - {status.state === states.LOADING && ( - - )} -
- {showCaptcha && showResendOtp && (
+ {showTimer && ( - + {resendOtpCountDown} )} - {showResendOtp && ( + +
- )} +
); diff --git a/oidc-ui/src/components/Password.js b/oidc-ui/src/components/Password.js index 62f1f7372..17f6731ba 100644 --- a/oidc-ui/src/components/Password.js +++ b/oidc-ui/src/components/Password.js @@ -16,12 +16,14 @@ import ReCAPTCHA from "react-google-recaptcha"; import ErrorBanner from "../common/ErrorBanner"; import langConfigService from "../services/langConfigService"; import redirectOnError from "../helpers/redirectOnError"; +import LoginIDOptions from "./LoginIDOptions"; +import InputWithPrefix from "./InputWithPrefix"; const fields = passwordFields; let fieldsState = {}; fields.forEach((field) => (fieldsState["Password_" + field.id] = "")); -const langConfig = await langConfigService.getEnLocaleConfiguration(); +const langConfig = await langConfigService.getEnLocaleConfiguration(); export default function Password({ param, @@ -31,7 +33,6 @@ export default function Password({ i18nKeyPrefix1 = "password", i18nKeyPrefix2 = "errors", }) { - const { t: t1, i18n } = useTranslation("translation", { keyPrefix: i18nKeyPrefix1, }); @@ -47,23 +48,38 @@ export default function Password({ const post_AuthenticateUser = authService.post_AuthenticateUser; const buildRedirectParams = authService.buildRedirectParams; - const [loginState, setLoginState] = useState(fieldsState); const [errorBanner, setErrorBanner] = useState(null); const [inputErrorBanner, setInputErrorBanner] = useState([]); const [status, setStatus] = useState(states.LOADED); - const [invalidState, setInvalidState] = useState(true); const [forgotPassword, setForgotPassword] = useState(false); const [forgotPasswordURL, setForgotPasswordURL] = useState(""); + const [password, setPassword] = useState(null); + const [currentLoginID, setCurrentLoginID] = useState(null); + const [countryCode, setCountryCode] = useState(null); + const [individualId, setIndividualId] = useState(null); + const [selectedCountry, setSelectedCountry] = useState(null); + const [isValid, setIsValid] = useState(false); + const [isBtnDisabled, setIsBtnDisabled] = useState(true); + const [prevLanguage, setPrevLanguage] = useState(i18n.language); + + var loginIDs = openIDConnectService.getEsignetConfiguration( + configurationKeys.loginIdOptions + ); + let forgotPasswordConfig = openIDConnectService.getEsignetConfiguration( configurationKeys.forgotPasswordConfig ); - + useEffect(() => { - if(forgotPasswordConfig?.[configurationKeys.forgotPassword]) { + if (forgotPasswordConfig?.[configurationKeys.forgotPassword]) { setForgotPassword(true); - setForgotPasswordURL(forgotPasswordConfig[configurationKeys.forgotPasswordURL] + "#" + authService.getAuthorizeQueryParam()) + setForgotPasswordURL( + forgotPasswordConfig[configurationKeys.forgotPasswordURL] + + "#" + + authService.getAuthorizeQueryParam() + ); } }, [i18n.language]); @@ -74,15 +90,103 @@ export default function Password({ const navigate = useNavigate(); + function getPropertiesForLoginID(loginID, label) { + const { prefixes, maxLength: outerMaxLength, regex: outerRegex } = loginID; + + if (Array.isArray(prefixes) && prefixes.length > 0) { + const prefix = prefixes.find((prefix) => prefix.label === label); + if (prefix) { + return { + maxLength: prefix.maxLength || outerMaxLength || null, + regex: prefix.regex || outerRegex || null, + }; + } + } + + return { + maxLength: outerMaxLength || null, + regex: outerRegex || null, + }; + } + const handleChange = (e) => { + setIsValid(true); onCloseHandle(); - setLoginState({ ...loginState, [e.target.id]: e.target.value }); + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + const trimmedValue = e.target.value.trim(); + + let newValue = regex + ? trimmedValue + .split("") + .filter((char) => regex.test(char)) + .join("") + : trimmedValue; + + setIndividualId(newValue); // Update state with the visible valid value + if (e.target.type === "password") { + setPassword(e.target.value.trim()); + } else { + setIndividualId(newValue); + } + + setIsBtnDisabled( + !( + ( + (!maxLength && !regex) || // Case 1: No maxLength, no regex + (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only + (maxLength && + regex && + newValue.length === parseInt(maxLength) && + regex.test(newValue)) + ) // Case 4: Both maxLength and regex + ) + ); + }; + + const handleBlur = (e) => { + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + setIsValid( + (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!regex || regex.test(e.target.value.trim())) + ); + }; + + const handlePasswordChange = (e) => { + onCloseHandle(); + setPassword(e.target.value.trim()); }; - + + useEffect(() => { + if (i18n.language === prevLanguage) { + setIndividualId(null); + setPassword(null); + setIsValid(false); + setIsBtnDisabled(true); + onCloseHandle(); + if (currentLoginID && currentLoginID.prefixes) { + setSelectedCountry(currentLoginID.prefixes[0]); + } + } else { + setPrevLanguage(i18n.language); + } + }, [currentLoginID]); + const handleSubmit = (e) => { e.preventDefault(); authenticateUser(); }; + const captchaEnableComponents = openIDConnectService.getEsignetConfiguration( configurationKeys.captchaEnableComponents @@ -114,16 +218,24 @@ export default function Password({ const resetCaptcha = () => { _reCaptchaRef.current.reset(); setCaptchaToken(null); - } + }; //Handle Login API Integration here const authenticateUser = async () => { try { let transactionId = openIDConnectService.getTransactionId(); - let uin = fields[0].prefix + loginState["Password_mosip-uin"] + fields[0].postfix; + let prefix = currentLoginID.prefixes + ? typeof currentLoginID.prefixes === "object" + ? countryCode + : currentLoginID.prefixes + : ""; + let id = individualId; + let postfix = currentLoginID.postfix ? currentLoginID.postfix : ""; + + let ID = prefix + id + postfix; let challengeType = challengeTypes.pwd; - let challenge = loginState["Password_password"]; + let challenge = password; let challengeFormat = challengeFormats.pwd; let challengeList = [ @@ -138,7 +250,7 @@ export default function Password({ const authenticateResponse = await post_AuthenticateUser( transactionId, - uin, + ID, challengeList, captchaToken ); @@ -146,24 +258,23 @@ export default function Password({ setStatus(states.LOADED); const { response, errors } = authenticateResponse; - + if (errors != null && errors.length > 0) { - - let errorCodeCondition = langConfig.errors.password[errors[0].errorCode] !== undefined && langConfig.errors.password[errors[0].errorCode] !== null; + let errorCodeCondition = + langConfig.errors.password[errors[0].errorCode] !== undefined && + langConfig.errors.password[errors[0].errorCode] !== null; if (errorCodeCondition) { setErrorBanner({ errorCode: `password.${errors[0].errorCode}`, - show: true + show: true, }); - } - else if (errors[0].errorCode === "invalid_transaction") { + } else if (errors[0].errorCode === "invalid_transaction") { redirectOnError(errors[0].errorCode, t2(`${errors[0].errorCode}`)); - } - else { + } else { setErrorBanner({ errorCode: `${errors[0].errorCode}`, - show: true + show: true, }); } if (showCaptcha) { @@ -190,7 +301,7 @@ export default function Password({ } catch (error) { setErrorBanner({ errorCode: "password.auth_failed", - show: true + show: true, }); setStatus(states.ERROR); if (showCaptcha) { @@ -215,21 +326,17 @@ export default function Password({ loadComponent(); }, []); - useEffect(() => { - setInvalidState(!Object.values(loginState).every((value) => value?.trim())); - }, [loginState]); - const onCloseHandle = () => { setErrorBanner(null); }; const onBlurChange = (e, errors) => { let id = e.target.id; - let tempError = inputErrorBanner.map(_ => _); + let tempError = inputErrorBanner.map((_) => _); if (errors.length > 0) { - tempError.push(id) + tempError.push(id); } else { - let errorIndex = tempError.findIndex(_ => _ === id); + let errorIndex = tempError.findIndex((_) => _ === id); if (errorIndex !== -1) { tempError.splice(errorIndex, 1); } @@ -238,77 +345,171 @@ export default function Password({ }; const handleForgotPassword = () => { - window.onbeforeunload = null - } + window.onbeforeunload = null; + }; return ( <> -
+
{backButtonDiv} + {currentLoginID && ( +
+ {loginIDs && loginIDs.length > 1 + ? t1("multiple_login_ids") + : `${t1("login_with_id", { + currentID: `${t1(currentLoginID.id)}` + })}`} +
+ )}
{errorBanner !== null && ( - +
+ +
+ )} + { + setCurrentLoginID(value); + }} + /> + {currentLoginID ? ( +
+ {currentLoginID?.prefixes?.length > 0 ? ( + <> + { + setCountryCode(val); + }} + selectedCountry={(val) => { + setSelectedCountry(val); + }} + individualId={(val) => { + setIndividualId(val); + }} + isBtnDisabled={(val) => { + setIsBtnDisabled(val); + }} + i18nPrefix={i18nKeyPrefix1} + /> + + {fields.map( + (field, idx) => + idx === 1 && ( +
+ +
+ ) + )} + + ) : ( + <> + {fields.map((field, idx) => ( +
+ +
+ ))} + + )} + + {forgotPassword && ( + handleForgotPassword()} + target="_self" + > + {t1("forgot_password")} + + )} + + {showCaptcha && ( +
+ +
+ )} + + 0) || + (showCaptcha && captchaToken === null) + } + /> + + ) : ( +
+ +
)} - -
- {fields.map((field) => ( -
- -
- ))} - - {forgotPassword && - handleForgotPassword()} target="_self">{t1("forgot_password")} - } - - {showCaptcha && ( -
- -
- )} - - 0) || - (showCaptcha && captchaToken === null) - } - /> - {status === states.LOADING && (
diff --git a/oidc-ui/src/components/Pin.js b/oidc-ui/src/components/Pin.js index b30f8752e..f2084ba55 100644 --- a/oidc-ui/src/components/Pin.js +++ b/oidc-ui/src/components/Pin.js @@ -16,6 +16,8 @@ import langConfigService from "../services/langConfigService"; import InputWithImage from "./InputWithImage"; import redirectOnError from "../helpers/redirectOnError"; import ReCAPTCHA from "react-google-recaptcha"; +import LoginIDOptions from "./LoginIDOptions"; +import InputWithPrefix from "./InputWithPrefix"; const fields = pinFields; let fieldsState = {}; @@ -45,14 +47,25 @@ export default function Pin({ const post_AuthenticateUser = authService.post_AuthenticateUser; const buildRedirectParams = authService.buildRedirectParams; - const [loginState, setLoginState] = useState(fieldsState); const [status, setStatus] = useState(states.LOADED); const [errorBanner, setErrorBanner] = useState(null); const [inputError, setInputError] = useState([]); - const [invalidState, setInvalidState] = useState(true); const [captchaToken, setCaptchaToken] = useState(null); const _reCaptchaRef = useRef(null); + const [pin, setPin] = useState(null); + const [currentLoginID, setCurrentLoginID] = useState(null); + const [countryCode, setCountryCode] = useState(null); + const [individualId, setIndividualId] = useState(null); + const [selectedCountry, setSelectedCountry] = useState(null); + const [isValid, setIsValid] = useState(false); + const [isBtnDisabled, setIsBtnDisabled] = useState(true); + const [prevLanguage, setPrevLanguage] = useState(i18n.language); + + var loginIDs = openIDConnectService.getEsignetConfiguration( + configurationKeys.loginIdOptions + ); + const captchaEnableComponents = openIDConnectService.getEsignetConfiguration( configurationKeys.captchaEnableComponents @@ -73,10 +86,98 @@ export default function Pin({ const navigate = useNavigate(); + function getPropertiesForLoginID(loginID, label) { + const { prefixes, maxLength: outerMaxLength, regex: outerRegex } = loginID; + + if (Array.isArray(prefixes) && prefixes.length > 0) { + const prefix = prefixes.find((prefix) => prefix.label === label); + if (prefix) { + return { + maxLength: prefix.maxLength || outerMaxLength || null, + regex: prefix.regex || outerRegex || null, + }; + } + } + + return { + maxLength: outerMaxLength || null, + regex: outerRegex || null, + }; + } + const handleChange = (e) => { - setLoginState({ ...loginState, [e.target.id]: e.target.value }); + setIsValid(true); + onCloseHandle(); + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + const trimmedValue = e.target.value.trim(); + + let newValue = regex + ? trimmedValue + .split("") + .filter((char) => regex.test(char)) + .join("") + : trimmedValue; + + setIndividualId(newValue); // Update state with the visible valid value + if (e.target.type === "password") { + setPin(e.target.value.trim()); + } else { + setIndividualId(newValue); + } + + setIsBtnDisabled( + !( + ( + (!maxLength && !regex) || // Case 1: No maxLength, no regex + (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only + (maxLength && + regex && + newValue.length === parseInt(maxLength) && + regex.test(newValue)) + ) // Case 4: Both maxLength and regex + ) + ); + }; + + const handleBlur = (e) => { + const idProperties = getPropertiesForLoginID( + currentLoginID, + e.target.name.split("_")[1] + ); + const maxLength = idProperties.maxLength; + const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; + setIsValid( + (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!regex || regex.test(e.target.value.trim())) + ); }; + const handlePinChange = (e) => { + onCloseHandle(); + setPin(e.target.value.trim()); + }; + + useEffect(() => { + if (i18n.language === prevLanguage) { + setIndividualId(null); + setPin(null); + setIsValid(false); + setIsBtnDisabled(true); + onCloseHandle(); + if (currentLoginID && currentLoginID.prefixes) { + setSelectedCountry(currentLoginID.prefixes[0]); + } + } else { + setPrevLanguage(i18n.language); + } + }, [currentLoginID]); + const onBlurChange = (e, errors) => { let id = e.target.id; let tempError = inputError.map((_) => _); @@ -126,10 +227,17 @@ export default function Pin({ try { let transactionId = openIDConnectService.getTransactionId(); - let uin = - fields[0].prefix + loginState["Pin_mosip-uin"] + fields[0].postfix; + let prefix = currentLoginID.prefixes + ? typeof currentLoginID.prefixes === "object" + ? countryCode + : currentLoginID.prefixes + : ""; + let id = individualId; + let postfix = currentLoginID.postfix ? currentLoginID.postfix : ""; + + let ID = prefix + id + postfix; let challengeType = challengeTypes.pin; - let challenge = loginState["Pin_pin"]; + let challenge = pin; let challengeFormat = challengeFormats.pin; let challengeList = [ @@ -143,7 +251,7 @@ export default function Pin({ setStatus(states.LOADING); const authenticateResponse = await post_AuthenticateUser( transactionId, - uin, + ID, challengeList, captchaToken ); @@ -207,87 +315,169 @@ export default function Pin({ setErrorBanner(null); }; - useEffect(() => { - setInvalidState(!Object.values(loginState).every((value) => value?.trim())); - }, [loginState]); - return ( <> -
{backButtonDiv}
- {errorBanner !== null && ( - - )} -
- {fields.map((field) => ( -
- -
- ))} - -
-
- - +
+ {backButtonDiv} + {currentLoginID && ( +
+ {loginIDs && loginIDs.length > 1 + ? t1("multiple_login_ids") + : `${t1("login_with_id", { + currentID: `${t1(currentLoginID.id)}` + })}`}
+ )} +
+ {errorBanner !== null && ( +
+
- - {showCaptcha && ( -
- + )} + { + setCurrentLoginID(value); + }} + /> + {currentLoginID ? ( + + {currentLoginID?.prefixes?.length > 0 ? ( + <> + { + setCountryCode(val); + }} + selectedCountry={(val) => { + setSelectedCountry(val); + }} + individualId={(val) => { + setIndividualId(val); + }} + isBtnDisabled={(val) => { + setIsBtnDisabled(val); + }} + i18nPrefix={i18nKeyPrefix1} + /> + + {fields.map( + (field, idx) => + idx === 1 && ( +
+ +
+ ) + )} + + ) : ( + <> + {fields.map((field, idx) => ( +
+ +
+ ))} + + )} + +
+
+ + +
- )} - 0) || - (showCaptcha && captchaToken === null) - } - /> - + {showCaptcha && ( +
+ +
+ )} + + 0) || + (showCaptcha && captchaToken === null) + } + /> + + ) : ( +
+ +
+ )} {status === states.LOADING && ( -
+
)} diff --git a/oidc-ui/src/components/SignInOptions.js b/oidc-ui/src/components/SignInOptions.js index a18f08a24..f8d34f935 100644 --- a/oidc-ui/src/components/SignInOptions.js +++ b/oidc-ui/src/components/SignInOptions.js @@ -1,7 +1,10 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import LoadingIndicator from "../common/LoadingIndicator"; -import { configurationKeys } from "../constants/clientConstants"; +import { + configurationKeys, + walletConfigKeys, +} from "../constants/clientConstants"; import { LoadingStates as states } from "../constants/states"; import { getAllAuthFactors } from "../services/walletService"; @@ -9,12 +12,102 @@ export default function SignInOptions({ openIDConnectService, handleSignInOptionClick, i18nKeyPrefix = "signInOption", + icons, }) { const { t } = useTranslation("translation", { keyPrefix: i18nKeyPrefix }); const [status, setStatus] = useState({ state: states.LOADED, msg: "" }); const [singinOptions, setSinginOptions] = useState(null); const [showMoreOptions, setShowMoreOptions] = useState(false); + const [iconsMap, setIconsMap] = useState({}); // To store preloaded SVGs + + const walletConfig = openIDConnectService.getEsignetConfiguration( + configurationKeys.walletConfig + ); + + var walletLogoUrl = walletConfig[0][walletConfigKeys.walletLogoUrl]; + + const fetchSvg = async (path) => { + try { + const response = await fetch(`/${path}`); + if (!response.ok) { + throw new Error("Failed to fetch SVG"); + } + let svgText = await response.text(); + + // Function to fetch and apply the background color dynamically + function updateLanguageIconBgColor() { + // Construct the CSS variable name dynamically + const cssVariableName = `--primary-color`; + + // Get the computed style for the body element + const bodyStyles = getComputedStyle(document.body); + + // Fetch the value of the custom property + const primaryColor = bodyStyles + .getPropertyValue(cssVariableName) + .trim(); + + if (primaryColor) { + // Apply the color to the stroke and fill attributes of the SVG + svgText = svgText.replace( + /fill="(?!#fff|white|none)[^"]*"/g, + `fill="${primaryColor}"` + ); + + svgText = svgText.replace( + /stroke="(?!#fff|white|none)[^"]*"/g, + `stroke="${primaryColor}"` + ); + } + } + + // Call the function to apply the color on page load or dynamically + updateLanguageIconBgColor(); + + return svgText; + } catch (error) { + return ""; // Return empty string if error occurs + } + }; + + useEffect(() => { + const preloadIcons = async () => { + const svgPromises = singinOptions.map(async (option) => { + if (option.icon !== walletLogoUrl) { + const svgContent = await fetchSvg(option.icon); + return { id: option.id, svgContent }; + } + else return { id: option.id }; + }); + + const svgResults = await Promise.all(svgPromises); + const svgMap = svgResults.reduce((acc, { id, svgContent }) => { + acc[id] = svgContent; + return acc; + }, {}); + + setIconsMap(svgMap); // Store preloaded SVGs + }; + + if (singinOptions) { + if ( + Object.keys(iconsMap)?.length === 0 && + icons && + Object.keys(icons)?.length + ) { + setIconsMap(icons); + } else { + preloadIcons(); + } + } + + if (iconsMap && Object.keys(iconsMap)?.length > 0) { + setStatus({ state: states.LOADED, msg: "" }); + } else { + setStatus({ state: states.LOADING, msg: "loading_msg" }); + } + }, [singinOptions, iconsMap]); useEffect(() => { setStatus({ state: states.LOADING, msg: "loading_msg" }); @@ -40,48 +133,64 @@ export default function SignInOptions({ return ( <> -

+

{t("preferred_mode_of_login")}

{status.state === states.LOADING && ( -
+
)} - {status.state === states.LOADED && singinOptions && ( -
- {singinOptions - .slice(0, showMoreOptions ? 4 : undefined) - .map((option, idx) => ( -
handleSignInOptionClick(option.value)} - > - {option.id} -
- {t("login_with", { - option: t(option.label, option.label), - })} + {status.state === states.LOADED && + singinOptions && + Object.keys(iconsMap)?.length > 0 && ( +
+ {singinOptions + .slice(0, showMoreOptions ? 4 : undefined) + .map((option, idx) => ( +
+ handleSignInOptionClick(option.value, iconsMap) + } + > + {option.icon !== walletLogoUrl ? ( +
+ ) : ( + {option.id} + )} +
+ {t("login_with_id", { + option: t(option.label, option.label), + })} +
-
- ))} -
- )} + ))} +
+ )} - {showMoreOptions && ( + {showMoreOptions && Object.keys(iconsMap)?.length > 0 && (
setShowMoreOptions(false)} id="show-more-options" + onClick={() => setShowMoreOptions(false)} + id="show-more-options" > - {t("more_ways_to_sign_in")} + + {t("more_ways_to_sign_in")} +
{errorMsg}
); } -function createDynamicLoginElements( - authFactor, - oidcService, - backButtonDiv -) { +function createDynamicLoginElements(authFactor, oidcService, backButtonDiv) { const authFactorType = authFactor.type; if (typeof authFactorType === "undefined") { return InitiateInvalidAuthFactor( "The component " + { authFactorType } + " has not been created yet." ); } - + if (authFactorType === validAuthFactors.OTP) { return InitiateOtp(oidcService, backButtonDiv); } @@ -121,7 +118,7 @@ function createDynamicLoginElements( if (authFactorType === validAuthFactors.KBI) { return InitiateForm(oidcService, backButtonDiv); } - + if (authFactorType === validAuthFactors.WLA) { return InitiateLinkedWallet(authFactor, oidcService, backButtonDiv); } @@ -131,11 +128,12 @@ function createDynamicLoginElements( } export default function LoginPage({ i18nKeyPrefix = "header" }) { - const { t, i18n } = useTranslation("translation", { keyPrefix: i18nKeyPrefix }); + const { t, i18n } = useTranslation("translation", { + keyPrefix: i18nKeyPrefix, + }); const [compToShow, setCompToShow] = useState(null); const [clientLogoURL, setClientLogoURL] = useState(null); const [clientName, setClientName] = useState(null); - const [subHeaderText, setSubHeaderText] = useState(null); const [authFactorType, setAuthFactorType] = useState(null); const [searchParams] = useSearchParams(); const location = useLocation(); @@ -143,6 +141,7 @@ export default function LoginPage({ i18nKeyPrefix = "header" }) { var decodeOAuth = Buffer.from(location.hash ?? "", "base64")?.toString(); var nonce = searchParams.get("nonce"); var state = searchParams.get("state"); + var icons; useEffect(() => { if (!decodeOAuth) { @@ -150,35 +149,6 @@ export default function LoginPage({ i18nKeyPrefix = "header" }) { } loadComponent(); }, []); - - useEffect(() => { - if (authFactorType === null) { - setSubHeaderText(t("subheader_text.all_login_options")); - } else { - setSubHeader(); - } - }, [authFactorType, i18n.language]); - - const setSubHeader = () => { - if (authFactorType === "OTP") { - setSubHeaderText(t("subheader_text.otp_login")); - } - else if (authFactorType === "BIO") { - setSubHeaderText(t("subheader_text.biometrics_login")); - } - else if (authFactorType === "PIN") { - setSubHeaderText(t("subheader_text.pin_login")); - } - else if (authFactorType === "PWD") { - setSubHeaderText(t("subheader_text.password_login")); - } - else if (authFactorType === "KBI") { - setSubHeaderText(t("subheader_text.kbi_login")); - } - else if(authFactorType === "WLA") { - setSubHeaderText(t("subheader_text.wallet_login")); - } - } let parsedOauth = null; @@ -195,33 +165,64 @@ export default function LoginPage({ i18nKeyPrefix = "header" }) { const oidcService = new openIDConnectService(parsedOauth, nonce, state); - const handleSignInOptionClick = (authFactor) => { - setAuthFactorType(authFactor.type) + const handleSignInOptionClick = (authFactor, icon) => { + icons = icon; + setAuthFactorType(authFactor.type); //TODO handle multifactor auth setCompToShow( createDynamicLoginElements( authFactor, oidcService, - backButtonDiv(oidcService.getAuthFactorList().length > 1 ? handleBackButtonClick : null) + backButtonDiv( + oidcService.getAuthFactorList().length > 1 + ? handleBackButtonClick + : null + ) ) ); }; - + const handleBackButtonClick = () => { - setAuthFactorType(null) - setCompToShow(InitiateSignInOptions(handleSignInOptionClick, oidcService)); + setAuthFactorType(null); + setCompToShow( + InitiateSignInOptions(handleSignInOptionClick, oidcService, icons) + ); }; const backButtonDiv = (handleBackButtonClick) => { return ( handleBackButtonClick && ( -
+
) @@ -234,7 +235,7 @@ export default function LoginPage({ i18nKeyPrefix = "header" }) { setClientName(oAuthDetailResponse?.clientName); handleBackButtonClick(); }; - + function checkForIDT(authFactors) { for (const factor of authFactors) { if (Array.isArray(factor)) { @@ -255,7 +256,7 @@ export default function LoginPage({ i18nKeyPrefix = "header" }) { heading={t("login_heading", { idProviderName: window._env_.DEFAULT_ID_PROVIDER_NAME, })} - subheading={subHeaderText} + subheading={clientName} clientLogoPath={clientLogoURL} clientName={clientName} component={compToShow} From 15fa7d3bdebaaaccbe4bfd4e0503de94240e72a4 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:51:20 +0530 Subject: [PATCH 08/40] [ES-1908] Modified login-id config for dev testing. (#1026) Signed-off-by: Gk <76690271+gk-4VII@users.noreply.github.com> --- .../src/main/resources/application-default.properties | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index 6c27012e2..ba979e252 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -375,10 +375,10 @@ mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text mosip.esignet.ui.config.login-id.options={ \ - { "id": "mobile", "svg": "mobile_icon", "prefixes": [{"label": "IND", "value": "+91", "maxLength": "", "regex": ""}, {"label": "KHM", "value": "+855"}, {"label": "USA", "value": "+1"}], "postfix": "@phone", "maxLength": "", "regex": "" }, \ - { "id": "nrc", "svg": "nrc_id_icon", "prefixes": "", "postfix": "@NRC", "maxLength": "", "regex": "" }, \ - { "id": "vid", "svg": "vid_icon", "prefixes": "", "postfix": "@ID", "maxLength": "", "regex": "" }, \ - { "id": "email", "svg": "email_icon", "prefixes": "", "postfix": "@email", "maxLength": "", "regex": "" } \ + { "id": "mobile", "svg": "mobile_icon", "prefixes": {{"label": "IND", "value": "+91", "maxLength": "", "regex": ""}, {"label": "KHM", "value": "+855", "regex": "^[a-zA-Z]+$"}, {"label": "USA", "value": "+1", "maxLength": "5"}}, "postfix": "@phone", "maxLength": 10, "regex": "^\\d*$" }, \ + { "id": "nrc", "svg": "nrc_id_icon", "prefixes": "nrc", "postfix": "@NRC", "maxLength": 5, "regex": "" }, \ + { "id": "vid", "svg": "vid_icon", "prefixes": "", "postfix": "@ID", "maxLength": "7", "regex": "^[a-zA-Z]+$" }, \ + { "id": "email", "svg": "email_icon", "prefixes": "", "postfix": "", "maxLength": "", "regex": "^\\d*$" } \ } mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ @@ -409,4 +409,4 @@ mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.id ##-------------------------------------------- Default Integrations ----------------------------------------------------- mosip.esignet.integration.audit-plugin=LoggerAuditService -mosip.esignet.integration.key-binder=NoOpKeyBinder \ No newline at end of file +mosip.esignet.integration.key-binder=NoOpKeyBinder From ade00d2c83e2ed0d1ed20f8a6db37eccd05e2473 Mon Sep 17 00:00:00 2001 From: Nandhukumar Date: Thu, 5 Dec 2024 12:24:05 +0530 Subject: [PATCH 09/40] MOSIP-36485 | ES-1987 | updated the validation for IDA plugin and removed VCI test cases from esignet Signed-off-by: Nandhukumar --- api-test/pom.xml | 4 +- .../apirig/esignet/utils/EsignetUtil.java | 6 +- .../AuthenticateUser/AuthenticateUser.yml | 17 +- .../esignet/WalletBinding/WalletBinding.yml | 20 ++- api-test/testNgXmlFiles/esignetSuite.xml | 159 ------------------ 5 files changed, 27 insertions(+), 179 deletions(-) diff --git a/api-test/pom.xml b/api-test/pom.xml index e6fa76c01..0f909aa6f 100644 --- a/api-test/pom.xml +++ b/api-test/pom.xml @@ -54,9 +54,9 @@ - io.mosip.testrig.apitest.commons + io.mosip.testrig.apirig.apitest.commons apitest-commons - 1.3.0-SNAPSHOT + 1.2.2-SNAPSHOT diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java index 56083c679..71554f745 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java @@ -184,7 +184,11 @@ public static String isTestCaseValidForExecution(TestCaseDTO testCaseDTO) { } else if (getIdentityPluginNameFromEsignetActuator().toLowerCase().contains("idaauthenticatorimpl")) { // Let run test cases eSignet & MOSIP API calls --- both UIN and VID - BaseTestCase.setSupportedIdTypes(Arrays.asList("UIN", "VID")); +// BaseTestCase.setSupportedIdTypes(Arrays.asList("UIN", "VID")); + + getSupportedIdTypesValueFromActuator(); + + logger.info("supportedIdType = " + supportedIdType); String endpoint = testCaseDTO.getEndPoint(); if (endpoint.contains("/v1/signup/") == true || endpoint.contains("/mock-identity-system/") == true diff --git a/api-test/src/main/resources/esignet/AuthenticateUser/AuthenticateUser.yml b/api-test/src/main/resources/esignet/AuthenticateUser/AuthenticateUser.yml index a4803ed73..ca81ad404 100644 --- a/api-test/src/main/resources/esignet/AuthenticateUser/AuthenticateUser.yml +++ b/api-test/src/main/resources/esignet/AuthenticateUser/AuthenticateUser.yml @@ -1737,6 +1737,7 @@ AuthenticateUser: restMethod: post checkErrorsOnlyInResponse: true validityCheckRequired: true + allowedErrorCodes: invalid_challenge_length,invalid_auth_factor_type inputTemplate: esignet/AuthenticateUser/AuthenticateUser outputTemplate: esignet/error input: '{ @@ -1759,7 +1760,7 @@ AuthenticateUser: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "invalid_auth_factor_type", "errorMessage": "$IGNORE$" } ], @@ -1857,7 +1858,7 @@ AuthenticateUser: description: Authenticate user with Null in AuthFactorType VID role: resident restMethod: post - allowedErrorCodes: invalid_challenge_length + allowedErrorCodes: invalid_challenge_length,invalid_auth_factor_type checkErrorsOnlyInResponse: true validityCheckRequired: true inputTemplate: esignet/AuthenticateUser/AuthenticateUser @@ -1882,7 +1883,7 @@ AuthenticateUser: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "invalid_auth_factor_type", "errorMessage": "$IGNORE$" } ], @@ -1898,7 +1899,7 @@ AuthenticateUser: description: Authenticate user with different AuthFactorType VID role: resident restMethod: post - allowedErrorCodes: invalid_challenge_length,invalid_auth_factor_type_format + allowedErrorCodes: invalid_challenge_length,invalid_auth_factor_type_format,invalid_challenge_format checkErrorsOnlyInResponse: true validityCheckRequired: true inputTemplate: esignet/AuthenticateUser/AuthenticateUser @@ -1923,7 +1924,7 @@ AuthenticateUser: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "invalid_challenge_format", "errorMessage": "$IGNORE$" } ], @@ -1963,7 +1964,7 @@ AuthenticateUser: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "invalid_challenge", "errorMessage": "$IGNORE$" } ], @@ -2061,7 +2062,7 @@ AuthenticateUser: description: Authenticate user with Null in challenge VID role: resident restMethod: post - allowedErrorCodes: invalid_challenge_length + allowedErrorCodes: invalid_challenge_length,invalid_challenge checkErrorsOnlyInResponse: true validityCheckRequired: true inputTemplate: esignet/AuthenticateUser/AuthenticateUser @@ -2086,7 +2087,7 @@ AuthenticateUser: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "invalid_challenge", "errorMessage": "$IGNORE$" } ], diff --git a/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml b/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml index 1031d40d9..7918c0b42 100644 --- a/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml +++ b/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml @@ -289,7 +289,7 @@ WalletBinding: role: mobileauth checkErrorsOnlyInResponse: true validityCheckRequired: true - allowedErrorCodes: binding_auth_failed,invalid_individual_id + allowedErrorCodes: binding_auth_failed,invalid_individual_id,IDA-MLC-009 restMethod: post inputTemplate: esignet/WalletBinding/WalletBinding outputTemplate: esignet/error @@ -317,8 +317,8 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed", - "errorMessage": "request.individualId: invalid_individual_id" + "errorCode": "IDA-MLC-009", + "errorMessage": "$IGNORE$" } ], "sendOtpResp": { @@ -378,7 +378,7 @@ WalletBinding: role: mobileauth checkErrorsOnlyInResponse: true validityCheckRequired: true - allowedErrorCodes: binding_auth_failed,invalid_individual_id + allowedErrorCodes: binding_auth_failed,invalid_individual_id,IDA-MLC-009 restMethod: post inputTemplate: esignet/WalletBinding/WalletBinding outputTemplate: esignet/error @@ -406,7 +406,7 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed", + "errorCode": "IDA-MLC-009", "errorMessage": "request.individualId: invalid_individual_id" } ], @@ -1608,6 +1608,7 @@ WalletBinding: role: mobileauth checkErrorsOnlyInResponse: true validityCheckRequired: true + allowedErrorCodes: binding_auth_failed,IDA-MLC-009 restMethod: post inputTemplate: esignet/WalletBinding/WalletBinding outputTemplate: esignet/error @@ -1635,8 +1636,8 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed", - "errorMessage": "request.individualId: invalid_individual_id" + "errorCode": "IDA-MLC-009", + "errorMessage": "$IGNORE$" } ], "sendOtpResp": { @@ -1696,6 +1697,7 @@ WalletBinding: role: mobileauth checkErrorsOnlyInResponse: true validityCheckRequired: true + allowedErrorCodes: binding_auth_failed,IDA-MLC-009 restMethod: post inputTemplate: esignet/WalletBinding/WalletBinding outputTemplate: esignet/error @@ -1723,8 +1725,8 @@ WalletBinding: output: '{ "errors": [ { - "errorCode": "binding_auth_failed", - "errorMessage": "request.individualId: invalid_individual_id" + "errorCode": "IDA-MLC-009", + "errorMessage": "$IGNORE$" } ], "sendOtpResp": { diff --git a/api-test/testNgXmlFiles/esignetSuite.xml b/api-test/testNgXmlFiles/esignetSuite.xml index dbaacbb11..9c0ae6a95 100644 --- a/api-test/testNgXmlFiles/esignetSuite.xml +++ b/api-test/testNgXmlFiles/esignetSuite.xml @@ -279,165 +279,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From a01896ce04953c7e060b7d5e156add8e3f322605 Mon Sep 17 00:00:00 2001 From: Nandhukumar Date: Thu, 5 Dec 2024 15:05:10 +0530 Subject: [PATCH 10/40] MOSIP-36485 Signed-off-by: Nandhukumar --- .../apirig/esignet/testscripts/EsignetBioAuth.java | 8 ++++++-- .../esignet/BioAuthIdp/BioAuthIdentityEncrypt.hbs | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/EsignetBioAuth.java b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/EsignetBioAuth.java index ba7330f3d..376f8cda6 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/EsignetBioAuth.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/EsignetBioAuth.java @@ -133,20 +133,24 @@ public void test(TestCaseDTO testCaseDTO) throws AuthenticationTestException, Ad identityRequest = getJsonFromTemplate(identityReqJson.toString(), identityRequestTemplate); if (identityRequest.contains("$DOMAINURI$")) { - String domainUrl = ApplnURI.replace("api-internal", GlobalConstants.ESIGNET); + String domainUrl = EsignetConfigManager.getEsignetBaseUrl(); identityRequest = identityRequest.replace("$DOMAINURI$", domainUrl); } + + logger.info("identityRequest = " + identityRequest); String encryptedIdentityReq = null; try { encryptedIdentityReq = bioDataUtil.constractBioIdentityRequest(identityRequest, getResourcePath() + properties.getProperty("bioValueEncryptionTemplate"), testCaseName, isInternal); + + logger.info("encryptedIdentityReq = " + encryptedIdentityReq); if (encryptedIdentityReq == null) throw new AdminTestException("bioDataUtil.constractBioIdentityRequest is null"); JSONObject encryptedIdentityReqObject = new JSONObject(encryptedIdentityReq); - logger.info(encryptedIdentityReqObject); + logger.info("encryptedIdentityReqObject = " + encryptedIdentityReqObject); JSONArray arrayBiometrics = encryptedIdentityReqObject.getJSONArray(GlobalConstants.BIOMETRICS); String bioData = arrayBiometrics.toString(); diff --git a/api-test/src/main/resources/esignet/BioAuthIdp/BioAuthIdentityEncrypt.hbs b/api-test/src/main/resources/esignet/BioAuthIdp/BioAuthIdentityEncrypt.hbs index d5666414d..42d4f1048 100644 --- a/api-test/src/main/resources/esignet/BioAuthIdp/BioAuthIdentityEncrypt.hbs +++ b/api-test/src/main/resources/esignet/BioAuthIdp/BioAuthIdentityEncrypt.hbs @@ -20,7 +20,7 @@ "type": "{{type}}" }, "domainUri": "$DOMAINURI$", - "env": "Developer", + "env": "Staging", "purpose": "AUTH", "qualityScore": 70, "requestedScore": 70, From 5d84d41d83e306099342b46e31319df609e28982 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:31:36 +0530 Subject: [PATCH 11/40] [ES-1908] [ES-1909] UI fixes for pre and postfix. (#1029) Signed-off-by: GurukiranP --- .../resources/application-default.properties | 6 ++---- oidc-ui/public/locales/ar.json | 2 +- oidc-ui/public/locales/en.json | 2 +- oidc-ui/public/locales/hi.json | 2 +- oidc-ui/public/locales/km.json | 2 +- oidc-ui/public/locales/kn.json | 2 +- oidc-ui/public/locales/ta.json | 2 +- oidc-ui/public/theme/variables.css | 7 +++++-- oidc-ui/src/App.css | 1 + oidc-ui/src/components/Background.js | 2 +- oidc-ui/src/components/LoginIDOptions.js | 2 +- oidc-ui/src/components/Password.js | 4 ++-- oidc-ui/src/components/Pin.js | 4 ++-- oidc-ui/src/components/SignInOptions.js | 18 +++++++++++++----- 14 files changed, 33 insertions(+), 23 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index ba979e252..c04936399 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -374,12 +374,10 @@ mosip.esignet.ui.config.username.prefix= mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text -mosip.esignet.ui.config.login-id.options={ \ - { "id": "mobile", "svg": "mobile_icon", "prefixes": {{"label": "IND", "value": "+91", "maxLength": "", "regex": ""}, {"label": "KHM", "value": "+855", "regex": "^[a-zA-Z]+$"}, {"label": "USA", "value": "+1", "maxLength": "5"}}, "postfix": "@phone", "maxLength": 10, "regex": "^\\d*$" }, \ +mosip.esignet.ui.config.login-id.options={{ "id": "mobile", "svg": "mobile_icon", "prefixes": {{"label": "IND", "value": "+91", "maxLength": "", "regex": ""}, {"label": "KHM", "value": "+855", "regex": "^[a-zA-Z]+$"}, {"label": "USA", "value": "+1", "maxLength": "5"}}, "postfix": "@phone", "maxLength": 10, "regex": "^\\d*$" }, \ { "id": "nrc", "svg": "nrc_id_icon", "prefixes": "nrc", "postfix": "@NRC", "maxLength": 5, "regex": "" }, \ { "id": "vid", "svg": "vid_icon", "prefixes": "", "postfix": "@ID", "maxLength": "7", "regex": "^[a-zA-Z]+$" }, \ - { "id": "email", "svg": "email_icon", "prefixes": "", "postfix": "", "maxLength": "", "regex": "^\\d*$" } \ -} + { "id": "email", "svg": "email_icon", "prefixes": "", "postfix": "", "maxLength": "", "regex": "^\\d*$" }} mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ diff --git a/oidc-ui/public/locales/ar.json b/oidc-ui/public/locales/ar.json index 86247e58b..b449b1f1c 100644 --- a/oidc-ui/public/locales/ar.json +++ b/oidc-ui/public/locales/ar.json @@ -89,7 +89,7 @@ "password": { "sign_in_with_password": "تسجيل الدخول بكلمة المرور", "remember_me": "تذكرنى", - "login": "يكمل", + "login": "تسجيل الدخول", "uin_label_text": "UIN/VID", "password_label_text": "كلمة المرور", "uin_placeholder": "أدخل رقم UIN أو VID الخاص بك", diff --git a/oidc-ui/public/locales/en.json b/oidc-ui/public/locales/en.json index 3032e966b..bdeb06ce7 100644 --- a/oidc-ui/public/locales/en.json +++ b/oidc-ui/public/locales/en.json @@ -89,7 +89,7 @@ "password": { "sign_in_with_password": "Login with Password", "remember_me": "Remember Me", - "login": "Continue", + "login": "Login", "uin_label_text": "UIN/VID", "password_label_text": "Password", "uin_placeholder": "Enter Your UIN or VID", diff --git a/oidc-ui/public/locales/hi.json b/oidc-ui/public/locales/hi.json index ef6af6d53..5db430d60 100644 --- a/oidc-ui/public/locales/hi.json +++ b/oidc-ui/public/locales/hi.json @@ -89,7 +89,7 @@ "password": { "sign_in_with_password": "पासवर्ड से लॉगिन करें", "remember_me": "मुझे याद करो", - "login": "जारी रखना", + "login": "लॉग इन करें", "uin_label_text": "यूआईएन/वीआईडी", "password_label_text": "पासवर्ड", "uin_placeholder": "अपना यूआईएन या वीआईडी ​​दर्ज करें", diff --git a/oidc-ui/public/locales/km.json b/oidc-ui/public/locales/km.json index 5a509fa0a..7ffbdd48b 100644 --- a/oidc-ui/public/locales/km.json +++ b/oidc-ui/public/locales/km.json @@ -89,7 +89,7 @@ "password": { "sign_in_with_password": "ចូលដោយប្រើពាក្យសម្ងាត់", "remember_me": "ចងចាំខ្ញុំ", - "login": "បន្ត", + "login": "ចូល", "uin_label_text": "UIN/VID", "password_label_text": "ពាក្យសម្ងាត់", "uin_placeholder": "បញ្ចូល UIN ឬ VID របស់អ្នក។", diff --git a/oidc-ui/public/locales/kn.json b/oidc-ui/public/locales/kn.json index ba5639ccf..83cbf1015 100644 --- a/oidc-ui/public/locales/kn.json +++ b/oidc-ui/public/locales/kn.json @@ -89,7 +89,7 @@ "password": { "sign_in_with_password": "ಪಾಸ್ವರ್ಡ್ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", "remember_me": "ನನ್ನನ್ನು ನೆನಪಿಸಿಕೊಳ್ಳಿ", - "login": "ಮುಂದುವರಿಸಿ", + "login": "ಲಾಗಿನ್ ಮಾಡಿ", "uin_label_text": "UIN/VID", "password_label_text": "ಪಾಸ್ವರ್ಡ್", "uin_placeholder": "ನಿಮ್ಮ UIN ಅಥವಾ VID ನಮೂದಿಸಿ", diff --git a/oidc-ui/public/locales/ta.json b/oidc-ui/public/locales/ta.json index a55ee9654..86ae6a03d 100644 --- a/oidc-ui/public/locales/ta.json +++ b/oidc-ui/public/locales/ta.json @@ -89,7 +89,7 @@ "password": { "sign_in_with_password": "கடவுச்சொல் மூலம் உள்நுழைக", "remember_me": "என்னை நினைவு செய்யுங்கள்", - "login": "தொடரவும்", + "login": "உள்நுழைக", "uin_label_text": "UIN/VID", "password_label_text": "கடவுச்சொல்", "uin_placeholder": "உங்கள் UIN அல்லது VID ஐ உள்ளிடவும்", diff --git a/oidc-ui/public/theme/variables.css b/oidc-ui/public/theme/variables.css index f05234fab..d6a55676e 100644 --- a/oidc-ui/public/theme/variables.css +++ b/oidc-ui/public/theme/variables.css @@ -63,9 +63,9 @@ --login-client-logo-image-height: 3.25rem; --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21CB"; - --login-alternate-arrow-size: 3rem; + --login-alternate-arrow-size: 1.5rem; --login-text-color: #000000; - --login-card-separator: linear-gradient(0deg, rgb(0,0,0,0.2) 0%, rgb(0,0,0,0.2) 100%); + --login-card-separator: linear-gradient(0deg, #FFFBF7 0%, #FFFFFF 100%); --login-card-separator-height: 1px; --footer-text-color: #898989; --footer-text-size: 14px; @@ -75,6 +75,7 @@ --forgot-password-width: max-content; --forgot-password-margin-top: 1rem; --password-google-reCaptcha-margin-top: 3rem; + --password-google-reCaptcha-margin-bottom: 1rem; --navbar-position: relative; --navbar-zIndex: 1; --navbar-box-shadow: 0px 2px 5px #0000001A; @@ -197,6 +198,7 @@ --forgot-password-width: max-content; --forgot-password-margin-top: 1rem; --password-google-reCaptcha-margin-top: 3rem; + --password-google-reCaptcha-margin-bottom: 1rem; --navbar-position: relative; --navbar-zIndex: 1; --navbar-box-shadow: 0px 2px 5px #0000001a; @@ -327,6 +329,7 @@ --forgot-password-width: max-content; --forgot-password-margin-top: 1rem; --password-google-reCaptcha-margin-top: 3rem; + --password-google-reCaptcha-margin-bottom: 1rem; --navbar-position: relative; --navbar-zIndex: 1; --navbar-box-shadow: 0px 2px 5px #0000001a; diff --git a/oidc-ui/src/App.css b/oidc-ui/src/App.css index fe7ffae5a..4ec195684 100644 --- a/oidc-ui/src/App.css +++ b/oidc-ui/src/App.css @@ -506,6 +506,7 @@ input[type="checkbox"]:hover { .password-google-reCaptcha { margin-top: var(--password-google-reCaptcha-margin-top) !important; + margin-bottom: var(--password-google-reCaptcha-margin-bottom) !important; } .errorInput { diff --git a/oidc-ui/src/components/Background.js b/oidc-ui/src/components/Background.js index 2f5e51e26..0cdc656e8 100644 --- a/oidc-ui/src/components/Background.js +++ b/oidc-ui/src/components/Background.js @@ -68,7 +68,7 @@ export default function Background({ )}
-
+
{clientLogoPath && ( { id: "vid", svg: "vid_icon", prefixes: "", - postfix: "@ID", + postfix: "", maxLength: "", regex: "", }, diff --git a/oidc-ui/src/components/Password.js b/oidc-ui/src/components/Password.js index 17f6731ba..37bde58e8 100644 --- a/oidc-ui/src/components/Password.js +++ b/oidc-ui/src/components/Password.js @@ -440,8 +440,8 @@ export default function Password({ ? currentLoginID.input_label : t1(field.labelText) } - labelFor={"Password_" + currentLoginID.id} - id={"Password_" + currentLoginID.id} + labelFor={idx === 0 ? currentLoginID.id : "Password_" + currentLoginID.id} + id={idx === 0 ? currentLoginID.id : "Password_" + currentLoginID.id} name={ idx === 0 ? "Password_" + currentLoginID.id : "password" } diff --git a/oidc-ui/src/components/Pin.js b/oidc-ui/src/components/Pin.js index f2084ba55..cc964c063 100644 --- a/oidc-ui/src/components/Pin.js +++ b/oidc-ui/src/components/Pin.js @@ -405,8 +405,8 @@ export default function Pin({ ? currentLoginID.input_label : t1(field.labelText) } - labelFor={"Pin_" + currentLoginID.id} - id={"Pin_" + currentLoginID.id} + labelFor={idx === 0 ? currentLoginID.id : "Pin_" + currentLoginID.id} + id={idx === 0 ? currentLoginID.id : "Pin_" + currentLoginID.id} name={idx === 0 ? "Pin_" + currentLoginID.id : "pin"} type={field.type} isRequired={field.isRequired} diff --git a/oidc-ui/src/components/SignInOptions.js b/oidc-ui/src/components/SignInOptions.js index f8d34f935..e98c87b08 100644 --- a/oidc-ui/src/components/SignInOptions.js +++ b/oidc-ui/src/components/SignInOptions.js @@ -97,7 +97,7 @@ export default function SignInOptions({ Object.keys(icons)?.length ) { setIconsMap(icons); - } else { + } else if (!icons){ preloadIcons(); } } @@ -107,7 +107,15 @@ export default function SignInOptions({ } else { setStatus({ state: states.LOADING, msg: "loading_msg" }); } - }, [singinOptions, iconsMap]); + }, [singinOptions]); + + useEffect(() => { + if (iconsMap && Object.keys(iconsMap)?.length > 0) { + setStatus({ state: states.LOADED, msg: "" }); + } else { + setStatus({ state: states.LOADING, msg: "loading_msg" }); + } + }, [iconsMap]); useEffect(() => { setStatus({ state: states.LOADING, msg: "loading_msg" }); @@ -127,13 +135,13 @@ export default function SignInOptions({ } setSinginOptions(loginOptions); - setShowMoreOptions(loginOptions.length > 4 && loginOptions.length !== 5); + setShowMoreOptions(loginOptions.length > 4); setStatus({ state: states.LOADED, msg: "" }); }, []); return ( <> -

+

{t("preferred_mode_of_login")}

@@ -146,7 +154,7 @@ export default function SignInOptions({ {status.state === states.LOADED && singinOptions && Object.keys(iconsMap)?.length > 0 && ( -
+
{singinOptions .slice(0, showMoreOptions ? 4 : undefined) .map((option, idx) => ( From 58ca60f5dec69aae87e6ff7fa113b7194f4d963f Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Thu, 5 Dec 2024 17:22:31 +0530 Subject: [PATCH 12/40] [ES-1908] Update application-default.properties (#1030) Signed-off-by: Gk <76690271+gk-4VII@users.noreply.github.com> --- .../src/main/resources/application-default.properties | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index c04936399..af2daea2b 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -374,10 +374,7 @@ mosip.esignet.ui.config.username.prefix= mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text -mosip.esignet.ui.config.login-id.options={{ "id": "mobile", "svg": "mobile_icon", "prefixes": {{"label": "IND", "value": "+91", "maxLength": "", "regex": ""}, {"label": "KHM", "value": "+855", "regex": "^[a-zA-Z]+$"}, {"label": "USA", "value": "+1", "maxLength": "5"}}, "postfix": "@phone", "maxLength": 10, "regex": "^\\d*$" }, \ - { "id": "nrc", "svg": "nrc_id_icon", "prefixes": "nrc", "postfix": "@NRC", "maxLength": 5, "regex": "" }, \ - { "id": "vid", "svg": "vid_icon", "prefixes": "", "postfix": "@ID", "maxLength": "7", "regex": "^[a-zA-Z]+$" }, \ - { "id": "email", "svg": "email_icon", "prefixes": "", "postfix": "", "maxLength": "", "regex": "^\\d*$" }} +mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'IND', 'value': '+91', 'maxLength': "", 'regex': ""}, {'label': 'KHM', 'value': '+855', 'regex': '^[a-zA-Z]+$'}, {'label': 'USA', 'value': '+1', 'maxLength': '5'}}, 'postfix': '@phone', 'maxLength': 10, 'regex': '^\\d*$' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': 'nrc', 'postfix': '@NRC', 'maxLength': 5, 'regex': "" }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': "", 'postfix': '@ID', 'maxLength': '7', 'regex': '^[a-zA-Z]+$' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': "", 'postfix': "", 'maxLength': "", 'regex': '^\\d*$' }} mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ From 8846559a756c4c0f27b977f731b32be1449a3d46 Mon Sep 17 00:00:00 2001 From: Nandhukumar <100744358+nandhu-kumar@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:53:22 +0530 Subject: [PATCH 13/40] MOSIP-36485 (#1032) Signed-off-by: Nandhukumar --- api-test/src/main/resources/esignet/BioAuthIdp/BioNonAuth.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api-test/src/main/resources/esignet/BioAuthIdp/BioNonAuth.yml b/api-test/src/main/resources/esignet/BioAuthIdp/BioNonAuth.yml index 663d17562..744074bf7 100644 --- a/api-test/src/main/resources/esignet/BioAuthIdp/BioNonAuth.yml +++ b/api-test/src/main/resources/esignet/BioAuthIdp/BioNonAuth.yml @@ -435,7 +435,7 @@ BioNonAuth: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "auth_factor_mismatch", "errorMessage": "$IGNORE$" } ] @@ -965,7 +965,7 @@ BioNonAuth: output: '{ "errors": [ { - "errorCode": "invalid_challenge_length", + "errorCode": "auth_factor_mismatch", "errorMessage": "$IGNORE$" } ] From 6c028a09f86dce0f509a17666fd33bc6c592f97f Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:01:59 +0530 Subject: [PATCH 14/40] [ES-1908] [ES-1909] minor UI fixes for pre and postfix. (#1033) Signed-off-by: GurukiranP --- .../src/main/resources/application-default.properties | 2 +- oidc-ui/src/components/InputWithPrefix.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index af2daea2b..25f3738e8 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -374,7 +374,7 @@ mosip.esignet.ui.config.username.prefix= mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text -mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'IND', 'value': '+91', 'maxLength': "", 'regex': ""}, {'label': 'KHM', 'value': '+855', 'regex': '^[a-zA-Z]+$'}, {'label': 'USA', 'value': '+1', 'maxLength': '5'}}, 'postfix': '@phone', 'maxLength': 10, 'regex': '^\\d*$' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': 'nrc', 'postfix': '@NRC', 'maxLength': 5, 'regex': "" }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': "", 'postfix': '@ID', 'maxLength': '7', 'regex': '^[a-zA-Z]+$' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': "", 'postfix': "", 'maxLength': "", 'regex': '^\\d*$' }} +mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'IND', 'value': '+91', 'maxLength': "", 'regex': ""}, {'label': 'KHM', 'value': '+855', 'regex': '^\\d*$'}, {'label': 'USA', 'value': '+1', 'maxLength': '5'}}, 'postfix': '', 'maxLength': 9, 'regex': '^[a-zA-Z]+$' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': 'nrc', 'postfix': '@NRC', 'maxLength': 5, 'regex': "" }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': "", 'postfix': '@ID', 'maxLength': '7', 'regex': '^[a-zA-Z]+$' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': "", 'postfix': "", 'maxLength': "", 'regex': '^\\d*$' }} mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ diff --git a/oidc-ui/src/components/InputWithPrefix.js b/oidc-ui/src/components/InputWithPrefix.js index f3158b559..4b0a31ad5 100644 --- a/oidc-ui/src/components/InputWithPrefix.js +++ b/oidc-ui/src/components/InputWithPrefix.js @@ -168,7 +168,7 @@ const InputWithPrefix = (props) => { countries.find( (country) => country.alpha3 === selectedCountry.label - ).alpha2 + )?.alpha2 } alt={selectedCountry.label} svg @@ -221,7 +221,7 @@ const InputWithPrefix = (props) => { countryCode={ countries.find( (country) => country.alpha3 === option.label - ).alpha2 + )?.alpha2 } svg style={countryFlagStyles} From ddeab5d7c307597580f1004b6c6372b79fa7c781 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:57:52 +0530 Subject: [PATCH 15/40] [ES-1908] reverted the changes to keep maxLength, regex and postfix as empty and minor i18n translation fixes. (#1034) Signed-off-by: GurukiranP --- .../src/main/resources/application-default.properties | 2 +- oidc-ui/public/locales/ar.json | 3 ++- oidc-ui/public/locales/en.json | 3 ++- oidc-ui/public/locales/hi.json | 3 ++- oidc-ui/public/locales/km.json | 3 ++- oidc-ui/public/locales/kn.json | 3 ++- oidc-ui/public/locales/ta.json | 3 ++- oidc-ui/src/components/LoginQRCode.js | 5 +++-- oidc-ui/src/components/OtpVerify.js | 2 +- 9 files changed, 17 insertions(+), 10 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index 25f3738e8..4c9239a25 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -374,7 +374,7 @@ mosip.esignet.ui.config.username.prefix= mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text -mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'IND', 'value': '+91', 'maxLength': "", 'regex': ""}, {'label': 'KHM', 'value': '+855', 'regex': '^\\d*$'}, {'label': 'USA', 'value': '+1', 'maxLength': '5'}}, 'postfix': '', 'maxLength': 9, 'regex': '^[a-zA-Z]+$' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': 'nrc', 'postfix': '@NRC', 'maxLength': 5, 'regex': "" }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': "", 'postfix': '@ID', 'maxLength': '7', 'regex': '^[a-zA-Z]+$' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': "", 'postfix': "", 'maxLength': "", 'regex': '^\\d*$' }} +mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'KHM', 'value': '+855', 'regex': ''}, {'label': 'IND', 'value': '+91', 'maxLength': '', 'regex': ''}, {'label': 'USA', 'value': '+1', 'maxLength': ''}}, 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }} mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ diff --git a/oidc-ui/public/locales/ar.json b/oidc-ui/public/locales/ar.json index b449b1f1c..ec0fa6e4f 100644 --- a/oidc-ui/public/locales/ar.json +++ b/oidc-ui/public/locales/ar.json @@ -123,7 +123,8 @@ "failed": "غير قادر على تسجيل الدخول. ", "refresh": "تحديث رمز الاستجابة السريعة", "open_wallet_app": "يفتح {{walletName}} برنامج", - "login_with_id": "تسجيل الدخول باستخدام {{currentID}}" + "login_with_id": "تسجيل الدخول باستخدام {{currentID}}", + "wallet_header": "امسح رمز QR ضوئيًا باستخدام تطبيق {{walletName}}." }, "otp": { "sign_in_with_otp": "تسجيل الدخول باستخدام OTP", diff --git a/oidc-ui/public/locales/en.json b/oidc-ui/public/locales/en.json index bdeb06ce7..a3b6a08a9 100644 --- a/oidc-ui/public/locales/en.json +++ b/oidc-ui/public/locales/en.json @@ -123,7 +123,8 @@ "failed": "Unable to login. Please scan QR code and try again.", "refresh": "Refresh QR Code", "open_wallet_app": "Open {{walletName}} App", - "login_with_id": "Login with {{currentID}}" + "login_with_id": "Login with {{currentID}}", + "wallet_header": "Scan the QR code with {{walletName}} app" }, "otp": { "sign_in_with_otp": "Login with OTP", diff --git a/oidc-ui/public/locales/hi.json b/oidc-ui/public/locales/hi.json index 5db430d60..28c21d3d4 100644 --- a/oidc-ui/public/locales/hi.json +++ b/oidc-ui/public/locales/hi.json @@ -123,7 +123,8 @@ "failed": "लॉगिन करने में असमर्थ। ", "refresh": "क्यूआर कोड रिफ्रेश करें", "open_wallet_app": "खुला {{walletName}} अनुप्रयोग", - "login_with_id": "{{currentID}} से लॉगिन करें" + "login_with_id": "{{currentID}} से लॉगिन करें", + "wallet_header": "{{walletName}} ऐप से QR कोड को स्कैन करें" }, "otp": { "sign_in_with_otp": "ओटीपी से लॉगइन करें", diff --git a/oidc-ui/public/locales/km.json b/oidc-ui/public/locales/km.json index 7ffbdd48b..e7f77d89f 100644 --- a/oidc-ui/public/locales/km.json +++ b/oidc-ui/public/locales/km.json @@ -123,7 +123,8 @@ "failed": "មិនអាចចូលបានទេ។ ", "refresh": "ផ្ទុកកូដ QR ឡើងវិញ", "open_wallet_app": "បើក {{walletName}} កម្មវិធី", - "login_with_id": "ចូលដោយប្រើ {{currentID}}" + "login_with_id": "ចូលដោយប្រើ {{currentID}}", + "wallet_header": "ស្កេនកូដ QR ជាមួយកម្មវិធី {{walletName}}" }, "otp": { "sign_in_with_otp": "ចូលជាមួយ OTP", diff --git a/oidc-ui/public/locales/kn.json b/oidc-ui/public/locales/kn.json index 83cbf1015..c8e6b29cd 100644 --- a/oidc-ui/public/locales/kn.json +++ b/oidc-ui/public/locales/kn.json @@ -123,7 +123,8 @@ "failed": "ಲಾಗಿನ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ. ", "refresh": "QR ಕೋಡ್ ಅನ್ನು ರಿಫ್ರೆಶ್ ಮಾಡಿ", "open_wallet_app": "ತೆರೆಯಿರಿ {{walletName}} ಅಪ್ಲಿಕೇಶನ್", - "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ" + "login_with_id": "{{currentID}} ನೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", + "wallet_header": "{{walletName}} ಅಪ್ಲಿಕೇಶನ್‌ನೊಂದಿಗೆ QR ಕೋಡ್ ಅನ್ನು ಸ್ಕ್ಯಾನ್ ಮಾಡಿ" }, "otp": { "sign_in_with_otp": "OTP ಯೊಂದಿಗೆ ಲಾಗಿನ್ ಮಾಡಿ", diff --git a/oidc-ui/public/locales/ta.json b/oidc-ui/public/locales/ta.json index 86ae6a03d..fcc81a7fc 100644 --- a/oidc-ui/public/locales/ta.json +++ b/oidc-ui/public/locales/ta.json @@ -123,7 +123,8 @@ "failed": "உள்நுழைய முடியவில்லை. ", "refresh": "QR குறியீட்டைப் புதுப்பிக்கவும்", "open_wallet_app": "திற {{walletName}} ஆப்", - "login_with_id": "{{currentID}} மூலம் உள்நுழைக" + "login_with_id": "{{currentID}} மூலம் உள்நுழைக", + "wallet_header": "{{walletName}} ஆப்ஸ் மூலம் QR குறியீட்டை ஸ்கேன் செய்யவும்" }, "otp": { "sign_in_with_otp": "OTP மூலம் உள்நுழையவும்", diff --git a/oidc-ui/src/components/LoginQRCode.js b/oidc-ui/src/components/LoginQRCode.js index f68d876de..60a1ebf72 100644 --- a/oidc-ui/src/components/LoginQRCode.js +++ b/oidc-ui/src/components/LoginQRCode.js @@ -514,8 +514,9 @@ export default function LoginQRCode({ {qr && ( <>

- Scan the QR code with {walletDetail[walletConfigKeys.walletName]}{" "} - app + {t1("wallet_header", { + walletName: walletDetail[walletConfigKeys.walletName], + })}

)} - - {status.state === states.LOADING && ( - - )}
(pin = n)} + disabled={status.state === states.LOADING} />
@@ -426,12 +423,20 @@ export default function OtpVerify({ text={t1("resend_otp")} handleClick={handleSendOtp} id="resend_otp" - disabled={(showCaptcha && captchaToken === null) || !showResendOtp} + disabled={ + (showCaptcha && captchaToken === null) || + !showResendOtp || + status.state === states.LOADING + } customClassName={`!bg-white !border-none !p-0 !w-max !m-auto ${ showResendOtp ? "resend_otp" : "!text-gray-400" }`} />
+ + {status.state === states.LOADING && ( + + )} ); From ce72dcbcf81fcc7610f4e1943742dda86d8e579f Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Wed, 11 Dec 2024 12:20:27 +0530 Subject: [PATCH 17/40] [ES-1908] minor css fixes. (#1038) Signed-off-by: GurukiranP --- oidc-ui/public/theme/variables.css | 8 ++++---- oidc-ui/src/app/AppRouter.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/oidc-ui/public/theme/variables.css b/oidc-ui/public/theme/variables.css index d6a55676e..d4105dd86 100644 --- a/oidc-ui/public/theme/variables.css +++ b/oidc-ui/public/theme/variables.css @@ -3,7 +3,7 @@ --login-card-box-color: #BCC0C7; --login-card-box-hover-color: var(--primary-color); --login-card-box-focus-color: var(--primary-color); - --login-background: #FCFCFF; + --login-background: #f7f9fd; --language-icon-color: #FFFFFF; --language-icon-bg-color: var(--primary-color); --login-background-min-height: calc(100vh - 104.6px) !important; @@ -250,7 +250,7 @@ --login-card-box-color: #bcc0c7; --login-card-box-hover-color: var(--primary-color); --login-card-box-focus-color: var(--primary-color); - --login-background: #fcfcff; + --login-background: #f7f9fd; --login-background-image: url("/images/section-bg.png"); --login-background-min-height: calc(100vh - 157.6px) !important; --language-icon-color: #ffffff; @@ -306,13 +306,13 @@ --multipurpose-login-card-background-color: #ffffff; --side-section-bg-with: 226px; --top-left-bg-logo-url: url("/images/top_left_bg_logo.svg"); - --top-left-bg-logo-display: none; + --top-left-bg-logo-display: block; --bottom-left-bg-logo-url: url("/images/bg_bottom_left.png"); --bottom-left-bg-logo-display: none; --top-right-bg-logo-url: url("/images/top_left_bg_logo.svg"); --top-right-bg-logo-display: none; --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); - --bottom-right-bg-logo-display: none; + --bottom-right-bg-logo-display: block; --login-client-logo-image-height: 3.25rem; --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21CB"; diff --git a/oidc-ui/src/app/AppRouter.js b/oidc-ui/src/app/AppRouter.js index 49b915fa5..c3fc433a8 100644 --- a/oidc-ui/src/app/AppRouter.js +++ b/oidc-ui/src/app/AppRouter.js @@ -57,19 +57,19 @@ export const AppRouter = () => { ) : ( <> top left background bottom left background top right background bottom right background From d9c0b4d2eb72212b668ca032ba77e1c43c44d576 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:26:20 +0530 Subject: [PATCH 18/40] [ES-1908] UI fixes. (#1045) Signed-off-by: GurukiranP --- oidc-ui/public/theme/variables.css | 44 ++++++++++++++--------------- oidc-ui/src/components/OtpVerify.js | 2 +- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/oidc-ui/public/theme/variables.css b/oidc-ui/public/theme/variables.css index d4105dd86..5338abd8b 100644 --- a/oidc-ui/public/theme/variables.css +++ b/oidc-ui/public/theme/variables.css @@ -115,7 +115,7 @@ --selected_login_id_focus_outline_color: var(--primary-color); --qr_code_title_color: #696969; --resend_otp_button_color: var(--primary-color); - --login-client-logo-image-shadow: 0px 4px 15px 2px #EB6F2D4D; + --login-client-logo-image-shadow: 0px 4px 15px 2px #1262C94D; --caps-lock-text-color: #2D86BA; --login-header-text-color: rgb(0, 0, 0); --login-subheader-text-color: #838383; @@ -245,16 +245,15 @@ } [class="green"] { - --primary-color: #4CBB17; - --font-family: "Kantumruy Pro", sans-serif; + --primary-color: #32CD32; --login-card-box-color: #bcc0c7; --login-card-box-hover-color: var(--primary-color); --login-card-box-focus-color: var(--primary-color); --login-background: #f7f9fd; --login-background-image: url("/images/section-bg.png"); - --login-background-min-height: calc(100vh - 157.6px) !important; --language-icon-color: #ffffff; --language-icon-bg-color: var(--primary-color); + --login-background-min-height: calc(100vh - 104.6px) !important; --brand-only-logo-url: url("/logo.png"); --brand-logo-url: url("/images/brand_logo.png"); --background-logo-url: url("/images/illustration_one.png"); @@ -265,9 +264,9 @@ --primary-button-hover-color: #ffffff; --primary-button-hover-bg-color: var(--primary-color); --primary-button-hover-border-color: var(--primary-color); - --primary-button-disable-color: #94a3b8; - --primary-button-disable-bg-color: #ffffff; - --primary-button-disable-border-color: #94a3b8; + --primary-button-disable-color: #ffffff; + --primary-button-disable-bg-color: #cbcbcb; + --primary-button-disable-border-color: #cbcbcb; --secondary-button-color: green; --secondary-button-bg-color: #ffffff; --secondary-button-border-color: #ffffff; @@ -284,27 +283,26 @@ --info-icon-color: var(--primary-color); --toggle-button-inactive-bg-color: #ffffff; --toggle-button-inactive-border-color: #d8d8d8; - --toggle-button-active-bg-color: #ffffff; - --toggle-button-active-border-color: var(--primary-color); - --toggle-button-inactive-ball-color: #a7a8a9; - --toggle-button-inactive-ball-border-color: #a7a8a9; + --toggle-button-active-bg-color: #ffdecc; + --toggle-button-active-border-color: #ffffff; + --toggle-button-inactive-ball-color: #cbcbcb; + --toggle-button-inactive-ball-border-color: #cbcbcb; --toggle-button-active-ball-color: var(--primary-color); --toggle-button-active-ball-border-color: var(--primary-color); --toggle-button-ring-color: var(--primary-color); - --header-height: 70px; - --footer-height: 56px; - --signup-banner-background-color: #90EE90; - --signup-banner-bottom-radius: 20px; + --header-height: 55px; + --footer-height: 47px; + --signup-banner-background-color: #98FB98; + --signup-banner-bottom-radius: 10px; --signup-banner-padding-top: 20px; --signup-banner-padding-bottom: 20px; --signup-banner-margin-top: 16px; --signup-banner-signup-url-color: green; --signup-banner-signup-url-color-hover: var(--primary-color); --signup-banner-signup-url-color-focus: var(--primary-color); - --signup-banner-font: normal normal normal 15px/19px "Kantumruy Pro"; + --signup-banner-font: normal normal normal 15px/19px Roboto; --multipurpose-login-card-border-radius: 20px; --multipurpose-login-card-background-color: #ffffff; - --side-section-bg-with: 226px; --top-left-bg-logo-url: url("/images/top_left_bg_logo.svg"); --top-left-bg-logo-display: block; --bottom-left-bg-logo-url: url("/images/bg_bottom_left.png"); @@ -315,14 +313,14 @@ --bottom-right-bg-logo-display: block; --login-client-logo-image-height: 3.25rem; --login-client-logo-image-width: 3.25rem; - --login-alternate-arrow: "\21CB"; - --login-alternate-arrow-size: 3rem; + --login-alternate-arrow: "\21C4"; + --login-alternate-arrow-size: 1.5rem; --login-text-color: #000000; --login-card-separator: linear-gradient(0deg, #ffffff 0%, #f7fcff 100%); - --login-card-separator-height: 20px; - --footer-text-color: #ffffff; + --login-card-separator-height: 1px; + --footer-text-color: #898989; --footer-text-size: 14px; - --footer-bg-color: var(--primary-color); + --footer-bg-color: #ffffff; --language-check-icon-color: var(--primary-color); --forgot-password-color: var(--primary-color); --forgot-password-font-size: small; @@ -369,7 +367,7 @@ --selected_login_id_focus_outline_color: var(--primary-color); --qr_code_title_color: #696969; --resend_otp_button_color: var(--primary-color); - --login-client-logo-image-shadow: 0px 4px 15px 2px #EB6F2D4D; + --login-client-logo-image-shadow: 0px 4px 15px 2px #32CD324D; --caps-lock-text-color: #2D86BA; --login-header-text-color: rgb(0, 0, 0); --login-subheader-text-color: #838383; diff --git a/oidc-ui/src/components/OtpVerify.js b/oidc-ui/src/components/OtpVerify.js index 2a911cf46..7e4afa233 100644 --- a/oidc-ui/src/components/OtpVerify.js +++ b/oidc-ui/src/components/OtpVerify.js @@ -405,7 +405,7 @@ export default function OtpVerify({ )} Date: Wed, 18 Dec 2024 12:21:30 +0530 Subject: [PATCH 19/40] [DSD-6895] Updated base image from node:12.18.4-alpine to node:18-alpine (#1058) Signed-off-by: Praful Rakhade --- oidc-ui/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oidc-ui/Dockerfile b/oidc-ui/Dockerfile index 5039904ce..0b3271d18 100644 --- a/oidc-ui/Dockerfile +++ b/oidc-ui/Dockerfile @@ -1,4 +1,4 @@ -FROM node:12.18.4-alpine as build_esignet_ui +FROM node:18-alpine as build_esignet_ui # Set a build-time environment variable (replace YOUR_ENV_VARIABLE_NAME with the desired variable name) ARG oidcUIPublicUrl From 1c8da2b404db0ccfc7cedfd3bcab7a0d8aa1335b Mon Sep 17 00:00:00 2001 From: Zeeshan Mehboob <82993262+zesu22@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:00:44 +0530 Subject: [PATCH 20/40] [ES-1991] added css for removing extra eye icon in edge browser (#1054) * [ADDED] css for removing extra eye icon in edge browser Signed-off-by: Zeeshan Mehboob * Revert [ADDED] css for removing extra eye icon in edge browser Signed-off-by: Zeeshan Mehboob * [ADDED] css for extra eye removal from password field in edge Signed-off-by: Zeeshan Mehboob --------- Signed-off-by: Zeeshan Mehboob --- oidc-ui/src/App.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/oidc-ui/src/App.css b/oidc-ui/src/App.css index 4ec195684..bf5fca9f5 100644 --- a/oidc-ui/src/App.css +++ b/oidc-ui/src/App.css @@ -618,6 +618,11 @@ input[type="number"] { padding-top: 10px; } +input[type='password']::-ms-reveal, +input[type='password']::-ms-clear { + display: none; +} + @media screen and (max-width: 375px) { .pincode-input-text { width: 2em !important; From b26d59cf8975315e6460abd0ebc4b5e0a12e1e76 Mon Sep 17 00:00:00 2001 From: ase-101 Date: Thu, 19 Dec 2024 12:53:24 +0530 Subject: [PATCH 21/40] Merge from release-1.5.x to develop (#1059) Signed-off-by: ckm007 * [DSD-6382] updated chart version in helm and deployment scripts Signed-off-by: ckm007 * [DSD-6382] updated changes as reported during dev round Signed-off-by: ckm007 * [DSD-6382] adding fixes for issues reported during dev round Signed-off-by: ckm007 * [DSD-6382] removing swp file added by mistake Signed-off-by: ckm007 * [DSD-6382] increased initialDelaySeconds in esignet helm Signed-off-by: ckm007 * [DSD-6382] updated healthcheck configurations Signed-off-by: ckm007 * [DSD-6382] removed unused clusterrolebinding Signed-off-by: ckm007 * Updated readme, pom.xml. Added NoOpKeyBinder default implementation Signed-off-by: ase-101 * [DSD-6382] updated correct onboarder chart version Signed-off-by: ckm007 * [DSD-6382] removed not required enabled params from realm attribute Signed-off-by: ckm007 * [DSD-6382] added misp key update part Signed-off-by: ckm007 * [DSD-6382] updated message Signed-off-by: ckm007 * MOSIP-36454 - fixed build issue by adding suite setup in runner file (#947) Signed-off-by: Mohanachandran S * [DSD-6382] added captcha secret update part to initialisation script (#943) * [DSD-6382] added captcha secret update part to initialisation script Signed-off-by: ckm007 * Update initialise-prereq.sh Signed-off-by: Chandra Keshav Mishra * Update initialise-prereq.sh Signed-off-by: Chandra Keshav Mishra --------- Signed-off-by: ckm007 Signed-off-by: Chandra Keshav Mishra * [ES-842] Updated the stoplight (#944) (#945) Signed-off-by: Venkata Saidurga Polamraju Signed-off-by: ase-101 Co-authored-by: pvsaidurga <132046494+pvsaidurga@users.noreply.github.com> * ES-842 Using mulitple profiles to avoid duplicate configuration Signed-off-by: ase-101 * Delete .github/workflows/codeql-analysis.yml (#965) Signed-off-by: rajapandi1234 <138785181+rajapandi1234@users.noreply.github.com> * ES-1807 & updated postman collection (#964) * ES-1807 Signed-off-by: ase-101 * Updated postman collection Signed-off-by: ase-101 --------- Signed-off-by: ase-101 * [DSD-6445] (#974) * [DSD-6445 updated the readme n install script] Signed-off-by: techno-376 * [DSD-6445] Signed-off-by: techno-376 * [DSD-6445] Signed-off-by: techno-376 * Update install-prereq.sh Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> * Update install.sh Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> --------- Signed-off-by: techno-376 Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> Co-authored-by: techno-376 Co-authored-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> * [DSD-6445] Updated esignet-global-cm.yaml.sample Signed-off-by: Abhishek S <127825992+abhishek8shankar@users.noreply.github.com> * [DSD-6445] Updated esignet-global-cm.yaml.sample (#977) Signed-off-by: Abhishek S <127825992+abhishek8shankar@users.noreply.github.com> * [DSD-6445] Updated install.sh scripts for softhsm . Signed-off-by: Abhishek S <127825992+abhishek8shankar@users.noreply.github.com> * [DSD-6445]Updated application-default.properties Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> * [ES-1892] [ES-1358] i18n translation changes and minor UI changes. (#986) Signed-off-by: GurukiranP * ES-842 Signed-off-by: ase-101 * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37214 | Syncing changes from develop to Release 1.5.x Signed-off-by: Nandhukumar * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37214 Signed-off-by: Nandhukumar * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37476 - updated the docker file for jdk version Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com> * MOSIP-37214 Signed-off-by: Nandhukumar * MOSIP-37214 Signed-off-by: Nandhukumar * [DSD-6445] Updated install.sh for opting plugings Signed-off-by: Abhi * MOSIP-37214 Signed-off-by: Nandhukumar * [MOSIP-37447] added nfs helm changes for apitestrig Signed-off-by: bhumi46 * MOSIP-37214 Signed-off-by: Nandhukumar * [ES-1553] Hidden the language dropdown in the claim-details screen. (#1005) Signed-off-by: GurukiranP * ES-1929 (#1007) Signed-off-by: ase-101 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * [MOSIP-37447] restructured postgres Signed-off-by: bhumi46 * MOSIP-37793 - Updated the Readme file Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com> * [MOSIP-37815] Added Unique Config as User input (#1017) * [MOSIP-37815] Added Unique Config as Userinput Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Fixed Chart lint Failure Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Updated as per requirement Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Updated values.yaml Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Tested Working as expected Signed-off-by: VSIVAKALYAN * [MOSIP-37815] Signed-off-by: VSIVAKALYAN --------- Signed-off-by: VSIVAKALYAN * [DSD-6742] updated/restructured install scripts for fixes Signed-off-by: ckm007 * MOSIP-36485 Signed-off-by: Nandhukumar * MOSIP-36485 Signed-off-by: Nandhukumar * ES-1987 Signed-off-by: Nandhukumar * MOSIP-36485 (#1028) Signed-off-by: Nandhukumar * MOSIP-36485 | changes validation message (#1031) * MOSIP-36485 Signed-off-by: Nandhukumar * MOSIP-36485 Signed-off-by: Nandhukumar --------- Signed-off-by: Nandhukumar * ES-1856 updated Readme file as per review comments (#1023) * ES-1856 Signed-off-by: ase-101 * ES-1856 Signed-off-by: ase-101 * ES-1856 Signed-off-by: ase-101 * Updated branch name Signed-off-by: ase-101 --------- Signed-off-by: ase-101 * [DSD-6742] Updated values.yaml Signed-off-by: shubham_G * [DSD-6742] Updated install.sh replaced global cm as esignet-global Signed-off-by: shubham_G * [DSD-6742] Updated apitestrig README Signed-off-by: ckm007 * [DSD-6742] Updated Env variables in values.yaml Signed-off-by: ckm007 * [DSD-6742] removed unwanted Env variables in values.yaml Signed-off-by: ckm007 * [DSD-6742] updated README Signed-off-by: ckm007 * ES-842 (#1046) Signed-off-by: ase-101 * Updated postman collection & docker compose (#1048) * Updated postman collection & docker compose Signed-off-by: ase-101 * Updated postman collection & docker compose Signed-off-by: ase-101 --------- Signed-off-by: ase-101 * ES-842 Update esignet-openapi.yaml Signed-off-by: ase-101 * [DSD-6888]Update push-trigger.yml to check results for release. (#1051) Signed-off-by: Mahesh-Binayak <76687012+Mahesh-Binayak@users.noreply.github.com> * [DSD-6888]Update push-trigger.yml reverting changes for sonar test (#1052) Signed-off-by: Mahesh-Binayak <76687012+Mahesh-Binayak@users.noreply.github.com> * Update README.md Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> * Update Dockerfile (#1057) Signed-off-by: Praful Rakhade * MOSIP-36485 | Updated the commons released version in pom.xml (#1056) * MOSIP-36485 Signed-off-by: Nandhukumar * MOSIP-36485 Signed-off-by: Nandhukumar * MOSIP-36485 Signed-off-by: Nandhukumar --------- Signed-off-by: Nandhukumar * [MOSIP-36849]updated README.md (#1055) * [MOSIP-36849]updated README.md Signed-off-by: techno-376 * Update README.md Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> --------- Signed-off-by: techno-376 Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> * Updated develop branch name Signed-off-by: ase-101 * Updated api-test pom.xml Signed-off-by: ase-101 --------- Signed-off-by: ckm007 Signed-off-by: ase-101 Signed-off-by: Mohanachandran S Signed-off-by: Chandra Keshav Mishra Signed-off-by: Venkata Saidurga Polamraju Signed-off-by: rajapandi1234 <138785181+rajapandi1234@users.noreply.github.com> Signed-off-by: techno-376 Signed-off-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> Signed-off-by: Abhishek S <127825992+abhishek8shankar@users.noreply.github.com> Signed-off-by: GurukiranP Signed-off-by: Nandhukumar Signed-off-by: bhumi46 Signed-off-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com> Signed-off-by: Abhi Signed-off-by: VSIVAKALYAN Signed-off-by: shubham_G Signed-off-by: Mahesh-Binayak <76687012+Mahesh-Binayak@users.noreply.github.com> Signed-off-by: Praful Rakhade Co-authored-by: ckm007 Co-authored-by: Mohanachandran S <165888272+mohanachandran-s@users.noreply.github.com> Co-authored-by: pvsaidurga <132046494+pvsaidurga@users.noreply.github.com> Co-authored-by: rajapandi1234 <138785181+rajapandi1234@users.noreply.github.com> Co-authored-by: techno-376 Co-authored-by: Rakshitha650 <76676196+Rakshitha650@users.noreply.github.com> Co-authored-by: Abhishek S <127825992+abhishek8shankar@users.noreply.github.com> Co-authored-by: Gk <76690271+gk-4VII@users.noreply.github.com> Co-authored-by: Nandhukumar Co-authored-by: bhumi46 Co-authored-by: Abhi Co-authored-by: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com> Co-authored-by: Nandhukumar <100744358+nandhu-kumar@users.noreply.github.com> Co-authored-by: shubham_G Co-authored-by: Mahesh-Binayak <76687012+Mahesh-Binayak@users.noreply.github.com> Co-authored-by: Praful Rakhade --- README.md | 10 +- api-test/pom.xml | 8 +- .../esignet/testrunner/MosipTestRunner.java | 6 +- .../PostWithAutogenIdWithOtpGenerate.java | 6 +- .../apirig/esignet/utils/EsignetUtil.java | 5 +- .../main/resources/config/esignet.properties | 6 +- .../esignet/WalletBinding/WalletBinding.yml | 4 +- api-test/testNgXmlFiles/esignetSuite.xml | 6 +- deploy/.gitignore | 1 + deploy/README.md | 44 +- deploy/captcha/install.sh | 63 +- deploy/config-server/install.sh | 15 +- deploy/esignet-apitestrig/README.md | 20 +- deploy/esignet-apitestrig/delete.sh | 2 +- deploy/esignet-apitestrig/install.sh | 109 +++- deploy/esignet-apitestrig/values.yaml | 12 +- ...-cm.yaml => esignet-global-cm.yaml.sample} | 0 deploy/esignet/install.sh | 30 +- deploy/initialise-prereq.sh | 26 +- deploy/install-esignet.sh | 2 +- deploy/install-prereq.sh | 32 +- deploy/keycloak/install.sh | 1 + deploy/keycloak/keycloak-init-values.yaml | 2 - deploy/oidc-ui/install.sh | 22 +- deploy/postgres/.gitignore | 3 +- .../chart/istio-addons/templates/gateway.yaml | 2 +- .../chart/istio-addons/templates/vs.yaml | 5 +- .../postgres/chart/istio-addons/values.yaml | 2 +- deploy/postgres/delete.sh | 10 +- deploy/postgres/generate-secret-cm.py | 6 +- deploy/postgres/init_values.yaml | 6 +- deploy/postgres/install.sh | 48 +- deploy/postgres/nginx/transportserver.yaml | 4 +- deploy/postgres/postgres-config.yaml | 8 +- deploy/postgres/postgres-init.sh | 13 +- deploy/postgres/postgres-postgresql.yaml | 9 + deploy/redis/install.sh | 34 +- deploy/softhsm/install.sh | 34 ++ deploy/{ => softhsm}/softhsm-values.yaml | 0 docker-compose/README.md | 37 +- docker-compose/dependent-docker-compose.yml | 2 +- docker-compose/docker-compose.yml | 13 +- docs/design/eSignet-authorize-flow.md | 26 + docs/design/eSignet-authorize-qr-code-flow.md | 20 + docs/eSignet-design-OIDC-authorize-flow.png | Bin 0 -> 208733 bytes docs/esignet-authorize-qr-code-flow.png | Bin 0 -> 352166 bytes docs/esignet-openapi.yaml | 212 ++----- docs/esignet-overview.png | Bin 0 -> 198430 bytes docs/esignet-wallet-binding.png | Bin 0 -> 139868 bytes ...ometrics.png => esignet-with-bio-auth.png} | Bin esignet-service/README.md | 52 +- esignet-service/configure_start.sh | 7 + .../resources/application-default.properties | 35 +- .../resources/application-local.properties | 383 +----------- .../src/main/resources/bootstrap.properties | 5 +- helm/apitestrig/.gitignore | 1 + helm/apitestrig/.helmignore | 21 + helm/apitestrig/Chart.yaml | 19 + helm/apitestrig/README.md | 10 + helm/apitestrig/templates/NOTES.txt | 1 + helm/apitestrig/templates/_helpers.tpl | 63 ++ helm/apitestrig/templates/clusterrole.yaml | 10 + .../templates/clusterrolebinding.yaml | 6 +- helm/apitestrig/templates/configmaps.yaml | 21 + helm/apitestrig/templates/cronjob.yaml | 119 ++++ helm/apitestrig/templates/extra-list.yaml | 4 + helm/apitestrig/templates/pv.yaml | 23 + helm/apitestrig/templates/pvc.yaml | 23 + helm/apitestrig/templates/secrets.yaml | 22 + .../templates/service-account.yaml} | 15 +- helm/apitestrig/values.yaml | 559 ++++++++++++++++++ helm/esignet/Chart.yaml | 2 +- helm/esignet/values.yaml | 14 +- helm/oidc-ui/Chart.yaml | 2 +- helm/oidc-ui/values.yaml | 2 +- .../services/AuthorizationHelperService.java | 4 +- .../services/AuthorizationServiceImpl.java | 7 +- .../esignet/services/ClaimsHelperService.java | 3 +- .../services/AuthorizationServiceTest.java | 55 +- partner-onboarder/README.md | 13 + partner-onboarder/install.sh | 8 +- partner-onboarder/values.yaml | 17 +- ...Signet-with-MOSIP.postman_environment.json | 14 +- ...eSignet-with-mock.postman_environment.json | 14 +- .../eSignet.postman_collection.json | 91 ++- 85 files changed, 1763 insertions(+), 808 deletions(-) create mode 100644 deploy/.gitignore rename deploy/{esignet-global-cm.yaml => esignet-global-cm.yaml.sample} (100%) create mode 100644 deploy/postgres/postgres-postgresql.yaml create mode 100755 deploy/softhsm/install.sh rename deploy/{ => softhsm}/softhsm-values.yaml (100%) create mode 100644 docs/design/eSignet-authorize-flow.md create mode 100644 docs/design/eSignet-authorize-qr-code-flow.md create mode 100644 docs/eSignet-design-OIDC-authorize-flow.png create mode 100644 docs/esignet-authorize-qr-code-flow.png create mode 100644 docs/esignet-overview.png create mode 100644 docs/esignet-wallet-binding.png rename docs/{IdP - OIDC Flow - Biometrics.png => esignet-with-bio-auth.png} (100%) create mode 100644 helm/apitestrig/.gitignore create mode 100644 helm/apitestrig/.helmignore create mode 100644 helm/apitestrig/Chart.yaml create mode 100644 helm/apitestrig/README.md create mode 100644 helm/apitestrig/templates/NOTES.txt create mode 100644 helm/apitestrig/templates/_helpers.tpl create mode 100644 helm/apitestrig/templates/clusterrole.yaml rename helm/{esignet => apitestrig}/templates/clusterrolebinding.yaml (74%) create mode 100644 helm/apitestrig/templates/configmaps.yaml create mode 100644 helm/apitestrig/templates/cronjob.yaml create mode 100644 helm/apitestrig/templates/extra-list.yaml create mode 100644 helm/apitestrig/templates/pv.yaml create mode 100644 helm/apitestrig/templates/pvc.yaml create mode 100644 helm/apitestrig/templates/secrets.yaml rename helm/{oidc-ui/templates/clusterrolebinding.yaml => apitestrig/templates/service-account.yaml} (51%) create mode 100644 helm/apitestrig/values.yaml diff --git a/README.md b/README.md index 384649b6b..ffe9e5bc8 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mosip_esignet&id=mosip_esignet&metric=alert_status)](https://sonarcloud.io/dashboard?id=mosip_esignet) # eSignet Project ## Overview + +eSignet offers a seamless and straightforward solution for incorporating an existing trusted identity database into the digital realm via plugins. + This repository contains limited OpenId protocol implementation with: * OAuth 2.0 RFC 6749 - Authorization code flow support * OAuth 2.0 RFC 7636 - PKCE security extension @@ -9,13 +12,12 @@ This repository contains limited OpenId protocol implementation with: * RFC 7519 - ID token and access token as JWT * OpenID Connect Discovery 1.0 - /.well-known/openid-configuration * RFC 5785 - Followed for both openid and oauth well-knowns -* Identity assurance 1.0 +* Identity Assurance 1.0 ## High level overview of eSignet with external systems -![esignet-architecture-overview.png](docs/esignet-architecture-overview.png) +![esignet-overview.png](docs/esignet-overview.png) -`Note: Kindly refer `[eSignet signup repository](https://github.com/mosip/esignet-signup)` for more details on eSignet signup module.` eSignet repository contains following: @@ -43,7 +45,7 @@ Refer to [SQL scripts](db_scripts). The project requires JDK 11. 1. Build: ``` - $ mvn clean install -Dgpg.skip=true + $ mvn clean install -Dgpg.skip=true -Dmaven.gitcommitid.skip=true ``` ## [Deployment in K8 cluster](deploy/README.md) diff --git a/api-test/pom.xml b/api-test/pom.xml index 0f909aa6f..edbd33246 100644 --- a/api-test/pom.xml +++ b/api-test/pom.xml @@ -8,7 +8,7 @@ apitest-esignet Parent project of MOSIP Esignet apitests https://github.com/mosip/esignet - 1.2.1-SNAPSHOT + 1.5.1-SNAPSHOT @@ -49,14 +49,14 @@ 2.2.1 3.0.1 - apitest-esignet-1.2.1-SNAPSHOT-jar-with-dependencies + apitest-esignet-1.5.1-SNAPSHOT-jar-with-dependencies - io.mosip.testrig.apirig.apitest.commons + io.mosip.testrig.apitest.commons apitest-commons - 1.2.2-SNAPSHOT + 1.3.0 diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java index 74d3e0fe7..255c95f5a 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testrunner/MosipTestRunner.java @@ -159,13 +159,13 @@ public static void suiteSetup(String runType) { BaseTestCase.certsForModule = GlobalConstants.ESIGNET; DBManager.executeDBQueries(EsignetConfigManager.getKMDbUrl(), EsignetConfigManager.getKMDbUser(), EsignetConfigManager.getKMDbPass(), EsignetConfigManager.getKMDbSchema(), - getGlobalResourcePath() + "/" + "config/keyManagerCertDataDeleteQueries.txt"); + getGlobalResourcePath() + "/" + "config/keyManagerDataDeleteQueriesForEsignet.txt"); DBManager.executeDBQueries(EsignetConfigManager.getIdaDbUrl(), EsignetConfigManager.getIdaDbUser(), EsignetConfigManager.getPMSDbPass(), EsignetConfigManager.getIdaDbSchema(), - getGlobalResourcePath() + "/" + "config/idaCertDataDeleteQueries.txt"); + getGlobalResourcePath() + "/" + "config/idaDeleteQueriesForEsignet.txt"); DBManager.executeDBQueries(EsignetConfigManager.getMASTERDbUrl(), EsignetConfigManager.getMasterDbUser(), EsignetConfigManager.getMasterDbPass(), EsignetConfigManager.getMasterDbSchema(), - getGlobalResourcePath() + "/" + "config/masterDataCertDataDeleteQueries.txt"); + getGlobalResourcePath() + "/" + "config/masterDataDeleteQueriesForEsignet.txt"); AdminTestUtil.initiateesignetTest(); BaseTestCase.otpListener = new OTPListener(); BaseTestCase.otpListener.run(); diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/PostWithAutogenIdWithOtpGenerate.java b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/PostWithAutogenIdWithOtpGenerate.java index c953c2b45..32de9e2b8 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/PostWithAutogenIdWithOtpGenerate.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/testscripts/PostWithAutogenIdWithOtpGenerate.java @@ -93,16 +93,12 @@ public void test(TestCaseDTO testCaseDTO) if (testCaseDTO.getTestCaseName().contains("VID") || testCaseDTO.getTestCaseName().contains("Vid")) { - - - - - if (!BaseTestCase.getSupportedIdTypesValue().contains("VID") && !BaseTestCase.getSupportedIdTypesValue().contains("vid")) { throw new SkipException(GlobalConstants.VID_FEATURE_NOT_SUPPORTED); } } + String inputJson = testCaseDTO.getInput().toString(); JSONObject req = new JSONObject(testCaseDTO.getInput()); diff --git a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java index 71554f745..3dda3ab15 100644 --- a/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java +++ b/api-test/src/main/java/io/mosip/testrig/apirig/esignet/utils/EsignetUtil.java @@ -185,9 +185,8 @@ public static String isTestCaseValidForExecution(TestCaseDTO testCaseDTO) { // Let run test cases eSignet & MOSIP API calls --- both UIN and VID // BaseTestCase.setSupportedIdTypes(Arrays.asList("UIN", "VID")); - getSupportedIdTypesValueFromActuator(); - + logger.info("supportedIdType = " + supportedIdType); String endpoint = testCaseDTO.getEndPoint(); @@ -251,4 +250,4 @@ public static String replaceKeywordValue(String jsonString, String keyword, Stri throw new SkipException("Marking testcase as skipped as required fields are empty " + keyword); } -} \ No newline at end of file +} diff --git a/api-test/src/main/resources/config/esignet.properties b/api-test/src/main/resources/config/esignet.properties index b6a0a4ff4..79fbd4cf8 100644 --- a/api-test/src/main/resources/config/esignet.properties +++ b/api-test/src/main/resources/config/esignet.properties @@ -5,9 +5,9 @@ tokenEndpoint=/v1/esignet/oauth/token validateBindingEndpoint=ida-binding esignetWellKnownEndPoint=/v1/esignet/oidc/.well-known/openid-configuration signupSettingsEndPoint=/v1/signup/settings -esignetActuatorPropertySection=application-default.properties +esignetActuatorPropertySection=classpath:/application-default.properties # uncomment below property while executing on camdgc env locally -servicesNotDeployed=resident,ridgenerator,admin,hotlist,preregistration,registrationprocessor,hub,print,packetcreator,packetmanager,mock-abis-service,mockmv,sunbirdrc +servicesNotDeployed=sunbirdrc # uncomment below property while executing on non camdgc env locally #servicesNotDeployed= esignetMockBaseURL=esignet-insurance. @@ -17,4 +17,4 @@ sunBirdBaseURL=https://registry.dev1.mosip.net #------------------------- Need to check if these are used or not ------------------------# OTPTimeOut = 181 -attempt = 10 \ No newline at end of file +attempt = 10 diff --git a/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml b/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml index 7918c0b42..aaf4092c6 100644 --- a/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml +++ b/api-test/src/main/resources/esignet/WalletBinding/WalletBinding.yml @@ -407,7 +407,7 @@ WalletBinding: "errors": [ { "errorCode": "IDA-MLC-009", - "errorMessage": "request.individualId: invalid_individual_id" + "errorMessage": "$IGNORE$" } ], "sendOtpResp": { @@ -2789,4 +2789,4 @@ WalletBinding: "sendOtpResTemplate":"esignet/SendBindingOtp/SendBindingOtpResult", "maskedEmail": "$IGNORE$" } -}' \ No newline at end of file +}' diff --git a/api-test/testNgXmlFiles/esignetSuite.xml b/api-test/testNgXmlFiles/esignetSuite.xml index 9c0ae6a95..5763e2215 100644 --- a/api-test/testNgXmlFiles/esignetSuite.xml +++ b/api-test/testNgXmlFiles/esignetSuite.xml @@ -7,6 +7,7 @@ + @@ -494,6 +495,5 @@ - --> - - \ No newline at end of file + --> + diff --git a/deploy/.gitignore b/deploy/.gitignore new file mode 100644 index 000000000..5440660c2 --- /dev/null +++ b/deploy/.gitignore @@ -0,0 +1 @@ +esignet-global-cm.yaml diff --git a/deploy/README.md b/deploy/README.md index d1ca09675..22ba37543 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -24,19 +24,51 @@ * __Logging__ : Setup logging as per [steps](https://github.com/mosip/k8s-infra/tree/v1.2.0.2/logging). * __Monitoring__ : Setup monitoring consisting elasticsearch, kibana, grafana using [steps](https://github.com/mosip/k8s-infra/tree/v1.2.0.2/monitoring). ### Install Pre-requisites -``` -./install-prereq.sh -``` +* `esignet-global` configmap: For eSignet K8's env, `esignet-global` configmap in `esignet` namespace contains Domain related information. Follow below steps to add domain details for `esignet-global` configmap. + * Copy `esignet-global-cm.yaml.sample` to `esignet-global-cm.yaml`. + * Update the domain names in `esignet-global-cm.yaml` correctly for your environment. + * Create a google recaptcha v2 ("I am not a Robot") from Google with required domain name ex:[sandbox.mosip.net] [Recaptcha Admin](https://www.google.com/recaptcha/about/) and set esignet captcha. +* Install pre-requisites + ``` + ./install-prereq.sh + ``` ### Initialise pre-requisites * Update values file for postgres init [here](postgres/init_values.yaml). -* Execute `initialise-prereq.sh` script to initialise postgres and keycloak and set esignet captcha. +* Execute `initialise-prereq.sh` script to initialise postgres and keycloak. ``` ./initialise-prereq.sh ``` -### Install esignet, oidc and captcha service +### Install esignet and oidc +During deployment, the system will prompt for user input to select the appropriate plugin. The available options are listed below: +1. esignet-mock-plugin.jar +2. mosip-identity-plugin.jar + +For current scope of deployment, as it is limited to mock functionality, +select option 1 (esignet-mock-plugin.jar). ``` ./install-esignet.sh ``` ## Onboarder -* If Esignet is getting deployed with MOSIP than we need to execute the onboarder for MISP partner. +* There are two ways to proceed, either with mosip identity plugin or with mock plugin. +### MOSIP Identity Plugin +* If Esignet is getting deployed with MOSIP then we need to execute the onboarder for MISP partner and mock-rp oidc clientId. * Onboarder [scripts](../partner-onboarder/). + +### MOCK Plugin +* Download postman collection from [here](../postman-collection) +* Fetch auth token from esignet keycloak. Update the "client_secret" in the request body. +* Run the request under "OIDC Client Mgmt" -> "Mock" -> "Get Auth token" +* Run the requests under + + a. "OIDC Client Mgmt" -> "Mock" -> "Get CSRF token" + + b. "OIDC Client Mgmt" -> "Mock" -> "Create OIDC client" + +### NOTE: +This deployment is limited to mock +Below section related to configuring IDA is not tested. Still it can be tried out + +### CONFIGURE IDA for Esignet : +Onboard eSignet as MISP partner in MOSIP PMS using our onboarder script +We should override properties defined [here](https://github.com/mosip/esignet-plugins/blob/release-1.3.x/mosip-identity-plugin/src/main/resources/application.properties) if there is any change in the MOSIP IDA domain names. +Update the 'MOSIP_ESIGNET_AUTHENTICATOR_IDA_SECRET_KEY' property with MOSIP IDA keycloak client secret. diff --git a/deploy/captcha/install.sh b/deploy/captcha/install.sh index 6ba347253..a1878aee7 100755 --- a/deploy/captcha/install.sh +++ b/deploy/captcha/install.sh @@ -1,20 +1,31 @@ #!/bin/bash -## Installing captcha validation server. +## Combined Script: Installing Captcha Validation Server and Initializing Prerequisites ## Usage: ./install.sh [kubeconfig] if [ $# -ge 1 ] ; then export KUBECONFIG=$1 fi +ROOT_DIR=`pwd` NS=captcha CHART_VERSION=0.1.0-develop -echo Create $NS namespace -kubectl create ns $NS - function installing_captcha() { - echo Istio label + while true; do + read -p "Do you want to continue installing captcha validation service? (y/n): " 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 + + echo "Creating $NS namespace" + kubectl create ns $NS || true + echo "Applying Istio label to namespace" kubectl label ns $NS istio-injection=disabled --overwrite helm repo update @@ -31,11 +42,47 @@ function installing_captcha() { fi done - echo Installing captcha + echo "Installing captcha" helm -n $NS install captcha mosip/captcha --version $CHART_VERSION --set metrics.serviceMonitor.enabled=$servicemonitorflag --wait - echo Installed captcha service - return 0 + echo "Installed captcha service" + + # Set up Captcha secrets for eSignet + while true; do + read -p "Do you want to continue configuring Captcha secrets for esignet? (y/n): " ans + if [[ "$ans" == "Y" || "$ans" == "y" ]]; then + ESIGNET_HOST=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-esignet-host}) + echo "Please create captcha site and secret key for esignet domain: $ESIGNET_HOST" + + echo "Please enter the reCAPTCHA admin site key for domain $ESIGNET_HOST:" + read ESITE_KEY + echo "Please enter the reCAPTCHA admin secret key for domain $ESIGNET_HOST:" + read ESECRET_KEY + + echo "Setting up Captcha secrets" + kubectl -n esignet create secret generic esignet-captcha --from-literal=esignet-captcha-site-key=$ESITE_KEY --from-literal=esignet-captcha-secret-key=$ESECRET_KEY --dry-run=client -o yaml | kubectl apply -f - + echo "Captcha secrets for esignet configured successfully" + + ../copy_cm_func.sh secret esignet-captcha esignet $NS + + # Update or add environment variable + ENV_VAR_EXISTS=$(kubectl -n $NS get deployment captcha -o jsonpath="{.spec.template.spec.containers[0].env[?(@.name=='MOSIP_CAPTCHA_SECRET_ESIGNET')].name}") + if [[ -z "$ENV_VAR_EXISTS" ]]; then + echo "Environment variable 'MOSIP_CAPTCHA_SECRET_ESIGNET' does not exist. Adding it..." + kubectl patch deployment -n $NS captcha --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/env/-", "value": {"name": "MOSIP_CAPTCHA_SECRET_ESIGNET", "valueFrom": {"secretKeyRef": {"name": "esignet-captcha", "key": "esignet-captcha-secret-key"}}}}]' + else + echo "Environment variable 'MOSIP_CAPTCHA_SECRET_ESIGNET' exists. Updating it..." + kubectl patch deployment -n $NS captcha --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/env[?(@.name==\"MOSIP_CAPTCHA_SECRET_ESIGNET\")]", "value": {"name": "MOSIP_CAPTCHA_SECRET_ESIGNET", "valueFrom": {"secretKeyRef": {"name": "esignet-captcha", "key": "esignet-captcha-secret-key"}}}}]' + fi + + break + elif [[ "$ans" == "N" || "$ans" == "n" ]]; then + echo "Skipping Captcha secrets configuration." + break + else + echo "Please provide a correct option (Y or N)" + fi + done } # set commands for error handling. diff --git a/deploy/config-server/install.sh b/deploy/config-server/install.sh index eee57f6d6..83ec9c90a 100755 --- a/deploy/config-server/install.sh +++ b/deploy/config-server/install.sh @@ -6,17 +6,18 @@ 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=esignet CHART_VERSION=0.0.1-develop echo Create $NS namespace - kubectl create ns $NS - # set commands for error handling. - set -e - set -o errexit ## set -e : exit the script if any statement returns a non-true return value - set -o nounset ## set -u : exit the script if you try to use an uninitialised variable - set -o errtrace # trace ERR through 'time command' and other functions - set -o pipefail # trace ERR through pipes + kubectl create ns $NS || true echo Istio label kubectl label ns $NS istio-injection=enabled --overwrite diff --git a/deploy/esignet-apitestrig/README.md b/deploy/esignet-apitestrig/README.md index a5e277981..e2647c3d9 100644 --- a/deploy/esignet-apitestrig/README.md +++ b/deploy/esignet-apitestrig/README.md @@ -2,13 +2,25 @@ ## Introduction ApiTestRig will test the working of APIs of the MOSIP modules. - ## Install +* Create a directory for apitestrig on the NFS server at `/srv/nfs/mosip//apitestrig/`: +``` +mkdir -p /srv/nfs/mosip//apitestrig/ +``` +* Ensure the directory has 777 permissions: +``` +chmod 777 /srv/nfs/mosip//apitestrig +``` +* Add the following entry to the /etc/exports file: +``` +/srv/nfs/mosip//apitestrig *(ro,sync,no_root_squash,no_all_squash,insecure,subtree_check) +``` * Review `values.yaml` and, Make sure to enable required modules for apitestrig operation. -* Install -```sh +* run `./install.sh`. +``` ./install.sh ``` + * During the execution of the `install.sh` script, a prompt appears requesting information regarding the presence of a public domain and a valid SSL certificate on the server. * If the server lacks a public domain and a valid SSL certificate, it is advisable to select the `n` option. Opting it will enable the `init-container` with an `emptyDir` volume and include it in the deployment process. * The init-container will proceed to download the server's self-signed SSL certificate and mount it to the specified location within the container's Java keystore (i.e., `cacerts`) file. @@ -41,4 +53,4 @@ ApiTestRig will test the working of APIs of the MOSIP modules. example: ``` kubectl --kubeconfig=/home/xxx/Downloads/qa4.config -n apitestrig create job --from=cronjob/cronjob-apitestrig-masterdata cronjob-apitestrig-masterdata - ``` \ No newline at end of file + ``` diff --git a/deploy/esignet-apitestrig/delete.sh b/deploy/esignet-apitestrig/delete.sh index 38c79022c..bdd4ca061 100755 --- a/deploy/esignet-apitestrig/delete.sh +++ b/deploy/esignet-apitestrig/delete.sh @@ -27,4 +27,4 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true set -o nounset ## set -u : exit the script if you try to use an uninitialised variable set -o errtrace # trace ERR through 'time command' and other functions set -o pipefail # trace ERR through pipes -deleting_apitestrig # calling function \ No newline at end of file +deleting_apitestrig # calling function diff --git a/deploy/esignet-apitestrig/install.sh b/deploy/esignet-apitestrig/install.sh index 7fed643b6..564b9e599 100755 --- a/deploy/esignet-apitestrig/install.sh +++ b/deploy/esignet-apitestrig/install.sh @@ -7,7 +7,7 @@ if [ $# -ge 1 ] ; then fi NS=esignet -CHART_VERSION=0.0.1-develop +CHART_VERSION=1.5.0-develop COPY_UTIL=../copy_cm_func.sh echo Create $NS namespace @@ -19,14 +19,12 @@ function installing_apitestrig() { helm repo update echo Copy Configmaps - $COPY_UTIL configmap global default $NS $COPY_UTIL configmap keycloak-host keycloak $NS $COPY_UTIL configmap artifactory-share artifactory $NS $COPY_UTIL configmap config-server-share config-server $NS echo echo Copy Secrtes $COPY_UTIL secret keycloak-client-secrets keycloak $NS - $COPY_UTIL secret s3 s3 $NS $COPY_UTIL secret postgres-postgresql postgres $NS echo "Delete s3, db, & apitestrig configmap if exists" @@ -34,9 +32,9 @@ function installing_apitestrig() { kubectl -n $NS delete --ignore-not-found=true configmap db kubectl -n $NS delete --ignore-not-found=true configmap apitestrig - DB_HOST=$( kubectl -n default get cm global -o json |jq -r '.data."mosip-api-internal-host"' ) - API_INTERNAL_HOST=$( kubectl -n default get cm global -o json |jq -r '.data."mosip-api-internal-host"' ) - ENV_USER=$( kubectl -n default get cm global -o json |jq -r '.data."mosip-api-internal-host"' | awk -F '.' '/api-internal/{print $1"."$2}') + DB_HOST=$( kubectl -n esignet get cm esignet-global -o json |jq -r '.data."mosip-api-internal-host"' ) + API_INTERNAL_HOST=$( kubectl -n esignet get cm esignet-global -o json |jq -r '.data."mosip-api-internal-host"' ) + ENV_USER=$( kubectl -n esignet get cm esignet-global -o json |jq -r '.data."mosip-api-internal-host"' | awk -F '.' '/api-internal/{print $1"."$2}') read -p "Please enter the time(hr) to run the cronjob every day (time: 0-23) : " time if [ -z "$time" ]; then @@ -96,29 +94,80 @@ function installing_apitestrig() { else echo "eSignet service is not deployed. hence will be skipping esignet related test-cases..." fi - - echo Installing esignet apitestrig - helm -n $NS install esignet-apitestrig mosip/apitestrig \ - --set crontime="0 $time * * *" \ - -f values.yaml \ - --version $CHART_VERSION \ - --set apitestrig.configmaps.s3.s3-host='http://minio.minio:9000' \ - --set apitestrig.configmaps.s3.s3-user-key='admin' \ - --set apitestrig.configmaps.s3.s3-region='' \ - --set apitestrig.configmaps.db.db-server="$DB_HOST" \ - --set apitestrig.configmaps.db.db-su-user="postgres" \ - --set apitestrig.configmaps.db.db-port="5432" \ - --set apitestrig.configmaps.apitestrig.ENV_USER="$ENV_USER" \ - --set apitestrig.configmaps.apitestrig.ENV_ENDPOINT="https://$API_INTERNAL_HOST" \ - --set apitestrig.configmaps.apitestrig.ENV_TESTLEVEL="smokeAndRegression" \ - --set apitestrig.configmaps.apitestrig.reportExpirationInDays="$reportExpirationInDays" \ - --set apitestrig.configmaps.apitestrig.slack-webhook-url="$slackWebhookUrl" \ - --set apitestrig.configmaps.apitestrig.eSignetDeployed="$eSignetDeployed" \ - --set apitestrig.configmaps.apitestrig.NS="$NS" \ - $ENABLE_INSECURE - - echo Installed esignet apitestrig. - return 0 + read -p "Is values.yaml for apitestrig chart set correctly as part of pre-requisites? (Y/n) : " yn; + if [[ $yn = "Y" ]] || [[ $yn = "y" ]] ; then + NFS_OPTION='' + S3_OPTION='' + config_complete=false # flag to check if S3 or NFS is configured + while [ "$config_complete" = false ]; do + read -p "Do you have S3 details for storing apitestrig reports? (Y/n) : " ans + if [[ "$ans" == "y" || "$ans" == "Y" ]]; then + read -p "Please provide S3 host: " s3_host + if [[ -z $s3_host ]]; then + echo "S3 host not provided; EXITING;" + exit 1; + fi + read -p "Please provide S3 region: " s3_region + if [[ $s3_region == *[' !@#$%^&*()+']* ]]; then + echo "S3 region should not contain spaces or special characters; EXITING;" + exit 1; + fi + + read -p "Please provide S3 access key: " s3_user_key + if [[ -z $s3_user_key ]]; then + echo "S3 access key not provided; EXITING;" + exit 1; + fi + S3_OPTION="--set apitestrig.configmaps.s3.s3-host=$s3_host --set apitestrig.configmaps.s3.s3-user-key=$s3_user_key --set apitestrig.configmaps.s3.s3-region=$s3_region" + push_reports_to_s3="yes" + config_complete=true + elif [[ "$ans" == "n" || "$ans" == "N" ]]; then + push_reports_to_s3="no" + read -p "Since S3 details are not available, do you want to use NFS directory mount for storing reports? (y/n) : " answer + if [[ $answer == "Y" ]] || [[ $answer == "y" ]]; then + read -p "Please provide NFS Server IP: " nfs_server + if [[ -z $nfs_server ]]; then + echo "NFS server not provided; EXITING." + exit 1; + fi + read -p "Please provide NFS directory to store reports from NFS server (e.g. /srv/nfs/mosip//apitestrig/), make sure permission is 777 for the folder: " nfs_path + if [[ -z $nfs_path ]]; then + echo "NFS Path not provided; EXITING." + exit 1; + fi + NFS_OPTION="--set apitestrig.volumes.reports.nfs.server=$nfs_server --set apitestrig.volumes.reports.nfs.path=$nfs_path" + config_complete=true + else + echo "Please rerun the script with either S3 or NFS server details." + exit 1; + fi + else + echo "Invalid input. Please respond with Y (yes) or N (no)." + fi + done + echo Installing esignet apitestrig + helm -n $NS install esignet-apitestrig mosip/apitestrig \ + --set crontime="0 $time * * *" \ + -f values.yaml \ + --version $CHART_VERSION \ + $NFS_OPTION \ + $S3_OPTION \ + --set apitestrig.variables.push_reports_to_s3=$push_reports_to_s3 \ + --set apitestrig.configmaps.db.db-server="$DB_HOST" \ + --set apitestrig.configmaps.db.db-su-user="postgres" \ + --set apitestrig.configmaps.db.db-port="5432" \ + --set apitestrig.configmaps.apitestrig.ENV_USER="$ENV_USER" \ + --set apitestrig.configmaps.apitestrig.ENV_ENDPOINT="https://$API_INTERNAL_HOST" \ + --set apitestrig.configmaps.apitestrig.ENV_TESTLEVEL="smokeAndRegression" \ + --set apitestrig.configmaps.apitestrig.reportExpirationInDays="$reportExpirationInDays" \ + --set apitestrig.configmaps.apitestrig.slack-webhook-url="$slackWebhookUrl" \ + --set apitestrig.configmaps.apitestrig.eSignetDeployed="$eSignetDeployed" \ + --set apitestrig.configmaps.apitestrig.NS="$NS" \ + $ENABLE_INSECURE + + echo Installed esignet apitestrig. + return 0 + fi } # set commands for error handling. @@ -127,4 +176,4 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true set -o nounset ## set -u : exit the script if you try to use an uninitialised variable set -o errtrace # trace ERR through 'time command' and other functions set -o pipefail # trace ERR through pipes -installing_apitestrig # calling function \ No newline at end of file +installing_apitestrig # calling function diff --git a/deploy/esignet-apitestrig/values.yaml b/deploy/esignet-apitestrig/values.yaml index 0d4623d75..d3c38f391 100644 --- a/deploy/esignet-apitestrig/values.yaml +++ b/deploy/esignet-apitestrig/values.yaml @@ -2,6 +2,14 @@ modules: esignet: enabled: true image: - repository: mosipqa/apitest-esignet - tag: develop + repository: mosipdev/apitest-esignet + tag: release-1.5.x pullPolicy: Always + +resources: + limits: + cpu: 300m + memory: 500Mi + requests: + cpu: 300m + memory: 500Mi diff --git a/deploy/esignet-global-cm.yaml b/deploy/esignet-global-cm.yaml.sample similarity index 100% rename from deploy/esignet-global-cm.yaml rename to deploy/esignet-global-cm.yaml.sample diff --git a/deploy/esignet/install.sh b/deploy/esignet/install.sh index c17d41f92..080f10721 100755 --- a/deploy/esignet/install.sh +++ b/deploy/esignet/install.sh @@ -6,16 +6,31 @@ if [ $# -ge 1 ] ; then export KUBECONFIG=$1 fi -NS=esignet -CHART_VERSION=0.0.1-test - -ESIGNET_HOST=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-esignet-host}) - echo Create $NS namespace kubectl create ns $NS function installing_esignet() { + while true; do + read -p "Do you want to continue installing esignet services? (y/n): " 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 + + + NS=esignet + CHART_VERSION=1.5.0-develop + + ESIGNET_HOST=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-esignet-host}) + + echo Create $NS namespace + kubectl create ns $NS || true + echo Istio label kubectl label ns $NS istio-injection=enabled --overwrite helm repo add mosip https://mosip.github.io/mosip-helm @@ -23,6 +38,7 @@ function installing_esignet() { COPY_UTIL=../copy_cm_func.sh $COPY_UTIL configmap esignet-softhsm-share softhsm $NS + $COPY_UTIL configmap postgres-config postgres $NS $COPY_UTIL configmap redis-config redis $NS $COPY_UTIL secret esignet-softhsm softhsm $NS $COPY_UTIL secret redis redis $NS @@ -63,10 +79,10 @@ function installing_esignet() { read -p "Enter the plugin number: " plugin_no while true; do if [[ "$plugin_no" == "1" ]]; then - plugin_option="--set plugin_name_env=esignet-mock-plugin.jar" + plugin_option="--set pluginNameEnv=esignet-mock-plugin.jar" break elif [[ "$plugin_no" == "2" ]]; then - plugin_option="--set plugin_name_env=mosip-identity-plugin.jar" + plugin_option="--set pluginNameEnv=mosip-identity-plugin.jar" break else echo "please provide the correct plugin number (1 or 2)." diff --git a/deploy/initialise-prereq.sh b/deploy/initialise-prereq.sh index a8259252f..df5ceabde 100755 --- a/deploy/initialise-prereq.sh +++ b/deploy/initialise-prereq.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Initialises prerequisite services for Esignet +# Initializes prerequisite services for Esignet ## Usage: ./install.sh [kubeconfig] if [ $# -ge 1 ] ; then @@ -23,38 +23,28 @@ function prompt_for_initialisation() { cd $ROOT_DIR/"$module_name" ./"$module_name"-init.sh else - echo "Skipping initialising of $module_name." + echo "Skipping initialization of $module_name." fi } -function initialising_Prerequisites() { - - declare -a modules=("postgres" "keycloak" ) +function initialising_prerequisites() { + declare -a modules=("postgres" "keycloak") declare -A prompts=( ["postgres"]="Do you want to continue executing postgres init?" ["keycloak"]="Do you want to continue executing keycloak init?" ) - echo "Initialising prerequisite services" + echo "Initializing prerequisite services" for module in "${modules[@]}" do prompt_for_initialisation "$module" "${prompts[$module]}" done - ESIGNET_HOST=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-esignet-host}) - echo Please enter the recaptcha admin site key for domain $ESIGNET_HOST - read ESITE_KEY - echo Please enter the recaptcha admin secret key for domain $ESIGNET_HOST - read ESECRET_KEY - - echo Setting up captcha secrets - kubectl -n $NS create secret generic esignet-captcha --from-literal=esignet-captcha-site-key=$ESITE_KEY --from-literal=esignet-captcha-secret-key=$ESECRET_KEY --dry-run=client -o yaml | kubectl apply -f - - - echo Setting up dummy values for esignet misp license key + echo "Setting up dummy values for Esignet MISP license key" kubectl -n $NS create secret generic esignet-misp-onboarder-key --from-literal=mosip-esignet-misp-key='' --dry-run=client -o yaml | kubectl apply -f - - echo "All prerequisite services initialised successfully." + echo "All prerequisite services initialized successfully." return 0 } @@ -64,4 +54,4 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true set -o nounset ## set -u : exit the script if you try to use an uninitialized variable set -o errtrace # trace ERR through 'time command' and other functions set -o pipefail # trace ERR through pipes -initialising_Prerequisites # calling function +initialising_prerequisites # calling function diff --git a/deploy/install-esignet.sh b/deploy/install-esignet.sh index 4888a39b2..4c3c606ec 100755 --- a/deploy/install-esignet.sh +++ b/deploy/install-esignet.sh @@ -13,7 +13,7 @@ function installing_eSignet() { helm repo add mosip https://mosip.github.io/mosip-helm # List of modules to install - declare -a modules=("esignet" "oidc-ui" "captcha") + declare -a modules=("esignet" "oidc-ui") echo "Installing eSignet services" diff --git a/deploy/install-prereq.sh b/deploy/install-prereq.sh index ee18d008c..1fcc442cb 100755 --- a/deploy/install-prereq.sh +++ b/deploy/install-prereq.sh @@ -8,8 +8,7 @@ if [ $# -ge 1 ]; then fi ROOT_DIR=$(pwd) -SOFTHSM_NS=softhsm -SOFTHSM_CHART_VERSION=12.0.1 +kubectl create ns esignet || true function prompt_for_deployment() { local module_name=$1 @@ -30,36 +29,25 @@ function prompt_for_deployment() { function installing_prerequisites() { - helm repo add mosip https://mosip.github.io/mosip-helm - helm repo update - - # Create esignet, softhsm namespace if not present - kubectl create ns esignet || true - kubectl create ns "$SOFTHSM_NS" || true - # Apply esignet-global config-map - kubectl apply -f esignet-global-cm.yaml - - echo "Istio label" - kubectl label ns "$SOFTHSM_NS" istio-injection=enabled --overwrite - - # Deploy Softhsm for Esignet. - echo "Installing Softhsm for esignet" - helm -n "$SOFTHSM_NS" install esignet-softhsm mosip/softhsm -f softhsm-values.yaml --version "$SOFTHSM_CHART_VERSION" --wait - echo "Installed Softhsm for esignet" + echo "Creating esignet-global configmap in esignet namespace" + kubectl -n esignet apply -f esignet-global-cm.yaml - declare -a modules=("istio-gateway" "postgres" "keycloak" "kafka" "redis") + declare -a modules=("istio-gateway" "postgres" "keycloak" "kafka" "redis" "softhsm" "captcha") declare -A prompts=( - ["keycloak"]="Do you want to deploy keycloak in the keycloak namespace?" - ["kafka"]="Do you want to deploy Kafka in the kafka namespace?" + ["softhsm"]="Do you want to install softhsm for esignet service in softhsm namespace? Opt "n" in case it already exists in Softhsm namespace: " + ["keycloak"]="Do you want to deploy keycloak in the keycloak namespace? Opt "n" in case it already exists in keycloak namespace : " + ["kafka"]="Do you want to deploy Kafka in the kafka namespace? Opt "n" in case it already exists in kafka namespace : " ) echo "Installing prerequisite services" for module in "${modules[@]}"; do - if [ "$module" == "istio-gateway" ] || [ "$module" == "redis" ] || [ "$module" == "postgres" ]; then + if [ "$module" == "istio-gateway" ] || [ "$module" == "postgres" ] || [ "$module" == "redis" ] || [ "$module" == "captcha" ]; then cd "$ROOT_DIR/$module" ./install.sh + elif [[ -n "${prompts[$module]}" ]]; then + prompt_for_deployment "$module" "${prompts[$module]}" fi done echo "All prerequisite services deployed successfully." diff --git a/deploy/keycloak/install.sh b/deploy/keycloak/install.sh index 43623a6f7..6f85c7f4d 100755 --- a/deploy/keycloak/install.sh +++ b/deploy/keycloak/install.sh @@ -17,6 +17,7 @@ function installing_keycloak() { ## TODO: enable istio injection after testing well. kubectl label ns $NS istio-injection=disabled --overwrite helm repo add bitnami https://charts.bitnami.com/bitnami + helm repo add mosip https://mosip.github.io/mosip-helm helm repo update echo Installing diff --git a/deploy/keycloak/keycloak-init-values.yaml b/deploy/keycloak/keycloak-init-values.yaml index 3dd04e513..9a8edccc0 100644 --- a/deploy/keycloak/keycloak-init-values.yaml +++ b/deploy/keycloak/keycloak-init-values.yaml @@ -14,8 +14,6 @@ keycloak: "actionTokenGeneratedByUserLifespan": 300 "passwordPolicy": "length(8)" "resetPasswordAllowed": 'True' - "registrationAllowed": 'True' - "verifyEmail": 'True' "bruteForceProtected": 'True' "permanentLockout": 'False' "maxFailureWaitSeconds": 900 diff --git a/deploy/oidc-ui/install.sh b/deploy/oidc-ui/install.sh index 4927f4a58..b458ba682 100755 --- a/deploy/oidc-ui/install.sh +++ b/deploy/oidc-ui/install.sh @@ -6,13 +6,25 @@ if [ $# -ge 1 ] ; then export KUBECONFIG=$1 fi -NS=esignet -CHART_VERSION=0.0.1-test +function installing_oidc-ui() { -echo Create $NS namespace -kubectl create ns $NS + while true; do + read -p "Do you want to continue installing OIDC ui? (y/n) :" 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 + + NS=esignet + CHART_VERSION=1.5.0-develop + + echo Create $NS namespace + kubectl create ns $NS || true -function installing_oidc-ui() { echo Istio label kubectl label ns $NS istio-injection=enabled --overwrite diff --git a/deploy/postgres/.gitignore b/deploy/postgres/.gitignore index 5e1d061c3..6d5b3a287 100644 --- a/deploy/postgres/.gitignore +++ b/deploy/postgres/.gitignore @@ -1,3 +1,4 @@ db-common-secrets.yaml -esignet-postgres-postgresql.yaml +postgres-postgresql.yaml postgres-host.yaml +.*.swp diff --git a/deploy/postgres/chart/istio-addons/templates/gateway.yaml b/deploy/postgres/chart/istio-addons/templates/gateway.yaml index c1e96ab0e..97619efa1 100644 --- a/deploy/postgres/chart/istio-addons/templates/gateway.yaml +++ b/deploy/postgres/chart/istio-addons/templates/gateway.yaml @@ -11,4 +11,4 @@ spec: name: postgres protocol: TCP hosts: - - {{ .Values.postgresHost }} + - {{ .Values.postgresHost }} \ No newline at end of file diff --git a/deploy/postgres/chart/istio-addons/templates/vs.yaml b/deploy/postgres/chart/istio-addons/templates/vs.yaml index 30843dd48..5cae69b5a 100644 --- a/deploy/postgres/chart/istio-addons/templates/vs.yaml +++ b/deploy/postgres/chart/istio-addons/templates/vs.yaml @@ -1,7 +1,7 @@ apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: - name: esignet-postgres + name: postgres spec: hosts: - "*" @@ -12,8 +12,7 @@ spec: - port: 5432 route: - destination: - host: esignet-postgres-postgresql + host: postgres-postgresql port: number: 5432 - diff --git a/deploy/postgres/chart/istio-addons/values.yaml b/deploy/postgres/chart/istio-addons/values.yaml index c05d63fab..7770edb91 100644 --- a/deploy/postgres/chart/istio-addons/values.yaml +++ b/deploy/postgres/chart/istio-addons/values.yaml @@ -1 +1 @@ -postgresHost: esignet-postgres.sandbox.xyz.net +postgresHost: postgres.sandbox.xyz.net diff --git a/deploy/postgres/delete.sh b/deploy/postgres/delete.sh index 41020f325..61186c043 100755 --- a/deploy/postgres/delete.sh +++ b/deploy/postgres/delete.sh @@ -7,16 +7,16 @@ if [ $# -ge 1 ] ; then fi function deleting_postgres() { - NS=esignet + NS=postgres while true; do read -p "CAUTION: PVC, PV will get deleted. If your PV is not in 'Retain' mode all Postgres data will be lost. Are you sure? (Y/n): " yn if [ "$yn" = "Y" ] || [ "$yn" = "y" ]; then echo "Deleting Postgres resources..." - helm -n $NS delete esignet-postgres || echo "Failed to delete esignet-postgres helm release" + helm -n $NS delete postgres || echo "Failed to delete postgres helm release" helm -n $NS delete istio-addons || echo "Failed to delete istio-addons helm release" - kubectl -n $NS delete pvc data-esignet-postgres-postgresql-0 || echo "Failed to delete PVC" - helm -n $NS delete esignet-postgres-init || echo "Failed to delete esignet-postgres-init helm release" - kubectl -n $NS delete secret esignet-postgres-postgresql || echo "Failed to delete esignet-postgres-init secret" + kubectl -n $NS delete pvc data-postgres-postgresql-0 || echo "Failed to delete PVC" + helm -n $NS delete postgres-init || echo "Failed to delete postgres-init helm release" + kubectl -n $NS delete secret postgres-postgresql || echo "Failed to delete postgres-init secret" kubectl -n $NS delete secret db-common-secrets || echo "Failed to delete db-common-secrets secret" break elif [ "$yn" = "N" ] || [ "$yn" = "n" ]; then diff --git a/deploy/postgres/generate-secret-cm.py b/deploy/postgres/generate-secret-cm.py index 46403b2a6..acf85875f 100644 --- a/deploy/postgres/generate-secret-cm.py +++ b/deploy/postgres/generate-secret-cm.py @@ -73,7 +73,7 @@ def create_or_update_configmap(configmap_name, namespace, postgres_host, postgre os.system(f"kubectl create -f {yaml_file} --save-config") # Main script logic -namespace = "esignet" +namespace = "postgres" check_namespace(namespace) # Handle db-dbuser-password secret @@ -91,7 +91,7 @@ def create_or_update_configmap(configmap_name, namespace, postgres_host, postgre create_or_update_secret(db_secret_name, namespace, "db-dbuser-password", password) # Handle postgres-password secret -postgres_secret_name = "esignet-postgres-postgresql" +postgres_secret_name = "postgres-postgresql" if secret_exists(postgres_secret_name, namespace): overwrite = input(f"Secret '{postgres_secret_name}' already exists in namespace '{namespace}'. Overwrite? (y/n): ") if overwrite.lower() == 'y': @@ -105,7 +105,7 @@ def create_or_update_configmap(configmap_name, namespace, postgres_host, postgre create_or_update_secret(postgres_secret_name, namespace, "postgres-password", postgres_password) # Handle ConfigMap creation for PostgreSQL -configmap_name = "esignet-postgres-config" +configmap_name = "postgres-config" if configmap_exists(configmap_name, namespace): overwrite = input(f"ConfigMap '{configmap_name}' already exists in namespace '{namespace}'. Overwrite? (y/n): ") if overwrite.lower() == 'y': diff --git a/deploy/postgres/init_values.yaml b/deploy/postgres/init_values.yaml index f82530230..ddcdd051f 100644 --- a/deploy/postgres/init_values.yaml +++ b/deploy/postgres/init_values.yaml @@ -4,13 +4,13 @@ dbUserPasswords: databases: mosip_esignet: enabled: true - host: "esignet-postgres-postgresql.esignet" + host: "postgres-postgresql" port: 5432 su: user: postgres secret: - name: esignet-postgres-postgresql + name: postgres-postgresql key: postgres-password dml: 1 repoUrl: https://github.com/mosip/esignet.git - branch: develop + branch: develop \ No newline at end of file diff --git a/deploy/postgres/install.sh b/deploy/postgres/install.sh index 22f017242..e94d6da16 100755 --- a/deploy/postgres/install.sh +++ b/deploy/postgres/install.sh @@ -6,7 +6,7 @@ if [ $# -ge 1 ] ; then export KUBECONFIG=$1 fi -NS=esignet +NS=postgres # Function to check and delete secret if it exists function check_and_delete_secret() { @@ -16,7 +16,8 @@ function check_and_delete_secret() { if kubectl -n $secret_namespace get secret $secret_name > /dev/null 2>&1; then echo "Secret $secret_name exists in namespace $secret_namespace." while true; do - read -p "Do you want to delete secret $secret_name before installation? (Y/n): " yn + POSTGRES_HOST=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-postgres-host}) + helm -n $NS install istio-addons chart/istio-addons --set postg read -p "Do you want to delete secret $secret_name before installation? (Y/n): " yn if [ "$yn" = "Y" ] || [ "$yn" = "y" ]; then echo "Deleting secret $secret_name..." kubectl -n $secret_namespace delete secret $secret_name || { echo "Failed to delete secret $secret_name"; exit 1; } @@ -35,7 +36,7 @@ function check_and_delete_secret() { function installing_postgres() { # Check and handle the existing secret - check_and_delete_secret "esignet-postgres-postgresql" $NS + check_and_delete_secret "postgres-postgresql" $NS helm repo add bitnami https://charts.bitnami.com/bitnami helm repo update @@ -44,26 +45,49 @@ function installing_postgres() { kubectl label ns $NS istio-injection=enabled --overwrite echo Installing Postgres - helm -n $NS install esignet-postgres bitnami/postgresql --version 13.1.5 -f values.yaml --wait - echo Installed Postgres + helm -n $NS install postgres bitnami/postgresql --version 13.1.5 -f values.yaml --wait + # Run the Python script to generate secrets and configmap + if [ -f generate-secret-cm.py ]; then + echo "Running generate_secret.py to create Postgres secrets and configmap..." + python3 generate-secret-cm.py || { echo "Failed to run generate_secret.py"; exit 1; } + echo "Secrets and configmap generated successfully." + else + echo "Error: generate-secret-cm.py not found. Ensure the script is in the current directory." + exit 1 + fi echo Installing gateways and virtual services POSTGRES_HOST=$(kubectl -n esignet get cm esignet-global -o jsonpath={.data.mosip-postgres-host}) helm -n $NS install istio-addons chart/istio-addons --set postgresHost=$POSTGRES_HOST --wait - kubectl apply -f postgres-config.yaml return 0 } # Prompt the user if they want to install PostgreSQL while true; do - read -p "Do you want to install Postgres? Opt for 'n' if you have Postgres already installed. (y/n): " answer + read -p "Do you want to install default Postgres ? (y/n): " answer if [ "$answer" = "Y" ] || [ "$answer" = "y" ]; then echo "Continuing with Postgres server deployment..." - break + break # Proceed with the installation elif [ "$answer" = "N" ] || [ "$answer" = "n" ]; then - echo "Skipping Postgres installation. Running generate_secret.py to create Postgres secrets..." - python3 generate-secret-cm.py # Ensure that Python and the script are available in the environment - echo "Secrets generated. Exiting script." - exit 0 # Exit the script after generating secrets + # Prompt the user for further options + while true; do + echo "You opted not to install Postgres. What would you like to do next?" + echo "1. Skip Postgres server installation and configuration." + echo "2. Configure external Postgres details by generating secrets and configmap ." + + read -p "Enter your choice (1/2): " option + + if [ "$option" = "1" ]; then + echo "Skipping Postgres server installation and configuration in namespace." + exit 0 # Exit the script as the user chose to skip Postgres installation + elif [ "$option" = "2" ]; then + echo "Running generate_secret.py to create Postgres secrets and configmap..." + python3 generate-secret-cm.py # Ensure Python and the script are available in the environment + echo "Secrets generated successfully." + exit 0 # Exit the script after generating secrets and configmap + else + echo "Not a correct option. Please try again or press Ctrl + C to exit." + fi + done else echo "Please provide a correct option (Y or N)" fi diff --git a/deploy/postgres/nginx/transportserver.yaml b/deploy/postgres/nginx/transportserver.yaml index c031467aa..9b51871ca 100644 --- a/deploy/postgres/nginx/transportserver.yaml +++ b/deploy/postgres/nginx/transportserver.yaml @@ -9,7 +9,7 @@ spec: protocol: TCP upstreams: - name: postgres - service: esignet-postgres-postgresql + service: postgres-postgresql port: 5432 action: - pass: postgres + pass: postgres \ No newline at end of file diff --git a/deploy/postgres/postgres-config.yaml b/deploy/postgres/postgres-config.yaml index c2eb6276f..6fb3b41ee 100644 --- a/deploy/postgres/postgres-config.yaml +++ b/deploy/postgres/postgres-config.yaml @@ -1,12 +1,12 @@ apiVersion: v1 kind: ConfigMap metadata: - name: esignet-postgres-config - namespace: esignet + name: postgres-config + namespace: postgres labels: app: postgres data: - database-host: "esignet-postgres-postgresql" + database-host: "postgres-postgresql" database-port: "5432" database-username: "esignetuser" - database-name: "mosip_esignet" + database-name: "mosip_esignet" \ No newline at end of file diff --git a/deploy/postgres/postgres-init.sh b/deploy/postgres/postgres-init.sh index 7b0068fdf..33b236907 100755 --- a/deploy/postgres/postgres-init.sh +++ b/deploy/postgres/postgres-init.sh @@ -12,11 +12,11 @@ function initialize_db() { helm repo update while true; do - read -p "Please confirm with "Y" once values-init.yaml is updated correctly with tag, postgres host, and password details else "N" to exit installation: " ans + read -p "Please confirm with "Y" once init-values.yaml is updated correctly with tag, postgres host, and password details else "N" to exit installation: " ans if [ "$ans" = "Y" ] || [ "$ans" = "y" ]; then break elif [ "$ans" = "N" ] || [ "$ans" = "n" ]; then - exit + exit 1 else echo "Please provide a correct option (Y or N)" fi @@ -28,15 +28,16 @@ function initialize_db() { then echo Removing existing mosip_esignet installation and secret helm -n $NS delete esignet-postgres-init || true - kubectl delete secret db-common-secrets -n $NS || 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 esignet-postgres-init mosip/postgres-init --version $CHART_VERSION -f init_values.yaml --wait --wait-for-jobs + helm -n $NS install postgres-init mosip/postgres-init --set image.repository=mosipdev/postgres-init --set image.tag=develop --version $CHART_VERSION -f init_values.yaml --wait --wait-for-jobs break elif [ "$yn" = "N" ] || [ "$yn" = "n" ]; then - echo "Skipping postgres initialisation as per your input" + echo "Skipping esignet postgres DB initialisation as per your input" break else - echo "Incorrect Input" + echo "Incorrect Input. Please choose again" break fi done diff --git a/deploy/postgres/postgres-postgresql.yaml b/deploy/postgres/postgres-postgresql.yaml new file mode 100644 index 000000000..028dd0ba5 --- /dev/null +++ b/deploy/postgres/postgres-postgresql.yaml @@ -0,0 +1,9 @@ + +apiVersion: v1 +kind: Secret +metadata: + name: postgres-postgresql + namespace: postgres +type: Opaque +data: + postgres-password: cG9zdGdyZXM= diff --git a/deploy/redis/install.sh b/deploy/redis/install.sh index 7a33fd5ae..9bf297b73 100755 --- a/deploy/redis/install.sh +++ b/deploy/redis/install.sh @@ -6,13 +6,37 @@ if [ $# -ge 1 ] ; then export KUBECONFIG=$1 fi -NS=redis -CHART_VERSION=17.3.14 +function installing_redis() { -echo Create $NS namespace -kubectl create ns $NS + while true; do + read -p "Do you want to install Redis in redis namespace? (y/n) :" ans + if [ "$ans" = "n" ] || [ "$ans" = "N" ]; then + read -p "Please confirm if redis-config is already present in redis namespace. (y/n):" response + if [ "$response" = "Y" ] || [ "$response" = "y" ]; then + echo "Skipping Redis installation as well as redis-config creation." + exit 1 + elif [ "$response" = "N" ] || [ "$response" = "n" ]; then + echo "Skipping Redis installation and continuing with redis-config creation." + kubectl apply -f redis-config.yaml + echo "redis-config configmap creation completed." + exit 1 + else + echo "Incorrect response. Please re-execute the redis installation script." + exit 1 + fi + elif [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then + break + else + echo "Please provide a correct option (Y or N)" + fi + done + + NS=redis + CHART_VERSION=17.3.14 + + echo Create $NS namespace + kubectl create ns $NS || true -function installing_redis() { echo Istio label kubectl label ns $NS istio-injection=enabled --overwrite diff --git a/deploy/softhsm/install.sh b/deploy/softhsm/install.sh new file mode 100755 index 000000000..65b14d092 --- /dev/null +++ b/deploy/softhsm/install.sh @@ -0,0 +1,34 @@ +#!/bin/bash +# Installs Softhsm service for Esignet +## Usage: ./install.sh [kubeconfig] + +if [ $# -ge 1 ] ; then + export KUBECONFIG=$1 +fi + +SOFTHSM_NS=softhsm +SOFTHSM_CHART_VERSION=12.0.1 + +function installing_softhsm() { + echo Create $SOFTHSM_NS namespaces + kubectl create ns $SOFTHSM_NS || true + + echo Istio label + kubectl label ns $SOFTHSM_NS istio-injection=enabled --overwrite + helm repo update + + # Deploy Softhsm for Esignet. + echo "Installing Softhsm for esignet" + helm -n "$SOFTHSM_NS" install esignet-softhsm mosip/softhsm -f softhsm-values.yaml --version "$SOFTHSM_CHART_VERSION" --wait + echo "Installed Softhsm for esignet" + + 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_softhsm # calling function diff --git a/deploy/softhsm-values.yaml b/deploy/softhsm/softhsm-values.yaml similarity index 100% rename from deploy/softhsm-values.yaml rename to deploy/softhsm/softhsm-values.yaml diff --git a/docker-compose/README.md b/docker-compose/README.md index afba8b9a1..3c3c51e80 100644 --- a/docker-compose/README.md +++ b/docker-compose/README.md @@ -4,21 +4,34 @@ This is the docker compose setup to run esignet UI and esignet-service with mock ## I am a developer, how to setup dependent services to edit and test esignet-service? -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](../esignet-service/target/esignet-plugins/esignet-mock-plugin.jar) to esignet-service classpath in your IDE. -4. Start the [EsignetServiceApplication.java](../esignet-service/src/main/java/io/mosip/esignet/EsignetServiceApplication.java) from your IDE. -5. Import files under [postman-collection](../postman-collection) folder into your postman to test/validate OIDC flow. +1. Open terminal and go to "docker-compose" folder. +2. Run `docker compose --file dependent-docker-compose.yml up` to start all the dependent services. +3. Go to command line for the project root directory and run `mvn clean install -Dgpg.skip=true -DskipTests=true` +4. Add [esignet-mock-plugin.jar](../esignet-service/target/esignet-plugins/esignet-mock-plugin.jar) to esignet-service classpath in your IDE. +5. Start the [EsignetServiceApplication.java](../esignet-service/src/main/java/io/mosip/esignet/EsignetServiceApplication.java) from your IDE. +6. Import files under [postman-collection](../postman-collection) folder into your postman to test/validate OIDC flow. ## How to bring up the complete eSignet setup for a Demo? -1. Run [docker-compose.yml](docker-compose.yml) to start eSignet UI and backend service. -2. Access eSignet UI at http://localhost:3000 -3. Access eSignet backend services at http://localhost:8088/v1/esignet/swagger-ui.html -4. Onboard relying party in eSignet, import files under [postman-collection](../postman-collection) folder into your postman. And invoke requests under `OIDC Client Mgmt/Mock` folder in postman. Copy the client ID in the `Create OIDC client` response. -5. Add a `SignIn with eSignet` button in the relying party website and embed [eSignet authorize URL](http://localhost:3000/authorize?nonce=ere973eieljznge2311&state=eree2311&client_id=client_id&redirect_uri=redirect_uri&scope=openid&response_type=code&acr_values=mosip:idp:acr:generated-code&claims_locales=en&ui_locales=en-IN) in the button. Make sure to replace the query parameter values in the url before embedding in the button. -6. Add a user in the mock-identity-system. -7. Now the setup is completely ready to start the OIDC flow. [Refer eSignet user guides](https://docs.esignet.io/end-user-guide) for more information. +1. Open terminal and go to "docker-compose" folder. +2. Run `docker compose --file docker-compose.yml up` to start eSignet UI and backend service. +3. Access eSignet UI at http://localhost:3000 +4. Access eSignet backend services at http://localhost:8088/v1/esignet/swagger-ui.html +5. Onboard relying party in eSignet, import all files under [postman-collection](../postman-collection) folder into your postman. Choose `eSignet-with-mock` environment in the postman and invoke below requests under `OIDC Client Mgmt` -> `Mock` folder in postman. + + a. `Get CSRF token` + + b. `Create OIDC client` -> Make sure to update redirect Urls and logo URL as per your requirement in the request body. + +6. Copy the client ID in the `Create OIDC client` response. +7. Add a `SignIn with eSignet` button in the relying party website and embed [eSignet authorize URL](http://localhost:3000/authorize?nonce=ere973eieljznge2311&state=eree2311&client_id=client_id&redirect_uri=redirect_uri&scope=openid&response_type=code&acr_values=mosip:idp:acr:generated-code&claims_locales=en&ui_locales=en-IN) in the button. Update the below query parameter in the eSignet authorize URL before embedding in the button. + + a. `client_id` -> value should be replace with the value copied in the step 6 + + b. `redirect_uri` -> As updated in step 5 + +8. Add a user in the mock-identity-system. Invoke `Creat User` request under `User Mgmt` -> `Mock` folder in the postman. +9. Now the setup is completely ready to start the OIDC flow. [Refer eSignet user guides](https://docs.esignet.io/end-user-guide) for more information. `Note: To know more about the relying party onboard and query parameters used in the eSignet authorize URL `[refer eSignet docs](https://docs.esignet.io/integration/relying-party) diff --git a/docker-compose/dependent-docker-compose.yml b/docker-compose/dependent-docker-compose.yml index b3d462420..c5ea9a107 100644 --- a/docker-compose/dependent-docker-compose.yml +++ b/docker-compose/dependent-docker-compose.yml @@ -23,7 +23,7 @@ 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 diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml index 2d438f128..4c97a66c3 100644 --- a/docker-compose/docker-compose.yml +++ b/docker-compose/docker-compose.yml @@ -16,33 +16,32 @@ 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=900 depends_on: - database esignet: - image: 'mosipdev/esignet:release-1.5.x' + image: 'mosipdev/esignet:develop' user: root ports: - 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_AUTOCONFIGURE_EXCLUDE=org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration - SPRING_DATASOURCE_URL=jdbc:postgresql://database:5432/mosip_esignet?currentSchema=esignet - - SPRING_CACHE_TYPE=simple - MOSIP_ESIGNET_MOCK_DOMAIN_URL=http://mock-identity-system:8082 + - MOSIP_ESIGNET_INTEGRATION_KEY_BINDER=MockKeyBindingWrapperService depends_on: - database - mock-identity-system esignet-ui: - image: 'mosipdev/oidc-ui:release-1.5.x' + image: 'mosipdev/oidc-ui:develop' user: root ports: - 3000:3000 diff --git a/docs/design/eSignet-authorize-flow.md b/docs/design/eSignet-authorize-flow.md new file mode 100644 index 000000000..2e93ef58c --- /dev/null +++ b/docs/design/eSignet-authorize-flow.md @@ -0,0 +1,26 @@ +# Overview + +eSignet offers a seamless and straightforward solution for incorporating an existing trusted identity database into the +digital realm via plugins. + +## Design Objectives + +1. Should be an overlay solution on top of any of the existing identity API. Users can adopt the IDP solution and plugin the backend identity verification against their existing identity registry. +2. Should be able to independently operate without much dependency on existing services of MOSIP +3. Support all the mandatory features of OIDC (Open ID Connect) specification so the solution can be OIDC certified +4. When there are multiple options for a OIDC feature, support only the most secure option. +5. All integration libraries to ID system should be a runtime plugable to reuse the official container images which helps with easier upgrades. +6. Number of containers / runable applications to be kept to a minimum to avoid management overhead. +7. Standard interfaces to be used as much as possible for integrations between components +8. Should manage all the transactional data in expiring cache to increase data retrieval performance +9. The API endpoint exposed should be stateless (avoid stickiness) to support better horizontal scaling. +10. IAM based API authorization should have configuration to switched off to allow external network based mechanisms to control authorization and avoid high dependency on IAM system +11. All personally identifiable information (PII) should be kept encrypted in storage and transit + +## Authorization code flow + +![eSignet-design-OIDC-authorize-flow.png](../eSignet-design-OIDC-authorize-flow.png) + +## Authorization code flow using biometric authentication + +![esignet-with-bio-auth.png](../esignet-with-bio-auth.png) diff --git a/docs/design/eSignet-authorize-qr-code-flow.md b/docs/design/eSignet-authorize-qr-code-flow.md new file mode 100644 index 000000000..e0baea0a6 --- /dev/null +++ b/docs/design/eSignet-authorize-qr-code-flow.md @@ -0,0 +1,20 @@ +# Overview + +eSignet offers QR code based login using Wallet apps. Wallet apps store verifiable credentials(VC). Both demographic and biometric data +are available in the VC. Biometric data in the VC could be leveraged to perform local 1:1 biometric match to authenticate into eSignet and access any relying party services. + +## Binding Process + +VC issued by an identity system against an individual ID. Issued VC should be mapped to a key pair, private key resides in the wallet app's secure storage. +The corresponding public key is mapped to the same individual ID in the identity system (VC issuer). + +![esignet-wallet-binding.png](../esignet-wallet-binding.png) + +## Wallet based authentication using QR code + +Below diagram depicts the QR code based authorization code flow in eSignet. + +![esignet-authorize-qr-code-flow.png](../esignet-authorize-qr-code-flow.png) + + + diff --git a/docs/eSignet-design-OIDC-authorize-flow.png b/docs/eSignet-design-OIDC-authorize-flow.png new file mode 100644 index 0000000000000000000000000000000000000000..2f57339ab2ebb9e693756cf025e5518f27a5aae5 GIT binary patch literal 208733 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 wZ#wy0m1)RY516-MfwQJ7*`6{+ooGz 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

~=$j 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

8aQY&w%z>JRV1(qA7ax@;)61LmuF;4XEb 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 YJ^+NdRJU_+A-GLW_VS4PjA8;RSnKv!` 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 Bgk}H$ literal 0 HcmV?d00001 diff --git a/docs/esignet-openapi.yaml b/docs/esignet-openapi.yaml index 8da7669b9..d2e0a5f9f 100644 --- a/docs/esignet-openapi.yaml +++ b/docs/esignet-openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.1.0 x-stoplight: - id: 2vyk8uw76c0j6 + id: 7oz4lmhu3pf6b info: version: '1.0' title: eSignet @@ -255,7 +255,7 @@ paths: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-internal: false x-stoplight: - id: 6pu0p3a6mhqvr + id: ufuc57uqvy8nd /client-mgmt/oauth-client: post: tags: @@ -488,7 +488,7 @@ paths: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-internal: false x-stoplight: - id: 91b3a1508w0a5 + id: llrtvbxzzypg9 '/client-mgmt/oidc-client/{client_id}': put: tags: @@ -687,7 +687,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: zr1f21tws4y9y + id: twjsfa0yqpyej '/client-mgmt/oauth-client/{client_id}': put: tags: @@ -900,7 +900,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: dnbbtzbmppcsl + id: xwqybfwp9me23 /authorize: get: tags: @@ -1041,7 +1041,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: xel51v9q4xtvm + id: bx55bzakduy97 /authorization/oauth-details: post: tags: @@ -1062,7 +1062,7 @@ paths: 4. scope / acrValues / claims / locales / claim_locales - unknown values are ignored. Only valid values are considered. 5. scopes like profile, email and phone are allowed only if "openid" is also part of the requested scope. 6. Claims request parameter is allowed, only if 'openid' is part of the scope request parameter - 7. claims considered only if part of registered claims. + 7. Claims considered only if part of registered claims. 8. ACR in claims request parameter is given the first priority over acr_values query parameter. if none of them are part of the registered acrs, registered ACRs are only considered to derive the auth factors. operationId: post-oauth-details parameters: @@ -1261,7 +1261,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: plbkmewzwbi21 + id: bhi8yst5xtovl /authorization/v2/oauth-details: post: tags: @@ -1503,7 +1503,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: i5ht0irlohp9g + id: f8fifvn9td0n1 /authorization/v3/oauth-details: post: tags: @@ -1526,9 +1526,8 @@ paths: 6. Claims request parameter is allowed, only if 'openid' is part of the scope request parameter 7. claims considered only if part of registered claims. 8. ACR in claims request parameter is given the first priority over acr_values query parameter. if none of them are part of the registered acrs, registered ACRs are only considered to derive the auth factors. - 9. Verified claims in the 'claims' request parameter is supported only if the 'verified_claims_supported' flag is enabled. - 10. Unknown or supported claims in the verified_claims parameter are ignored. - 11. idTokenHint is optional, if provided then it MUST be a valid JWT. 'sub' claim in the idTokenHint JWT payload must match the cookie name(set on the domain).If the cookie is not found with same name as of 'sub' claim, then the error is thrown. + 9. Unknown or unsupported claims in the verified_claims parameter are ignored. + 10. idTokenHint is optional, if provided then it MUST be a valid JWT. 'sub' claim in the idTokenHint JWT payload must match the cookie name(set on the domain).If the cookie is not found with same name as of 'sub' claim, then the error is thrown. operationId: post-oauth-details-v3 parameters: - name: X-XSRF-TOKEN @@ -1755,7 +1754,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 0d1dmkcadb7iu + id: 5kwsv2k5932g8 /authorization/send-otp: post: tags: @@ -1894,7 +1893,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: olqbk7247688b + id: owaql9khq2ii9 /linked-authorization/send-otp: post: tags: @@ -2008,7 +2007,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: ww5t6kob3q1sc + id: b3138thajn9u4 /authorization/authenticate: post: tags: @@ -2138,7 +2137,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: yo34tum285pjb + id: xt4n2ff4zogdj /authorization/v2/authenticate: post: tags: @@ -2276,7 +2275,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: lrym71d3b78v6 + id: 5obwls7xlixv9 /authorization/v3/authenticate: post: tags: @@ -2420,6 +2419,7 @@ paths: - invalid_challenge - invalid_challenge_length - invalid_challenge_format + - invalid_captcha errorMessage: type: string examples: @@ -2433,7 +2433,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 4kjy5afix1m2p + id: mgs48hfrem91q /authorization/auth-code: post: tags: @@ -2560,7 +2560,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 4nac0grjh3ckp + id: ihwzp1k49ndtp /linked-authorization/link-code: post: tags: @@ -2668,7 +2668,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 3dysrxbef3ht3 + id: flzfkwilbocho /linked-authorization/link-transaction: post: tags: @@ -2799,7 +2799,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 6tybrn5kekolm + id: 3ee98xoyw0bww /linked-authorization/v2/link-transaction: post: tags: @@ -2939,7 +2939,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: t2hbrly00fjl3 + id: zevye0dm733qx /linked-authorization/link-status: post: tags: @@ -3052,7 +3052,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: vj7j4uk0geldw + id: 9gs5mjdlxs88n /linked-authorization/authenticate: post: tags: @@ -3163,7 +3163,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 1gukg1x66cs69 + id: bgjc5950ilnb9 /linked-authorization/v2/authenticate: post: tags: @@ -3282,7 +3282,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: lozvgnj5yg1be + id: r7hj34tn80w9x /linked-authorization/consent: post: tags: @@ -3378,7 +3378,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: w1xz5wefevlib + id: da66r92cxqvam /linked-authorization/v2/consent: post: tags: @@ -3477,7 +3477,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: m3xipj5r1svou + id: xext4ahumzbch /linked-authorization/link-auth-code: post: tags: @@ -3598,7 +3598,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: h35gkz1v60phs + id: 05s8uwy4a3tr6 /authorization/claim-details: get: tags: @@ -3716,7 +3716,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: b7jnx7byz0ek4 + id: fdlzxtdexapsn parameters: [] /authorization/prepare-signup-redirect: post: @@ -3773,7 +3773,7 @@ paths: description: uuid=encoded-json operationId: post-authorization-prepare-signup-redirect x-stoplight: - id: 9wb2ekkaeg7mk + id: d7hwkqd8hta5n parameters: - schema: type: string @@ -3943,7 +3943,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 5nj96giav3npy + id: flbtdntgp97vt /oauth/token: post: tags: @@ -4089,7 +4089,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: cdwhsjgoq9xbf + id: oq5wdn2tl5b68 /oauth/v2/token: post: tags: @@ -4252,7 +4252,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: zkcvd1seyedmd + id: bx4h4esbzst37 /oidc/userinfo: get: tags: @@ -4323,7 +4323,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: 5dgir3dl27lvp + id: 6iqtka3aua3f2 /binding/binding-otp: post: tags: @@ -4410,6 +4410,7 @@ paths: - unknown_error - invalid_individual_id - send_otp_failed + - invalid_captcha errorMessage: type: string required: @@ -4427,113 +4428,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: d3di5edwwt70b - /binding/v2/binding-otp: - post: - tags: - - WALLET BACKEND - summary: Send Binding OTP Endpoint - description: Send wallet binding OTP endpoint is invoked by Mimoto server. - operationId: post-binding-otp - parameters: - - name: partner-api-key - in: header - description: 'API key of the binding partner, this will be passed to binder implementation to interact with authentication system.' - schema: - type: string - - name: partner-id - in: header - description: 'Binding partner Identifier, this will be passed to binder implementation to interact with authentication system.' - schema: - type: string - requestBody: - content: - application/json: - schema: - type: object - properties: - requestTime: - type: string - request: - type: object - properties: - individualId: - type: string - description: User Id (UIN/VID) - otpChannels: - type: array - description: Channels to which OTP should be delivered. - items: - type: string - captchaToken: - type: string - description: 'Captcha token, if enabled.' - required: - - individualId - - otpChannels - required: - - requestTime - - request - examples: - Example 1: - value: - requestTime: '2023-09-22T08:01:13.000Z' - request: - individualId: '24554655645' - otpChannels: - - sms - - email - captchaToken: ALSKDJFURIEOQPZMKFURHFVBH - responses: - '200': - description: OK - content: - application/json: - schema: - type: object - properties: - responseTIme: - type: string - response: - type: object - properties: - maskedEmail: - type: string - description: Masked email id of the individualId user. - maskedMobile: - type: string - description: Masked mobile number of the individualId user. - errors: - type: array - items: - type: object - properties: - errorCode: - type: string - enum: - - invalid_otp_channel - - unknown_error - - invalid_individual_id - - send_otp_failed - - invalid_captcha - errorMessage: - type: string - required: - - responseTIme - examples: - Example 1: - value: - responseTIme: '2023-09-22T08:01:16.000Z' - response: - maskedEmail: XXdXXaXXhXXkX@gmail.com - maskedMobile: XXXXXXX357934 - errors: [ ] - security: - - Authorization-send_binding_otp: [ ] - servers: - - url: 'https://esignet.collab.mosip.net/v1/esignet' - x-stoplight: - id: xnl3gyq4v4bh4 + id: t315hcecaulyy /binding/wallet-binding: post: tags: @@ -4671,7 +4566,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: chsz43i4s7rkd + id: coqr4q7xd7k3x /.well-known/jwks.json: get: tags: @@ -4755,7 +4650,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: azj8cuvdurps6 + id: 4jt4qby3nx2e4 /.well-known/openid-configuration: get: tags: @@ -4952,7 +4847,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: o0uqnqgz9zm7j + id: 0t3znwjii5pcv /oauth/introspect: get: tags: @@ -5008,7 +4903,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: o9h010bmb1db5 + id: 4m5xw29vz2q9k tags: - name: Management description: Management level API's used for internal use. @@ -5056,7 +4951,7 @@ components: **Note:** Unknown claim names either in userinfo or id_token are ignored. x-stoplight: - id: hhww7s6bqjn8j + id: ia7rtr773oztz properties: userinfo: type: object @@ -5100,7 +4995,7 @@ components: type: object title: ClaimDetail x-stoplight: - id: 4enwly0dhq8tp + id: 6fpl3ntftytb2 properties: essential: type: boolean @@ -5148,13 +5043,13 @@ components: required: - type x-stoplight: - id: 50labrntmysgm + id: tsp0z081f1rag AuthChallenge: type: object title: AuthChallenge description: Model to take any type of challenge from the end user as part of authenticate request. x-stoplight: - id: thl682syr59bm + id: w3az95o1dv9h8 required: - authFactorType - challenge @@ -5186,7 +5081,7 @@ components: ClaimStatus: title: ClaimStatus x-stoplight: - id: o85s4acpltciv + id: dw0e0qr345403 type: object description: Resolved claims among the RP requested claims with their availability and verification status. required: @@ -5209,10 +5104,15 @@ components: x-stoplight: id: 8z1n64eu4tc1f description: True only if the claim is available and verified by atleast one trust framework. And also the verification process completed before the requested max_age. + purpose: + type: string + x-stoplight: + id: lkp89wz5vzwr2 + description: Purpose of the claim as provided in the authorize request by the relying party VerifiedClaimDetail: title: VerifiedClaimDetail x-stoplight: - id: ombll0ox1abk0 + id: aam5fhnleu0tk type: object description: |- Verified claim detail that can be requested by the RP @@ -5396,6 +5296,8 @@ components: + + @@ -5523,7 +5425,7 @@ components: EvidenceCheckDetail: title: EvidenceCheckDetail x-stoplight: - id: 56ntbetl6kt55 + id: z3endvsasoabe type: object description: |- Object representing the checks done in relation to the evidence. @@ -5560,7 +5462,7 @@ components: FilterCriteria: title: FilterCriteria x-stoplight: - id: xvfexlq01bb2w + id: tl65ie7ligsw6 type: object description: Value or values used to filter the verified claims. if provided in the values parameter its a logical OR condition. properties: @@ -5579,7 +5481,7 @@ components: EvidenceIssuer: title: EvidenceIssuer x-stoplight: - id: hia062eivzuvp + id: e24cvlea7chj3 type: object description: ' JSON object containing information about the issuer of this document.' properties: @@ -5606,7 +5508,7 @@ components: ElectronicRecord: title: ElectronicRecord x-stoplight: - id: 79aws43989s8i + id: 85iwcimsvb5yr type: object description: 'JSON object representing the record used to perform the identity verification. It consists of the following properties:' properties: diff --git a/docs/esignet-overview.png b/docs/esignet-overview.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf116060cbdeb40551d35decf2d73e4df2af8da GIT binary patch literal 198430 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 z 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%0DK0Xo83CL?WD47tf8i=4D zQ1{u79;vVBpMz8NqM;?}&(RYTpItdr(s0>+oOW*^*Zpfl$a(?gk%eET<*8zC?19xX z_a(WCv)rg+>rplFpC_DW8ZAxmKhyllMd%b!lP<2*nQx(>U?F4FaoAK*^#_Q-hM)x* z>FnF3?awFD)dWDkgV~s9Ew_++d~+Kr0dt~XDQrxz56ZvBBCKTF; z_Rs(%vl?-J`bj%uo5*7td_vIu){7|hJdcF8D$^;c94rwj-crK5c_ zR#SmdbQqEui8j3GC2}gx{+-Mon6TE-m$fui^yfqFRQXJuUFJ*hn59wW4?%%>icd8@ zBV)Sv6Qq<2waDdDkBNDEsnwGt-c4d3cDyly^bWawoha`}udLn>pI0ypeV9QB{HEEfUhR+<-k)gL zuH2bn%M(@}{VAd3x8?D`IkLiVm%oj#9yoP;wt(O*w^=l_NkI&9;??v;D1u{FO8wrN zRWygf)P5=K!^68CzsFZX$m&Q`TnBxz%wh!Imw_XyypJ6G$47r3Z3T>VFr5pizE_O^ zN$;rM5@%*Q+wr%bJxa&BMQBF!J4FYFdJU)t+$LzslTHfSg4)xCkLf8UzZu=*fEa3c zV(ahLJ^0G|P^V;4wnrs~D64&Xm$Tjm{%7y=1&s&>{qf`8PD~H?8~?iDB0PnG+6SUij@*`Vg*qI{$=8mPifJcB z8}j@PyYpSAWf-%5LBCgh6>kwxP5CcGvPK|4YPL&_0Y`;*VU~Kc;>kUp6lz|;Rn9cXVG0QXo%;Yk<2uM;Uc*j zt0ZdLlM@8zyC;!gK~0V6guGE8NcH5V!2ZD_Yoq~VlQpW;k(BZy5O_Vlsp$6i;LdH& zevyL5J^FLo!tz1$RSeIesydwP`09!+0OdgC$J`Q`6r^nRAzQA_waryC2Q)2kwUy6Z z=bFhWD*6xa4++FS66|ivE8H>#9@~4TN$j@cz%yZ?Gr{+M9OCc5rfL-VVa(pyXqEs1 z{SCVq=_t8;Fvd7u_J?&_8OTBUPxen(0TgvOAj;Z0&P zcyqqTOjN{aaL>0rfYe`N;FW0#n|I$0^J|B$oa>APOF#CXRUd5xgRjm`Uv9&y3gtUw zhv!($eWupcYCrdTMOD+>ZhbBSwVTb827UJzb-+QudxKj_Y5 zDnA<8Q=tEry>L5yf5$<}r5A|mZ%g3)&7S=1{^Q#+?a}}`R4=ooZnK{9fD7i94g(Ds zw@GV6btllxUTHIG<`N>|@FMcnih?jYYZ?ku?wUCbT7d@}JwmioYupfCqF=6 z0l-?&eWG6qJP6eWRCINl16{_!f$mN%u;Gn8)xn|gw@o1t!#OrTcl!ekKIyC`oiA@6 zz7V?c?}miqPOCRV7)bVKD6w-1StqJ--QN08uya%Wus4XFH*o81vV7yCL^3c6EMfOY z9>vRp;2f_>(08}1{@vz!%jd?%Jj5OSK~BDtI)=J6|JaIFtGmfYz6%9<^rE70_`&>| zN`hhR!Ul_vFh%YPVNZL<`}yfNu76v1I_zV1McMW`?~hrE#%@WGAN)LuZRCz0B?LI- zy6-lJ*Z&!itteC-*a+D6_YSI|>1Y+%!NO2MC4oC4Tt+RSapGygSHWm)TE!UD?|fcM zf&{p>LUUN|}P{g-#n;nwBM- zJ_VHkRpqpela_JKVQS+iLtb~HUd+_gcY_sb`7yYT-n-Nbvo|dIG)uuIzXZtE+OCht z1yU@7G!EBU%>g$_oSOkaDU^o$xxZWWC+0Pu_u~=o@~2!u%|Mf@yG5uLVkyn`i){fX+pFfKQ*77{Wqjsam{$ zH07N~V%RjgUt_gPbM%Dn;&8?L>iLK_k~hE_wZ~~a_{ELixc#X+TT9! zEJq3Av5?_ibTYn|T6Uut>p&}7TY5>*ePM3yD*ikA-Nf5gtI|eWynW}?G~)BpzsA{L zPH(1#*~ZPQ3U2tosR<$2c@JKF0RHGQC?V$pr-(ReqOwwnmr*O zSBm`ahmvpA1)m|k5}B_}rywGnp?HWS=9cWrt@Y!daRF>}v@)3yUKh3N94?`f4VTgy z;n4MmgHo;}(if1HffCKB%WPC%aFqHLM&->Fe8MWze(8^YjY21i7fABg z-2X1Xrrf`O<8Z2|kJ#e8oi4`cq_XjMmJLUU2@{0$j~x~+Tjb$@0;t%d>X`! zjm1&PI`x={M)+q>IB^83a5W;5Oii8q9-J3+A4Pv0>W9|xk**D>b>?@^wS;%Pq+eK2 zI=77biMl9#bzP1n>iu%d{61h|l09db8`sGfE|5Xar_?+x3Kjl`_wTc>Mc%>o+U7Mm zav%~hL;y{;YlPrqo;^HpY8yw(60hRGV$Y<6eVGc$Pz!I{0gC`El?nC^H)PEXK>{(A4?A0TJyFFab z7!USEGS)IDW?smhb@!qUI@xvwCm*w#x%aOet}bSoT`1fYYK41zp%0F=mR2Y{v89(a zLU!QH(MHu8o~nYt>^@`0_6+}fhtq(=&7B_G+b-8D84-?4CV5-eX=YUQW7JTmanlWm zXSv=4|Ed%IUef9{b!kiW#-tBRSCZB!&;G}`!RQcpxX#`;G+bnBUXY8d;jg>B9+P5o zT4Q0k!1BM8-R~mDYqoYQY-}&*-S!C6MoYCzFMsrVDr3@IjTc+aw$-wlna^$dw4*IO zG{3S=sqNrT`M_pek0twTC+q`7d&bI5#jIQ>UH*9EM!D7I$mjq*%`Ij-Mr?qN*&oWP zNo(~=YbGD@O$J?d&(v{BK?ituK8qT-+-q|0HPigGuBe08R?2=vrYjvWWH+Gtyb=8J zu}qlYVxbponDL`zdI$OKJ=XV&JqJ-{#MGxhpnG9eE0S{?-dM1STBO&g(UM98lqs6f3(QHPFZM%(7 z54!(NA~%!vTm9A}4GyX~FiL7nrM0vxjfF}lTe|2*Lu2-6TW=ftA)h~_3_k12Jz(f_ zG`>E~nXUR_W%}2SC$)Xh5g21O1oHog)m8d(@I`^HHE(K^9isD@W+5=f2+(17szkk| zp87s)W&1!S2TevTE0o^7N>crrX2Se=_lRY_1-Icq#r0C4b&;l$1N8(CFFhiTT0*78 zN>ZWIZC@<-DgEoP+a*XAyl1qV+GC*S#Sy90=}X$GCj`?-cnVzHHR?Q*ag!2!^&sr@ zD*c1M#raWkNQQMC3nR1X$Ls?glhlb=y|mN_FV%6k%H83@_A!wh#BNs$Radr4fYKNU z_(9OBnmx)LE)vr&iUdBU>b>a#12F8PKO$~iGG0SFegl*XpG;sbwnReQPyh2~QL{-T z%{TpXD+Y9h&O5kBZtU*KMt6jodG7ca?$PqHli~MHl>Q~#keS=K5NG?j@Dj~$2O=gh zgXayMD|ZWD=rx;}|C)S86v={&l78#rXNBL{U`{V*uV@~xUs**Jwp4L0y*5p=Qdt*Q z`E{2ia4&;hGjH(NH#0HhZ!!hxtVXKynWl$LyoQ+ji$DB@Y%xoRu9i~P~Kxie{7 zlix*?Z2zrsjHVPspRQU8Ui>JT?GDYEp3j`<`e-LXpJ>+kJtC=iG*Y>1O@cb;17eyK z@&^bqF+q`~n;xxGBX2$jjDM-}`YLecv5j)ok7ChBgTgW5eOKD=>xaPNtdwBBz$@5Kc2oBJXYO%jMwbNf;?<`D5pQp5zhcz+~` z?EWilAj+mTSH6Mm8EO(kZkRGnVleE0&T)rF=jRIUkM|OkUw?8ZlQh7*-F6wR>onmb zjhyMG@Bx$B$IEjzL8c;rYl6xKV_umG_)p$&@-+LaQNBBADp`8a*>c;SSW_9_y3F*h zU2gp%udFED(dPNa^G-gQOOJAAWWL3hBl?vHQo0{JFhofw{N^Ae=+x{YbuBpN4cI+j zt?{)jQ!|lzC;nf{H%Ds*UH<74>>oWpDD(HqboDNv?iCdadVCX#dKL-)ai#r!A!@tT>a^=w}x z`QS8EeUNR}9u{CqSx)a=%ZDrsgu_39yux4H$_qTqPeWl-I zNgvML5L&s|*CU+dbav>k87`8*N^dd296OL1?`=7r%X0D1NQ!=fnZ`Lf=a;RPj#;R| zgmvR@F`9pwlOWXS&Br`qNSo+Ouo4*=DLZ(8`oLJyO=>treEp`B?-C(sRzFWQQUo4AE^76S zwmJ~`!E(AXyV+BcX+|}(B_Oa@yuV{<+U)$sBpCORY%MhZp`5DL`uJTkx4|pAOX?g* zs{8R}mdVjW_%RzuOl(2m5&C}l{dAJM38*$Qk}8mYw~_ z`<1o+nctn6oc0e-x(7P$I&q{(!cOY$q;kv3L^DM2Po@AhfOL85O0BEwWviP+KFuuq zd0cH{8~fhd?aj1FpIM!n)7<^M5nT!DJlLwd+DWyWsYi%@e#@PCsWh_GMU(z5UpGOT zTyS{XPsDQpiy7f|Uy`u2jEo)_emgdOj*vI|*Ze|kf(U|`gwW-jBkNq4=h^RD@q*RD zN=&YZo(u>1=G6HI4w^EVLse=CD3;!)9I8Gd6scqsHd@5s&SuN!_700*LW8CAWw%CR9?sT1!QY~Yf|+DN*e^P!+G^QAnF#aj>{b%|i~AVTTb-5l z<`&?Tlk>k1n{yd7wODgk&vBx1LJgQQKD<@P>$vEz{*b%4I^6uY1zynYd=57o`}qDy zNggXkUe9D4Z)8s%Mz;#1BFnd@5RFerYdzD*B8J?~2ePTt%eHsj0>-bx$}qCSGA!09 zhnta_%Pzv#KW>>uGdhFu<$RgttF59%`Yl+Pn6Q5`br{aO4@Qp-VueiC>&;h#cdJ&- z1GH3MzoXZPpZK8fI*`|r1TNI@nL2m6}~?5}$8>*ZuQ=@HT{c8O8hD ztZ}v9<5Tk(XB4g@>=j`ECRAI&c|83kV+uzVzaarG*+D~x@4vAE0rr`w&m2bH{sD_{ zQ3hW2R{+rNN<<&{_j;X9z0}?%L#SEB1E)Zc`z;12Z}| zd+ohz&r2dyONiBK*48o0D!GNLuFwkOL6F>vAXtw&&%3f(lwp-J89NyHVT2!L)1m{62ySe`c4a-lzQ@kUD|2Y? zTIs_Zi(ej6IRJG}PV7>?Nej0}Lf$R_1Nr4oe<6|8W^X$W( zq_WM1BIy-1hb$MeYF$QzNTn>nJ;$_zG&1@@@B*3r;(C=`WuEG^gwGvlFKO<-YJ%^# zR|(hL19*e?m~k56H&0&3zJ?^6CxXJW3_q}3$T5DEXOG>4t+|;8^gk+O%rI#F%w)gR zVv$gi&XhD^)jtx6 z2~*kI&9QlD{1i|ex)?iR^YB zB2jv@nM7<6*HAqyc?5$sSJbiJw?R5TfIUg_q;TUf53=FGLBLqM~Q4j&Pkjbn}Aauk* zp-(d$L+(AczvK!69IL#l%>jUiRpamNr5}ZZn=J#WLfWK-@~foK5`+0TlJjbFpo0$a6O_9f>KXGZd71<`qFT_>SE?66}(<_{0#LVBe`4U{?c zcwWIB9GMCIl2YP-dhg80$oLV_Hy(5qmnQUnkBI}6xln7R0?2=|lfO2c4!arZz4BAT zk=5YJNyFnbe$Nfux-HB?KmF<35&G#({MNcb8|1qQY-aYAi0$#p{O>_T0|{~<^GGkm zc|4|+*DYXtJKBJ5&y`j-|4A;H7z4mhFc1(Dab59sT%kQd{yGzSM0rPc`bjI7*pghQ zF}5Q_prjMH5q<`?&FMZYu3h4 zbz3~RfCFp1aElgJs;5E#{t-s>Tjl>~B!+w3VtIamU)bkz&DvJCR^5kAo(G7qd_oZ1 zID=#4qkMh$+kNnDlq7XyWOGxM2uFaRLubC*(6STUn z^7~K1k9NB)V-lJiV&yw&CLwN*ofkwcg-(yMku?%$xyArG%xK)qxW4A3cm zF{3^Yh`-FrX$^xiugD8>F=6imqm}i8Y^_!>=3&<{#^YGNpG$RajVDRm)F-`RNz=&E%!#13hO<$Pv zAd*3^!y>)hnq;j#VUzRpR`W5Cc>?c4uv^&AS%}@^{7qX%sz%_H|9y1(J|>;OdhW>P zkR@$yK_p;5ic|R(MfDlub%^Zt$$C$2Rz8Rvg}+ee>(8%?M4mVcwE&+|KE};GvBhIX z01*il=l9EGDl{};baXrQSf1^jHpZ++dS@f8YQdhO-coDFF_g9o_Oq-66XWXB`V8CE zWhWnfo%{V9|Izj_al&MVX|hDNh}!3Oz>=w7Q6d*bph}TXq=y057`F3CF31bDxtQUA zJKYZh9VMnM-+I(Kd%kt}heYEq@Vt)MKk|8cQ=x$zy#5`vfrW0~;MxOOo34pSIC9 z0J0Z_UH|}h=OEi~PUXeI9Z3GnrsS3PKPsuoC3=K2-g`8{#f2($qQh6<>&zj^Y?C__AQ)+of?@iG?I=_}r+G2l3EcGb{`;S|r`im@H&1#ft-{7E`TPYHEA% zS6oy*^EH(I@jkaE!p)-bljd;IUlztZgar*&!~Ob9OWy0HmBnmY9^S}*Z*ym3X=JP< ztR2MPg{m<92oDZ+wgn6o06^7&ZCK*9G2=gRZCskSguK5TRfjLdx0g7SL}%L%-vAmd zD^bJFIfnY)rQBg>6sZb@xm8AnYthWnNaucES-;B!RVWEW@tr(EP;mE&>QbpL{C>cC z10D+~smrtXM|DklNm*I|4FdceC4Ri&WBgDkZ{eif4^u?<*4Vxoh~%_3+=sJT4&1N~i3K zghr1thvW#qlQqh6%fUYCPmkkl?br=+W~lLDgJnsmHVgqyOb&!Ef&SlG9>hBT7~K^f9FQj zLX;Qch?q8hb~?nS(aLjj^+v`6+xe(+CBa;e)Nib?pLC`N3t=Xuc~`ez$5ss9l2_Y5 zB9nC(N1UFfMqOR}J9LwtMRuUOyVDNNY>#%S^NSxYEj9{e*r!!_Vs}3qaE-rvKYhmb zu~hf1KrFzu)I(84tKPv-yK)_W2XC0`z7iW z!XH0CKTgMud|Nf93` zD3BTCAI~0ALi{NeU_Y*LEQk-EDf2r5leOG@-G|cqBNd|2IG;UPAg!MR7RVg!mpM8~ z^P(-C9CkX0F=eoYXw9lC<@D3UEh^c{BZmw!C#{JfV(U1v6c&LGk$QQ3gdh*JrIRef zVndMNQ_#@F0d{=DndiY7$fnZIgZh+kOd6S&qsWnQu`KVC5?Wnf<4nf{>sON6AAtU< zz!!HQ!{_iMNuZzyV9#PmWCDP28Rd(?k1JCvFd7a#Kz!8mzao@Ym_P&oD89qbJUFRC zfklHa>bO{CK`U9%1-lpH;==p)HC&X?gZ@N$O01WfvO$UMAS1jP1XGYnHJt!{#%`x- zx8#1~bJkl>=8N-;F5p^G5eh=vpBfJD|MDH`w>!~U2h zq5j7!MVL8zQ3OsG~LY-~zgB)Pi^P5`!U&$1D_blkc zI^ol*2{+qR@nT0Q`fT(Ijhk(sGW(!Z=hKpJdKn_o%N}uX5Wxtif3gIMuvvPc#%I_{ z8fFl6o^j%AzqsHhr51BROUBUp?uy$Xj&Y^#DUeig_*ADNLSHs+-kUCNij zQIhRD)Q)04(C)at-*U7D)e1gJ`xHHr>hzj5qXM57zCz-OA06-l>pPJ%^Og?MfRS@> z02|xo=OC^?%UAjK%{OAM7NPTo*dwJs6`RiF!D`Anv;w06_t`I`VMa!=djZ^|;wV4v zj#-7*x!T%Dj>ccX7k%q7o+>{|p3%w}{dJ|_0KDq65`5l-Vrtl^zMT2)1e&mK^0(W z;dZIOg3qX{NhBVwMlqd5q|(l9|9Uy+kLN#Fp0f(PK@$%lTB$^CV_R#Dm~WL;u|Fiq zuc~jMs-+ToXq!kMPjjOU(KH;9cz!HZ-90!c7Y`|2VBVmu{eX@SgcxRD+laR4ff?U& zb-D+X$#nsRY}5e(FTwsEXvT6so<0Pd9t+dte~t`E>BNKzxj}MAD^2Q!c?u+Q+e7nx zWV!4HPWES56*Pf9oY|6W@vy>0uUmWrL!FYt%bCE> z_*+pG0dzIiuVmm^AUN0elbq-P+^45N$B#+<^HfWR#IWpzr7jKVCP_WjJ>Cx5diDXU zNo;B4{iQ67$WGXjwkQO|rIIt8RC({;%13M9)b8uu;OZTGC55a)ktda)10(1S+0=6= z*hlQ&ky4tB`?77caJJtDkg*~3yOeM(C(lGcM8e*E#Y$m2s&2Ffs0}r5T4s?skF}1M za*ny#!$nGgA_1EQeemR0q7dh&y3JcU;0gU^PKN&XJ4H+4paA5WjSy<|A}6}j!lh{= z!~BudeoVcfuZ)H96+w1|q+uH}_At)hF)e76>Fo0{(|j4I;!T)PZFH86|BvW3O|72> zsZ1FKx-KEz!`_SP#SHwDQU2RR zmwmoO)q{gP#&p`93kAm1xqCR(9&NE;HKk|!!rpR%1IGf}nWv(Umv!E#r^1zY3}Fp7 z@HG2T+;OA-sf%nRkcaIavx-v>gMun7*KsKJ_MbDAX3Sc8eMP3Lt<`Den|O=f)|T^W zTCRNjTez30Dg86S>F%vFB)vp=Hrd(a0&tM-JXE@YPVKa*@`bt2L4O})+A z%4gRGvH45bK#O-=_}E`fjAfw`y4n0t%dPD4#38ce(Hl$|#6dST8O<9;;0*e|f9;eS`Iv+(~O^-hx3R zZ((|(iO~!hXVOtqkDixd3!NI}a zT}ui}>9`@C=(S+75Cn|ge}T!^XG*FD#_5qg}LXlU&Q)H z?$Zp~#1KUmZhlqvm}&cJ_MNFwR3H!S!NQ&>&`C3_a26QC4^{#Ybw@(m>kp?=F$0=edG98-|$=7v;C|Q0Q8rSLHXhiBBy3qS$R}kPj zyKjLKjAZrLRhWYIrSKH#TzKbaHD)&4=F$_^X*B1eb%par>R}s0Ij|bt{`!`^^XYpj z@gv@rm#dL(w1WK$bLZ-&!WOeq->U?;(8s>27V|}^?WY_V{nranoLv}wc;W0lwkgM9 zGJw_0^mHDd=z3~KOHL6E;eapUHOes4$b)~sS3tf);%3SY_L(T2?IXm+J6e)kyw+%* zU$}Gi%}c~$_&%Z$+CM;Bxz>?!7qsS~W8f zU0f(&;n8hq)AFSGb#X55#azeu*mU*45 zJ<=_es;V;%^6E7a>H)BSxSDdPx_lvcoTJ^5{gIyWDp?IYF}!c2F~KGPUz}ZZdD!{I z@CM}|zOF&#qOvuRr^LpzY-NzwXgFL&U%WyE@yp-GfCOC|>j)WXSM0Vm6`{uDIs35&; zC-Jwa@0h5f%2vgpu#i#*tPNWy0owD=wB-`|Gf{60p6f+}I z1uYt2dz6!`{`fPubeU9pBArNkbNWhHAZ_9CVz29m-oWd-M(h2X=d$^#Mx1y+S*rkR z`aqe|%jJj)Z=>|MckbrH%)gsBI1(e9p1-N^bo-#ntT$owQtbmc)H74>vY`2;5Ynad zCCN*8;S6xBFr~UrC?BTz`p>NrC+?QaqsUPAQa!yIUh$CkM>N}j0H!>@(@h9)&n5*S z;U`lJda?qfSHn;#0^>@ZRB0bn6z5!UzB#M$f^OspHOP0X@-qR?fr;MNJMkT2d-~I+ z{BvokX`hoB+s`vxjn_xJ*&^JHnr%>*0uAD*yfW%>zH>%J%yCLt3|UG zJ5y8azRU^Isu@QcJk-sqknPYjBhq>-Aea-+juLU-wRD#sJcgo7i-+u1SXk^0yLkm9pG{2(^&4VE>D@kHS*~NIkFAm z8zuyZ7PvN&0J!97hA*f}&eipdFZFVZa1j=Vg^_TcljD>38Mi^d0g-DD|Io+Z2!PL7 zKTAm5+A|L#2c(wPTT3J=1-P(fJ%xsiQ3W2)%7Yje8p@&YUq1}5MIff)0#LBCQDxOA*! zC+lc-%=mweoQgOQsLg-DE<%XwJQm}xbD>nsXC9Jb zYOA17%U_;O<56G)Ob`thkL2o4HH2&wyh>$0k(8f!grP&hFi zz&|vTT-mrX6+|cq8J0bOK^LWV$x0xMXF?$Y1MMK6J>0pp>M|sp{{72B+s`c`qS#lG zv}!T8{kvT~mp;fb9%>AAeF@ZCY3NlOqppvnpBO`psEr6BG} zNVFfG6k`^^qi{d$DUC`cB&1?OD?pNv&|=jlg6lU1SI4=BDiu&51zMxKs#ZO7zg~Z= zS+9#rNVcq4vM;^9zRt0pbY?XuLOaE zIv=qV6o$j&)JQCt8>VmZq}K!&6EiEJ^FalCjC(4>&~+ScG4GIr_Yc6$)PffzNq8JX z?vZ&}=+0vWSq|3CYbr7FMobGhK4dH6=jc)_FEZt!$?sG(U`+qG`U{j+Y=L-b-yrOA zjs5AkPDxsY@S?cM287=nc0L9Yv&2TF$y2fLi?Oj0rg9ktcZ3QinX%e8_ycir(Nf6$b-G?k@tfGs`t}kSc(tc?^!*!9{2GU;Qa2{xwKsBmK#`)MT-9x* z0l&1PquRs~xkTE+V~D!P(V4N)!|MYu*yzwRXR8T1(8B{&3k0zn231cEz*2M9X2ga848LvRc33@&%id-9&|)V+W1 zk9(`8D5%F#HB)BJjYR5~CMo{eu*l3{cHtd{FOC%Xs>d$3m%LPRU|=?dX@ zp`s>>I2!gJYhrPNGfT(7-b%*+G1tA;tylOjUDr^`45|-lUb5;cY5tK86`>{6y-t9z z1~cmAUOnWaC%XF<>cgvwC>y2j7OLK3QrWWZWWpB3Uw!gQgj<^}Dp{Hk0+%N!>e;Qq zMXEWr_$4TA#0-Ax-}U5D5Q&jPEv}eRqEGtF$=)078#P0)&tETuoqp*47378YyDsWS z3cC;z5|UPhA*S@?fMa{mqm~9wNlZ!JpoYl!&0k!#Jx>j!o--L9|+2y9;df#z`7$u-J zJ;l>K4PUi;Iq6rBz@mYFcXy|mFSkRSzf`KD_&7Um-Y6ZH_l%e=TwT zlJs2E2IKwpsfdgI=oc0Gc|wcSA5C(zOadU^&ujAl>7RAyl4>LS=$qT-;7j*EF)U75 zmzkJIhc=X@$}OcyKwMh2tVQk@6B0L5%qOMxI-JO-9=16Mquiu`APj9XdQ-;)eH16P5 z%kL1JRSX*1$cgLI?du8Sr_J?z8Uh&<>DT6H4_i7-e{IJMtZm0Xjrw)v<73g)q_Mj7 zsb*Jw=DPGL{T`Ih*v)~ldxT0#`&@_Y)w`T|OHO*kL}#vzss#)-TK%7nA*G!CJWR`_ z15hTn!%{0y$wX9G+5%a~Bt}z)VBPYcrB(m#Bgu~s&aM*%O*7;$voRQ$7dJcw|XM-OiQ)DtYxVdD7kX{_~tGs z(#yoMZe>TdmXxH){+#=c12Xe*Lyl2FtZRZuw3b))CUqn5M9KRf6O{8C;YOoLj9c*L&Yg5ii`tvG0_)h1sdxI%vp#=TcJ4 zUzS*DvKJNyx;nEDRIC__vn9Y*9}t~WI6`KyP))Mcb7F39XAscdv!W)C>XFo&()-nr)R`g?# zYLst?4X!F$(AkI;tR)(m-#itk?+SK)P?f3o$NI)*k+JV)M7Mj>iy|g+M}&Qo*|*M? z%(hX!OrVm*P2^$JVdCRD{jAgKCq=SY9p3y$+icOJbOILvzzRWsN1gbRBZ|@`5NMUK z0#Wqi?J;CvVTu{;@~zaG-8L24{ZVuE%M;Vm?7Z?N_N9;_!y4I(6wWNkSza)BWG$@7 z5@AQa;+HA_E_{DUEu;%QtSo%#75;4Z%g%N`E#sTMYeqWx>5I4Jzk(roiMPVbM){Mp3f4< zwNlOVdv%qSqt+Dbjo;`;OcDGOwrnnHV4Cu-Y=V*Ze}YNLB_WO}qd}fxrF|@TVqAt` z$M2kiB*Mg$U$?{)x2XDk7p%M%iW-|GJBc%(?0s`Ys}$pJdEc$(TNdEX;9NPa33+jW zu|fuO=o%sw1~@fu08Wj3vDLgw;)gZs7f3X5@Wi_i!jc5TGdgoltx*hKVIq$Sk5A+3 z@yRwk(Lan<{@C#R2!Tw5Mph^;i+tENvF&@Te5;#3(rCwYMO=BQOH900bj$mz#Pbbg>*JJihgz9$|mOT?#_;`*fx4w8s+=y z6@Pv{{aDQ#(bnrflt$!ii|uoIla4QZL_Yioho}yz#(@>KgP@62L~$`H?xg`p3FYr} zAOT2~AAyM1l%}g93j-ht1L)7zcjg4G{9m^61GOBL0M?TTB{HDqFi-%>q?#lB8r1F_$TEV&dv^pwFp73|Vw%XK)2}3agg0j!f)lrV6is z#(@oW`rRxrsq8#`c-=SD02!(^nnfvxA1VBY$GV}zC7R^ko7{*+)1s>DhuF;#0yEAo zJ`Egs-?Oqp00FSgJ*052b^LLg`En21IvQcH^%9U$QC$1%D1f#OM4$>GTW9*Yxz){e zB7L6$NtAZtrhH{(5&7#Sa$s9_Op_H~zmHgyz%$0*SLx!GlgQtUCvgVtD}++jCrWj< z4J)4Ok`Q(=^()ZR?T0jJ6D8oiTmFXCLet6J=)@q%`X=?dgGN$X&XOLuppixv|4tS- z=X3b4geapOWtY$gJtj8;Bw9uK;h9NS!?Ra_+5{?YeWZ?5B&->HZ#;aba$W_g>3a1x z!qATwQ}M9qHB#(g{iwawt(Duf)1L{p6ivZre%Vh_8R(BUcj~6q`v5Ri*i=x5Z~o#B z$Ht~4H(aX$FwrLmuX@B4?sig5ixC0)GK&xr;FaXEQne&TzEJ0?taYR({lUCrzc_rz zI|T2|xuL6skyTvdq#3S$o)I(CdPVrkJ1a(;9B8my0Gb!mE3^XY_$pn3BILWutnP|s z807gYQFq)MY+b*JCrZt<+U-j`Y%s=6RvMWW-%`3lZ*v$>JgB8^A~KiAsL@RXl8i-sauPEAZ3N`W{KuXQ&@O<) zsdu(?eRZI8a*1q3Rp>;9gEGX3pC<64n1BG9qln=EhauZ@ZTcwHvB$Tf2@q3^mw142 z8tKWlwfXm{{~y2XR6-7@M0|9%Iwgz?{M)#mbX(v&{%3Gxi0JS+NJ~Li_#G%uCHSj= zJ}dw4o&=Kq|96prXW8JA!-Dc~fBE_q&<*x=(7gZ_P(m;&M%?=tLG~kd7fX(RD22eg z*|(lF(<$)9QYq=Pw?+{o1%{UGchTzp&!Pdt>vY8TKHb2?BH_TuQjY+-iLOS_21)^^ z^=sh0FTibo#>j{wEiLU-_j*a`g1|gKJ7E7x1$f!z{}`)(Mgt#|r(CY>XtrUq8&hjr z8>&S-7)%uJwdAANIuc6&C&|@O1CIu?!$ICt^{AO2|CtB;%`fy)WxlK3;XtkMobgnQ z+bmHpL$y(2@zEXNS@)52uR)Ba7jOV|eeQkmZ$2Mbf{zHLyFfGF&@OZQ!MB%(i$-d; zl)Qj37okLfV=g54E33)gvH$bRQ>5ZDk@6jV=|2})fWH}D1%d*kx#sPua)ao-+tbaV zlQBiwC^0mkA+d`em8tay!2Q-!kDht!49oziB1Mw}NZa3irHT$NEUQaD)SR^z{{p}j zBxK|~?NXPcC2LM#Z zk)%&wYjOsQD}{crc(UGubJ}vTK49;m`qr$HeQjEQ@4~qL`E!}Ks8(vTR%$ZO@ClO= z0zSX8xef}n{evrHfz&l3#q6V=HOk0$ne8`nNrg3f?HIubG$K-xYYj$GT5-W3CM8`5 zx={B6-3L!rKH#x6PBf^k$rtI4@08@k0ll10f{*$fVT~R4oFkHt{;>GcvNE>G>1oT0 z{kh#sAXr5+W9G*R#Pd8}%8FLlr*asD@{PP)5BBTL^1M1;HT-6m7#%LIJ2A>6Dr#b# zT~%G(5As?Jk0N5B*}Xt+mn3wT+?PHwPyP-K9Ha?M0a!6=sME3raL zKp#r~VALlhRKFSxx&Mr0fql;YSo}=mZQ3c$$d~8WhluWFMS4fP%aDZkNULWND5G$`J5xq>)ocT_V6wn(< zKux%t)MX@h{U%=gBdbpF3ef4g|DbU>E;<@(YEk@hp)7z^n{LYc!J#3Y(U$8! z87DxOakdk#fI+&jn4pKdYc{PZpg4Jr+op;@LrhGJ{*QSqk85p?U7%63*1DZ2n$H8! zwya`Mwe9|5eo#+mS6bDf$ne9td(dua$)w@t7g4W6-8XONm#MuLTnif!auksmrI51X zf(@r=y;l%DX6Sk4W4)FfBZ81wEi6qvOhXc+?ZFt`)a?pIBm(zd-4D zfK{g)cWfl7CR_W%8^D_ROvh1+CjVHjwFVPzo>shx6IN)h0LrAUf16P6!DPwIt+;RV zyVO6d2b!iU7#mZxKit^E+IppTP5AS;F2O4#GYfWa@W;3#(17N%dZ6JnUgT~)Nv|*Q zO%Bl+?fjf{+TR|uwY3TT4P38)nKB+P6aA{#{Z{;V$uWlvY`QXB@+BHgYD(t2QPqM58!w zMI6s9EhIOSb&?H92{_bDe>34efVtc4m?rCO--1gqJ%mx3Hb0waYHGUt@`wYvFr0Gf z!{Q|$Uha}@(D+nQsYj*;8!lUC`sO|SV2IBxE=C&_zck&rUJkefLGL|=hJPrtpoB<0 z3kE?|YTm)#O-@aDrt_+jS3e-%tS9r9(2G~X5KT+S8PUh-klf7K)xdhG9iqRDC#>7% z{@KMkpexi72YZ)mf)pt|z8VBvWE@sEjoaTq{s!$|E&vR4SXxmb7f&OusjZ#A&?<1k zVW~$rEPlIRS7u+My|uBiGHv2~kf2Fg4r}q)Gae&#t!RWavP>3XCo}fg`vuJx#Q;;g z*G^hhb}O)ZlOWNnBUMGtV~%jxc4-!=XUaWE8*r}Gw;4`q2CU5dUiA}4UiBg{!Sf}v zn39}8!X0vQvKQVMuWX3$$MrDYNy`19dXTvlcgr$ueT4qd=+eYff$UT}-h+Kd9_D3d zd`((s8ywjc;$D)>n7LCEO?a#Ylwh7T$U59MH?;xg66Spmo3W@jp3ljfTW^;jTXSB-2@I@5qoL(~@?&;cUThDc}B+xDb?l z0{qqU;{v=qMKe58pH025^9}^AT;0u;v3&F`9^Gvzowz|NmHjP$IZ+H~+ze&R|Cs~Zu^0MABY*wLH1d3m>S9^c*{H++*4<1Z_-*IfPuiF*@+ zhTLm-=-Jr0^DcD2N?JvQFzFN3F4U%)R3D7mEc}IysP*pgZBh?n!2kw$uHqt|`4>zk zD_>k9BgANVy(guOnFd(EUK75!G!zkm5TH5Ag4c4uVC-G(5SPwlCxHd0^}O6GaK;U= zrDbcCeKLWo;iPNP>{WIt(jY=el>A-m*@%woN)n^`VQS1#5~>-;Qpe-{kse1gW9p}u zjxap_YEqy>F}`YDQ}xi9*#U1 zPz=8>9g%Mmc$S9=^a2!n z*m$$+vS%@oF3D1gSOAH7%sl4=i(#ALyk}| zfL%9$@X4@Gjk&O>gh8Bv2scCC=qD#hUG3+LpEha3%zB_6iU&}|KK*{9Gj~@DY_G=t zk|t%#>i5Qz?$AXFg4=Yv67%@Q>|A+qPmVbqOQ6lE#y)Hv=pp={=~!TLn4NMi-QGf* z9^26qv$N4k>R2r+b-NYoAbVjg%+tT4or=~|nC_&8d^Vt8$MKAoWI6HM%|Jx6&XqG& zyks#-s%i#H{HmWhzBAy77nf;#r8YvUZQzukovvICYtz$v=j^atLnNx=RZufPPB?;A zFz9SfGSsXN$>`R9zd7wx|7^f1Ld{H&vO*tBxSu`*jr6Qrpd>juZ4RSzoYRktZktNu zc`(g%xl@{Toc#uJUYXs5kr zUuUG7*)m5+a$unf1q}M3POr?@i(|cSvl@BMg!mExz+6*ouY}9wo8QtlnX7J3TXavon6=a;DapYa5@v=nadzo3wUhE5BI%G}M}{#%yb84dH-W#l?V`=7J6XhRZV4?M3@Nx_CvsY?}o(PaZ{^u?yYZ{ssC+`L7^+tA4SC7 z*;KPGBD`9~C+0Z3OG$A&m+3QWVpK%f9^PAn$<>Ii#zl(sfPfTOQ=1W#peY2_TY6q~ zM1L+TIe&orbe?N%Ve5>6N#LnqIXO8iNs8%+Q=?*UOXQs_HIGM@-YR^t*VKY##FxV6 zB>#>$L5dv->Y1_@Rg&EqLq7B7I5)(lIdk>s(sojUtTwj;Rl1t!xS4=|4ng$=HnaQ2 z=Qzfs36v&@5B~ENN52Jjsig~dzk=Jx960DsrZp%;?ubh=bx1klGILb3BR- z{2!38W`Aa`V;eB)LogyMD=QnCxPUOGnmTEcj`pg{OWo^E#tckcd(HbQi6>wW;g`9Z z@keInM~uI+;3uJE>W}H)lfCksDB%!dQFK~A7-PR&N5 z=_GD*cLU@@g{A<(mulpln4~>PbBavl6R8dpww{szk|o-xteTiIBBlES`vxJ51e!b` z?v>HzwH0D|u=ZT!^bI9+*f`2TRIk;Aw+XtqxV9UYefikfE-m~9a4Sqx@IDakphaf* zJ;NXi4IZ!zQ8S&c)SvCC+B>Z?{J}|5{#%m1-8w$nJ2i)T73KY@li*dN)d#om)YM21 z*N~E1KLj*pp$f47ejI|OJTygJrT+tqft5D|EPQ<~&3{=21V z$d*p%>Rx7X<}2mSHC}gOW;6*uG8#Abh3TY2kyemw7NW0Pq1{< zdxA6i$s$QC*z7*?v{pQh;ziOG;I$D2;RqxDqXG~Kl`xRZs!6Lyd{JX@aGK6WmP=n9 zNxW9I+DB)1ed;np$Mop*Qd?)GV)2Q0dV=Tl5q`y;Pz;IC?$AC4f3v3K8tKypRoV}@=WTdRR-I4Q$NFjd!b#QpCkJ%! zBFi-*`;>B}cf)VK51X98Lgnq=Ip|g$`GQ+UkNPL#B||?l3+V&S#ZdF>yt#Qoojk6p zBOC9PuU}G>W&p1M((fcYmQN%4d7rfg@C#TjZd}jdP6O~s;6V{|o{JG$%B-v{wzVlP zsL@LLL7gV0o};NANRnqW`#|n%PPF?(sGwyFB@NjO`)&*qL(l;&GsFDnD+Se9JoQFO zi5#2|8%b=>C{;nEKa6B*1Hof)=AGc+MiLjno-Mw7E@!L?pMJUC28nq;_7g6x-L70V z#Lt<@p+%~n>4C!y5Ai?_wuQM*jF7{Y8lio%ynFzG^H3{L$2ic#3I=AGtPdExE z^>Q1^DAcyZ(BG_5Vei|b_zPeNrgkS=qB}_BHX}{#U3S(rVk&?Unac?zKH!iPM0!vf zn;RTnbhU^d3kvkut4G`>`z456CF}DS7)`)(tP5?PU+w~3Ro5E%ob`?R=hhUZ#Hbkw z^00Ee*Q+6wbEJBZ(bm-&cr-wNi*!YDnM=V(-uhK|%c5oI@J4KJiT%I^<6)MJc;Mb> zwsNj0+%1nQ9})gLuTBEy<3_K?Xz@+!riqXOau+IaTIK{GB@=5H4#MRh|rcvvdHaJ(DbZSp1kL`%~Dk-8T z-3#dzbAg+6s>4DO?#R(ukJV@6-wH~tbr_O$eH9BSuxU5PSH6i{IXgoQ14pnXy+2;)1(|AknzYe{gItiimhfcfQQsb>Eij zu)vJPO7H{3xkBenoQ=<<>Ee8yO0>Wf)Pcm;Td3!75&ctNSh264uwesQ|!x(eK-&W}rRxIYZvmp6H)BjG|N6_1EGbk1PPB=XE2X+>5wN zG?;S8))PHtuW4u++U;nv`qoLh6Ty4Y$;#GxCB*MB_)cK&tA8Jx!(7Y3*RU3~=9>8x z$CJF8JQ6*>p_&3KQFXBsIaBv3Jr{wgZl@>K{xd8|MAbMp{_(>jmejCW7c1^44FHIp z%p~x^Auk;Dsw+!Xtk4GB(E&Ks<<_yWptyk|>0UgEgDaEo(EPC5_k_!Gcnj7EO2RYI zVeNHYTHKEis;AwT{oc9ymDUB}mwxVEUn7mNAHDc4Y*l3cG^ zTH9BhDtQC4WQn|mp}NP=)&(ku-^MYu;+v35B#Am|X)tmCI<9m>AD*)ZYlGurMX}S1 zs06`op!m7~5qs0(YVe{>#IVzc<)pAO6821Wme4~qZ*lb|`^#>pV+LVXa<2tnz6yPG z4z_iuIQ^vFf(T!G|9d~c66$2`bA-Sx;qjX5_TO{(>kB5opx|1{jM?`AmiVsiv4Wav z=Hw6)`^s76saORMC+M(f03qUsYfJy(Z7aytVNZm(;CYV-+{7 z_Ippvvhp&Q^`9BZ32(sSh(KVDc`7{uv3x3&uL=~F#ODF=EwrTM8~*S1se*p>pJF^|7F zZKmIdFdM1NyIN!$e%b^jb)wxEjrjZYIz__m5dgbD&vxAp|IQ8n4N_8}T5Xg;v%^XTjk zclXEGj1^btijumtr#j89sl-0SDXgv!FJ}O^gcp@Zb&j9!?z5GW%uBUbs`S!9bx#nh z(L+e^Vuo>euF@?WT)S+rEq~VZ=Q=wv*YfdM@O(-5oP_kL3mwWtMzFhfQ^l0@sSJ^L zchSq)?GGb99)Lqnat9^;V(eT>!UT$$*TWeRID0efUgp;Uw<8BvQyv}NUsrop-wQKX zzU{cc`RMVrI89dD_%VRJvLb3Lq1DMFYbm-)d9R^nn=X&KU8q&zc5%Ju^*f+GcAJW{ zmy}b>Gk*!m1t+0D0Rp7fP}Q+Ujn|CARFDOesG9YW-fvHKuxQIAv48~tx8LKk8<2J$ zybGP!(JuN;HBh=e6NJA%@D}1xpW9SHDE)x|u}+}X1l#3SrWp(SBYaIwinbo-pR4Vw@qxYOfWTTS5V#w zMbds03q|G<^5KJ2p|dzgvxKN95fG+U;F%5GnJ8i47Q!nxsM4R&fNtlfeYIxELkV7c z;4-7kXe3!X6Fyt<6lIWUs~Ny-oDa9JYDcU0zC8UG^1eqT!v;WBT!J|snuP$l zMT4=04*3GL%S)=VnAfUx20qeCSD`(LC=rV^B)fuXIOx-ww%@%DUjfR>8!$QXq(&F2 z|G-))q{WzE4A{(Tzo*D(c%7~V3SBd6J$^@R5kPW>o<#^9>Y1t#xlcMncT$A;oavhY z0eAN+cUe5}+h#$=%$_x4mhmaCRXfmLO~Y@fUAcIYkJs-vHqSeE^~$HDQNw-rAMovDK-1QK1M2?g z16wXT9J?wIsA<#PscK?+dbD2o6Tr5{ikv5sxdTC^g0VuBW{i^rGOyh0Chi^T~|7^XT1|&!5L~P^FADU;Aptah4&jCqA znWD-`Kkw&g13OC%)*?Ksq!lo7>EOms}4D)*plh|K`8bpCU%~hbpN7@mH zGuJV0Vcm07C-jYzu^y0cx&mG;f#$eIK)?rI01M>L?cqe-eiZ=q8b1P4)@`NMFUJY+ zz3eSkGvTrO^{g)s(nQAa5l?ofYVp`Ro!4Pj(_x{Bd}rq^uw)`y-ZfooMmg%>LC#JlrWvMUjqbPqg7x%@?TA-L-;G~=Jd`V zo6-|oT@9Zp*H3sYxEbll zH2h@9H+WK>o`U`lB)aR^BIULGC-gfayUf+f!x{vz5V*v9*NbHHhb^~Y0voJ=O3{TYJ%4#Lij|JerIqf(~| zoN(Zv{|A=Aky#r4uQ1<^kbB)}FIR!on>8W?};~K=_(}`Ezh$_+1 z#73Fi^!w<7t0Q7<8fGcZI4PSG?NMi?$m)5xP>Q0LvlNi*o%+&US*n;RnaFjjquOv% zRtB^%C*-&g)@1>1aSNd3r7E)2a(4QD+&}`OX0qE&iT~_NsZALN%$T_$HBu7~QgI}H z=KJD{=_}94kgRRhZ5Yb)%9!s9PF9OvHNAx{_meK$&%LmqTVgw_+zf+*HNode!mm zN5;%Ee!t0pN+Qqjg%yFsD(-paszc7D&yjj&?mjawetp|V~y?MOr^3%6k@%JS5dij-*CKUHS z!-^^jG+6ArHFf^ntyO5}0|ULIESVu5d(#Ex!^Zslf*wkc3{KCs`Xb z%g4-`uaQ$?E1_*!F-OF^u7-A=%1Dfz6-Num|Me4V=vLfBP6mg#^L&>jHa0eL`({Gd z4*HtbqKANxbi|^g=`vGd#_&5wriK&g(=2R7k~hU)Kogck!dA!7+S$raH%WX(S8E^M2brIpsiWU>gmO zJgmE$e)ECDX!&xVtLN7dNdYs4Znme$`1p9T=5nnM1H~RC9n-wFwvOwbW8-Q?-Dn(+ z=|w)qHebh4Wkt8>QBbHtHR5!I#4(w}tXU1-VK9NRGV2f3x}I6hxWRlJml@q}K!YuG z{zG!{JQA$Z8)M`EL-w!*S=6#OFWMWo?phz9{RbVb_v| zx(GDpPH)OugNWG5?6pkl@RUiAZwlvm3vEeWP%+Pjnpv^TC1hKL4CAXXmNd897tJwB z0LEgiX#KQ7V!p6l9Pkt& z;SOD$k6{@28E%w)+mvpY%Z9s}9C+9SR3-8Y^Wy83!F?EKbyA^v)tJ%m&80{WkSPBz zSV0(6KLYgV?^7js_!Vdtn}Q#r!J>8{Oaeq7&~T(n=dnM`Va=hE)L|-oAgJ}RTD&7PUd8LQL}6U(w&0~- z#Il;jQNrbC7Y>tVSHAwfaQo!TOx6eDTZ5#AIHUetcC(~ZdC z+D+hCxMY6~L%iPS_dd$`ybV|Qzib0v*ZXq*;l3lKpvba&cab+@A3SQ+(?>MYEm1-` z1ZLBmw+tT3#wwT#&F#X5&LNa5mtm8cKe;iNK^Rb=-6!E2-)FWGj!|m-lDPJj)!@>~ z9>Ft1_#M44faUHf>f(I&ztr~k0^FzUw7u_I|21&T&n!U;%k`;-Ga2dW^yIVk`-@q= zgj#4NOt?fL%-jSUJ3Biny#u4Cga+OXR|LQ~tDWsSZ)xw5=T$u+_e@bMt%2sdg;6fP z0fyRN$!{q@!DC1rb7&vNu*>ISh%?9qGXPA9Goa6q+oQm>7c?plB+-fn zu!$ZL&j7Xe*GBC#9edTH#>)d_{06q<89)NgUYkVSEZt@A{K=;k9mpE90WS&>r5UAUH6NX(7qs+8dU0!)*WZ(w}g<7Z|SS$(oDZzmC2hhTU9? zKw?odH_PukU{nDCUTu{A`NJ$){pImGsXSoL`RL=FEq6_0W30yh52c@2diKuV^a0lk zDk_ptHoYEy9u=d~528MFZH(35oW<5p^g|iSZbaiPyYmN@6 z$eQV6QC(<~eK3OzjnYr3kycfWc;sjEgz;v;UAx&IsiV zY)U8fz&%VWn?gEH0n|3XKXk$axDkuE;65sX9pAhvCalRV`dlRq2xm`Ml2kVw#Wpdm z2mkLB_{&?&Dgbq??)26VB;42k;u(;D zieUFs6N4w98f~a}|EhiXfTavw>@l&J%s}N_xl!-cAof#e-=dKz#DL%C?f_<4fI?7! zsffJqT?6eNq(7e1Pn`q)E%jzpod5GJ!EwP$wOpa5W;eCLzirdyK~OWId1JI7f}P*0 za?Kax5)-yJTS&d}2%NhoZ-%mozzE^KmX)N6|GJ=XdHeNIYW_4R{pAeD&Tr_0$WDHx zJjgw0My2}gkuRxFHw#=I7^H(`?txpCe1EeR|7_=)iWFFM#&osbYj_dlH&Zq7#G}o8 zW^D>~^t;|Q3qchACL%T55kmi8+2WFw;+}<(yuaW2Z6$E&b?;}gC%ire{W-lN2M{EH zePx++12QH5N&fuvlbSBKF|+Z~-Grd(F$<}UA=w89Sin}`(S9(;?cWSI{Ga{%389q& z#)~es&iTJz1N>gY0^pSz+d&$lPw&Rx+-P_-F?q5P zYjd_aeab9Zld%xbhf+a7!7>jYIBT#Xa)t3b_kJoNfOJF4pT5X$W~1hD)NYTDH@ z1aW8sujtx=OAD-u+#=?bI7+hu?(DR#Nw{&FkR{rK)| z)a(88)h<38|H@MweTXb33{FTI9AvX>d)*OOslX<)c;LBy2tE3KxCl*2 z0dX7|ybchkt*xD`C*DKrZZY3;GjMra| z>g?DpE9Y^THAB!bbZE01demMn#`pU|2;e+z z4$%22UW@@;#26qNBZKSvnfrCs#rW}RPs&tIEIi0=DG!9A^%>{z{ymLS3mB1_+&~#s zfcQ(}^L^gAqmpC8QsAl3#Sj-1+x^eat}slZWIICDW`2U*0)<%Z{Qc7*?tiGO&F))e zS5~g&C7SW`wf8gB0JksbEMpl0`;_o8DTxr9f*<1vDVHt{;p@QbqTf$Wfv%GP_wzd) zI{%ygIK3GlYl!AMbNP6`{HW)*tr2viGx^Z##w{a`k6{kThRQEhT7dAW6R@?p30ys= zF4GW%pOgP*lE;9-k!Y*mdP>+`J3NR%AFv3nXFk2gbZX}*^i2y#kjdY?7e=ZAlN0G;F3`x zoG2XKwZDd=TY6|=n zbhA?e=PsVizKNGV7(Kd_ebFa*+=-mV|2E0@@aGr46sR~Qe7}1?0NO(gQmgS_g_A}I z42Kk4+Dm9pG7K>ADAY@Im@+0fc1yB6DSG>iIC9n$xJmS|U};=BKSjaJlfdm*yg9t( zphxl3v$K>GQ;sz-38%L5A34Zt*S@E0>h}QEtFN>qyR0g);=wlCr?{oKQZqFgagE|} zo%ZqNyCC{;rb5esB&I(V=5su)B#mk+m!2i)CiyGIPC;|Ck#$h23+9ff zagfmB`%i?Igb{&@9y6s9->5K?Ko3ppao{km_gv9YLKM?l|HPHnWr4#7M4H(DJ$!$2 zcosl=)q9?hlT$9$V41cdE>0GJ=Jh{@1pa$MBpmR#PQA4Gck}*_+4%qFwO@-EcYrL) z*w_>gM$_yuZ$b*gQmk}r!IB}eC!evm;r#7~tA0tDcRv*E0rt2w2t*B#xA(8`(lq{K z!y;LMfI`}umF?fbyS&Hn2UP2GR#vUe@4%CMrhW(YR|n2P15!VT=w1LtT1gR1lZHF?Ez52ETILLo>lC&o*5NKC)PP8*ldyV zumkJBR3gAausI8f1L{w}{H5bAEw(Gb_0B5X9S zEF&tV8Mv1l~0HJIB z=m$=BH4p$)($Fvvn3lXdmKA%vJELya1D%z>zQ+O%Pj^AQL^PnY#N3vLg_bMc`Kf{~ z-%b>JKsw@%4*|{{#j5rj|EV&)-LD^FQA9)$r?rmu*?Ui@Spid|7|ov}`**Ca^v8f= zesg~VOB1ly-@uF@+{Kf)%5CvnZ1HFUw3zFdJSpoFe+!Ge+VN;je}LWpEZ^`Z2^qn) zo@C4)NVj&bpGYm#$+vQwHsMR&?`Sa4!+;A4`peX1K?L6{!)XAjLGSL}mgJ+6xT?M1 z#eP=;o$N7?2!O{ME4QPRVm+&4j5vO~k=3|wsW15MRgy^twx*|2X`2SXrr>;b9{(#B zxH7{hxE~FAAkvV$=MNizUp}a625gE)f5f;e-{GD(Im|3>hyH+{&9^!si;1&jVv1 zN(GVqcs3%6mkRPESAV&RBCtF;sms(#3&6C#6QPdlSdrv}YYe`Q)9`?RpcPc0O;HG8IPaX(TyOm9418@^B&5JhI`TXmLQ_|MQhua@OXT}Jw+K=O5rBoPp5iFSPi^BeJ#k5TVs3~aQ3-_e| ztRLu0aAYSY5%Y7`;(A%5Nu~>vih94xL%n=wfO4ji3x>9~7DmbI_jX{&wrP^?9RwpH zC;uR|fEX1TRGU?Xy$6EEBIA#vffsxmo13L%@5m#1ot6OtIXXv*|0Mvi*1p(gYjvCS zx_|TA{GOgp;c=Ynj83%s!hm3FymydV4KU=uy_JY)(V)AN{-;Fbs`Jbw9t1r}$WpjO zy;C4u+}?F*TvUE=i#Kf~%x}Cw|8reN zY37NC5xeU^j+jsFt}Nh>7WZ}2#Cj=)w@}%A%YvSBM<5W2J_4xEvtN=XlMil^7#RWm z9cC(soBkKCTo5HV93~}#Ov~y$dB81AC2%;D#%HU}SS6MY-0f*I>}QgIR%(*+{$JB- z3SP5q(#0BJ%USF2j)cT@i0o&&9@^xxH)O_VmE%Z+=`lzk2;XDaqM8_I`hLRa;2tLfHji zFVPNo=0ec~#q(2_iNAVeI$_eJMR&fdiZ?v_1_6DNI^8HRt`KRk}_AF-u%;bVu(_VV1Y4*icj|eo?O}^3iTpyWdMD`vpCE2i&L- z@b2ZKX?o`czh~-t&WyP8|MUF+2U3hy9?AJsvvHNViAlnn8ynl-#3x-+Eu4KeEx7aQ zk=(c=8?FHxkjJ!~!kz`h#Pk5WQ}H{OZ!cfpap~vh=c}8!1z#Bb2X4BQ2-@ctbI$e8 z>CH!g-H2O{S{1J4el(iU%z5&cp5fg|D|~H4Ri?$>xy#GeoTSlt>&b-0xx)XHrns*0 z?%CJzdY)~y*6IJik;pak6xY2Ix;<~soHcdg-;C5ZUh($a@vY)?jq3e|4HM_P7M!-q zxv{}<=PBJBYB$Dt|J?49IcaU&rqABK zBx$lXiO+8Ev~EC{SMJnWO&h zQ}VH%&e-FH?>rw}RbAuYBy+<k0glMv8cPALM2}=z zv-n=>^l8G@J|HJ^_DqM4bHbc_q#ZPp^6*rga6iz$kJd`M1Gg%oH83xQM!bA>(*VRgE#g0q)1HJNb$?1K_!&pEES@|@SUa{`!5-%$RDu}IVw6sMD zeh(3*Ei2Sdc@@t22pq1K%RlsHKCJTv9{qv%iLd#W68iOtc1p%c&r4&(0 zP(q~t=T0cP?&^E%?!Mjs?&m$3Tj$<;&M&{;-#Pc#!TqWm$+wZOS+i!Nx|)*Cnl(hh zYu2m_Bi#V@Y`G(~4g6W_rlYE`Ci^kd@R~IzL)?`O-JQH`P>#qo+@gxOU)&->c4#+u zZc!y}5fNoaq_sN+?F@c`{cu~PGyZ@!+85>M2UPlcdm=GzDDX1aS0gkG9dNUTJ0fwr!L6V@R%i?YiNWmx?86IDc@ z;I@$HqKDa$nl2W+4*kTCx!M_N! zLl@;scsu-cwjL-1(hc7l?n-ww+R+{5^5srzw6imifGw!fv{s$1McX7A2Dtb2}jUR3J8=Z=ms};AG~TvxOzbSr{IqCcISsX zqHKX8K^ozRw1IYm%kU?FU$*!?*oNF(;J|=^viDe)iL$5L%AdQ!C`A4PSB{iV8D-W zO9!`zsJArkAXZR7kFOpGbtUL}!Xto#zj%t(qY2qsb>OpviE?zyt7fV25JojWeOmWgY?@`9_NLWoKOe^ z<_;nQ!WNJpAkg^11aJQJP=5*yp+k;vE2N{sr?}!P3BySuArysV6Ot1ZxD(0|8XkQl z1_5`5<9FllC0YSyuy5F`8-!yC(q$3EqJXf~@FoSPcsUK z_#Y|YQeOoI{~Q4axx`mK|6dFK+Y!b;LBN4mghO#J!4tSZ_S_8;h2J)?IHSD!Unm~^ z+8apVtv|sVfS?}RY2S~7SGP=X0N-t2oDe|$r1r%Kz$PI+`|sMvIwM?1|9GcLom`ev-59c@vEQ)q&Ldl z5TD0Gzl|UOK>!OGfGs5`t%X{K4k9|P*H{Ua&|<+5r8lh5dwXIfJzBK(aEpT)6gibat5EtMhLe579F`N zGWaV9Dx6$>Hzxztd^iF83HAgJ+>7F68n4(AcxSTOyb!>U)#gR~E4^NAU4BHNe?Cqx zjJLI`hW%e0^S5zKi1#8yw7Ae0+B8}n;QqddmLRX+4QUCM^}8{Q@Sh*U{AbScD?~%Sg&UhgApFhs{9{IvG{G{{%pC-8>p(!v>k_7)MArOK) zV{-&Yl(hqN9GWGAOQQb7j7b#<3OYd+2X1}f@WEAC%Bp!Q7fpg_DGu z%&$@uVFJ>y%F%wBiL%<${uToA7v(5;6Yxi9hAyaKg9ILfboD@jnPPaXfFOLUBu@pO z$Md7zT?mH(YP;$TfG&XvJz$m;wsaqD12u-jA+UP)Zx;RsXW=3QK>Kef;(raB^FNZu z5`z2h1_J~R=N}&o5PIZ~FnRc306YHxGJRMZ^|hM^2GJS`>R+L#0OyQ<3_1PRKSM+J z<^Q1Cr0@D?5ovtp^Y=4e1g-odgq08z5`tC-qz~v`7dQ$7eDGHyi4S7k@Gc$adtE%N zKu`;=!Ci@qbFmVQV+9DVe3c(p!9n-@zYW}fadU7>Y$+h2H4qqlIoar6z4%Hgz~&F> zg{U+hGyk4mNZ|A2{~d%#MD&j%zLb#G7#|mGE{BuzU(!v`tZ$I%VG}zK7X*$T`-RTv zD?+M;k6hiL1pvSnf$jaI0l2oXG#XycaoPhWN#LSp7YqtaP=+pEt!JMOLk)prNN*Pu zG;iz^6ZWr?;HCwC=`dES%C9I1xBxOK1pY}(1i*OMJ>OFk$*-XFzaB^x`6lr3g?R;U z`VDh$@JUR;R5)OP!C5=BQBG)gXxWXg4DSDIfdZaT{8WvAA5rLqp;uTvod0pb0@TQ# zUsfdYZ|S80e%dR9%R@-&_a*B%F)Jx?**CZD1n6TuZ_l5!iGM(s{gx{S?&xo`{wZ9N>ax zTs-k*RQ>l(6!;0& zB}GD$2mp@sDJWcZs?cYQ|MPqTJi(7kjlc_`0L%)l$_lY+pf<%8DhI$dF9faN<$_(F z>|5&t{=x?QQz}~sTKF@wr7vy&-#J_Q^DA3$ssI*0{-V_C3*r7(a()PDqsHpf@>s*{vYRV z#RzNEt)`~FIoS>GrU?P-U(YlWrq%wJlimKyQu;r5wisc=zi;gDHthSx4qu7%&o_4X z9{D4T-Ri=nUxW9opjsw7xEoko6Z*o!mj>)pNd`z4J|`sq8nK^JFd;PnIA|+W4M06G}+_5}#~?R(DbIkaWjr z8Tz7x`E`9T#u9|DkF1hre7)Z)C;jD&Sg5Gt%Y_I2%o(wNz+eg!+9Xs)d}AI10sUJ& zQ-`qcFB!~luCYXzr@d-4v3SE*`1b#LF_zd@^Unz#y;{2nnzVXABnfKxKQtnEc45_s z5Tc3S4YsZhaaI+F;J{NW47d;W(>cy)ckp=z+}bSI=};cu{7S>;JP-fjhR+Bp(g}`& zK4XMk)I?WXm+%GvwEce(4}DUrpGcs6iJ|_DktnfmmXH2tA8Y!x<%RTdi2f?sT_xv2 z0L&n8`at2q(m5X1)=0Ni<Gr`$}hjH$VX*D=b=J1g#SBd0~j(x10^0{r@gLK`0&jE&pdWzp-A;k1{`a9v!=uJ7^nhemhaY%glF$ixB$%^M&h|Y4@KX zTt9hO38f|99acin_|G3!JjDICvl)1!09g!z_4piyKucXgUAw zcab|`Y4F*VRsZ4r3|PDDfL~eNtk2tCrU455&!&I zu!JRn|6HT=DR=v8#ep!&-xUYK5()o!aUk%`f37(EXIlOz4#Mw>WP}MW5br~Tg}!zo zf7_|VAXEN5lR^Hv6#owj*Z)2uiO;h>iKO&DC9WZ?*Zk*Nu2tyK4~y0(=Z8gg&1Qo*IQ`aIwR?zxH zXlE-lSWy8x?-onj$z!MgfHlQ1?qE^`I0Hx2p-~8E*)VHI6p$zekllEVf<+x)&{WjrC ziumu2ubxMW-$nT9yd%;EY5}T|LRrHd-l>^DR(Xf)_;Wp1G_X;;Fhf}l?e zXrqC!eQ^%Q-&tY%#v9n}d`nt0#oeMn2 zbgstTpxLISoDv_Sbon9|OyNQ8))d;XhLpOroww8w8u2^buF@bKMd;=&&d)4Nypes< zBYWQMyxaIfW`LPl`&8PkhzVoci3xLuw`I$%TfB{6wVxZ|o8z*()h!#{G?yRgbr`<8c<`Od`6O}(kNJ|)X@ zl2Ni_egicN-+BMZV`+ZlMZO!?K50mmJU}HoeNcMnLTJm~x%ttY`g4BcI~U#-obL{p z?U~nl7|o1)bb6-<{2uXB=aRQKdl!~w`>yC^=!>`y9UOjMkh8p)iY|{8Fe|RT^<*!v zcB;gdk>|dnSxF9~)2#=R9CFI^Z5oo<`AzPgj$rXsqmt@h!ys;dy5X#hfy#-khh`r4 z1+DPQjOB(-kN23cc=xZB{@5RiY`P$CB%CVYbS65*t9e`8cYj3qBdk&lX4s#bxJ(jt(cU05vLq4|8Ijp#v+Mw?A=UlV%jHqq&p2c_f^*qdi zmS!{_obw&odBdWjyxZ-GED^kPEWiF0X!CK`YjeN1+&N_*&cq2vBG2y)cs2E8p8-*d zv1hN3t@MX3>lp@e1G9h+Pi^z-cv9_ByH}E;iFG=6k7i!xav4l8NX)wXOyhaUXm&}u z?UQr)RT~%&JKXe}9oHDSeyT!Oy`S^c9uC%k>DV;4$I^NeMZznfr!|%X=ZCj*ADtO# zkKgSvf?!%PcF4L?C~n`j|?AF2to= z?>bD-3GW9UYGxVVLA_Y?WS*gbB*aOe*_J}(v*=v}ErV&&n%3}+BUqe4ReC_-r3c=EY?E)RL zeD1v2g_pYBTeUU%y{UNnK?^y=r$2bVDIAE-P$6PZysUakUnim_+oB@ySubd6$7oJP z`P~<4{a$?m+b!=U=MgE03@@lKI;AISs!i;?mUfA*fkoE$`rv7a1G|U0o(8Qf-Pu@c z8%4)|wD_FQYleVp9-|%hDe2K?#2q#W=w>v5f!xKqmNPg((3-+57d*t?8o?AunpFVxol|2~rWz6>T{yIImi!YefEP?8t zzHrv7*Js4Md>~rpRkC&Tp{S=uwr8)T+X0c!0q+wLb9=Ve??9bFPqD`xe!jSv#J=eTch}lZIZd~jlH*8GjfQ=s932(00zL7nv6xrCpej7xtL(I9`!p4zs{r}pk@ zOqJ~1H%-q$TI364msnEh)mZUpwn;Rx)y6$j4Wf56)J%87YLnS7PE#idr?+bov3tiL zUQ$+3N_z9aNOyL7K6gr(_IUeD&zga~Tb0RGX}{K6oNa1hx8!P?z6U8EyIdwCoq(4R zqPx&_)%i$`uUJP##)5nU5N-h7|P>*cZSH<9CWZ_XzT zw_dA{U=6&kuaxN@1Fv%j3ih1|Yi@2w^YY}O45664emPo)UUHy(?saNH zGJN=Q09B=>PnxvbRpZUB76T6g-kkNCj-g0Yde%?v@fO%K%uKsMy`F*Dl|rXmjS?Dc zc{(p)56v1r-e5G4Mr?UmJeX)QA9t|~veR`+m1>qEgU**Fh$(|bl#h6;QT3Sm3?_^O zKHx|mzU`4h)m-cZ&SM8XB*b+giveruL*&*_^f#-r^2>}DUS^45zZ;vZhI1~Pj{_&5 zu<6#DjjOEw^)mJ&5OOQQK+e$WeeAt30jO8+W>T(UVse#~nH(4*(Sv_{gzP&|p zZANDP@q0nNK5tN$+*{cA4RhJ9sdbq9IX(5CY7~bD*bn!V6hJOua<(t19(vH2nIi-`BQ^4^&KVukJ9l2><<{anRfNT5D z=6lM}vY<=fho||xPMLh2ihcyFFmqeeRQAIOu8%XF7McSOP7c1ik5xPE=Wl_3?Dm@| zJ7Y3)DN3N_{2d^TdzYql=YSN8QSq6P8Jh!5&l5xUnl8v$gzSJ1iAae#bZGHtpSf+C z1T3T1Q$)OWI`Fob<+(SSz#&ejkkRtgviOZ%TnQY$l9a9(K{bz__PCOuPtHU#qH^xG z-&o#5o3Ia^7C{MDwNveH-ce-@xc%%_;EbT|eUFj0<{}jE=BPJU^Q<4*^NpQg5WOk% zs%neAq5ONK?c!JIMlz)6l;WL_5fGFtkq}_Y_q{K5ddXHyR zCB(vH&bd_^bSeS^Qh;iEo>!g*svIvn+pT*pXxXQSROoS!S6|sPj+EUUR%(_KJ_%OM z$IcMPWh82^Gh6|KsA2gr8bnXtP@tmV3;JQSqU!8~&%jB^#5d>s_N#AtC2}1za^u>g zkM9QtfkS*^H0aT3wtah=ONdsgMAo)pa)p%^aq}HyuS~|ei*7Bxf5<9g)3EggBQo@E zkLUA2rc}R5{?m}-Oc9EpWZi2j`5Z(+Ws4u4qm}EcG>M)bO;kANLoHSx3fa8H*@_kI zh3c0zQP!OkptETVRm@a(!y@;t09#qye6(MF`lHv$ZQ{2}8l)Qrwu*h60|qVq&}EfF z)ZBO9s_rK;DR9ox&jL}J(e}*njf}e(*oBKZgF$x|>_-^-8+JpUC!V{8|Jm52k8`WG zWC96`x17XtN6|VPvh+Z{P_2BXSexJ@ralxUlypM1#I!q{-t(>4T*@{n$Tn8Um1FjK z6>E=FLXm*jl5c?t9pFjImgh&qMtzFCCbDIcR9SqKsH9)T+;mPi{#ZUg!u!H%*qK^-1r75?|7R--ij7oi6h?W^|s+qkQ!J=cD|H8Gy$o2+u(}_+qubzWjBqG&m zqR0{xl%wcwII=x?j;<*4pKewa30n5&ZXDW@%X2QevfFd)`bX<9#iWnS^9>WZ zR(|UJ`X)|S==Rgi)x~eG?=Es{9T7iz(pIo+TIWt$Gn!PmkG#WXIjDM*>d;Z2$-3?J z`~}^YcE>-4)fPOsv#WF<)6oARU#_KM@L2P9iEYDt!9&{10Tl3{I>e-vl1W$;WwNcrUq z$H=+8rF45FYGYnT36>ikld0ZBRChJ4X0z3)XiE>c;`&7JaC2>LD@(H((W2DwEpPlT z?MK5|F0(ciH54>x)~3ddFIWc-lt>My$4{d(6%@lMo9AMsmq7T}>c!sW9~85J@A6}j zQ+laI+eU?0l0&I>t4RtD?Z5hAGC{fgNrJ}8q|?5~Pp)tVoA^Tg+_`rVJnK zN)sv&nivC7{k^MOWbekY1lOvw$+$&Q({8417kIdMueuawLNf?&xhy;1-`!GKbE#T^ z_?V@FrE73*-i{TJk=(T8i=!!mV#4hnJX*E%QR8$bj)#aRKI4u#-H4WN zqaWY$?S~&@C4DtZVs<{*;5;PY`;dtIg%)?P<2klqV4<>2L7b|`6*I9lLyU`NvdZd7 zwWjQ_8k_HNJ#wA3KFSkp(T+-N-iB2lA0)p+p_gVlJNsUYr0<_U#uqac$E`VCVl7$( zMrxN4!t?|QOwO(n#LPE#^<qx5v2^UN7nM{`prG!=z9~A3}h~&`y8Ysvt7iZD?&Mk45vt| zj&NRB@L%^D_Ly%DNLrL|_5nKbtulEse22Bx_?H?Q8BQB+o{KSRFiK+!ElfAmiez1U z6@S3}v=$rRajjg#EiE;~`QDdk|f(1?ezTF_nb|0uzhz=tuYvcm5Wu%5K9_BH#YwBe7?2@meD^xs_JeJmNbYxDz z`%T_M7TpZW?4aq;lO)yg>r>9(dheod*{Q#~EZp+qlwNF?`p*wN)H z3sj{P$(#$(O>&l19hyY38{H`8>t55GV1Aq(_?n)6lXzJ_nz zctO@cHq=_`Gc|4Ens?YSrkSGGn)bwAx%oT|8;;+AT5Oa$50cNBOWx{i7SIav%}?H z(-O5hyOt>OPaSGch$7CvXdN2)B1D^_!(n=>&#gKeZCi8UB$pHFP{Mmn#+;6mbR!8z z*LoRPE#lafBVotxQL#`QvtHJM0Sx4D)WwC5J=HboXQGKkp60GYszyIOPsDIJKrM!M z&%>jY)e#}W@-32kjRPZ)8+0BK)e;}Ol$VOU_{Mf@k7%r10jX3yXB4B1m#O_zHDD_n z`gX|3d~oM#@;ex=46|x{b^auA3))nEtb<(}p2)@{Pp9kL`*4Y**_zMLBxTxYEBm$> zS~X?iNel&>#}S#y2!FW{w#6bfwurNaT|1*`a+}y3sB&|gbhZcO##z2so*0sJc0#*S zaLh;bzH1bEkvCVD-r4`sY_W+52Oc16*>D9Krh!Fb7Ah35jS$k#bP%krE=2OWWD|@qi$i`+%4CyG*yR-9!pzwJ=SF zL|qM<`J{2b_X3vw&;6%j$jKQG3u$@@RkyGA915SMp?wvpO%fK$X?VJhn&SweqElLt2R+W4Gy7RkNQ*Dh#Tb4|7hctZ8eshZdc zN=>RMW@{DC;qrZ%@-!09Dq&i#=jha!Rfr!v4nKJ^WPz&kluyXXkoc(kBF|?Z`))z( zOjut3^x^f9N5G!YcWo`%$c53`IC^QqQLT|X&HQ+*2~nPJu2g;r(kfgnPqP6*R-IWN z@i<*>jFgKastZbFIT5p@!qzXXV8Gfvo!ev`renF}66X7HfM1Kouq9b3mvS;f`8eBQ z9@FmZG4o+s8Eqb6o(@y4808~pMLU>zI1F3@ketC<4O;dwaz|~s-UYSXyVSr%T~)QM zI^d3Xe8ljxhFoI%FAkpIakfNaE;ObjU_Bx+X2(liaejWYxtDeve_1+{YWK(|Oxr zYJNwNh3h*35|C@C+N{av%J(u*oh>3Imv}d?tP;_jJWJ({v)nOzE#JCLt@XG-J6&(Y zaGxhQ#{M<4Em?Kkv9K0^TVz^EBc0)wqr5IP#R>2<`Rq=fsN`v9PCHh5k)B4+mQ3jh z``NHKIW^`fX?O|}wDj#|mvo1Mxd-1}G=r`u9la~G}h?$cDt^oLv# zYKI*+^`<&N7FNv0;$>=XRI83#s*Z#8u+0X4D@Q$WC#pA|48Kv1#5WC*+hPxnB+~FL44z5 zJZ$KgjJ9J*%2s>tS!%gG@`Wb1pDisF4vvwN*s+JiM^)1BqfxR^x?wHbc0S)2rDuu< znmLzgY>%64?YwR>&E~KtG+~=0nx`y0258Nb#8elb(3%2ahkrBgvZeY1llvFXDyUz`AfXUhQsnVJ zaAVxEbPv-rOVR35Ra5%Omy82%Qp)#9@C;|KaNLfNNG{M-~r}^77Ct@EuL$%=n6xP+VYF`9h0`DqRv>j5c%f0XJ0Y4l|2a~NPrt3^Rl_kYI+@J% zTE$xZG`dzzrmiH4Y1}+JG%ql2FG1IOCUaj`Zz|frw#Sgl#jAL$PT*@*^ZqKUaF0z6 zobm%ST550!A9=MVbvkcE_#pjRx#-6|HCKbzF;@@Ble3cy5z9~b(+=$t_T%&1ljN&R zIcfEVg5loHwEa1{{A}i4LNme_VFw#S)GeCVN$3sb6%-cA@Ff|xmnO=UOY&HfmpDsc zfExBujqfeje(*57wnn&xD*0W|>$MUQ{-QJ_(9L_XhYQ8W}-)nXWlN-}XookqpBaD*PX~pSc zkl_$p*Wlr74CIiAJFia)yikz^{0>HOAb zlMu`>eEvuY_biN#qrx?v2YB_a%kFiUHSZ=X!x8=rPA{r8C@V{xbr;yI(Du?imO4m&GG)Nr0m0Hx#~j0 z+i2c?)Lyw%vu`*nb(!02-6iIV*h;(i1syfnYC_zRbZbqHUjF65lf-AJlujJszqg5k z?L{NF&~z;aOL=T%^X7N$wjXJr`AoF4y!KOKM&ima8W1 z;$X^K1M4jvwz#*2?d1zbvJ-}DI!cW|4_#8Oq8nAm z30fla$oVAfrV3G13?;q8zIT=DL;$gqB5b`u(n-*OqVg`PRb{{I%wg&NBkPXaaZBfP zaV0YuJ#1%%p>NLu$wAG6sI3STwYI!R}w%Y49be0*=O7sL{BwJCILj#;_Cjh zyC~@RuFo$Cl#FqllKqgJ2>_P$cN0%Mbb@Jxrw#1ZXssyJ;9*!2S<=LAat5zg+mJLd zUNRwI;r~9Y+~ebmi2DAj&?^92sc)aGB4Mq)l4QPXw%hI0ayF{<>Lj3#)BvzaVJFOR z>e;Wo=hW$joQmaLfN#hGko@+R3-aqA?C*+Zl7MtyAlP#yXk~d4&}y2uY#PM^a{=Zj z-KW{IPJ)a_{n>lQjA4sKL|i&c$}%^0Gq!RMQo*zjFdKZMnYW@#0m+x&VeBwCceNI9 zT5KlhvO5roNVR*2t!ScrVKPvXj2b)}@y>@{C$0v)Ptb<>+RP_mH&uwHVj4lJ%>fZI zd*@m-1=SZmyq)sSnCE=5cOlb#yxS;93e}Q+bDli=I*~=LdM;6zv5KO|5-%rQ&(c79 zW!J&$NHH$XU?0+`P-ciEWO8c|7V(`P+S9AHQhQ|Wy0(*)$Bt?l&+Q|2go$ZK!q&D{ z>}W2ytG%K{atY!EsHW)!On0O$4{l2~x@7|xL`R+5rF$3ODWaa{UunC3NYR>jv^~SX z-7I1Hl6FV}SK}mO)Ud@|U1prGayO^t@WNXDnxFPRI~ifrZ4Bt1mpGqF|YjbL75G$6_W{O+|%9DIk0rYa%&-2vZ7~_ zDdt!jU_j5zS-+}{E$0q+wDX$X)z}yJBkEIZ&kdK9Yg`9lzebGJTy)iRjz7|XOjdF! zgLr4+#8zOoVtN2z!1j>YE6shhL;F_XNB*{{YT5%sYiX&@80LktUtQPCVHxFe`{VX9 zkJgHG2f5n=lCb1;LE{@+##4>#MzBAGVKd2HJ7m-T%pNTZxRE>RcqyvQr|O)B~fM|L_pv!^<@|c`n;=v)@)pB7}(F2gl*2 zi=qAikDBiQopW`TB#vC*jz4%DU;2K40%TM?^By=Uu1T7x#M{0JdM}P&yx00mD}Z$$ zIU5YP6qWW=!1yLGdvzbW+hs)oLH5he?_bvFRiHbw&+eHYx||Q_sLfr*A}j^!LJXMb z(H+_=FL$LNrt7B#v70A}>DU>lyl7axYql8FP7k-XcC5*TY;)^@eP+HN#*CDB>wIc> zs$SLZj^-3!in@F_u628B1#S~Iq`M1{&pI5DQOQjAtH~u$ElOl;bPBvh;Ms*FRX!*U z(+Dl`GT+sAa|XkMu|^y?O$CN#t(60-o2%tE=b9Hb==Gt%0}DwR^VhIj&Koc}f6x~K ztS#jR)vTJmuU}S*bsxXq*h{_vm<3Ch+s4O%E6tJW9?P<%5rS={j8-8E=(mf%K>cn^ zuMar%x$vf4?}CpEpU3UkN;bXm#mcVSQ$ubdX#%9NG(5_&Q!Nk87L zATrnxdfDi^4P7)MT2cV_vLJpNun)SI{FC09C~z;_4sfqf^G78ggy~7HzR<_LNAmLe z(97*QOn}|PziMoyrAwRfn6&_v*8ME}dn{3GP|dg^1$qeos+_@9{dQbUB+(LN^J|`% z&zuA|L6c??u|jvn?TD3QrP&WmknhY0_hprnae+(odPYPKPcU#opgmxyG(h!9!0nh5 z2EXda_Hsk)xjKM~(^XDx6Y&~1MYUazd;^G=3ItzKd5fFx#eAh=o88duMvmg@s=X5m^u%g zS+3C&sT)cfw!m&~BcoIkdA!X|zw+MNJLX=dDT0bNc0u%VX}D~#}A zt963KLman==pt`uhpZ>EIYn|D$m9hVW8`|k$QrO&e(=6PdU`$CB8V9VD^`}!7uHc1 ziaXhWx9RXiQ*Ij$J}2pfm^a$i6GR z5Ruy7H1=ML)bVz+$|UHFTELxfKX)oPOUWR@rIYIjJF$7WS`q|l-diF!uW7ANdvU;j zAdfQ`^1ahT&5EE7h#f$4u~`MZ#u8nYq16<-?AEZg1E-$da8A~wrxS!8@ns*L@ebKc z-h9GSRce_Zp>Y(v+_58f?57lfE(j)%U4*gI@WMWV4y^?|3;SZ@S_x+UUxCE-U)@<8lS@LJoeyp3zI^Zi$-fW+v)(Rp}kGQaYx1SyuJ&@4+1c`byj)@Og~Ruh%Y_stUJ^}=AS!=Ef`dtB?1uem)1t)4_#_illnM3e!TSGlyNF3M$CE zVSp&sB^a3R1oU_8wex|-aAdb7gHAV%!vM@7xZD|42 zXI(KX@w8`_9WNXZ<2M8L=@lrQiCepFL+#z?&KXaZmKP?V!k%58(`RjEJjZqbaxiYM z8z?EL1BGc^Zm%1f_tBfW$m^#`i)%TuT?@az3rZ<(-?8Qbxr;Mt*CM^-F|Le#vGH=5 z_A%L}>72A=NBLxzKMrV&(*ZLi+3S5dt_osIi~)b^wNc>}oB8IP)YGH3g%L5cecl5b z?-VzJ0Jtu3ftgO2Dyth*J3v(eqpaPn0}oFbz|>4!Jv{E?eE&TIf`{v2jkCw^{lr7~LFT`5o6 zNCwd_?IGa{r!k-m@dj_AcL@+93$wF@{xa@EY*4BqRj})c6?c6Eu|i_5noa>{Wa%l* zEpc_hG~G|kVUZ4WycapIO_T6)Wm}S3PxO|}v`xLLiw|2T6tm~t!}a??EQY>0!`kv% z11k5BHSILl>?hlvcA-PvU`SBFvb(o8Ph>-8N)U_kK zf`DCnWD!2Nhp4pzlW0CSTjr={$#gNC2IDdS%GO%P-=j&{NVFam9hKb~)?+SR|6rbF z4{%*o*7oABp&?v0d>UWH2@2CluBPqfDUS}NPy4_f9@AP-lcH4mX&fsEcH$o--@R3=QrM3W;zA z53*f}S=jm^eY2+@`+Zbn4HGcI$rRV`mDtW3yda96dv}8PfvMafcfm3Bons#^Rkl4l zAqXDvt29-Ui!uMGOAlobiJnyCy)B~EEEtWhrBvNo`9$39VZ0d`i<`jhgBTL0NI|z4 z`40AZfjptMDK%B{)Wexg*@zb-DQxO&GgRBe)P|_5pDawY1-b^$?3a0)-uZm9F}7cG z4(=J7d!z!Q(ELHh+2)kx8Z(82o{D^~;BeZdwRe?{%feH zr_k*bMcgPT+~M~cf5e+(%3mQgJ9DL;^Td#@+Z5*gRI;%3O#$!y+&+L_&lj_6l7s47 z%LY#M#W!=A^;!rd^6;vG5=;aj^nES*L9NFQP-k_#S6$7x2uf^qTiS2xY1XmM1-Hh^ zW#4!aGQt}8QQmF6TLIgQYXQgb82L{40S$P80V&-F$3ae@lXD``Q=}jn;5++h!o(=9 z667za4KxDgJgNfpN{V+2*|yzl1Lc@EPq0FTk>=!Y8_!Z))Myl>WYBEn9IKvaY@~p{ zQm_-q8V8w>&3g7!vM@0Z8licR18`}JJ{)X#I=PGqo(43p;Q7G$8&}ir7n7(XDFKh0 z__{IG!INf^bzp-{+6^umnAUEombM;HRIDLYcjn%$m}Ujdu6UiUw_A=!$XYxF8mV{J ze_-!!fW&BC&tEV|#bjKQjjpH?4S{x%vCd8O?;C)R{6`U(*$`84;;v zTPTT;>O@co^VBkQqnIpHcAus|kQ ztIqbSYE{G>(m~6MA>!Ov#US5MZGPPj==(0?(md9*VT&>4!_CZmny|?F&9INa#n%I4 zD+qyBAhuL8y9OxLHvh7iJ5W7G*3nK-^mnJb$gr+-F0Af;>KWB!|59;>j>9iXx=UkJ z)wnxUyQ`{M&0TA@=#k$W?&&S_t3TtAeM36@m=Tm54=zqUd6GPvl$2BSdfOa8$Hd+U z($#@NsvBYNL8WkWX*H;p*14ofWogBl$M@2g$AoO<90-JAY7gqV7N&)jCUz$IiubWG6;cM8>36V{iI{qje| zR-Xd}mxo5)Gu(=;{8-B&A&YyMt~E}~$JPVGlSiXyB?F&NGHiZm)vrRtNwEP`)W~Ne!W@wF2Aup^I|&A+DIi_E2SDOnWG8Np|VN zc!_UZqGr-40Fv~ixR&D@)nHTubY&;XFpd#Y>~%8PiscfrvIgkczQ zhLMT;Sf_DVC@ZfEm6qSiQlMfnAiQM$qjaJSUoe0!8|PVe4isLa=4B;z0oZC&18cU+J~l6^ zIA1D-lrz>4;*goRE@j#Sei~<@*C{$&_J=$KtC6170dV(2HwQ(pD#ghOz#lMor>oOy z-|4uhNK$6H4MwVdUqlk%DeS7k(mr)-wa!`bh0zG>JTUWrUjtz>wgJ>uomnQ5>>kWM z;pz^vLzhqKqey(Bhe_UUqE8lhm|Kq!LHF*tV-UKwDzvZ)M9jKs`&9Q_ z&yMedTvpN{?%Ag4oVY8DD|y^!h_eBx3i6g~JA~_1 zh}8E*EF??a^XChWCRP}8A&K{lj<2}!KKvLqiL82L7L@eeNcC#IqzskVKLQc^4PxqiPf(Yi;ov#>s!pSS$6{)-+_~qdYiD19 z;JcHl!f-CXEMm|FRBb1M{N0R3fct=A58w%$*5x|_mhm@5Yr39X#+ z!>L~30D6gn9$e+}{%*%jAfC3{HgOXH%0g-1Ri_AS0HOsia8`yATct{qTOfNszf<8f zfRRqXmXhx>V`B^9u%6;Ao4_b+T8nPnMkdA_$poON*eJF@W>UXHlqF>!*_~yNe+fUc zec*Zrym{jC(!waSZ(x1J$}%sg$4yxT<_-fCjQ()Xy)cR}2NQsl?F1yllaJ0ZOIKnu zvWqnYvtLgr)T?hl%T?w#+o4<6t8iwIKm(7=@sh_678bDb&bwLFuRI@wBpHr#4+7i) zd69GwIV<>qi{@j|-u0L*NS$Tg51tKHs?jsGN(D4b7squl-zCM*0 zbBZ1HR^Jf5zU;!8sH|3O;ytyMk&IFyBQqKUi3>LF>zU43L>fNkj7%f8I{O?{MnjWy za>6;ih5+p7*K!-BTLcyM6Gt**UQFE1~kQ}zXwFZXH^>5kriS!;I0bZik63)TY%%)PQWb;^9l#yLMw zZ!Pvv&}+r@8f$>bUB?s%4EKFwVIS0UB$p$OKiy~QweOpLkZfcvrI$fjEQF3uxCODz z5>1xGAJ}WOP0WrB$YT75wVG^+YC1gu05d&vH`=t?ko%f;Do@GVyQxruc4j2{z3%KG zRc7v``5E0yk|5)KYIphMQQG?32`7o2OW!|$X6Oib6?xp)+{06)!|pKi8Z!HPcRUrn z0^pFG!?ng`b8IxyWa;rVjC|(M#IL>392F>$Oh2yB0jj=jt}8b!Q7Y6_cA(Qc}wU@njx zBNTVkU1s`qr~%B_iRX-#QUKQcCJf|#(M$l8)}Ul2J6Z^z3F$uw(smgiFw5k0W;Xz+ z?Qi&j^f4hhHu_|;q##+fuPSrtlfIaT?lVv!y!~3v_}Zq6iV28j`p8($U|Jrnm~G!LyXFS)dTWeU_58pokeXQpN0VKI=*pRFs;hH z7tEM>W*fyZ=HL5{{K}*m32JjAfrp)HBuB+ON9@4GnIDPw%g;9^%323s7 zyYS`FJkLF@9Rg=}dYcZh!_%LD{^<2&N|dka2HOO(waK|oO37s+V%Q@$CCX?~fIO)_Lfr@-&k4I;F+9Nn!D)D`#=g%~h zA&&fWFKi^Me!1b|i+5oOb~ODLHVUA-9}-R#f!Xb**-q@Z92GAX*O}B6BX?oDK6TXA zYu?iB4%|${xocoK-s44Y9)UofVgKcG`k>%}8RO6d+Gpc|z$03DuG@@x9Kp3m6zL0h zL)dTC+2c`dHIKLtt8g<&2!|9tpIZPJZEFeX{kv<5gP(F4!Zw*)@c}47()NQN4Ar3+ zg)CDab&j+;nz%S{VB5Lf9`^0r%@$yG+BwDihZy$CfZ*zF3c1oq=GW{o?g6*dg_5er zX0HHw%sD+|+*1Tvs1J)-DnCtHok;9UvJw8`Sz=)lW-fUk+_tEn&THuw$SHtfaxHvn zQU!c1JucG#AT*z|-HQq&;7^hn|K(4xJkWo~pY+iz=MXIpDnO)jA|bH(!gUK!7LQu4XkM{tJd=;8i#uYkts?%b4|s&Dwam}U z2PAq|03<%yWqoS>VF!_$0J;7)r?`QO!StT2?{t%)oZEW(4L7wz!d@T`fP$T2i(GGG zr!a`=+iL#7Jr`p7NWtXpIo0SrlWN$S2Y_X+Z9j9u-tn6S7>^9&ar2IMpW5Vuf(weHH5$33a+v_~a7qz(NKA8mz<>c3P!r5*4sqgo^2`y| zK)b^)lMAH$3B>vtVh4bmnIwM1!b?U3?1@*mYs8+V>DEbT78@d0A7w)vY5VF4n3J<@ zw-!LKsfxshK#K9C?Gsxa4a^)-?G0njBh5RxxpD!L+*0B-aZm=x*tbX%w&3bDkd2)L z@qUUDhsu$z%9<#8idmL$Jz(P~P!8LfL4bReo-mQnlTQue%y=Q-m~xv~Ef-9f$iUP! zEY1}{;2oGDn#ENS&@6@OTt-WPW`Sni4W`H69A8Nw)B@~}0~IIS6iZv96BnpJrHb## zM#>D}Y3W)zqQTty;6ipV!LgewE_1m$gjmA9P3^$VXFL0c5>DyfH)R&}8t-n}4QL>{ zgC>e7VE9Due5&%WmM4148JWZp&VMTRc@+J#j@0WQ9*#05s&np}7JNE+%6A%s`H6Cr z&#AHb!&U`nv*R>CK}o9$uGUOHInAa)Y^6v9;&MOPtw4@pMS!7b6JG=rgv09?5S3|w zf*FmFR)`TYD|T-l;{r^^PIX3kJ>PacgSxS&x56qY_^VAZGxbz5V+Y^1gNcI%pcY7E z4onamgl0J#~OVUqxZdnElPO*d@! z3N-sjb5|IkMT`K7bDo|IXbr3QzW-zIy`rMpx^O`$3IRbvAwkJd1OdrWGKdsW0fi#x zoU>$$oO2W;D2gZ`pddL*5Rf2IauyJgoCJDp&ws|fef#eoqaS*Vezbuk-Tn%xmw-L1Xx6%GeUYd6k0yg8#3j%G0qrpGn}i(!C6eai zBjALB+%rZ&ivbB_Jz%&@1yh}e?e~wxGRlBQ{0jjH2JvaPjcV(wL9k=h!(H)`h*>Ey zpFq&&LDDN-(=c)i;A}_%>_(aL2mvK`>Nj<9wPzyE+$n4i(!uMc_dwyBh-M8iO_=r` zYy7|{(dN}VvVvaQpD z&0Zi~O5KO}fV&i@>KH=bE+Ihf)~>(K-ai6B?QjVPF0DtRRQo_Ya6{5chaxobZWa#g z({gga$y69FX8{$G26x-dAe&sa44=P+|51st2UJ7V%Kex0_3Os_{3thKGX+>h?j5it z0I^I1M;0L<>$pL);sc!3Y27BeA%A2{Af{%l8g%S-U-bh6fz47oWexH^rSq5(L23Zd zk3Xn7a;jMf=RiE^ylIUBZpT8PRa1UZet57or2~wv+K<`B?kmEy&O)n_cEJc%%;E&r zMjFr%aP+vX0Q4@Y%&=Y=>eK_TYP{U;kOd4;rQW62w5#F?81zPy*HH5SB1ZzCB->+q z)-cLqAXgId3BF^(e692W%ogy~0E373=g8`C!kaHB9x-E-a$ycXtMRgl{s-U5gz0!? zy5I{|%VU1P044!k>_MrIanN~Hop1u!9I8fS30*^kou<)wVVS@a_e;`^%>4~)aWx>p zo7T1t6o1R-o?{xHZsUw|rr+gy${lwrgWcS-T`uE0+eX3s#jAvx*A;y}O7oGV%p*c^ zG2`Ss_>leQ?SV6B+ugvM4y=G?qZWjqiYq>*rO9_qml2_hG>1|=z+O>O3BLfZMl?3J z?FaaGxCvfp5Xl!_=oO^G7Yv9*V9-f2Pel(#v{pcE9yMr7jCQ9xC;Qc^>qupTNNJA#`@6?z1k4awyRh`3`7addK+&jnbuA}nzX{ifL%My!EE&B zx1t0{FQ)VzylO?r(J5&z42jb8RL(LBD4v(5)aBfZes@3$8{- zTThgI=TmVS9l?9M0HD&0VM3DIuR$iwt$#?ORQjAM;2u-GTx3f@9(3>XC=JKYq0JRx zOSl@lzBm4YEj=l3;zX&Ty@>wby{H=6C{{@DpgJx|R?BGeOB8GzkTc!@nba2uUj5h8 zAL8%+Ma)!}!=cQU|Mj1NtabW2<}v{YTVG%MpMNiwJ8m`+@)i)o0&9FXuJb8nEgoV9 zz&p1W*fexya3B$eh9Uwr})9#$5ACmzH>pu&$ zE2qj=ER4|~J<@$4%WYN?7!OY_#{R_p>w5^X6dKI>pH@)wz?LRB_g*BT ze4O?tieXY+Gq0Ui^?Nn6L6xJDhwljG@vYCYe~rfLY1f;m^R%zRvr^vH=J5Q7($!qB zI!I>&FW<`TPs;06Kun0Ll<3mfK|+$2N1JqICS3OD<>4fm5*JYRUGJ-y*byywAW2kA zHrx|sh@QpX5>h@r+~Nd{PTlLz%riN?)`X;L*7(bkMCl+;WFVo}*N_fu&IbP{{FD3v zEQ3q;uTJ(auUi7~x_T?+bxFpQW)KJvSIbO9@@X4fLrDb&be&3RH^4I=FVWS)Ujh(U zac!6wgWNB}lHgf`_83Z@bjWnPL}(jwEWJzaw|CPSz!ZxII&UIMf{ zT~kn%r{<>_Ts~cnAMeAKSd*ys+5Rp;vnv&zjQ2sG`!_e@-xvuX1yAB#9RBA|#SnZQ zOLzher^I5&DoUUTPxF`PbVTyfxkWza`3F|lHqo~RNf!T!u6P|3MG>?dB7&f*#akT5 z{Jr29`o*!A|E`xQXhv6te|ZLXi!gd`)M&BVm*r!A44Sb`%l@1?L!jMV0VL}fA&o&7 zqKF^JLL1*FYJCBu+dD|F^6XCNrX$okG&(1y^Z>aI34fA5P-y!OFphtf0L_S7A758@ z{dUtkw!GTBaxiN65dT9f?_O=AQ-y;&5=G+ffD1+~{UPKFbHL}-re+bOG2R17LBkI8 zCpWrp+!TkvUL8Zvv?h>N#os9N%-%rR@UJf)pz9VuV5^(WgfsIx+vzPbF_twIFrdcx zo4_((pnI<-_bPvPm570@JZloTW)%o0q$Sq*)&P3OVZc_LXRDO&-TAwAGI)p%4fsK~ z{EzPZKQeRUE}BB0rC(CkIQ1X_ z;njmYDSpc@H=z8f^c3DKSt5zwGP(f=qn5zAoB?GCrIA#A#u-Q~fr4XRD8#&#jKGQa zQc^hrEe9%sh&HvuG$|w>B9|e_N+VmMADqz^NR8fWcYz&Ve>4y5c82kyi+H63Dhy#B zZp*Ztr;{bR0_Ry7@VX4f5ApC|eDCXKr0^pDe@NYxm=MVfLa=)zGp^Od5AXAoB`e); z{t^#oN_YG;qa8()E|Z}a(szrDO+6JN&hIDCmD(=U;N8;1wJnrE^nAsrJb4)>j6l%` zu^SBf126TDu)dl{#TgFtiAruK$qU$Wk)u1HM3x@Xc)BbIbR-CuFDHpX2=lWY?8-yX zecLsn8G=BcD+W~h8Sh@2wug_N_K0)8%3&^#B~8wXfy-U|z`aND&Xn;#%xY^}ZmB=G zWDes_SGt0A}unG>d=VzeL zOnsCKnsg`x%(D>+&v64X1q`{NdLyqrqVSrrDuxbr#OD}bmPy`X#&vt=su4kgc zeDqo*`8LquKDGz#4vB!X{)p`NyVovPk4V(J*>TKbUM2C#^13IO4u2WQ0c0vt~?s13Rx)}`cP|{iA-es$F_igUOI1jTwV!|LB3L~~t|6>T|ya52dFUfGk z-VqQxvad6BCN$KM2rOVQ~P)HMH7zCcI zY)E|$=&L8`2h(UVXI2P18dmXj1|+6!%pOQ}EfAQ&d?NfiYSP7?$BZW4?Us#Aa|5cm zWQbW73x%77c{nc6tiR{(Bw8F)q)FT9k0Lo z3mp6pd=v*C%14>q`UL2kYybt!172e+rTYP}#n?y!&_nP|DdS%SB{bS+;v-FGAO&S& z$1VGk0!kVEC<-j1JSa8AH^CGQgq`Jy)m|Wg>eSj7Fl(=aG+sD6b_K(eqh1;{`ueiO zq}h}Cs)G87e$c6XYx+=4@V z!uT(jbG1zHH+YAtt2LpH>iPee|K(w%p8-?!3YjQ=P!bw*#hdmJgxPI#6V?NGL3nX2 z7tnsv-y;6qAH=nhB(8~ir64iQx0QcV63{Y^Md2#>MLDoQ@RowUT53q8sG=j>5zC{! zQmGsctLE4PVjUjmH$Phu5N8Q{So)=bG?Es-a1Free=Qh5zMuLAr1|k|V@-yfFt$`l zko?@Lo>B+H3c`-i#E#ez(f7dW@DYSva9%Idwhsi`;z~q{a7ON*x!Keb`J`TqabjuoJ61yZ4sfwo== ztVs*NqYb~&VAcgN0EEwiY#yGxDz+uhY=uJb3*mh0!qUJfT;CjHNdUBJ-oc~?9C4jV6(B==?ruM>Jv&0 z1#h4sc=4I~to0_qobREr!FX!mi4Ap#2o7x-e0z!}~6OVOfF~K~YT*5#?N%kCVT?{08 z!poGTV+TAub}*UFZzyPoE%;v7jEBPJa_^zVnjL^)fTu2g`z_>(0FJaPlLNH?JQxdTr3@vD<0>|=V7C~vjP z-5bvax>PUUV%zwxX%+1HOX4*y&Cfxh1m10Thd+U71l-+|>yVy4H{ehgA_;(VO?qihV<6~tkeGsQ zCGLxyBQqc~W`l6~n^;^~bt`35VaV%7Kp^WbO*2oX@gVps%;uCb|6$AD>n?M_(@yY*QCES`?KiQt?dpq4_9$N`*rxA zc-B`AZ1yu9OeO!pp-(p2g~kn-!NfvQX5ZQmMSW2i(9@p)Z1uqWff=JP31Pd$G-3rA z85=U(jNDKeG*?ds-xH@)^7D9x%64R4rLM-iWMdh!6k8f>`EVj$9~5ZEG`I_rn-Q!C9i1^fld zJjZkuH=o`0d^-)83A@P`&q2v~qkEYjLX-^F<^AJh2nG{7Sx!0dhQthkzo$)=m)Ro&UiDgFBJiz`!=Wg+fzxL zKOmCp_H94VZjHWu0)Roz9coG6k3bl&s5A=3-8ZZOQck(;ZWCY>VzEe6=)So_O)Ks- zGPlS5YY0^1$357UUq0MGQ6YEA+lf*DZp+yNJhNNNK*REFuD-f@F3_(uo2DHoamGzd z%Sb>RF0mj{@;57T14sdfqcySEG6`fx zEn~}yeBrkPteL55!jt#ZMHfPW?%-SV@uo4AWGoIo;WPMeS^03*2e~EM^%!j2FV{Fr z^3!V|%sx6(whq#KUP3urqhD50^W90z2OFs%kcBH=mF1t^} zp$jKS(Sp=>$qFTraM~pz>xk|sh{$@-!!XwEfGh`6n!6|S{I3mB?6~58((_l6-btH& z%J`o5W@;ep!EhjbwM3iKK##y%uawshPU1zLOH53=ujTE*UyUjJAiPQ_Ap#O^=w3iZ zrT0s_>2LNK-g@n&DGukxDASPxfxITk`&>!Cx4(XZnfkP%LaQDOIHP*2(I38wAtDvF zKm%K5h2Md*IbBi{$flZ^m<~^7B^v*bjV#}HwH|?_3vuvYAjT}1oVW#Kpaeo{$lbZi z3!S`kK;ev@`z?Sux0*0HW^9s@a$VEjN6NCpYN!ZFm#X; z&}|!n$y*tgg^cWNSMK?hbWyKmTf-`0Qzic@K{hLjKlbs zlYhV1E+Ymo7oYTwCQZ0?EAQEjl@u>9hTT{kouI1Zvs%*q8s?LL-iD@`HKWyga!$b3 z!L_$GFz%BH$DZw%+Pk|ax-9Z z^(E!ibO&#S)wYHk&6l8-Lr%)ejg$!$n!&Bzs| z`O^_Mw8rFfG&X){2SQKYCZ2QMNv6BdwBZ3BVYZWSvqM=$i^lInm)-PDw844kxAL9U zHHSa$;L9WqjPjl&%Zy>g;#1)@u*9EcM3W=^^8yZsn(8;(TPWmE>{_>#D@065=pX!% z&hXU&IK21)p_xl-ONgu`i0&I#!`V4}GB)9`LkK4kBW;etyqVDK^QL@72cSOfVX;~H z0!Z2-&g#XuqS>b1Sw1CzpmJ6t)Taj?3wlI%KQ`oy4-X(&>pfsf9hoAEvRS9A?>27eU%Sb6FbXDtTSEO{@QKz)#rFl+R59M*dc zcclEw5M9LYV_iTZ?UzqMxO>FyX-C{Yxt#TAgQ%CQMuH_uv(zNz`4;tC4Al5=YA3fE zfb@7Kt?|x5q>ESp5X8NvOEX}3PWP1D?Mj9!i!lQA1R)|0We-~_!T|iN%%YWH9O$D* z)lk`W7uAO4K@lUIuBUEXw87mGL-vitT<*njJC-`0B%L79N=-Izq~Fm7%V~;wUTr~$ zmu-r*!6f)qk8vT-@p)1Pd>2^Fy^tjD_{Zdkp4GJ-HwL^6I`Q`ob7*>z69EkD&u9^c zXXR;p(E9AuFK8c&D4zlzpIt%`yqZA-g$zY8xFIf>U2!>8mjJh?*Hx~vNi%XM@}Nk^ zh)|572(wib%hnTT@u&OXp3PIh@MsC%hzmvtC2+oHz)Tm)lw@yG2DHAqS{uUP>nv`< zxJvt;WzY{>u560P4WPVt6O9czz^?tC`*{9}SO*JXQn&W-Iu_$yTk&dpCP6mGEu z$=CrT#?FMFK1oI*(gl9+q}OcZeM*@BB8wv8*TidHB>pwV({wE7*~#g*A9o$gg2bLf zTfQxme`W-0C%A?2JxE#CZvPOucQhNi5I||DTr}W-WXLFvVk?Kgl>W+;69;$G?}A`n zYJ?8nr`^NrSu1-pH}U)2OQSZJyFd6v`Efb;gwEt9wLE#}F>y-b=V z)WU7k1+$^{XeAn^n7X6t5Wvnda^m-SPRVj3@YX~AxE@e&n_F)!pYm|M{WWO9y7iz4 zcbZ*RLgbU!@tuxDGViO}EB9ehA`y>fgem{k0^o_M%tH(#g{d(454E>HE8;<*>f^X$ zFQPkEb;Z~cgXSfeZkHc}1)x0-=0Ie=&8RsQSQh}L@~E+p{#1;m)+*ok zaTagUOs=C^@M314wZ~uneKX=KUS!t+j+hGIGB?+*P{*(ZHH=%kULhvP{{XK$YH%8d zmfa&W#x|C%F9535_QaNZ%ffq59RgAVn|O`P#$0g7IJ?76Z=A+k;u1WlPF5mjNQ9)Y zv;BGwj5}A#Y>Cb~EH7@~+fHIhw^`&hRLy7BM5hfYz5hAMUI@asTwZ72jl7f(tO>42 zHIwalyXMq}8dWLMU*FIAjk`!`o6z>b;R+ssi-@bm2l(qRY=(2gNi808&p}>CbEe0a zNNPC7G?29n=O~1e9U(5TU3Dkx*rYSll`G;^isq%&+Zt;ORexX!1 z`yQMvEo$_Ibd7XHUH&I%00%};8ybqOVJT#v-xStm#2~+QEtPF0vX+|U*zolfkxj^- z`d=Qrp|KP4tM_L^Cfv+nx_RY+ZfGof(y%63-s34+3RF?v0A+tc(!~vy{+J$6_@ZO`2=82YjgkKEwRes0$zG4U=Z8PrDLD?%&_eb7|R@+x_g~@y9J$l>#D& zu~s@A7g)8up8xD%PozvGU+jwbQixxD!)t*=sqE!W`rmr0$*1Z_|AMd<_Mn?Th~+SNwr`^FllWIW4_Zy^ojp4KMcnn6=vKr?e#L^y(ig zUapDZv7|F>7mIm4{^cZ(qOIp~Hg&ZeZ=W-fOXNQQIei_t71-v%0*IQDYcL+52{y;V zIG8#5Lu&^6um`f8Hs z`-SJAkWSmy`)vZP;O8ml@^+48vzADKT_XX~LklV|zZGSem}9Rg`EvF^qu)w3@7gf- zs!|iy7^y)=?X_&z26Ty179NA=vYm*I_~#3$w_Y(@&aav&(IO#}Q7==%T=#JocLj;RNkM=ZsK}g0EXh;znT6IR;d6x%x(-lKD4-bVEvt_rX-yW)HQ2sl+21Ps zO1?-I`3-Yr(YNKXN((0e4>y7fgPy9nbyWqyt=ml-Q^%QzHsDreB7v19Ea>5OGnSEe zw%5;UxpTW@+tcwr-t%+DOqdC?UGi2)kzk7`S$}|Q;&xEFZFu5GKwWpn-K@e7aD)pn ztAhl?LXM03N&XOs>a*%tNxa{gwX96o55$FOBL!rMLfcWQT;JjEUoMd-dB8uV9a+5E z6!7h zwwUeU*l4D>$>Y59?m%aptGuwQ(yrruh4>f4&2PS!?tlI`v9rQSY~cS3F1!Z|Iu<_8 zItwAl>ka?*`O50Epy;ijg6$RYS-H)swHSE@4xbLdLJ&MngBdq5W4HuCipr?MCWI2j z_wBf&ZtO6iEz6&f=(jMX$H9H)n5(gd6(8OvU>l?8MrzBd-_ju7;p><&jWdr>>{A2h zIwYBo_mN|l$-qTf6dDs1@L=~s>T6tN!F0xz#j0;q|wx&`6qDOn<%H@Hmn#f62j%W}VqTr2ivxSYKCh(Nh6NT~xiSGV@!n5E^-w zQF8DqD#DZ&+S)=kv4U4X)+vokyD5%2mP*wRpB|cMEKF|~;zs~60d8v%oJ0QSl`ef3 zPBQlX7hhp<;^gm$?h(Z>J8eEvVHhwm+^5Jb2wbcaJ%}JL1f=9BD*tFCCxh3536n?= z?9v)x;-FKRBxCT4#In510)fZ(#lxF&Nga??e3(CUZt=ZoiEw3*H8X<2NpC%^A640)!M6xm?zh}Zdw1mHA$D+& z-t|*W<9ZSE%O65_Ltjm<8OA5c-f186)0WW6 zuW)P2*&o-hx$Z_H_z+v*#}TX0|P`wqLPStYvrSXB@UQcy3#>qm5j)AcRUl zeL9r8Kkkz;jS+*m*ukf38?ZX>habo}FoOO8Z`s ziWM@vc?CSKUav6 zBC+^#$EjSFrLbk?z|uTcG5k8Z;?>}vO`5UZbXw%M2^H{ebsE0)I{#r@CPjJbx9v!2 zbV93=c(rFLX21YqHW_ID>#J6InK*WCdjG8L5^lu{?c)V|rzy z96<7%YkrrHrDX2|#BLzk*}oX2}wBx$UEz;67lAYSe}U8 ziUDT?V;Pxg*+xOdNnzLdc|gqbYY#wY&uVT&jjj-jC_bbgv(XVLWEP63*wndwz%Vtf z`NfN1s9A1JdR0{dUMv>8L-@p9#yzof#tYLYS`+Dyrca`@XU!$ur%#~&gkut5MY}=_ zsmRxC-Eq!p?_d$beN{y%d}Xg9I+y#Eerm21MEr?&?#kX4eQsU+kY*`AsG@08ERJ7Gj zs0x?N=9H|veL_qrvCD3(dt`J`zQ}rZG&<9xd``kkVeM@C?H(8995D+uK5{vX7uTTf zh6J*BtM*Sc%#y-DgaVM{V%=bh?Z_y7neEuD~Q$`Q-0vd3N~?M}i;33-}J{T4bU(LJE>v@V1q9 znah;}Kc$L)^QZ|MGTMlz;*Zp)0x9KLz;bHiL3KJ$>v(&L^WJvktM&~HP7F?}0{;QB zy1pw(q5ICW9PNShdZC$h55>@O-@>L%B1Th5=CKjK&*aO*528N2wFc!~k;~s;FKlJt z)uysH2dzB=oPvM7KOipPf;XJwl@+RclZjWV4X4a-cVKe2#ea=HGCd_ivQ7MynSW&{$XRgZyRjG zv$ZI<<1m>?5oV}9rdq52?YVk{4Wb$Mwz4G4uiuPP1e-*-GX@S!xFO|IzRowgt?*Sp z-<>J;ur77KcU^^{W8kwn&_zrq&;&0I;%Rq+*V|wYSPttd{M@O;RZLjGfkfazPO{SC zcNlI|!7K4wUUXrRcxQg?VM}2l$#S>pbEM;6xpwHT4=bQVr`NKy>%w{nSNbciW2rVe zGJd;G`+1&n%xVOckBhZ!i&UreBfxsc&c!EOoFx}c&YN-i!lal|f>NJ;oBWx1^n<^8FllGs_K! z(oeq)I?2sm()F??7e8cP{C9Qxy7#st0f|h;c4izZk>#h)K>Y;^2kW5UtscyLzvq`w zOg-}(@t6imnYEX^vs#ZPb9q+Q`^(cRMz&0QOcr9JLx-KJ4JLAOEl4eQJjCZz16W>1 zD-k2QZTtM)ZM5i}1ZMjmGmwMq>)Ds=uK?VIGAZy?FcE`Y0#sPCAm@MAd>ol=) z_p@Q^dFaop6RvJIJ?zRJO;eV+B==-52^~Bldu@?{gEW*>{iAxC6U@Y$=7-OxNS5)m zzROSUTII86=E4~(Z(JS|N@rL;`eMAHG%=dTNhy3cl#Yb#e%;;uv@-@qv!C%phWl8( z^te|iZp*{O6teD63J=Ihxqgtn5;fGOwseOk+H^e565gu}~1Q5OX|Bj7ew2kmFPFBsWHIRYHB?Yn=foN$_f^)O&WcC;&rPHyUhFM>vNFLvY8 zp=O{%=qhl~=xdA^ZchpwVaO9UKSAw}e^#iJ1(-&ly;)WHtx8f1?`vkvx|`{l;vCB4Ht)x~1d911dor)- zSW0KwfCo+F#YjlB2W}BKiLcZJ;|>n4lSFfm7mp);fVdKi;Og3H#;O`E6|RvEYBRN+ zT)W*)`0L#LrE6D%Q)?4?$uk6~#uYd4i#^1w;^3!w49X`vD=7rjDu%*{Zc{9E!Buno z{bU)+Cvo+ZoSt?K1>=bBX8gh~#6+#Tb>o%v5ZsgHwnHAwhES>|-TLV*FFz}MLOt`~ zVjf#xlF=t2N!lx;h%^a~F+^mvNzxL(;xLtI5_8klBdWr9oZ*!)7Apc30*Xcms#wCS zm5xu5?=U9iS&=MQ`!)39y84B$dxM)+-O(d5a>?EhV2U@Q<{bZwy?bWXs z{7s{M-`Q~bSuR)@Q6Cv!71!IAXSHso?!GDWLA&qe#%j)DuM=h?xZH1CsM#Ua z%S(ZF^A=}hAW3>6SR9^}N%Nt%;Uw=Ewx4|CFPOVbXgs>o{E~g@a*fTfP=&ODK{*M< zqlp7Av`L(ex)yokHV2l5j4W*T7AY@`Nm3JOQpm}KBE@IfFJrpHz|It&k>7oJ+IN;$ zGx<}j-rCyR>Z{n1fle86Zd-lQFZX%vT2|2@9e+F}UBpc})_FxVlOiN$9N_l<>k9&^qnCHO(_({eQ}E-Hl6tH4ub%Dhy(%xLo!J8ol>JkNl)-<(70wZ4=@(mw5H z9NZ$pTf#TvUfTF)hR8M<81&a6Nmxj6ZRNfs>QHY7s8&aAJT<%o{i{Qo*bU7g*A~|6 zbhr*q-S%_(F?L< zel@WRVSVP|Yg0M&XsI>cV2|!&(&f!;k53X`L~*@1Zhw&-b*}pA{(LrLdv@iNg#)X# z)6fC%g)epG307C5L2yEuiJ()V^zz#BGz&*7@ON8Uvgc7#`yKlW#{aBeUPIJcPm5ME zM%>eonYvFw?8Lph8_i`pkU&G8)B?_svwZCwL89wVkf`P25*nv}i$#dz!kc@|nJAO1 z?Px{@hgH$z9SPytlRQ)D>;bh{v9R{HQ)yPwZiA~L(^~=0Lzv)S`w5cZ010Lg*GX*k zFPo1dW7C^l7RxYw$u8ohAxOl=qu}$p;-xha3jkW*q~NyU#c3vX!HjdkziV#1J& z{rS{hbHdsgM{Ugz6*}R@-@_Q`OiXRp&4$d_jxresW}#}9rwwFQM#m(g2vjJdFR=Ww zE0j=7&60lD(q}1%|c#Mf5$P4tz3|c z9sF&2mcxdz=^6Ywc=bD-pai$q?X3#F3e#`toDShOVl}_yzQGoHKTSn}4$|uR_yohG zM@0yf0a7xCaKp-S?+Bjz)&=k?yO)fKL78!5q*7%^6Vr@ICwWQzh5B?WPHovQXg;1) z9Dvt{Qt?Nn`-LsdL z8=}GXr{a3(Z&RlbhZk5KwZ#2`8Z?8j)o-_lDGC#JTzGrz;pSiR1Tl_*wgD4f(BjWS znS7VK39WOTFnGEGS9fwtgK&sFzjtGHf6SKyzVQN^Ap=)xYf}7r9vQ=MxIp-)%# zN|T$b^gY73@GX`)&T?D}J~Jl6wv~GjF(3@D9r37*QugWxB#Bw%7{W0Hn7*w4NaPWS zsR-VKUB<|W)Ou@h)ExGIRUyR>{j&_>&PYt1TxvW#qutCwvKx5ZVuBt_7@O7IT|E#c z-$YxM9)n^K4y1X(Zi5_Zd3gtzKGw)SzUUUzlTm~X<(l(XfHbg)I-BpcdaHhY>6x2} zD3dtJq!c2u`0MKL!@{l~@DgY@eN3{i8Q%$`=o~zcf**2_n&uHh&-tAktX_dj=B%aa zeed;H?SxHD2dT-Ykqj-KkEM7xsP9q>U%gmHg-y7ob*62C;}0Dp8FmZq)@uZIg-51` z9b8jm8h1JIs>t4;FvQqbI>@Rh4ekQ5*@PW-1;Ns_?461Ngbo>xXIbbZJL40ZuWh`~1A2LXvH4^y$d6HZZEZwE(NK^FU&}j0VPg-Njwu zUW|X4Ntw~)9Iv>HfFDSh(Nm0eaB^6_fk;c}Tv%qwvwl|F5$J)zp(x)=2)P=8FNj&z zVvELlR#Vd8Mz!c|(9ZAFO>ObjaK~*X`_hZSy+L}rW5NK_!Sq4pPE~4}(rf~fc)p$9 z0r!OZfr%okkJxSZZNPcMr$&{gZAf6T4}@X7wvM8afM5Jzm#iu@*!<^x{16Y$7DM)v z>Ko@Jzs?9_XgZRmS2}-jXYs8#zY)SrPB(H(=IqridGsmaEj>_Yncj213!JgIKL={O zp2Ao6aNIXcT4@20?n%A&D72z-FW;Q8-IHe2Yc@NIq48^Y!7sB_eV&iJ;lCar2X*AW zm(oM;Hl;mTr%~bop4!!Fr4Dfg`(a|7>#^VDOhfM9KFu1a|MxN|@P9^L;n3-R^sSY> zmjW(#ecAme*@=78vuhOJ&SvCGqNsXqqRh2wqBm`OVu7yo-oiL<0`R?`h7b4*S~jNE z`3R)NHOI{ftRA964fg!1Ioi`c)3KBB33Q(o1IBB#D$MpVmr1I6H)uqzf=%L?fFdVE2@ zR3^kkfA(!8MHx@*$00p4Y0|eHteF4uca-v%AME17lqTSdV-KjV5T?kdznE^+MMP4I zI!#k~YW|K5IX1oVY!trg{>Mu47Y#n#W=(cY5R>@DX;zi7!X+S%R(x!JOObc%A&W46 zEK5+q@2#T1aHR{R_d3dour@{w#uAj=Yys3p!X42}874oH`)6X-rhs-*WM-3SfU#lZ zxAM|0ssUyclg>DZKWZ2~oB`)xYTKvx@2JFbevEz6w1A1wc{mmlf2<9k?huI1vu@1# z(M2lHFB!^xPsCbk@M$Q&k0cis9A_<1fsp@xWpxfo zu9i=ovHV_Fm(xydS44m#xqczJJ{6UJICE&3K&Pp9lspO#&$@PyjDQslA8|;_X9O8%R!S%1A`(xnBSCbWFHjVwXnE$W8n-V{j5E=7y ztoiWueegN`q!zc>{d>K5Hlz+WO@w$5>^D&qSfxQ*YH{G2s+lk=RO?acq=QTo{4I7y z$cOxiTrj=M@7cG&=NuGZy?n!Dcfi*ww|qLZTM`NAYM<9Mrp(zL?$^z28+2t5cz9r;vWKe0#+xwhM z3@7jK9>sNmdlBfdw6C&<`}qy^1AldJ0#jT7cZTzfbDyXX#;zqd|-b z@0(E#ELgI1l!7X_@T~$KP@^0X$lpMW!%x6nlP?Bj$p3uS{M4A=mF|tx77m#3)3Gza8r}`c$#F(%bjwV2L zN$77~V*bxn3Tdp6k#FEPgyYi+Sbj-;Xn5;RaBIG{A`Z9(ND4mibgCknY81q(l@etF z;NWL>qXzd#>zWhMZ{P2OfBi4jTmIb8zc08DdxB8*yMPW3(lzP@J9*$mwjYQCaDEj9(o}w z?J96t@ItxnP|`^skm=ci6SqnBM&AdHLYn!NNaSt@bAp5!6nX(2_>4=h@%QGmVm*#f zVrDo|@%6nL=mIv&*J!ZNr!Y*IhF`$eW=SOd$7OHR_|o@uDrhfzPH^tztM`|o-~NmC z#%IsO7DCDTNUv)P+TPX$aRevgU z-{lox(77*rdwG}Bec+(+Rq!7TS-^1snOXY*)9=)647rK3fuCjeC&>O;rQLoKfk~|0 zUEv9u^#2_~F9_+ySyF-Xz2fAjwX{8}+A|B`X&3nqKzf}GvIys({1_|n9Y1-_?19Yx zx|8WODQgN{3(`H1u=fn4>N|wH#!~UMdF=tM`oQ4TXMH?@J2Bete87sz0{Q(T7G16& zlHi%HRn{y2bU*)p-T}IW!6oeYr_0VQY@$2kDSxU&WTAJLh#a2=SO9cEu<(}9CUD~Q z0cEK*SeAl&W!{awKY`AFRiybO4vNX?12TX6?t=OWvlw0YcFhve8BOTUQ*5I00gzhR zc@b1ryUL$;I#SfP9dzmU0i5(T*$fVT_?4A=IkO#Tt;DD^43yK`Npa)FeNYGH_QtngW`L^=`FpiA_{*pJoW>{7 zvYTsPk`(D|<0TK#AcJcFJ=FqUpP?LJ^|b=odxGF*zHe;$eE3UI`gF)+F)i#DI4wm8 zoc5YN`Ug6wzzPJNR(zL1&DYPJJMHZx1Wg;si^C$S`7Zx}nr5d2C8`L16FsiwR6za) zT5EpbhZY10`CnDHpw!rv57SHkEWQK02s(NfPh;>nrD*0xAN0-^K9-iP$2Nnlf4nG2U);4_4&_nm$WebaSQHi=9Q1m zx<8zn&%Ms3L86hsc^A0SNpSuI2%QnJ`NI%;vQ|Ovf#j7Ya-cADb#$@Z>Rd!I-0JhM zU(uRABeRS_9HtvYh&=vsYX6qLiWweFlK4l7+YFR)d*T47$HI+=?|zX!Uw=?aOS>Fz zKL-e(39jZpyQ`^M>U@x&hb0oq9P|O+lztGn;_TAW!s=2GMo}4L40fVbbNaIhk9`oI z<7od)1nfo=c_0UTY*irdB-RtA3rh8t&=xR~TWuXOJfFKb{B&o>V?c{iUn*B21j@hZ zp$?2XJ}@Z zy@&_rt8a3()0g$ThT6Ub4e2$dDlJ^FJJF6jD!L6mr^YDeE)Km|P-_XQA;S&lrjyr= za^B0trMGd2tsOYBx%A-K_p}@jKnML^r(@qg1=NIR_uM$lvDY zxOGjhNyI?a`&GLNE3N$^uG`a)&2$)1d8C{Ch7 zhvCW(uaaZgnqBp@I$LT)2A%Ug2)V1A)FS&_H{pn*65qojagOxeLtzZvm3MZUcs69} zc6Y;_c#}b8-z@}%x4hTGz-!V{_=xwWl<)1(;?~}bC&3*Q9ykf#&Ev&7xvEAqwd=z( z_y`AEjm=5a>h-}XN268*Z# z|M-VF{3HI+&hqt^L6#tF-l%{-*O)Mdti)%Zf-cYIgi8(Hhw<`Ru=OO%v^6C;tI^DXCo`itMV)Rc)_tj4Fq84fg3W<<$f;;k8;!rNHy(C%EH;#P0QjbQJ3z3Rj#cwjaCa$r%~@PEXpBn<=+wwn&{_K)`9`cd=km>#2_8 z8)~%63CDpQJMPFtbV$dz-%6T}LyojgH;EjH9c2cu3d|=we+&epd?9jV{N5w1@^`Os zG%Kn;BL-J<*+0n;hu#%IeV7c|<}XJk(lix%0-JmebkKeOeQHMwI^Q3R0w8f<4+POo zId@B%;AOuWO3*fADslgj^C?)H)Ym;&e zo*;qs2MX4s)&T4eQ$N{A?swZ~v;1v1ge$bB&LhBzxmi5_vl)%yG>&Y!?yoZtdB49K4Zg1m&C%s=lJZbIJc%bdK z6|04}>;X`-6-mDfys?z`-u`_u1h5x(*Qr6t2@rUUE%vZyPDb5+u3@Alxuq!! z1INAfch?I}v`3MGZcqVkY)IXc7s|j2W5VzaJ@~l&w+lmr8Ma4p!SrP81v>3zn} z_qXo*UeB|BpMRfct>0guthuf^=Q`(Hd%ySIuT4uC^h@9*o#emyv=-%6`k16pP0OFW znyy!hCErh6JV&>vQ~hT%Hm}~f4yVLP?acnhGwbvBA;fmqSg9$4i^F`q__cw7ECas# zWErv@Cvu)j z;g_fh(|!8!@(Rx2(_VINAcACntK8+(M4kiyE(b8toz&SmurI_sK*1X|1qF~@>i>L1 z6KjLg0YL$gm%_%V1H1rWGf5B&G^R*{E~aRb%b(_e+EZr_$VuzKKOmBj`o~Vk+P1y$ ziPJ1hB*}~$_-3H9lPLCJ!IslTV?UOa-#5~KtziV=>}Cv&P^wN+1)51La2|x}VD$A% zoJx?cb5LR~27Du7P}_5oqOBzJO4eglvjME!9Bk#sx1vTu{3w%xHS8Oi6Z=MvkL@_s ziH2UM2FGOwR~RrcK8--tEdgcxy6kpi`V1&y#XYY2n$v}2h!#Z_X+MDc*J*Qe%KLaB>YCqCuWSK9{0x+@G0nkshR3o`57c;@2cx(*RoNP;RBYJRz z*}eQ}Nes>j<16~w2I@#@#K6Ep;GwoBsKLy;na*ugf+Zp91+&RNAjRoEP^`rk;dz2i zT+r7zBguE;EniE2)ZIn`n}WdbwJ|BUpm_dIJy}qo3HjnTb!0NtZ%S^nWI!%C<%kdd zC@NKv=@0JqW9fzQC=!XlkR&xx2aWvdq#9(<;F+~EF&gkM* z#J8|g8YS{Qy(vbp5F{fVJ8{8J0^f=siJ_WBl97L06j?pxg`-U5zv@-egS9of_|o79 z@F6SFd5+={VD*YhX;J>mMLs}Jl>tzkR%z;3N%3GKqVUHlcru4-cYg)J;jo`dLWYL_ zdXrg$pCZ$^x-C;Z+OKoLRi;Uj9G|m*W$WKkZXik+1C@v<1$jeB#!&fAU42*ZP3Pei zAW4ohgk`b3LK2L29)8)3^Y1gF{Vx{*qa`k;QL=#QF90*=DY<{eW_;-C_&^ltyRPmeu}Maq%l=aoD9?clibQ+bIm2cDr{Xj#QP7_Gm`?*;sSpAoTt zxyV)RBB4lQXb^>75G$A}g>U6GLcq=cXKwF0S zhIxMLIsI*b-DwX@C2`=+C7hg2zuTEPbM_r8J$o4G?Lkw|QM-A-GWaeqD$>s+dj2j<5dRIVAdYyvmqob=~U z?mr&bPf*Nu0$kdVIqo>nP7H{BfuSimao{FatmY=QEbY{q>!LF5RpF*9G>t0^ysjm%-59xolNzyJ`9-2Pq%;JON?xQp?qQqjV105{FI!&0 z@U(Z{s>%n`%!Y$=djkcUma?AJ1_3JIU5@QfnG5<(cY>b^>wsIjz3uYz@i?L7ubIdu+-?1Oh@@G~IJP3v=hIJU2wJLFM&p za0uwf*uphOYwlY->Wg^uOc}%riybcioHBZ!q<3y9OZjucI@&|^Uq}t8}_O+y(+jBtV{-O8E zj0X>ls8T*CQJ+a2d?#^kh1ZO6Ts|PE1};Ap_^Z-F99TuoUQDawXoa6_c?kXYS zFM;}#|0wwn03PI}>&OgNfmRs7q>^`KVTc&Xuk;T2RxTg)mf33o5GEV+9-Y9VZ@Tai zc~I~4cUbMl8R#GOfa4+xg=*-Y0+~;mP$9qbyA#lDM4n!3Io#S<0ozSPcBiGZ6?jUw zo@)OJvr2pS$fLh60AqzVlAQzPydUk^PiLn)K7VZd#dkwqoaOh@jXx*j>$faU)BJV= z7MwaxMqP~^ngB$k(-}Az61%^Nh1x91U;WjLlD7coP6Ey35%an^-|mUgn zsny`7J*Gh0i#F}!+h$kHNCHUiSwd!vTK~11DV+c@oD0nE?A@T<0Fk=83w;>1^r)Yp zvY9Me!x`~_JB0Ookzf?-6wP1k_X!Jwq?N+@6%O=oHo!$)s=>Xj{52(m4*zkuZ@L2D-vd+A{ zIQp_Z4w@u=tl92Qd8?<1^z44KUVE*8P=10Aa+UUpsmI_;$s#bsIqxr{&fvhrI!q*!~EnQ&ZL*t3kf zc-uJ-8@^~Su}n9&j^(PtPdhzdVy-n?2?7Vi{aZ3KEwZmkPmp~Zk{$b`XFKPk_(=s6 zX!%n_~a1bpXP&!UER$$>C}P9EXPb!RCSc zY7K+jc@OAI#>azpJVhJd&DiJ7;4YQK3p!#qM>c4|o&$L5Gh?#3w;Mfv9#^{tIdK~) zKJmQ-8$IGep7V)GG{E94q?o6>K14Hn3j+c8Y+IZ&cX&(kR|M<1B|gP>b%>u^>)I1h zEK#SuueWLqNT08btAVqTIe-0q%3F{cv2bG-Fb1=xAGGQ}he0O@cYxlE6&7z(j{f?p zj^Iqe+X6zBNE|Na@Lb${2`g5`%NYt3YyDlT@T)cP$PiN)>*EO}Z(Ie?2B7+L#^^cF zr-iZIw75(nrL1kR-J-W&j4ohXJYzAOqoU(-Hk-E%5G+1~)1>T|nV z>h^&lx6cSGCQSUW-xMipd5IO^$G8lO&Xc5-^L~}z2^vBiRUAZ&Aa67(g^}2Qakz90 z)$9JRTmUI4Wct8yZ!BeX75mDF&>xN9xt4kle5KVtz5Z+Ph*+3ceXe2*^Mz;8)zcRW z;JMzhh#t~Jx+n&>vR{6`6XK|FRF`bT1ZoUFO-On3iL8Qbi5O4F7_!`cndmja`z^G1 z)~|fV7j#@QFitK6F%bi4rWo9^!J&)%wQw(InEt9z<=?m?SCCHS`@Zn)&wQ0t)|Wra zZWysQ13!Hx)>b#7x459IX`c`tsFH&{5J^ z#Az`|9edPc4mA6!7ZiEQV)Cft&j8hFLaUtXlJ1u+Y!&{?dgX3k3o}er^c&$iV6Ql= zMTmGg%HD$1t(AmG1Pt(cYrqqA?C4fs#eJ0yoOlVO&O(Ep5hXv+NA9&M(U&`9JOIc4 z2B{5is5nUH3LCKL2qKxDlqi|mR)>^lW#P@Iim=HhxC4mg4=s?t#!Dt&laZ>Z$+}8D zD!P#J@kyzlMrquZ4}bBRpag972oj(b)1hSw6*gNuQ|W@gQRNA1bGwazGwcO_6Z#n# zxg&Ig!0y7~W>IC{w6U9f-!Df{Vz6HTTF`kPJ~n1j@UGFtwj^*ONSlT#L$J_wMsD6p z&^^k-1t0cYFpu93=oUwBXi5~_w_8c`w14*BxO89g!T?68yhBU82#ohr zxr=k-ggNNMCmu$prjjq4S$~2$WdxCx%u!Z$f-oLyNG?N+?2!xVwDYU9Vv*5sx?-M5b>!)lU+*Xbn8%5OE4&w!5;sr9}CjyK-vya=W5wP((w&{QeL!8i9fX;mpymUnkC-w9yg&4ZJIh&8`wuz&Z5;}C3B?X-dv57gAd^LqF-VsGs zqA~b(Ut8QiZZ7L7nV=+6XltCMxjOdy7^8jt8)Iq4s2gu!5mt{&VDxq$BYMI6`bI-o zCq4Q0WX)Tx=py?sz0fOpe2v}-wDm*Of{~Pvp)*glK~Yob1M`B}zC6lTj?S!;wTi(Y z%#@r9qE6%1Q-jaZRfczH+o}nNQ$y@uF!uk0-@IGG`3pBkYI*m%Ia~c|==Ok%WGDvZ~Brld<&$g*b zr^Y*plseEk7d+E0F87F1ND<#)9+8Yr{c4B5Wd%2a>bDb-2`| zUp53AR!1B?an0ZTKs_S=Aghx&=L+QjOp+GvwC96INwuX`9%05OF)`D_d^jdUpyc}I zQ*31Cn|K4d!z;GYd2q6?RSyT`ccV$zo>JKsm@s@+Bjc-jZ@FmOJnf?#EmGX&oz)UH z6Mij1)`rCFG#q-c@6n!NH;7`8df%w;zi(VAgJ1T%{4{WG1A}=x^Dg5xnboMtU&c`YIUb^3wKqI`ZM?KD_TJ(igS_Jk0{!BuwoF>Hd;cmSYg6!VLl{|Uh z`veU%WJM{8vnzk3uG!rxL*aRq64=U*1jI&ODYp5emN%*t2EkGPIJ$(#K2iwlTA34R zR&+{)B_1mGPkwg|Im`3vt~3$eUt$~rJ^p$O^7f<1*ym_DQ$8}XBstR3M9&^_A$@rGuJVO6N&;qd$*DZV5TINzA+4EqkB29a;m2V8$ajDj9|No%%z z!%2Bn)h|1{T5>^s7+!^zZhETE!B%9{&xDgsWGMXHDtXm@GV>a2)nVqYD(dSJM5?(p zZ7yIE)vypw97z{247w)t%`-?>D=@hC-B*R!3@!W-#lQUN-C&_dUY*%4cXanjaC{@w`n4!N$WDnlmtJ1ICu3VrNe%>$t?*t zjvs#~>Fw&GxT7O!%4@)DcX;o4qIn?(rhiC;T0aVt$l!luD5K`r$waKp^^B6~o^tCqa?E5LbiHG{J~=ty z@s?6PH(XYL@?29;PWNCpav)QWI;4WMI%hE|_npDNpWe!!2>|-$j_OSMA zWp;;?w@ma4NwMS3G>SdGjq)SJ{w_E3FhSbj7R0S-Ku?E~@ME0>}|C0t(h}}*cxSS^8Rdmcf*#2X@gMMvf?j6H8 zP2_6oo=T+5^7@$BG|l$$%vEwXsu&SdT$4vX-0?sj&K~uXpg`2F<$+P2qNLHwVt1OQ z(~s1&$3v-#Vvh(p)W1K1G?;s-&F79exd5nmZuz76$`5{yG_TWx%*5hQ`z_sS@D^*q zgBlRCOK!5b_+cpee9t{sF_MVGRH3Dg>?`)4Wa07~&K`G< zI#NWbSbUFddiq@$bHy~-a%_STRT+_%`H~YV9n3v}ap-X$MdR5R^=@Fozb>wo?itb0)wTk#(i+wJO;h3mMBk;p@2soTHP#g_G}>xqw-x? z)S?d8Or1^J5Va_S#uh_enQfbIw1I&BdZ{CL>cn#v!MEFwlFyleqb;E*dZ6-zOzU3F z{j2~w0kII%u834h@#_Q2dS!0!$JrBIoTe%-OM>I%&VD5DnDqCH)=r=7F*|*`^atRW z_PrnuPs1M@Eh;2665cTb|LQoUv$GOcC_zF>0mc8of`fP^w+v~weOUlm|D8qQqx-ot#T)A z)O(4bJtlD{YVE)l`y!*JY&qW3K>Nk7rm9k1`O1wL-bh!_xwJ_lz;vxX$!0@yiP^OF zu@q#yDDxSM&h@XWcDE)$KhBifeS?xXClPxkRpG=%=Qz_PV$onDM3YQ#UvRrpVm=R- zeHQf233Mi8=5vg^YVmvTu1m9#GO^9K%Rta=p3OW(A*PMLz#}tF04gAs$FV!a44G`RbW7es zy0YCwvI)fK47U&Exf<_csDF?!)DLwOM=160eVtWvdF3gXgwMGwAzubYJ%lQ z!T5`Q0XPGHYG=;7i3c7F!!qKx$NO^0Q#&J{B|J7Wb+SQ!vo^Iuqg9A=;Za|7;2BNFvA_RGL1{v>V-HQPMvIv11F%LsW|&ukw-l@~Gl8f_Y@p+ z{jZsy!Ti)#(4^G(e?IxR!;G@9-L5MKSN3r3$pHXs^)XLxf|z&Jy|2B(_9ELv zm-w_HY;fJg>*&?0y%|Vh+2Wobq*TbWcv4BWQLrI**7CW>`HGPLdgS8E$h!j@l-_+a zkqQI;%_>^LHUk0pM|?ZQYiy50^lu_PBl8t&QgAhqw#l24gfw`{1oLo~8Yi}RjPhYX z)_Z)>4Ck&oGWw;{C;YOjl=V;;=K)SHtF)?a{}gpILWh!2Iug1Zik=~wwnfcO(0Np& zojIOHULcD^Rml#b`c5nehd@dphM( z>Y@K@{5cFrvjLb#Xf$miLod^)5MG|HSi@LqVo?W%)HCt6Hv^F^`F#TV$i~tcjx&N> zMdyx*4zOiX34SQj)oq7oD;-V9l)%)_K#GI0`~X+3IvFX7ygY)0O(;cSHEDsuKQe-F z0k%MR^c7W@%G>Se&XvTSzxn+daQm{iBQ6JD-Bu?o1lix05SziESDRh6a;#1YkD2AJ zAwKs6L}ROj#I+%NDgJ<#x?wgh_07-!aj=fqFBv@JKHZan#VTuV(lRa&=hV|mlESuV(ntCBWZ zgT|=F<^ihC=3#}~@;rbgZh_|5x#3nfcu;Nmv6z~#UG2qloBjRK@e+6lHBR4}Z!&9q zYeR9ZR-$&LoG++Fgj&^m3}CyK5^`Fp&Gd}I#i0{%MP8*sjViwK%Pu3-NJbHshp2rz z%A*h?SMSr2jjZ?4aic8vHQ$XjkSh;F5B(CK_72pT3GDWLZF3xjW`W{KkAUq&SYH?> zcjsD-xz;OSyL?d_kH(fZLM@gHWcmpj{ZWtDPEu~z!d;Nw-b{)uxn9K;*{=ZqbZnN! zMrh!5CI5(cQv^I9hMGZko$0Mi%|&c;T$N&t=Bp@fG65Fb{TPmc`N4=}_OG#BTIs#a?MJ`BqMP#Rroz#9rlhT#-Ou(pkmMQ+ zy7RPBEhio-qn6IW3c`gzGzas2Yw%AdoQZu47 zS+d!59{KY#S6h^oIATff8kNhi+HLDy1bw@_fAYIxS=4zv3j4AUSA*Ke%n%G8pGL|u8RCns+s#DqY!f__| zgybF8@Mtu5dR#S8)18yA?WQ8Ohh#SBwDBm=)ru}Jok%%OAc5aBODf{bnTS5&7n`H0 z*#uAbduseb=Ne=rCPZm|g2iJBtq7*ftk=6H;v^IX)f2pZ?1l-kKi=L7&Ktiu5^R*=;|oL~(XE+mp4wOS zhBn5bu{bXXPc9G*kl=Oa=l4!mrE$*$R^H39T%Q3`5G2w*qG4pxe%|4#X-VmKiqy(i zuYDd?RIE0aeZ(ZPpXsY~n_&Z8fNYAidCJS1d%-?UJ3YVdm@a*=M z2HPXl-rd5MpsA=U*1I0kJ8W8Tfg`ivzQQPVa27g@_5Rt1q8|XJsl1o>!iylo1L@dF zcAp{H{JDn7^|hNw?}?L(4;Lh^{zOO5{l-EiGLCz~V)ysTVi+?9Erky6eKrt!x66pY zs1_fbkY7<#-Og8EWX5j2z9qTIoM-A?}l7OO*8CJweBg{I!u^_;`#TRB$-Wz zk9tiICSif|R?zF4`!Pc~82x+%p;hNQv#z6YKQnU>8TyP5aW!*hmWp)?${$VtCKm_* zIgA;Uc#tIg*bit%qE)W z4>%JxakqG6pVtvxblCkCefnshFW~dV{QG#01j6|qU(I?cj$0Q7%HtX)7q5AJ+Ew}7 z4tE*Fi{hB3Vuuv(IyS1OL{>yGGLU_dny<4*2u2^PMDz*}Gb+U4))%^63}muz@}gZo z+kVFV?Be5Fw$QX%v{f+sBP#}vO6;Mp-85GiX5Br;Ub{OwIYj}^a&$L_%3cGUD;)>Y zJ20Iw^T<(I?;`b^B*8$*cDTiE`xV-|NnFM6!zhi>hA zUa(b2T4xv3D@NSPbzdd;U9WS(1(mD@q_R|Q$OB&rYSo4;3`|F<&iKT)C|#3&`N7p9 zrL&%vK60THwp!|;WNy>0YZ8C|8iFu(V|9={JQ_yoCQ;S*Tg$%6^LgDCB0ssC7^$(>aeMoIJ9`W zVvi>)>Reqx<=i?GGdU!M=%?mtdVorB3s!l!xa{U}*wZ}TMHt|uJii!8ptS0?qh$h| zVI7xW#D!HHRlHTO6idz9F~_8-D;*@$!Dm6>uhA+CrUY5-n(zLw1gOz8fyTA7g>rkB ziTdE^in*jQ_qEz|QSa*LNmXhj@U=m=1#d~b@=fs!cj#HRjg5wu+Do{J-9mXz3a6W~ z<^7>f^q_#PYDHGk33IRF=fZDOV$NGmJ9orhBa~U{cfZ<)=&zB@j0Bh}zZhbtUffcD zqxhNZLqA0X`An#u$T7wu)qzfwidH*=B+7P zFq`*X(_PRyV2t7G9}uN#TyZn>oAGs(nrgjlDNu|+7toR62jV91#1qWY7`BuvHLTp< z*!TRg^jIBq8-L4HO(ZncH9AF%P4ix}YnAqi&8Q;378^I`tYkick;d!~Nxh`A zPoD)A_8Y}n?N&fuzY2;lK>)Hhft36sUN^P7b{92n z9t)4(!GJYK`uLwAd=piOyJ@#ck{j7GW}7hkh_#ap@vt6lpKN!x>UB9mk?qjh3^l&a zC$RhEuw^&grHCs^6Q*PiHTAzGB}rFLp3i|fFbpzHUh_0^I8>$e^LgXf{ebnPd#iWm zh7GG4GZ{;Yz-rEhW|CF%ttxG}B#mwpc zAcc6Ciq8{hUxw^k`i)EBLi6CA&yNhzFX7oFcGHprNGOxG72l@$?XSAuO*n;%*nIiY z3x7!cT?hvZ^aIH0ZWM$qg7;c9Cf-BKfx|Y?xe&E0yr=F7k&bL!h82$g(fS<2j)-Z( z-6mZPUF=3BF~K59r1JC{$5AQwud=POofWzq8S3vGuw<3xd&Eu6>&NOXg8b&-$P#t~ zdT-qnexe&H0k@gII?<{x>5u{m_hdlJ}i~cwRE0QyPVbmjY;Jt!mHY;rx)nrRYL!> zoBdPYw)Jxz-sifnWI!3?2`!Axrbw}%a}kL;(V&d!^5;PecwJLJWs<6YzZT2=SUuIJ zEP!U4S?A`QSQutaS(7(gs^t>61_1EKn)XX^GzsQ_Q^F4SW&Ln-sWws~&FPWWjF(&L z*JdsPyM+fP7xg-!#B^bjy~%#wFVtOrnH+VT28lrG);V%EE#=yN*GkX1BS`MGSld|5 zf=ZaG?L_|k>yrqp%I=35l!D_+b-iAsvy`S2B8uxWIOJAspI-K45)i}71QKjEsc0nY zacpC?jAT<7X_Vwt%7?9o|Fr(tdL2pA*L22VLnPsak)^hp_5P#3zrjbBnE&>J6{Q1( zL%J=l24%P@^;(G95k~Li0%T!3y}F#Zw6xG;!zV7`*=a%vCfpcR3R}paAL%~;73tOKiCtM^AV9${Maisfuj7BTck zE`v%l$CC!{7Kb{9NFj$Cgbj>$C@-$R!Wi23q||+8m&7nPlx+~ZUrC*5E0Z|8ie@{~ z%lFHcY;4MItksY;@mtT7C*$F^t*S!4`K<= zY0|0Upb$D94w^B*=%}+SWs>%I_C5nIvQo5WRVwPbg4_r^c&n*aW$Dw9sPjaAuThGd zpU-nzAz3lS#>BiJA-t_xQg%*=O>bdY47A<|ej|>&`sF{*>N|it>OOq?$9(71+yV!z%r}@(tr)zOJpMtmKO)naFN=B4KmTUeT z(%BX~&M%WdBOWP96}kCZv8v^Cb;Qwp%&Bp+T%Nt*^zpW)_>oNhVjK~gf{cLersb>F zwf$)=bWC{2D9mE6LxKrf^B=sw+w#cTD|>N62BGRKZsk*c3V+`LtP;Y>F2di zhlMW7%ez^>=O#*ottLxIdC4FM>DP>-D5;vPYZPDZDXe5@fhv^CWS&AGp7TP8=}Uhu z+w~vrh$X$CC{|aARJp=RWt*0cC}WVI@8%NLQBY*UKj2KkMEc@cGbL!#@r3ADEp~o? z`K=TtkWhTae@bY}_;K^YxVaFQxdg<89`#V`*t~ZV_ ziB0rc1;iTU1kok3bQQp)-_OS_NBa70sq~^${lX34&v986X6~<+lkWFa;b};bZR>a% zMM1ksh3ynKV+kJ5^UtL!9w~3Re#bSr84NIgyIqCG8N--FMpDTDX$r>Lr!UMizxErw z`u*sDLYFKJ@N%xptsrGzy2Gx=meGwo6)Hap)ii*{wc=~8CIC#4MLMbXo;4Fz#T%O4K^*aLN7L5>-3Tb z-3zPsrVjgt1=i5N5i3rghgz2)@-THAb?#Ofbv?XR&-ROAEa(F<6X_HbLvhAoPohIP zpf7(WIxAPRB`EnKQ}GP`rp-`h2$69MrO8&E z5VavpySM;2fVc`;B zuuX>b7BTM%4v;u5I}dIkR5yX_WCe#m$Rej>ESG~l6NrR_M>>9?b~ckTE4ud#I6SlK z8DS<0tWXIhKg5G|yZ(pqbUK)wJtakdKMfu8oyU=u;pDSQjiH2cW!Du7f!E^G_-&ORO%2SL_-?yA*8mKYyq^XTyBOBLF=9&CZQT~&B2{zN0*&d^Alc+v_DF>9ci0@jH$9hHgVPyS*A$OjLiXf)&IbM%{6I z4a`EuAkqoH464`x3}Jg=M>V(zRUbSj9nG)S3I&*+BwDE;s)vw=#4eoPu(6LM)WXr_ zsH+8WZ}PZnzhzeX&rFg(OMuX;+YItXk;5PaVQU4_;v)Esfpinz>|u1Vo@Tw&uSKKP z(+RyBE%Yh`$*5m6ec&iQql8t72~4v5XYdK!#%8iCf8F32{+U}M&B}G5*5M{Ez!xkg zRKq)?h$AY4ZK*=c79UCSW(%h3Bz2n;Kjhex3aayQPuEbQ>~}GfSmRr_!K{~7k2pb= zsC@HEHJ697nqXPI)yOy+COy-^^f%yg<%Bz-e)P7rF;JQ{1sYh@UOPvEX;1fWHW|3s z09?Wq)n>V)nvVW$<*dafKw~W4=3~Y?#1%75cR~oW2xa57P4GtgoAQh1VDOa_Os%eA z=!#C&_a>!05bj9~P(3xq*jdJ|UYjI^i3iJ1lC#V@^!Dpl#KJ=-oB0<*eRv>uUJ`to zX!*S&6%IHy1DJBOW1cRFc`;l=L3wYb{UM_RTr32mY&ne+8|mgC@@&DJ22J$Q zA!(c)Sof#Y#b!ISh;4Vv=VyN+b)@i96Xv%nQaHg-+n0j@&l^HYCs*Gt#pUc<$_|{~ zK2>v|%pSKRS$fRowP(=p&BQ+Vn`vppe};BVp)aI3?xTUw;baDhRO_8S?%JXFeGyz5 zoO}1yD#Viiq1@5KR*y0DmXDJQG@EK;Y6%fMWI9`tLv3<^kH3OOtukv5WOq1V+8;q-dc7fUV{+4}^z)&Ttak zi^=sbo9L!{^R9w3<;q)B+5X)PV@RH!PD8%;U_4P*k09PCzai2&8uPFuhpP>4$eu4Y z+4*E@g%3}ZpmAdL^qH$$@w040n(8ala`}ONH{_1kps3tQWzV(nzMMbcD=&YGvd{C+ ze-^KN9Ela$s6hyo3}fp4EmW?wRgp5fSXL5_x|n<z{pBGmP|Uy=v|~1AUco6 zbmDv9?(-8nZlfCbOnrmZc*aay!xM!2+Qkn?HmJrNnHo}&6I(@8i!v12+;~@jpA@N$6{|(-;5t8%?zKkhU8v< zC`(#Q+Z)T#lQ%}Y+J;$;-fELXZ~SiBe{na`PO%uV+(UTXu+fpC+pk8lrWkATxrpkJ z-5h{7EuiMu5)+7D>2|P<1RO|5u~pZsawxfN_jN^H&ixJm!&V~;v~A6LQ#aq7B|`SG zp>AD;(=Gd1)$-fF(FcHlo6)Rw?|D%jGh&q&mnB_y&Fs<~l9ZLIXqYTlfvG4VEe?`U za+WZ=DgNpj8$A+^`Lx`IXVlk6<8l{tC`!u&u*Z9ko#bh-BeQmhI?DJ4#lStElXawA zEx~tN99aGl!-OU$lrk1Y^D}%4oCD6+7WEo*#~x{+3ZXBpT+u|{P@zHkos%plN0V>+ z7eW!7bOJ1#WG)=nlPN$02DN)k2dT)G(`u0AmQQ^@HNsfgwDf+}aka>&;+F^~1yu-I zxy?Ud@b3!edxo0_Qo?$P$<8>%yyk!qSVXa8Tp7m=>Sh51;m%vD-mlNbayuqmb8&?1!2o%&lyIX)aEfHNZIRLT3z#amd*Cq8+)Bo$ zNBv#BKR}fg*qr7xEt?9iE%AfhRtJ=_ny^%N!Y_k7Pvy*j(I7EKU>foEeuggQy{AAN zw5w%OW(B#}_aVODvC0dDIo)=VWGF1N8B(&Cf!eE;q|@?@!x^FyVX}FT@EEVvh!FOT zh!55Jxzkwv#VZ1Eo(I?PF9Z}n5o*}!EQe{f(v51f{Mc)cpVRXQo=8-`|{7;xIJ5R`p_{``n#F^SG8w2E7qLZ03QC-AZfaSH6 z+gn61m8kPW1ysBUZRa|LD5TqM>E2Lt7&F^CwNy5eJo~O2bN)E*h^%fLyOO2r(A(mh z1pFO08_V?N3;>Z!QkqaHNO?%U>xI!ut-c*|XCL)UPYr{g;#eKQ`=eYH(oG@z>s8FZ zuk1-4jK154a+?dG$NBKIrP2PYVzyedd^W=hlpYEP7e@lHRW%tu_j-{A;X3*(Y8uu? zp=*}yzV7v%N+bh6?ltS}KN)EgmZuuda&+Pb9<*OV(zd193wF|j*LibJ3>V(aF-9Jdgqvt&8& zroXsTjcO6u^G+HLFHPnos#!hsaA@z0x9xlWj2f0tU(oT6EE>ArF4bDHY#fzh#)~=lhC#BzFD63y&{&AYn>4%}XfTJ@P&3Xfnmk z-$CwY*K1OnzW7ATOkabNMAf?|(z6^o`*ixA&m;QM_uVUeSi+#gBLRC>zT>fRAE|c@ znj_7bl#jC*V5`rFmOPbi<+yION>Q9;1dsAFVo7&A2&o1=&&>E0YLR8r9WOFrHf*lf zw_`ht)fZ3?w85i!P#t0AKQk;st;i9BOCjRHn~i~+xPb_vAQif;Una=*So=s-17hSP zV$@|zpe^G4^}MU_NggX12Z}ybt#N1TuvqKc$v}-t&@0$TfV*-FoU=yutNl>sm1$?V za?JwJgr9f9Aa7|1sb+hw@7{@|Dv4Rgx3RC0YKbm#bBS@6$Ufy}DdR0R} zhcQJyuEETz%jQ~s0y5AP&uU@0aOF*6h^PPZ9P+gvxk8PlFRcRPDNxZtNie`G=Ts_H z{b#hq$#XSt+P7V*EY`TiNtld@4p2TN=fDyGlq@NK_-PPZ*fVbx zScE+9nVUj7zG|Xa*;OM`C7T%Tl=eDZcg_^~!VAfFuGZ!eywu#Lwv*in@$#AOi&d&5 z4+cs(!p7YB7}H+5l#|M{0-QwwEsy$!9Vfh7wY&tp3l!V-2v<69vhBY21C)+0n`Vtk zHB!6MATbH5d_F*=G?=caH~*|*MPZyTVI@ww=THqUwIWwrf%C#`kG z&b^zqCAD0`rd-}g?G%gh&Rz}u>3|wlo;8!n1D@!ILG1#WDL$)uy#A|w@26_{G5sSEw^=wv@X98c;{Mjw@WX+=(~w?T@+W`R{Pgg3Z{azU+T$95D{Ds$8cS~ zs8g-edG6YQc*UM3KiI?LR6Fnq{A7JR*nMy>}uF?|X=whPFI(4PUoz?%drER347eW__npIrv)K zu(weyny99=j;-gdU|uu)j*K&L9rbGIOOwAWO8|iddUY-rTD(k4Q#PIo%9IYN5t<-e zS-QFCU~6;!d;dwN%0z5wzdix6Rl$88q1sKfh=<$Y`dshh(9m`|XWF&1(PWXxGPD0= z)G-gbz8k9@#5rT5Pr{&Q1-};7u_AWjd{ed_qLv3N?)1@(Jf9$`zf-!`Nu1BrN#*=( zP%zR3IW7-qHTJPwPijGrsEsgT zRL4#9OyiGqP;40aU4Ih%HnG~}&RcBSBv>79(iR@ITczXHn8qe7t*Byl5=icB!pA|= z;`!)sBC`^ZJnn${VYzg17qfWJhH@-ayRgO3_vAF=Q=61Z<#QW>ju9iF1GIWpOIp5! zSyz7OW=JC{;~MPoc+sR6XG%J^M7~a5LN#$OT!6jIM5iy1+>T`p1vOJ*)X77oh`6m+ zM2f5(HY~fNHJHk*NJ{D~)Ks=Lk@X4XxFG#>FqT1W^62)dx4C> zi|Lz`=6gYq(0^nVY9jDRG?46B-G=ksQ=TW>aa@lwm9xHarqGg=KOrM^d-sc0MSwXS zdc+=r2}9)a1gLdR2^-R#n*lRoG_;y$`VUpxJhnD-W}6^H)OK4>0kLbh9^N~2smzE6?O={dR_8U7nJQ5WLR!nxc3F=DvUiW zk-Tr(&MtdZDAVhe2^oq^0psDW+r^zZu92VY7+M7nV`Kb&MOrz)Y&CY!g-G>Tjh||c zoc=}7U%cn{r`Hrrn>`5iV5+|wiQ##`+KLZW^%ROs#!Nv*Y&rjadbJ*Z&nQh&w8xMQF5r`VQd(E$-_?TglpVBVE+=$fhv@LVW=0MEN>CS7E;a&z$gJ$M+ zQ1ha{Q?a;yc)OWt0%YGVo1G6i3xdW@W_q}`^vPkI<*c zAB&C+b@ZIg{6nF#SBjS|;2h?GA9S86plFQf+$Ef@%D@RS{&cI$Zk z`5EmE3A5ED)VEIWG+!l`l@xn!np#Y)nwZQZj5lsXb6@B4xwe9ym^~(7F{R6TnBb`Z zhS>TBniOd)?Mw{w(tBUZw5+ML(oZeOGXz?g_pfg}TL=s`xPR3{e3*`oyeYLggungBE+`&CFzP&u{KT z>+am=40E^del@LkMEEZZ$39mTpC1_q`+TKLIN-kqRf+aP=ou4p8 z^8JD%?HP?Ynaa8ALo|gJbxL)9aXdMnN^j74%^5Pam+@7Ytykap+LL^Zx+vFsS(bTri}3nZ`W)$FFRe;PPP$`A
G?ZSk?--38SEXeesuhg`DXBJLOgt%}ad}Po_GGL-;PY z^Wx7`C#dWoSD89eRb;K>39ZWk`LD)@Xf(= zp1gk6kgrVM7^`)FLXZG;<+mCNeqxM?4yIR9()~?;pJ<~si&%pz=B|K)hVjZh?N92+ zrt^aTgLC`xf~?{J`TyqJzMNLXeQiB#f%^*n8n(1S@b3Td`4{KKIQdfUC4`pSGn=OS zSjd~tS9kOYp#M3}|Elrv3WOtCKn81Wf%j~A)V&Nzdfm$*jjT(`?r~QDmAcr|-NuJc zXnegA<5(gAsaqvy>;$|6%v^um zc}<}5^JCz)lSKok1E7agU+k3Y1p-{wNWSNokxYK`LhzTbSbW^;DTB?^Ei41@Fwr%z z#}q&;p1T}$V(M%J=pA&FvmPAKp2~muap(658$aS3`l+L2XsE(6zM>xGYl;}IkUZCp zRwZ13l102cO zpnCj^V`I}oa6rRGVZiLhMvenZrP}Iv^ScWd+?%H-;~|eg?t{c3_+|covGS*{I*_`<@qlCVs0%OO)n?{;RNBp|G#@wkXFoK(d<-Vf zU0hmPE;^a|fMl*^^HEG`JW!k%$KVfmwR_B}N;2Ab|K$Av0wkk0eVztPCi`oePm6yi z@KWi%3El!$ zCQqhUOfNj|&prF00d+R-(6XkJ5vj8)r<2X$^-|BmP+(Z6K zI6(Kzs4|M$Ys|k-@GTzQE~|&&7XOik8qn(8y|zt}p2C?Y_MSyS&MJO)EU&)F@>2hF zwqr`sSm@%i-}`@vEU`DryXQdbwo2{LrU(@nfsn-YJ$U1lg;bz1`Xgi2)Dhf6 zr7RI}mdH5-TCed<*z>^VfdRSiZU=0t9dX3E$K94kKvh?MiC}}xtMT6LxSty1<8{zx z>38Lp%sZFDFPSfqpZQn0z`-`w3lx!$ANxZ{{gOq@9aZQ-frGMO77)NEd5 ztFirquRinsdjOuQfc|GVy1SHhuK5rOfui+xhUfKbRvA)6K_>VG*~lJI@Z6W*iSG+ z7#yXqOzg*F^uh(IpFz{HVA*Q_PnNCTsXBfh5#1folVQfUsEjzprK%>)-Zc8${~jPn z>)Z>Mq2uBhWJa-ShRk5>+)Aw~)f3FU=$+28@&~G*d3a!w|4)MH&#U6+V_#K+*@%B$ z5k_xed3dnW{y+Y)X%FOn!LOf}H_@dN9t^`ibX~_uzcRr7r)wirhej9vfewNX`=nzcjqnYr3P1&2X9l`VWm_|Ow(LF4N@LjjZufV zUFw%WJ-nlPmlu#pL(6Lwb4!Fk&q#qmHWjMDSc13Ba#4P;H(~gMN0bj}dL@~~!!nhrLXj567q3fZgI?R~(RV41>7==MAI5t`k z-m@c62Q(`!-f?${p_%ACs&B90bTW`qyIc8DMA`J6fK)#q3<=o}3G|Nov(-#>rIB8g?yZ1A~YQw0l9(*H$8h{XK`>_RqC-A`AW}hb%h^7TCxEAW9$IMqG zGFa{9>AOC@OET9$RAknz`R17oGO5rnaarAEL?A@{ll=v$vtJ;kI064Mf}!FJ5WRWz ze#WqI6^}|29~S$!B^tQ=(BCtg91yEWs!x48*V$xG9v(z9UUhCh}$6jtzE#uKG(t zuF8j>{_@ZHpK&HGFEDYZB2^|{@$C#u`}_qGVixZ;O9C`WTUmtnfXa5cKsxe`v~`G_+rF#eEAk zDwFa3DEPZa;@LFCu|J(L8)?w=vbav}O}8;8X**B%IA?WK_@^$v+a(~9rjJ}i?zLUg zP}0vKt)=%=AbTq+&3e3_jeW9xRb13Ifq}NS?SY$0n-=Lr<0IF$SZ)kt$ah6M6y#8l z5sNDlkOBVTk>7x>2XBW==H1QWmwP$Dk3W!Tc`K!EOpccl)h-LVE8D`@1e)5wU z1FQA2@G;1F?&n2a95L>A^oL9na`*OaqBuHxE0 zhzfjM_->RM5Wphgc<>`JF9a--?J*{Hv(+Yh)x^~~%9@hn7hQFTuAE3Xt53`QQx<>- z5EtfSb&IN)x|^dv#j5l({EZHzmW--QL_HeUHskfcrY#Xt-ioCm1X}tNfr$Q?PeUmn zB`St|pY$IYnb2WSne~*V`=dCje@=+EA;zpgV{EQWl40q0NW2xVYl`9i5D|r5ha{gf z>7TSV4zqqHp*YpbLpt-@GMp?1mgs}jV1Mrvl2?2)hs zeI%*VYHg_k2AC-s$j+(06igt2vVcjYCFCpn*N`{Ft5oa1XCn9-D`|~Uf7+U7g0Fsn zz8VO-(#@MmMcO1kgMAopLIb(<&#O~Jv#)fl#2OabHocT*SFo+Vhlgn4W%MT=iQo+QIm)2^bLH zlS$8GZ$uk1+AHYOH1dWw)+} zD+ZVviSW2Se5X!~%ITMl9}mCJfx*NgoWpn9Mf#TYTU4yG<`wqIdfwD&)?Ik)SW@`F zT7%X>no$GA{mbrmRi?{G8USlPHe{|9atR=@s&;;>^my&`u!VyX2Ty>B|HVUkl)vV7M7Xc|0RHQtk9JN+(Y6{ zoHQPg_)A52goA>HA^v3*Dxv5keG^G0~}hq&&LAQ=skv`*+mOE z*NU!!Btbyac9rxU@LbB2Yx{r-9(l}Z(0VYgM|e7<`Ua%D(#jH8-uSiv4LOUPXL^r+ zERioTeJX>b$0EsllF7a3N@{;lGQl*dViz*}iA+~+BBp&fJ*JxFK>&IZ2uiWZoS*o> z?BO;E2_x9=Za5VES6FgmIOow5MJMbKiu`#JLzwG2FD~_9q=>IIT5i~baRUU>{#hVj zZdK*oAxtr@Sq*^0L}4VNj(vZ=n+!Zw!{Fx*yWR#hK==1b{P${lvB!7`+5-e&%_Lw4 zyr)FdyfcWvel8qg^4%^dEGZX=k5e3F{!ZV@&HA5c3c*d zza*lJDX$zX{<~)S3|s1F0yV&+q+i|5B)-kN+;eO(!`8Hum%1ZKjG~e$CsiWRG+wPW zq#P`xV`{l6A0LQViEA02N3Xq1FiQO?9DT@-=uFKL&HsWlf^p@^43D>9L#0*awm8ZD z(>6*ZwiPN&B;oL{Lr{%GSpe4PE$#N28IURd`0%Z=3AWekB;5+$0yfeEZ77qC@c=Ci zEP-%wpU1;q7l4H||9zvRcKN9-5Evg2MwFOi2b#}GvW(h8Fu}2&5`;P*&d895GNis} zA#+eYSK$fC(Xq!8bp$PZMrk461$7fJAH$v{&Wbu`AoprynjA(`RweN=xN2QTwV+vL zQ7?OLurtMKqZwQsu|YHSLXfjJTtTG(jPc>ydk_~cywg`sX%ph7_;Rpbxj!!+q_ZG1 z&HtPeR0~^^5&R0V5mX^&x;=Ou->#fxNK~tVYJ1t>(sbe^Eb$) zNrY_3_NNke;G`Un&Ib62Af?P^1+aV#EN{U^PbM z1|xfzn^*5ZPRj5zD?Nn=Dq(A_SBiK^5~a>PvfA`-21Lt}7F80$*kODsEf$SIGdp}R zVZ>{oP;c4s3HHQN@*3{<@4=Fa%v!ZHRmJ?Owt^(*f!<=#`0tD=ZXo5>Z{=}}Z>HWI z^JY+>@`8}v-FS<{+^J7+52`tntZ?)yJ20fC=rl_bK@hA^;oq*TS&ev$(;X3z zk(-dh_3q}Roq8%LzJ;&%q%HGcepM*P;t0={GUN%fbHq=4-}Fe=gpI9C$$S=K<(aM1 zgpapp?__!T;(-kEi}l8{adi{)ox-hGwQXIIEw66{5lcZB*%D@8zt9j6DuE;sq!_`K z&QA%M7Tv!-9{y;Q@C~H!T@88tci@$l0sZ_%(ISyAi#e60S~O5dxCXMCon7}rK9i!x zk?*2>C(s!dO$&?JHR(R)reJMH_lv3PhmKpR?`Jw(qX32WyTnp2f@178ujGkl=X`** z6fQI;#J4^Sfl7eq&r0j1u)wE;sSV@5Ws7`?H{@<|41o>`8LP)5xbj7y{bltk&kXA) zFs%sYeF$H0;aVQgBZKKdK~nD&|KWy)rlFE%$RShZxO|t(L&1>&&qy-+q$Xy%We9=iQ%WXSsrrHXBG3e zqY^crTpuanJq>39fprJ^`d-6+HK|d+6QHz2?1tdrj}f~Vk>epuLO&}W3O z5UQA22s0V2h>#5ZIu3Ia^%0wTrK%yx*)s>!di5c(MST4O0GU;*2>~I=h^@hO36d6) zutO0XO#}Vw&k~DW_*Uk)m?j&7(P+o=Cq)0n6`_?tsAea&dXcgliOcGi$gF7i=YW}i zhDn@@4{SkuN)5y5?s6ju$V0T71L%ri3lrfoxUdAN> z7U1co|Kb2-+p}X|0nBdD%Ef>pxq1|SJe@wEUtT0Qfa$*_mBWlfNGTMu!+f0kLKE!Q z&2uGB6EYHv`1QtT<9U@xFaF5_1H7_<6L=&mn^kaPZ~_Yq+x@2VnL^zXD^(y)Ux+S@ zS+i6A=Tk9Bf*1ro*g_5s(RPrwY(KjV$n0gQy+RKnPBZb5DHQ#s)F(q=kuSn_$Y7}h zrig(O;`nGo)hLrAXxbx(e*Xdd6tuhrOHzk?4Cz!u!eU$6;$0Ns+VX-7X>`y&f z! zvG=<)M}9BvN1S#A_foX68%UjMT$(m~po>}Ciii>qL=J~$_I3Y2phW!PO1C=d9M^jM zuk>tilArl%Xpds6Ni)_idT&*`tnLwlL7#r}BLEyG1~D~H-K7>9AORs@B2#Ttb-`pM z631}fE?-BM4l{t*yroWaE3ZPxvhr{Q&QY^}J5DAY+DyJ(LZJA}o@vgRK4?ec0Iw|Q zg{@XTn*?42FDBh`&#IZ%_95E>jZrP2y_w&}@^;0mbr0wpPnFv$*zFl@53y=qrf5jHSCsWwt4XFs>C#kf_a-ru&JJo_I}ylB1TgWX9&XO_ zJ-P&}ML6qo=n=>JXAt>;N1S@M$&?1#2%wOjpt2mvl8DU2f`Ov-M0Roe zXHe%eo6NJHta#$;5!lZj3Hb;_x!7$8sej))vGt}cP$2UyZTNpR?fwtn4EQWrx{R7DV+P0%O<0VdqRZ;3FN7`fSLuU?hYNXU08dNE93yxE3+ey( zd+NY`0*w$M0=I=0%>&dn2;-rF;MoEDni|M{4On6@dnl6J_@)sf77+f2e@3AdH_3Sv56GWaUtV%0MK5fJO^p;O!R-0HbZW2!{}&%he?P_ z)HgS1A9kS=r%PDd9Wq&id_Pt=9{qpkhVJ;z_F!5KrZ>d*cR3$kMgJ5w7DS@GQV`6d z$&V)eLG!~PM>sJ!u;+uIiPBR%7Qf)nl2|Mhk-ij)Shw2yr`M$$4QX>t`?hJe3maP3 z$?nCI+s?d$wgdC7#iw;8_Qiws6Q3rP3A{*{R{}{6P;R<}Nah&>`NLZyH^t@z5QUz6 zCr6_&~b<9$tBVl~RxXt4h*V*K^hIw}cQ+u~E9pIySIW?Q8=Vgm@ey84>MuE_ZwnxDllV4skOu24u%(;d3*8%r z-2+M9ZPOTKm3&L)HpRb9pI9zWa>me0TZ0Oj7HjWBiaizj3_*&H`aCwEmCal&4NNDr^*FVB> z1J%!PaAay99Ti*J7iQn_)s&v?7Z0`E^)^cJzQCBGG&!kc(1p3az6iDBKC!a&UVIL9 zk&>#t;((m?>La)DLM^tMu+t0ho&Uw9I^I-pylMPu+x_OmYHzADX$8EVFK*9PQ1r>r zM88NsSULTPhIE_6?RIItNa|03q1DROj(f!+wzymsNCG}e_NGg$OMoPJk#n=NVTBCs zio+3R@rZAOYcL-e%1yyzYZ}-7@{O%<2;IBT-@LHn71w#cd3j40A~dG` z*o(yL^OvwsW=Ov1&+KUe`EgG6J22&n0Bm0h2kMQb6TlA?j)3aK?^hF#ApHnx4^Hiz7Yy4!N=s zSOh9V%e?e(UvMLgb2&h`tJh#AJNizhAx&PJ18k5fnYxn#Ww2N%_Xg~%qwg$$*7|sm z*2xdJb(pKh#+xm5A4q3eRgBo(?p+esu7c!!&fH*LX%Z3)c_&B1WxU{xcN2!=#yfS% zdlhZ>Q#(aAON2O854%TT|ovWIU9cfWG zb$EVXQkfggBRM|%@^2>LXK3SN-{&jD?U@Z{2(*veW~2-QA6LU3uHGUA zOU&h?m<$8-lE=YIroBxPZenC5a64$*iamt(DjaMqd;7_T`KlRU(<&+WPGt`C zdF`Tbb>1rt$$KjKeSmG#D_ZpKkZ16A=Uqco_(mT<)BVQ8dmrAhf3k9`rm_23;m9Mk@F&d*u{9RROP07W8IFT6hM8 z#6n=N&^Y6`;9iK+xcKO$VEh8ur23d5fhF zJk5JMB4Z5d$5&#q_!4nPqiH9?6(YmrjORut`!uO|9(9V zoOC}lZSd~rs{pZmQ&7;A41f-Dbkqe%t%@M6ZPkUFbq>YfZwE=VnB107Ms1-`i}7<7 zSg*aNIR1ujnFsMil?R==SPKS$6Jqc21z078R{-m17;8ZKu{&GpwnjJZ2@Z>~ZyI;N zEs|=VLJz(VaR_DrdR_?qeqcJ8ucg$+*>f_l>c%9C+YajY5rR9u)L-PE_ipxKfu+kS zQkGfJh7`<=_qfEXl@kf9eTlK4EKOkS7`RD$gf=TEazS*OC#GB-!%y|z-=q? z%DK*r7J!mBLrM5!%$WbWw zyNSeW#n`iP))9oKGtGzZhVCV#zY%VVzSZ?2`v5I;ru~!)hfC?jFprZ=y9mUHSx^QS zfR19$C%}P-70=l+so>qgTV=-CxMe}EY%+7bAUdxVBlANdKqazTf~5Yl=OJ1O-Fz=7 zYuM05h{W9c{Avz?FIYh{XkX_ZyNfRs0?|=8=#`gxfU}QsgtHIRsR67RLyOY^$O#k9 z9Om3NHN%1I+?S1>4Ns9$UrFio2eGb`di2p`<{NJmS%aX0w(JutjMJH???DR!<8b-8 z9k&zExi5XebI`hK;ra`IK3rVyJ!Zfg_bn;y0;j{9RL3w5*}cDzPJG}2m!r}4xO0=D zEEnbN60O??_kx-`unZ^y&F6#4xodE+zfVE#+b4@=wEnxTbuPJql`>t3vjGDfn|*jz zl&s`}%{MFHMUjs-X~cIuJr3&8$bn}^(qu_EfZK5jw7$9AkKdoy$sJ+52M3^3jYO*c~n=r~3|vHJOzH+WGmVzqro zjP{mJdA~h+Ta(!?&A*uY{N7j&53G{dHeS{ zC1L($%ZElm7)u;DbX#brD#x5yU*gG*H2}l!XKs~2E^idmme0MqYhI84cP>CJI5P-v z+MGPiVUl8prC$+T|>$l?#D!2E(nIehAy+^#{OSSUcI~ zPI8zkG|jEvn+63=A4^m^4O~MO;6TPh%K8G0kIR+#YREq)m{6e%8Kd|@vqVm3W4#8w z?;Y~02?Y40@c4MknO$q{yG&vcB#Ozq32rUhr~w-e#WY#o(=n`#q5Z0wRKAbvm|Jk@ z?`08miKp+-^yEuZDNHr3F<9k$0JqUx{?RbERdPihp~FYF*TCB$5BNlfuRgj3Jvb#z z+kQ6M$GYlUKZxkRBVea&zJ-o-hE$KYCDDhwPwI#NB%p!w`WL)Efz_!VIy`=3!-0!L zG7`SRL?6Nph?hTIfayn^&4byy7}@9Y6(X*{FemG4pNIbrsa<&>iMc{2au;+ya(&+C ze{Xwed)hn3yg6%+VH~8Q$>YruTU6HmG`|U)=niCh-l~&QmiI>Qj}x%IzFIO{N1X>$ z3B{ywCRA(Qs6ac%`KVtX?;DxilQ*2-pAPPyJc+miJ@3dX|INq9$tJ=b``5l8ki3^@ z$aH65Lw3XpHJ*ru@{P=J{tqNPJwf#NG8jFX{xDKyu&oCbu)`9>b*LO0earnIhnU`D zYLR4iHi<<5fN~$q`iQ4jI0wWrsOPU3Jc!Vf%?T5rcvGX?6=ry+gC|pI4_Hakt9w8# z@nhtGY!bSr)&Lc9BWPf!eK~4?++1%|EkbN%dNYrWI#CH^J+b0Fy-VKL}n%?xPr*phG;3jB7L?5x(R~trA!u z5=L}}p$i!8L&P{`K-P0Rts8V_%&x*0MNFYKUq^KiShsX+P$vr?neO!m9?D^oh!GmI zqxLrV6kl@C(OdR0!7IL|*bP0a8l(6t&-p`5QS{%A!r5AyTj^XLp8LzzwjS$PRwM`XR*I1nbhv}%Z!0Mr_W4+?nPBmnBlv_^-Lbj8s;-)&Incmf6ElTOTZ6K#~ba zVaJ7lQ+7w|8;r&|APvrwa!~OiQ^I!%p#4T2dB>J_Vlc(mk=bnz)&%%jp(`Ksf1&yt z5eyOJkhm@%2tWkx6dBrn41$ls>Di|#p?BJnMA`7?yhtEQetThu2tJU4CS-*#58dgk zy1?Mjp~4Z3vel*lsNmk%rDKBRddC;nWi~+Jio>*N`JNZ-f|3bv_}g($WCua?z;Ph( zKP%}5Y6kVv`sMF*BNht{f7cjk)w`jDZ5MRXXw|>b;|<(3?OIq#dBqvME)S_ir@s!s zuRE59eZy9oVOY^fmcqJ&SZfCM;OKRp82?S$xLZA&GusNq~O+3l<0m+>l(mU*j^Wq$*iJXX!CBmZXS| z$zT&)xl|H|7i$$B%sL=Ws$+Bci}>zC6Q~EQnrcbs)6tlYMG&jVH`qk4V7Azg#U@Zm zKl>IZNcG|=xVe73f9&qc4zkwSnR!~}eTr+eg-=kF*QhO5f6IVMUTZ8|N5T#2B11*1 z6xdNZDR)5H0rm1xZxeJ*P~3=-dRg{<&!lcCx$p>x(`e!OKGOdgV>7l?nhcI+HZ(X_ z`EnyH_rpg#YZS;dJU5t#>*hDmI*-FD9JJ6Uk~naA2xk5tB`O_EL7QyG2&#R`J#@G1ERIw*vYBcc)?>#Rqkpd!oYtvEZz@iimD; zVuHgAh11ZfMkn(KD1(SVaU!*23SJZpA6d502Q`#TQvwV)qo4I&L*r}N$?fI3B;FkJ zPWoh@AZvDUituw8%Q*nXzeQ)Eoi*p!2jtsceVR!|xkZ4yhW8}yDi1GeZ(h-l(e`KUWvP?M zQ*QF~$~SLqh-ZoJ0ihw%TOUO%N+R9PuFf;lbkBU18O|Z*T%y#JS;>&9b_2bK8^U? z?F-N(rEx~fPOs<;u6zN;NnwWG349kx3pZ5GD9#jBGM*920{JQS(d&%ji$918hU=X9 z0eZmm^;KFjA!0?sGo$l0r*~-a|83oF&$iWxfkM)plB2{1m_}3RQTQbte;pWW0t6u0 zmK9ockR!wA#1VQcWMDN8Rg=AL-p?i^ zNR|_?IVv9Cv``z8hsn2a{kF}96J}c+_AQHnJ6Mjh-f+bgEjuRrH1?bxoRbrfuXm2c z3}8u9pf|Xgv}5l9pT$TPkArWA%cRCVNwz+Xj*YIVV&QNR*<}bC83#4Zj2$-G;Zcct z!@%ev#nI~F&a|E=XA0q}wcFxD;@VYdo%bz{plgv`tnE9R(4+wpgk(F1LF|eOpX#Wp zKMuQ4_FP@Y)O@ilS)w!B1h2|APjUo}7%AZwUdo~G;CT=@Cg*Pdc&NtUk$}>z^sIRt zc~SLm+xr{yXqEyV(aQ<*RIroHXK)7@wLmar)S70DPxV9Bc=O{^^m7IXaL|kvOyJuZJM?UNM8;n?pfs1m`JD4#eqpx8o2=pR75GB?RCqT7fHm_uvH;r9lqs)E z4Ix7kKTitf0eVTfUvgTVb(i9H)Jt2s4?6zSY#&`li873R7b6~J3)AL%QeNTPJD@-p z6)DD~jwxCer?)SiE496VOr0Dqp8nQMJ}2nw&TBd6---&)eko*E-o?Nj$7Y{{XYU~N zqRCPOw@oXk#c87<-7`1U#k2}Ak(K?4J)G=8{@dU(pfzI=ZY@w{~1~K z<(DSQKmz#mEk5e)|1C%w=uNb=TJgY^cOyagq7U>KBZQ7yPDBB{E=++%oQn{D{zgp< za7UEGVf8-Xti}Snug{4L=?1ao5G|laiVTgSjN(e(F}!uiYZ(a^h$jd0(q7~sLOgVk z0M9g5m=FE}1NR-s0sddd+yDQY18;61sueoQ#~~ZdTTsm8LwHhbhpE7X`}OQXb3Hh+ zm3bEtQ1d|QJ!@Z%${c8?K;LAdw7IY&dYasEvt0o11v)pq9f8isMHdznls@?!G%1oUcIhEIN-3@8@nXxWvHOj)@w=v%SCf1Rj``gwq+Uqr zwp0y!^=5GgYGW3c0HalM2UfqO1Az1JDUm+y; z6X_)&$gBLzn)f`;8&p9GCHvWIRKy=j*~XvJ6%8TK6v9DhDo8!>rB(uS8`OnRa|6?A zbh7!Z%I@F_{GY7y{W<4L=G6h6=B4LWxErkcP57c;=r5Mnm_ZCm;{G~~kE_*Mrw_U> zjyuc!hv+o2h%La&RD}tEU4>R|EBLHuUD#BSRlp%I+bE(Naxoqs79-|eX|Q#Fz$_A- z>}WzKp5`+GT0@^tYrA<%Y@}Y~r`TEFt!JRR8>$i| z&!MJ)lI7YPr;)@@>|!LYS&0`+0m<$+0)JMiR4Gp_uBVgS6qu{BXq&9kG6S_pMN zs4?>2FF=%5ja#trWwWdR>+9;zgEH}hyOjQheNLM;^7hHqRvgLIr=ZpF20a6(`M@ubAKB*!0B22~079-9+(tkrmxnc54SBd0 z0wMwxdPxnaw8rVQFMkX7RCb=R*lacV^Q@ANL-{M*HtkyD9bayfc-8?@-2pb4)ih=v zp$FteN-5|7uphpN5!M0D%=*ID#{kAc4WyzddZAWQL;O@0(VpJFLco41ubgUzqA*GPy?o#h?d z4A31BLBNiTdOc$viDTWt(6p?3pFUBPIJuk-l@~O)6h$g_;6(H!CWL*4#u>qxPWxsV zhx*9GOnr_gkQj7{F!9WROlkgIY1PE1t>gJg!&~C2WRI(@7{12PQASNxZD#x?44g zfe{c7@nw2h&jHrLYk?2m)x894e|F){VT~Zufm=NuW!d-Z(b@F(HCikIPbmF22-dv^ zn6Lsb09p%DL0?wo9E7u2IHS!5&yVUOL(|Omq~3?l-Q4rc&JQgn3vhaEFNM->lT@Qf zg`7^&)n1~_R9EbWD1@8rw_D6)(}^BHLs(D0Zm-)9D6I4;f=BRSP-~^A;OC!*+V#;B ze6S(JVZ1e(QIS?IOP8&S5BUn8?j=vT;k%C!JH<&@Iv;lN@&Vz2{Mp9xsDsB)G>{Tc z|AVKA%*AS<5w0Yx`Z~`fmJwF~$02!IxYb!VvyTZ8v<}tkoj%K-Bu+LOde9{;ZBw2O zVwYvo58#W@`g~>3#ktxcilL!AEfx069Izz)5DfFqh&}?-2X(zZ>(#{);!85FW0^!T zkw1>FPJjMu8B_!l(tXkGX4nIl`gL+%Z-_#pAbN}sXEr$~lwna}q*Ng`3Tn{wX8iAx zd9BD+%fNK*V%pEalH(}O8zgd2_vulv!eaz6oYc!cqa5;Ql#;Z!wp>f9Emz9+9q3}w zn+{`YLj2K>>)?LNi4L^co}+t4W0Nvany<(x@=5tOZcM?Xn%su?qxnz;Y%hg(>66)T zhqk_HaU<8vXpz>~#*Rmqk&AED6+xGIx;wA10m9`<%yZ_;6}s}2nxLAXepB&EdS>j= zT-(!>UOVhbnQN0cg{uv+IF!%mj}2!qTAbYnavJQYi>4~20HWc*m)UGdHiU<if6jf;C!WwqHpXfno=`LbgwXPerMa8 zK1W&esRrGB?55&N=;El=BWg2!euKv}UdfwkFLon$D7-;VeVKkmty}SUS|ODkFZ|^5 zU$P%0xkvev@oV-0%3iK|c`$wm`xzmETqt2Se2Fvh-m$Xc8{H8b9gCc?Vaqz<=} za1)s~WtQ=aN>IU@G#t0cA5vpsMGj@EsedLGMFXMOg`nR}E#lhab@IfWW0J3EAYqj` z@XVoiZ$Q;^xd8>mOE!YP#jZgtIqdNSXQ_A|oj2{f7ak9AGdVw-%VVvL z;G;9;c)o8F*O0Y?H*bAVfB9mW+JnePm+iH%&=DkKO5>^+M1kZ-2C z-&ZSXX%`O5&&2V19i2XvweVwNOeq4Ca2rWUHq}AxKFqHl4;a^WXv9f;URMOF4^St6 z5j0gF!098Iutg?+q=(LaOvIsFB`5l&SrBVnVi8cHlcLqS`t{#Ygi_#rzojW<4NHoT z5+_R`q;SQgVtT7b6_fD>n+N-(hx5>C;Z+v-={_-FAV~a$W1vLs1!mGxys&yYhe@tb zByPdnW%xUg*Fns>-ptkDcu8Z8My8j=!0N)40_Pkrn3h^IyB#r%EUe^4Wi z949t`JiJ);-BwnFc`R1^>3&7qcNeGYv9)VEH>jP!r>~E$TCGGywp%``-{z+gStasK z#F zIU>R7nH2V~B{iZ_H4y(`n6;Je^Fq?dEWDmy0bQt5mp1TXuOB3oR6F2R!m(A8SebEECs6o*67Li`Z4mN<>e8bb%L( zIZI|X@|k(7jb^HE42*sjo2<+ThQ1h%F=RY~W(ETfNCO%NR|9oOU3e_bQ@zbeKUW)| zvtO})@5eZ3zH`TmvC&OJWYam8V?>4ogVHhQqvJ3Kp zq_aUmzDYi5M(}*G5(d^16ix@Siw_5uh0>!a!Y6-aoq;poi3GW?tJA>nv;WtAVZ>u@ zl~HJhJqlqJV71SXWH2_}a0UaEw@EOj`nDU^V$~U$^M1{6yhiT33SZ11)N= z;2QdeD#zs-##U5b{Tl7di&blY>*X{mEM|WOBeEZp4{U|tO4+RMsfV#W;Y-**7Py~m zoR1LIqeV#Q#5U=yzbK3Hav%_`5old7?Hp9uL-V=*U7|w7&Ko1fGMrfgQq(I_PfWR=Bvgu zkwMQO+{Hkbx6d$e<>S2vZ+!iZxZN_S0Q292P8IdCiGa|#5t-Mt;iSc~xlR7{9k}00 zPnMrB;R!pPNk2X5i6{h&T5+8e5ZcXVms(M&zeH|Q-M(F$d*b( zw74G=JSc8J|2Ng1TdX^2|A;9#iWz^5Gkdi1Xpyj=VyLb+nPtz9$xXMuM=yzK1I2P3 zqncql_MB6E@g|(#2+{f4Y1GNk$$H|Weu(fQ*1R^+D*;oPc=4FdXYM`a8(ZuM+$`xN z;f?Mr1Y^VjQhFw`vr6tR9jUm^>%{c3|i9$HQR%z@8#0@=&7`4^^f zL+eB`u66TgZe?BEHFO8dZ*d)i&g#j;wtJ6h`)St_o!w>nc(_@sHF}ZL4T%A9jlh7>P4BB|36q2bj(@`~lqzpEt7cqf?LuH6G_1+hM?yL=& zlGMWO09K7=wR;cE32Z?-|2urK>gSp~i%SD58-T<_l!%F+pJI>@h$T~AQnKCjh_HQw zo;t~ZbdkN`xMfviP-I|2P3a?ylc+uRlU{CO>mT*`$en2#_Lt5@ zp2QM`IPZWtuTjpi{|@n>`dn=Do_(p*Y_s_oAh^WtP_)+r!FH;6$mE5Z318 zTZb3*3~}{bevaz5zXZ`vW$xF_csd<%un_@%FzVfp#}G9K+5($Zg?;b-lM7(S_)WKG z)_BzPSD6c%m%?SxwINLEv_|HR^7F!@IVQ@mP8BAy!EYq`@}gooKeyU8dBdo$gZcNx zNthB@*{dTu`6?veUOHq%4`~18Apcm<;$v4_xzmt-pS}8X3U2>mECL-T+9@bA_*<*q zsL!wqq1-8<*L*~4Gm6{C`+t9n!|ux)#O&4K8W*8^!F$0ch+e?}<{a{)L^`oxp&wuP zP#5n@igqTVQy$ts1H**wl&V7_tnx3;dN(+#s|&ceMW$ks#U5M`s{L5ac~ z%F?6tJW`4`>?Cjh*;VKtyBQL5RmPR|yi_^f0#8?VeO)T&KGh_tac=F~zm zGEyFSN2wi|H-n|xUytrGUJUa9g{dJMT|pu$tu+-bl3

sullv&d_t(So;WAKx#I<3|%_2q)X8_mJ*vs8;E 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

;P%l_}|_A@5LlP_Fer~3-JH( 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 literal 0 HcmV?d00001 diff --git a/docs/IdP - OIDC Flow - Biometrics.png b/docs/esignet-with-bio-auth.png similarity index 100% rename from docs/IdP - OIDC Flow - Biometrics.png rename to docs/esignet-with-bio-auth.png diff --git a/esignet-service/README.md b/esignet-service/README.md index c14821025..5fb85d12c 100644 --- a/esignet-service/README.md +++ b/esignet-service/README.md @@ -21,7 +21,7 @@ Note: VCI implementations are permanently moved to Inji-Certify. 4. All the required plugins are runtime dependency to esignet-service. -![](/docs/esignet-service-basic-interations.png) +![](../docs/esignet-service-basic-interactions.png) ## Local setup of eSignet with mock plugins @@ -29,6 +29,9 @@ Kindly check our docker compose setup files to run eSignet locally [here](../doc ## Caching details +All the transaction details are stored in cache. We support simple(in-memory) and redis cache. The way transaction is added or evicted from +the caches on every stage of OIDC transaction is detailed in the below tables. + UI transaction | Endpoint | Cache | Evict | @@ -69,12 +72,55 @@ eKYC verification transaction | userinfo | | | +## eSignet Service Configuration + +We've set up the eSignet service and OIDC UI so that they can run independently, without needing an external configuration server or Artifactory server. Here’s a breakdown of how to configure and customize these services in different environments. + +**Profile Configurations** + +The eSignet service comes with two profile-based property files, each designed to help with different deployment needs: + +* Default Profile Properties File (application-default.properties) + +This file contains properties with ideal default values suited for a typical deployment. + +* Local Profile Properties File (application-local.properties) + +This file only includes properties that need to be overridden to run the eSignet service in a local environment. + +**Note:** The bootstrap.properties file is configured to enable multiple profile mode "default,local", allowing easy local setup. + +**Using Placeholders** + +In application-default.properties, we use placeholders for values that are defined in the eSignet deployment scripts. +This allows you to easily substitute values as needed without modifying the properties file directly. + +**Running eSignet in a Docker Container** + +When running eSignet in Docker, you can override properties by setting environment variables. Spring Boot conveniently converts property names to an uppercase format with underscores, making it easy to map properties as needed. + +For example: + +* spring.datasource.url maps to SPRING_DATASOURCE_URL +* mosip.esignet.domain.url maps to MOSIP_ESIGNET_DOMAIN_URL +* mosip.kernel.keymanager.hsm.keystore-type maps to MOSIP_KERNEL_KEYMANAGER_HSM_KEYSTORE_TYPE + +**Flexible Configuration with or without a Config Server** + +The eSignet service can run independently of a config server, yet remains flexible. If you need to connect it to an external configuration server, you can easily do so by setting the following environment variables in the Docker container: + +* SPRING_CONFIG_LABEL_ENV +* ACTIVE_PROFILE_ENV +* SPRING_CONFIG_URL_ENV + +This approach gives you the flexibility to run eSignet in a standalone mode or connect it to an external configuration server as your setup requires. + ## API document eSignet API documentation can be found [here](../docs/esignet-openapi.yaml) ## Databases -Refer to [SQL scripts](db_scripts/mosip_esignet). +Refer to [SQL scripts](../db_scripts/mosip_esignet). ## License -This project is licensed under the terms of [Mozilla Public License 2.0](LICENSE). +This project is licensed under the terms of [Mozilla Public License 2.0](../LICENSE). diff --git a/esignet-service/configure_start.sh b/esignet-service/configure_start.sh index 2f82143f9..19346edb9 100644 --- a/esignet-service/configure_start.sh +++ b/esignet-service/configure_start.sh @@ -69,5 +69,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/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index de1a4a980..0de45f46b 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -28,15 +28,15 @@ mosip.esignet.auth-challenge.PWD.max-length=30 mosip.esignet.auth-challenge.BIO.format=encoded-json mosip.esignet.auth-challenge.BIO.min-length=500 -mosip.esignet.auth-challenge.BIO.max-length=4000 +mosip.esignet.auth-challenge.BIO.max-length=500000 mosip.esignet.auth-challenge.WLA.format=jwt mosip.esignet.auth-challenge.WLA.min-length=500 -mosip.esignet.auth-challenge.WLA.max-length=800 +mosip.esignet.auth-challenge.WLA.max-length=1000 mosip.esignet.auth-challenge.KBI.format=base64url-encoded-json mosip.esignet.auth-challenge.KBI.min-length=50 -mosip.esignet.auth-challenge.KBI.max-length=100 +mosip.esignet.auth-challenge.KBI.max-length=500 mosip.esignet.auth-challenge.PIN.format=number mosip.esignet.auth-challenge.PIN.min-length=6 @@ -60,16 +60,17 @@ mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authoriza '${server.servlet.path}/authorization/complete-signup-redirect' } ## captcha validation is enabled for the auth-factors - otp, pwd, bio and pin. -mosip.esignet.captcha.required=send-otp,pwd,kbi,binding-otp +mosip.esignet.captcha.required=send-otp,pwd,kbi mosip.esignet.captcha.validator-url=http://captcha.captcha/v1/captcha/validatecaptcha mosip.esignet.captcha.module-name=esignet mosip.esignet.captcha.site-key=${esignet.captcha.site.key} ## Applicable for signup redirection to update profile -mosip.esignet.signup-id-token-expire-seconds=1800 +mosip.esignet.signup-id-token-expire-seconds=300 mosip.esignet.signup-id-token-audience=mosip-signup-oauth-client -mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json +## Transaction halted with prepare-signup-redirect wait time to resume back with complete-signup-redirect API +mosip.esignet.signup.halt.expire-seconds=1800 ## ------------------------------------------ e-Signet binding --------------------------------------------------------- mosip.esignet.binding.salt-length=16 @@ -211,7 +212,7 @@ mosip.esignet.cache.expire-in-seconds={'clientdetails' : 86400, \ 'bindingtransaction': 600, \ 'apiratelimit' : 180, \ 'blocked': 300, \ -'halted' : ${mosip.esignet.signup-id-token-expire-seconds} } +'halted' : ${mosip.esignet.signup.halt.expire-seconds} } ## ------------------------------------------ Discovery openid-configuration ------------------------------------------- @@ -258,7 +259,8 @@ mosip.esignet.discovery.key-values={'issuer': '${mosip.esignet.domain.url}' ,\ \ 'claims_locales_supported' : {'en'}, \ \ 'request_parameter_supported' : false, \ \ 'display_values_supported' : ${mosip.esignet.supported.ui.displays}, \ - \ 'ui_locales_supported' : {'en'} } + \ 'ui_locales_supported' : {'en'},\ + \ 'claims_in_verified_claims_supported' : { 'name','address','gender','birthdate','picture','email','phone_number' } } ##----------------------------------------- Database properties -------------------------------------------------------- mosip.esignet.database.hostname=${database.host} @@ -350,8 +352,8 @@ mosip.kernel.keymgr.hsm.health.check.encrypt=true # 2. A new Qrcode will be autogenerated before the expiry of current qr-code, and the time difference in seconds for the same is defined in wallet.qr-code-buffer-in-secs property # 3. If esignet is deployed with MOSIP IDA, then 'resend.otp.delay.secs' must be the same as 'mosip.kernel.otp.expiry-time' -mosip.esignet.ui.wallet.config={{'wallet.name': 'Inji', 'wallet.logo-url': '/images/qr_code.png', 'wallet.download-uri': '#', \ - 'wallet.deep-link-uri': 'inji://landing-page-name?linkCode=LINK_CODE&linkExpireDateTime=LINK_EXPIRE_DT' }} +mosip.esignet.ui.wallet.config={{'wallet.name': 'walletName', 'wallet.logo-url': '/images/qr_code.png', 'wallet.download-uri': '#', \ + 'wallet.deep-link-uri': 'io.mosip.residentapp.inji://wla-auth?linkCode=LINK_CODE&linkExpireDateTime=LINK_EXPIRE_DT' }} mosip.signup.domain.url=https://${mosip.signup.host} mosip.esignet.ui.signup.config={'signup.banner': true, 'signup.url': '${mosip.signup.domain.url}/signup'} @@ -370,11 +372,15 @@ mosip.esignet.ui.config.otp.length=6 mosip.esignet.ui.config.password.regex=^.{8,20}$ mosip.esignet.ui.config.password.max-length=20 mosip.esignet.ui.config.username.regex=.* + +## Prefix and Postfix support is added to support MOSIP handle feature, Prefix will be visible in the oidc-ui, but +# postfix is automatically added to entered individualId. Finally, the individualId sent to the backend is with both +# prefix and postfix appended if configured. mosip.esignet.ui.config.username.prefix= mosip.esignet.ui.config.username.postfix= + mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text -mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'KHM', 'value': '+855', 'regex': ''}, {'label': 'IND', 'value': '+91', 'maxLength': '', 'regex': ''}}, 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }} mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ @@ -398,10 +404,9 @@ mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.id 'eKYC-steps.config': '${mosip.esignet.ui.eKYC-steps.url}', \ 'error.banner.close-timer': 10,\ 'auth.factor.kbi.individual-id-field' : '${mosip.esignet.authenticator.default.auth-factor.kbi.individual-id-field}',\ - 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}, \ - 'login-id.options': ${mosip.esignet.ui.config.login-id.options}} + 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}} ##-------------------------------------------- Default Integrations ----------------------------------------------------- -mosip.esignet.integration.audit-plugin=LoggerAuditService -mosip.esignet.integration.key-binder=NoOpKeyBinder +#mosip.esignet.integration.audit-plugin=LoggerAuditService +#mosip.esignet.integration.key-binder=NoOpKeyBinder diff --git a/esignet-service/src/main/resources/application-local.properties b/esignet-service/src/main/resources/application-local.properties index 5412b7b03..5d2dbba6d 100644 --- a/esignet-service/src/main/resources/application-local.properties +++ b/esignet-service/src/main/resources/application-local.properties @@ -2,397 +2,50 @@ # 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/. +## Domain configurations +mosip.esignet.domain.url=http://localhost:8088 +mosip.signup.domain.url=http://localhost:8089 mosip.esignet.mock.domain.url=http://localhost:8082 -logging.level.io.mosip.esignet=DEBUG - -## eSignet configurations +## ACR & AMR mapping configurations mosip.esignet.amr-acr-mapping-file-path=amr_acr_mapping.json -mosip.esignet.auth-txn-id-length=10 -mosip.esignet.supported-id-regex=\\S* -# Generated ID and access tokens 'exp' depends on the below properties, default value is 1-hour -mosip.esignet.id-token-expire-seconds=3600 -mosip.esignet.access-token-expire-seconds=3600 -# By default, only 2 link codes can be active, and the time period it can be active is defined here, default value is 1 minute -mosip.esignet.link-code-expire-in-secs=600 -# Number of link code allowed to be generated in a transaction, the default value is 10 -mosip.esignet.generate-link-code.limit-per-transaction=10 -# Time to complete consent after successful authentication, the default value is 120 -mosip.esignet.authentication-expire-in-secs=600 -# Time to complete authentication -mosip.esignet.preauthentication-expire-in-secs=600 - -## Auth challenge type & format mapping. Auth challenge length validations for each auth factor type. -mosip.esignet.auth-challenge.OTP.format=alpha-numeric -mosip.esignet.auth-challenge.OTP.min-length=6 -mosip.esignet.auth-challenge.OTP.max-length=6 - -mosip.esignet.auth-challenge.PWD.format=alpha-numeric -mosip.esignet.auth-challenge.PWD.min-length=8 -mosip.esignet.auth-challenge.PWD.max-length=30 - -mosip.esignet.auth-challenge.BIO.format=encoded-json -mosip.esignet.auth-challenge.BIO.min-length=500 -mosip.esignet.auth-challenge.BIO.max-length=4000 - -mosip.esignet.auth-challenge.WLA.format=jwt -mosip.esignet.auth-challenge.WLA.min-length=500 -mosip.esignet.auth-challenge.WLA.max-length=800 -mosip.esignet.auth-challenge.KBI.format=base64url-encoded-json -mosip.esignet.auth-challenge.KBI.min-length=50 -mosip.esignet.auth-challenge.KBI.max-length=100 - -mosip.esignet.auth-challenge.PIN.format=number -mosip.esignet.auth-challenge.PIN.min-length=6 -mosip.esignet.auth-challenge.PIN.max-length=6 - -mosip.esignet.auth-challenge.IDT.format=base64url-encoded-json -mosip.esignet.auth-challenge.IDT.min-length=20 -mosip.esignet.auth-challenge.IDT.max-length=2000 - -mosip.esignet.claim-detail.purpose.min-length=3 -mosip.esignet.claim-detail.purpose.max-length=300 - -## Endpoints required to have oauth-details-hash and oauth-details-key HTTP header -mosip.esignet.header-filter.paths-to-validate={'${server.servlet.path}/authorization/send-otp', \ - '${server.servlet.path}/authorization/authenticate', \ - '${server.servlet.path}/authorization/v2/authenticate', \ - '${server.servlet.path}/authorization/v3/authenticate', \ - '${server.servlet.path}/authorization/auth-code',\ - '${server.servlet.path}/authorization/prepare-signup-redirect',\ - '${server.servlet.path}/authorization/claim-details',\ - '${server.servlet.path}/authorization/complete-signup-redirect' } - -## captcha validation is enabled for the auth-factors - otp, pwd, bio and pin. +## Captcha configurations mosip.esignet.captcha.required= -mosip.esignet.captcha.validator-url=http://captcha.captcha/v1/captcha/validatecaptcha -mosip.esignet.captcha.module-name=esignet mosip.esignet.captcha.site-key=test-site-key -## Applicable for signup redirection to update profile -mosip.esignet.signup-id-token-expire-seconds=1800 -mosip.esignet.signup-id-token-audience=mosip-signup-oauth-client - -mosip.esignet.host=localhost -mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json - -## ------------------------------------------ e-Signet binding --------------------------------------------------------- -mosip.esignet.binding.salt-length=16 -mosip.esignet.binding.audience-id=esignet-binding -mosip.esignet.binding.key-expire-days=10 - -## -------------------------------------- Authentication & Authorization ----------------------------------------------- +## Security configurations mosip.esignet.security.auth.post-urls={} mosip.esignet.security.auth.put-urls={} mosip.esignet.security.auth.get-urls={} +spring.security.oauth2.resourceserver.jwt.issuer-uri= +spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://localhost:8080/auth/realms/mosip/protocol/openid-connect/certs -mosip.esignet.security.ignore-csrf-urls=${server.servlet.path}/oidc/**,${server.servlet.path}/oauth/**,\ - ${server.servlet.path}/actuator/**,/favicon.ico,${server.servlet.path}/error,\ - ${server.servlet.path}/swagger-ui/**,${server.servlet.path}/v3/api-docs/**,\ - ${server.servlet.path}/linked-authorization/link-transaction,${server.servlet.path}/linked-authorization/authenticate,\ - ${server.servlet.path}/linked-authorization/consent,${server.servlet.path}/binding/**,${server.servlet.path}/client-mgmt/**,\ - ${server.servlet.path}/system-info/**,${server.servlet.path}/linked-authorization/v2/link-transaction,\ - ${server.servlet.path}/linked-authorization/v2/authenticate,${server.servlet.path}/linked-authorization/v2/consent - -mosip.esignet.security.ignore-auth-urls=${server.servlet.path}/csrf/**,${server.servlet.path}/authorization/**,\ - ${server.servlet.path}/linked-authorization/**,${server.servlet.path}/oidc/**,${server.servlet.path}/oauth/**,\ - ${server.servlet.path}/actuator/**,/favicon.ico,${server.servlet.path}/error,${server.servlet.path}/swagger-ui/**,\ - ${server.servlet.path}/v3/api-docs/**,${server.servlet.path}/binding/** - -##------------------------------------------ Kafka configurations ------------------------------------------------------ +## Kafka configurations kafka.enabled=false spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration spring.kafka.bootstrap-servers=localhost:9092 -spring.kafka.consumer.group-id=esignet-consumer -spring.kafka.consumer.enable-auto-commit=true -#spring.kafka.listener.concurrency=1 - -mosip.esignet.kafka.linked-session.topic=esignet-linked -mosip.esignet.kafka.linked-auth-code.topic=esignet-consented - -## ------------------------------------------ oauth & openid supported values ------------------------------------------ - -## supported scopes -mosip.esignet.supported.authorize.scopes={'Manage-Identity-Data','Manage-VID','Manage-Authentication','Manage-Service-Requests','Manage-Credentials'} -mosip.esignet.supported.openid.scopes={'profile','email','phone'} -mosip.esignet.openid.scope.claims={'profile' : {'name','address','gender','birthdate','picture','email','phone_number','phone_number_verified','registration_type','updated_at'},'email' : {'email'}, 'phone' : {'phone_number','phone_number_verified'}} -mosip.esignet.supported.credential.scopes={'mosip_identity_vc_ldp'} -mosip.esignet.credential.scope-resource-mapping={ 'mosip_identity_vc_ldp': '${mosip.esignet.domain.url}${server.servlet.path}/vci/credential'} - -## supported authorization processing flow to be used, Currently only supports Authorization Code Flow. -mosip.esignet.supported.response.types={'code'} - -## Form of Authorization Grant presented to token endpoint -mosip.esignet.supported.grant.types={'authorization_code'} - -## specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User -# page-The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. -# popup-The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. -# touch-The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. -# wap-The Authorization Server SHOULD display the authentication and consent UI consistent with a "feature phone" type display. -mosip.esignet.supported.ui.displays={'page','popup','touch','wap'} - -## specifies whether the Authorization Server prompts the End-User for reauthentication and consent -# none-The Authorization Server MUST NOT display any authentication or consent user interface pages. -# An error is returned if an End-User is not already authenticated or the Client does not have pre-configured consent -# for the requested Claims or does not fulfill other conditions for processing the request. -# The error code will typically be login_required, interaction_required, or another code defined in Section 3.1.2.6. -# This can be used as a method to check for existing authentication and/or consent. -# login-The Authorization Server SHOULD prompt the End-User for reauthentication. If it cannot reauthenticate the End-User, \ -# it MUST return an error, typically login_required. -# consent-The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client. -# If it cannot obtain consent, it MUST return an error, typically consent_required. -# select_account-The Authorization Server SHOULD prompt the End-User to select a user account. This enables an End-User -# who has multiple accounts at the Authorization Server to select amongst the multiple accounts that they might have current -# sessions for. If it cannot obtain an account selection choice made by the End-User, it MUST return an error, -# typically account_selection_required. -mosip.esignet.supported.ui.prompts={'none','login','consent','select_account'} - -## Type of the client assertion -mosip.esignet.supported.client.assertion.types={'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'} - -## Type of the client authentication methods for token endpoint -mosip.esignet.supported.client.auth.methods={'private_key_jwt'} - -## Only S256 method supported -mosip.esignet.supported-pkce-methods={'S256'} - -## ---------------------------------------- Cache configuration -------------------------------------------------------- - -mosip.esignet.cache.secure.individual-id=true -mosip.esignet.cache.store.individual-id=true -mosip.esignet.cache.security.secretkey.reference-id=TRANSACTION_CACHE -mosip.esignet.cache.security.algorithm-name=AES/ECB/PKCS5Padding -mosip.esignet.cache.key.hash.algorithm=SHA3-256 -mosip.esignet.cache.names=clientdetails,preauth,authenticated,authcodegenerated,userinfo,linkcodegenerated,linked,linkedcode,linkedauth,consented,authtokens,bindingtransaction,apiratelimit,blocked,halted - -# 'simple' cache type is only applicable only for Non-Production setup -spring.cache.type=redis -spring.cache.cache-names=${mosip.esignet.cache.names} +## Redis configuration +spring.cache.type=simple spring.redis.host=localhost spring.redis.port=6379 -management.health.redis.enabled=false - -# Cache size setup is applicable only for 'simple' cache type. -# Cache size configuration will not be considered with 'Redis' cache type -mosip.esignet.cache.size={'clientdetails' : 200, \ -'preauth': 200, \ -'authenticated': 200, \ -'authcodegenerated': 200, \ -'userinfo': 200, \ -'linkcodegenerated' : 500, \ -'linked': 200 , \ -'linkedcode': 200, \ -'linkedauth' : 200 , \ -'consented' :200, \ -'authtokens': 2, \ -'bindingtransaction': 200, \ -'apiratelimit' : 500, \ -'blocked': 500, \ -'halted' : 500 } - -# Cache expire in seconds is applicable for both 'simple' and 'Redis' cache type -mosip.esignet.cache.expire-in-seconds={'clientdetails' : 86400, \ -'preauth': ${mosip.esignet.preauthentication-expire-in-secs},\ -'authenticated': ${mosip.esignet.authentication-expire-in-secs}, \ -'authcodegenerated': 600, \ -'userinfo': ${mosip.esignet.access-token-expire-seconds}, \ -'linkcodegenerated' : ${mosip.esignet.link-code-expire-in-secs}, \ -'linked': 600, \ -'linkedcode': ${mosip.esignet.link-code-expire-in-secs}, \ -'linkedauth' : ${mosip.esignet.authentication-expire-in-secs}, \ -'consented': 600, \ -'authtokens': 86400, \ -'bindingtransaction': 600, \ -'apiratelimit' : 180, \ -'blocked': 300, \ -'halted' : ${mosip.esignet.signup-id-token-expire-seconds} } - -## ------------------------------------------ Discovery openid-configuration ------------------------------------------- - -mosip.esignet.domain.url=http://localhost:8088 -mosip.esignet.discovery.issuer-id=${mosip.esignet.domain.url}${server.servlet.path} - -# This property holds ./wellknown/jwks.json URL, -# for local deployments without esignet-ui nginx change the value to ${mosip.esignet.domain.url}${server.servlet.path}/oauth/.well-known/jwks.json -mosip.esignet.jwks-uri=${mosip.esignet.domain.url}/.well-known/jwks.json - -mosip.esignet.token.endpoint=${mosip.esignet.domain.url}${server.servlet.path}/oauth/v2/token -mosip.esignet.oauth.key-values={'issuer': '${mosip.esignet.domain.url}' ,\ - \ 'authorization_endpoint': '${mosip.esignet.domain.url}/authorize' , \ - \ 'token_endpoint': '${mosip.esignet.token.endpoint}' , \ - \ 'jwks_uri' : '${mosip.esignet.jwks-uri}' , \ - \ 'token_endpoint_auth_methods_supported' : ${mosip.esignet.supported.client.auth.methods}, \ - \ 'token_endpoint_auth_signing_alg_values_supported' : {'RS256'},\ - \ 'scopes_supported' : ${mosip.esignet.supported.openid.scopes}, \ - \ 'response_modes_supported' : { 'query' }, \ - \ 'grant_types_supported' : ${mosip.esignet.supported.grant.types},\ - \ 'response_types_supported' : ${mosip.esignet.supported.response.types}} - -mosip.esignet.discovery.key-values={'issuer': '${mosip.esignet.domain.url}' ,\ - \ 'authorization_endpoint': '${mosip.esignet.domain.url}/authorize' , \ - \ 'token_endpoint': '${mosip.esignet.token.endpoint}' ,\ - \ 'userinfo_endpoint' : '${mosip.esignet.domain.url}${server.servlet.path}/oidc/userinfo' ,\ - \ 'jwks_uri' : '${mosip.esignet.jwks-uri}' , \ - \ 'scopes_supported' : ${mosip.esignet.supported.openid.scopes}, \ - \ 'response_types_supported' : ${mosip.esignet.supported.response.types}, \ - \ 'acr_values_supported' : {'mosip:idp:acr:knowledge', 'mosip:idp:acr:password', 'mosip:idp:acr:static-code', 'mosip:idp:acr:generated-code', 'mosip:idp:acr:linked-wallet', 'mosip:idp:acr:biometrics'},\ - \ 'userinfo_signing_alg_values_supported' : {'RS256'}, \ - \ 'userinfo_encryption_alg_values_supported' : {'RSAXXXXX'},\ - \ 'userinfo_encryption_enc_values_supported' : {'A128GCM'}, \ - \ 'response_modes_supported' : { 'query' }, \ - \ 'token_endpoint_auth_methods_supported' : ${mosip.esignet.supported.client.auth.methods}, \ - \ 'token_endpoint_auth_signing_alg_values_supported' : {'RS256'}, \ - \ 'id_token_signing_alg_values_supported' : {'RS256'}, \ - \ 'claim_types_supported': {'normal'}, \ - \ 'claims_parameter_supported' : true, \ - \ 'display_values_supported' : ${mosip.esignet.supported.ui.displays}, \ - \ 'subject_types_supported' : { 'pairwise' }, \ - \ 'claims_supported' : {'name','address','gender','birthdate','picture','email','phone_number','individual_id','phone_number_verified','registration_type','updated_at'}, \ - \ 'claims_locales_supported' : {'en'}, \ - \ 'request_parameter_supported' : false, \ - \ 'display_values_supported' : ${mosip.esignet.supported.ui.displays}, \ - \ 'ui_locales_supported' : {'en'} } - -##----------------------------------------- Database properties -------------------------------------------------------- +## Database configuration mosip.esignet.database.hostname=localhost mosip.esignet.database.port=5455 mosip.esignet.database.name=mosip_esignet mosip.esignet.database.username=postgres -spring.datasource.url=jdbc:postgresql://${mosip.esignet.database.hostname}:${mosip.esignet.database.port}/${mosip.esignet.database.name}?currentSchema=esignet -spring.datasource.username=${mosip.esignet.database.username} spring.datasource.password=postgres - -spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL95Dialect -spring.jpa.show-sql=false -spring.jpa.hibernate.ddl-auto=none -spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true - -#------------------------------------ Key-manager specific properties -------------------------------------------------- -#Crypto asymmetric algorithm name -mosip.kernel.crypto.asymmetric-algorithm-name=RSA/ECB/OAEPWITHSHA-256ANDMGF1PADDING -#Crypto symmetric algorithm name -mosip.kernel.crypto.symmetric-algorithm-name=AES/GCM/PKCS5Padding -#Keygenerator asymmetric algorithm name -mosip.kernel.keygenerator.asymmetric-algorithm-name=RSA -#Keygenerator symmetric algorithm name -mosip.kernel.keygenerator.symmetric-algorithm-name=AES -#Asymmetric algorithm key length -mosip.kernel.keygenerator.asymmetric-key-length=2048 -#Symmetric algorithm key length -mosip.kernel.keygenerator.symmetric-key-length=256 -#Encrypted data and encrypted symmetric key separator -mosip.kernel.data-key-splitter=#KEY_SPLITTER# -#GCM tag length -mosip.kernel.crypto.gcm-tag-length=128 -#Hash algo name -mosip.kernel.crypto.hash-algorithm-name=PBKDF2WithHmacSHA512 -#Symmtric key length used in hash -mosip.kernel.crypto.hash-symmetric-key-length=256 -#No of iterations in hash -mosip.kernel.crypto.hash-iteration=100000 -#Sign algo name -mosip.kernel.crypto.sign-algorithm-name=RS256 -#Certificate Sign algo name -mosip.kernel.certificate.sign.algorithm=SHA256withRSA - -#Type of keystore, Supported Types: PKCS11, PKCS12, Offline, JCE +## Keymanager configuration mosip.kernel.keymanager.hsm.keystore-type=PKCS12 -# For PKCS11 provide Path of config file. -# For PKCS12 keystore type provide the p12/pfx file path. P12 file will be created internally so provide only file path & file name. -# For Offline & JCE property can be left blank, specified value will be ignored. mosip.kernel.keymanager.hsm.config-path=esignet_local.p12 -# Passkey of keystore for PKCS11, PKCS12 -# For Offline & JCE proer can be left blank. JCE password use other JCE specific properties. mosip.kernel.keymanager.hsm.keystore-pass=localtest -mosip.kernel.keymanager.certificate.default.common-name=www.mosip.io -mosip.kernel.keymanager.certificate.default.organizational-unit=MOSIP-TECH-CENTER -mosip.kernel.keymanager.certificate.default.organization=IITB -mosip.kernel.keymanager.certificate.default.location=BANGALORE -mosip.kernel.keymanager.certificate.default.state=KA -mosip.kernel.keymanager.certificate.default.country=IN - -mosip.kernel.keymanager.softhsm.certificate.common-name=www.mosip.io -mosip.kernel.keymanager.softhsm.certificate.organizational-unit=MOSIP -mosip.kernel.keymanager.softhsm.certificate.organization=IITB -mosip.kernel.keymanager.softhsm.certificate.country=IN - -# Application Id for PMS master key. -mosip.kernel.partner.sign.masterkey.application.id=PMS -mosip.kernel.partner.allowed.domains=DEVICE - -mosip.kernel.keymanager-service-validate-url=https://${mosip.hostname}/keymanager/validate -mosip.kernel.keymanager.jwtsign.validate.json=false -mosip.keymanager.dao.enabled=false -crypto.PrependThumbprint.enable=true - -mosip.kernel.keymgr.hsm.health.check.enabled=true -mosip.kernel.keymgr.hsm.health.key.app-id=OIDC_SERVICE -mosip.kernel.keymgr.hsm.healthkey.ref-id=TRANSACTION_CACHE -mosip.kernel.keymgr.hsm.health.check.encrypt=true - -## -------------------------------------------- IDP-UI config ---------------------------------------------------------- - -# NOTE: -# 1. linked-transaction-expire-in-secs value should be a sum of 'mosip.esignet.authentication-expire-in-secs' and 'linked' cache expire in seconds under mosip.esignet.cache.expire-in-seconds property -# 2. A new Qrcode will be autogenerated before the expiry of current qr-code, and the time difference in seconds for the same is defined in wallet.qr-code-buffer-in-secs property -# 3. If esignet is deployed with MOSIP IDA, then 'resend.otp.delay.secs' must be the same as 'mosip.kernel.otp.expiry-time' - -mosip.esignet.ui.wallet.config={{'wallet.name': 'walletName', 'wallet.logo-url': '/images/qr_code.png', 'wallet.download-uri': '#', \ - 'wallet.deep-link-uri': 'inji://landing-page-name?linkCode=LINK_CODE&linkExpireDateTime=LINK_EXPIRE_DT' }} - -mosip.signup.domain.url=http://localhost:8089 -mosip.esignet.ui.signup.config={'signup.banner': true, 'signup.url': '${mosip.signup.domain.url}/signup'} -mosip.esignet.ui.forgot-password.config={'forgot-password': true, 'forgot-password.url': '${mosip.signup.domain.url}/reset-password'} -mosip.esignet.ui.eKYC-steps.url=${mosip.signup.domain.url}/identity-verification - -## Configuration required to display KBI form. -# individual-id-field is set with field id which should be considered as an individual ID in the authenticate request. -# form-details holds the list of field details like below: -# id -> unique field Id, type -> holds datatype, format -> only supported for date fields, regex -> pattern to validate the input value, maxLength -> number of allowed characters -# Example: mosip.esignet.authenticator.default.auth-factor.kba.field-details={{'id': '${mosip.esignet.authenticator.default.auth-factor.kba.individual-id-field}', 'type':'text', 'format':'', 'maxLength': 50, 'regex': '^\\s*[+-]?(\\d+|\\d*\\.\\d+|\\d+\\.\\d*)([Ee][+-]?\\d*)?\\s*$'},{'id':'fullName', 'type':'text', 'format':'', 'maxLength': 50, 'regex': '^[A-Za-z\\s]{1,}[\\.]{0,1}[A-Za-z\\s]{0,}$'},{'id':'dob', 'type':'date', 'format':'dd/mm/yyyy'}} -mosip.esignet.authenticator.default.auth-factor.kbi.individual-id-field= -mosip.esignet.authenticator.default.auth-factor.kbi.field-details={} - -mosip.esignet.ui.config.otp.length=6 -mosip.esignet.ui.config.password.regex=^.{8,20}$ -mosip.esignet.ui.config.password.max-length=20 -mosip.esignet.ui.config.username.regex=.* -mosip.esignet.ui.config.username.prefix= -mosip.esignet.ui.config.username.postfix= -mosip.esignet.ui.config.username.max-length=12 -mosip.esignet.ui.config.username.input-type=text - -mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ - 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ - 'sbi.capture.count.iris': 1, 'sbi.capture.score.face': 70, 'sbi.capture.score.finger':70, 'sbi.capture.score.iris':70, \ - 'resend.otp.delay.secs': ${mosip.kernel.otp.expiry-time:180}, 'send.otp.channels' : '${mosip.esignet.authenticator.ida.otp-channels:email,phone}', \ - 'captcha.sitekey' : '${mosip.esignet.captcha.site-key}', 'captcha.enable' : '${mosip.esignet.captcha.required}', \ - 'auth.txnid.length' : '${mosip.esignet.auth-txn-id-length}', \ - 'preauth-screen-timeout-in-secs':${mosip.esignet.preauthentication-expire-in-secs}, \ - 'consent.screen.timeout-in-secs':${mosip.esignet.authentication-expire-in-secs}, \ - 'consent.screen.timeout-buffer-in-secs': 5, 'linked-transaction-expire-in-secs': 240, 'sbi.port.range': '4501-4600', \ - 'sbi.bio.subtypes.iris': 'UNKNOWN', 'sbi.bio.subtypes.finger': 'UNKNOWN', 'wallet.qr-code-buffer-in-secs': 10, \ - 'otp.length': ${mosip.esignet.ui.config.otp.length}, \ - 'password.regex': '${mosip.esignet.ui.config.password.regex}', \ - 'password.max-length': ${mosip.esignet.ui.config.password.max-length}, \ - 'username.regex': '${mosip.esignet.ui.config.username.regex}',\ - 'username.prefix': '${mosip.esignet.ui.config.username.prefix}', \ - 'username.postfix': '${mosip.esignet.ui.config.username.postfix}', \ - 'username.max-length': ${mosip.esignet.ui.config.username.max-length}, \ - 'username.input-type': '${mosip.esignet.ui.config.username.input-type}', 'wallet.config': ${mosip.esignet.ui.wallet.config}, \'signup.config': ${mosip.esignet.ui.signup.config}, \ - 'forgot-password.config': ${mosip.esignet.ui.forgot-password.config}, \ - 'eKYC-steps.config': '${mosip.esignet.ui.eKYC-steps.url}', \ - 'error.banner.close-timer': 10,\ - 'auth.factor.kbi.individual-id-field' : '${mosip.esignet.authenticator.default.auth-factor.kbi.individual-id-field}',\ - 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}} - -##-------------------------------------------- Default Integrations ----------------------------------------------------- - +## Overridden plugin configurations mosip.esignet.integration.audit-plugin=LoggerAuditService -mosip.esignet.integration.key-binder=NoOpKeyBinder +#mosip.esignet.integration.key-binder=NoOpKeyBinder + +## Logging configuration +logging.level.io.mosip.esignet=DEBUG diff --git a/esignet-service/src/main/resources/bootstrap.properties b/esignet-service/src/main/resources/bootstrap.properties index b79c7e7a3..5549e7880 100644 --- a/esignet-service/src/main/resources/bootstrap.properties +++ b/esignet-service/src/main/resources/bootstrap.properties @@ -5,7 +5,7 @@ ## Application Name spring.application.name=esignet spring.cloud.config.uri=http://localhost:8888 -spring.profiles.active=local +spring.profiles.active=default,local server.port=8088 server.servlet.path=/v1/esignet @@ -46,6 +46,3 @@ management.endpoint.metrics.enabled=true management.endpoints.web.exposure.include=* management.endpoint.prometheus.enabled=true management.metrics.export.prometheus.enabled=true - -# to accept string as valid type for jsonb column -spring.datasource.hikari.data-source-properties=stringtype=unspecified \ No newline at end of file diff --git a/helm/apitestrig/.gitignore b/helm/apitestrig/.gitignore new file mode 100644 index 000000000..ee3892e87 --- /dev/null +++ b/helm/apitestrig/.gitignore @@ -0,0 +1 @@ +charts/ diff --git a/helm/apitestrig/.helmignore b/helm/apitestrig/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/helm/apitestrig/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/helm/apitestrig/Chart.yaml b/helm/apitestrig/Chart.yaml new file mode 100644 index 000000000..5815c0228 --- /dev/null +++ b/helm/apitestrig/Chart.yaml @@ -0,0 +1,19 @@ +apiVersion: v2 +name: apitestrig +description: A Helm chart to deploy APITESTRIG for MOSIP modules +type: application +version: 1.5.0-develop +appVersion: "" +dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 1.x.x +home: https://mosip.io +keywords: + - mosip + - apitestrig +maintainers: + - email: info@mosip.io + name: MOSIP diff --git a/helm/apitestrig/README.md b/helm/apitestrig/README.md new file mode 100644 index 000000000..25c35e359 --- /dev/null +++ b/helm/apitestrig/README.md @@ -0,0 +1,10 @@ +# APITESTRIG + +Helm chart to deploy APITESTRIG for `MOSIP` modules + +## TL;DR + +```console +$ helm repo add mosip https://mosip.github.io +$ helm install my-release mosip/apitestrig +``` diff --git a/helm/apitestrig/templates/NOTES.txt b/helm/apitestrig/templates/NOTES.txt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/helm/apitestrig/templates/NOTES.txt @@ -0,0 +1 @@ + diff --git a/helm/apitestrig/templates/_helpers.tpl b/helm/apitestrig/templates/_helpers.tpl new file mode 100644 index 000000000..d99caf0c4 --- /dev/null +++ b/helm/apitestrig/templates/_helpers.tpl @@ -0,0 +1,63 @@ +{{/* +Return the proper image name +*/}} +{{- define "apitestrig.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "apitestrig.volumePermissions.image" -}} +{{- include "common.images.image" ( dict "imageRoot" .Values.volumePermissions.image "global" .Values.global ) -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "apitestrig.imagePullSecrets" -}} +{{- include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.volumePermissions.image) "global" .Values.global) -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "apitestrig.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (printf "%s" (include "common.names.fullname" .)) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message. +*/}} +{{- define "apitestrig.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "apitestrig.validateValues.foo" .) -}} +{{- $messages := append $messages (include "apitestrig.validateValues.bar" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message -}} +{{- end -}} +{{- end -}} + +{{/* +Return podAnnotations +*/}} +{{- define "apitestrig.podAnnotations" -}} +{{- if .Values.podAnnotations }} +{{ include "common.tplvalues.render" (dict "value" .Values.podAnnotations "context" $) }} +{{- end }} +{{- if and .Values.metrics.enabled .Values.metrics.podAnnotations }} +{{ include "common.tplvalues.render" (dict "value" .Values.metrics.podAnnotations "context" $) }} +{{- end }} +{{- end -}} + +{{/* Create the name for restart cronjob */}} +{{- define "apitestrig.cronjob" -}} +{{ default (printf "cronjob-%s" (include "common.names.fullname" .)) .Values.serviceAccount.name }} +{{- end -}} \ No newline at end of file diff --git a/helm/apitestrig/templates/clusterrole.yaml b/helm/apitestrig/templates/clusterrole.yaml new file mode 100644 index 000000000..da268fdf5 --- /dev/null +++ b/helm/apitestrig/templates/clusterrole.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "apitestrig.serviceAccountName" . }}-{{ .Release.Namespace }} + namespace: {{ .Release.Namespace }} +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get","patch","list","watch"] diff --git a/helm/esignet/templates/clusterrolebinding.yaml b/helm/apitestrig/templates/clusterrolebinding.yaml similarity index 74% rename from helm/esignet/templates/clusterrolebinding.yaml rename to helm/apitestrig/templates/clusterrolebinding.yaml index e44824537..12594c8d1 100644 --- a/helm/esignet/templates/clusterrolebinding.yaml +++ b/helm/apitestrig/templates/clusterrolebinding.yaml @@ -5,15 +5,15 @@ metadata: {{- if .Values.commonLabels }} {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} {{- end }} - name: {{ template "common.names.fullname" . }} + name: {{ template "common.names.fullname" . }}-{{ .Release.Namespace }} {{- if .Values.commonAnnotations }} annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} {{- end }} roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ template "common.names.fullname" . }} + name: {{ template "apitestrig.serviceAccountName" . }}-{{ .Release.Namespace }} subjects: - kind: ServiceAccount - name: {{ template "esignet.serviceAccountName" . }} + name: {{ template "apitestrig.serviceAccountName" . }} namespace: {{ .Release.Namespace }} diff --git a/helm/apitestrig/templates/configmaps.yaml b/helm/apitestrig/templates/configmaps.yaml new file mode 100644 index 000000000..492508377 --- /dev/null +++ b/helm/apitestrig/templates/configmaps.yaml @@ -0,0 +1,21 @@ +{{- if .Values.apitestrig.configmaps }} +{{- range $cm_name, $cm_value := .Values.apitestrig.configmaps }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ $cm_name }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 8 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 8 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 8 }} + {{- end }} +data: + {{- range $key, $value := $cm_value }} + {{ $key }}: {{ $value | quote }} + {{- end }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/apitestrig/templates/cronjob.yaml b/helm/apitestrig/templates/cronjob.yaml new file mode 100644 index 000000000..9a87054da --- /dev/null +++ b/helm/apitestrig/templates/cronjob.yaml @@ -0,0 +1,119 @@ +{{- range $modulename, $module := $.Values.modules }} +{{- if $module.enabled }} +--- +apiVersion: {{ include "common.capabilities.cronjob.apiVersion" $ }} +kind: CronJob +metadata: + name: {{ template "apitestrig.cronjob" $ }}-{{ $modulename }} + namespace: {{ $.Release.Namespace }} + annotations: + {{- if $.Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + labels: {{- include "common.labels.standard" $ | nindent 4 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + +spec: + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 1 # remove jobs which are successfully executed + failedJobsHistoryLimit: 1 # except 1 recent failed job, remove jobs which are not successfully executed + #schedule: '*/3 * * * *' # cron spec of time, here, 8 o'clock + schedule: {{ $.Values.crontime }} + jobTemplate: + spec: + backoffLimit: 0 # this has very low chance of failing, as all this does + # is prompt kubernetes to schedule new replica set for + # the deployment + # activeDeadlineSeconds: 600 # timeout, makes most sense with + # "waiting for rollout" variant specified below + template: + spec: + # account configured above + restartPolicy: Never + serviceAccountName: {{ template "apitestrig.serviceAccountName" $ }} + initContainers: + {{- if $.Values.enable_insecure }} + {{- include "common.tplvalues.render" (dict "value" $.Values.initContainers "context" $) | nindent 12 }} + {{- end }} + containers: + - name: {{ template "apitestrig.serviceAccountName" $ }}-{{ $modulename }} + image: {{ $module.image.repository }}:{{ $module.image.tag }} + imagePullPolicy: {{ $module.image.pullPolicy }} + {{- if $.Values.lifecycleHooks }} + lifecycle: {{- include "common.tpvalues.render" (dict "value" $.Values.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + {{- if $.Values.containerSecurityContext.enabled }} + securityContext: {{- omit $.Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if $.Values.command }} + command: {{- include "common.tplvalues.render" (dict "value" $.Values.command "context" $) | nindent 12 }} + {{- end }} + {{- if $.Values.args }} + args: {{- include "common.tplvalues.render" (dict "value" $.Values.args "context" $) | nindent 12 }} + {{- end }} + env: + - name: container_user + value: {{ $.Values.containerSecurityContext.runAsUser }} + - name: JDK_JAVA_OPTIONS + value: {{ $.Values.additionalResources.javaOpts }} + - name: MODULES + value: {{ $modulename }} + - name: push-reports-to-s3 + value: {{ quote $.Values.apitestrig.variables.push_reports_to_s3 }} + {{- if $.Values.extraEnvVars }} + {{- include "common.tpvalues.render" (dict "value" $.Values.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + envFrom: + {{- if $.Values.extraEnvVarsCM }} + {{- range $.Values.extraEnvVarsCM }} + - configMapRef: + name: {{ . }} + {{- end }} + {{- end }} + {{- if $.Values.extraEnvVarsSecret }} + {{- range $.Values.extraEnvVarsSecret }} + - secretRef: + name: {{ . }} + {{- end }} + {{- end }} + ports: + - name: spring-service + containerPort: {{ $.Values.springServicePort }} + volumeMounts: + {{- if $.Values.enable_insecure }} + - mountPath: /usr/local/openjdk-11/lib/security/cacerts + name: cacerts + subPath: cacerts + {{- end }} + {{- if $.Values.apitestrig.volumes }} + {{- range $volume_name, $volume_value := $.Values.apitestrig.volumes.configmaps }} + - name: {{ $volume_name }} + mountPath: {{ $volume_value.volumeMounts.mountPath }} + {{- end }} + {{- end }} + {{- if eq $.Values.apitestrig.variables.push_reports_to_s3 "no" }} + - name: {{ $.Values.apitestrig.volumes.reports.name }} + mountPath: /home/mosip/testrig/report + {{- end }} + volumes: + {{- if $.Values.enable_insecure }} + - name: cacerts + emptyDir: {} + {{- end }} + {{- if $.Values.apitestrig.volumes }} + {{- range $volume_name, $volume_value := $.Values.apitestrig.volumes.configmaps }} + - name: {{ $volume_name }} + configMap: + defaultMode: {{ $volume_value.defaultMode }} + name: {{ $volume_name }} + {{- end }} + {{- end }} + {{- if eq $.Values.apitestrig.variables.push_reports_to_s3 "no" }} + - name: {{ $.Values.apitestrig.volumes.reports.name }} + persistentVolumeClaim: + claimName: {{ $.Values.apitestrig.volumes.reports.name }}-{{ $.Release.Namespace }}-{{ $modulename }}-pvc + {{- end }} +{{- end }} +{{- end }} diff --git a/helm/apitestrig/templates/extra-list.yaml b/helm/apitestrig/templates/extra-list.yaml new file mode 100644 index 000000000..9ac65f9e1 --- /dev/null +++ b/helm/apitestrig/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/helm/apitestrig/templates/pv.yaml b/helm/apitestrig/templates/pv.yaml new file mode 100644 index 000000000..0ae1e5472 --- /dev/null +++ b/helm/apitestrig/templates/pv.yaml @@ -0,0 +1,23 @@ +{{- range $modulename, $module := $.Values.modules }} +{{- if $module.enabled }} +{{- if eq $.Values.apitestrig.variables.push_reports_to_s3 "no" }} +apiVersion: v1 +kind: PersistentVolume +metadata: + name: {{ $.Values.apitestrig.volumes.reports.name }}-{{ $.Release.Namespace }}-{{ $modulename }}-pvc + labels: + name: {{ $.Values.apitestrig.volumes.reports.name }} +spec: + storageClassName: {{ $.Values.apitestrig.volumes.reports.storageClass }} + capacity: + storage: {{ $.Values.apitestrig.volumes.reports.size }} + accessModes: + {{- range $.Values.apitestrig.volumes.reports.accessModes }} + - {{ . }} + {{- end }} + nfs: + server: {{ $.Values.apitestrig.volumes.reports.nfs.server }} + path: {{ $.Values.apitestrig.volumes.reports.nfs.path }} +{{- end }} +{{- end }} +{{- end }} diff --git a/helm/apitestrig/templates/pvc.yaml b/helm/apitestrig/templates/pvc.yaml new file mode 100644 index 000000000..605375b0c --- /dev/null +++ b/helm/apitestrig/templates/pvc.yaml @@ -0,0 +1,23 @@ +{{- range $modulename, $module := $.Values.modules }} +{{- if $module.enabled }} +{{- if eq $.Values.apitestrig.variables.push_reports_to_s3 "no" }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ $.Values.apitestrig.volumes.reports.name }}-{{ $.Release.Namespace }}-{{ $modulename }}-pvc + namespace: {{ $.Release.Namespace | quote }} +spec: + storageClassName: {{ $.Values.apitestrig.volumes.reports.storageClass }} + accessModes: + {{- range $.Values.apitestrig.volumes.reports.accessModes }} + - {{ . }} + {{- end }} + resources: + requests: + storage: {{ $.Values.apitestrig.volumes.reports.size }} + selector: + matchLabels: + name: {{ $.Values.apitestrig.volumes.reports.name }} +{{- end }} +{{- end }} +{{- end }} diff --git a/helm/apitestrig/templates/secrets.yaml b/helm/apitestrig/templates/secrets.yaml new file mode 100644 index 000000000..a3b9561dc --- /dev/null +++ b/helm/apitestrig/templates/secrets.yaml @@ -0,0 +1,22 @@ +{{- if .Values.apitestrig.secrets }} +{{- range $secret_name, $secret_value := .Values.apitestrig.secrets }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secret_name }}-{{ $.Release.Name }} + namespace: {{ $.Release.Namespace }} + labels: {{- include "common.labels.standard" $ | nindent 8 }} + {{- if $.Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" $.Values.commonLabels "context" $ ) | nindent 8 }} + {{- end }} + {{- if $.Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" $.Values.commonAnnotations "context" $ ) | nindent 8 }} + {{- end }} +type: Opaque +data: + {{- range $key, $value := $secret_value }} + {{ $key }}: {{ $value | b64enc | quote }} + {{- end }} +{{- end }} +{{- end }} diff --git a/helm/oidc-ui/templates/clusterrolebinding.yaml b/helm/apitestrig/templates/service-account.yaml similarity index 51% rename from helm/oidc-ui/templates/clusterrolebinding.yaml rename to helm/apitestrig/templates/service-account.yaml index a839a9b4c..466590df4 100644 --- a/helm/oidc-ui/templates/clusterrolebinding.yaml +++ b/helm/apitestrig/templates/service-account.yaml @@ -1,19 +1,12 @@ -kind: ClusterRoleBinding -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +apiVersion: v1 +kind: ServiceAccount metadata: labels: {{- include "common.labels.standard" . | nindent 4 }} {{- if .Values.commonLabels }} {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} {{- end }} - name: {{ template "common.names.fullname" . }} + name: {{ template "apitestrig.serviceAccountName" . }} {{- if .Values.commonAnnotations }} annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} {{- end }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ template "common.names.fullname" . }} -subjects: - - kind: ServiceAccount - name: {{ template "oidc-ui.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} + namespace: {{ .Release.Namespace }} diff --git a/helm/apitestrig/values.yaml b/helm/apitestrig/values.yaml new file mode 100644 index 000000000..7c22d0a0a --- /dev/null +++ b/helm/apitestrig/values.yaml @@ -0,0 +1,559 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +# global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Add labels to all the deployed resources +## +commonLabels: + app.kubernetes.io/component: mosip + +## Add annotations to all the deployed resources +## +commonAnnotations: {} + +## Kubernetes Cluster Domain +## +clusterDomain: cluster.local + +## Extra objects to deploy (value evaluated as a template) +## +extraDeploy: [] + +## Number of nodes +## +replicaCount: 1 + +service: + type: ClusterIP + port: 80 + ## loadBalancerIP for the SuiteCRM Service (optional, cloud specific) + ## ref: http://kubernetes.io/docs/user-guide/services/#type-loadbalancer + ## + ## loadBalancerIP: + ## + ## nodePorts: + ## http: + ## https: + ## + nodePorts: + http: "" + https: "" + ## Enable client source IP preservation + ## ref http://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + +## Port on which this particular spring service module is running. +springServicePort: 8083 + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes +## + +## +# existingConfigmap: + +## Command and args for running the container (set to default if not set). Use array form +## +command: ['/bin/bash'] +args: ['-c', "/home/${container_user}/scripts/fetch_docker_image_hash_ids.sh"] + +## Deployment pod host aliases +## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ +## +hostAliases: [] + +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + limits: + cpu: 1500m + memory: 3500Mi + requests: + cpu: 1000m + memory: 3500Mi + +additionalResources: + ## Specify any JAVA_OPTS string here. These typically will be specified in conjunction with above resources + ## Example: java_opts: "-Xms500M -Xmx500M" + javaOpts: "-Xms2600M -Xmx2600M" + +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container +## Clamav container already runs as 'mosip' user, so we may not need to enable this +containerSecurityContext: + enabled: false + runAsUser: mosip + runAsNonRoot: true + +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod +## +podSecurityContext: + enabled: false + fsGroup: 1001 + +## Pod affinity preset +## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## Allowed values: soft, hard +## +podAffinityPreset: "" + +## Pod anti-affinity preset +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity +## Allowed values: soft, hard +## +podAntiAffinityPreset: soft + +## Node affinity preset +## Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity +## Allowed values: soft, hard +## +nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + ## + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + +## Affinity for pod assignment. Evaluated as a template. +## Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity +## +affinity: {} + +## Node labels for pod assignment. Evaluated as a template. +## ref: https://kubernetes.io/docs/user-guide/node-selection/ +## +nodeSelector: {} + +## Tolerations for pod assignment. Evaluated as a template. +## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ +## +tolerations: [] + +## Pod extra labels +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ +## +podLabels: {} + +## Annotations for server pods. +## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ +## +podAnnotations: {} + +## pods' priority. +## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/ +## +# priorityClassName: "" + +## lifecycleHooks for the container to automate configuration before or after startup. +## +lifecycleHooks: {} + +## Custom Liveness probes for +## +customLivenessProbe: {} + +## Custom Rediness probes +## +customReadinessProbe: {} + +## Update strategy - only really applicable for deployments with RWO PVs attached +## If replicas = 1, an update can get "stuck", as the previous pod remains attached to the +## PV, and the "incoming" pod can never start. Changing the strategy to "Recreate" will +## terminate the single previous pod, so that the new, incoming pod can attach to the PV +## +updateStrategy: + type: RollingUpdate + +## Additional environment variables to set +## Example: +## extraEnvVars: +## - name: FOO +## value: "bar" +## +extraEnvVars: [] + +## ConfigMap with extra environment variables +## +extraEnvVarsCM: + - global + - s3 + - keycloak-host + - db + - apitestrig + - config-server-share + - artifactory-share +## Secret with extra environment variables +## +extraEnvVarsSecret: + - apitestrig + - s3 + - keycloak-client-secrets + - postgres-postgresql + +## Extra volumes to add to the deployment +## +extraVolumes: [] + +## Extra volume mounts to add to the container +## +extraVolumeMounts: [] + +## Add init containers to the pods. +## Example: +## initContainers: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +initContainers: + - command: + - /bin/bash + - -c + - if [ "$ENABLE_INSECURE" = "true" ]; then HOST=$( env | grep "mosip-api-internal-host" + |sed "s/mosip-api-internal-host=//g"); if [ -z "$HOST" ]; then echo "HOST + $HOST is empty; EXITING"; exit 1; fi; openssl s_client -servername "$HOST" + -connect "$HOST":443 > "$HOST.cer" 2>/dev/null & sleep 2 ; sed -i -ne '/-BEGIN + CERTIFICATE-/,/-END CERTIFICATE-/p' "$HOST.cer"; cat "$HOST.cer"; /usr/local/openjdk-11/bin/keytool + -delete -alias "$HOST" -keystore $JAVA_HOME/lib/security/cacerts -storepass + changeit; /usr/local/openjdk-11/bin/keytool -trustcacerts -keystore "$JAVA_HOME/lib/security/cacerts" + -storepass changeit -noprompt -importcert -alias "$HOST" -file "$HOST.cer" + ; if [ $? -gt 0 ]; then echo "Failed to add SSL certificate for host $host; + EXITING"; exit 1; fi; cp /usr/local/openjdk-11/lib/security/cacerts /cacerts; + fi + env: + - name: ENABLE_INSECURE + value: "true" + envFrom: + - configMapRef: + name: global + image: docker.io/openjdk:11-jre + imagePullPolicy: Always + name: cacerts + resources: {} + securityContext: + runAsUser: 0 + terminationMessagePath: /dev/termination-log + terminationMessagePolicy: File + volumeMounts: + - mountPath: /cacerts + name: cacerts + +## Add sidecars to the pods. +## Example: +## sidecars: +## - name: your-image-name +## image: your-image +## imagePullPolicy: Always +## ports: +## - name: portname +## containerPort: 1234 +## +sidecars: {} + +persistence: + enabled: false + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack). + ## + # storageClass: "-" + ## + ## If you want to reuse an existing claim, you can pass the name of the PVC using + ## the existingClaim variable + # existingClaim: your-claim + ## ReadWriteMany not supported by AWS gp2 + storageClass: + accessModes: + - ReadWriteOnce + size: 10M + existingClaim: + # Dir where config and keys are written inside container + mountDir: + +## Init containers parameters: +## volumePermissions: Change the owner and group of the persistent volume mountpoint to runAsUser:fsGroup values from the securityContext section. +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: "10" + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + pullSecrets: [] + ## - myRegistryKeySecretName + ## Init containers' resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + resources: + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## + limits: {} + ## cpu: 100m + ## memory: 128Mi + ## + requests: {} + ## cpu: 100m + ## memory: 128Mi + ## + +## Specifies whether RBAC resources should be created +## +rbac: + create: true + +## Specifies whether a ServiceAccount should be created +## +serviceAccount: + create: true + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + ## + name: + +## Prometheus Metrics +## +metrics: + enabled: false + ## Prometheus pod annotations + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: + prometheus.io/scrape: "true" + + endpointPath: + + ## Prometheus Service Monitor + ## ref: https://github.com/coreos/prometheus-operator + ## + serviceMonitor: + ## If the operator is installed in your cluster, set to true to create a Service Monitor Entry + ## + enabled: true + ## Specify the namespace in which the serviceMonitor resource will be created + ## + # namespace: "" + ## Specify the interval at which metrics should be scraped + ## + interval: 10s + ## Specify the timeout after which the scrape is ended + ## + # scrapeTimeout: 30s + ## Specify Metric Relabellings to add to the scrape endpoint + ## + # relabellings: + ## Specify honorLabels parameter to add the scrape endpoint + ## + honorLabels: false + ## Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec + ## + additionalLabels: {} + + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + ## + prometheusRule: + enabled: false + additionalLabels: {} + namespace: '' + ## List of rules, used as template by Helm. + ## These are just examples rules inspired from https://awesome-prometheus-alerts.grep.to/rules.html + # rules: + # - alert: RabbitmqDown + # expr: rabbitmq_up{service="{{ template "rabbitmq.fullname" . }}"} == 0 + # for: 5m + # labels: + # severity: error + rules: [] + +## Admin swagger should have only internal access. Hence linked to internal gateway +istio: + enabled: false + gateways: + - istio-system/internal + prefix: + corsPolicy: + allowOrigins: + - prefix: https://api-internal.sandbox.xyz.net + allowCredentials: true + allowHeaders: + - Accept + - Accept-Encoding + - Accept-Language + - Connection + - Content-Type + - Cookie + - Host + - Referer + - Sec-Fetch-Dest + - Sec-Fetch-Mode + - Sec-Fetch-Site + - Sec-Fetch-User + - Origin + - Upgrade-Insecure-Requests + - User-Agent + - sec-ch-ua + - sec-ch-ua-mobile + - sec-ch-ua-platform + - x-xsrf-token + - xsrf-token + allowMethods: + - GET + - POST + - PATCH + - PUT + - DELETE + +modules: + prereg: + enabled: false + image: + repository: mosipqa/apitest-prereg + tag: develop + pullPolicy: Always + masterdata: + enabled: false + image: + repository: mosipqa/apitest-masterdata + tag: develop + pullPolicy: Always + idrepo: + enabled: false + image: + repository: mosipqa/apitest-idrepo + tag: develop + pullPolicy: Always + partner: + enabled: false + image: + repository: mosipqa/apitest-pms + tag: develop + pullPolicy: Always + pms: + enabled: false + image: + repository: mosipdev/apitest-pms + tag: develop + pullPolicy: Always + resident: + enabled: false + image: + repository: mosipqa/apitest-resident + tag: develop + pullPolicy: Always + auth: + enabled: false + image: + repository: mosipqa/apitest-auth + tag: develop + pullPolicy: Always + esignet: + enabled: false + image: + repository: mosipqa/apitest-esignet + tag: develop + pullPolicy: Always + mimoto: + enabled: false + image: + repository: mosipqa/apitest-mimoto + tag: develop + pullPolicy: Always + +crontime: "0 3 * * *" ## run cronjob every day at 3 AM (time hr: 0-23 ) + +apitestrig: + configmaps: + s3: + s3-host: 'http://minio.minio:9000' + s3-user-key: 'admin' + s3-region: '' + db: + db-port: '5432' + db-su-user: 'postgres' + db-server: 'api-internal.sandbox.xyz.net' + apitestrig: + ENV_USER: 'api-internal.sandbox' + ENV_ENDPOINT: 'https://api-internal.sandbox.xyz.net' + ENV_TESTLEVEL: 'smokeAndRegression' + authDemoServiceBaseURL: http://authdemo.authdemo + authDemoServicePort: 80 + eSignetDeployed: yes or no + authCertsPath: '/home/mosip/authcerts' + scripts: + fetch_docker_image_hash_ids.sh: | + #!/bin/bash + sleep 5 + export DOCKER_HASH_ID=$( kubectl get pod "$HOSTNAME" -n "$NS" -o jsonpath='{.status.containerStatuses[*].imageID}' | sed 's/ /\n/g' | grep -v 'istio' | sed 's/docker\-pullable\:\/\///g' ) + export DOCKER_IMAGE=$( kubectl get pod "$HOSTNAME" -n "$NS" -o jsonpath='{.status.containerStatuses[*].image}' | sed 's/ /\n/g' | grep -v 'istio' | sed 's/docker\-pullable\:\/\///g' ) + if [[ -z $DOCKER_HASH_ID ]]; then + echo "DOCKER_HASH_ID IS EMPTY;EXITING"; + exit 1; + fi + echo "DOCKER_HASH_ID ; $DOCKER_HASH_ID" + echo "DOCKER_IMAGE : $DOCKER_IMAGE" + kubectl get pods -A -o=jsonpath='{range .items[*]}{.metadata.namespace}{","}{.metadata.labels.app\.kubernetes\.io\/name}{","}{.status.containerStatuses[?(@.name!="istio-proxy")].image}{","}{.status.containerStatuses[?(@.name!="istio-proxy")].imageID}{","}{.metadata.creationTimestamp}{"\n"}' | sed 's/ /\n/g' | grep -vE 'istio*|longhorn*|cattle*|rancher|kube' | sed 's/docker\-pullable\:\/\///g' | sort -u | sed '/,,,/d' | awk -F ',' 'BEGIN {print "{ \"POD_NAME\": \"'$(echo $HOSTNAME)'\", \"DOCKER_IMAGE\": \"'$(echo $DOCKER_IMAGE)'\", \"DOCKER_HASH_ID\": \"'$(echo $DOCKER_HASH_ID)'\", \"k8s-cluster-image-list\": ["} {print "{"} {print "\"namespace\": \"" $1 "\","} {print "\"app_name\": \"" $2 "\","} {print "\"docker_image_name\": \"" $3 "\","} {print "\"docker_image_id\": \"" $4 "\","} {print "\"creation_timestamp\": \"" $5 "\"" } {print "},"} END {print "]}"}' | sed -z 's/},\n]/}\n]/g' | jq -r . | tee -a images-list.json + ## run entrypoint script + sleep 5 + cd /home/${container_user}/ + bash ./entrypoint.sh + secrets: + s3: + s3-user-secret: 'password' + apitestrig: + volumes: + configmaps: + scripts: + defaultMode: 0777 + volumeMounts: + mountPath: '/home/mosip/scripts/' + reports: + name: apitestrig-reports + storageClass: nfs-client + accessModes: + - ReadWriteMany + size: 10Mi + existingClaim: + # Dir where config and keys are written inside container + mountDir: /home/mosip/testrig/report + nfs: + path: "/srv/nfs/sandbox/onboarding" # Dir within the nfs server where config repo is cloned/maintained locally. + server: "nfs-server" # Ip address of nfs server. + variables: + push_reports_to_s3: "no" +enable_insecure: false diff --git a/helm/esignet/Chart.yaml b/helm/esignet/Chart.yaml index 265aabe4c..d23e1a56d 100644 --- a/helm/esignet/Chart.yaml +++ b/helm/esignet/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: esignet description: A Helm chart for MOSIP esignet module type: application -version: 0.0.1-test +version: 1.5.0-develop appVersion: "" dependencies: - name: common diff --git a/helm/esignet/values.yaml b/helm/esignet/values.yaml index f394b1e75..db5bb3f51 100644 --- a/helm/esignet/values.yaml +++ b/helm/esignet/values.yaml @@ -53,7 +53,7 @@ service: image: registry: docker.io repository: mosipdev/esignet - tag: develop + tag: release-1.5.x ## Specify a imagePullPolicy ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images @@ -77,10 +77,10 @@ startupProbe: httpGet: path: /v1/esignet/actuator/health port: 8088 - initialDelaySeconds: 0 + initialDelaySeconds: 180 periodSeconds: 10 timeoutSeconds: 5 - failureThreshold: 30 + failureThreshold: 60 successThreshold: 1 livenessProbe: @@ -88,7 +88,7 @@ livenessProbe: httpGet: path: /v1/esignet/actuator/health port: 8088 - initialDelaySeconds: 20 + initialDelaySeconds: 200 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 @@ -99,7 +99,7 @@ readinessProbe: httpGet: path: /v1/esignet/actuator/health port: 8088 - initialDelaySeconds: 0 + initialDelaySeconds: 180 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 6 @@ -252,10 +252,14 @@ extraEnvVars: | secretKeyRef: name: esignet-captcha key: esignet-captcha-site-key + - name: MOSIP_ESIGNET_INTEGRATION_AUDIT_PLUGIN + value: LoggerAuditService - name: MOSIP_ESIGNET_CAPTCHA_MODULE_NAME value: esignet - name: IDA_AUTHENTICATOR_ENV value: Staging + - name: MOSIP_ESIGNET_UI_CONFIG_USERNAME_PREFIX + value: '+91' - name: REDIS_HOST valueFrom: configMapKeyRef: diff --git a/helm/oidc-ui/Chart.yaml b/helm/oidc-ui/Chart.yaml index 1993fec29..e54a454e9 100644 --- a/helm/oidc-ui/Chart.yaml +++ b/helm/oidc-ui/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: oidc-ui description: A Helm chart for MOSIP OIDC UI module type: application -version: 0.0.1-test +version: 1.5.0-develop appVersion: "" dependencies: - name: common diff --git a/helm/oidc-ui/values.yaml b/helm/oidc-ui/values.yaml index 5c7b25212..db05da2aa 100755 --- a/helm/oidc-ui/values.yaml +++ b/helm/oidc-ui/values.yaml @@ -52,7 +52,7 @@ service: image: registry: docker.io repository: mosipdev/oidc-ui - tag: develop + tag: release-1.5.x ## Specify a imagePullPolicy ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java index b9405ffd1..a4095bc2e 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java @@ -146,7 +146,7 @@ protected void addEntryInLinkAuthCodeStatusDeferredResultMap(String key, Deferre LINK_AUTH_CODE_STATUS_DEFERRED_RESULT_MAP.put(key, deferredResult); } - @KafkaListener(id = "link-status-consumer", autoStartup = "${kafka.enabled:true}", topics = "${mosip.esignet.kafka.linked-session.topic}") + @KafkaListener(id = "${spring.kafka.consumer.group-id}"+"-link-status", autoStartup = "${kafka.enabled:true}", topics = "${mosip.esignet.kafka.linked-session.topic}") public void consumeLinkStatus(String linkCodeHash) { DeferredResult deferredResult = LINK_STATUS_DEFERRED_RESULT_MAP.get(linkCodeHash); if(deferredResult != null) { @@ -156,7 +156,7 @@ public void consumeLinkStatus(String linkCodeHash) { } } - @KafkaListener(id = "link-auth-code-status-consumer", autoStartup = "${kafka.enabled:true}", topics = "${mosip.esignet.kafka.linked-auth-code.topic}") + @KafkaListener(id = "${spring.kafka.consumer.group-id}"+"-linked-auth-code", autoStartup = "${kafka.enabled:true}", topics = "${mosip.esignet.kafka.linked-auth-code.topic}") public void consumeLinkAuthCodeStatus(String linkTransactionId) { DeferredResult deferredResult = LINK_AUTH_CODE_STATUS_DEFERRED_RESULT_MAP.get(linkTransactionId); if(deferredResult != null) { diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java index c485c04c4..6292e24a3 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java @@ -277,12 +277,13 @@ public ClaimDetailResponse getClaimDetails(String transactionId) { } //Profile update is mandated only if any essential verified claim is requested - boolean unverifiedEssentialClaimsExist = transaction.getResolvedClaims().getUserinfo() + boolean unverifiedEssentialClaimsExists = transaction.getResolvedClaims().getUserinfo() .entrySet() .stream() .anyMatch( entry -> entry.getValue().stream() - .anyMatch(m -> (boolean) m.getOrDefault("essential", false) && m.get("verification") == null )); - claimDetailResponse.setProfileUpdateRequired(unverifiedEssentialClaimsExist); + .anyMatch(m -> (boolean) m.getOrDefault("essential", false) && m.get("verification") != null && + transaction.getClaimMetadata().getOrDefault(entry.getKey(), Collections.EMPTY_LIST).isEmpty() )); + claimDetailResponse.setProfileUpdateRequired(unverifiedEssentialClaimsExists); claimDetailResponse.setClaimStatus(list); auditWrapper.logAudit(Action.CLAIM_DETAILS, ActionStatus.SUCCESS, AuditHelper.buildAuditDto(transactionId, transaction), null); diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/ClaimsHelperService.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/ClaimsHelperService.java index 446875ec4..e3cce84b6 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/ClaimsHelperService.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/ClaimsHelperService.java @@ -164,7 +164,8 @@ protected ClaimStatus getClaimStatus(String claim, List> cla //if the claim is requested without any verification metadata if(CollectionUtils.isEmpty(claimDetails) || claimDetails.stream().allMatch( m -> m.get("verification") == null)) - return new ClaimStatus(claim, false, storedVerificationMetadata.containsKey(claim)); + return new ClaimStatus(claim, !storedVerificationMetadata.getOrDefault(claim, Collections.emptyList()).isEmpty(), + storedVerificationMetadata.containsKey(claim)); log.info("Request to fetch verification metadata for {} with filter criteria : {}", claim, claimDetails); List storedVerificationDetails = storedVerificationMetadata.get(claim); diff --git a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java index 3de60b0ce..c982a6ac9 100644 --- a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java +++ b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java @@ -19,6 +19,7 @@ import io.mosip.esignet.api.spi.AuditPlugin; import io.mosip.esignet.api.spi.Authenticator; import io.mosip.esignet.api.util.ConsentAction; +import io.mosip.esignet.api.util.FilterCriteriaMatcher; import io.mosip.esignet.core.constants.Constants; import io.mosip.esignet.core.dto.*; import io.mosip.esignet.core.exception.EsignetException; @@ -102,9 +103,14 @@ public void setUp() { claims.put("profile", Arrays.asList("given_name", "profile_picture", "name", "phone_number", "email")); claims.put("email", Arrays.asList("email","email_verified")); claims.put("phone", Arrays.asList("phone_number","phone_number_verified")); + + FilterCriteriaMatcher filterCriteriaMatcher = new FilterCriteriaMatcher(); + ReflectionTestUtils.setField(filterCriteriaMatcher,"objectMapper", new ObjectMapper()); + ClaimsHelperService claimsHelperService = new ClaimsHelperService(); ReflectionTestUtils.setField(claimsHelperService,"claims", claims); ReflectionTestUtils.setField(claimsHelperService,"objectMapper", new ObjectMapper()); + ReflectionTestUtils.setField(claimsHelperService,"filterCriteriaMatcher", filterCriteriaMatcher); ReflectionTestUtils.setField(authorizationHelperService, "credentialScopes", Arrays.asList("sample_ldp_vc")); ReflectionTestUtils.setField(authorizationHelperService, "authorizeScopes", Arrays.asList("resident-service")); @@ -1371,24 +1377,67 @@ public void getAuthCode_withInValidTransactionId_thenFail() { } @Test - public void getClaimDetails_withValidTransaction_thenPass(){ + public void getClaimDetails_withUnVerifiedClaimsRequest_thenPass(){ OIDCTransaction transaction=new OIDCTransaction(); Claims resolvedClaims = new Claims(); resolvedClaims.setUserinfo(new HashMap<>()); Map map = new HashMap<>(); map.put("essential", true); - map.put("verification", new HashMap<>()); resolvedClaims.getUserinfo().put("name", Arrays.asList(map)); - transaction.setResolvedClaims(resolvedClaims); transaction.setEssentialClaims(List.of("name", "email")); transaction.setVoluntaryClaims(List.of("phone_number")); + + Map> claimMetadata = new HashMap<>(); + transaction.setClaimMetadata(claimMetadata); transaction.setConsentAction(ConsentAction.NOCAPTURE); Mockito.when(cacheUtilService.getAuthenticatedTransaction(Mockito.anyString())).thenReturn(transaction); ClaimDetailResponse claimDetailResponse = authorizationServiceImpl.getClaimDetails("transactionId"); Assert.assertEquals(claimDetailResponse.getConsentAction(),ConsentAction.NOCAPTURE); Assert.assertEquals(claimDetailResponse.getTransactionId(),"transactionId"); + Assert.assertFalse(claimDetailResponse.isProfileUpdateRequired()); + } + + @Test + public void getClaimDetails_withVerifiedClaimsRequest_thenPass() throws JsonProcessingException { + OIDCTransaction transaction=new OIDCTransaction(); + Claims resolvedClaims = new Claims(); + resolvedClaims.setUserinfo(new HashMap<>()); + Map map = new HashMap<>(); + map.put("essential", true); + Map requestedVerification = new HashMap<>(); + requestedVerification.put("trust_framework", null); + map.put("verification", requestedVerification); + resolvedClaims.getUserinfo().put("name", Arrays.asList(map)); + transaction.setResolvedClaims(resolvedClaims); + transaction.setEssentialClaims(List.of("name", "email")); + transaction.setVoluntaryClaims(List.of("phone_number")); + + Map> claimMetadata = new HashMap<>(); + transaction.setClaimMetadata(claimMetadata); + transaction.setConsentAction(ConsentAction.CAPTURE); + Mockito.when(cacheUtilService.getAuthenticatedTransaction(Mockito.anyString())).thenReturn(transaction); + + ClaimDetailResponse claimDetailResponse = authorizationServiceImpl.getClaimDetails("transactionId"); + Assert.assertEquals(claimDetailResponse.getConsentAction(),ConsentAction.CAPTURE); + Assert.assertEquals(claimDetailResponse.getTransactionId(),"transactionId"); + Assert.assertTrue(claimDetailResponse.getClaimStatus().stream().allMatch(cs -> !cs.isVerified() && !cs.isAvailable())); + Assert.assertTrue(claimDetailResponse.isProfileUpdateRequired()); + + Map emailMap = new HashMap<>(); + emailMap.put("essential", true); + resolvedClaims.getUserinfo().put("email", Arrays.asList(emailMap)); + Map phoneMap = new HashMap<>(); + phoneMap.put("essential", false); + resolvedClaims.getUserinfo().put("phone_number", Arrays.asList(phoneMap)); + claimMetadata.put("name", Arrays.asList(objectMapper.readTree("{\"verification\": {\"trust_framework\": \"XYZ TF\"}}"))); + claimMetadata.put("phone_number", Arrays.asList()); + claimDetailResponse = authorizationServiceImpl.getClaimDetails("transactionId"); + Assert.assertTrue(claimDetailResponse.getClaimStatus().stream().anyMatch(cs -> cs.getClaim().equals("name") && cs.isVerified() && cs.isAvailable())); + Assert.assertTrue(claimDetailResponse.getClaimStatus().stream().anyMatch(cs -> cs.getClaim().equals("email") && !cs.isVerified() && !cs.isAvailable())); + Assert.assertTrue(claimDetailResponse.getClaimStatus().stream().anyMatch(cs -> cs.getClaim().equals("phone_number") && !cs.isVerified() && cs.isAvailable())); + Assert.assertFalse(claimDetailResponse.isProfileUpdateRequired()); } @Test diff --git a/partner-onboarder/README.md b/partner-onboarder/README.md index 45b62cbb3..1fc1204ee 100644 --- a/partner-onboarder/README.md +++ b/partner-onboarder/README.md @@ -4,6 +4,19 @@ Exchanges certificates for eSignet MISP partner. Refer [mosip-onboarding repo](https://github.com/mosip/mosip-onboarding). ## Install +* Create a directory for onboarder on the NFS server at `/srv/nfs//onboarder/`: +``` +mkdir -p /srv/nfs/mosip//onboarder/ +``` +* Ensure the directory has 777 permissions: +``` +chmod 777 /srv/nfs/mosip//onboarder +``` +* Add the following entry to the /etc/exports file: +``` +/srv/nfs/mosip//onboarder *(ro,sync,no_root_squash,no_all_squash,insecure,subtree_check) +``` + * Set `values.yaml` to run onboarder for specific modules. * run `./install.sh`. ``` diff --git a/partner-onboarder/install.sh b/partner-onboarder/install.sh index ed442938d..82599ed23 100755 --- a/partner-onboarder/install.sh +++ b/partner-onboarder/install.sh @@ -21,7 +21,7 @@ if [ "$flag" = "n" ]; then fi NS=esignet -CHART_VERSION=0.0.1-develop +CHART_VERSION=1.5.0-es-develop echo Create $NS namespace kubectl create ns $NS || true @@ -73,7 +73,7 @@ function installing_onboarder() { echo "NFS server not provided; EXITING." exit 1; fi - read -p "Please provide NFS directory to store reports from NFS server (e.g. /srv/nfs//onboarder/), make sure permission is 777 for the folder: " nfs_path + read -p "Please provide NFS directory to store reports from NFS server (e.g. /srv/nfs/mosip//onboarder/), make sure permission is 777 for the folder: " nfs_path if [[ -z $nfs_path ]]; then echo "NFS Path not provided; EXITING." exit 1; @@ -113,7 +113,9 @@ function installing_onboarder() { -f values.yaml \ --version $CHART_VERSION \ --wait --wait-for-jobs - echo "Partner onboarded successfully and reports are moved to S3 or NFS" + echo "Partner onboarder executed and reports are moved to S3 or NFS please check the same to make sure partner was onboarded sucessfully." + kubectl rollout restart deployment -n esignet esignet + echo eSignet MISP License Key updated successfully to eSignet. return 0 fi } diff --git a/partner-onboarder/values.yaml b/partner-onboarder/values.yaml index c6adfd94c..cb214c694 100644 --- a/partner-onboarder/values.yaml +++ b/partner-onboarder/values.yaml @@ -1,7 +1,14 @@ -image: - registry: docker.io - repository: mosipdev/partner-onboarder - tag: develop +#image: +# registry: docker.io +# repository: mosipdev/partner-onboarder +# tag: develop +resources: + limits: + cpu: 500m + memory: 500Mi + requests: + cpu: 500m + memory: 500Mi onboarding: modules: @@ -17,7 +24,7 @@ onboarding: # ns_esignet: esignet # ns_signup: signup # secrets: -# s3: +# s3: # s3-user-secret: 'password' # volumes: # reports: diff --git a/postman-collection/eSignet-with-MOSIP.postman_environment.json b/postman-collection/eSignet-with-MOSIP.postman_environment.json index ea264c137..043f15d72 100644 --- a/postman-collection/eSignet-with-MOSIP.postman_environment.json +++ b/postman-collection/eSignet-with-MOSIP.postman_environment.json @@ -84,7 +84,7 @@ }, { "key": "claims_v3", - "value": "{\n \"userinfo\": {\n \"name\": {\n \"essential\": false\n },\n \"phone_number\": {\n \"essential\": true\n },\n \"verified_claims\": [\n {\n \"verification\": {\n \"trust_framework\": null,\n \"time\": null\n },\n \"claims\": {\n \"name\": {\n \"essential\": true\n }\n }\n }\n ]\n },\n \"id_token\": {}\n}", + "value": "{\n \"userinfo\": {\n \"name\": {\n \"essential\": false\n },\n \"phone_number\": {\n \"essential\": true\n },\n \"verified_claims\": [\n {\n \"verification\": {\n \"trust_framework\": null,\n \"time\": null\n },\n \"claims\": {\n \"email\": {\n \"essential\": true\n }\n }\n }\n ]\n },\n \"id_token\": {}\n}", "type": "default", "enabled": true }, @@ -96,7 +96,7 @@ }, { "key": "accepted_claims", - "value": "[\"phone_number\", \"name\"]", + "value": "[\"email\", \"name\", \"phone_number\"]", "type": "default", "enabled": true }, @@ -117,9 +117,15 @@ "value": "http://localhost:8082", "type": "default", "enabled": true + }, + { + "key": "iam_url", + "value": "https://iam.collab.mosip.net", + "type": "default", + "enabled": true } ], "_postman_variable_scope": "environment", - "_postman_exported_at": "2024-09-19T19:20:29.482Z", - "_postman_exported_using": "Postman/11.12.0" + "_postman_exported_at": "2024-12-12T18:27:58.549Z", + "_postman_exported_using": "Postman/11.21.0" } \ No newline at end of file diff --git a/postman-collection/eSignet-with-mock.postman_environment.json b/postman-collection/eSignet-with-mock.postman_environment.json index c3e72fe96..7ca9ffef6 100644 --- a/postman-collection/eSignet-with-mock.postman_environment.json +++ b/postman-collection/eSignet-with-mock.postman_environment.json @@ -1,5 +1,5 @@ { - "id": "0affa285-388e-4f7e-8634-f6860ba94edd", + "id": "de435b57-a284-4514-81a9-ce20a91c88be", "name": "eSignet-with-mock", "values": [ { @@ -83,7 +83,7 @@ }, { "key": "accepted_claims", - "value": "[\"phone_number\", \"name\"]", + "value": "[\"email\", \"phone_number\", \"name\"]", "type": "default", "enabled": true }, @@ -98,9 +98,15 @@ "value": "http://localhost:8082", "type": "default", "enabled": true + }, + { + "key": "iam_url", + "value": "https://iam.collab.mosip.net", + "type": "default", + "enabled": true } ], "_postman_variable_scope": "environment", - "_postman_exported_at": "2024-09-19T19:18:36.603Z", - "_postman_exported_using": "Postman/11.12.0" + "_postman_exported_at": "2024-12-12T18:27:48.937Z", + "_postman_exported_using": "Postman/11.21.0" } \ No newline at end of file diff --git a/postman-collection/eSignet.postman_collection.json b/postman-collection/eSignet.postman_collection.json index 7e047571d..06ccc7b1e 100644 --- a/postman-collection/eSignet.postman_collection.json +++ b/postman-collection/eSignet.postman_collection.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "702e0fb0-606d-452f-b160-b9bbf6cbe31f", + "_postman_id": "3267907a-75db-4d3c-a42e-89050178c6f4", "name": "eSignet", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "20579541", - "_collection_link": "https://mosip-idp.postman.co/workspace/MOSIP-Team-Workspace~76ac5ada-d630-4817-8d7b-25f499b093d4/collection/20579541-702e0fb0-606d-452f-b160-b9bbf6cbe31f?action=share&source=collection_link&creator=20579541" + "_collection_link": "https://mosip-idp.postman.co/workspace/MOSIP-Team-Workspace~76ac5ada-d630-4817-8d7b-25f499b093d4/collection/20579541-3267907a-75db-4d3c-a42e-89050178c6f4?action=share&source=collection_link&creator=20579541" }, "item": [ { @@ -131,6 +131,63 @@ { "name": "Mock", "item": [ + { + "name": "Get Auth token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var jsonData = JSON.parse(pm.response.text());", + "var data = jsonData.access_token;", + "console.log(data);", + "pm.environment.set(\"authtoken\",data);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "client_secret", + "value": "ysd1exB4FXsZebbA", + "type": "text" + }, + { + "key": "client_id", + "value": "mosip-pms-client", + "type": "text" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "text" + } + ] + }, + "url": { + "raw": "{{iam_url}}/auth/realms/mosip/protocol/openid-connect/token", + "host": [ + "{{iam_url}}" + ], + "path": [ + "auth", + "realms", + "mosip", + "protocol", + "openid-connect", + "token" + ] + } + }, + "response": [] + }, { "name": "Get CSRF token", "event": [ @@ -192,7 +249,8 @@ "const publicKeyPem = pmlib.rs.KEYUTIL.getPEM(kp.pubKeyObj, \"PKCS8PUB\");", "// Convert the PEM format to a simple Base64 string (removing PEM headers)", "const publicKeyBase64 = publicKeyPem.replace(/-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----|\\n/g, '');", - "pm.environment.set(\"client_id\", publicKeyBase64.substring(2, 50));", + "let updatedStr = publicKeyBase64.replace(/\\//g, \"_\");", + "pm.environment.set(\"client_id\", updatedStr.substring(2, 50));", "", "// Log the result to Postman's console (option);", "pm.environment.set(\"client_private_key\", JSON.stringify(privateKey_jwk));", @@ -205,7 +263,14 @@ ], "request": { "auth": { - "type": "noauth" + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{authtoken}}", + "type": "string" + } + ] }, "method": "POST", "header": [ @@ -217,7 +282,7 @@ ], "body": { "mode": "raw", - "raw": "{ \n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"clientId\": \"{{client_id}}\",\n \"clientName\": \"{{$randomCompanyName}}\",\n \"publicKey\": {{client_public_key}},\n \"relyingPartyId\": \"mock-relying-party-id\",\n \"userClaims\": [\n \"name\",\n \"email\",\n \"gender\",\n \"phone_number\",\n \"picture\",\n \"birthdate\"\n ],\n \"authContextRefs\": [\n \"mosip:idp:acr:generated-code\",\n \"mosip:idp:acr:password\",\n \"mosip:idp:acr:linked-wallet\"\n ],\n \"logoUri\": \"{{$randomImageUrl}}\",\n \"redirectUris\": [\n \"{{redirection_url}}\",\n \"io.mosip.residentapp://oauth\",\n \"http://loclahost:3000/**\",\n \"http://loclahost:5000/registration/*\"\n ],\n \"grantTypes\": [\n \"authorization_code\"\n ],\n \"clientAuthMethods\": [\n \"private_key_jwt\"\n ]\n }\n}", + "raw": "{ \n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"clientId\": \"{{client_id}}\",\n \"clientName\": \"{{$randomCompanyName}}\",\n \"publicKey\": {{client_public_key}},\n \"relyingPartyId\": \"mock-relying-party-id\",\n \"userClaims\": [\n \"name\",\n \"email\",\n \"gender\",\n \"phone_number\",\n \"picture\",\n \"birthdate\"\n ],\n \"authContextRefs\": [\n \"mosip:idp:acr:generated-code\",\n \"mosip:idp:acr:password\",\n \"mosip:idp:acr:linked-wallet\"\n ],\n \"logoUri\": \"{{$randomImageUrl}}\",\n \"redirectUris\": [\n \"{{redirection_url}}\",\n \"io.mosip.residentapp://oauth\",\n \"http://localhost:3000/**\",\n \"http://localhost:5000/registration/*\"\n ],\n \"grantTypes\": [\n \"authorization_code\"\n ],\n \"clientAuthMethods\": [\n \"private_key_jwt\"\n ]\n }\n}", "options": { "raw": { "language": "json" @@ -255,7 +320,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"logoUri\": \"{{$randomImageUrl}}\",\n \"redirectUris\": [\n \"{{redirection_url}}\",\n \"io.mosip.residentapp://oauth\",\n \"http://loclahost:3000/**\",\n \"http://loclahost:5000/registration/*\"\n ],\n \"userClaims\": [\n \"name\",\n \"email\",\n \"gender\",\n \"phone_number\",\n \"birthdate\",\n \"picture\",\n \"address\"\n ],\n \"authContextRefs\": [\n \"mosip:idp:acr:generated-code\",\n \"mosip:idp:acr:password\",\n \"mosip:idp:acr:linked-wallet\",\n \"mosip:idp:acr:biometrics\"\n ],\n \"status\": \"ACTIVE\",\n \"grantTypes\": [\n \"authorization_code\"\n ],\n \"clientName\": \"{{$randomCompanyName}}\",\n \"clientAuthMethods\": [\n \"private_key_jwt\"\n ]\n }\n}", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"logoUri\": \"{{$randomImageUrl}}\",\n \"redirectUris\": [\n \"{{redirection_url}}\",\n \"io.mosip.residentapp://oauth\",\n \"http://localhost:3000/**\",\n \"http://localhost:5000/registration/*\"\n ],\n \"userClaims\": [\n \"name\",\n \"email\",\n \"gender\",\n \"phone_number\",\n \"birthdate\",\n \"picture\",\n \"address\"\n ],\n \"authContextRefs\": [\n \"mosip:idp:acr:generated-code\",\n \"mosip:idp:acr:password\",\n \"mosip:idp:acr:linked-wallet\",\n \"mosip:idp:acr:biometrics\",\n \"mosip:idp:acr:static-code\"\n ],\n \"status\": \"ACTIVE\",\n \"grantTypes\": [\n \"authorization_code\"\n ],\n \"clientName\": \"{{$randomCompanyName}}\",\n \"clientAuthMethods\": [\n \"private_key_jwt\"\n ]\n }\n}", "options": { "raw": { "language": "json" @@ -295,7 +360,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\"individualId\":\"{{individual_id}}\",\"pin\":\"545411\",\"email\":\"siddhartha.km@gmail.com\",\"phone\":\"+919427357934\",\"fullName\":[{\"language\":\"fra\",\"value\":\"Siddharth K Mansour\"},{\"language\":\"ara\",\"value\":\"تتگلدكنسَزقهِقِفل دسييسيكدكنوڤو\"},{\"language\":\"eng\",\"value\":\"Siddharth K Mansour\"}],\"gender\":[{\"language\":\"eng\",\"value\":\"Male\"},{\"language\":\"fra\",\"value\":\"Mâle\"},{\"language\":\"ara\",\"value\":\"ذكر\"}],\"dateOfBirth\":\"1987/11/25\",\"streetAddress\":[{\"language\":\"fra\",\"value\":\"yuān⥍\"},{\"language\":\"ara\",\"value\":\"$لُنگᆑ\"},{\"language\":\"eng\",\"value\":\"Slung\"}],\"locality\":[{\"language\":\"fra\",\"value\":\"yuān 2\"},{\"language\":\"ara\",\"value\":\"يَُانꉛ⥍\"},{\"language\":\"eng\",\"value\":\"yuan wee\"}],\"region\":[{\"language\":\"fra\",\"value\":\"yuān 3\"},{\"language\":\"ara\",\"value\":\"$لُنگᆑ\"},{\"language\":\"eng\",\"value\":\"yuan wee 3\"}],\"postalCode\":\"45009\",\"country\":[{\"language\":\"fra\",\"value\":\"CMâttye\"},{\"language\":\"ara\",\"value\":\"دسييسيكدك\"},{\"language\":\"eng\",\"value\":\"Cmattey\"}],\"encodedPhoto\":\"data:image/jpeg;base64,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\"}\n}", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"individualId\": \"{{individual_id}}\",\n \"pin\": \"545411\",\n \"email\": \"siddhartha.km@gmail.com\",\n \"phone\": \"+919427357934\",\n \"fullName\": [\n {\n \"language\": \"fra\",\n \"value\": \"Siddharth K Mansour\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"تتگلدكنسَزقهِقِفل دسييسيكدكنوڤو\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"Siddharth K Mansour\"\n }\n ],\n \"givenName\": [\n {\n \"language\": \"fra\",\n \"value\": \"Siddharth K Mansour\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"تتگلدكنسَزقهِقِفل دسييسيكدكنوڤو\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"Siddharth K Mansour\"\n }\n ],\n \"familyName\": [\n {\n \"language\": \"fra\",\n \"value\": \"Mansour\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"تتگلدكنسَزقهِقِفل\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"Mansour\"\n }\n ],\n \"gender\": [\n {\n \"language\": \"eng\",\n \"value\": \"Male\"\n },\n {\n \"language\": \"fra\",\n \"value\": \"Mâle\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"ذكر\"\n }\n ],\n \"dateOfBirth\": \"1987/11/25\",\n \"streetAddress\": [\n {\n \"language\": \"fra\",\n \"value\": \"yuān⥍\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"$لُنگᆑ\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"Slung\"\n }\n ],\n \"locality\": [\n {\n \"language\": \"fra\",\n \"value\": \"yuān 2\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"يَُانꉛ⥍\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"yuan wee\"\n }\n ],\n \"region\": [\n {\n \"language\": \"fra\",\n \"value\": \"yuān 3\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"$لُنگᆑ\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"yuan wee 3\"\n }\n ],\n \"postalCode\": \"45009\",\n \"country\": [\n {\n \"language\": \"fra\",\n \"value\": \"CMâttye\"\n },\n {\n \"language\": \"ara\",\n \"value\": \"دسييسيكدك\"\n },\n {\n \"language\": \"eng\",\n \"value\": \"Cmattey\"\n }\n ],\n \"encodedPhoto\": \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCgoKBggLDAsKDAkKCgr/2wBDAQICAgICAgUDAwUKBwYHCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgr/wAARCACBAH0DASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8afAXw+8B6trH9lat4djuUkulDSGV0YDPPKsK9Mvfg98AvB3xIs9G1P4bpqOmXJ4WTWbyHau4DA8uYep712nxf/Zw/wCFRfFCVNAsV+yTzkwLjHljIAAA7V55451q4v8AxHALtXU20WYwwwecMK4cPivaaHbXw3sq3sz6csf2Kf2Ftaso9UtvgBbqkq7kRfEup4I/8Ca19I/Yh/YNRB9q/ZrtnIPzE+K9WBI7j/j64z7Vm/A7xBear8OdJuZ2J/0Uc59zXeQ3ZZtwbvWybT3OJRTR8Kf8FG/hj8Evhb8btJ8MfA/4eReGNMuPCUV3c2EGp3V2GuDd3abt9zJI+fLji4zj2r54e3TGVNfRH/BTqUQftD6RIzcP4QhOfc3V0P6CvnhGbBXNb7oerGJERnJpJNxY4qbJI5PeoGkkMu1T3pqyFa71G7GZgDDvLMAF9TXvPwk+DHgU+CxqHjixSe5vdssMZHMCYIZcggj1rxnRYp31OAQwpJIsqsEc4Bwa9xPiuz0XRZ5dULJc3ELMqRjcsfBGAfSvPx1SqopQep62X0qd+dnN2nwn8GeKvibJ4S8L6XHHaRykPNNPIwVRjuWPYmv0Y/Yn/wCCDnwc+K9vZ+NPizDqA0i6QSQWazyxPeqf44h5kREYIIOMn3rwP/gkB+zRbfGv4qy/ELxRA1zpNjc7pLOdP3U7bQw3Ht0xxX7y/BnQ7a0sIZcCQuoMajlUHopHUV8vnGZYylWVOnUPt8pyfBuh9YnTPmux/wCDdL/gl9LppnPwJuzIMYb/AIS3WB+gvMV45+0P/wAG437NC6Hd6l8EPCU2nXK5+z20mp3M65IOP9fM/Nfq7pfmR23kvxVLxTbN9hlwOMUqmIzFUeb2hVHLcteI0gfyxftZfsC+PP2X/Fj+H/F3g84Qtul2McYOOcDivEprLwxpz+RceHrZm9Wz/jX9LX7WP7MPw8+P+gXGi+K9DtzIyHZcsuWB59Tivwb/AG8P2VpvgH8c77wdp+nNLZiWT7JIiZygIHbjvXTlOd1KkvZ1dTjzTIcPSfPTPtb9u3wymj6imo2zqkkc6gOrD7m/5ua+OPjXZ6Xf6wutaRMjB1IfYQRkmvsH9urWbuG7m0HWdPCq0mElbOCucHnpXxJNpiN4tn8PWl3ILdZgYo9vBHY4/OujKWuVX2PFzumvban1J+zN4Z1O8+FGk4ty2bVegJ7mvVNN+GPii9lCWekSnPrGR/SvRP8Agl98H7P4gLo/hjUIMxpZEkFc8jmv1D+Hv7GXwx0mNbi90SKWRSPvRf8A169+lhpVHd6HydSr7LRI/mF/4K1eE9S8NftF6Fp2q25hlPgq3cxnqP8ATLwZx74/T2r5kkLYLIT7V+oX/B2v4T0vwZ/wUU8D6L4fskgg/wCFGaW6xoMAf8TnWh/QV+X9qVbhzxXRfl+Q07CQi5l2wxjLMwVR6k11Vv8ACDxdDZjUr6zjiR4i6ySSYGB7nv7Vl+EoYH8Swm9H+jod5bHQg5FdRrfjjXPFV2mj6xr03kGVY4LYsCsQJ7fTNYzqvc6YRUpWE+DXw8m8QeIJr/UpWEEEL528jftyBU/xI1iawgW0C5dTsCZ/hJ5r17wt4MtfC3gCbRvD9tcfaHAkuL+dNrSFQfucYZSOp5rwjx1dTXPiLzbiQuqSfOT9a8ajXeKxT7I96rhfquEV+p+nP/BGrxf4t8BfBqCz8HeB4tSuLu9ubu8utQn8i1jiAVYozLnAPDYGOCa/QvwB+3/o/g7WLfR/ij8JNT0yaYgfb9Ju4ryzU5xzICvfofrXxF+xV+zJ8R/iR+wloLfBDxkNE1S7sFuWkK8ThpHlK5HIbOAD2Ir3j4BfskftVaN40v4tf+N2t6r4dh0+JLTSdeeN/tMh3earkx9MHG7IPpXzNf2dXG1O59zhVVoYOlzH6IeFPinoHiXSU1uxvGaGVdyFlwcc9fSvMPjZ+3v4B8A6v/wg2mfD7xDr+pB9jjSrXKKc459eo54FN+Gvh9vBvw98Q6TOyzSWGUs5Mgkjyye3fNeMfHf9mr4x61pOo3/wv8XXVlcT6U8tnqVvLGss11hcK4bAC8tyOflHrXPTxFWvL2Z01cNCF53Ol8dftFXV74bl1zXvg7rWmwOMtKLu1kKDn/lispkB+tfnj+374MPjr4gaTrcEBbfZSliR3L17b8EP2dv+ChOieJrSH4hfEA61ootn/tOW9ulWQt22qO2M+v1qr+0vo3h/SfF1vpeoSlBBE6xAL23fWudSpYTFO7NYUvrVMo/8FDvhrYeK/hNJqsOmrJfQXcJRiOdu4k18LaJ8C/Evi/x1pEnhmzR7q5iAuTI+0YD8DP8AwI/nX6UftX2N5cfCnUUglaOZRwV6gYavPv8Agnz+yNceP/CKeJ/FGpXBuruDzNPDRqQ8ZGCcn/awPxr28FifZULo+fxWAWPxmp9S/wDBJv8AZr8YeA7yTXPE9pBGLdTHCY5gxwU/Tmv0W0yIrDg8V87/ALDXgyPwd4OuvC8cwdrG4SJ8Y+U7Se1fRsEnluI84zX3GW1atbCc8z89znCUcLmHJT6H85n/AAeESGL/AIKXeAgDyfgNpg/8rWt1+UsS7ecnrX6q/wDB4U5f/gpn4Gz2+BOmAf8Ag51uvyo3sqnmqSZy043aRPHqNzaviI/rV/w7cW914ls7nVhmNblDJxngMDWXbw3F3L5duhZj0ArqPBfw61bUIbvXNSYxQWj7XVzgkkZ4B6isqsVynVS5vbnvniX4v3N3oF34e8C2YsbOM7Wuo8q0wwecHg9ccV4pPYaddeIrK11S4IiusG6cj7rbsZ/Kt3wfq2seJrB9H0aF5Y7chIhg9MZrUl/Z98X6hbnXtTCww7grRvIA3Psea8CPscNUd9D6DEe1xVKy6H7Gf8EMPHumar+zrodrBdE2mnvJYw+3lOwPHbsfxr9IfGHin4faZ4fafiS5MZHyJlsnPYGvxa/4I1eIvFPh7wVrXgDwo6/8SfxIh8vzMfLJDvwfqY5M/Uetfd2rfFqz8G31r4W+Kd/f/wBo6xaSGzSzhMkkqcq2Mc5+gr43Ht0sbUhD1P0jBUIYrAUud2se/wDhqTTpvAupasCymSdSQ64YfKeorf8AhV4w8Jat4ej0nVoVZ7dRGrOvf86+YtC1VrPTR4b0L4w6vomkR4FxpWraJK9zIR02S4B6cc9zXSad8c/gn8MNDk0O41HXZrq6cKjX2iXUs0smCFMbDcP061lClUbudNXDxqKpTZ738Wb3RNB0AG0hiTMJBljOcZz71+bP7TtheeNviLNJo+mm9S0Z43kZTwSQe1fUnxF134qS+Hzpk3Nq6bre5ll2yyRjOSy44NfCn7Q3jTxzZ+Nm0r4d/HaPwpNb7v7Tt50ObonBjm47Ebh+FTDBSzGroNYqjlmGVz6k+P8AZW19plzpTn5JQVxjrnI/rXp/7PXg1vhh8N/CGo6fcssOn6HbRzRJjcoZ0mPA57Y/GvHfi1ryXWolVfcIpwzL6kHOK+gNWuLHwb8P5rOfUBaSQ2abLnIGI/L4GTxnbgY9q9Olf2Vzw4Vv9otsdx+xX8XYpPip4k8CT2xJnkhulcqed0bfyAH5mvq4xv5+4+vFfJH/AATb8EP421vWvi/e2qxxtOY7Z+7RbW2dfbn8a+wNqSAsDX6VgoKlgaaZ+SZhi1i8zqVD+b7/AIPCWx/wUw8CEH/mhGmZ/wDB1rdflVGwZCGr9U/+DwoAf8FMPAgH/RCNMz9f7a1qvyrgAKnNN2bJprY1fDBvbTVIruzhVwrDcHbAxnnJr23xi7aj4TtJbTS4oSloTdpbktvbJ5P4V43odzBBp0wlbgxsu31BB4q3pXxP8UwWzxnWZlR1KtEG4IIxXJicPKqz0MNXhTi7lj4e/ETU/BHjmPU/D7t5Ly7XgYlRg4Gfwr6WvviZ4d1fw35mu6oZbqUhoERhIw69cdOfavkOKS4n1fdaDLscgV2P2LWtBsU1Zt0NzPgxsp52ng1y5hgKVZx8jpwOMdN+5sfUX7CH7XVh+zh+0XeWnijUzDoniVkh1KRWz9nuQwWOUDuQGPWv2W8W3Wo/F/4daN4q8H6vsv7RVeyuIZBlXy2CDz69a/m3v9Nu5orjVZLozmVsz+aQPmI/nX7If8EwfjP4u8Bfs0eB9R16e5v9JbTYku/OUlrZi7AKFA+7jJJPSvm8+wlOlRWJ7H2PCuY1K1d0p9D74+Gn7SPx/wBB0t7Dx34Rnv78L5cVzYaelwFJBGDKVBGTjvVK20jxR4m8aJ8T/jQM3GnSl9KsE8s/ZcjrNglhIDnHsa6vwX+0D8IjoC3FtqdmwaPcrPKo3Lg5x83NeQfGz9pPU/Gt5ceBvgLorXtzJkSzzrshtj/dzk54OcYrw54+9K6R9Z7al7Zqnh9TB/bJ/af0vwppMj6XI811FYv9mgt1LkK2cFgvKgnHNfjZb/tq+NfAXxM8Y/EDXfAWka/eeJtYEey/vXAs4LQGOJEK9VIcnn0r9UPif8C7zwb4Q1jxP4j15dU1y6s2865uIkhZUQMVgjVT/qxgflX4ffHjw9r3hfxzNZfZPLyzHAJ9a9LhetSdSaW58txRSxio00ftp8VLr+y2udV24WO9SRj/AHkByR+OK8+8Tftc/FD44araeHvEFqtpZsRHNa2crOJQW6kEehxxXX/GC9k1XS7iytDuYoXx7AHNYn7DHwL1L4k/FO1vjYeZHDKDuxnHINenkuEpVqup4XEGPrYVq3U/WP8AYq8Dx/Dz4DabpCAK81ujTBf7w3CvW7b5YzWF4A0FPDnhWy0hF2+TCFxj3Nb+0gYr7VnxMkoxv1Z/N9/weEoW/wCCmXgUHv8AAnTMf+DrW6/K2OMJGa/VX/g8Kf8A42YeAiD934D6WD9f7a1uvyrM4SPLnqeMVlZ8xpBvlQ5bpfLMBPU1C0WH2rmpdKtJL3UVihtJJXZwqxhCSxJ4AAr2/wCGX7Av7SnxfuYrvw38Obu0tJSMXF/G0EYz0BLCsMRisPg1z1Klj0MFlmaY+ryYak53PHvC729vrEU0rHOcV6v4z0XXfENxo2haNZvPcvYwNAkYJLmVm+Xj0+X8TX0LpH/BHPXfBHh278efGLx3FpzWds8gt7V0ljLhSQMjJ6jrXuX/AATw/ZYFukHxc8RQCdoIfJ0zzgBmGRclvbkV8vmfEmApU/bUZXP0HI+BM1r1VRxlP2Z4v8Hf+Cbes+HfAreNfjTYGSa6KS2ehmPcGYg7fMHDLzj6A1+g37Cfw6XQvhZp/wAPtYgWJo7PZKi8/MAf8a7fSfhRe+L/ADr7T7RJ47KQRIrN7A4rrvgr8PNW03x8Y7jSfJU7twUHAbAxXx1TNMVmVG9TY+wqZXl2SYz2dO1zo/DX7Ofw6khW31vwVaXTxEBZpkO79DXf+F/hf4d8Mf8AIC0yO2iHSOJcAV22k+E7nIFzDg1sf2DbQhodmSAcDHeuNUmndnDUrJqyPm39pLwnd+I7N9MSLcjNtx7E/wD16/NP9sj9izwnq/xDgbQ7PfNHC4vx5YG2Td0/Kv1v+Mup+HvCelTajdSpJqHIt7EkfM5zgnByBnHPavjzx+nh7Q9YJ8QFJLy5Z5bi5flpnLZLHnqSa0wuK+q1Wunke/hMkpZrSXtXYm+DmhR/E3Q7vxlrdsbO11GVfsDTKVENu/yknd90ZPevu39in9jm0+CUcOs28kMsdzDvhniYEMCMA5HrX51/8FDNVbwR+xt4i03RblbeJ7S2trSBHA+9dR4QD6Bj9AT2r27/AIN9f+CnM3xU8MWv7GHxx8XW83iDw/b+b4R1K+cRG/08gRiKY4wWjYMVx2AzxX6bw9S/dVKiPxLi5SdanA/UVYnA3EdKc74OeakeREVAHBVxkYOajlaEttQ5zX0EXdnx8ZSqbn83v/B4Kxl/4KXeCAP+iF6X/wCnnWq/Mj4XfDTxR8WvHFj4J8J2xuL2+m2Qx54GBksTzgADrX6c/wDB4CAn/BTLwMEzz8CNM3fX+2daryD/AIIj/Ae38QeN9W+MerWuU06IWmnyEZBkdSXGe3ykfpXl5tjVl+EnXPqeHMs/tXMqeHXU+sf2Sf8AgmB8Ifgx4YtdZ8SeHLfVNbSIST6lcwAvFIAeEIOCDwc19EeG30iPRJXgiMc9s6rcBlx2ycevFeiaVpq/8I7DEybSI/nX35ry/wCJGga7HqbDSb10jll2uqkfdJwf0r8RxOOxuNxDq1Gf1BlmV4XL6CpUOh5x8eNFuvjWf+Fa+HXAtLo5vpi2P3YOGHPBypNek+C/COmeEbO08K6Fp8dvplhYmKBYugCjCDHt0qbwz4K07wpbSPDaQyyTD/XSNh+mOla8Mf2XR2SPrj+hrllUl7K1z0eV+1527npX7LPnpaXLSaYz2z6jIzTKhJ3+XHkflt/M17r4W0Lw82tT6hKxSRpcp5iY7e9edfst61Y2PwpZLRJnm/tVjcbIiQCY4vSvR7xLvVmNxa6Y554d0IxX1GBVOnh031PynO3WrY+ozukhssiRrmMgtgYcHrXDfHHx6fCFt9j8O28b3+0/fcrzn1FW40m0LQ5NT1TCLChYMD/EASK8P8SeL9R8ba7NrV3ICBJhDuzwajHV40oK24siyqtiJ3bOD8V6L4o8Va7L448W6q4uzu2QbwyRg9QD+FfKP7VmpaxN41t4rK+tlMcTiQSXIXnd29a+w/GF8biGSyLZDdq8yufCmgWd7NLc+Gba4kmfc8kmc5/A148ddWz9MwuFpU6ap9j5f/4LOfECw0rQfDHwm0rUALm+vG1S8iiYExpFuijjPoMyynnnivjHTdb1DwZDpPi/wjrd3peq2VzHPa3NjP5TxyBuGz3HqO9T/Gf4teK/jb8Tr74ieOZmuL29kzEjybhbpnOxT6ZJPPrWFrSedDDB2Uc/XNfuWAw/1SgqZ/J+a46OOxnOz90/+CK3/BXBP2ntMh/Zq/aS8U2cfj2wgY6NrFxNsXV4M4LzHHEnHABPXmv0Shilwtx50ZQruyJAcD3r+R3SfFfiXwnf23iDwxq93pd7ZOGt9Q0+ZkmjIOeCpyORX2T+y7/wXw/bz+Bd1Bpvizxfa+PdAaMxzW3iiBXudh4x533ycZ5J+teh7RR2PBcLJmJ/weBQTD/gpn4FLIQH+Bel9fQ61rQ/xruf+CSfw/8A+EA/ZW8NNcRbJdbv7i/uDjrvwIf/ACHEPzxXy1/wWt/bksv+Cmf7ZHgP4qaF8PrnQryy8AWGg6hp9zfGZZJY9U1CX91x93/SccdTX6I/s7+CrL4ffDrQfBMo2R6XpccCgDvGuB/LNfBccYm2ChS7s/WPDDAyeOni/wCQ9++3wQaZES/JTMn1rifEOoR3OvpHE+YyDuP41FN4kuNUMlhBcEbWwxB6GqDRwWN1snumd89WFflibtsf0BTpJyuupo69DAI0mtmyUYAfTPNPmeJrMRseZBlfpWfLeh5hb7s7mwPrWvHYSzm306O1VrnzFjhDHqSen51iqcvrKQ6/7iDe+h9N/svafongz4UQNNaN5mpCOd2WMkkgEV6HL4r0uH5Bf3CoT/qfK/pXJaB8PPEum+F9GsbPWp7drWz2NCuAo+YmrV1puraVK1/rMccvl9bgyZINfZ0XVp0D8dxkYYjG1Glucr+0r8WtNsfD8PhjRJJvNuSvmI8ZXjdg/pXkUT2+l6SdrYY4I4o8c+KX8e+OZ9WZy1tG7CBj/dPNZupTtcX6WSHMYBHWvnMXWlVq2P0DKMu+r4KCW5GRLqMn2lxmsrWrOT7Wdi8c10EqDTLYpGcAVkzXqTvvZ+frWDex78Yvksj8PmJlvhcZz71eaM3DZ61V02Dzx0rZt7PyecV/Qq7H8XtLm0KN5bmCwbt6VlWcJkXzlGWre1v/AI82U+tU/D9mZYQ4Bq4yigV2WfgN8Mx8Uf23/BNjNbebHpmmJqF0uM48mSZl/wDInl/nX6xziXTrRZZD82K/PD/gnxbpD+3dLv6f8K4mL/X7bHiv0B+LustpXgK41W0c+fEuVx9DX5ZxrVlUzCnSP6G8LsPRo5HVxD3MvwX43uZvE+q2L7yyXfy4BxjbXf28djqMP26UbpgeFI614j+zR4hufF1tNrl4x86ZgZsn1XmvY7eQWs4a1OYx3r4ydP2VZ3P0nCz9tRLqWi3jiSGECWKdcqO3Oa7/AOGmkT+IPipodq8e9f7SgeZSeNolXOfwrh9GvUGpu8R/1zbm+tesfs0SQ3Pxtt7S4b5fsspH1AFbYenzYynY8/MKjp5dU5j6ueymuJBIbyQD+5jivJf2pviIfB/hk+H9JuP9Mu2UGPOMoTgn8jXr2vX0Gm2stzPKYkQH94vr2B9q+LfGni3Vvir8RbzxPqczG1jmZLJWPCR8bQPwAr3swrqlRPz3hjL5YzMva1P4cGVtMSKx0pICcSFeR71HElz54uM4x3Jq5JbwG7DyHmq3ifXtM0XTXkK+YwOAmM5P4V8rZuzP1aDS0RH4qvr27it9NsXXzpsFnD9OcVv+F/B2nWdiYdat47iUY+djn+Vcj4Nj1C7tZ9f1SzjCSzf6IrPzGuBkY7cmtGXxrPaOY1l/WuqlaT1FGM5n4heH/wCH61ut978aKK/oGXxn8Wx3M7W/9Q9P8J/8ew/CiisX8RrT2keo/sF/8nzX3/ZOJf8A0vhr7t+MP/JPrr/rif5GiivzHjD/AJGdM/oLw0/5JeZ57+xv/wAgW7+o/wDQDXt9h/yDjRRXxuJ/jH6JhSx4b/5CCV67+zX/AMl6tf8Ar0m/pRRXThv96gcOcf7lUPqX4wf8k/1T/rm3/oBr430H/kEj/fFFFdOabM+U4V/3ep6suXf/AB8VynjX/j9i/wCvhaKK8dbfd+aPvKeyOln/AOQWn+7/AI1x2t/8fh/GiiuiH8Rndh/jf9dD/9k=\"\n }\n}", "options": { "raw": { "language": "json" @@ -1092,7 +1157,7 @@ "pm.test(\"Validate linkTransactionId\", function () {", " var jsonData = pm.response.json();", " pm.expect(jsonData.response.linkTransactionId).not.equals(null);", - " pm.environment.set(\"linkTransactionId\", jsonData.response.linkTransactionId);", + " pm.environment.set(\"link_transaction_id\", jsonData.response.linkTransactionId);", "});" ], "type": "text/javascript", @@ -1210,7 +1275,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"linkedTransactionId\": \"{{linkTransactionId}}\",\n \"individualId\": \"{{individual_id}}\",\n \"challengeList\" : [\n {\n \"authFactorType\" : \"WLA\",\n \"challenge\" : \"{{wla_challenge}}\",\n \"format\" : \"jwt\"\n }\n ]\n }\n}", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"linkedTransactionId\": \"{{link_transaction_id}}\",\n \"individualId\": \"{{individual_id}}\",\n \"challengeList\" : [\n {\n \"authFactorType\" : \"WLA\",\n \"challenge\" : \"{{wla_challenge}}\",\n \"format\" : \"jwt\"\n }\n ]\n }\n}", "options": { "raw": { "language": "json" @@ -1278,7 +1343,7 @@ "pm.test(\"Validate code\", function () {", " var jsonData = pm.response.json();", " pm.expect(jsonData.response.code).not.equals(null);", - " pm.environment.set(\"code\", jsonData.response.code);", + " pm.collectionVariables.set(\"code\", jsonData.response.code);", "});" ], "type": "text/javascript", @@ -1299,7 +1364,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"linkedTransactionId\": \"{{linkTransactionId}}\",\n \"acceptedClaims\": {{accepted_claims}},\n \"signature\": \"{{detachedSignature}}\"\n }\n}", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"linkedTransactionId\": \"{{link_transaction_id}}\",\n \"acceptedClaims\": {{accepted_claims}},\n \"signature\": \"{{detachedSignature}}\"\n }\n}", "options": { "raw": { "language": "json" @@ -1874,7 +1939,7 @@ "response": [] }, { - "name": "Complete the verification process & Resume Halted Transaction", + "name": "Complete Signup Redirect", "request": { "method": "POST", "header": [ @@ -1970,7 +2035,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"transactionId\": \"{{transaction_id}}\",\n \"acceptedClaims\": {{accepted_claims}},\n \"permittedAuthorizeScopes\" : {{permitted_authorized_scopes}}\n }\n}", + "raw": "{\n \"requestTime\": \"{{$isoTimestamp}}\",\n \"request\": {\n \"transactionId\": \"{{transaction_id}}\",\n \"acceptedClaims\": [\"name\",\"phone_number\"],\n \"permittedAuthorizeScopes\" : {{permitted_authorized_scopes}}\n }\n}", "options": { "raw": { "language": "json" From 298c465c6c0333e22c4fa289dde664bbe4f5afda Mon Sep 17 00:00:00 2001 From: Nandhukumar <100744358+nandhu-kumar@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:42:04 +0530 Subject: [PATCH 22/40] MOSIP-36485 | Updated POM version (#1060) * MOSIP-36485 Signed-off-by: Nandhukumar * MOSIP-36485 Signed-off-by: Nandhukumar * MOSIP-36485 Signed-off-by: Nandhukumar --------- Signed-off-by: Nandhukumar From 1a3b0b51f95febf6814009d81a39e2f564ab83a9 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:37:54 +0530 Subject: [PATCH 23/40] [ES-1989] Fixed the issue of rendering the DEFAULT_LANG when i18n language doesn't exits in the esignet. (#1061) Signed-off-by: GurukiranP --- oidc-ui/src/App.js | 8 +++- oidc-ui/src/components/NavHeader.js | 61 +++++++++++++++-------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/oidc-ui/src/App.js b/oidc-ui/src/App.js index ae6ffa69c..4e9181052 100644 --- a/oidc-ui/src/App.js +++ b/oidc-ui/src/App.js @@ -86,7 +86,8 @@ function App() { for (let idx in languages) { if (supportedLanguages[languages[idx]]) { i18n.changeLanguage(languages[idx]); - return; + } else { + i18n.changeLanguage(window._env_.DEFAULT_LANG); } } @@ -95,9 +96,12 @@ function App() { for (let idx in languages) { if (langCodeMapping[languages[idx]]) { i18n.changeLanguage(langCodeMapping[languages[idx]]); - return; + } else { + i18n.changeLanguage(window._env_.DEFAULT_LANG); } } + } else { + return i18n.changeLanguage(window._env_.DEFAULT_LANG); } //2. Check for cookie diff --git a/oidc-ui/src/components/NavHeader.js b/oidc-ui/src/components/NavHeader.js index 48ad9740f..3a7aed771 100644 --- a/oidc-ui/src/components/NavHeader.js +++ b/oidc-ui/src/components/NavHeader.js @@ -18,6 +18,14 @@ export default function NavHeader({ langOptions, i18nKeyPrefix = "header" }) { const authorizeQueryParam = "authorize_query_param"; const ui_locales = "ui_locales"; + // Decode the authorize query param + const decodedBase64 = Buffer.from( + authServices.getAuthorizeQueryParam(), + "base64" + ).toString(); + + var urlSearchParams = new URLSearchParams(decodedBase64); + const changeLanguageHandler = (e) => { i18n.changeLanguage(e.value); }; @@ -49,43 +57,17 @@ export default function NavHeader({ langOptions, i18nKeyPrefix = "header" }) { }; useEffect(() => { - if (!langOptions || langOptions.length === 0) { - return; - } - - let lang = langOptions.find((option) => { - return option.value === i18n.language; - }); - - if (lang == null) { - const defaultLanguageCode = window._env_.DEFAULT_LANG; - - // Find the language option that matches the extracted language code - const defaultLang = langOptions.find( - (option) => option.value === defaultLanguageCode - ); - setSelectedLang(defaultLang); - } else { - setSelectedLang(lang); - } - //Gets fired when changeLanguage got called. i18n.on("languageChanged", function (lng) { let language = langOptions.find((option) => { return option.value === lng; }); - setSelectedLang(language); + if (language) { + setSelectedLang(language); + } // Setting up the current i18n language in the URL on every language change. - // Decode the authorize query param - const decodedBase64 = Buffer.from( - authServices.getAuthorizeQueryParam(), - "base64" - ).toString(); - - var urlSearchParams = new URLSearchParams(decodedBase64); - // Convert the decoded string to JSON var jsonObject = {}; urlSearchParams.forEach(function (value, key) { @@ -113,6 +95,27 @@ export default function NavHeader({ langOptions, i18nKeyPrefix = "header" }) { // Insert the new authorizeQueryParam to the local storage localStorage.setItem(authorizeQueryParam, encodedBase64); }); + + if (!langOptions || langOptions.length === 0) { + return; + } + + let lang = langOptions.find((option) => { + const currentUILocale = urlSearchParams.get("ui_locales"); + return option.value === i18n.language; + }); + + if (!lang) { + const defaultLanguageCode = window._env_.DEFAULT_LANG; + + // Find the language option that matches the extracted language code + const defaultLang = langOptions.find( + (option) => option.value === defaultLanguageCode + ); + setSelectedLang(defaultLang); + } else { + setSelectedLang(lang); + } }, [langOptions]); var dropdownItemClass = From 05eb3b7a184d94e931d08bbdad6d5ee0df05348f Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:10:03 +0530 Subject: [PATCH 24/40] [ES-1665] theme changes. (#1062) Signed-off-by: GurukiranP --- oidc-ui/public/theme/variables.css | 52 ++++++++++++++++++++++++++++-- oidc-ui/src/App.css | 41 +++++++++++++++-------- oidc-ui/src/app/AppRouter.js | 8 ++--- 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/oidc-ui/public/theme/variables.css b/oidc-ui/public/theme/variables.css index 5338abd8b..919d2fea0 100644 --- a/oidc-ui/public/theme/variables.css +++ b/oidc-ui/public/theme/variables.css @@ -60,6 +60,22 @@ --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); --bottom-left-bg-logo-display: none; --top-right-bg-logo-display: none; + --top-left-bg-width: 14rem; + --top-left-bg-height: 14rem; + --top-left-bg-position-top: 10px; + --top-left-bg-position-left: 10px; + --bottom-left-bg-width: 14rem; + --bottom-left-bg-height: 14rem; + --bottom-left-bg-position-bottom: 10px; + --bottom-left-bg-position-left: 10px; + --top-right-bg-width: 14rem; + --top-right-bg-height: 14rem; + --top-right-bg-position-top: 10px; + --top-right-bg-position-right: 10px; + --bottom-right-bg-width: 17rem; + --bottom-right-bg-height: 17rem; + --bottom-right-bg-position-bottom: 10px; + --bottom-right-bg-position-right: 10px; --login-client-logo-image-height: 3.25rem; --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21CB"; @@ -183,6 +199,22 @@ --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); --bottom-left-bg-logo-display: none; --top-right-bg-logo-display: none; + --top-left-bg-width: 14rem; + --top-left-bg-height: 14rem; + --top-left-bg-position-top: 10px; + --top-left-bg-position-left: 10px; + --bottom-left-bg-width: 14rem; + --bottom-left-bg-height: 14rem; + --bottom-left-bg-position-bottom: 10px; + --bottom-left-bg-position-left: 10px; + --top-right-bg-width: 14rem; + --top-right-bg-height: 14rem; + --top-right-bg-position-top: 10px; + --top-right-bg-position-right: 10px; + --bottom-right-bg-width: 17rem; + --bottom-right-bg-height: 17rem; + --bottom-right-bg-position-bottom: 10px; + --bottom-right-bg-position-right: 10px; --login-client-logo-image-height: 3.25rem; --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21C4"; @@ -305,12 +337,28 @@ --multipurpose-login-card-background-color: #ffffff; --top-left-bg-logo-url: url("/images/top_left_bg_logo.svg"); --top-left-bg-logo-display: block; - --bottom-left-bg-logo-url: url("/images/bg_bottom_left.png"); + --bottom-left-bg-logo-url: url("/images/top_left_bg_logo.svg"); --bottom-left-bg-logo-display: none; --top-right-bg-logo-url: url("/images/top_left_bg_logo.svg"); --top-right-bg-logo-display: none; --bottom-right-bg-logo-url: url("/images/bottom_right_bg_logo.svg"); --bottom-right-bg-logo-display: block; + --top-left-bg-width: 14rem; + --top-left-bg-height: 14rem; + --top-left-bg-position-top: 10px; + --top-left-bg-position-left: 10px; + --bottom-left-bg-width: 14rem; + --bottom-left-bg-height: 14rem; + --bottom-left-bg-position-bottom: 10px; + --bottom-left-bg-position-left: 10px; + --top-right-bg-width: 14rem; + --top-right-bg-height: 14rem; + --top-right-bg-position-top: 10px; + --top-right-bg-position-right: 10px; + --bottom-right-bg-width: 17rem; + --bottom-right-bg-height: 17rem; + --bottom-right-bg-position-bottom: 10px; + --bottom-right-bg-position-right: 10px; --login-client-logo-image-height: 3.25rem; --login-client-logo-image-width: 3.25rem; --login-alternate-arrow: "\21C4"; @@ -338,7 +386,7 @@ --consent-details-header-font: normal normal 600 24px/29px Roboto; --consent-details-header-color: var(--primary-color); --consent-details-subheader-font: normal normal bold 16px/21px Roboto; - --consent-details-subheader-color: var(--primary-color); + --consent-details-subheader-color: #2c2c2c; --consent-details-infoIcon-fill: var(--primary-color); --consent-details-infoIcon-stroke: var(--primary-color); --consent-details-available-font: normal normal medium 14px/17px Helvetica Neue; diff --git a/oidc-ui/src/App.css b/oidc-ui/src/App.css index bf5fca9f5..8d87ae090 100644 --- a/oidc-ui/src/App.css +++ b/oidc-ui/src/App.css @@ -233,36 +233,51 @@ body { .section-background .top_right_bg_logo, .section-background .bottom_right_bg_logo, .section-background .bottom_left_bg_logo { - width: var(--side-section-bg-with); position: absolute; } .section-background .top_left_bg_logo { display: var(--top-left-bg-logo-display); - content: var(--top-left-bg-logo-url); - top: 10px; - left: 10px; + -webkit-mask-image: var(--top-left-bg-logo-url); + mask-image: var(--top-left-bg-logo-url); + background-color: var(--primary-color); + width: var(--top-left-bg-width); + height: var(--top-left-bg-height); + top: var(--top-left-bg-position-top); + left: var(--top-left-bg-position-left); } .section-background .bottom_left_bg_logo { display: var(--bottom-left-bg-logo-display); - content: var(--bottom-left-bg-logo-url); - left: 10px; - bottom: 10px; + -webkit-mask-image: var(--bottom-left-bg-logo-url); + mask-image: var(--bottom-left-bg-logo-url); + background-color: var(--primary-color); + width: var(--bottom-left-bg-width); + height: var(--bottom-left-bg-height); + bottom: var(--bottom-left-bg-position-bottom); + left: var(--bottom-left-bg-position-left); } .section-background .top_right_bg_logo { display: var(--top-right-bg-logo-display); - content: var(--top-right-bg-logo-url); - top: 10px; - right: 10px; + -webkit-mask-image: var(--top-right-bg-logo-url); + mask-image: var(--top-right-bg-logo-url); + background-color: var(--primary-color); + width: var(--top-right-bg-width); + height: var(--top-right-bg-height); + top: var(--top-right-bg-position-top); + right: var(--top-right-bg-position-right); } .section-background .bottom_right_bg_logo { display: var(--bottom-right-bg-logo-display); - content: var(--bottom-right-bg-logo-url); - right: 10px; - bottom: 10px; + -webkit-mask-image: var(--bottom-right-bg-logo-url); + mask-image: var(--bottom-right-bg-logo-url); + background-color: var(--primary-color); + width: var(--bottom-right-bg-width); + height: var(--bottom-right-bg-height); + bottom: var(--bottom-right-bg-position-bottom); + right: var(--bottom-right-bg-position-right); } .back-button-color { diff --git a/oidc-ui/src/app/AppRouter.js b/oidc-ui/src/app/AppRouter.js index c3fc433a8..49b915fa5 100644 --- a/oidc-ui/src/app/AppRouter.js +++ b/oidc-ui/src/app/AppRouter.js @@ -57,19 +57,19 @@ export const AppRouter = () => { ) : ( <> top left background bottom left background top right background bottom right background From 293d51d383b98f7a3e32257dd9250966285a2cc6 Mon Sep 17 00:00:00 2001 From: pvsaidurga <132046494+pvsaidurga@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:59:18 +0530 Subject: [PATCH 25/40] [Es-1985] Added validation for lower case (#1047) Signed-off-by: pvsaidurga <132046494+pvsaidurga@users.noreply.github.com> --- .../AuthChallengeFactorFormatValidator.java | 2 +- .../mosip/esignet/api/validator/ValidatorTest.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/esignet-integration-api/src/main/java/io/mosip/esignet/api/validator/AuthChallengeFactorFormatValidator.java b/esignet-integration-api/src/main/java/io/mosip/esignet/api/validator/AuthChallengeFactorFormatValidator.java index b89fc299c..0dac78515 100644 --- a/esignet-integration-api/src/main/java/io/mosip/esignet/api/validator/AuthChallengeFactorFormatValidator.java +++ b/esignet-integration-api/src/main/java/io/mosip/esignet/api/validator/AuthChallengeFactorFormatValidator.java @@ -47,7 +47,7 @@ public boolean isValid(AuthChallenge authChallenge, ConstraintValidatorContext c String authFactor = authChallenge.getAuthFactorType(); String format = environment.getProperty(String.format(FORMAT_KEY_PREFIX, authFactor), String.class); - if( !StringUtils.hasText(authFactor) || !StringUtils.hasText(format)) { + if( !StringUtils.hasText(authFactor) || !StringUtils.hasText(format) || !authChallenge.getAuthFactorType().equals(authFactor.toUpperCase()) ) { context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(ErrorConstants.INVALID_AUTH_FACTOR_TYPE).addConstraintViolation(); return false; diff --git a/esignet-integration-api/src/test/java/io/mosip/esignet/api/validator/ValidatorTest.java b/esignet-integration-api/src/test/java/io/mosip/esignet/api/validator/ValidatorTest.java index 42289b657..df28cea33 100644 --- a/esignet-integration-api/src/test/java/io/mosip/esignet/api/validator/ValidatorTest.java +++ b/esignet-integration-api/src/test/java/io/mosip/esignet/api/validator/ValidatorTest.java @@ -97,6 +97,19 @@ public void authChallengeFactorFormatValidator_invalidAuthFactorType_thenFail() assertFalse(isValid); } + @Test + public void authChallengeFactorFormatValidator_lowerCaseAuthFactorType_thenFail() { + AuthChallenge authChallenge = new AuthChallenge(); + authChallenge.setAuthFactorType("otp"); + authChallenge.setFormat("alpha-numeric"); + authChallenge.setChallenge("111111"); + Mockito.when(constraintValidatorContext.buildConstraintViolationWithTemplate(anyString())) + .thenReturn(mock(ConstraintValidatorContext.ConstraintViolationBuilder.class)); + boolean isValid = authChallengeFactorFormatValidator.isValid(authChallenge, constraintValidatorContext); + Mockito.verify(constraintValidatorContext).buildConstraintViolationWithTemplate(ErrorConstants.INVALID_AUTH_FACTOR_TYPE); + assertFalse(isValid); + } + @Test public void authChallengeFactorFormatValidator_invalidChallengeLength_theFail() { AuthChallenge authChallenge = new AuthChallenge(); From 9eb372bdc5d6c8e20cb63a19a9669f9238a7f6f6 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:39:08 +0530 Subject: [PATCH 26/40] [ES-1989] [ES-1665] i18n and theme changes. (#1065) Signed-off-by: GurukiranP --- oidc-ui/src/App.js | 2 -- oidc-ui/src/common/ModalPopup.js | 22 +++++++++++++++------- oidc-ui/src/components/ClaimDetails.js | 22 +++++++++++++++------- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/oidc-ui/src/App.js b/oidc-ui/src/App.js index 4e9181052..85e0ea635 100644 --- a/oidc-ui/src/App.js +++ b/oidc-ui/src/App.js @@ -100,8 +100,6 @@ function App() { i18n.changeLanguage(window._env_.DEFAULT_LANG); } } - } else { - return i18n.changeLanguage(window._env_.DEFAULT_LANG); } //2. Check for cookie diff --git a/oidc-ui/src/common/ModalPopup.js b/oidc-ui/src/common/ModalPopup.js index d40cc388b..f7bf5320b 100644 --- a/oidc-ui/src/common/ModalPopup.js +++ b/oidc-ui/src/common/ModalPopup.js @@ -20,13 +20,21 @@ const ModalPopup = ({ return ( <> top left background - bottom left background + className="top_left_bg_logo hidden md:block" + alt="top left background" + /> + bottom left background + top right background + bottom right background

top left background - bottom left background + className="top_left_bg_logo hidden md:block" + alt="top left background" + /> + bottom left background + top right background + bottom right background
Date: Fri, 20 Dec 2024 19:23:14 +0530 Subject: [PATCH 27/40] OAuth Details API is initiating the transaction despite a mismatch between the aud claim in the provided ID token and the clientId (#1049) Signed-off-by: Md-Humair-KK --- .../services/AuthorizationHelperService.java | 2 +- .../services/AuthorizationServiceTest.java | 39 ++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java index a4095bc2e..bcae28040 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java @@ -399,7 +399,7 @@ protected String validateAndGetSubject(String clientId, String idTokenHint) { String payload = new String(Base64.getDecoder().decode(jwtParts[1])); JSONObject payloadJson = new JSONObject(payload); String audience = payloadJson.getString(TokenService.AUD); - if(!signupIDTokenAudience.equals(audience) && signupIDTokenAudience.equals(clientId)) + if(!signupIDTokenAudience.equals(audience) || !signupIDTokenAudience.equals(clientId)) throw new EsignetException(ErrorConstants.INVALID_ID_TOKEN_HINT); return payloadJson.getString(TokenService.SUB); } diff --git a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java index c982a6ac9..268ec3e9a 100644 --- a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java +++ b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java @@ -855,13 +855,13 @@ public void getOauthDetailsV3_withValidIDTokenHint_thenPass() { ClientDetail clientDetail = new ClientDetail(); clientDetail.setName(new HashMap<>()); clientDetail.getName().put(Constants.NONE_LANG_KEY, "clientName"); - clientDetail.setId("34567"); + clientDetail.setId("mosip-signup-oauth-client"); clientDetail.setRedirectUris(Arrays.asList("https://localshot:3044/logo.png","http://localhost:8088/v1/idp","/v1/idp")); clientDetail.setClaims(Arrays.asList("email","given_name")); clientDetail.setAcrValues(Arrays.asList("mosip:idp:acr:generated-code", "mosip:idp:acr:wallet")); OAuthDetailRequestV3 oauthDetailRequest = new OAuthDetailRequestV3(); - oauthDetailRequest.setClientId("34567"); + oauthDetailRequest.setClientId("mosip-signup-oauth-client"); oauthDetailRequest.setRedirectUri("http://localhost:8088/v1/idp"); oauthDetailRequest.setNonce("test-nonce"); ClaimsV2 claims = new ClaimsV2(); @@ -885,10 +885,45 @@ public void getOauthDetailsV3_withValidIDTokenHint_thenPass() { Assert.assertNotNull(oauthDetailResponseV2); } + @Test + public void getOauthDetailsV3_withValidIDTokenHintClientIdAndAUDMismatch_thenFail() { + ClientDetail clientDetail = new ClientDetail(); + clientDetail.setName(new HashMap<>()); + clientDetail.getName().put(Constants.NONE_LANG_KEY, "clientName"); + clientDetail.setId("34567"); + clientDetail.setRedirectUris(Arrays.asList("https://localshot:3044/logo.png","http://localhost:8088/v1/idp","/v1/idp")); + clientDetail.setClaims(Arrays.asList("email","given_name")); + clientDetail.setAcrValues(Arrays.asList("mosip:idp:acr:generated-code", "mosip:idp:acr:wallet")); + + OAuthDetailRequestV3 oauthDetailRequest = new OAuthDetailRequestV3(); + oauthDetailRequest.setClientId("34567"); + oauthDetailRequest.setRedirectUri("http://localhost:8088/v1/idp"); + oauthDetailRequest.setNonce("test-nonce"); + ClaimsV2 claims = new ClaimsV2(); + claims.setId_token(new HashMap<>()); + ClaimDetail claimDetail = new ClaimDetail(); + claimDetail.setValues(new String[]{"mosip:idp:acr:wallet", "mosip:idp:acr:webauthn"}); + claims.getId_token().put("acr", claimDetail); + oauthDetailRequest.setClaims(claims); + oauthDetailRequest.setAcrValues("mosip:idp:acr:biometrics mosip:idp:acr:generated-code"); + oauthDetailRequest.setIdTokenHint("eyJraWQiOiJtbG02RVNRaFB5dVVsWmY0dnBZbGJTVWlSMXBXcG5jdW9kamtnRjNaNU5nIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJxWS0tNVk0VG9Ga1dUb1hKclJGbVBXUEhEWkxrY2lNTDQtX2cxTDJBNXhJIiwiYXVkIjoibW9zaXAtc2lnbnVwLW9hdXRoLWNsaWVudCIsImFjciI6Im1vc2lwOmlkcDphY3I6Z2VuZXJhdGVkLWNvZGUiLCJhdXRoX3RpbWUiOjE3MjUyNjk4ODUsImlzcyI6Imh0dHBzOlwvXC9lc2lnbmV0bDIuY2FtZGdjLXFhLm1vc2lwLm5ldFwvdjFcL2VzaWduZXQiLCJleHAiOjE3MjUyNzAwNzMsImlhdCI6MTcyNTI2OTg5Mywibm9uY2UiOiI5NzNlaWVsanpuZyJ9.VMMn92CFzGkVyx8Jwrq03KhuXOXj3wRlUoxZQQBN7MxlfIxGSX_yE7iw3JWxohzQuHticndtQX2LELcGTPhclzRop3skHCeo6ZPGJklCiRA3F5SyfCYLvDprgE_-pQhLWeECqRtW_8jFFgZSORMoxy8eBj5Vvc8q2zcoDjE-JiLZvqE9UWDRpAKzumJcD3iJvBwE-9jkzQtWZbp-tZrpPrm-KCZU6-Q3qhWU23E9DSMg_6byq4iH51TFwO0nHW1kaxhsqHvCsTX7YTvmfWXUwPVRLNZh5Uszt8EIsgpKIUDkRImqmCUbP1LwoFG55MsW67QzHNTFuR6H-4LidSKnnA"); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(new Cookie("qY--5Y4ToFkWToXJrRFmPWPHDZLkciML4-_g1L2A5xI", "5Y4ToFkWToXJrRFmPWPHDZLkciML4"+SERVER_NONCE_SEPARATOR+"test-state")); + + try { + OAuthDetailResponseV2 oauthDetailResponseV2 = authorizationServiceImpl.getOauthDetailsV3(oauthDetailRequest, request); + Assert.assertNotNull(oauthDetailResponseV2); + } catch (EsignetException e) { + Assert.assertEquals(ErrorConstants.INVALID_ID_TOKEN_HINT, e.getErrorCode()); + } + } + @Test public void getOauthDetailsV3_withValidIDTokenHintNoCookie_thenFail() throws Exception { OAuthDetailRequestV3 oauthDetailRequest = new OAuthDetailRequestV3(); oauthDetailRequest.setIdTokenHint("eyJraWQiOiJtbG02RVNRaFB5dVVsWmY0dnBZbGJTVWlSMXBXcG5jdW9kamtnRjNaNU5nIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJxWS0tNVk0VG9Ga1dUb1hKclJGbVBXUEhEWkxrY2lNTDQtX2cxTDJBNXhJIiwiYXVkIjoibW9zaXAtc2lnbnVwLW9hdXRoLWNsaWVudCIsImFjciI6Im1vc2lwOmlkcDphY3I6Z2VuZXJhdGVkLWNvZGUiLCJhdXRoX3RpbWUiOjE3MjUyNjk4ODUsImlzcyI6Imh0dHBzOlwvXC9lc2lnbmV0bDIuY2FtZGdjLXFhLm1vc2lwLm5ldFwvdjFcL2VzaWduZXQiLCJleHAiOjE3MjUyNzAwNzMsImlhdCI6MTcyNTI2OTg5Mywibm9uY2UiOiI5NzNlaWVsanpuZyJ9.VMMn92CFzGkVyx8Jwrq03KhuXOXj3wRlUoxZQQBN7MxlfIxGSX_yE7iw3JWxohzQuHticndtQX2LELcGTPhclzRop3skHCeo6ZPGJklCiRA3F5SyfCYLvDprgE_-pQhLWeECqRtW_8jFFgZSORMoxy8eBj5Vvc8q2zcoDjE-JiLZvqE9UWDRpAKzumJcD3iJvBwE-9jkzQtWZbp-tZrpPrm-KCZU6-Q3qhWU23E9DSMg_6byq4iH51TFwO0nHW1kaxhsqHvCsTX7YTvmfWXUwPVRLNZh5Uszt8EIsgpKIUDkRImqmCUbP1LwoFG55MsW67QzHNTFuR6H-4LidSKnnA"); + oauthDetailRequest.setClientId("mosip-signup-oauth-client"); MockHttpServletRequest request = new MockHttpServletRequest(); try { authorizationServiceImpl.getOauthDetailsV3(oauthDetailRequest, request); From c0c61542bb86459f77992b43e1e3bb6effd5ad4a Mon Sep 17 00:00:00 2001 From: pvsaidurga <132046494+pvsaidurga@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:23:36 +0530 Subject: [PATCH 28/40] [ES-1886] Updated the stoplight for client mgmt endpoints (#1064) Signed-off-by: pvsaidurga <132046494+pvsaidurga@users.noreply.github.com> --- docs/esignet-openapi.yaml | 764 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 728 insertions(+), 36 deletions(-) diff --git a/docs/esignet-openapi.yaml b/docs/esignet-openapi.yaml index d2e0a5f9f..effcd069b 100644 --- a/docs/esignet-openapi.yaml +++ b/docs/esignet-openapi.yaml @@ -488,17 +488,519 @@ paths: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-internal: false x-stoplight: - id: llrtvbxzzypg9 + id: fzn9175pquxhd + deprecated: true + /client-mgmt/client: + post: + tags: + - Management + summary: Create OAuth/OIDC Client Endpoint + description: |- + API to add new OAuth or open ID connect (OIDC) clients. This API should be used to create client in esignet by the partner management modules in the integrated ID system. + + Each relying party can associate with one or more client ids. + + On create, client status will be by default set to "**ACTIVE**". + operationId: post-client-mgmt-client + requestBody: + description: '' + content: + application/json: + schema: + type: object + description: OIDC client details + required: + - requestTime + - request + properties: + requestTime: + type: string + description: Current date and time when the request is sent + format: date-time + pattern: '' + request: + type: object + required: + - clientId + - clientName + - clientNameLangMap + - relyingPartyId + - logoUri + - authContextRefs + - publicKey + - userClaims + - grantTypes + - clientAuthMethods + - additionalConfig + properties: + clientId: + type: string + description: 'Unique client id (Case-Sensitive). If duplicates found, request will be rejected.' + minLength: 1 + maxLength: 50 + examples: + - 785b806d0e594657b05aabdb30fff8a4 + clientName: + type: string + description: Name of OAuth/OIDC client. + minLength: 1 + maxLength: 256 + examples: + - ABC Health Care + clientNameLangMap: + type: object + description: |- + Client name in different languages. The language code needs to be passed as key and + client name in the desired language needs to be passed as value + relyingPartyId: + type: string + description: |- + Relying Party ID of the client. This will be passed on to authentications servers when KYC is fetched. + + Note: Use the client Id as relyingPartyId if there is no separate concept of relying party in the ID authentication system. + minLength: 1 + maxLength: 50 + examples: + - bharathi-inc + logoUri: + type: string + description: Relying party logo URI which will used to display logo in the login and consent pages. + format: uri + minLength: 1 + maxLength: 1024 + redirectUris: + type: array + description: |- + Valid list of callback Uris of the relying party. + When the authorize API is called, any one Uri from this list should be sent as redirect_uri. authorization_code will be redirected to this Uri on successful authentication. + items: + type: string + authContextRefs: + type: array + description: The Authentication Context Class Reference is case-sensitive string specifying a list of Authentication Context Class values that identifies the Authentication Context Class. Values that the authentication performed satisfied implying a Level Of Assurance. + minItems: 1 + items: + type: string + enum: + - 'mosip:idp:acr:static-code' + - 'mosip:idp:acr:generated-code' + - 'mosip:idp:acr:linked-wallet' + - 'mosip:idp:acr:biometrics' + - 'mosip:idp:acr:knowledge' + - 'mosip:idp:acr:id-token' + - 'mosip:idp:acr:password' + publicKey: + type: object + description: |- + OAuth/OIDC client's public key used to verify the client's private_key_jwt when token endpoint is invoked. + This field will not be allowed to udpate later, if the private key is compromised, then new OAuth/OIDC client to be created. + Format : Json Web Key (JWK). + userClaims: + type: array + description: 'Allowed user info claims, that can be requested by OIDC client in the authorize API' + minItems: 1 + items: + type: string + enum: + - name + - given_name + - middle_name + - preferred_username + - nickname + - gender + - birthdate + - email + - phone_number + - picture + - address + grantTypes: + type: array + description: Form of Authorization Grant presented to token endpoint + minItems: 1 + uniqueItems: true + items: + const: authorization_code + clientAuthMethods: + type: array + description: Auth method supported for token endpoint. At present only "private_key_jwt" is supported. + minItems: 1 + items: + const: private_key_jwt + additionalConfig: + type: object + required: + - userinfo_response_type + - purpose + - signup_banner_required + - forgot_pwd_link_required + - consent_expire_in_days + description: 'This parameter allow us to configure the required values based on their specific authentication and integration needs, ensuring efficient implementation of eSignet for ID verification/authentication.' + properties: + userinfo_response_type: + x-stoplight: + id: 6gpkj6mqys6va + enum: + - JWS + - JWE + description: The response type for the user info endpoint should be configurable to allow the Relying Party to choose between only signed tokens or signed tokens with encryption. + purpose: + type: object + x-stoplight: + id: sfa9a8ygak752 + description: 'Relying Parties should indicate the purpose of using eSignet (e.g., for login, verification) during registration, allowing for specific customisation.' + required: + - type + - title + - subTitle + properties: + type: + x-stoplight: + id: 9hqgv3ivrenfp + enum: + - Verify + - Link + - Login + title: + type: string + x-stoplight: + id: 7brcj2q2o7yyi + subTitle: + type: string + x-stoplight: + id: qagoh90f7hecz + signup_banner_required: + type: boolean + x-stoplight: + id: 4i1vlnmxu16e4 + description: Relying Parties should be able to specify whether they require eSignet sign-up. If no signup service is required UI should not have “Sign up with Unified login“ option. + forgot_pwd_link_required: + type: boolean + x-stoplight: + id: s1pb151yorc5y + description: Relying Parties should be able to specify whether they require eSignet forgot password feature. If it is not required UI should not have “Forgot password“ option + consent_expire_in_days: + type: number + x-stoplight: + id: peyhsfc3w4ajc + minimum: 0 + description: The number of days after which a user's given consent will expire. + examples: + example-1: + value: + requestTime: '2011-10-05T14:48:00.000Z' + request: + clientId: WMX5pO6dYdCFR3iaVWGclVPNxTNSADDv + clientName: Fastlane e-Sim Service + clientNameLangMap: + fra: Service e-Sim de Fastlane + ara: خدمة فاست لين e-SIM + relyingPartyId: Fastlane + logoUri: 'https://fastlane.com/fastlane-esim.png' + redirectUris: + - 'https://fastlane.com/homepage' + - 'io.mosip.residentapp://oauth' + publicKey: + kty: RSA + e: AQAB + use: sig + alg: RS256 + 'n': g7KPXZdZ18H2JoW9FhYz8WrSbLeKA5mO8ROW5YQVyzYDfjbRA9sy0FwpF7pa7mBmU1_G0RvD0xbEhSaFtCL5hyNVVZCfgVqNl41C7-F2yUWhfVQPhT5YnT3eH3gV9ZczhP1trNjIzGuH-8D7EDJcoxuwdGaaY-wTmEtHykHRyab08qr62hfwLuSjHAGN6VgV-Na81XIdXmR7Dwnd1U4MxWJxzRvnVlHFCBaZIG6jNJ21vbzM-DBMq1d8tvtrGQx4w3niK_sctUZ5NP1BLkQhYSEGLr-e_mbmHFCnGtuKfnfIm-PVD-6ihfEwX3j_YQT3LhphBZj7AdXg6iyyQn9EJQ + authContextRefs: + - 'mosip:idp:acr:generated-code' + - 'mosip:idp:acr:biometrics' + - 'mosip:idp:acr:linked-wallet' + userClaims: + - name + - email + - phone_number + - address + grantTypes: + - authorization_code + clientAuthMethods: + - private_key_jwt + additionalConfig: + userinfo_response_type: JWS + purpose: + title: Verify your info with e-Signet + type: Verify + subTitle: Please verify the required details + signup_banner_required: true + forgot_pwd_link_required: true + consent_expire_in_days: 30 + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + responseTime: + type: string + description: Date and time when the response is generated + response: + type: object + properties: + clientId: + type: string + description: Client id as provided in the request. + status: + type: string + enum: + - ACTIVE + - INACTIVE + errors: + type: array + items: + type: object + additionalProperties: false + properties: + errorCode: + type: string + enum: + - duplicate_client_id + - invalid_public_key + - invalid_input + - invalid_client_id + - invalid_client_name + - invalid_rp_id + - invalid_claim + - invalid_acr + - invalid_uri + - invalid_redirect_uri + - invalid_grant_type + - invalid_client_auth + - invalid_language_code + - invalid_client_name_value + - invalid_additional_config + errorMessage: + type: string + examples: + Example 1: + value: + responseTime: '2023-09-22T08:01:13.000Z' + response: + clientId: WMX5pO6dYdCFR3iaVWGclVPNxTNSADDv + status: ACTIVE + errors: [] + '401': + description: Unauthorized + security: + - Authorization-add_oidc_client: [] + servers: + - url: 'https://esignet.collab.mosip.net/v1/esignet' + x-internal: false + x-stoplight: + id: z8gcu89m21n91 '/client-mgmt/oidc-client/{client_id}': put: tags: - Management - summary: Update OIDC Client Endpoint + summary: Update OIDC Client Endpoint + description: |- + API to update existing Open ID Connect (OIDC) client, it can be invoked by other modules which manages the relying parties / partners when there any updates on the fields accepted in this API. + + **Authentication and authorization** is based on a valid JWT issued by a trusted IAM system including "**update_oidc_client**" scope. + operationId: put-oidc-client-client_id + parameters: + - name: client_id + in: path + description: Client Identifier + required: true + schema: + type: string + examples: + - WMX5pO6dYdCFR3iaVWGclVPNxTNSADDv + requestBody: + description: '' + content: + application/json: + schema: + type: object + properties: + requestTime: + type: string + description: Current date and time when the request is sent + pattern: '' + request: + type: object + properties: + clientName: + type: string + description: Name of the OIDC client. + minLength: 1 + maxLength: 256 + examples: + - ABC Health Care + status: + type: string + enum: + - active + - inactive + description: Status of OIDC client. + logoUri: + type: string + description: Relying party logo URI which will used to display logo in OIDC login and consent pages. + format: uri + minLength: 1 + maxLength: 1024 + redirectUris: + type: array + description: 'Valid list of callback Uris of the relying party. When OIDC authorize API is called, any one Uri from this list should be sent as redirect_uri. authorization_code will be redirected to this Uri on successful authentication.' + minItems: 1 + uniqueItems: true + items: + type: string + format: uri + userClaims: + type: array + description: 'Allowed user info claims, that can be requested by OIDC client in the authorize API' + minItems: 1 + items: + type: string + enum: + - name + - given_name + - middle_name + - preferred_username + - nickname + - gender + - birthdate + - email + - phone_number + - picture + - address + authContextRefs: + type: array + description: The Authentication Context Class Reference is case-sensitive string specifying a list of Authentication Context Class values that identifies the Authentication Context Class. Values that the authentication performed satisfied implying a Level Of Assurance. + minItems: 1 + uniqueItems: true + items: + type: string + enum: + - 'mosip:idp:acr:static-code' + - 'mosip:idp:acr:generated-code' + - 'mosip:idp:acr:linked-wallet' + - 'mosip:idp:acr:biometrics' + grantTypes: + type: array + description: Form of Authorization Grant presented to token endpoint. + minItems: 1 + items: + const: authorization_code + clientAuthMethods: + type: array + description: Auth method supported for token endpoint. At present only "private_key_jwt" is supported. + minItems: 1 + items: + const: private_key_jwt + required: + - clientName + - status + - logoUri + - redirectUris + - userClaims + - authContextRefs + - grantTypes + - clientAuthMethods + required: + - requestTime + - request + examples: + example-1: + value: + requestTime: '2011-10-05T14:48:00.000Z' + request: + clientName: Fastlane e-Sim Service + status: active + relyingPartyId: Fastlane + logoUri: 'https://fastline.com/logo.png' + redirectUris: + - 'https://fastlane.com/homepage' + - 'https://fastlane-dev.com/*' + - 'fastlaneapp://oauth/*' + authContextRefs: + - 'mosip:idp:acr:biometrics' + - 'mosip:idp:acr:generated-code' + - 'mosip:idp:acr:linked-wallet' + userClaims: + - name + - email + - phone_number + - address + grantTypes: + - authorization_code + clientAuthMethods: + - private_key_jwt + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + responseTime: + type: string + description: Date and time when the response is generated + response: + type: object + properties: + clientId: + type: string + description: OIDC client identifier. + status: + type: string + enum: + - ACTIVE + - INACTIVE + required: + - clientId + errors: + type: array + description: 'List of Errors in case of request validation / processing failure in Idp server. When request processing is fully successful, this array will be empty.' + items: + type: object + properties: + errorCode: + type: string + enum: + - invalid_client_id + - invalid_client_name + - invalid_claim + - invalid_acr + - invalid_uri + - invalid_redirect_uri + - invalid_grant_type + - invalid_client_auth + errorMessage: + type: string + examples: + Example 1: + value: + value: + responseTime: '2023-09-22T08:01:13.000Z' + response: + clientId: WMX5pO6dYdCFR3iaVWGclVPNxTNSADDv + status: ACTIVE + errors: [] + deprecated: true + security: + - Authorization-update_oidc_client: [] + servers: + - url: 'https://esignet.collab.mosip.net/v1/esignet' + x-stoplight: + id: twjsfa0yqpyej + '/client-mgmt/oauth-client/{client_id}': + put: + tags: + - Management + summary: Update OAuth/OIDC Client Endpoint description: |- - API to update existing Open ID Connect (OIDC) client, it can be invoked by other modules which manages the relying parties / partners when there any updates on the fields accepted in this API. + API to update existing OAuth/Open ID Connect (OIDC) client, it can be invoked by other modules which manages the relying parties / partners when there any updates on the fields accepted in this API. **Authentication and authorization** is based on a valid JWT issued by a trusted IAM system including "**update_oidc_client**" scope. - operationId: put-oidc-client-client_id + operationId: put-oauth-client-client_id parameters: - name: client_id in: path @@ -524,26 +1026,31 @@ paths: properties: clientName: type: string - description: Name of the OIDC client. + description: Name of the OAuth/OIDC client. minLength: 1 maxLength: 256 examples: - ABC Health Care + clientNameLangMap: + type: object + description: |- + Client name in different languages. The 3 letter language code needs to be passed as key and + client name in the desired language needs to be passed as value status: type: string enum: - active - inactive - description: Status of OIDC client. + description: Status of the Client. logoUri: type: string - description: Relying party logo URI which will used to display logo in OIDC login and consent pages. + description: Relying party logo URI which will used to display logo in the login and consent pages. format: uri minLength: 1 maxLength: 1024 redirectUris: type: array - description: 'Valid list of callback Uris of the relying party. When OIDC authorize API is called, any one Uri from this list should be sent as redirect_uri. authorization_code will be redirected to this Uri on successful authentication.' + description: 'Valid list of callback Uris of the relying party. When the authorize API is called, any one Uri from this list should be sent as redirect_uri. authorization_code will be redirected to this Uri on successful authentication.' minItems: 1 uniqueItems: true items: @@ -579,6 +1086,9 @@ paths: - 'mosip:idp:acr:generated-code' - 'mosip:idp:acr:linked-wallet' - 'mosip:idp:acr:biometrics' + - 'mosip:idp:acr:id-token' + - 'mosip:idp:acr:password' + - 'mosip:idp:acr:knowledge' grantTypes: type: array description: Form of Authorization Grant presented to token endpoint. @@ -593,6 +1103,7 @@ paths: const: private_key_jwt required: - clientName + - clientNameLangMap - status - logoUri - redirectUris @@ -604,17 +1115,20 @@ paths: - requestTime - request examples: - example-1: + Example 1: value: requestTime: '2011-10-05T14:48:00.000Z' request: clientName: Fastlane e-Sim Service status: active + clientNameLangMap: + fra: Service e-Sim de Fastlane + ara: خدمة فاست لين e-SIM relyingPartyId: Fastlane - logoUri: 'https://fastline.com/logo.png' + logoUri: 'https://fastlane.com/logo.png' redirectUris: - 'https://fastlane.com/homepage' - - 'https://fastlane-dev.com/*' + - 'http://fastlane-dev.com/*' - 'fastlaneapp://oauth/*' authContextRefs: - 'mosip:idp:acr:biometrics' @@ -645,7 +1159,7 @@ paths: properties: clientId: type: string - description: OIDC client identifier. + description: Client identifier. status: type: string enum: @@ -670,25 +1184,28 @@ paths: - invalid_redirect_uri - invalid_grant_type - invalid_client_auth + - invalid_client_name_value + - invalid_language_code errorMessage: type: string examples: Example 1: value: value: - responseTime: '2023-09-22T08:01:13.000Z' - response: - clientId: WMX5pO6dYdCFR3iaVWGclVPNxTNSADDv - status: ACTIVE - errors: [] - deprecated: true + value: + responseTime: '2023-09-22T08:01:13.000Z' + response: + clientId: WMX5pO6dYdCFR3iaVWGclVPNxTNSADDv + status: ACTIVE + errors: [] security: - Authorization-update_oidc_client: [] servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: twjsfa0yqpyej - '/client-mgmt/oauth-client/{client_id}': + id: z27nk9p9gk06g + deprecated: true + '/client-mgmt/client/{client_id}': put: tags: - Management @@ -697,7 +1214,7 @@ paths: API to update existing OAuth/Open ID Connect (OIDC) client, it can be invoked by other modules which manages the relying parties / partners when there any updates on the fields accepted in this API. **Authentication and authorization** is based on a valid JWT issued by a trusted IAM system including "**update_oidc_client**" scope. - operationId: put-oauth-client-client_id + operationId: put-client-client_id parameters: - name: client_id in: path @@ -713,6 +1230,9 @@ paths: application/json: schema: type: object + required: + - requestTime + - request properties: requestTime: type: string @@ -720,6 +1240,17 @@ paths: pattern: '' request: type: object + required: + - clientName + - clientNameLangMap + - status + - logoUri + - redirectUris + - userClaims + - authContextRefs + - grantTypes + - clientAuthMethods + - additionalConfig properties: clientName: type: string @@ -798,19 +1329,64 @@ paths: minItems: 1 items: const: private_key_jwt - required: - - clientName - - clientNameLangMap - - status - - logoUri - - redirectUris - - userClaims - - authContextRefs - - grantTypes - - clientAuthMethods - required: - - requestTime - - request + additionalConfig: + type: object + description: 'This parameter allow us to configure the required values based on their specific authentication and integration needs, ensuring efficient implementation of eSignet for ID verification/authentication.' + required: + - userinfo_response_type + - purpose + - signup_banner_required + - forgot_pwd_link_required + - consent_expire_in_days + properties: + userinfo_response_type: + x-stoplight: + id: 6gpkj6mqys6va + enum: + - JWS + - JWE + description: The response type for the user info endpoint should be configurable to allow the Relying Party to choose between only signed tokens or signed tokens with encryption. + purpose: + type: object + x-stoplight: + id: sfa9a8ygak752 + description: 'Relying Parties should indicate the purpose of using eSignet (e.g., for login, verification) during registration, allowing for specific customisation.' + required: + - type + - title + - subTitle + properties: + type: + x-stoplight: + id: 9hqgv3ivrenfp + enum: + - Verify + - Link + - Login + title: + type: string + x-stoplight: + id: 7brcj2q2o7yyi + subTitle: + type: string + x-stoplight: + id: qagoh90f7hecz + signup_banner_required: + type: boolean + x-stoplight: + id: 4i1vlnmxu16e4 + description: Relying Parties should be able to specify whether they require eSignet sign-up. If no signup service is required UI should not have “Sign up with Unified login“ option. + forgot_pwd_link_required: + type: boolean + x-stoplight: + id: s1pb151yorc5y + description: Relying Parties should be able to specify whether they require eSignet forgot password feature. If it is not required UI should not have “Forgot password“ option + consent_expire_in_days: + type: number + x-stoplight: + id: peyhsfc3w4ajc + minimum: 0 + description: The number of days after which a user's given consent will expire. examples: Example 1: value: @@ -840,6 +1416,15 @@ paths: - authorization_code clientAuthMethods: - private_key_jwt + additionalConfig: + userinfo_response_type: JWS + purpose: + title: Verify your info with e-Signet + type: Verify + subTitle: Please verify the required details + signup_banner_required: true + forgot_pwd_link_required: true + consent_expire_in_days: 30 responses: '200': description: OK @@ -883,6 +1468,7 @@ paths: - invalid_client_auth - invalid_client_name_value - invalid_language_code + - invalid_additional_config errorMessage: type: string examples: @@ -900,7 +1486,7 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: xwqybfwp9me23 + id: 40q06fgspxdz2 /authorize: get: tags: @@ -4428,7 +5014,113 @@ paths: servers: - url: 'https://esignet.collab.mosip.net/v1/esignet' x-stoplight: - id: t315hcecaulyy + id: d3di5edwwt70b + /binding/v2/binding-otp: + post: + tags: + - WALLET BACKEND + summary: Send Binding OTP Endpoint + description: Send wallet binding OTP endpoint is invoked by Mimoto server. + operationId: post-binding-otp + parameters: + - name: partner-api-key + in: header + description: 'API key of the binding partner, this will be passed to binder implementation to interact with authentication system.' + schema: + type: string + - name: partner-id + in: header + description: 'Binding partner Identifier, this will be passed to binder implementation to interact with authentication system.' + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + requestTime: + type: string + request: + type: object + properties: + individualId: + type: string + description: User Id (UIN/VID) + otpChannels: + type: array + description: Channels to which OTP should be delivered. + items: + type: string + captchaToken: + type: string + description: 'Captcha token, if enabled.' + required: + - individualId + - otpChannels + required: + - requestTime + - request + examples: + Example 1: + value: + requestTime: '2023-09-22T08:01:13.000Z' + request: + individualId: '24554655645' + otpChannels: + - sms + - email + captchaToken: ALSKDJFURIEOQPZMKFURHFVBH + responses: + '200': + description: OK + content: + application/json: + schema: + type: object + properties: + responseTIme: + type: string + response: + type: object + properties: + maskedEmail: + type: string + description: Masked email id of the individualId user. + maskedMobile: + type: string + description: Masked mobile number of the individualId user. + errors: + type: array + items: + type: object + properties: + errorCode: + type: string + enum: + - invalid_otp_channel + - unknown_error + - invalid_individual_id + - send_otp_failed + - invalid_captcha + errorMessage: + type: string + required: + - responseTIme + examples: + Example 1: + value: + responseTIme: '2023-09-22T08:01:16.000Z' + response: + maskedEmail: XXdXXaXXhXXkX@gmail.com + maskedMobile: XXXXXXX357934 + errors: [ ] + security: + - Authorization-send_binding_otp: [ ] + servers: + - url: 'https://esignet.collab.mosip.net/v1/esignet' + x-stoplight: + id: xnl3gyq4v4bh4 /binding/wallet-binding: post: tags: From d7e9ce072efde542aa28b3c6e355a4df77368349 Mon Sep 17 00:00:00 2001 From: Sachin Rana Date: Fri, 20 Dec 2024 19:30:14 +0530 Subject: [PATCH 29/40] added missing properties (#1066) Signed-off-by: Sachin Rana --- .../src/main/resources/application-default.properties | 4 ++++ esignet-service/src/main/resources/bootstrap.properties | 3 +++ 2 files changed, 7 insertions(+) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index 0de45f46b..e247b19d7 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -410,3 +410,7 @@ mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.id #mosip.esignet.integration.audit-plugin=LoggerAuditService #mosip.esignet.integration.key-binder=NoOpKeyBinder + + +## Validation schema files +mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json diff --git a/esignet-service/src/main/resources/bootstrap.properties b/esignet-service/src/main/resources/bootstrap.properties index 5549e7880..ff8cfeda9 100644 --- a/esignet-service/src/main/resources/bootstrap.properties +++ b/esignet-service/src/main/resources/bootstrap.properties @@ -46,3 +46,6 @@ management.endpoint.metrics.enabled=true management.endpoints.web.exposure.include=* management.endpoint.prometheus.enabled=true management.metrics.export.prometheus.enabled=true + +# to accept string as valid type for jsonb column +spring.datasource.hikari.data-source-properties=stringtype=unspecified From 7a00945925e81c3d9068e9ac24e6e3cc3749fee6 Mon Sep 17 00:00:00 2001 From: ase-101 Date: Fri, 20 Dec 2024 19:37:41 +0530 Subject: [PATCH 30/40] Update Dockerfile Signed-off-by: ase-101 --- esignet-service/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esignet-service/Dockerfile b/esignet-service/Dockerfile index 28ef466f2..ec6a7056e 100644 --- a/esignet-service/Dockerfile +++ b/esignet-service/Dockerfile @@ -88,7 +88,7 @@ RUN apt-get -y update \ && mkdir -p ${loader_path} \ && mkdir -p ${plugins_path} \ && chmod +x configure_start.sh \ -&& wget https://raw.githubusercontent.com/mosip/artifactory-ref-impl/master/artifacts/src/hsm/client.zip -O client.zip \ +&& wget https://raw.githubusercontent.com/mosip/artifactory-ref-impl/v1.2.0.3/artifacts/src/hsm/client.zip -O client.zip \ && chown -R ${container_user}:${container_user} /home/${container_user} # copy all files under target/esignet-plugins to the plugins folder From 76af09adecc5a1adeb8db42cb3ac3ef05eb1a279 Mon Sep 17 00:00:00 2001 From: Md Humair Kankudti <163233654+Md-Humair-KK@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:39:49 +0530 Subject: [PATCH 31/40] ES-1977 Individual ID is not validated with IDT auth factor (#1042) * Individual ID is not validated with IDT auth factor Signed-off-by: Md-Humair-KK * added EsignetSxception catch block instead of if condition Signed-off-by: Md-Humair-KK * review comments changes and test case fix Signed-off-by: Md-Humair-KK --------- Signed-off-by: Md-Humair-KK --- .../services/AuthorizationHelperService.java | 20 ++++++++--- .../services/AuthorizationServiceImpl.java | 2 +- .../AuthorizationHelperServiceTest.java | 24 +++++++++++-- .../services/AuthorizationServiceTest.java | 36 +++++++++++++++++-- 4 files changed, 71 insertions(+), 11 deletions(-) diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java index bcae28040..fa8b44298 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationHelperService.java @@ -213,13 +213,15 @@ protected KycAuthResult delegateAuthenticateRequest(String transactionId, String /** * Method validates challenge with "IDT" auth factor - * @param authChallenge - * @param transaction - * @param httpServletRequest - * @return + * + * @param authChallenge {@link AuthChallenge} + * @param individualId individual id from {@link AuthRequestV2} + * @param transaction {@link OIDCTransaction} + * @param httpServletRequest {@link HttpServletRequest} + * @return {@link KycAuthResult} */ protected KycAuthResult handleInternalAuthenticateRequest(@NonNull AuthChallenge authChallenge, - @NonNull OIDCTransaction transaction, HttpServletRequest httpServletRequest) { + @NotNull String individualId, @NonNull OIDCTransaction transaction, HttpServletRequest httpServletRequest) { try { JsonNode jsonNode = objectMapper.readTree(IdentityProviderUtil.b64Decode(authChallenge.getChallenge())); if(jsonNode.isNull() || jsonNode.get("token").isNull()) @@ -228,6 +230,12 @@ protected KycAuthResult handleInternalAuthenticateRequest(@NonNull AuthChallenge tokenService.verifyIdToken(token, signupIDTokenAudience); JWT jwt = JWTParser.parse(token); String subject = jwt.getJWTClaimsSet().getSubject(); + + //compares individual from auth request against subject from jwt token. + if(!individualId.equals(subject)){ + throw new EsignetException(INVALID_INDIVIDUAL_ID); + } + Optional result = Arrays.stream(httpServletRequest.getCookies()) .filter(x -> x.getName().equals(subject)) .findFirst(); @@ -244,6 +252,8 @@ protected KycAuthResult handleInternalAuthenticateRequest(@NonNull AuthChallenge } log.error("ID token in the challenge is not matching the required conditions. isCookiePresent: {}, isHaltedTransactionFound: {}", result.isPresent(), haltedTransaction!=null); + } catch (EsignetException e) { + throw e; } catch (Exception e) { log.error("Failed to parse ID token as challenge", e); } diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java index 6292e24a3..1113638ea 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java @@ -363,7 +363,7 @@ private OIDCTransaction authenticate(AuthRequest authRequest, boolean checkConse KycAuthResult kycAuthResult; if(authRequest.getChallengeList().size() == 1 && authRequest.getChallengeList().get(0).getAuthFactorType().equals("IDT")) { - kycAuthResult = authorizationHelperService.handleInternalAuthenticateRequest(authRequest.getChallengeList().get(0), transaction, + kycAuthResult = authorizationHelperService.handleInternalAuthenticateRequest(authRequest.getChallengeList().get(0),authRequest.getIndividualId(), transaction, httpServletRequest); transaction.setInternalAuthSuccess(true); } diff --git a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationHelperServiceTest.java b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationHelperServiceTest.java index 39d5180ce..d12b30197 100644 --- a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationHelperServiceTest.java +++ b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationHelperServiceTest.java @@ -515,7 +515,7 @@ public void testHandleInternalAuthenticateRequest_ValidDetails_thenPass(){ haltedTransaction.setServerNonce("subject"); Mockito.when(cacheUtilService.getHaltedTransaction(Mockito.anyString())).thenReturn(haltedTransaction); - KycAuthResult result = authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, transaction, httpServletRequest); + KycAuthResult result = authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, "subject", transaction, httpServletRequest); Assert.assertNotNull(result); Assert.assertEquals("subject", result.getKycToken()); @@ -523,6 +523,24 @@ public void testHandleInternalAuthenticateRequest_ValidDetails_thenPass(){ Assert.assertEquals("individualId", transaction.getIndividualId()); } + @Test + public void testHandleInternalAuthenticateRequest_InvalidIndividualId_thenFail(){ + ReflectionTestUtils.setField(authorizationHelperService, "objectMapper",objectMapper); + + AuthChallenge authChallenge = new AuthChallenge(); + authChallenge.setChallenge("eyJ0b2tlbiI6ImV5SmhiR2NpT2lKSVV6STFOaUo5LmV5SnpkV0lpT2lKemRXSnFaV04wSW4wLjl0MG5GMkNtVWZaeTlCYlA3cjM4bElhSlJSeTNaSk41MnBRNlpLSl9qVWMifQ=="); + OIDCTransaction transaction = new OIDCTransaction(); + transaction.setIndividualId("individualId"); + Mockito.doNothing().when(tokenService).verifyIdToken(any(), any()); + + try{ + authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, + "invalid_individualId", transaction, httpServletRequest); + }catch(EsignetException e){ + Assert.assertEquals(INVALID_INDIVIDUAL_ID,e.getErrorCode()); + } + } + @Test public void testHandleInternalAuthenticateRequest_NoCookie_thenFail() { @@ -531,7 +549,7 @@ public void testHandleInternalAuthenticateRequest_NoCookie_thenFail() { OIDCTransaction transaction = new OIDCTransaction(); HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); try{ - authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, transaction, httpServletRequest); + authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, "individualId", transaction, httpServletRequest); Assert.fail(); }catch(EsignetException e){ Assert.assertEquals("auth_failed",e.getErrorCode()); @@ -544,7 +562,7 @@ public void testHandleInternalAuthenticateRequest_NoHaltedTransaction_thenFail() authChallenge.setChallenge("base64encodedchallenge"); OIDCTransaction transaction = new OIDCTransaction(); try { - authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, transaction, httpServletRequest); + authorizationHelperService.handleInternalAuthenticateRequest(authChallenge, "individualId", transaction, httpServletRequest); Assert.fail(); }catch(EsignetException e) { diff --git a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java index 268ec3e9a..8e0fc9071 100644 --- a/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java +++ b/oidc-service-impl/src/test/java/io/mosip/esignet/services/AuthorizationServiceTest.java @@ -1292,7 +1292,7 @@ public void authenticateV3_enableCaptcha_thenPass() throws KycAuthException { } @Test - public void authenticateV3_withIDToken_thenPass() { + public void authenticateV3_withIDTokenInvalidIndividualId_thenFail() { String transactionId = "test-transaction"; String individualId = "23423434234"; when(cacheUtilService.getPreAuthTransaction(transactionId)).thenReturn(createIdpTransaction( @@ -1316,6 +1316,39 @@ public void authenticateV3_withIDToken_thenPass() { authChallenges.add(authChallenge); authRequest.setChallengeList(authChallenges); + try{ + AuthResponseV2 authResponseV2 = authorizationServiceImpl.authenticateUserV3(authRequest, httpServletRequest); + Assert.assertNotNull(authResponseV2); + }catch (EsignetException ex){ + Assert.assertEquals(ErrorConstants.INVALID_INDIVIDUAL_ID,ex.getErrorCode()); + } + } + + @Test + public void authenticateV3_withIDToken_thenPass() { + String transactionId = "test-transaction"; + String individualId = "subject"; + when(cacheUtilService.getPreAuthTransaction(transactionId)).thenReturn(createIdpTransaction( + new String[]{"mosip:idp:acr:id-token"})); + when(cacheUtilService.updateIndividualIdHashInPreAuthCache(transactionId, individualId)).thenReturn(createIdpTransaction( + new String[]{"mosip:idp:acr:id-token"})); + + List> allAuthFactors=new ArrayList<>(); + allAuthFactors.add(getAuthFactors("mosip:idp:acr:id-token")); + when(authenticationContextClassRefUtil.getAuthFactors(new String[]{"mosip:idp:acr:id-token"})).thenReturn(allAuthFactors); + + AuthRequestV2 authRequest = new AuthRequestV2(); + authRequest.setTransactionId(transactionId); + authRequest.setIndividualId(individualId); + authRequest.setCaptchaToken("captcha-token"); + + List authChallenges = new ArrayList<>(); + AuthChallenge authChallenge = new AuthChallenge(); + authChallenge.setAuthFactorType("IDT"); + authChallenge.setChallenge("eyJ0b2tlbiI6ImV5SmhiR2NpT2lKSVV6STFOaUo5LmV5SnpkV0lpT2lKemRXSnFaV04wSW4wLjl0MG5GMkNtVWZaeTlCYlA3cjM4bElhSlJSeTNaSk41MnBRNlpLSl9qVWMifQ=="); + authChallenges.add(authChallenge); + authRequest.setChallengeList(authChallenges); + Mockito.when(httpServletRequest.getCookies()).thenReturn(new Cookie[]{new Cookie("subject", "subject")}); OIDCTransaction haltedTransaction = new OIDCTransaction(); @@ -1329,7 +1362,6 @@ public void authenticateV3_withIDToken_thenPass() { Assert.assertNotNull(authResponseV2); } - @Test public void completeSignupRedirect_withValidTransactionId_thenPass() { String transactionId = "validTransactionId"; From 48c2cf8b4a27062bff032a47234f801c0a862f7b Mon Sep 17 00:00:00 2001 From: Sachin Rana Date: Sat, 21 Dec 2024 18:40:38 +0530 Subject: [PATCH 32/40] [ES-1887] Added additionalConfig validator and new client-mgmt apis (#1016) * Added additionalConfig validator and new client-mgmt apis Signed-off-by: Sachin Rana * handled review comments Signed-off-by: Sachin Rana * resolved review comments Signed-off-by: Sachin Rana * resolved review comments Signed-off-by: Sachin Rana * worked on testcases, code improvement and db script changes Signed-off-by: Sachin Rana * removed redundant constraint Signed-off-by: Sachin Rana --------- Signed-off-by: Sachin Rana Signed-off-by: sacrana0 --- .../services/ClientManagementServiceImpl.java | 3 +- .../esignet/ClientManagementServiceTest.java | 4 +- db_scripts/mosip_esignet/db.sql | 2 +- db_scripts/mosip_esignet/dml.sql | 17 +- .../sql/1.5.0_to_1.5.1_upgrade.sql | 11 +- docker-compose/init.sql | 6 +- .../core/constants/ErrorConstants.java | 1 + .../core/dto/ClientDetailCreateRequestV3.java | 4 +- .../core/dto/ClientDetailUpdateRequestV3.java | 10 +- .../validator/ClientAdditionalConfig.java | 24 + .../ClientAdditionalConfigValidator.java | 81 + .../ClientAdditionalConfigConverterTest.java | 36 + .../io/mosip/esignet/core/ValidatorTest.java | 1554 +++++++++-------- .../additional_config_request_schema.json | 44 + .../ClientManagementController.java | 67 +- .../additional_config_request_schema.json | 44 + .../resources/application-default.properties | 4 + ...ientMgmtV2ControllerParameterizedTest.java | 342 ++++ .../additional_config_request_schema.json | 44 + .../resources/application-test.properties | 1 + 20 files changed, 1509 insertions(+), 790 deletions(-) create mode 100644 esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java create mode 100644 esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java create mode 100644 esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java create mode 100644 esignet-core/src/test/resources/additional_config_request_schema.json create mode 100644 esignet-service/src/main/resources/additional_config_request_schema.json create mode 100644 esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java create mode 100644 esignet-service/src/test/resources/additional_config_request_schema.json diff --git a/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java b/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java index 0a5bdc6ff..0a93b4951 100644 --- a/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java +++ b/client-management-service-impl/src/main/java/io/mosip/esignet/services/ClientManagementServiceImpl.java @@ -276,8 +276,7 @@ public ClientDetailResponseV2 updateClient(String clientId, ClientDetailUpdateRe } public ClientDetail buildClient(ClientDetailCreateRequestV2 clientDetailCreateRequestV2) { - Optional result = clientDetailRepository.findById(clientDetailCreateRequestV2.getClientId()); - if (result.isPresent()) { + if (clientDetailRepository.existsById(clientDetailCreateRequestV2.getClientId())) { log.error("Duplicate Client Id : {}", ErrorConstants.DUPLICATE_CLIENT_ID); throw new EsignetException(ErrorConstants.DUPLICATE_CLIENT_ID); } diff --git a/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java b/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java index 5ec7aae35..adc8208e0 100644 --- a/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java +++ b/client-management-service-impl/src/test/java/io/mosip/esignet/ClientManagementServiceTest.java @@ -124,7 +124,7 @@ public void createClientV2_withValidDetail_thenPass() throws Exception { @Test public void createClientV2_withExistingClientId_thenFail() { - Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.of(new ClientDetail())); + Mockito.when(clientDetailRepository.existsById("client_id_v1")).thenReturn(true); ClientDetailCreateRequestV2 clientCreateV2ReqDto = new ClientDetailCreateRequestV2(); clientCreateV2ReqDto.setClientId("client_id_v1"); try { @@ -164,7 +164,7 @@ public void createClientV3_withValidDetail_thenPass() { @Test public void createClientV3_withExistingClientId_thenFail() { - Mockito.when(clientDetailRepository.findById("client_id_v1")).thenReturn(Optional.of(new ClientDetail())); + Mockito.when(clientDetailRepository.existsById("client_id_v1")).thenReturn(true); ClientDetailCreateRequestV3 clientCreateV3ReqDto = new ClientDetailCreateRequestV3(); clientCreateV3ReqDto.setClientId("client_id_v1"); try { diff --git a/db_scripts/mosip_esignet/db.sql b/db_scripts/mosip_esignet/db.sql index 0d9150299..42e3331b9 100644 --- a/db_scripts/mosip_esignet/db.sql +++ b/db_scripts/mosip_esignet/db.sql @@ -6,7 +6,7 @@ CREATE DATABASE mosip_esignet OWNER = postgres TEMPLATE = template0; -COMMENT ON DATABASE mosip_idp IS 'e-Signet related data is stored in this database'; +COMMENT ON DATABASE mosip_esignet IS 'e-Signet related data is stored in this database'; \c mosip_esignet postgres diff --git a/db_scripts/mosip_esignet/dml.sql b/db_scripts/mosip_esignet/dml.sql index 626d1bea1..bdb3eccc3 100644 --- a/db_scripts/mosip_esignet/dml.sql +++ b/db_scripts/mosip_esignet/dml.sql @@ -3,19 +3,4 @@ ----- TRUNCATE esignet.client_detail TABLE Data and It's reference Data and COPY Data from CSV file ----- TRUNCATE TABLE esignet.client_detail cascade ; -\COPY esignet.key_policy_def (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) FROM './dml/esignet-key_policy_def.csv' delimiter ',' HEADER csv; - - - - - - - - - - - - - - - +\COPY esignet.key_policy_def (APP_ID,KEY_VALIDITY_DURATION,PRE_EXPIRE_DAYS,ACCESS_ALLOWED,IS_ACTIVE,CR_BY,CR_DTIMES) FROM './dml/esignet-key_policy_def.csv' delimiter ',' HEADER csv; \ No newline at end of file diff --git a/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql b/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql index e96ef8a6a..afd2517ba 100644 --- a/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql +++ b/db_upgrade_script/mosip_esignet/sql/1.5.0_to_1.5.1_upgrade.sql @@ -31,17 +31,12 @@ EXCEPTION END; $$ LANGUAGE plpgsql; --- Add the new column with a default value -ALTER TABLE client_detail -ADD COLUMN additional_config jsonb DEFAULT '{}'::jsonb; - --- Update existing entries to set the default value for the new column -UPDATE client_detail -SET additional_config = '{}'::jsonb -WHERE additional_config IS NULL; DO $$ BEGIN +-- Add the new column with a default value +ALTER TABLE client_detail ADD COLUMN additional_config jsonb; + IF NOT is_column_jsonb('client_detail', 'public_key') THEN -- create backup diff --git a/docker-compose/init.sql b/docker-compose/init.sql index 5e4bf5802..16f2e20a4 100644 --- a/docker-compose/init.sql +++ b/docker-compose/init.sql @@ -33,16 +33,18 @@ CREATE TABLE esignet.client_detail( redirect_uris character varying NOT NULL, claims character varying NOT NULL, acr_values character varying NOT NULL, - public_key character varying NOT NULL, + public_key jsonb NOT NULL, grant_types character varying NOT NULL, auth_methods character varying NOT NULL, status character varying(20) NOT NULL, + additional_config jsonb, cr_dtimes timestamp NOT NULL, upd_dtimes timestamp, CONSTRAINT pk_clntdtl_id PRIMARY KEY (id), - CONSTRAINT uk_clntdtl_key UNIQUE (public_key) ); +CREATE UNIQUE INDEX unique_n_value ON client_detail ((public_key->>'n')); + create table esignet.consent_detail ( id UUID NOT NULL, client_id VARCHAR NOT NULL, diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java b/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java index 2b8248378..03cdf8ee5 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/constants/ErrorConstants.java @@ -7,6 +7,7 @@ public class ErrorConstants { + public static final String INVALID_ADDITIONAL_CONFIG="invalid_additional_config"; public static final String INVALID_REQUEST="invalid_request"; public static final String INVALID_CLIENT_ID="invalid_client_id"; public static final String INVALID_CLIENT_NAME="invalid_client_name"; diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java index 42d0a4ec4..a358acf38 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailCreateRequestV3.java @@ -1,6 +1,6 @@ package io.mosip.esignet.core.dto; -import lombok.AllArgsConstructor; +import io.mosip.esignet.core.validator.ClientAdditionalConfig; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,6 +10,8 @@ @Data @NoArgsConstructor public class ClientDetailCreateRequestV3 extends ClientDetailCreateRequestV2 { + + @ClientAdditionalConfig private Map additionalConfig; public ClientDetailCreateRequestV3(String clientId, String clientName, Map publicKey, String relyingPartyId, diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java index 1944227c4..2fa677000 100644 --- a/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java +++ b/esignet-core/src/main/java/io/mosip/esignet/core/dto/ClientDetailUpdateRequestV3.java @@ -1,6 +1,6 @@ package io.mosip.esignet.core.dto; -import lombok.AllArgsConstructor; +import io.mosip.esignet.core.validator.ClientAdditionalConfig; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,10 +10,12 @@ @Data @NoArgsConstructor public class ClientDetailUpdateRequestV3 extends ClientDetailUpdateRequestV2 { + + @ClientAdditionalConfig private Map additionalConfig; - public ClientDetailUpdateRequestV3(String logUri, List redirectUris, List userClaims, List authContextRefs, String status, List grantTypes, String clientName, List clientAuthMethods, Map clientNameLangMap, Map additionalConfig){ - super(logUri,redirectUris,userClaims,authContextRefs,status,grantTypes,clientName,clientAuthMethods, clientNameLangMap); - this.additionalConfig=additionalConfig; + public ClientDetailUpdateRequestV3(String logUri, List redirectUris, List userClaims, List authContextRefs, String status, List grantTypes, String clientName, List clientAuthMethods, Map clientNameLangMap, Map additionalConfig) { + super(logUri, redirectUris, userClaims, authContextRefs, status, grantTypes, clientName, clientAuthMethods, clientNameLangMap); + this.additionalConfig = additionalConfig; } } diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java new file mode 100644 index 000000000..f3989b667 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfig.java @@ -0,0 +1,24 @@ +package io.mosip.esignet.core.validator; + +import io.mosip.esignet.core.constants.ErrorConstants; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Target({FIELD}) +@Retention(RUNTIME) +@Constraint(validatedBy = ClientAdditionalConfigValidator.class) +@Documented +public @interface ClientAdditionalConfig { + String message() default ErrorConstants.INVALID_ADDITIONAL_CONFIG; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java new file mode 100644 index 000000000..3139725e5 --- /dev/null +++ b/esignet-core/src/main/java/io/mosip/esignet/core/validator/ClientAdditionalConfigValidator.java @@ -0,0 +1,81 @@ +package io.mosip.esignet.core.validator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; +import io.mosip.esignet.core.exception.EsignetException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Set; + +@Slf4j +public class ClientAdditionalConfigValidator implements + ConstraintValidator> { + + @Value("${mosip.esignet.additional-config.schema.url}") + private String schemaUrl; + + private volatile JsonSchema cachedSchema; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private ResourceLoader resourceLoader; + + @Override + public void initialize(ClientAdditionalConfig constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(Map additionalConfig, ConstraintValidatorContext context) { + if (additionalConfig == null) { + return false; + } + Set errors = null; + try { + JsonNode jsonNode = objectMapper.valueToTree(additionalConfig); + errors = getCachedSchema().validate(jsonNode); + if (errors.isEmpty()) return true; + } catch (Exception e) { + log.error("Error validating additional_config schema: ", e); + } + log.error("Validation failed for additional_config ---> {}", errors); + return false; + } + + private JsonSchema getCachedSchema() throws EsignetException { + if(cachedSchema!=null ) return cachedSchema; + synchronized (this) { + if (cachedSchema == null) { + InputStream schemaResponse = getResource(schemaUrl); + JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); + cachedSchema = jsonSchemaFactory.getSchema(schemaResponse); + } + } + return cachedSchema; + } + + private InputStream getResource(String url) { + try { + Resource resource = resourceLoader.getResource(url); + return resource.getInputStream(); + } catch (IOException e) { + log.error("Failed to parse data: {}", url, e); + } + throw new EsignetException("invalid_configuration"); + } +} diff --git a/esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java b/esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java new file mode 100644 index 000000000..50828632d --- /dev/null +++ b/esignet-core/src/test/java/io/mosip/esignet/core/ClientAdditionalConfigConverterTest.java @@ -0,0 +1,36 @@ +package io.mosip.esignet.core; + +import io.mosip.esignet.core.exception.InvalidClientException; +import io.mosip.esignet.core.util.ClientAdditionalConfigConverter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ClientAdditionalConfigConverterTest { + + private ClientAdditionalConfigConverter converter; + + @BeforeEach + void setUp() { + converter = new ClientAdditionalConfigConverter(); + } + + @Test + public void convertToDatabaseColumn_NullMap_ReturnsNull() { + Assertions.assertNull(converter.convertToDatabaseColumn(null)); + } + + @Test + public void convertToEntityAttribute_NullJson_ReturnsNull() { + Assertions.assertNull(converter.convertToEntityAttribute(null)); + } + + @Test + public void convertToEntityAttribute_InvalidJson_ThrowsException() { + String invalidJson = "{\"invalid: value}"; + Assertions.assertThrows(InvalidClientException.class, () -> + converter.convertToEntityAttribute(invalidJson) + ); + } + +} diff --git a/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java b/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java index 75ca8b8c3..5ca5da8b5 100644 --- a/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java +++ b/esignet-core/src/test/java/io/mosip/esignet/core/ValidatorTest.java @@ -28,15 +28,13 @@ import org.springframework.core.io.ResourceLoader; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.client.RestTemplate; + import java.io.IOException; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; + import static org.mockito.Mockito.when; @@ -44,743 +42,817 @@ @RunWith(MockitoJUnitRunner.class) public class ValidatorTest { - @InjectMocks - ClaimsSchemaValidator claimSchemaValidator; - - @Mock - AuthenticationContextClassRefUtil authenticationContextClassRefUtil; - - @Mock - Authenticator authenticator; - - @Mock - Environment environment; - - - @Mock - RestTemplate restTemplate; - - - ResourceLoader resourceLoader= new DefaultResourceLoader(); - - ObjectMapper mapper= new ObjectMapper(); - - - - private Map discoveryMap = new HashMap<>(); - - @Before - public void setup() throws EsignetException { - Set mockACRs = new HashSet<>(); - mockACRs.add("level1"); - mockACRs.add("level2"); - mockACRs.add("level3"); - mockACRs.add("level4"); - discoveryMap.put("claims_supported", Arrays.asList("name", "gender", "address")); - when(authenticationContextClassRefUtil.getSupportedACRValues()).thenReturn(mockACRs); - when(authenticator.isSupportedOtpChannel("email")).thenReturn(true); - } - - // ============================ Display Validator ========================= - - @Test - public void test_displayValidator_valid_thenPass() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertTrue(validator.isValid("wap", null)); - } - - @Test - public void test_displayValidator_invalid_thenFail() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertFalse(validator.isValid("wap2", null)); - } - - @Test - public void test_displayValidator_invalidWithSpace_thenFail() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertFalse(validator.isValid("page wap", null)); - } - - @Test - public void test_displayValidator_nullValue_thenPass() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_displayValidator_EmptyValue_thenFail() { - OIDCDisplayValidator validator = new OIDCDisplayValidator(); - ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ GranType Validator ========================= - - @Test - public void test_grantTypeValidator_valid_thenPass() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertTrue(validator.isValid("authorization_code", null)); - } - - @Test - public void test_grantTypeValidator_invalid_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid("code", null)); - } - - @Test - public void test_grantTypeValidator_invalidWithSpace_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid(" authorization_code ", null)); - } - - @Test - public void test_grantTypeValidator_nullValue_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_grantTypeValidator_EmptyValue_thenFail() { - OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ Prompt Validator ========================= - - @Test - public void test_PromptValidator_valid_thenPass() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertTrue(validator.isValid("consent", null)); - } - - @Test - public void test_PromptValidator_invalid_thenFail() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertFalse(validator.isValid("pop-up", null)); - } - - @Test - public void test_PromptValidator_invalidWithSpace_thenFail() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertFalse(validator.isValid(" login ", null)); - } - - @Test - public void test_PromptValidator_nullValue_thenPass() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_PromptValidator_EmptyValue_thenFail() { - OIDCPromptValidator validator = new OIDCPromptValidator(); - ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ ResponseType Validator ========================= - - @Test - public void test_ResponseTypeValidator_valid_thenPass() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertTrue(validator.isValid("code", null)); - } - - @Test - public void test_ResponseTypeValidator_invalid_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid("code----", null)); - } - - @Test - public void test_ResponseTypeValidator_invalidWithSpace_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid(" code ", null)); - } - - @Test - public void test_ResponseTypeValidator_nullValue_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_ResponseTypeValidator_EmptyValue_thenFail() { - OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ Client Assertion type Validator - // ========================= - - @Test - public void test_ClientAssertionTypeValidator_valid_thenPass() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertTrue(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer", null)); - } - - @Test - public void test_ClientAssertionTypeValidator_invalid_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid("jwt-bearer", null)); - } - - @Test - public void test_ClientAssertionTypeValidator_invalidWithSpace_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer ", null)); - } - - @Test - public void test_ClientAssertionTypeValidator_nullValue_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_ClientAssertionTypeValidator_EmptyValue_thenFail() { - OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); - ReflectionTestUtils.setField(validator, "supportedAssertionTypes", - Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); - Assert.assertFalse(validator.isValid("", null)); - } - - // ============================ Optional ACR Validator ========================= - - @Test - public void test_OptionalACRValidator_valid_thenPass() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_OptionalACRValidator_EmptyValue_thenFail() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OptionalACRValidator_SingleValue_thenPass() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertTrue(validator.isValid("level2", null)); - } - - @Test - public void test_OptionalACRValidator_MultipleValue_thenPass() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertTrue(validator.isValid("level4 level2", null)); - } - - @Test - public void test_OptionalACRValidator_InvalidMultipleValue_thenFail() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - Assert.assertFalse(validator.isValid("level5 level1", null)); - } - - @Test - public void test_OptionalACRValidator_throwsException_thenFail() { - AuthContextRefValidator validator = new AuthContextRefValidator(); - ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); - when(authenticationContextClassRefUtil.getSupportedACRValues()).thenThrow(EsignetException.class); - Assert.assertFalse(validator.isValid("level5 level1", null)); - } - - // ============================ Request time Validator ========================= - - @Test - public void test_RequestTimeValidator_nullValue_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - Assert.assertFalse(validator.isValid(null, null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_RequestTimeValidator_validValue_thenPass() { - RequestTimeValidator validator = new RequestTimeValidator(); - ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - Assert.assertTrue(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - - requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.plusMinutes(1); - Assert.assertTrue(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - - requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.minusMinutes(1); - Assert.assertTrue(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - } - - @Test - public void test_RequestTimeValidator_futureDateValue_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.plusMinutes(4); - Assert.assertFalse(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - } - - @Test - public void test_RequestTimeValidator_oldDateValue_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - requestTime = requestTime.minusMinutes(5); - Assert.assertFalse(validator - .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); - } - - @Test - public void test_RequestTimeValidator_invalidFormat_thenFail() { - RequestTimeValidator validator = new RequestTimeValidator(); - ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); - String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; - Assert.assertFalse(validator.isValid(requestTime.format(DateTimeFormatter.ofPattern(DATETIME_PATTERN)), null)); - } - - // ============================ Otp channel Validator ========================= - - @Test - public void test_OtpChannelValidator_valid_thenPass() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertTrue(validator.isValid("email", null)); - } - - @Test - public void test_OtpChannelValidator_null_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_OtpChannelValidator_invalid_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid("mobile", null)); - } - - @Test - public void test_OtpChannelValidator_blank_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_OtpChannelValidator_spaceAppended_thenFail() { - OtpChannelValidator validator = new OtpChannelValidator(); - ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); - Assert.assertFalse(validator.isValid(" email ", null)); - } - - // ============================ Format Validator ========================= - - @Test - public void test_FormatValidator_nullValue_thenFail() { - IdFormatValidator validator = new IdFormatValidator(); - Assert.assertFalse(validator.isValid(null, null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_FormatValidator_validValue_thenPass() { - IdFormatValidator validator = new IdFormatValidator(); - Assert.assertTrue(validator.isValid("id-#4_$%", null)); - } - - @Test - public void test_FormatValidator_withValidValue_thenPass() { - IdFormatValidator validator = new IdFormatValidator(); - ReflectionTestUtils.setField(validator, "supportedRegex", "\\S*"); - Assert.assertTrue(validator.isValid("id-#4_$%", null)); - } - - @Test - public void test_FormatValidator_withInvalidValue_thenFail() { - IdFormatValidator validator = new IdFormatValidator(); - Assert.assertFalse(validator.isValid(" id#4$%", null)); - Assert.assertFalse(validator.isValid("id#4$% ", null)); - Assert.assertFalse(validator.isValid("id #4$%", null)); - Assert.assertFalse(validator.isValid("id #4$ %", null)); - } - - // ============================ OIDC Claim Validator ========================= - - @Test - public void test_OIDCClaimValidator_withValidClaim_thenPass() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertTrue(validator.isValid("name", null)); - } - - @Test - public void test_OIDCClaimValidator_withInvalidClaim_thenFail() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertFalse(validator.isValid("email", null)); - } - - @Test - public void test_OIDCClaimValidator_emptyValue_thenFail() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OIDCClaimValidator_nullValue_thenFail() { - OIDCClaimValidator validator = new OIDCClaimValidator(); - ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); - Assert.assertFalse(validator.isValid(null, null)); - } - - // ============================ OIDC Client Auth Validator ========================= - - @Test - public void test_OIDCClientAuthValidator_withValidAuth_thenPass() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertTrue(validator.isValid("pwd", null)); - } - - @Test - public void test_OIDCClientAuthValidator_withInvalidAuth_thenFail() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertFalse(validator.isValid("OTP", null)); - } - - @Test - public void test_OIDCClientAuthValidator_withEmptyAuth_thenFail() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OIDCClientAuthValidator_withNullAuth_thenFail() { - OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); - ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); - Assert.assertFalse(validator.isValid(null, null)); - } - - // ============================ OIDC Scope Validator ========================= - - @Test - public void test_OIDCScopeValidator_withValidScopes_thenPass() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertTrue(validator.isValid("resident-service email openid", null)); - Assert.assertTrue(validator.isValid("resident-service", null)); - Assert.assertTrue(validator.isValid("mosip_identity_json_vc", null)); - } - - @Test - public void test_OIDCScopeValidator_withInvalidScopes_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertFalse(validator.isValid("test scope", null)); - Assert.assertFalse(validator.isValid("resident-service sample_ldp_vc", null)); - } - - @Test - public void test_OIDCScopeValidator_withoutOpenidScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertFalse(validator.isValid("email", null)); - } - - @Test - public void test_OIDCScopeValidator_withEmptyScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - Assert.assertFalse(validator.isValid("", null)); - } - - @Test - public void test_OIDCScopeValidator_withNullScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - Assert.assertFalse(validator.isValid(null, null)); - } - - @Test - public void test_OIDCScopeValidator_withBothOpenIdAndCredentialScope_thenFail() { - OIDCScopeValidator validator = new OIDCScopeValidator(); - ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); - ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); - ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); - Assert.assertFalse(validator.isValid("profile sample_ldp_vc", null)); - } - - // ============================ PKCECodeChallengeMethodValidator Validator ========================= - - @Test - public void test_challengeMethodValidator_withValidValues_thenPass() { - PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); - ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); - Assert.assertTrue(validator.isValid("S256", null)); - Assert.assertTrue(validator.isValid("plain", null)); - Assert.assertTrue(validator.isValid(null, null)); - } - - @Test - public void test_challengeMethodValidator_withInvalidValues_thenFail() { - PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); - ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); - Assert.assertFalse(validator.isValid("s256", null)); - Assert.assertFalse(validator.isValid("PLAIN", null)); - Assert.assertFalse(validator.isValid("null", null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - // ============================ RedirectURLValidator Validator ========================= - - @Test - public void test_redirectURLValidator_withValidValues_thenPass() { - RedirectURLValidator validator = new RedirectURLValidator(); - Assert.assertTrue(validator.isValid("https://domain.com/test", null)); - Assert.assertTrue(validator.isValid("http://localhost:9090/png", null)); - Assert.assertTrue(validator.isValid("http://domain.com/*", null)); - Assert.assertTrue(validator.isValid("https://domain.com/test/*", null)); - Assert.assertTrue(validator.isValid("io.mosip.residentapp://oauth", null)); - Assert.assertTrue(validator.isValid("residentapp://oauth/*", null)); - } - - @Test - public void test_redirectURLValidator_withInvalidValues_thenFail() { - RedirectURLValidator validator = new RedirectURLValidator(); - Assert.assertFalse(validator.isValid("*", null)); - Assert.assertFalse(validator.isValid("https://domain*", null)); - Assert.assertFalse(validator.isValid("io.mosip.residentapp://*", null)); - Assert.assertFalse(validator.isValid("residentapp*", null)); - Assert.assertFalse(validator.isValid("http*", null)); - } + @InjectMocks + ClaimsSchemaValidator claimSchemaValidator; + + @InjectMocks + ClientAdditionalConfigValidator clientAdditionalConfigValidator; + + @Mock + AuthenticationContextClassRefUtil authenticationContextClassRefUtil; + + @Mock + Authenticator authenticator; + + @Mock + Environment environment; + + + @Mock + RestTemplate restTemplate; + + + ResourceLoader resourceLoader = new DefaultResourceLoader(); + + ObjectMapper mapper = new ObjectMapper(); + + + private Map discoveryMap = new HashMap<>(); + + @Before + public void setup() throws EsignetException { + Set mockACRs = new HashSet<>(); + mockACRs.add("level1"); + mockACRs.add("level2"); + mockACRs.add("level3"); + mockACRs.add("level4"); + discoveryMap.put("claims_supported", Arrays.asList("name", "gender", "address")); + when(authenticationContextClassRefUtil.getSupportedACRValues()).thenReturn(mockACRs); + when(authenticator.isSupportedOtpChannel("email")).thenReturn(true); + } + + // ============================ Display Validator ========================= + + @Test + public void test_displayValidator_valid_thenPass() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertTrue(validator.isValid("wap", null)); + } + + @Test + public void test_displayValidator_invalid_thenFail() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertFalse(validator.isValid("wap2", null)); + } + + @Test + public void test_displayValidator_invalidWithSpace_thenFail() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertFalse(validator.isValid("page wap", null)); + } + + @Test + public void test_displayValidator_nullValue_thenPass() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_displayValidator_EmptyValue_thenFail() { + OIDCDisplayValidator validator = new OIDCDisplayValidator(); + ReflectionTestUtils.setField(validator, "supportedDisplays", Arrays.asList("page", "wap")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ GranType Validator ========================= + + @Test + public void test_grantTypeValidator_valid_thenPass() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertTrue(validator.isValid("authorization_code", null)); + } + + @Test + public void test_grantTypeValidator_invalid_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid("code", null)); + } + + @Test + public void test_grantTypeValidator_invalidWithSpace_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid(" authorization_code ", null)); + } + + @Test + public void test_grantTypeValidator_nullValue_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_grantTypeValidator_EmptyValue_thenFail() { + OIDCGrantTypeValidator validator = new OIDCGrantTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedGrantTypes", Arrays.asList("authorization_code")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ Prompt Validator ========================= + + @Test + public void test_PromptValidator_valid_thenPass() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertTrue(validator.isValid("consent", null)); + } + + @Test + public void test_PromptValidator_invalid_thenFail() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertFalse(validator.isValid("pop-up", null)); + } + + @Test + public void test_PromptValidator_invalidWithSpace_thenFail() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertFalse(validator.isValid(" login ", null)); + } + + @Test + public void test_PromptValidator_nullValue_thenPass() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_PromptValidator_EmptyValue_thenFail() { + OIDCPromptValidator validator = new OIDCPromptValidator(); + ReflectionTestUtils.setField(validator, "supportedPrompts", Arrays.asList("none", "login", "consent")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ ResponseType Validator ========================= + + @Test + public void test_ResponseTypeValidator_valid_thenPass() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertTrue(validator.isValid("code", null)); + } + + @Test + public void test_ResponseTypeValidator_invalid_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid("code----", null)); + } + + @Test + public void test_ResponseTypeValidator_invalidWithSpace_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid(" code ", null)); + } + + @Test + public void test_ResponseTypeValidator_nullValue_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_ResponseTypeValidator_EmptyValue_thenFail() { + OIDCResponseTypeValidator validator = new OIDCResponseTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedResponseTypes", Arrays.asList("code")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ Client Assertion type Validator + // ========================= + + @Test + public void test_ClientAssertionTypeValidator_valid_thenPass() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertTrue(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer", null)); + } + + @Test + public void test_ClientAssertionTypeValidator_invalid_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid("jwt-bearer", null)); + } + + @Test + public void test_ClientAssertionTypeValidator_invalidWithSpace_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid("urn:ietf:params:oauth:client-assertion-type:jwt-bearer ", null)); + } + + @Test + public void test_ClientAssertionTypeValidator_nullValue_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_ClientAssertionTypeValidator_EmptyValue_thenFail() { + OIDCClientAssertionTypeValidator validator = new OIDCClientAssertionTypeValidator(); + ReflectionTestUtils.setField(validator, "supportedAssertionTypes", + Arrays.asList("urn:ietf:params:oauth:client-assertion-type:jwt-bearer")); + Assert.assertFalse(validator.isValid("", null)); + } + + // ============================ Optional ACR Validator ========================= + + @Test + public void test_OptionalACRValidator_valid_thenPass() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_OptionalACRValidator_EmptyValue_thenFail() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OptionalACRValidator_SingleValue_thenPass() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertTrue(validator.isValid("level2", null)); + } + + @Test + public void test_OptionalACRValidator_MultipleValue_thenPass() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertTrue(validator.isValid("level4 level2", null)); + } + + @Test + public void test_OptionalACRValidator_InvalidMultipleValue_thenFail() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + Assert.assertFalse(validator.isValid("level5 level1", null)); + } + + @Test + public void test_OptionalACRValidator_throwsException_thenFail() { + AuthContextRefValidator validator = new AuthContextRefValidator(); + ReflectionTestUtils.setField(validator, "acrUtil", authenticationContextClassRefUtil); + when(authenticationContextClassRefUtil.getSupportedACRValues()).thenThrow(EsignetException.class); + Assert.assertFalse(validator.isValid("level5 level1", null)); + } + + // ============================ Request time Validator ========================= + + @Test + public void test_RequestTimeValidator_nullValue_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + Assert.assertFalse(validator.isValid(null, null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_RequestTimeValidator_validValue_thenPass() { + RequestTimeValidator validator = new RequestTimeValidator(); + ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + Assert.assertTrue(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + + requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.plusMinutes(1); + Assert.assertTrue(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + + requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.minusMinutes(1); + Assert.assertTrue(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + } + + @Test + public void test_RequestTimeValidator_futureDateValue_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.plusMinutes(4); + Assert.assertFalse(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + } + + @Test + public void test_RequestTimeValidator_oldDateValue_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + ReflectionTestUtils.setField(validator, "leewayInMinutes", 2); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + requestTime = requestTime.minusMinutes(5); + Assert.assertFalse(validator + .isValid(requestTime.format(DateTimeFormatter.ofPattern(Constants.UTC_DATETIME_PATTERN)), null)); + } + + @Test + public void test_RequestTimeValidator_invalidFormat_thenFail() { + RequestTimeValidator validator = new RequestTimeValidator(); + ZonedDateTime requestTime = ZonedDateTime.now(ZoneOffset.UTC); + String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; + Assert.assertFalse(validator.isValid(requestTime.format(DateTimeFormatter.ofPattern(DATETIME_PATTERN)), null)); + } + + // ============================ Otp channel Validator ========================= + + @Test + public void test_OtpChannelValidator_valid_thenPass() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertTrue(validator.isValid("email", null)); + } + + @Test + public void test_OtpChannelValidator_null_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_OtpChannelValidator_invalid_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid("mobile", null)); + } + + @Test + public void test_OtpChannelValidator_blank_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_OtpChannelValidator_spaceAppended_thenFail() { + OtpChannelValidator validator = new OtpChannelValidator(); + ReflectionTestUtils.setField(validator, "authenticationWrapper", authenticator); + Assert.assertFalse(validator.isValid(" email ", null)); + } + + // ============================ Format Validator ========================= + + @Test + public void test_FormatValidator_nullValue_thenFail() { + IdFormatValidator validator = new IdFormatValidator(); + Assert.assertFalse(validator.isValid(null, null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_FormatValidator_validValue_thenPass() { + IdFormatValidator validator = new IdFormatValidator(); + Assert.assertTrue(validator.isValid("id-#4_$%", null)); + } + + @Test + public void test_FormatValidator_withValidValue_thenPass() { + IdFormatValidator validator = new IdFormatValidator(); + ReflectionTestUtils.setField(validator, "supportedRegex", "\\S*"); + Assert.assertTrue(validator.isValid("id-#4_$%", null)); + } + + @Test + public void test_FormatValidator_withInvalidValue_thenFail() { + IdFormatValidator validator = new IdFormatValidator(); + Assert.assertFalse(validator.isValid(" id#4$%", null)); + Assert.assertFalse(validator.isValid("id#4$% ", null)); + Assert.assertFalse(validator.isValid("id #4$%", null)); + Assert.assertFalse(validator.isValid("id #4$ %", null)); + } + + // ============================ OIDC Claim Validator ========================= + + @Test + public void test_OIDCClaimValidator_withValidClaim_thenPass() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertTrue(validator.isValid("name", null)); + } + + @Test + public void test_OIDCClaimValidator_withInvalidClaim_thenFail() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertFalse(validator.isValid("email", null)); + } + + @Test + public void test_OIDCClaimValidator_emptyValue_thenFail() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OIDCClaimValidator_nullValue_thenFail() { + OIDCClaimValidator validator = new OIDCClaimValidator(); + ReflectionTestUtils.setField(validator, "discoveryMap", discoveryMap); + Assert.assertFalse(validator.isValid(null, null)); + } + + // ============================ OIDC Client Auth Validator ========================= + + @Test + public void test_OIDCClientAuthValidator_withValidAuth_thenPass() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertTrue(validator.isValid("pwd", null)); + } + + @Test + public void test_OIDCClientAuthValidator_withInvalidAuth_thenFail() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertFalse(validator.isValid("OTP", null)); + } + + @Test + public void test_OIDCClientAuthValidator_withEmptyAuth_thenFail() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OIDCClientAuthValidator_withNullAuth_thenFail() { + OIDCClientAuthValidator validator = new OIDCClientAuthValidator(); + ReflectionTestUtils.setField(validator, "supportedClientAuthMethods", Arrays.asList("pwd")); + Assert.assertFalse(validator.isValid(null, null)); + } + + // ============================ OIDC Scope Validator ========================= + + @Test + public void test_OIDCScopeValidator_withValidScopes_thenPass() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertTrue(validator.isValid("resident-service email openid", null)); + Assert.assertTrue(validator.isValid("resident-service", null)); + Assert.assertTrue(validator.isValid("mosip_identity_json_vc", null)); + } + + @Test + public void test_OIDCScopeValidator_withInvalidScopes_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertFalse(validator.isValid("test scope", null)); + Assert.assertFalse(validator.isValid("resident-service sample_ldp_vc", null)); + } + + @Test + public void test_OIDCScopeValidator_withoutOpenidScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertFalse(validator.isValid("email", null)); + } + + @Test + public void test_OIDCScopeValidator_withEmptyScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + Assert.assertFalse(validator.isValid("", null)); + } + + @Test + public void test_OIDCScopeValidator_withNullScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + Assert.assertFalse(validator.isValid(null, null)); + } + + @Test + public void test_OIDCScopeValidator_withBothOpenIdAndCredentialScope_thenFail() { + OIDCScopeValidator validator = new OIDCScopeValidator(); + ReflectionTestUtils.setField(validator, "authorizeScopes", Arrays.asList("resident-service")); + ReflectionTestUtils.setField(validator, "openidScopes", Arrays.asList("profile", "email", "phone")); + ReflectionTestUtils.setField(validator, "credentialScopes", Arrays.asList("sample_ldp_vc", "mosip_identity_json_vc")); + Assert.assertFalse(validator.isValid("profile sample_ldp_vc", null)); + } + + // ============================ PKCECodeChallengeMethodValidator Validator ========================= + + @Test + public void test_challengeMethodValidator_withValidValues_thenPass() { + PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); + ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); + Assert.assertTrue(validator.isValid("S256", null)); + Assert.assertTrue(validator.isValid("plain", null)); + Assert.assertTrue(validator.isValid(null, null)); + } + + @Test + public void test_challengeMethodValidator_withInvalidValues_thenFail() { + PKCECodeChallengeMethodValidator validator = new PKCECodeChallengeMethodValidator(); + ReflectionTestUtils.setField(validator, "supportedMethods", Arrays.asList("S256", "plain")); + Assert.assertFalse(validator.isValid("s256", null)); + Assert.assertFalse(validator.isValid("PLAIN", null)); + Assert.assertFalse(validator.isValid("null", null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + // ============================ RedirectURLValidator Validator ========================= + + @Test + public void test_redirectURLValidator_withValidValues_thenPass() { + RedirectURLValidator validator = new RedirectURLValidator(); + Assert.assertTrue(validator.isValid("https://domain.com/test", null)); + Assert.assertTrue(validator.isValid("http://localhost:9090/png", null)); + Assert.assertTrue(validator.isValid("http://domain.com/*", null)); + Assert.assertTrue(validator.isValid("https://domain.com/test/*", null)); + Assert.assertTrue(validator.isValid("io.mosip.residentapp://oauth", null)); + Assert.assertTrue(validator.isValid("residentapp://oauth/*", null)); + } + + @Test + public void test_redirectURLValidator_withInvalidValues_thenFail() { + RedirectURLValidator validator = new RedirectURLValidator(); + Assert.assertFalse(validator.isValid("*", null)); + Assert.assertFalse(validator.isValid("https://domain*", null)); + Assert.assertFalse(validator.isValid("io.mosip.residentapp://*", null)); + Assert.assertFalse(validator.isValid("residentapp*", null)); + Assert.assertFalse(validator.isValid("http*", null)); + } // ============================ Signature Format Validator ========================= - @Test - public void test_Signature_FormatValidator_nullValue_thenFail() { - SignatureFormatValidator validator = new SignatureFormatValidator(); - Assert.assertFalse(validator.isValid(null, null)); - Assert.assertFalse(validator.isValid("", null)); - Assert.assertFalse(validator.isValid(" ", null)); - } - - @Test - public void test_Signature_FormatValidator_validValue_thenPass() { - SignatureFormatValidator validator = new SignatureFormatValidator(); - Assert.assertTrue(validator.isValid("ea12d.iba13", null)); - } - @Test - public void test_Signature_FormatValidator_withInvalidValue_thenFail() { - SignatureFormatValidator validator = new SignatureFormatValidator(); - Assert.assertFalse(validator.isValid("eab234", null)); - Assert.assertFalse(validator.isValid("eabd2314.123cad.123d ", null)); - Assert.assertFalse(validator.isValid("akf.ia*..aha", null)); - Assert.assertFalse(validator.isValid("ajjf", null)); - } - - //=========================== CodeChallengeValidator ==============================// - - @Test - public void test_ValidCodeChallengeValidator_withValidDetails_thenPass(){ - CodeChallengeValidator validator=new CodeChallengeValidator(); - OAuthDetailRequestV2 request=new OAuthDetailRequestV2(); - request.setCodeChallenge("codeChallenge"); - request.setCodeChallengeMethod("codeChallengeMethod"); - Assert.assertTrue(validator.isValid(request,null)); - request.setCodeChallenge(null); - request.setCodeChallengeMethod(null); - Assert.assertTrue(validator.isValid(request,null)); - request.setCodeChallenge(""); - request.setCodeChallengeMethod(""); - Assert.assertTrue(validator.isValid(request,null)); - } - - @Test - public void test_ValidCodeChallengeValidator_withInvalidDetails_thenFail(){ - CodeChallengeValidator validator=new CodeChallengeValidator(); - OAuthDetailRequestV2 request=new OAuthDetailRequestV2(); - request.setCodeChallenge("codeChallenge"); - request.setCodeChallengeMethod(null); - Assert.assertFalse(validator.isValid(request,null)); - request.setCodeChallenge(null); - request.setCodeChallengeMethod("codeChallengeMethod"); - Assert.assertFalse(validator.isValid(request,null)); - request.setCodeChallenge(""); - request.setCodeChallengeMethod("codeChallengeMethod"); - Assert.assertFalse(validator.isValid(request,null)); - } - - // ============================ ClientNameLang Validator ========================= - - @Test - public void test_ClientNameLangValidator_WithValidDetails_thenPass(){ - ClientNameLangValidator validator=new ClientNameLangValidator(); - Assert.assertTrue(validator.isValid("eng", null)); - } - - @Test - public void test_ClientNameLangValidator_WithInValidDetail_thenFail(){ - ClientNameLangValidator validator=new ClientNameLangValidator(); - Assert.assertFalse(validator.isValid("abc", null)); - } - - // =============================ClaimSchemaValidator=============================// - - @Test - public void claimSchemaValidator_withValidDetails_thenPass() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":\"income-tax\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - idTokenMap.put("some_claim", claimDetail); - - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertTrue(claimSchemaValidator.isValid(claimsV2, null)); - } - - @Test - public void claimSchemaValidator_withTrustFrameWorkAsNull_thenFail() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":null}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - - idTokenMap.put("some_claim", claimDetail); - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); - - } - - @Test - public void claimSchemaValidator_withEssentialAsNonBoolean_thenFail() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - - idTokenMap.put("some_claim", claimDetail); - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); - } - - @Test - public void test_ClaimSchemaValidator_withInvalidValue_thenFail() throws IOException { - - ReflectionTestUtils.setField(claimSchemaValidator,"resourceLoader",resourceLoader); - ReflectionTestUtils.setField(claimSchemaValidator,"objectMapper",mapper); - ReflectionTestUtils.setField(claimSchemaValidator,"schemaUrl","classpath:/verified_claims_request_schema_test.json"); - - String address="{\"essential\":true}"; - String verifiedClaims="[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kf\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; - - JsonNode addressNode = mapper.readValue(address, JsonNode.class); - JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); - - Map userinfoMap = new HashMap<>(); - userinfoMap.put("address", addressNode); - userinfoMap.put("verified_claims", verifiedClaimNode); - Map idTokenMap = new HashMap<>(); - ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); - - idTokenMap.put("some_claim", claimDetail); - ClaimsV2 claimsV2 = new ClaimsV2(); - claimsV2.setUserinfo(userinfoMap); - claimsV2.setId_token(idTokenMap); - - Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); - } + @Test + public void test_Signature_FormatValidator_nullValue_thenFail() { + SignatureFormatValidator validator = new SignatureFormatValidator(); + Assert.assertFalse(validator.isValid(null, null)); + Assert.assertFalse(validator.isValid("", null)); + Assert.assertFalse(validator.isValid(" ", null)); + } + + @Test + public void test_Signature_FormatValidator_validValue_thenPass() { + SignatureFormatValidator validator = new SignatureFormatValidator(); + Assert.assertTrue(validator.isValid("ea12d.iba13", null)); + } + + @Test + public void test_Signature_FormatValidator_withInvalidValue_thenFail() { + SignatureFormatValidator validator = new SignatureFormatValidator(); + Assert.assertFalse(validator.isValid("eab234", null)); + Assert.assertFalse(validator.isValid("eabd2314.123cad.123d ", null)); + Assert.assertFalse(validator.isValid("akf.ia*..aha", null)); + Assert.assertFalse(validator.isValid("ajjf", null)); + } + + //=========================== CodeChallengeValidator ==============================// + + @Test + public void test_ValidCodeChallengeValidator_withValidDetails_thenPass() { + CodeChallengeValidator validator = new CodeChallengeValidator(); + OAuthDetailRequestV2 request = new OAuthDetailRequestV2(); + request.setCodeChallenge("codeChallenge"); + request.setCodeChallengeMethod("codeChallengeMethod"); + Assert.assertTrue(validator.isValid(request, null)); + request.setCodeChallenge(null); + request.setCodeChallengeMethod(null); + Assert.assertTrue(validator.isValid(request, null)); + request.setCodeChallenge(""); + request.setCodeChallengeMethod(""); + Assert.assertTrue(validator.isValid(request, null)); + } + + @Test + public void test_ValidCodeChallengeValidator_withInvalidDetails_thenFail() { + CodeChallengeValidator validator = new CodeChallengeValidator(); + OAuthDetailRequestV2 request = new OAuthDetailRequestV2(); + request.setCodeChallenge("codeChallenge"); + request.setCodeChallengeMethod(null); + Assert.assertFalse(validator.isValid(request, null)); + request.setCodeChallenge(null); + request.setCodeChallengeMethod("codeChallengeMethod"); + Assert.assertFalse(validator.isValid(request, null)); + request.setCodeChallenge(""); + request.setCodeChallengeMethod("codeChallengeMethod"); + Assert.assertFalse(validator.isValid(request, null)); + } + + // ============================ ClientNameLang Validator ========================= + + @Test + public void test_ClientNameLangValidator_WithValidDetails_thenPass() { + ClientNameLangValidator validator = new ClientNameLangValidator(); + Assert.assertTrue(validator.isValid("eng", null)); + } + + @Test + public void test_ClientNameLangValidator_WithInValidDetail_thenFail() { + ClientNameLangValidator validator = new ClientNameLangValidator(); + Assert.assertFalse(validator.isValid("abc", null)); + } + + // =============================ClaimSchemaValidator=============================// + + @Test + public void claimSchemaValidator_withValidDetails_thenPass() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":\"income-tax\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + idTokenMap.put("some_claim", claimDetail); + + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertTrue(claimSchemaValidator.isValid(claimsV2, null)); + } + + @Test + public void claimSchemaValidator_withTrustFrameWorkAsNull_thenFail() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":null}},\"claims\":{\"name\":null,\"email\":{\"essential\":true}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); + + } + + @Test + public void claimSchemaValidator_withEssentialAsNonBoolean_thenFail() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kaif\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); + } + + @Test + public void test_ClaimSchemaValidator_withInvalidValue_thenFail() throws IOException { + + ReflectionTestUtils.setField(claimSchemaValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(claimSchemaValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(claimSchemaValidator, "schemaUrl", "classpath:/verified_claims_request_schema_test.json"); + + String address = "{\"essential\":true}"; + String verifiedClaims = "[{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"name\":null,\"email\":{\"essential\":1}}},{\"verification\":{\"trust_framework\":{\"value\":\"pwd\"}},\"claims\":{\"birthdate\":{\"essential\":true},\"address\":null}},{\"verification\":{\"trust_framework\":{\"value\":\"kf\"}},\"claims\":{\"gender\":{\"essential\":true},\"email\":{\"essential\":true}}}]"; + + JsonNode addressNode = mapper.readValue(address, JsonNode.class); + JsonNode verifiedClaimNode = mapper.readValue(verifiedClaims, JsonNode.class); + + Map userinfoMap = new HashMap<>(); + userinfoMap.put("address", addressNode); + userinfoMap.put("verified_claims", verifiedClaimNode); + Map idTokenMap = new HashMap<>(); + ClaimDetail claimDetail = new ClaimDetail("claim_value", null, true, "secondary"); + + idTokenMap.put("some_claim", claimDetail); + ClaimsV2 claimsV2 = new ClaimsV2(); + claimsV2.setUserinfo(userinfoMap); + claimsV2.setId_token(idTokenMap); + + Assert.assertFalse(claimSchemaValidator.isValid(claimsV2, null)); + } + + // =============================ClientAdditionalConfigValidator=============================// + + public static Map getValidAdditionalConfig() { + Map validAdditionalConfig = new HashMap<>(); + validAdditionalConfig.put("userinfo_response_type", "JWS"); + validAdditionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", ""), + Map.entry("subTitle", "") + )); + validAdditionalConfig.put("signup_banner_required", true); + validAdditionalConfig.put("forgot_pwd_link_required", true); + validAdditionalConfig.put("consent_expire_in_days", 1); + return validAdditionalConfig; + } + + public static List> getInvalidAdditionalConfigs() { + List> invalidAdditionalConfigs = new ArrayList<>(); + + invalidAdditionalConfigs.add(null); + + Map additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("userinfo_response_type", "ABC"); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Collections.emptyMap()); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", 1), //anything other than string + Map.entry("subTitle", "") + )); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("signup_banner_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("forgot_pwd_link_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("consent_expire_in_days", ""); // anything other than number + invalidAdditionalConfigs.add(additionalConfig); + + return invalidAdditionalConfigs; + } + + @Test + public void test_ClientAdditionalConfigValidator_withValidValue_thenPass() { + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "schemaUrl", "classpath:additional_config_request_schema.json"); + Map validAdditionalConfig = getValidAdditionalConfig(); + Assert.assertTrue(clientAdditionalConfigValidator.isValid(validAdditionalConfig, null)); + } + + @Test + public void test_ClientAdditionalConfigValidator_withInvalidValue_thenFail() { + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "resourceLoader", resourceLoader); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "objectMapper", mapper); + ReflectionTestUtils.setField(clientAdditionalConfigValidator, "schemaUrl", "classpath:additional_config_request_schema.json"); + for (Map additionalConfig : getInvalidAdditionalConfigs()) { + Assert.assertFalse(clientAdditionalConfigValidator.isValid(additionalConfig, null)); + } + } } diff --git a/esignet-core/src/test/resources/additional_config_request_schema.json b/esignet-core/src/test/resources/additional_config_request_schema.json new file mode 100644 index 000000000..adfda02d4 --- /dev/null +++ b/esignet-core/src/test/resources/additional_config_request_schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "userinfo_response_type": { + "type": "string", + "enum": ["JWS", "JWE"] + }, + "purpose": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "subTitle": { + "type": "string" + } + }, + "required": ["type", "title", "subTitle"], + "additionalProperties": true + }, + "signup_banner_required": { + "type": "boolean" + }, + "forgot_pwd_link_required": { + "type": "boolean" + }, + "consent_expire_in_days": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "userinfo_response_type", + "purpose", + "signup_banner_required", + "forgot_pwd_link_required", + "consent_expire_in_days" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java b/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java index 0fa2303a7..c77862647 100644 --- a/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java +++ b/esignet-service/src/main/java/io/mosip/esignet/controllers/ClientManagementController.java @@ -30,18 +30,16 @@ public class ClientManagementController { @Autowired AuditPlugin auditWrapper; - + @Value("${mosip.esignet.audit.claim-name:preferred_username}") private String claimName; /** - * @deprecated - * This method is no longer acceptable to create oidc client - *

Use {@link ClientManagementController#createOAuthClient(RequestWrapper)}

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

Use {@link ClientManagementController#createClientV2(RequestWrapper)}

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

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

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

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

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

Use {@link ClientManagementController#createClientV2(RequestWrapper)}

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

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

+ */ + @Deprecated @PutMapping(value = "/client-mgmt/oauth-client/{client_id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public ResponseWrapper updateOAuthClient(@Valid @PathVariable("client_id") String clientId, - @Valid @RequestBody RequestWrapper requestWrapper) throws Exception { + @Valid @RequestBody RequestWrapper requestWrapper) throws Exception { ResponseWrapper response = new ResponseWrapper(); try { response.setResponse(clientManagementService.updateOAuthClient(clientId, requestWrapper.getRequest())); @@ -116,4 +127,34 @@ public ResponseWrapper updateOAuthClient(@Valid @PathVaria return response; } + @PostMapping(value = "/client-mgmt/client", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseWrapper createClientV2(@Valid @RequestBody RequestWrapper requestWrapper) { + ResponseWrapper response = new ResponseWrapper<>(); + try { + response.setResponse(clientManagementService.createClient(requestWrapper.getRequest())); + } catch (EsignetException ex) { + auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName), + Action.OAUTH_CLIENT_CREATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(requestWrapper.getRequest().getClientId()), ex); + throw ex; + } + response.setResponseTime(IdentityProviderUtil.getUTCDateTime()); + return response; + } + + + @PutMapping(value = "client-mgmt/client/{client_id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public ResponseWrapper updateClientV2(@Valid @PathVariable("client_id") String clientId, + @Valid @RequestBody RequestWrapper requestWrapper) { + ResponseWrapper response = new ResponseWrapper<>(); + try { + response.setResponse(clientManagementService.updateClient(clientId, requestWrapper.getRequest())); + } catch (EsignetException ex) { + auditWrapper.logAudit(AuditHelper.getClaimValue(SecurityContextHolder.getContext(), claimName), + Action.OAUTH_CLIENT_UPDATE, ActionStatus.ERROR, AuditHelper.buildAuditDto(clientId), ex); + throw ex; + } + response.setResponseTime(IdentityProviderUtil.getUTCDateTime()); + return response; + } + } diff --git a/esignet-service/src/main/resources/additional_config_request_schema.json b/esignet-service/src/main/resources/additional_config_request_schema.json new file mode 100644 index 000000000..adfda02d4 --- /dev/null +++ b/esignet-service/src/main/resources/additional_config_request_schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "userinfo_response_type": { + "type": "string", + "enum": ["JWS", "JWE"] + }, + "purpose": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "subTitle": { + "type": "string" + } + }, + "required": ["type", "title", "subTitle"], + "additionalProperties": true + }, + "signup_banner_required": { + "type": "boolean" + }, + "forgot_pwd_link_required": { + "type": "boolean" + }, + "consent_expire_in_days": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "userinfo_response_type", + "purpose", + "signup_banner_required", + "forgot_pwd_link_required", + "consent_expire_in_days" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index e247b19d7..7f6a5e64d 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -69,6 +69,10 @@ mosip.esignet.captcha.site-key=${esignet.captcha.site.key} mosip.esignet.signup-id-token-expire-seconds=300 mosip.esignet.signup-id-token-audience=mosip-signup-oauth-client +## Validation schema files +mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json +mosip.esignet.additional-config.schema.url=classpath:additional_config_request_schema.json + ## Transaction halted with prepare-signup-redirect wait time to resume back with complete-signup-redirect API mosip.esignet.signup.halt.expire-seconds=1800 diff --git a/esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java b/esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java new file mode 100644 index 000000000..4836e2fec --- /dev/null +++ b/esignet-service/src/test/java/io/mosip/esignet/controllers/ClientMgmtV2ControllerParameterizedTest.java @@ -0,0 +1,342 @@ +package io.mosip.esignet.controllers; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.mosip.esignet.TestUtil; +import io.mosip.esignet.core.constants.Constants; +import io.mosip.esignet.core.constants.ErrorConstants; +import io.mosip.esignet.core.dto.ClientDetailCreateRequestV3; +import io.mosip.esignet.core.dto.ClientDetailUpdateRequestV3; +import io.mosip.esignet.core.dto.RequestWrapper; +import lombok.AllArgsConstructor; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Stream; + +import static io.mosip.esignet.core.constants.Constants.UTC_DATETIME_PATTERN; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc +public class ClientMgmtV2ControllerParameterizedTest { + + private static Map jwk = TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(); + + @Autowired + private MockMvc mockMvc; + + ObjectMapper objectMapper = new ObjectMapper(); + + @AllArgsConstructor + public static class TestCase { + String title; + ClientDetailCreateRequestV3 clientDetailCreateRequestV3; + ClientDetailUpdateRequestV3 clientDetailUpdateRequestV3; + String clientIdQueryParam; + String errorCode; + } + + @ParameterizedTest(name = "{0}") + @MethodSource("getTestCases") + public void testClientManagementEndpoints(TestCase testCase) throws Exception { + ClientDetailCreateRequestV3 clientDetailCreateRequestV3 = testCase.clientDetailCreateRequestV3; + ClientDetailUpdateRequestV3 clientDetailUpdateRequestV3 = testCase.clientDetailUpdateRequestV3; + String clientIdQueryParam = testCase.clientIdQueryParam; + String errorCode = testCase.errorCode; + + if (clientDetailCreateRequestV3 != null) { + ResultActions createResultActions = mockMvc.perform(post("/client-mgmt/client") + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(getRequestWrapper(clientDetailCreateRequestV3))); + evaluateResultActions(createResultActions, clientDetailCreateRequestV3.getClientId(), + Constants.CLIENT_ACTIVE_STATUS, errorCode); + } + + if (clientDetailUpdateRequestV3 != null) { + ResultActions updateResultActions = mockMvc.perform(put("/client-mgmt/client/" + clientIdQueryParam) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .content(getRequestWrapper(clientDetailUpdateRequestV3))); + evaluateResultActions(updateResultActions, clientIdQueryParam, + clientDetailUpdateRequestV3.getStatus(), errorCode); + } + } + + private static Stream getTestCases() { + Map validAdditionalConfig = getValidAdditionalConfig(); + List TEST_CASES = new ArrayList<>(Arrays.asList( + // test-name, ClientDetailCreateRequest, ClientDetailUpdateRequest, clientIdQueryParam, errorCode + new TestCase("Successful create", new ClientDetailCreateRequestV3("client-id-#12c", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, null), + new TestCase("Duplicate client id", new ClientDetailCreateRequestV3("client-id-#12c", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.DUPLICATE_CLIENT_ID), + new TestCase("With Null ClientId", new ClientDetailCreateRequestV3(null, "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_ID), + new TestCase("With Empty ClientName", new ClientDetailCreateRequestV3("client-id-v2", " ", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_NAME), + new TestCase("With Invalid Language_code", new ClientDetailCreateRequestV3("client-id-v2", "clientname", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("abc", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_NAME_MAP_KEY), + new TestCase("With Invalid public key", new ClientDetailCreateRequestV3("client-id-v2", "Test client", new HashMap<>(), + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_PUBLIC_KEY), + new TestCase("With null public key", new ClientDetailCreateRequestV3("client-id-v2", "Test client", null, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_PUBLIC_KEY), + new TestCase("With null relying party id", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + null, Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_RP_ID), + new TestCase("With empty relying party id", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + " ", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_RP_ID), + new TestCase("With null user claims", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", null, + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLAIM), + new TestCase("With empty user claims", new ClientDetailCreateRequestV3("client-id-v2#2", "Test client", + TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(), + "rp-id", Arrays.asList(), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, null), + new TestCase("With invalid user claims", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", Arrays.asList(null, "given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLAIM), + new TestCase("With valid & invalid user claims", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", Arrays.asList("birthdate", "given_name", "gender", "street"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLAIM), + new TestCase("With invalid acr", new ClientDetailCreateRequestV3("client-id-v2", "Test client", jwk, + "rp-id", Arrays.asList("birthdate", "given_name", "gender"), + Arrays.asList("mosip:idp:acr:static-code-1"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_ACR), + new TestCase("With patterned redirectUri", new ClientDetailCreateRequestV3("client-id-v2#3", "Test client", jwk, + "rp-id", Arrays.asList("birthdate", "given_name", "gender"), + Arrays.asList("mosip:idp:acr:static-code-1"), "https://logo-url/png", + Arrays.asList("https://dev.mosip.net/home/**"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_ACR), + new TestCase("ClientId with spaces", new ClientDetailCreateRequestV3("client id", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_CLIENT_ID), + new TestCase("RP-Id with spaces", new ClientDetailCreateRequestV3("cid#1", "client-name", jwk, + "rp id 1", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, ErrorConstants.INVALID_RP_ID), + new TestCase("with duplicate key", new ClientDetailCreateRequestV3("client-id-v2#4", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, "unknown_error"), + new TestCase("update with invalid clientId", null, new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name#1", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), "cid#1", "invalid_client_id"), + new TestCase("update with invalid language_code", null, new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("abc", "clientname"); + }}, validAdditionalConfig), "cid#1", "invalid_language_code"), + new TestCase("update client-details", new ClientDetailCreateRequestV3("client-id-up2", "client-name", + TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(), + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png", "io.mosip.residentapp://oauth"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name#1", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), "client-id-up2", null), + new TestCase("Create with app redirect URL", new ClientDetailCreateRequestV3("client-id-v2", "client-name", + TestUtil.generateJWK_RSA().toPublicJWK().toJSONObject(), + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("io.mosip.residentapp://oauth", "residentapp://oauth/*"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, validAdditionalConfig), null, null, null) + )); + List> invalidAdditionalConfigs = getInvalidAdditionalConfigs(); + for (Map additionalConfig : invalidAdditionalConfigs) { + TEST_CASES.add( + new TestCase("with invalid additional config", new ClientDetailCreateRequestV3("client-id-v2", "client-name", jwk, + "rp-id", Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "https://logo-url/png", + Arrays.asList("https://logo-url/png"), Arrays.asList("authorization_code"), + Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, + additionalConfig), null, null, ErrorConstants.INVALID_ADDITIONAL_CONFIG) + ); + TEST_CASES.add( + new TestCase("update with invalid additional config", + null, + new ClientDetailUpdateRequestV3("https://logo-url/png", + Arrays.asList("https://logo-url/png", "io.mosip.residentapp://oauth"), Arrays.asList("given_name"), + Arrays.asList("mosip:idp:acr:static-code"), "ACTIVE", Arrays.asList("authorization_code"), + "client-name#1", Arrays.asList("private_key_jwt"), new HashMap() {{ + put("eng", "clientname"); + }}, additionalConfig), "client-id-up2", ErrorConstants.INVALID_ADDITIONAL_CONFIG) + ); + } + return TEST_CASES.stream(); + } + + private String getRequestWrapper(Object request) throws JsonProcessingException { + RequestWrapper wrapper = new RequestWrapper<>(); + wrapper.setRequest(request); + wrapper.setRequestTime(ZonedDateTime + .now(ZoneOffset.UTC) + .format(DateTimeFormatter.ofPattern(UTC_DATETIME_PATTERN))); + return objectMapper.writeValueAsString(wrapper); + } + + private void evaluateResultActions(ResultActions resultActions, String clientId, String status, String errorCode) + throws Exception { + if (errorCode != null) { + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isNotEmpty()) + .andExpect(jsonPath("$.errors[0].errorCode").value(errorCode)); + } else { + resultActions.andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isEmpty()) + .andExpect(jsonPath("$.response").isNotEmpty()) + .andExpect(jsonPath("$.response.clientId").value(clientId)) + .andExpect(jsonPath("$.response.status").value(Constants.CLIENT_ACTIVE_STATUS)); + } + } + + public static Map getValidAdditionalConfig() { + Map validAdditionalConfig = new HashMap<>(); + validAdditionalConfig.put("userinfo_response_type", "JWS"); + validAdditionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", ""), + Map.entry("subTitle", "") + )); + validAdditionalConfig.put("signup_banner_required", true); + validAdditionalConfig.put("forgot_pwd_link_required", true); + validAdditionalConfig.put("consent_expire_in_days", 1); + return validAdditionalConfig; + } + + public static List> getInvalidAdditionalConfigs() { + List> invalidAdditionalConfigs = new ArrayList<>(); + + invalidAdditionalConfigs.add(null); + + Map additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("userinfo_response_type", "ABC"); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Collections.emptyMap()); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("purpose", Map.ofEntries( + Map.entry("type", ""), + Map.entry("title", 1), //anything other than string + Map.entry("subTitle", "") + )); + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("signup_banner_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("forgot_pwd_link_required", 1); // anything other than boolean + invalidAdditionalConfigs.add(additionalConfig); + + additionalConfig = getValidAdditionalConfig(); + additionalConfig.put("consent_expire_in_days", ""); // anything other than number + invalidAdditionalConfigs.add(additionalConfig); + + return invalidAdditionalConfigs; + } +} \ No newline at end of file diff --git a/esignet-service/src/test/resources/additional_config_request_schema.json b/esignet-service/src/test/resources/additional_config_request_schema.json new file mode 100644 index 000000000..adfda02d4 --- /dev/null +++ b/esignet-service/src/test/resources/additional_config_request_schema.json @@ -0,0 +1,44 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "userinfo_response_type": { + "type": "string", + "enum": ["JWS", "JWE"] + }, + "purpose": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "subTitle": { + "type": "string" + } + }, + "required": ["type", "title", "subTitle"], + "additionalProperties": true + }, + "signup_banner_required": { + "type": "boolean" + }, + "forgot_pwd_link_required": { + "type": "boolean" + }, + "consent_expire_in_days": { + "type": "number", + "minimum": 0 + } + }, + "required": [ + "userinfo_response_type", + "purpose", + "signup_banner_required", + "forgot_pwd_link_required", + "consent_expire_in_days" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/esignet-service/src/test/resources/application-test.properties b/esignet-service/src/test/resources/application-test.properties index bb746f7b9..295ab384f 100644 --- a/esignet-service/src/test/resources/application-test.properties +++ b/esignet-service/src/test/resources/application-test.properties @@ -53,6 +53,7 @@ mosip.esignet.signup-id-token-audience=mosip-signup-client mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema_test.json +mosip.esignet.additional-config.schema.url=classpath:additional_config_request_schema.json ## ------------------------------------------ e-Signet binding --------------------------------------------------------- From bf203a3e1f558af34ff09cbcd84bd4b76cf55de1 Mon Sep 17 00:00:00 2001 From: rajapandi1234 <138785181+rajapandi1234@users.noreply.github.com> Date: Sat, 21 Dec 2024 18:53:38 +0530 Subject: [PATCH 33/40] updating vulnerable library versions (#1069) * fix: esignet-service/pom.xml to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-5953331 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKSECURITY-8309135 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-7945490 - https://snyk.io/vuln/SNYK-JAVA-ORGPOSTGRESQL-2401816 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-6435948 - https://snyk.io/vuln/SNYK-JAVA-ORGPOSTGRESQL-2390459 - https://snyk.io/vuln/SNYK-JAVA-ORGPOSTGRESQL-2970521 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-8383920 - https://snyk.io/vuln/SNYK-JAVA-ORGPOSTGRESQL-6252740 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-3152153 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-7430175 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-8073090 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-8230373 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-3326459 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-5862028 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-6092281 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-5441321 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1728264 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-6435950 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-5564390 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-2806360 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-6056527 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1048292 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-6183062 - https://snyk.io/vuln/SNYK-JAVA-ORGGLASSFISH-1297098 - https://snyk.io/vuln/SNYK-JAVA-CHQOSLOGBACK-6094942 - https://snyk.io/vuln/SNYK-JAVA-CHQOSLOGBACK-6094943 - https://snyk.io/vuln/SNYK-JAVA-CHQOSLOGBACK-6097492 - https://snyk.io/vuln/SNYK-JAVA-CHQOSLOGBACK-6097493 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1080637 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-2414084 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-7687447 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-8384234 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-3016891 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKSECURITY-8399269 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKSECURITY-8399272 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKSECURITY-8399278 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1080638 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-3016888 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1061939 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1728266 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-3369687 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-5959654 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-5959972 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORKBOOT-6226862 - https://snyk.io/vuln/SNYK-JAVA-CHQOSLOGBACK-1726923 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-1728265 - https://snyk.io/vuln/SNYK-JAVA-ORGPOSTGRESQL-3146847 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-3035793 - https://snyk.io/vuln/SNYK-JAVA-ORGAPACHETOMCATEMBED-3097829 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-3016889 - https://snyk.io/vuln/SNYK-JAVA-ORGYAML-3113851 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-8230364 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-8230365 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-8230366 - https://snyk.io/vuln/SNYK-JAVA-ORGSPRINGFRAMEWORK-8230368 Signed-off-by: rajapandi.m * fix: oidc-ui/package.json & oidc-ui/package-lock.json to reduce vulnerabilities The following vulnerabilities are fixed with an upgrade: - https://snyk.io/vuln/SNYK-JS-CRYPTOJS-6028119 - https://snyk.io/vuln/SNYK-JS-JOSE-6419224 Signed-off-by: rajapandi.m --------- Signed-off-by: rajapandi.m Co-authored-by: snyk-bot --- esignet-service/pom.xml | 2 +- oidc-ui/package-lock.json | 18 ++++++++++-------- oidc-ui/package.json | 4 ++-- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/esignet-service/pom.xml b/esignet-service/pom.xml index d6ebbcb35..9676d670f 100644 --- a/esignet-service/pom.xml +++ b/esignet-service/pom.xml @@ -45,7 +45,7 @@ org.springframework.boot spring-boot-starter-oauth2-resource-server - 3.2.7 + 3.2.12 org.postgresql diff --git a/oidc-ui/package-lock.json b/oidc-ui/package-lock.json index c1d8a0cdc..deaf069c6 100644 --- a/oidc-ui/package-lock.json +++ b/oidc-ui/package-lock.json @@ -16,12 +16,12 @@ "axios": "^1.7.3", "buffer": "^6.0.3", "cra-template": "1.1.3", - "crypto-js": "^4.1.1", + "crypto-js": "^4.2.0", "i18next": "^21.10.0", "i18next-browser-languagedetector": "^7.0.0", "i18next-http-backend": "^2.0.1", "iso-3166-1": "^2.1.1", - "jose": "^4.9.3", + "jose": "^4.15.5", "qrcode": "^1.5.1", "react": "^18.2.0", "react-country-flag": "^3.1.0", @@ -6625,9 +6625,10 @@ } }, "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" }, "node_modules/crypto-random-string": { "version": "2.0.0", @@ -12164,9 +12165,10 @@ } }, "node_modules/jose": { - "version": "4.14.4", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.14.4.tgz", - "integrity": "sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g==", + "version": "4.15.5", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz", + "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/panva" } diff --git a/oidc-ui/package.json b/oidc-ui/package.json index 3cbd834c9..3983a5435 100644 --- a/oidc-ui/package.json +++ b/oidc-ui/package.json @@ -11,12 +11,12 @@ "axios": "^1.7.3", "buffer": "^6.0.3", "cra-template": "1.1.3", - "crypto-js": "^4.1.1", + "crypto-js": "^4.2.0", "i18next": "^21.10.0", "i18next-browser-languagedetector": "^7.0.0", "i18next-http-backend": "^2.0.1", "iso-3166-1": "^2.1.1", - "jose": "^4.9.3", + "jose": "^4.15.5", "qrcode": "^1.5.1", "react": "^18.2.0", "react-country-flag": "^3.1.0", From b6a7b328b711549e2f91feb745b8d2148fa1974f Mon Sep 17 00:00:00 2001 From: Sachin Rana Date: Sat, 21 Dec 2024 19:45:11 +0530 Subject: [PATCH 34/40] removed a duplicate property (#1072) Signed-off-by: Sachin Rana --- .../src/main/resources/application-default.properties | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index 7f6a5e64d..7e5c4fceb 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -413,8 +413,4 @@ mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.id ##-------------------------------------------- Default Integrations ----------------------------------------------------- #mosip.esignet.integration.audit-plugin=LoggerAuditService -#mosip.esignet.integration.key-binder=NoOpKeyBinder - - -## Validation schema files -mosip.esignet.claims.schema.url=classpath:/verified_claims_request_schema.json +#mosip.esignet.integration.key-binder=NoOpKeyBinder \ No newline at end of file From 6d06a3dced571422d49f6414e5b1147b99c46452 Mon Sep 17 00:00:00 2001 From: Zeeshan Mehboob <82993262+zesu22@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:19:01 +0530 Subject: [PATCH 35/40] [ADDED] i18n language changes for default lang (#1067) Signed-off-by: Zeeshan Mehboob --- oidc-ui/src/App.js | 23 ++++++++++++++++------- oidc-ui/src/components/NavHeader.js | 1 - 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/oidc-ui/src/App.js b/oidc-ui/src/App.js index 85e0ea635..8047013d9 100644 --- a/oidc-ui/src/App.js +++ b/oidc-ui/src/App.js @@ -78,7 +78,7 @@ function App() { //1. Check for ui locales param. Highest priority. //This will override the language detectors selected language - let supportedLanguages = loadLang.languages_2Letters; + let { languages_2Letters: supportedLanguages, langCodeMapping } = loadLang; let searchUrlParams = new URLSearchParams(window.location.search); let uiLocales = searchUrlParams.get("ui_locales"); if (uiLocales) { @@ -86,20 +86,29 @@ function App() { for (let idx in languages) { if (supportedLanguages[languages[idx]]) { i18n.changeLanguage(languages[idx]); - } else { - i18n.changeLanguage(window._env_.DEFAULT_LANG); + return; } } // if language code not found in 2 letter codes, then check mapped language codes - let langCodeMapping = loadLang.langCodeMapping; for (let idx in languages) { if (langCodeMapping[languages[idx]]) { i18n.changeLanguage(langCodeMapping[languages[idx]]); - } else { - i18n.changeLanguage(window._env_.DEFAULT_LANG); + return; } } + + let defaultLang = window._env_.DEFAULT_LANG; + // checking default language in 2 letter language code + if (defaultLang in supportedLanguages) { + i18n.changeLanguage(defaultLang) + return + } + // checking default language in 3 letter language code + if (defaultLang in langCodeMapping) { + i18n.changeLanguage(langCodeMapping[defaultLang]) + return + } } //2. Check for cookie @@ -117,7 +126,7 @@ function App() { case states.LOADING: el = (
- +
); break; diff --git a/oidc-ui/src/components/NavHeader.js b/oidc-ui/src/components/NavHeader.js index 3a7aed771..27d653a5d 100644 --- a/oidc-ui/src/components/NavHeader.js +++ b/oidc-ui/src/components/NavHeader.js @@ -101,7 +101,6 @@ export default function NavHeader({ langOptions, i18nKeyPrefix = "header" }) { } let lang = langOptions.find((option) => { - const currentUILocale = urlSearchParams.get("ui_locales"); return option.value === i18n.language; }); From 653c96791c67347dceaec27aeb2991a4f1996f19 Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:22:05 +0530 Subject: [PATCH 36/40] [ES-1665] Update application-default.properties (#1073) * [ES-1665] Update application-default.properties Signed-off-by: Gk <76690271+gk-4VII@users.noreply.github.com> * [ES-1665] Update application-default.properties Signed-off-by: Gk <76690271+gk-4VII@users.noreply.github.com> --------- Signed-off-by: Gk <76690271+gk-4VII@users.noreply.github.com> --- .../src/main/resources/application-default.properties | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/esignet-service/src/main/resources/application-default.properties b/esignet-service/src/main/resources/application-default.properties index 7e5c4fceb..ed6c12d78 100644 --- a/esignet-service/src/main/resources/application-default.properties +++ b/esignet-service/src/main/resources/application-default.properties @@ -386,6 +386,10 @@ mosip.esignet.ui.config.username.postfix= mosip.esignet.ui.config.username.max-length=12 mosip.esignet.ui.config.username.input-type=text +## This configuration offers a structured approach to supporting multiple login ID types, providing customization options such as prefixes (e.g., country codes for mobile numbers), associated icons, and validation rules (e.g., regex patterns or length constraints). Each login ID type is configurable, ensuring flexibility and consistency across the UI. If no value is provided for the configuration, it defaults to the VID login type as a fallback. +# mosip.esignet.ui.config.login-id.options={{ 'id': 'mobile', 'svg': 'mobile_icon', 'prefixes': {{'label': 'KHM', 'value': '+855', 'regex': ''}, {'label': 'IND', 'value': '+91', 'maxLength': '', 'regex': ''}}, 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'nrc', 'svg': 'nrc_id_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'vid', 'svg': 'vid_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }, { 'id': 'email', 'svg': 'email_icon', 'prefixes': '', 'postfix': '', 'maxLength': '', 'regex': '' }} +mosip.esignet.ui.config.login-id.options={{ 'id': 'vid', 'svg': 'vid_icon', 'prefixes': '${mosip.esignet.ui.config.username.prefix}', 'postfix': '', 'maxLength': ${mosip.esignet.ui.config.username.max-length}, 'regex': '${mosip.esignet.ui.config.username.regex}' }} + mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.ida-env:Developer}', 'sbi.timeout.DISC': 30, \ 'sbi.timeout.DINFO': 30, 'sbi.timeout.CAPTURE': 30, 'sbi.capture.count.face': 1, 'sbi.capture.count.finger': 1, \ 'sbi.capture.count.iris': 1, 'sbi.capture.score.face': 70, 'sbi.capture.score.finger':70, 'sbi.capture.score.iris':70, \ @@ -408,9 +412,10 @@ mosip.esignet.ui.config.key-values={'sbi.env': '${mosip.esignet.authenticator.id 'eKYC-steps.config': '${mosip.esignet.ui.eKYC-steps.url}', \ 'error.banner.close-timer': 10,\ 'auth.factor.kbi.individual-id-field' : '${mosip.esignet.authenticator.default.auth-factor.kbi.individual-id-field}',\ - 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}} + 'auth.factor.kbi.field-details': ${mosip.esignet.authenticator.default.auth-factor.kbi.field-details}, \ + 'login-id.options': ${mosip.esignet.ui.config.login-id.options}} ##-------------------------------------------- Default Integrations ----------------------------------------------------- #mosip.esignet.integration.audit-plugin=LoggerAuditService -#mosip.esignet.integration.key-binder=NoOpKeyBinder \ No newline at end of file +#mosip.esignet.integration.key-binder=NoOpKeyBinder From f2f3bbf4aa6bd3010e01ebcd27f4b893973a6f8b Mon Sep 17 00:00:00 2001 From: Gk <76690271+gk-4VII@users.noreply.github.com> Date: Mon, 23 Dec 2024 13:42:02 +0530 Subject: [PATCH 37/40] [ES-1665] regex validation fixes. (#1074) Signed-off-by: GurukiranP --- oidc-ui/src/components/InputWithImage.js | 2 +- oidc-ui/src/components/InputWithPrefix.js | 13 +++++-------- oidc-ui/src/components/L1Biometrics.js | 13 +++++-------- oidc-ui/src/components/OtpGet.js | 13 +++++-------- oidc-ui/src/components/Password.js | 13 +++++-------- oidc-ui/src/components/Pin.js | 13 +++++-------- 6 files changed, 26 insertions(+), 41 deletions(-) diff --git a/oidc-ui/src/components/InputWithImage.js b/oidc-ui/src/components/InputWithImage.js index 9e56f9cf6..d088b19ba 100644 --- a/oidc-ui/src/components/InputWithImage.js +++ b/oidc-ui/src/components/InputWithImage.js @@ -262,7 +262,7 @@ export default function InputWithImage({ onBlur={onBlurChange} onKeyDown={handleKeyDown} onKeyUp={handleKeyUp} - value={value} + // value={value} type={type} id={id} name={name} diff --git a/oidc-ui/src/components/InputWithPrefix.js b/oidc-ui/src/components/InputWithPrefix.js index cee1a0db2..cc24eca6f 100644 --- a/oidc-ui/src/components/InputWithPrefix.js +++ b/oidc-ui/src/components/InputWithPrefix.js @@ -76,23 +76,20 @@ const InputWithPrefix = (props) => { const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; const trimmedValue = e.target.value.trim(); - let newValue = regex + let newValue = regex && regex.test(trimmedValue) ? trimmedValue - .split("") - .filter((char) => regex.test(char)) - .join("") : trimmedValue; - + setIndividualId(newValue); props.individualId(newValue); // Update state with the visible valid value const isValidInput = (!maxLength && !regex) || // Case 1: No maxLength, no regex - (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (maxLength && !regex && newValue.length <= parseInt(maxLength)) || // Case 2: maxLength only (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only (maxLength && regex && - newValue.length === parseInt(maxLength) && + newValue.length <= parseInt(maxLength) && regex.test(newValue)); // Case 4: Both maxLength and regex props.isBtnDisabled(!isValidInput); @@ -106,7 +103,7 @@ const InputWithPrefix = (props) => { const maxLength = idProperties.maxLength; const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; setIsValid( - (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!maxLength || e.target.value.trim().length <= parseInt(maxLength)) && (!regex || regex.test(e.target.value.trim())) ); }; diff --git a/oidc-ui/src/components/L1Biometrics.js b/oidc-ui/src/components/L1Biometrics.js index 325567c50..9597aeafb 100644 --- a/oidc-ui/src/components/L1Biometrics.js +++ b/oidc-ui/src/components/L1Biometrics.js @@ -129,11 +129,8 @@ export default function L1Biometrics({ const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; const trimmedValue = e.target.value.trim(); - let newValue = regex + let newValue = regex && regex.test(trimmedValue) ? trimmedValue - .split("") - .filter((char) => regex.test(char)) - .join("") : trimmedValue; setIndividualId(newValue); // Update state with the visible valid value @@ -142,11 +139,11 @@ export default function L1Biometrics({ !( ( (!maxLength && !regex) || // Case 1: No maxLength, no regex - (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (maxLength && !regex && newValue.length <= parseInt(maxLength)) || // Case 2: maxLength only (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only (maxLength && regex && - newValue.length === parseInt(maxLength) && + newValue.length <= parseInt(maxLength) && regex.test(newValue)) ) // Case 4: Both maxLength and regex ) @@ -161,7 +158,7 @@ export default function L1Biometrics({ const maxLength = idProperties.maxLength; const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; setIsValid( - (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!maxLength || e.target.value.trim().length <= parseInt(maxLength)) && (!regex || regex.test(e.target.value.trim())) ); }; @@ -386,7 +383,7 @@ export default function L1Biometrics({ } propChange({ disable: - !individualId?.trim() || + !individualId || isBtnDisabled || (showCaptcha && captchaToken === null), onCapture: (e) => authenticateBiometricResponse(e), diff --git a/oidc-ui/src/components/OtpGet.js b/oidc-ui/src/components/OtpGet.js index d2d9125ad..d2913edd1 100644 --- a/oidc-ui/src/components/OtpGet.js +++ b/oidc-ui/src/components/OtpGet.js @@ -128,11 +128,8 @@ export default function OtpGet({ const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; const trimmedValue = e.target.value.trim(); - let newValue = regex + let newValue = regex && regex.test(trimmedValue) ? trimmedValue - .split("") - .filter((char) => regex.test(char)) - .join("") : trimmedValue; setIndividualId(newValue); // Update state with the visible valid value @@ -141,11 +138,11 @@ export default function OtpGet({ !( ( (!maxLength && !regex) || // Case 1: No maxLength, no regex - (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (maxLength && !regex && newValue.length <= parseInt(maxLength)) || // Case 2: maxLength only (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only (maxLength && regex && - newValue.length === parseInt(maxLength) && + newValue.length <= parseInt(maxLength) && regex.test(newValue)) ) // Case 4: Both maxLength and regex ) @@ -160,7 +157,7 @@ export default function OtpGet({ const maxLength = idProperties.maxLength; const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; setIsValid( - (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!maxLength || e.target.value.trim().length <= parseInt(maxLength)) && (!regex || regex.test(e.target.value.trim())) ); }; @@ -342,7 +339,7 @@ export default function OtpGet({ handleClick={sendOTP} id="get_otp" disabled={ - !individualId?.trim() || + !individualId || isBtnDisabled || (showCaptcha && captchaToken === null) } diff --git a/oidc-ui/src/components/Password.js b/oidc-ui/src/components/Password.js index 37bde58e8..a255aa654 100644 --- a/oidc-ui/src/components/Password.js +++ b/oidc-ui/src/components/Password.js @@ -120,11 +120,8 @@ export default function Password({ const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; const trimmedValue = e.target.value.trim(); - let newValue = regex + let newValue = regex && regex.test(trimmedValue) ? trimmedValue - .split("") - .filter((char) => regex.test(char)) - .join("") : trimmedValue; setIndividualId(newValue); // Update state with the visible valid value @@ -138,11 +135,11 @@ export default function Password({ !( ( (!maxLength && !regex) || // Case 1: No maxLength, no regex - (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (maxLength && !regex && newValue.length <= parseInt(maxLength)) || // Case 2: maxLength only (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only (maxLength && regex && - newValue.length === parseInt(maxLength) && + newValue.length <= parseInt(maxLength) && regex.test(newValue)) ) // Case 4: Both maxLength and regex ) @@ -157,7 +154,7 @@ export default function Password({ const maxLength = idProperties.maxLength; const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; setIsValid( - (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!maxLength || e.target.value.trim().length <= parseInt(maxLength)) && (!regex || regex.test(e.target.value.trim())) ); }; @@ -497,7 +494,7 @@ export default function Password({ id="verify_password" className="mt-2" disabled={ - !individualId?.trim() || + !individualId || !password?.trim() || isBtnDisabled || (inputErrorBanner && inputErrorBanner.length > 0) || diff --git a/oidc-ui/src/components/Pin.js b/oidc-ui/src/components/Pin.js index cc964c063..9b43bd97b 100644 --- a/oidc-ui/src/components/Pin.js +++ b/oidc-ui/src/components/Pin.js @@ -116,11 +116,8 @@ export default function Pin({ const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; const trimmedValue = e.target.value.trim(); - let newValue = regex + let newValue = regex && regex.test(trimmedValue) ? trimmedValue - .split("") - .filter((char) => regex.test(char)) - .join("") : trimmedValue; setIndividualId(newValue); // Update state with the visible valid value @@ -134,11 +131,11 @@ export default function Pin({ !( ( (!maxLength && !regex) || // Case 1: No maxLength, no regex - (maxLength && !regex && newValue.length === parseInt(maxLength)) || // Case 2: maxLength only + (maxLength && !regex && newValue.length <= parseInt(maxLength)) || // Case 2: maxLength only (!maxLength && regex && regex.test(newValue)) || // Case 3: regex only (maxLength && regex && - newValue.length === parseInt(maxLength) && + newValue.length <= parseInt(maxLength) && regex.test(newValue)) ) // Case 4: Both maxLength and regex ) @@ -153,7 +150,7 @@ export default function Pin({ const maxLength = idProperties.maxLength; const regex = idProperties.regex ? new RegExp(idProperties.regex) : null; setIsValid( - (!maxLength || e.target.value.trim().length === parseInt(maxLength)) && + (!maxLength || e.target.value.trim().length <= parseInt(maxLength)) && (!regex || regex.test(e.target.value.trim())) ); }; @@ -463,7 +460,7 @@ export default function Pin({ text={t1("login")} id="verify_pin" disabled={ - !individualId?.trim() || + !individualId || !pin?.trim() || isBtnDisabled || (inputError && inputError.length > 0) || From e5afcdca49269ba027819b8fc778b5df82300c0d Mon Sep 17 00:00:00 2001 From: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:58:23 +0530 Subject: [PATCH 38/40] [MOSIP-38555] Removing apitestrig from Sonar analysis Signed-off-by: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com> --- .github/workflows/push-trigger.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/push-trigger.yml b/.github/workflows/push-trigger.yml index 2476b1a2a..cb30de97d 100644 --- a/.github/workflows/push-trigger.yml +++ b/.github/workflows/push-trigger.yml @@ -200,17 +200,17 @@ jobs: RELEASE_DOCKER_HUB: ${{ secrets.RELEASE_DOCKER_HUB }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - sonar_analysis_apitest_esignet: - needs: build-maven-apitest-esignet - if: "${{ github.event_name != 'pull_request' }}" - uses: mosip/kattu/.github/workflows/maven-sonar-analysis.yml@master-java21 - with: - SERVICE_LOCATION: ./api-test - secrets: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - ORG_KEY: ${{ secrets.ORG_KEY }} - OSSRH_USER: ${{ secrets.OSSRH_USER }} - OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }} - OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} - GPG_SECRET: ${{ secrets.GPG_SECRET }} - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} +# sonar_analysis_apitest_esignet: + # needs: build-maven-apitest-esignet + # if: "${{ github.event_name != 'pull_request' }}" + # uses: mosip/kattu/.github/workflows/maven-sonar-analysis.yml@master-java21 + #with: + # SERVICE_LOCATION: ./api-test + #secrets: + # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + # ORG_KEY: ${{ secrets.ORG_KEY }} + # OSSRH_USER: ${{ secrets.OSSRH_USER }} + # OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }} + # OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} + # GPG_SECRET: ${{ secrets.GPG_SECRET }} + # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} From 52faca36d66eb7090627feb9081ba9e3ba2fb974 Mon Sep 17 00:00:00 2001 From: ase-101 Date: Mon, 23 Dec 2024 21:52:06 +0530 Subject: [PATCH 39/40] ES-1975 (#1075) Signed-off-by: ase-101 --- .../io/mosip/esignet/services/AuthorizationServiceImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java index 1113638ea..f7f826bd4 100644 --- a/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java +++ b/oidc-service-impl/src/main/java/io/mosip/esignet/services/AuthorizationServiceImpl.java @@ -341,12 +341,11 @@ public CompleteSignupRedirectResponse completeSignupRedirect(CompleteSignupRedir } //As pathFragment is included in the response header, we should sanitize the input to mitigate - //response splitting vulnerability + //response splitting vulnerability. Removed all whitespace characters private String sanitizePathFragment(String pathFragment) { - return pathFragment.replaceAll("[\r\n]", ""); + return pathFragment.replaceAll("\\s", ""); } - private OIDCTransaction authenticate(AuthRequest authRequest, boolean checkConsentAction, HttpServletRequest httpServletRequest) { OIDCTransaction transaction = cacheUtilService.getPreAuthTransaction(authRequest.getTransactionId()); if(transaction == null) From ca1aa71ad93aee7ee4bfc4ec9226c5f968690198 Mon Sep 17 00:00:00 2001 From: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:54:32 +0530 Subject: [PATCH 40/40] Update push-trigger.yml Signed-off-by: VSIVAKALYAN <103260988+VSIVAKALYAN@users.noreply.github.com> --- .github/workflows/push-trigger.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/push-trigger.yml b/.github/workflows/push-trigger.yml index cb30de97d..030e50439 100644 --- a/.github/workflows/push-trigger.yml +++ b/.github/workflows/push-trigger.yml @@ -199,18 +199,3 @@ jobs: ACTOR_DOCKER_HUB: ${{ secrets.ACTOR_DOCKER_HUB }} RELEASE_DOCKER_HUB: ${{ secrets.RELEASE_DOCKER_HUB }} SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - -# sonar_analysis_apitest_esignet: - # needs: build-maven-apitest-esignet - # if: "${{ github.event_name != 'pull_request' }}" - # uses: mosip/kattu/.github/workflows/maven-sonar-analysis.yml@master-java21 - #with: - # SERVICE_LOCATION: ./api-test - #secrets: - # SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - # ORG_KEY: ${{ secrets.ORG_KEY }} - # OSSRH_USER: ${{ secrets.OSSRH_USER }} - # OSSRH_SECRET: ${{ secrets.OSSRH_SECRET }} - # OSSRH_TOKEN: ${{ secrets.OSSRH_TOKEN }} - # GPG_SECRET: ${{ secrets.GPG_SECRET }} - # SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

MX3P ztNvY{v+v?q|Mpk{FqM|B5AtFB5H%9_RdQk*IU|AI#AX?gg;yyjgPK@;8OFbt<{4dm zblx~g{?6pec&O0lHd3O$`KWj(x z$Z0HTLyMl2%;2_!1BNg^gP25bgXmr1q|ov-Vaq>`aRCNCS}6#iO8Hmd+-B$Z7Bg_$ zS?@JZ3m~@Fe2h&U8@e~uH^1FdvBc4SAgT2LD{ml>mL2*WNElqj;PykC%_NA;7Y-{V zv@@lwK5l=hx9_E%TXb!nO%N2FIK>y$>EL}H&hJncg U_Tj+B{dxA2jppT3rgAL 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�g^ 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�NAP~EBBTr3D24^?S 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