Compare commits
21 Commits
r-8c160b6e
...
master
Author | SHA1 | Date |
---|---|---|
|
fd6690ec85 | |
|
ba48cbb253 | |
|
9a98a657c2 | |
|
fae3a6e302 | |
|
d18a83bb2a | |
|
1b8503773e | |
|
3f615f856b | |
|
8820fd4bb5 | |
|
9d4a5fd184 | |
|
3a2c98739c | |
|
55d5df785c | |
|
75bfa98470 | |
|
7d10c7bdaf | |
|
a3fad2bd5f | |
|
559214b638 | |
|
93eed7e60c | |
|
fdd705eed5 | |
|
c7cbe4fe4d | |
|
5fb9ba8c87 | |
|
3c52d5ff3d | |
|
042d9dded4 |
|
@ -11,8 +11,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
type: "NevisAuth"
|
type: "NevisAuth"
|
||||||
replicas: 1
|
replicas: 1
|
||||||
version: "8.2411.3"
|
version: "8.2505.5"
|
||||||
gitInitVersion: "1.3.0"
|
gitInitVersion: "1.4.0"
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
ports:
|
ports:
|
||||||
management: 9000
|
management: 9000
|
||||||
|
@ -39,13 +39,14 @@ spec:
|
||||||
management:
|
management:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/nevisauth/liveness"
|
path: "/nevisauth/liveness"
|
||||||
|
initialDelaySeconds: 50
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
timeoutSeconds: 6
|
timeoutSeconds: 6
|
||||||
failureThreshold: 50
|
failureThreshold: 30
|
||||||
podDisruptionBudget:
|
podDisruptionBudget:
|
||||||
maxUnavailable: "50%"
|
maxUnavailable: "50%"
|
||||||
git:
|
git:
|
||||||
tag: "r-317ed268556b37656f27fb58fcffd4797cea27e4"
|
tag: "r-d6878093aefa2bfb8cc241b61fff5fe94bc95282"
|
||||||
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth-sts"
|
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth-sts"
|
||||||
credentials: "git-credentials"
|
credentials: "git-credentials"
|
||||||
keystores:
|
keystores:
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -3,6 +3,7 @@ accept.button.label=Accept
|
||||||
cancel.button.label=Cancel
|
cancel.button.label=Cancel
|
||||||
continue.button.label=Continue
|
continue.button.label=Continue
|
||||||
deputy.profile.label=(Deputy Profile)
|
deputy.profile.label=(Deputy Profile)
|
||||||
|
error.account.exists=Account already exists. Continue to log in.
|
||||||
error.saml.failed=Please close your browser and try again.
|
error.saml.failed=Please close your browser and try again.
|
||||||
error_1=Please check your input.
|
error_1=Please check your input.
|
||||||
error_10=Please select the correct user account.
|
error_10=Please select the correct user account.
|
||||||
|
@ -70,6 +71,8 @@ policyInfo.regex.numeric=▪ must contain at least {0} numeric characters.
|
||||||
policyInfo.regex.upper=▪ must contain at least {0} upper case characters.
|
policyInfo.regex.upper=▪ must contain at least {0} upper case characters.
|
||||||
policyInfo.title=The password has to comply with the following password policy:
|
policyInfo.title=The password has to comply with the following password policy:
|
||||||
reject.button.label=Deny
|
reject.button.label=Deny
|
||||||
|
signup.button.label=Signup
|
||||||
|
skip.button.label=Skip
|
||||||
submit.button.label=Submit
|
submit.button.label=Submit
|
||||||
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
||||||
title.logout=Logout
|
title.logout=Logout
|
||||||
|
@ -77,4 +80,5 @@ title.logout.confirmation=Logout
|
||||||
title.logout.reminder=Logout
|
title.logout.reminder=Logout
|
||||||
title.oauth.consent=Client Authorization
|
title.oauth.consent=Client Authorization
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Create account
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
|
|
|
@ -3,6 +3,7 @@ accept.button.label=Akzeptieren
|
||||||
cancel.button.label=Abbrechen
|
cancel.button.label=Abbrechen
|
||||||
continue.button.label=Weiter
|
continue.button.label=Weiter
|
||||||
deputy.profile.label=(Profil Stellvertreter)
|
deputy.profile.label=(Profil Stellvertreter)
|
||||||
|
error.account.exists=Konto existiert bereits. Melden Sie sich an.
|
||||||
error.saml.failed=Bitte schliessen Sie Ihren Browser und versuchen Sie es erneut.
|
error.saml.failed=Bitte schliessen Sie Ihren Browser und versuchen Sie es erneut.
|
||||||
error_1=Bitte überprüfen Sie Ihre Eingabe.
|
error_1=Bitte überprüfen Sie Ihre Eingabe.
|
||||||
error_10=Bitte wählen Sie den gewünschten Benutzer.
|
error_10=Bitte wählen Sie den gewünschten Benutzer.
|
||||||
|
@ -70,6 +71,8 @@ policyInfo.regex.numeric=▪ muss mindestens {0} numerische Zeichen enthalte
|
||||||
policyInfo.regex.upper=▪ muss mindestens {0} Grossbuchstaben enthalten.
|
policyInfo.regex.upper=▪ muss mindestens {0} Grossbuchstaben enthalten.
|
||||||
policyInfo.title=Das Passwort muss den folgenden Passwort-Richtlinien entsprechen:
|
policyInfo.title=Das Passwort muss den folgenden Passwort-Richtlinien entsprechen:
|
||||||
reject.button.label=Ablehnen
|
reject.button.label=Ablehnen
|
||||||
|
signup.button.label=Registrieren
|
||||||
|
skip.button.label=Überspringen
|
||||||
submit.button.label=Senden
|
submit.button.label=Senden
|
||||||
tan.sent=Bitte erfassen Sie den Sicherheitscode, welcher an Ihr Mobiltelefon gesendet wurde.
|
tan.sent=Bitte erfassen Sie den Sicherheitscode, welcher an Ihr Mobiltelefon gesendet wurde.
|
||||||
title.logout=Logout
|
title.logout=Logout
|
||||||
|
@ -77,4 +80,5 @@ title.logout.confirmation=Logout
|
||||||
title.logout.reminder=Logout
|
title.logout.reminder=Logout
|
||||||
title.oauth.consent=Client Authorisierung
|
title.oauth.consent=Client Authorisierung
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Konto erstellen
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
|
|
|
@ -3,6 +3,7 @@ accept.button.label=Accept
|
||||||
cancel.button.label=Cancel
|
cancel.button.label=Cancel
|
||||||
continue.button.label=Continue
|
continue.button.label=Continue
|
||||||
deputy.profile.label=(Deputy Profile)
|
deputy.profile.label=(Deputy Profile)
|
||||||
|
error.account.exists=Account already exists. Continue to log in.
|
||||||
error.saml.failed=Please close your browser and try again.
|
error.saml.failed=Please close your browser and try again.
|
||||||
error_1=Please check your input.
|
error_1=Please check your input.
|
||||||
error_10=Please select the correct user account.
|
error_10=Please select the correct user account.
|
||||||
|
@ -70,6 +71,8 @@ policyInfo.regex.numeric=▪ must contain at least {0} numeric characters.
|
||||||
policyInfo.regex.upper=▪ must contain at least {0} upper case characters.
|
policyInfo.regex.upper=▪ must contain at least {0} upper case characters.
|
||||||
policyInfo.title=The password has to comply with the following password policy:
|
policyInfo.title=The password has to comply with the following password policy:
|
||||||
reject.button.label=Deny
|
reject.button.label=Deny
|
||||||
|
signup.button.label=Signup
|
||||||
|
skip.button.label=Skip
|
||||||
submit.button.label=Submit
|
submit.button.label=Submit
|
||||||
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
||||||
title.logout=Logout
|
title.logout=Logout
|
||||||
|
@ -77,4 +80,5 @@ title.logout.confirmation=Logout
|
||||||
title.logout.reminder=Logout
|
title.logout.reminder=Logout
|
||||||
title.oauth.consent=Client Authorization
|
title.oauth.consent=Client Authorization
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Create account
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
|
|
|
@ -3,6 +3,7 @@ accept.button.label=Accepter
|
||||||
cancel.button.label=Abandonner
|
cancel.button.label=Abandonner
|
||||||
continue.button.label=Continuer
|
continue.button.label=Continuer
|
||||||
deputy.profile.label=(Profil du suppléant)
|
deputy.profile.label=(Profil du suppléant)
|
||||||
|
error.account.exists=Le compte existe déjà. Continuez à vous connecter.
|
||||||
error.saml.failed=Fermez votre navigateur et r;eacute;essayez.
|
error.saml.failed=Fermez votre navigateur et r;eacute;essayez.
|
||||||
error_1=Veuillez vérifier vos données, s.v.p.
|
error_1=Veuillez vérifier vos données, s.v.p.
|
||||||
error_10=Choisissez votre compte.
|
error_10=Choisissez votre compte.
|
||||||
|
@ -70,6 +71,8 @@ policyInfo.regex.numeric=▪ doit comprendre au minimum {0} caractères
|
||||||
policyInfo.regex.upper=▪ doit contenir au moins {0} caractère(s) majuscule(s).
|
policyInfo.regex.upper=▪ doit contenir au moins {0} caractère(s) majuscule(s).
|
||||||
policyInfo.title=Le mot de passe doit respecter les règles suivantes:
|
policyInfo.title=Le mot de passe doit respecter les règles suivantes:
|
||||||
reject.button.label=Refuser
|
reject.button.label=Refuser
|
||||||
|
signup.button.label=Inscription
|
||||||
|
skip.button.label=Passer
|
||||||
submit.button.label=Envoyer
|
submit.button.label=Envoyer
|
||||||
tan.sent=Veuillez saisir le code de sécurité que vous avez reçu au votre téléphone mobile.
|
tan.sent=Veuillez saisir le code de sécurité que vous avez reçu au votre téléphone mobile.
|
||||||
title.logout=Logout
|
title.logout=Logout
|
||||||
|
@ -77,4 +80,5 @@ title.logout.confirmation=Logout
|
||||||
title.logout.reminder=Logout
|
title.logout.reminder=Logout
|
||||||
title.oauth.consent=Autorisation du client
|
title.oauth.consent=Autorisation du client
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Créer un compte
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
|
||||||
accept.button.label=Accettare
|
accept.button.label=Accetta
|
||||||
cancel.button.label=Abortire
|
cancel.button.label=Annulla
|
||||||
continue.button.label=Continua
|
continue.button.label=Continua
|
||||||
deputy.profile.label=(profilo del delegato)
|
deputy.profile.label=(profilo del delegato)
|
||||||
|
error.account.exists=L'account esiste gi<67>. Prosegui col login.
|
||||||
error.saml.failed=Chiudi il browser e riprova.
|
error.saml.failed=Chiudi il browser e riprova.
|
||||||
error_1=Verificare i dati immessi.
|
error_1=Verificare i dati immessi.
|
||||||
error_10=Per favore selezionare il conto utente corretto.
|
error_10=Per favore selezionare il conto utente corretto.
|
||||||
|
@ -69,7 +70,9 @@ policyInfo.regex.nonLetter=▪ non può contenere più di {0} nu
|
||||||
policyInfo.regex.numeric=▪ deve contenere un minimo di {0} carattere/i numerico/i.
|
policyInfo.regex.numeric=▪ deve contenere un minimo di {0} carattere/i numerico/i.
|
||||||
policyInfo.regex.upper=▪ deve conenere almeno {0} carattere/i maiuscolo/i.
|
policyInfo.regex.upper=▪ deve conenere almeno {0} carattere/i maiuscolo/i.
|
||||||
policyInfo.title=La password deve rispettare le seguenti direttive:
|
policyInfo.title=La password deve rispettare le seguenti direttive:
|
||||||
reject.button.label=Rifiuti
|
reject.button.label=Rifiuta
|
||||||
|
signup.button.label=Iscriviti
|
||||||
|
skip.button.label=Salta
|
||||||
submit.button.label=Continua
|
submit.button.label=Continua
|
||||||
tan.sent=Inserisci il codice di sicurezza che è stato inviato al tuo telefono cellulare.
|
tan.sent=Inserisci il codice di sicurezza che è stato inviato al tuo telefono cellulare.
|
||||||
title.logout=Logout
|
title.logout=Logout
|
||||||
|
@ -77,4 +80,5 @@ title.logout.confirmation=Logout
|
||||||
title.logout.reminder=Logout
|
title.logout.reminder=Logout
|
||||||
title.oauth.consent=Autorizzazione del client
|
title.oauth.consent=Autorizzazione del client
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Crea un account
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
|
|
|
@ -13,8 +13,9 @@ JAVA_OPTS=(
|
||||||
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
||||||
"-Dotel.javaagent.logging=application"
|
"-Dotel.javaagent.logging=application"
|
||||||
"-Dotel.javaagent.configuration-file=/var/opt/nevisauth/default/conf/otel.properties"
|
"-Dotel.javaagent.configuration-file=/var/opt/nevisauth/default/conf/otel.properties"
|
||||||
"-Dotel.resource.attributes=service.version=8.2411.3,service.instance.id=$HOSTNAME"
|
"-Dotel.resource.attributes=service.version=8.2505.5,service.instance.id=$HOSTNAME"
|
||||||
"-Djavax.net.ssl.trustStore=/var/opt/keys/trust/auth-sts-idp-extended-truststore/truststore.p12"
|
"-Djavax.net.ssl.trustStore=/var/opt/keys/trust/auth-sts-idp-extended-truststore/truststore.p12"
|
||||||
"-Djavax.net.ssl.trustStorePassword=\${exec:/var/opt/keys/trust/auth-sts-idp-extended-truststore/keypass}"
|
"-Djavax.net.ssl.trustStorePassword=\${exec:/var/opt/keys/trust/auth-sts-idp-extended-truststore/keypass}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -431,4 +431,6 @@
|
||||||
<!-- source: pattern://eaae1a7d4c4e0ce653074f22 -->
|
<!-- source: pattern://eaae1a7d4c4e0ce653074f22 -->
|
||||||
<property name="secToken.binary" value="true"/>
|
<property name="secToken.binary" value="true"/>
|
||||||
</WebService>
|
</WebService>
|
||||||
|
<!-- source: pattern://4bad2fe3ccc54716cc87138f -->
|
||||||
|
<RESTService name="ManagementService" class="ch.nevis.esauth.rest.service.session.ManagementService"/>
|
||||||
</esauth-server>
|
</esauth-server>
|
||||||
|
|
|
@ -16,16 +16,12 @@ Configuration:
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
- name: "EsAuthStart"
|
- name: "EsAuthStart"
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
- name: "org.apache.catalina.loader.WebappClassLoader"
|
|
||||||
level: "FATAL"
|
|
||||||
- name: "org.apache.catalina.startup.HostConfig"
|
|
||||||
level: "ERROR"
|
|
||||||
- name: "ch.nevis.esauth.events"
|
|
||||||
level: "FATAL"
|
|
||||||
- name: "AGOV-ACCT"
|
- name: "AGOV-ACCT"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
- name: "AgovCaptcha"
|
- name: "AgovCaptcha"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
|
- name: "ArtifactResolutionService"
|
||||||
|
level: "DEBUG"
|
||||||
- name: "AuthEngine"
|
- name: "AuthEngine"
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
- name: "AuthPerf"
|
- name: "AuthPerf"
|
||||||
|
@ -33,9 +29,11 @@ Configuration:
|
||||||
- name: "IdmAuth"
|
- name: "IdmAuth"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
- name: "OpTrace"
|
- name: "OpTrace"
|
||||||
level: "DEBUG"
|
level: "INFO"
|
||||||
- name: "Recovery"
|
- name: "Recovery"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
|
- name: "Saml"
|
||||||
|
level: "DEBUG"
|
||||||
- name: "Script"
|
- name: "Script"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
- name: "SessCoord"
|
- name: "SessCoord"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
otel.service.name = auth-sts
|
otel.service.name = auth-sts
|
||||||
|
otel.traces.sampler = always_on
|
||||||
otel.traces.exporter = none
|
otel.traces.exporter = none
|
||||||
otel.metrics.exporter = none
|
otel.metrics.exporter = none
|
||||||
otel.logs.exporter = none
|
otel.logs.exporter = none
|
||||||
|
|
|
@ -14,4 +14,4 @@ try {
|
||||||
LOG.warn("Exception in Script: ${e}")
|
LOG.warn("Exception in Script: ${e}")
|
||||||
} finally {
|
} finally {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,4 @@ try {
|
||||||
LOG.warn("Exception in Script: ${e}")
|
LOG.warn("Exception in Script: ${e}")
|
||||||
} finally {
|
} finally {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
type: "NevisAuth"
|
type: "NevisAuth"
|
||||||
replicas: 1
|
replicas: 1
|
||||||
version: "8.2411.3"
|
version: "8.2505.5"
|
||||||
gitInitVersion: "1.3.0"
|
gitInitVersion: "1.4.0"
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
ports:
|
ports:
|
||||||
management: 9000
|
management: 9000
|
||||||
|
@ -39,15 +39,19 @@ spec:
|
||||||
management:
|
management:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/nevisauth/liveness"
|
path: "/nevisauth/liveness"
|
||||||
|
initialDelaySeconds: 50
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
timeoutSeconds: 6
|
timeoutSeconds: 6
|
||||||
failureThreshold: 50
|
failureThreshold: 30
|
||||||
podDisruptionBudget:
|
podDisruptionBudget:
|
||||||
maxUnavailable: "50%"
|
maxUnavailable: "50%"
|
||||||
git:
|
git:
|
||||||
tag: "r-8c160b6ed06647cec021e38b8bc8f4dffaab04c1"
|
tag: "r-53c09bd6632aebeda2b892197a01a8f7f185561d"
|
||||||
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth"
|
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth"
|
||||||
credentials: "git-credentials"
|
credentials: "git-credentials"
|
||||||
|
database:
|
||||||
|
name: "auth"
|
||||||
|
requiredVersion: "8.2505.5"
|
||||||
keystores:
|
keystores:
|
||||||
- "auth-sh4r3d-internal-idp-auth-signer"
|
- "auth-sh4r3d-internal-idp-auth-signer"
|
||||||
- "auth-auth-realm-mobile-fido-uaf-tls-client-nevisfido"
|
- "auth-auth-realm-mobile-fido-uaf-tls-client-nevisfido"
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
apiVersion: "operator.nevis-security.ch/v1"
|
||||||
|
kind: "NevisDatabase"
|
||||||
|
metadata:
|
||||||
|
name: "auth"
|
||||||
|
namespace: "adn-agov-nevisidm-01-uat"
|
||||||
|
labels:
|
||||||
|
deploymentTarget: "auth"
|
||||||
|
annotations:
|
||||||
|
projectKey: "DEFAULT-ADN-AGOV-PROJECT"
|
||||||
|
patternId: "b7b59e97b3fd18bb60178573"
|
||||||
|
spec:
|
||||||
|
type: "NevisAuth"
|
||||||
|
databaseType: "MariaDB"
|
||||||
|
version: "8.2505.5"
|
||||||
|
url: "mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat"
|
||||||
|
port: 3306
|
||||||
|
database: "nevisauth"
|
||||||
|
bootstrap: true
|
||||||
|
migrate: true
|
||||||
|
rootCredentials:
|
||||||
|
name: "root-mariadb-session-store"
|
||||||
|
namespace: "adn-agov-nevisidm-ob-01-uat"
|
||||||
|
podSecurity:
|
||||||
|
policy: "baseline"
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
timeZone: "Europe/Zurich"
|
|
@ -1,54 +1,54 @@
|
||||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
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
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
|
|
|
@ -1,56 +1,56 @@
|
||||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||||
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
|
||||||
-----END ENCRYPTED PRIVATE KEY-----
|
-----END ENCRYPTED PRIVATE KEY-----
|
||||||
|
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
echo 'password'
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,32 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
4g4p3ZpFGmoq4lM=
|
||||||
|
-----END CERTIFICATE-----
|
Binary file not shown.
Binary file not shown.
|
@ -10,11 +10,11 @@ agov-ident.invalid-url.message=Link can't be processed
|
||||||
agov-ident.invalid-url.title=Invalid Link
|
agov-ident.invalid-url.title=Invalid Link
|
||||||
agov-ident.onboarding=Registration & Verification
|
agov-ident.onboarding=Registration & Verification
|
||||||
agov-ident.retry=Try again
|
agov-ident.retry=Try again
|
||||||
button.submit=Submit
|
|
||||||
cancel.button.label=Cancel
|
cancel.button.label=Cancel
|
||||||
continue.button.label=Continue
|
continue.button.label=Continue
|
||||||
darkModeSwitch.aria.label=Dark mode toggle
|
darkModeSwitch.aria.label=Dark mode toggle
|
||||||
deputy.profile.label=(Deputy Profile)
|
deputy.profile.label=(Deputy Profile)
|
||||||
|
error.account.exists=Account already exists. Continue to log in.
|
||||||
error.policy.failed=The new password does not comply with the policy.
|
error.policy.failed=The new password does not comply with the policy.
|
||||||
error.saml.failed=Please close your browser and try again.
|
error.saml.failed=Please close your browser and try again.
|
||||||
error_1=Please check your input.
|
error_1=Please check your input.
|
||||||
|
@ -297,6 +297,8 @@ recovery_start_info.banner.warning=You will not be able to use your account unti
|
||||||
recovery_start_info.instruction=During the recovery process you will register a new login factor. If your account contains any verified information you might also have to go through a verification process to finish the recovery.
|
recovery_start_info.instruction=During the recovery process you will register a new login factor. If your account contains any verified information you might also have to go through a verification process to finish the recovery.
|
||||||
recovery_start_info.title=You are about to start the recovery process
|
recovery_start_info.title=You are about to start the recovery process
|
||||||
reject.button.label=Deny
|
reject.button.label=Deny
|
||||||
|
signup.button.label=Signup
|
||||||
|
skip.button.label=Skip
|
||||||
submit.button.label=Submit
|
submit.button.label=Submit
|
||||||
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
||||||
title.login=Login
|
title.login=Login
|
||||||
|
@ -307,6 +309,7 @@ title.oauth.consent=Client Authorization
|
||||||
title.pwchange.label=Password Change
|
title.pwchange.label=Password Change
|
||||||
title.pwreset=Password Forgotten
|
title.pwreset=Password Forgotten
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Create account
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
user_input.invalid.email=Please enter a valid email address
|
user_input.invalid.email=Please enter a valid email address
|
||||||
user_input.invalid.email.required=Field required
|
user_input.invalid.email.required=Field required
|
||||||
|
|
|
@ -10,11 +10,11 @@ agov-ident.invalid-url.message=Link kann nicht verarbeitet werden
|
||||||
agov-ident.invalid-url.title=Ungültiger Link
|
agov-ident.invalid-url.title=Ungültiger Link
|
||||||
agov-ident.onboarding=Registrierung & Verifikation
|
agov-ident.onboarding=Registrierung & Verifikation
|
||||||
agov-ident.retry=Versuchen Sie es erneut
|
agov-ident.retry=Versuchen Sie es erneut
|
||||||
button.submit=Senden
|
|
||||||
cancel.button.label=Abbrechen
|
cancel.button.label=Abbrechen
|
||||||
continue.button.label=Weiter
|
continue.button.label=Weiter
|
||||||
darkModeSwitch.aria.label=Dark-Mode-Schalter
|
darkModeSwitch.aria.label=Dark-Mode-Schalter
|
||||||
deputy.profile.label=(Profil Stellvertreter)
|
deputy.profile.label=(Profil Stellvertreter)
|
||||||
|
error.account.exists=Konto existiert bereits. Melden Sie sich an.
|
||||||
error.policy.failed=Das neue Passwort stimmt nicht mit der Richtlinie überein.
|
error.policy.failed=Das neue Passwort stimmt nicht mit der Richtlinie überein.
|
||||||
error.saml.failed=Bitte schliessen Sie Ihren Browser und versuchen Sie es erneut.
|
error.saml.failed=Bitte schliessen Sie Ihren Browser und versuchen Sie es erneut.
|
||||||
error_1=Bitte überprüfen Sie Ihre Eingaben.
|
error_1=Bitte überprüfen Sie Ihre Eingaben.
|
||||||
|
@ -297,6 +297,8 @@ recovery_start_info.banner.warning=Sie können Ihr Konto nicht nutzen, bis d
|
||||||
recovery_start_info.instruction=Während des Wiederherstellungsprozesses werden Sie einen neuen Login-Faktor registrieren. Wenn Ihr Konto verifizierte Informationen enthält, müssen Sie zum Abschluss des Wiederherstellungsprozesses möglicherweise auch einen Verifikationsprozess durchlaufen.
|
recovery_start_info.instruction=Während des Wiederherstellungsprozesses werden Sie einen neuen Login-Faktor registrieren. Wenn Ihr Konto verifizierte Informationen enthält, müssen Sie zum Abschluss des Wiederherstellungsprozesses möglicherweise auch einen Verifikationsprozess durchlaufen.
|
||||||
recovery_start_info.title=Sie sind dabei, den Wiederherstellungsprozess zu starten
|
recovery_start_info.title=Sie sind dabei, den Wiederherstellungsprozess zu starten
|
||||||
reject.button.label=Ablehnen
|
reject.button.label=Ablehnen
|
||||||
|
signup.button.label=Registrieren
|
||||||
|
skip.button.label=Überspringen
|
||||||
submit.button.label=Senden
|
submit.button.label=Senden
|
||||||
tan.sent=Bitte erfassen Sie den Sicherheitscode, welcher an Ihr Mobiltelefon gesendet wurde.
|
tan.sent=Bitte erfassen Sie den Sicherheitscode, welcher an Ihr Mobiltelefon gesendet wurde.
|
||||||
title.login=Login
|
title.login=Login
|
||||||
|
@ -307,6 +309,7 @@ title.oauth.consent=Client Authorisierung
|
||||||
title.pwchange.label=Passwort ändern
|
title.pwchange.label=Passwort ändern
|
||||||
title.pwreset=Passwort Vergesssen
|
title.pwreset=Passwort Vergesssen
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Konto erstellen
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
user_input.invalid.email=Bitte geben Sie eine gültige E-Mail ein
|
user_input.invalid.email=Bitte geben Sie eine gültige E-Mail ein
|
||||||
user_input.invalid.email.required=Erforderliches Feld
|
user_input.invalid.email.required=Erforderliches Feld
|
||||||
|
|
|
@ -10,11 +10,11 @@ agov-ident.invalid-url.message=Link can't be processed
|
||||||
agov-ident.invalid-url.title=Invalid Link
|
agov-ident.invalid-url.title=Invalid Link
|
||||||
agov-ident.onboarding=Registration & Verification
|
agov-ident.onboarding=Registration & Verification
|
||||||
agov-ident.retry=Try again
|
agov-ident.retry=Try again
|
||||||
button.submit=Submit
|
|
||||||
cancel.button.label=Cancel
|
cancel.button.label=Cancel
|
||||||
continue.button.label=Continue
|
continue.button.label=Continue
|
||||||
darkModeSwitch.aria.label=Dark mode toggle
|
darkModeSwitch.aria.label=Dark mode toggle
|
||||||
deputy.profile.label=(Deputy Profile)
|
deputy.profile.label=(Deputy Profile)
|
||||||
|
error.account.exists=Account already exists. Continue to log in.
|
||||||
error.policy.failed=The new password does not comply with the policy.
|
error.policy.failed=The new password does not comply with the policy.
|
||||||
error.saml.failed=Please close your browser and try again.
|
error.saml.failed=Please close your browser and try again.
|
||||||
error_1=Please check your input.
|
error_1=Please check your input.
|
||||||
|
@ -297,6 +297,8 @@ recovery_start_info.banner.warning=You will not be able to use your account unti
|
||||||
recovery_start_info.instruction=During the recovery process you will register a new login factor. If your account contains any verified information you might also have to go through a verification process to finish the recovery.
|
recovery_start_info.instruction=During the recovery process you will register a new login factor. If your account contains any verified information you might also have to go through a verification process to finish the recovery.
|
||||||
recovery_start_info.title=You are about to start the recovery process
|
recovery_start_info.title=You are about to start the recovery process
|
||||||
reject.button.label=Deny
|
reject.button.label=Deny
|
||||||
|
signup.button.label=Signup
|
||||||
|
skip.button.label=Skip
|
||||||
submit.button.label=Submit
|
submit.button.label=Submit
|
||||||
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
tan.sent=Please enter the security code which has been sent to your mobile phone.
|
||||||
title.login=Login
|
title.login=Login
|
||||||
|
@ -307,6 +309,7 @@ title.oauth.consent=Client Authorization
|
||||||
title.pwchange.label=Password Change
|
title.pwchange.label=Password Change
|
||||||
title.pwreset=Password Forgotten
|
title.pwreset=Password Forgotten
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Create account
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
user_input.invalid.email=Please enter a valid email address
|
user_input.invalid.email=Please enter a valid email address
|
||||||
user_input.invalid.email.required=Field required
|
user_input.invalid.email.required=Field required
|
||||||
|
|
|
@ -10,11 +10,11 @@ agov-ident.invalid-url.message=Le lien ne peut pas être traité
|
||||||
agov-ident.invalid-url.title=Lien non valide
|
agov-ident.invalid-url.title=Lien non valide
|
||||||
agov-ident.onboarding=Enregistrement et vérification
|
agov-ident.onboarding=Enregistrement et vérification
|
||||||
agov-ident.retry=Essayez à nouveau
|
agov-ident.retry=Essayez à nouveau
|
||||||
button.submit=Envoyer
|
|
||||||
cancel.button.label=Abandonner
|
cancel.button.label=Abandonner
|
||||||
continue.button.label=Continuer
|
continue.button.label=Continuer
|
||||||
darkModeSwitch.aria.label=Activer l'apparence sombre
|
darkModeSwitch.aria.label=Activer l'apparence sombre
|
||||||
deputy.profile.label=(Profil du suppléant)
|
deputy.profile.label=(Profil du suppléant)
|
||||||
|
error.account.exists=Le compte existe déjà. Continuez à vous connecter.
|
||||||
error.policy.failed=Votre nouveau mot de passe ne conforme pas aux mesures de sécurité
|
error.policy.failed=Votre nouveau mot de passe ne conforme pas aux mesures de sécurité
|
||||||
error.saml.failed=Fermez votre navigateur et r;eacute;essayez.
|
error.saml.failed=Fermez votre navigateur et r;eacute;essayez.
|
||||||
error_1=Veuillez vérifier votre saisie.
|
error_1=Veuillez vérifier votre saisie.
|
||||||
|
@ -297,6 +297,8 @@ recovery_start_info.banner.warning=Vous ne pourrez pas utiliser votre compte tan
|
||||||
recovery_start_info.instruction=Le processus de récupération nécessitera l’enregistrement d’un nouveau facteur d’authentification. Si votre compte contient des informations ayant déjà été vérifiées, il se peut que vous deviez les faire vérifier à nouveau pour terminer la récupération.
|
recovery_start_info.instruction=Le processus de récupération nécessitera l’enregistrement d’un nouveau facteur d’authentification. Si votre compte contient des informations ayant déjà été vérifiées, il se peut que vous deviez les faire vérifier à nouveau pour terminer la récupération.
|
||||||
recovery_start_info.title=Vous êtes sur le point de démarrer le processus de récupération.
|
recovery_start_info.title=Vous êtes sur le point de démarrer le processus de récupération.
|
||||||
reject.button.label=Refuser
|
reject.button.label=Refuser
|
||||||
|
signup.button.label=Inscription
|
||||||
|
skip.button.label=Passer
|
||||||
submit.button.label=Envoyer
|
submit.button.label=Envoyer
|
||||||
tan.sent=Veuillez saisir le code de sécurité que vous avez reçu au votre téléphone mobile.
|
tan.sent=Veuillez saisir le code de sécurité que vous avez reçu au votre téléphone mobile.
|
||||||
title.login=Login
|
title.login=Login
|
||||||
|
@ -307,6 +309,7 @@ title.oauth.consent=Autorisation du client
|
||||||
title.pwchange.label=Changer mot de passe
|
title.pwchange.label=Changer mot de passe
|
||||||
title.pwreset=Mot de Passe Oublié
|
title.pwreset=Mot de Passe Oublié
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Créer un compte
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
user_input.invalid.email=Veuillez saisir un e-mail valable.
|
user_input.invalid.email=Veuillez saisir un e-mail valable.
|
||||||
user_input.invalid.email.required=Champ requis
|
user_input.invalid.email.required=Champ requis
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
accept.button.label=Accettare
|
accept.button.label=Accetta
|
||||||
agov-ident.done.message=Il vostro conto AGOV è ora pronto per l'uso. Può chiudere questa pagina.
|
agov-ident.done.message=Il vostro conto AGOV è ora pronto per l'uso. Può chiudere questa pagina.
|
||||||
agov-ident.done.title=Finito
|
agov-ident.done.title=Finito
|
||||||
agov-ident.failed.instruction=Per completare la registrazione è necessario disporre di un account AGOV e superare la verifica dei dati suggerita. Riprova.
|
agov-ident.failed.instruction=Per completare la registrazione è necessario disporre di un account AGOV e superare la verifica dei dati suggerita. Riprova.
|
||||||
|
@ -10,11 +10,11 @@ agov-ident.invalid-url.message=Il link non può essere elaborato
|
||||||
agov-ident.invalid-url.title=Link non valido
|
agov-ident.invalid-url.title=Link non valido
|
||||||
agov-ident.onboarding=Registrazione e verifica
|
agov-ident.onboarding=Registrazione e verifica
|
||||||
agov-ident.retry=Riprova
|
agov-ident.retry=Riprova
|
||||||
button.submit=Continua
|
cancel.button.label=Annulla
|
||||||
cancel.button.label=Abortire
|
|
||||||
continue.button.label=Continua
|
continue.button.label=Continua
|
||||||
darkModeSwitch.aria.label=Attivare la modalità scura
|
darkModeSwitch.aria.label=Attivare la modalità scura
|
||||||
deputy.profile.label=(profilo del delegato)
|
deputy.profile.label=(profilo del delegato)
|
||||||
|
error.account.exists=L'account esiste gi<67>. Prosegui col login.
|
||||||
error.policy.failed=La nuova password non è stata accettata. Scegliere una password che sia conforme ai criteri di password.
|
error.policy.failed=La nuova password non è stata accettata. Scegliere una password che sia conforme ai criteri di password.
|
||||||
error.saml.failed=Chiudi il browser e riprova.
|
error.saml.failed=Chiudi il browser e riprova.
|
||||||
error_1=Verificare i dati inseriti.
|
error_1=Verificare i dati inseriti.
|
||||||
|
@ -296,7 +296,9 @@ recovery_questionnaire_reason_selection.instruction=Selezioni il motivo per cui
|
||||||
recovery_start_info.banner.warning=Non è possibile utilizzare l’account finché il processo di ripristino non sarà concluso.
|
recovery_start_info.banner.warning=Non è possibile utilizzare l’account finché il processo di ripristino non sarà concluso.
|
||||||
recovery_start_info.instruction=Durante il processo di ripristino registrerà un nuovo fattore di login. Se il suo account contiene informazioni verificate, potrebbe dover effettuare anche un processo di verificazione per completare il ripristino.
|
recovery_start_info.instruction=Durante il processo di ripristino registrerà un nuovo fattore di login. Se il suo account contiene informazioni verificate, potrebbe dover effettuare anche un processo di verificazione per completare il ripristino.
|
||||||
recovery_start_info.title=Sta per iniziare il processo di ripristino
|
recovery_start_info.title=Sta per iniziare il processo di ripristino
|
||||||
reject.button.label=Rifiuti
|
reject.button.label=Rifiuta
|
||||||
|
signup.button.label=Iscriviti
|
||||||
|
skip.button.label=Salta
|
||||||
submit.button.label=Continua
|
submit.button.label=Continua
|
||||||
tan.sent=Inserisci il codice di sicurezza che è stato inviato al tuo telefono cellulare.
|
tan.sent=Inserisci il codice di sicurezza che è stato inviato al tuo telefono cellulare.
|
||||||
title.login=Login
|
title.login=Login
|
||||||
|
@ -307,6 +309,7 @@ title.oauth.consent=Autorizzazione del client
|
||||||
title.pwchange.label=Cambiare Password
|
title.pwchange.label=Cambiare Password
|
||||||
title.pwreset=Password Dimenticata
|
title.pwreset=Password Dimenticata
|
||||||
title.saml.failed=Error
|
title.saml.failed=Error
|
||||||
|
title.signup=Crea un account
|
||||||
title.timeout.page=Logout
|
title.timeout.page=Logout
|
||||||
user_input.invalid.email=Inserire un'e-mail valida.
|
user_input.invalid.email=Inserire un'e-mail valida.
|
||||||
user_input.invalid.email.required=Campo obbligatorio
|
user_input.invalid.email.required=Campo obbligatorio
|
||||||
|
|
|
@ -50,3 +50,4 @@ if (inargs.containsKey('onReload')) {
|
||||||
clearFidoUAFSession()
|
clearFidoUAFSession()
|
||||||
response.setResult('default')
|
response.setResult('default')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,4 +21,4 @@ def agovLoginCookie = "agovLogin=deleted; Domain=${parameters.get('cookie.domain
|
||||||
response.setHeader('Set-Cookie', agovLoginCookie)
|
response.setHeader('Set-Cookie', agovLoginCookie)
|
||||||
|
|
||||||
response.setStatus(AuthResponse.AUTH_ERROR)
|
response.setStatus(AuthResponse.AUTH_ERROR)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,109 +1,109 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
import ch.nevis.idm.client.HTTPRequestWrapper
|
import ch.nevis.idm.client.HTTPRequestWrapper
|
||||||
|
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import groovy.xml.XmlSlurper
|
import groovy.xml.XmlSlurper
|
||||||
|
|
||||||
def getHeader(String name) {
|
def getHeader(String name) {
|
||||||
def inctx = request.getLoginContext()
|
def inctx = request.getLoginContext()
|
||||||
// case-insensitive lookup of HTTP headers
|
// case-insensitive lookup of HTTP headers
|
||||||
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
||||||
map.putAll(inctx)
|
map.putAll(inctx)
|
||||||
return map['connection.HttpHeader.' + name]
|
return map['connection.HttpHeader.' + name]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Accounting
|
// Accounting
|
||||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
|
|
||||||
String clientExtId = session.get('ch.adnovum.nevisidm.user.clientExtId')
|
String clientExtId = session.get('ch.adnovum.nevisidm.user.clientExtId')
|
||||||
String userExtId = session.get('ch.adnovum.nevisidm.user.extId')
|
String userExtId = session.get('ch.adnovum.nevisidm.user.extId')
|
||||||
String mobile = session.get('ch.nevis.idm.User.mobile')
|
String mobile = session.get('ch.nevis.idm.User.mobile')
|
||||||
|
|
||||||
String baseUrl = parameters.get('baseUrl')
|
String baseUrl = parameters.get('baseUrl')
|
||||||
String endPoint = "${baseUrl}/core/v1/${clientExtId}/users/${userExtId}"
|
String endPoint = "${baseUrl}/core/v1/${clientExtId}/users/${userExtId}"
|
||||||
|
|
||||||
|
|
||||||
if (!(parameters.get('ask_mobile_number_enabled')?.toLowerCase()?.trim() == "true")) {
|
if (!(parameters.get('ask_mobile_number_enabled')?.toLowerCase()?.trim() == "true")) {
|
||||||
LOG.debug("Feature 'ask mobile number' is disabled")
|
LOG.debug("Feature 'ask mobile number' is disabled")
|
||||||
response.setResult('done')
|
response.setResult('done')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mobile) {
|
if (mobile) {
|
||||||
LOG.debug("User '${user}' has already registered a mobile number")
|
LOG.debug("User '${user}' has already registered a mobile number")
|
||||||
response.setResult('done')
|
response.setResult('done')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def agovSkipAskingMobileCookieValue = 'missing'
|
def agovSkipAskingMobileCookieValue = 'missing'
|
||||||
|
|
||||||
if (getHeader('cookie') != null) {
|
if (getHeader('cookie') != null) {
|
||||||
def cookies = getHeader('cookie')
|
def cookies = getHeader('cookie')
|
||||||
if (cookies.matches('^.*agovSkipAskingMobile=([^;]+).*$')) {
|
if (cookies.matches('^.*agovSkipAskingMobile=([^;]+).*$')) {
|
||||||
agovSkipAskingMobileCookieValue = cookies.replaceAll('^.*agovSkipAskingMobile=([^;]+).*$', '$1')
|
agovSkipAskingMobileCookieValue = cookies.replaceAll('^.*agovSkipAskingMobile=([^;]+).*$', '$1')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (agovSkipAskingMobileCookieValue == 'true') {
|
if (agovSkipAskingMobileCookieValue == 'true') {
|
||||||
// Don't aske the user again...
|
// Don't aske the user again...
|
||||||
LOG.info("Event='SKIPPEDMOBILENUMBER', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
LOG.info("Event='SKIPPEDMOBILENUMBER', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||||
response.setResult('done')
|
response.setResult('done')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!inargs['submit'] && (!inargs['mobile'] || !inargs['mobile'].isEmpty()) && inargs['language'] && inargs['language'] != session['ch.nevis.session.user.language']) {
|
if (!inargs['submit'] && (!inargs['mobile'] || !inargs['mobile'].isEmpty()) && inargs['language'] && inargs['language'] != session['ch.nevis.session.user.language']) {
|
||||||
// language switch, nothing else to do, just display again the GUI
|
// language switch, nothing else to do, just display again the GUI
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['submit'] && (!inargs['mobile'] || inargs['mobile'].isEmpty()) && inargs['skip']) {
|
if (inargs['submit'] && (!inargs['mobile'] || inargs['mobile'].isEmpty()) && inargs['skip']) {
|
||||||
// no mobile, and user wants to skip it
|
// no mobile, and user wants to skip it
|
||||||
|
|
||||||
LOG.info("Event='NOMOBILENUMBER', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', Persistent='${ inargs['skip'] == 'persistent' ? true : false }'")
|
LOG.info("Event='NOMOBILENUMBER', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', Persistent='${ inargs['skip'] == 'persistent' ? true : false }'")
|
||||||
|
|
||||||
if (inargs['skip'] == 'persistent') {
|
if (inargs['skip'] == 'persistent') {
|
||||||
// persistent cookie for 30d;
|
// persistent cookie for 30d;
|
||||||
def agovSkipAskingMobileCookie = "agovSkipAskingMobile=true; Domain=${parameters.get('cookie.domain')}; Path=/; Max-Age=2592000; SameSite=Strict; Secure; HttpOnly"
|
def agovSkipAskingMobileCookie = "agovSkipAskingMobile=true; Domain=${parameters.get('cookie.domain')}; Path=/; Max-Age=2592000; SameSite=Strict; Secure; HttpOnly"
|
||||||
// setHeader doesn't support multiple headers with the same name, so we use
|
// setHeader doesn't support multiple headers with the same name, so we use
|
||||||
// a different one, and rewrite it in the proxy with Lua
|
// a different one, and rewrite it in the proxy with Lua
|
||||||
response.setHeader('Set-Cookie2', agovSkipAskingMobileCookie)
|
response.setHeader('Set-Cookie2', agovSkipAskingMobileCookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('done')
|
response.setResult('done')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (inargs['submit'] && inargs['mobile'] && !inargs['mobile'].isEmpty()) {
|
if (inargs['submit'] && inargs['mobile'] && !inargs['mobile'].isEmpty()) {
|
||||||
// IMPORTANT/haburger/2024-DEC-09: the pattern must be the same as ch.adnovum.agov.common.util.InputPatterns.PHONE_NUMBER_PATTERN
|
// IMPORTANT/haburger/2024-DEC-09: the pattern must be the same as ch.adnovum.agov.common.util.InputPatterns.PHONE_NUMBER_PATTERN
|
||||||
if (!inargs['mobile'].replaceAll('\\s', '').matches('^(?:\\+[0-9]+)?$')) {
|
if (!inargs['mobile'].replaceAll('\\s', '').matches('^(?:\\+[0-9]+)?$')) {
|
||||||
LOG.warn("Event='MOBILEFAILED', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='User provided invalid number (${inargs['mobile']})'")
|
LOG.warn("Event='MOBILEFAILED', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='User provided invalid number (${inargs['mobile']})'")
|
||||||
response.setResult('done')
|
response.setResult('done')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
String result
|
String result
|
||||||
// mobile is also stored without spaces
|
// mobile is also stored without spaces
|
||||||
def patchBdy = "{\"contacts\":{\"mobile\":\"${inargs['mobile'].replaceAll('\\s', '')}\"},\"modificationComment\":\"added mobile number from user during request ${requestId}\"}"
|
def patchBdy = "{\"contacts\":{\"mobile\":\"${inargs['mobile'].replaceAll('\\s', '')}\"},\"modificationComment\":\"added mobile number from user during request ${requestId}\"}"
|
||||||
try {
|
try {
|
||||||
result = idmRestClient.patch(endPoint, patchBdy)
|
result = idmRestClient.patch(endPoint, patchBdy)
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
LOG.warn("Event='MOBILEFAILED', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='failed to save number (${e})'")
|
LOG.warn("Event='MOBILEFAILED', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='failed to save number (${e})'")
|
||||||
}
|
}
|
||||||
response.setResult('done')
|
response.setResult('done')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// we should ask the user
|
// we should ask the user
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
|
|
|
@ -1,180 +1,180 @@
|
||||||
boolean isEnabled() {
|
boolean isEnabled() {
|
||||||
def paths = parameters.get("paths")
|
def paths = parameters.get("paths")
|
||||||
if (paths && !paths.isEmpty()) {
|
if (paths && !paths.isEmpty()) {
|
||||||
for (path in paths.split(',')) {
|
for (path in paths.split(',')) {
|
||||||
String url = request.currentResource
|
String url = request.currentResource
|
||||||
if (url.matches(path)) {
|
if (url.matches(path)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isLevel(String role) {
|
boolean isLevel(String role) {
|
||||||
if (role != null && role.isNumber()) {
|
if (role != null && role.isNumber()) {
|
||||||
def number = Integer.parseInt(role)
|
def number = Integer.parseInt(role)
|
||||||
if (number > 0 && number <= 9) {
|
if (number > 0 && number <= 9) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
int getCurrentLevel() {
|
int getCurrentLevel() {
|
||||||
int level = 1 // level 1 is reached by definition on successful authentication
|
int level = 1 // level 1 is reached by definition on successful authentication
|
||||||
// levels are stored as roles once the authentication is done
|
// levels are stored as roles once the authentication is done
|
||||||
for (String role : response.getActualRoles()) {
|
for (String role : response.getActualRoles()) {
|
||||||
if (isLevel(role)) {
|
if (isLevel(role)) {
|
||||||
Integer number = Integer.parseInt(role)
|
Integer number = Integer.parseInt(role)
|
||||||
if (number > level) {
|
if (number > level) {
|
||||||
level = number
|
level = number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG.debug("current level: $level")
|
LOG.debug("current level: $level")
|
||||||
return level
|
return level
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer getRequestedLevel() {
|
Integer getRequestedLevel() {
|
||||||
// try to determine required level based on SAML request (SP-initiated)
|
// try to determine required level based on SAML request (SP-initiated)
|
||||||
def context = session['ch.nevis.auth.saml.request.authnContextClassRef']
|
def context = session['ch.nevis.auth.saml.request.authnContextClassRef']
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
// this is expected for non-Nevis SAML partners
|
// this is expected for non-Nevis SAML partners
|
||||||
LOG.debug("unable to determine required authentication level: no AuthnContext")
|
LOG.debug("unable to determine required authentication level: no AuthnContext")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
String prefix = 'urn:nevis:level:'
|
String prefix = 'urn:nevis:level:'
|
||||||
Integer level = null
|
Integer level = null
|
||||||
if (context.contains(prefix)) {
|
if (context.contains(prefix)) {
|
||||||
def start = context.indexOf(prefix) // the prefix can appear anywhere in the context but only once
|
def start = context.indexOf(prefix) // the prefix can appear anywhere in the context but only once
|
||||||
def remainder = context.substring(start + prefix.length())
|
def remainder = context.substring(start + prefix.length())
|
||||||
for (String candidate : remainder.split(',')) {
|
for (String candidate : remainder.split(',')) {
|
||||||
if (!candidate.isNumber()) {
|
if (!candidate.isNumber()) {
|
||||||
continue // must be an actual role
|
continue // must be an actual role
|
||||||
}
|
}
|
||||||
def number = Integer.parseInt(candidate)
|
def number = Integer.parseInt(candidate)
|
||||||
if (level == null || number < level) {
|
if (level == null || number < level) {
|
||||||
level = number
|
level = number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (level == null) {
|
if (level == null) {
|
||||||
// an AuthnContext has been sent but it does not contain the required authentication level
|
// an AuthnContext has been sent but it does not contain the required authentication level
|
||||||
LOG.debug("unable to determine required authentication level from request: $context")
|
LOG.debug("unable to determine required authentication level from request: $context")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG.info("extracted required authentication level from request: $context -> $level")
|
LOG.info("extracted required authentication level from request: $context -> $level")
|
||||||
}
|
}
|
||||||
return level
|
return level
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer getRequiredLevel(levels, String issuer) {
|
Integer getRequiredLevel(levels, String issuer) {
|
||||||
// try to determine required level based on request
|
// try to determine required level based on request
|
||||||
def level = getRequestedLevel()
|
def level = getRequestedLevel()
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
LOG.info("required authentication level from request: $level")
|
LOG.info("required authentication level from request: $level")
|
||||||
return level
|
return level
|
||||||
}
|
}
|
||||||
// else determine required level based on configuration (IDP-initiated or no authnContextClassRef sent)
|
// else determine required level based on configuration (IDP-initiated or no authnContextClassRef sent)
|
||||||
if (issuer != null && levels.containsKey(issuer)) {
|
if (issuer != null && levels.containsKey(issuer)) {
|
||||||
level = levels[issuer]
|
level = levels[issuer]
|
||||||
LOG.debug("required authentication level for issuer $issuer defined as $level")
|
LOG.debug("required authentication level for issuer $issuer defined as $level")
|
||||||
return level
|
return level
|
||||||
}
|
}
|
||||||
// else return null
|
// else return null
|
||||||
LOG.debug("required authentication level for issuer $issuer is not defined")
|
LOG.debug("required authentication level for issuer $issuer is not defined")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
void setAuthnContext() {
|
void setAuthnContext() {
|
||||||
def parts = [] as Set
|
def parts = [] as Set
|
||||||
def authLevel = response.getAuthLevel()
|
def authLevel = response.getAuthLevel()
|
||||||
if (authLevel != null) {
|
if (authLevel != null) {
|
||||||
if (isLevel(authLevel)) {
|
if (isLevel(authLevel)) {
|
||||||
parts.add("urn:nevis:level:$authLevel")
|
parts.add("urn:nevis:level:$authLevel")
|
||||||
}
|
}
|
||||||
else { // might be legacy auth.weak / auth.strong
|
else { // might be legacy auth.weak / auth.strong
|
||||||
parts.add(authLevel)
|
parts.add(authLevel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (String role : response.getActualRoles()) {
|
for (String role : response.getActualRoles()) {
|
||||||
if (isLevel(role)) { // previous authLevels might have been added to the roles already
|
if (isLevel(role)) { // previous authLevels might have been added to the roles already
|
||||||
parts.add("urn:nevis:level:$role")
|
parts.add("urn:nevis:level:$role")
|
||||||
}
|
}
|
||||||
// levels can also be normal roles so we add them always
|
// levels can also be normal roles so we add them always
|
||||||
parts.add(role)
|
parts.add(role)
|
||||||
}
|
}
|
||||||
def value = parts.sort().join(",")
|
def value = parts.sort().join(",")
|
||||||
LOG.debug("calculated AuthnContextClassRef for SAML Response: $value")
|
LOG.debug("calculated AuthnContextClassRef for SAML Response: $value")
|
||||||
session['saml.idp.response.authncontext'] = value
|
session['saml.idp.response.authncontext'] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean stepupRequired(levels, String issuer) {
|
boolean stepupRequired(levels, String issuer) {
|
||||||
|
|
||||||
Integer requiredLevel = getRequiredLevel(levels, issuer)
|
Integer requiredLevel = getRequiredLevel(levels, issuer)
|
||||||
if (requiredLevel == null) {
|
if (requiredLevel == null) {
|
||||||
LOG.info("unable to determine required authentication level for request from issuer $issuer")
|
LOG.info("unable to determine required authentication level for request from issuer $issuer")
|
||||||
setAuthnContext()
|
setAuthnContext()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer currentLevel = getCurrentLevel()
|
Integer currentLevel = getCurrentLevel()
|
||||||
if (currentLevel >= requiredLevel) {
|
if (currentLevel >= requiredLevel) {
|
||||||
LOG.info("required authentication level $requiredLevel has been reached (current level $currentLevel)")
|
LOG.info("required authentication level $requiredLevel has been reached (current level $currentLevel)")
|
||||||
setAuthnContext()
|
setAuthnContext()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("required authentication level $requiredLevel has not been reached (current level $currentLevel) - session upgrade needed")
|
LOG.info("required authentication level $requiredLevel has not been reached (current level $currentLevel) - session upgrade needed")
|
||||||
request.setRequiredRoles("$requiredLevel")
|
request.setRequiredRoles("$requiredLevel")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasAnyRequiredRole(i2r, issuer) {
|
boolean hasAnyRequiredRole(i2r, issuer) {
|
||||||
if (issuer != null && i2r.containsKey(issuer)) {
|
if (issuer != null && i2r.containsKey(issuer)) {
|
||||||
def roles = i2r[issuer]
|
def roles = i2r[issuer]
|
||||||
for (role in response.getActualRoles()) {
|
for (role in response.getActualRoles()) {
|
||||||
if (roles.contains(role)) {
|
if (roles.contains(role)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
LOG.info("skipping SAML authorization checks.")
|
LOG.info("skipping SAML authorization checks.")
|
||||||
response.setResult('ok') // skip execution
|
response.setResult('ok') // skip execution
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// issuer set by IdentityProviderState (SP-initiated)
|
// issuer set by IdentityProviderState (SP-initiated)
|
||||||
def issuer = session['ch.nevis.auth.saml.request.issuer']
|
def issuer = session['ch.nevis.auth.saml.request.issuer']
|
||||||
|
|
||||||
// issuer to minimum required authentication level
|
// issuer to minimum required authentication level
|
||||||
def i2l = [:]
|
def i2l = [:]
|
||||||
|
|
||||||
|
|
||||||
if (stepupRequired(i2l, issuer)) {
|
if (stepupRequired(i2l, issuer)) {
|
||||||
LOG.info("authentication level stepup required.")
|
LOG.info("authentication level stepup required.")
|
||||||
response.setResult("stepup")
|
response.setResult("stepup")
|
||||||
return // we are done for now
|
return // we are done for now
|
||||||
}
|
}
|
||||||
|
|
||||||
// issuer to list of required roles
|
// issuer to list of required roles
|
||||||
def i2r = [:]
|
def i2r = [:]
|
||||||
|
|
||||||
|
|
||||||
// issuer to ResultCond name
|
// issuer to ResultCond name
|
||||||
def i2e = [:]
|
def i2e = [:]
|
||||||
i2e.put('https://trustbroker.agov-epr-lab.azure.adnovum.net', 'forbidden_0')
|
i2e.put('https://trustbroker.agov-epr-lab.azure.adnovum.net', 'forbidden_0')
|
||||||
i2e.put('https://trustbroker-idp.agov-epr-lab.azure.adnovum.net', 'forbidden_1')
|
i2e.put('https://trustbroker-idp.agov-epr-lab.azure.adnovum.net', 'forbidden_1')
|
||||||
|
|
||||||
|
|
||||||
if (!i2r.isEmpty() && !hasAnyRequiredRole(i2r, issuer)) {
|
if (!i2r.isEmpty() && !hasAnyRequiredRole(i2r, issuer)) {
|
||||||
LOG.info("required roles check failed.")
|
LOG.info("required roles check failed.")
|
||||||
response.setResult(i2e[issuer])
|
response.setResult(i2e[issuer])
|
||||||
return // we are done
|
return // we are done
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
|
@ -1,285 +1,285 @@
|
||||||
import org.codehaus.groovy.runtime.StackTraceUtils
|
import org.codehaus.groovy.runtime.StackTraceUtils
|
||||||
import groovy.xml.XmlSlurper
|
import groovy.xml.XmlSlurper
|
||||||
|
|
||||||
def getUserAGOVLoiRoles() {
|
def getUserAGOVLoiRoles() {
|
||||||
// we take the roles from actualRoles
|
// we take the roles from actualRoles
|
||||||
return request.getActualRoles().findAll { role -> role.startsWith('AGOV-Loi.') }.collect({ role -> role.substring(9) })
|
return request.getActualRoles().findAll { role -> role.startsWith('AGOV-Loi.') }.collect({ role -> role.substring(9) })
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserAGOVRecoveryRoles() {
|
def getUserAGOVRecoveryRoles() {
|
||||||
// set attibutes from DTO: -> AGOV
|
// set attibutes from DTO: -> AGOV
|
||||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
return list.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' }.collect({ node -> node.name.text() })
|
return list.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' }.collect({ node -> node.name.text() })
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserAGOVLoiIdVerification() {
|
def getUserAGOVLoiIdVerification() {
|
||||||
// set attibutes from DTO: -> idVerification
|
// set attibutes from DTO: -> idVerification
|
||||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text().contains('AGOV-Loi,')}.collect({ node -> node.value.text()})
|
return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text().contains('AGOV-Loi,')}.collect({ node -> node.value.text()})
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserAGOVLoiIdVerification(level) {
|
def getUserAGOVLoiIdVerification(level) {
|
||||||
// set attibutes from DTO: -> idVerification
|
// set attibutes from DTO: -> idVerification
|
||||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,level' + level}.collect({ node -> node.value.text()})
|
return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,level' + level}.collect({ node -> node.value.text()})
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserAGOVLoiValidFrom(level) {
|
def getUserAGOVLoiValidFrom(level) {
|
||||||
// set attibutes from DTO: -> validFrom
|
// set attibutes from DTO: -> validFrom
|
||||||
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}?.validFrom?.text()
|
return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}?.validFrom?.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserAGOVLoiValidTo(level) {
|
def getUserAGOVLoiValidTo(level) {
|
||||||
// set attibutes from DTO: -> validTo
|
// set attibutes from DTO: -> validTo
|
||||||
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}?.validTo?.text()
|
return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}?.validTo?.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserIdVerificationForRecovery() {
|
def getUserIdVerificationForRecovery() {
|
||||||
// application is AGOV-AccountStatus
|
// application is AGOV-AccountStatus
|
||||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
def result = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-AccountStatus,mustRecover'}?.value?.text()
|
def result = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-AccountStatus,mustRecover'}?.value?.text()
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// fallback if not explicitly set
|
// fallback if not explicitly set
|
||||||
def currentLoaRole = getUserAGOVLoiRoles()?.sort()?.last() ?: 'level100'
|
def currentLoaRole = getUserAGOVLoiRoles()?.sort()?.last() ?: 'level100'
|
||||||
def chDomicile = list.country.text() == 'ch'
|
def chDomicile = list.country.text() == 'ch'
|
||||||
def lastIdVerification = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + currentLoaRole}?.value?.text()
|
def lastIdVerification = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + currentLoaRole}?.value?.text()
|
||||||
switch (currentLoaRole) {
|
switch (currentLoaRole) {
|
||||||
case 'level100':
|
case 'level100':
|
||||||
result = chDomicile ? 'SimpleLetter' : 'Video'
|
result = chDomicile ? 'SimpleLetter' : 'Video'
|
||||||
break
|
break
|
||||||
case 'level200':
|
case 'level200':
|
||||||
result = chDomicile ? 'Bmid' : 'Video'
|
result = chDomicile ? 'Bmid' : 'Video'
|
||||||
break
|
break
|
||||||
case 'level300':
|
case 'level300':
|
||||||
case 'level400':
|
case 'level400':
|
||||||
result = chDomicile ? lastIdVerification : 'Video'
|
result = chDomicile ? lastIdVerification : 'Video'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
LOG.warn("unexpected loa on account: ${currentLoaRole}")
|
LOG.warn("unexpected loa on account: ${currentLoaRole}")
|
||||||
// safest default, should work in any case
|
// safest default, should work in any case
|
||||||
result = 'Video'
|
result = 'Video'
|
||||||
}
|
}
|
||||||
LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
|
LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber) {
|
def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber) {
|
||||||
def result = 'urn:qa.agov.ch:names:tc:ac:classes:'
|
def result = 'urn:qa.agov.ch:names:tc:ac:classes:'
|
||||||
|
|
||||||
switch (idVerification) {
|
switch (idVerification) {
|
||||||
case 'None':
|
case 'None':
|
||||||
result = result.concat('100')
|
result = result.concat('100')
|
||||||
break
|
break
|
||||||
case 'SimpleLetter':
|
case 'SimpleLetter':
|
||||||
result = result.concat('200')
|
result = result.concat('200')
|
||||||
break
|
break
|
||||||
case 'Video':
|
case 'Video':
|
||||||
case 'VideoSelfPaid':
|
case 'VideoSelfPaid':
|
||||||
case 'Bmid':
|
case 'Bmid':
|
||||||
case 'BmidSelfPaid':
|
case 'BmidSelfPaid':
|
||||||
case 'Counter':
|
case 'Counter':
|
||||||
result = result.concat((highestRoleLevelNumber == 400) ? '400' : '300')
|
result = result.concat((highestRoleLevelNumber == 400) ? '400' : '300')
|
||||||
break
|
break
|
||||||
case 'Eid':
|
case 'Eid':
|
||||||
result = result.concat('400')
|
result = result.concat('400')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
||||||
// safest default, should work in any case
|
// safest default, should work in any case
|
||||||
result = result.concat('' + highestRoleLevelNumber)
|
result = result.concat('' + highestRoleLevelNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserMustRecoverValidFrom() {
|
def getUserMustRecoverValidFrom() {
|
||||||
// set attibutes from DTO: -> validFrom
|
// set attibutes from DTO: -> validFrom
|
||||||
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
def authzNode = payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == 'mustRecover'}
|
def authzNode = payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == 'mustRecover'}
|
||||||
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accounting
|
// Accounting
|
||||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// beef
|
// beef
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
def highestRoleLevelNumber = 0
|
def highestRoleLevelNumber = 0
|
||||||
|
|
||||||
if (!session.get('agov.requestedRoleLevel')) {
|
if (!session.get('agov.requestedRoleLevel')) {
|
||||||
LOG.error("IDP: internal error: agov.requestedRoleLevel not set in session")
|
LOG.error("IDP: internal error: agov.requestedRoleLevel not set in session")
|
||||||
response.setResult('error');
|
response.setResult('error');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
|
def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
|
||||||
|
|
||||||
def authenticationMethod = session.get('authenticatedWith')
|
def authenticationMethod = session.get('authenticatedWith')
|
||||||
if (!authenticationMethod) {
|
if (!authenticationMethod) {
|
||||||
LOG.error("IDP: internal error: authenticationMethod not set in session")
|
LOG.error("IDP: internal error: authenticationMethod not set in session")
|
||||||
response.setResult('error');
|
response.setResult('error');
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// data transformations needed for SAML and OIDC
|
// data transformations needed for SAML and OIDC
|
||||||
// Transform sex to number
|
// Transform sex to number
|
||||||
if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
|
if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
|
||||||
s.setAttribute('ch.nevis.idm.User.gender', '1')
|
s.setAttribute('ch.nevis.idm.User.gender', '1')
|
||||||
}
|
}
|
||||||
if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
||||||
s.setAttribute('ch.nevis.idm.User.gender', '2')
|
s.setAttribute('ch.nevis.idm.User.gender', '2')
|
||||||
}
|
}
|
||||||
if(s.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
if(s.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
||||||
s.setAttribute('ch.nevis.idm.User.gender', '3')
|
s.setAttribute('ch.nevis.idm.User.gender', '3')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// handle accounts qa attributes, and set them in session
|
// handle accounts qa attributes, and set them in session
|
||||||
// account itself, only needed if not authenticated with e-ID
|
// account itself, only needed if not authenticated with e-ID
|
||||||
if (!'urn:qa.agov.ch:names:tc:authfactor:eid'.equalsIgnoreCase(authenticationMethod)) {
|
if (!'urn:qa.agov.ch:names:tc:authfactor:eid'.equalsIgnoreCase(authenticationMethod)) {
|
||||||
def idVerificationList = getUserAGOVLoiIdVerification()
|
def idVerificationList = getUserAGOVLoiIdVerification()
|
||||||
def idVerification = 'None'
|
def idVerification = 'None'
|
||||||
if (idVerificationList && !idVerificationList.isEmpty()) {
|
if (idVerificationList && !idVerificationList.isEmpty()) {
|
||||||
idVerification = idVerificationList.last()
|
idVerification = idVerificationList.last()
|
||||||
}
|
}
|
||||||
s.setAttribute('idVerification', idVerification)
|
s.setAttribute('idVerification', idVerification)
|
||||||
|
|
||||||
// contextClassRefToSet based on highest level-role assigned to default profile
|
// contextClassRefToSet based on highest level-role assigned to default profile
|
||||||
for (String role : getUserAGOVLoiRoles()) {
|
for (String role : getUserAGOVLoiRoles()) {
|
||||||
if (role.startsWith('level')) {
|
if (role.startsWith('level')) {
|
||||||
def roleLevel = role.substring(5)
|
def roleLevel = role.substring(5)
|
||||||
int roleLevelNumber = Integer.parseInt(roleLevel)
|
int roleLevelNumber = Integer.parseInt(roleLevel)
|
||||||
|
|
||||||
if (highestRoleLevelNumber< roleLevelNumber) {
|
if (highestRoleLevelNumber< roleLevelNumber) {
|
||||||
highestRoleLevelNumber=roleLevelNumber
|
highestRoleLevelNumber=roleLevelNumber
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.debug('CheckLoa: Highest role Level ' + highestRoleLevelNumber.toString() +' contextclassref ' + requestedRoleLevelNumber.toString())
|
LOG.debug('CheckLoa: Highest role Level ' + highestRoleLevelNumber.toString() +' contextclassref ' + requestedRoleLevelNumber.toString())
|
||||||
LOG.debug('CheckLoa: Compare ' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
|
LOG.debug('CheckLoa: Compare ' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
|
||||||
|
|
||||||
//set attribute Actual Role Level
|
//set attribute Actual Role Level
|
||||||
s.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
s.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
||||||
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
||||||
|
|
||||||
// set attribute ValidFrom and ValidTo (only for higher than 100)
|
// set attribute ValidFrom and ValidTo (only for higher than 100)
|
||||||
if (highestRoleLevelNumber > 100) {
|
if (highestRoleLevelNumber > 100) {
|
||||||
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
|
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
|
||||||
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
|
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
|
||||||
|
|
||||||
LOG.debug('CheckLoa: ValidFrom :' + validFrom)
|
LOG.debug('CheckLoa: ValidFrom :' + validFrom)
|
||||||
LOG.debug('CheckLoa: ValidTo :' + validTo)
|
LOG.debug('CheckLoa: ValidTo :' + validTo)
|
||||||
|
|
||||||
if(validFrom != '') {
|
if(validFrom != '') {
|
||||||
s.setAttribute('ValidFrom', '' + validFrom)
|
s.setAttribute('ValidFrom', '' + validFrom)
|
||||||
}
|
}
|
||||||
if(validTo != '') {
|
if(validTo != '') {
|
||||||
s.setAttribute('ValidTo', '' + validTo)
|
s.setAttribute('ValidTo', '' + validTo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (highestRoleLevelNumber > 0) {
|
if (highestRoleLevelNumber > 0) {
|
||||||
// set attribute contextClassRefToSet
|
// set attribute contextClassRefToSet
|
||||||
s.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
|
s.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
|
||||||
} else {
|
} else {
|
||||||
// by default 100
|
// by default 100
|
||||||
s.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' )
|
s.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// address related, needed in any case (also e-ID)
|
// address related, needed in any case (also e-ID)
|
||||||
def adressVerificationList = getUserAGOVLoiIdVerification('200')
|
def adressVerificationList = getUserAGOVLoiIdVerification('200')
|
||||||
def adressVerification = 'None'
|
def adressVerification = 'None'
|
||||||
if (adressVerificationList && !adressVerificationList.isEmpty()) {
|
if (adressVerificationList && !adressVerificationList.isEmpty()) {
|
||||||
adressVerification = adressVerificationList[0]
|
adressVerification = adressVerificationList[0]
|
||||||
}
|
}
|
||||||
s.setAttribute('agov.adressVerification', '' + adressVerification)
|
s.setAttribute('agov.adressVerification', '' + adressVerification)
|
||||||
|
|
||||||
if (!session.get('ch.adnovum.nevisidm.profileExtId')) {
|
if (!session.get('ch.adnovum.nevisidm.profileExtId')) {
|
||||||
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='Account without Profile', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='Account without Profile', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||||
|
|
||||||
// if the account has no profile, we must not return address or svnr
|
// if the account has no profile, we must not return address or svnr
|
||||||
s.setAttribute('agov.appAddressRequired', 'false')
|
s.setAttribute('agov.appAddressRequired', 'false')
|
||||||
s.setAttribute('agov.appSvnrAllowed', 'false')
|
s.setAttribute('agov.appSvnrAllowed', 'false')
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// no login for users with a recovery role (but onyl when not logging in with e-Id)
|
// no login for users with a recovery role (but onyl when not logging in with e-Id)
|
||||||
// TODO/haburger/2025-07-01: automatic recovery if logging in with e-Id
|
// TODO/haburger/2025-07-01: automatic recovery if logging in with e-Id
|
||||||
if (!'urn:qa.agov.ch:names:tc:authfactor:eid'.equalsIgnoreCase(authenticationMethod)) {
|
if (!'urn:qa.agov.ch:names:tc:authfactor:eid'.equalsIgnoreCase(authenticationMethod)) {
|
||||||
// no login for users with a recovery role
|
// no login for users with a recovery role
|
||||||
def recoveryRoleList = getUserAGOVRecoveryRoles()
|
def recoveryRoleList = getUserAGOVRecoveryRoles()
|
||||||
|
|
||||||
if (recoveryRoleList.contains('mustRecover')) {
|
if (recoveryRoleList.contains('mustRecover')) {
|
||||||
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
||||||
s.setAttribute('agov.recovery.authenticatedWith', session.get('authenticatedWith') ?: 'unknown' )
|
s.setAttribute('agov.recovery.authenticatedWith', session.get('authenticatedWith') ?: 'unknown' )
|
||||||
|
|
||||||
def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'
|
def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'
|
||||||
def idVerification = getUserIdVerificationForRecovery() ?: origIdVerification
|
def idVerification = getUserIdVerificationForRecovery() ?: origIdVerification
|
||||||
s.setAttribute('agov.recovery.currentIdVerification', '' + idVerification )
|
s.setAttribute('agov.recovery.currentIdVerification', '' + idVerification )
|
||||||
|
|
||||||
// align currentAgovAq with the method selected for idVerification
|
// align currentAgovAq with the method selected for idVerification
|
||||||
def currentAgovAqForRecovery = getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber)
|
def currentAgovAqForRecovery = getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber)
|
||||||
s.setAttribute('agov.recovery.currentAgovAq', '' + currentAgovAqForRecovery)
|
s.setAttribute('agov.recovery.currentAgovAq', '' + currentAgovAqForRecovery)
|
||||||
|
|
||||||
def validFrom = getUserMustRecoverValidFrom() ?: ''
|
def validFrom = getUserMustRecoverValidFrom() ?: ''
|
||||||
s.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + validFrom )
|
s.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + validFrom )
|
||||||
|
|
||||||
LOG.debug("CheckLoa: mustRecover: origIdVerification=${origIdVerification}, idVerification=${idVerification}, currentAgovAqForRecovery=${currentAgovAqForRecovery}")
|
LOG.debug("CheckLoa: mustRecover: origIdVerification=${origIdVerification}, idVerification=${idVerification}, currentAgovAqForRecovery=${currentAgovAqForRecovery}")
|
||||||
|
|
||||||
response.setResult('exit.2')
|
response.setResult('exit.2')
|
||||||
return
|
return
|
||||||
|
|
||||||
} else if (recoveryRoleList.contains('recovery')) {
|
} else if (recoveryRoleList.contains('recovery')) {
|
||||||
if (recoveryRoleList.contains('recoveryCascade')) {
|
if (recoveryRoleList.contains('recoveryCascade')) {
|
||||||
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recoveryCascade')
|
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recoveryCascade')
|
||||||
} else {
|
} else {
|
||||||
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
||||||
}
|
}
|
||||||
s.setAttribute('agov.recovery.authenticatedWith', session.get('authenticatedWith') ?: 'unknown')
|
s.setAttribute('agov.recovery.authenticatedWith', session.get('authenticatedWith') ?: 'unknown')
|
||||||
s.setAttribute('agov.recovery.currentAgovAq', session.get('contextClassRefToSet') ?: 'urn:qa.agov.ch:names:tc:ac:classes:100' )
|
s.setAttribute('agov.recovery.currentAgovAq', session.get('contextClassRefToSet') ?: 'urn:qa.agov.ch:names:tc:ac:classes:100' )
|
||||||
LOG.debug('CheckLoa: idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
|
LOG.debug('CheckLoa: idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
|
||||||
def idVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString())
|
def idVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString())
|
||||||
s.setAttribute('agov.recovery.currentIdVerification', (idVerification.isEmpty() ? 'None' : idVerification.first()))
|
s.setAttribute('agov.recovery.currentIdVerification', (idVerification.isEmpty() ? 'None' : idVerification.first()))
|
||||||
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString())) ?: ''
|
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString())) ?: ''
|
||||||
s.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', validFrom)
|
s.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', validFrom)
|
||||||
|
|
||||||
response.setResult('exit.2')
|
response.setResult('exit.2')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// authenticated with e-ID, we adjust highestRoleLevelNumber to e-ID login
|
// authenticated with e-ID, we adjust highestRoleLevelNumber to e-ID login
|
||||||
highestRoleLevelNumber = 500
|
highestRoleLevelNumber = 500
|
||||||
s.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
s.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
||||||
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
// verifiy that AQ level is high enough
|
// verifiy that AQ level is high enough
|
||||||
if (highestRoleLevelNumber>=requestedRoleLevelNumber) {
|
if (highestRoleLevelNumber>=requestedRoleLevelNumber) {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
// Insufficient_LoaInfo
|
// Insufficient_LoaInfo
|
||||||
response.setResult('exit.1');
|
response.setResult('exit.1');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='exception occured: ${ex}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='exception occured: ${ex}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||||
ex = StackTraceUtils.sanitize(ex)
|
ex = StackTraceUtils.sanitize(ex)
|
||||||
def affectedLines = ex.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
def affectedLines = ex.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
||||||
LOG.error("FATAL: Script failure (at lines: ${affectedLines})", ex)
|
LOG.error("FATAL: Script failure (at lines: ${affectedLines})", ex)
|
||||||
// AuthnFailed_Zero_RoleLvl
|
// AuthnFailed_Zero_RoleLvl
|
||||||
response.setResult('error');
|
response.setResult('error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,100 +1,100 @@
|
||||||
import groovy.json.JsonBuilder
|
import groovy.json.JsonBuilder
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
|
|
||||||
|
|
||||||
def getHeader(String name) {
|
def getHeader(String name) {
|
||||||
def inctx = request.getLoginContext()
|
def inctx = request.getLoginContext()
|
||||||
// case-insensitive lookup of HTTP headers
|
// case-insensitive lookup of HTTP headers
|
||||||
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
||||||
map.putAll(inctx)
|
map.putAll(inctx)
|
||||||
return map['connection.HttpHeader.' + name]
|
return map['connection.HttpHeader.' + name]
|
||||||
}
|
}
|
||||||
|
|
||||||
def clearFidoUAFSession() {
|
def clearFidoUAFSession() {
|
||||||
LOG.debug("start new FIDO UAF session (skipping ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']}")
|
LOG.debug("start new FIDO UAF session (skipping ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']}")
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
s.removeAttribute('ch.nevis.auth.fido.uaf.fidouafsessionid')
|
s.removeAttribute('ch.nevis.auth.fido.uaf.fidouafsessionid')
|
||||||
inargs.remove('fallback')
|
inargs.remove('fallback')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def clearIdmSessionAttributes() {
|
def clearIdmSessionAttributes() {
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
def sessionKeySet = new HashSet(session.keySet())
|
def sessionKeySet = new HashSet(session.keySet())
|
||||||
sessionKeySet.each { key ->
|
sessionKeySet.each { key ->
|
||||||
if ( key ==~ /ch.nevis.idm.*/ || key ==~ /ch.adnovum.nevisidm.*/ ) {
|
if ( key ==~ /ch.nevis.idm.*/ || key ==~ /ch.adnovum.nevisidm.*/ ) {
|
||||||
s.removeAttribute(key)
|
s.removeAttribute(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
// dispatch AJAX calls and form POST when operation is done
|
// dispatch AJAX calls and form POST when operation is done
|
||||||
if (inargs['fidoUafDone'] == 'true' ||
|
if (inargs['fidoUafDone'] == 'true' ||
|
||||||
inargs.containsKey('o.fidoUafSessionId.v') ||
|
inargs.containsKey('o.fidoUafSessionId.v') ||
|
||||||
getHeader('Content-Type') == 'application/json') {
|
getHeader('Content-Type') == 'application/json') {
|
||||||
|
|
||||||
if (inargs.containsKey('o.fidoUafSessionId.v') && (inargs['o.fidoUafSessionId.v'] != session['ch.nevis.auth.fido.uaf.fidouafsessionid'])) {
|
if (inargs.containsKey('o.fidoUafSessionId.v') && (inargs['o.fidoUafSessionId.v'] != session['ch.nevis.auth.fido.uaf.fidouafsessionid'])) {
|
||||||
// received polling for wrong fido session; make sure, that stops
|
// received polling for wrong fido session; make sure, that stops
|
||||||
LOG.debug("received polling for wrong fido session ${inargs['o.fidoUafSessionId.v']} (correct: ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']})")
|
LOG.debug("received polling for wrong fido session ${inargs['o.fidoUafSessionId.v']} (correct: ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']})")
|
||||||
def json = new JsonBuilder()
|
def json = new JsonBuilder()
|
||||||
json {
|
json {
|
||||||
"status" "unknown"
|
"status" "unknown"
|
||||||
"timestamp" org.joda.time.DateTime.now().toString()
|
"timestamp" org.joda.time.DateTime.now().toString()
|
||||||
}
|
}
|
||||||
String body = json.toString()
|
String body = json.toString()
|
||||||
|
|
||||||
response.setContent(body)
|
response.setContent(body)
|
||||||
response.setContentType('application/json')
|
response.setContentType('application/json')
|
||||||
response.setHttpStatusCode(200)
|
response.setHttpStatusCode(200)
|
||||||
response.setIsDirectResponse(true)
|
response.setIsDirectResponse(true)
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['fidoUafDone'] == 'true') {
|
if (inargs['fidoUafDone'] == 'true') {
|
||||||
// get clean state, before validating user in IDM
|
// get clean state, before validating user in IDM
|
||||||
LOG.debug("clear IDM session attributes")
|
LOG.debug("clear IDM session attributes")
|
||||||
clearIdmSessionAttributes()
|
clearIdmSessionAttributes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// continue with OutOfBandFidoUafAuthState
|
// continue with OutOfBandFidoUafAuthState
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch form post with fallback input field : transition to FIDO Token authentication
|
// dispatch form post with fallback input field : transition to FIDO Token authentication
|
||||||
if (inargs['fallback'] == 'fallback') {
|
if (inargs['fallback'] == 'fallback') {
|
||||||
sess.setAttribute("eid.placeholder.text", "Fido2 login not implemented yet")
|
sess.setAttribute("eid.placeholder.text", "Fido2 login not implemented yet")
|
||||||
response.setResult('fido2')
|
response.setResult('fido2')
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch to recovery
|
// dispatch to recovery
|
||||||
if (inargs['fallback'] == 'recovery') {
|
if (inargs['fallback'] == 'recovery') {
|
||||||
response.addOutArg('nevis.transfer.destination', parameters.get('recoveryurl'))
|
response.addOutArg('nevis.transfer.destination', parameters.get('recoveryurl'))
|
||||||
response.setStatus(ch.nevis.esauth.auth.engine.AuthResponse.AUTH_CONTINUE)
|
response.setStatus(ch.nevis.esauth.auth.engine.AuthResponse.AUTH_CONTINUE)
|
||||||
response.setIsRedirectTransfer(true)
|
response.setIsRedirectTransfer(true)
|
||||||
// Remove existing cookies before redirecting to RECOVERY
|
// Remove existing cookies before redirecting to RECOVERY
|
||||||
def agovRecoveryCookie = "agovRecovery=deleted; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict; Secure; HttpOnly"
|
def agovRecoveryCookie = "agovRecovery=deleted; Path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Strict; Secure; HttpOnly"
|
||||||
response.setHeader('Set-Cookie', agovRecoveryCookie)
|
response.setHeader('Set-Cookie', agovRecoveryCookie)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch form post with fallback input field : go to registration with right loa
|
// dispatch form post with fallback input field : go to registration with right loa
|
||||||
if (inargs['fallback'] == 'register') {
|
if (inargs['fallback'] == 'register') {
|
||||||
sess.setAttribute("eid.placeholder.text", "Registration should not be called here?")
|
sess.setAttribute("eid.placeholder.text", "Registration should not be called here?")
|
||||||
response.setResult('registration')
|
response.setResult('registration')
|
||||||
}
|
}
|
||||||
|
|
||||||
// cancel and go back to login
|
// cancel and go back to login
|
||||||
if (inargs['fallback'] == 'back') {
|
if (inargs['fallback'] == 'back') {
|
||||||
response.setResult('back')
|
response.setResult('back')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// dispatch form post with onReload input field : refresh QR-code FIDO UAF
|
// dispatch form post with onReload input field : refresh QR-code FIDO UAF
|
||||||
if (inargs.containsKey('onReload')) {
|
if (inargs.containsKey('onReload')) {
|
||||||
clearFidoUAFSession()
|
clearFidoUAFSession()
|
||||||
response.setResult('default')
|
response.setResult('default')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
if(outargs.containsKey('saml.SAMLResponse')) {
|
if(outargs.containsKey('saml.SAMLResponse')) {
|
||||||
// Accounting
|
// Accounting
|
||||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def credentialType = session['agov.authenticatedWith'] ?: 'unknown'
|
def credentialType = session['agov.authenticatedWith'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
LOG.info("Event='GOTOEIDLINKING', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
LOG.info("Event='GOTOEIDLINKING', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||||
|
|
||||||
// Redirect
|
// Redirect
|
||||||
response.addOutArg('nevis.transfer.destination', parameters.get('agovmedirecturl'))
|
response.addOutArg('nevis.transfer.destination', parameters.get('agovmedirecturl'))
|
||||||
response.addOutArg('nevis.transfer.field.SAMLResponse', outargs.getProperty('saml.SAMLResponse').bytes.encodeBase64().toString())
|
response.addOutArg('nevis.transfer.field.SAMLResponse', outargs.getProperty('saml.SAMLResponse').bytes.encodeBase64().toString())
|
||||||
response.setStatus(ch.nevis.esauth.auth.engine.AuthResponse.AUTH_CONTINUE)
|
response.setStatus(ch.nevis.esauth.auth.engine.AuthResponse.AUTH_CONTINUE)
|
||||||
response.setIsRedirectTransfer(false)
|
response.setIsRedirectTransfer(false)
|
||||||
|
|
||||||
response.removeOutArg('saml.SAMLResponse')
|
response.removeOutArg('saml.SAMLResponse')
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
}
|
}
|
|
@ -1,158 +1,159 @@
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import groovy.text.SimpleTemplateEngine
|
import groovy.text.SimpleTemplateEngine
|
||||||
|
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
|
|
||||||
def getDateWithoutTimestamp(String date){
|
def getDateWithoutTimestamp(String date){
|
||||||
def result = date
|
def result = date
|
||||||
if(date.matches('^[0-9-]+[+]{1}.*')){
|
if(date.matches('^[0-9-]+[+]{1}.*')){
|
||||||
result = date.replaceAll('[+]{1}.*', "")
|
result = date.replaceAll('[+]{1}.*', "")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE/aca/2025/06/19: We could also reload the data from idm after the update instead of updating the session variables manualy -> probably better and less error-prone
|
// NOTE/aca/2025/06/19: We could also reload the data from idm after the update instead of updating the session variables manualy -> probably better and less error-prone
|
||||||
def compareAndUpdateSessionVariables(sess, keys, isProperty){
|
def compareAndUpdateSessionVariables(sess, keys, isProperty){
|
||||||
def updatedKeys = []
|
def updatedKeys = []
|
||||||
for(key in keys){
|
for(key in keys){
|
||||||
def idmkey = isProperty ? "ch.nevis.idm.User.prop.$key" : "ch.nevis.idm.User.$key"
|
def idmkey = isProperty ? "ch.nevis.idm.User.prop.$key" : "ch.nevis.idm.User.$key"
|
||||||
def eidValue = session["agov.eid.User.$key"] ?: ""
|
def eidValue = session["agov.eid.User.$key"] ?: ""
|
||||||
def idmValue = session[idmkey] ?: ""
|
def idmValue = session[idmkey] ?: ""
|
||||||
if(!idmValue || eidValue != idmValue){
|
if(!idmValue || eidValue != idmValue){
|
||||||
sess.setAttribute(idmkey, eidValue)
|
sess.setAttribute(idmkey, eidValue)
|
||||||
updatedKeys.add(key)
|
updatedKeys.add(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return updatedKeys
|
return updatedKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO/haburger/2025-07-01: we should also set the verificationMethod, etc. of the level400 role
|
// TODO/haburger/2025-07-01: we should also set the verificationMethod, etc. of the level400 role
|
||||||
String user_update_dto_template = '''
|
String user_update_dto_template = '''
|
||||||
{
|
{
|
||||||
"name": {
|
"name": {
|
||||||
"firstName": "$firstName",
|
"firstName": "$firstName",
|
||||||
"familyName": "$familyName"
|
"familyName": "$familyName"
|
||||||
},
|
},
|
||||||
"properties": {
|
"properties": {
|
||||||
"svnr": "$svnr",
|
"svnr": "$svnr",
|
||||||
"placeOfBirth": "$placeOfBirth",
|
"placeOfBirth": "$placeOfBirth",
|
||||||
"nationality": "$nationality",
|
"nationality": "$nationality",
|
||||||
"eIdNumber": "$eIdNumber"
|
"eIdNumber": "$eIdNumber"
|
||||||
},
|
},
|
||||||
"gender": "$gender",
|
"gender": "$gender",
|
||||||
"birthDate": "$birthDate",
|
"birthDate": "$birthDate",
|
||||||
|
|
||||||
"modificationComment": "updated user information with eid attributes during request $request"
|
"modificationComment": "updated user information with eid attributes during request $request"
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
// Accounting
|
// Accounting
|
||||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
// Convert EID gender format to IDM
|
// Convert EID gender format to IDM
|
||||||
if(sess.get('agov.eid.User.gender') == '1'){
|
if(sess.get('agov.eid.User.gender') == '1'){
|
||||||
sess.setAttribute('agov.eid.User.gender', 'MALE')
|
sess.setAttribute('agov.eid.User.gender', 'MALE')
|
||||||
}
|
}
|
||||||
if(sess.get('agov.eid.User.gender') == '2'){
|
if(sess.get('agov.eid.User.gender') == '2'){
|
||||||
sess.setAttribute('agov.eid.User.gender', 'FEMALE')
|
sess.setAttribute('agov.eid.User.gender', 'FEMALE')
|
||||||
}
|
}
|
||||||
if(sess.get('agov.eid.User.gender') == '3'){
|
if(sess.get('agov.eid.User.gender') == '3'){
|
||||||
sess.setAttribute('agov.eid.User.gender', 'OTHER')
|
sess.setAttribute('agov.eid.User.gender', 'OTHER')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare eid and idm attributes + update idm session variables if they differ
|
// Compare eid and idm attributes + update idm session variables if they differ
|
||||||
def attributesToAudit = compareAndUpdateSessionVariables(sess, ["firstName", "lastName", "gender"], false)
|
def attributesToAudit = compareAndUpdateSessionVariables(sess, ["firstName", "lastName", "gender"], false)
|
||||||
// NOTE/aca/2025/06/14/: Potentally Throw a DATA ERROR if the properties are different? -> should the svnr number ever change?
|
// NOTE/aca/2025/06/14/: Potentally Throw a DATA ERROR if the properties are different? -> should the svnr number ever change?
|
||||||
def propertiesToAudit = compareAndUpdateSessionVariables(sess, ["svnr", "eIdNumber", "nationality", "placeOfBirth"], true)
|
def propertiesToAudit = compareAndUpdateSessionVariables(sess, ["svnr", "eIdNumber", "nationality", "placeOfBirth"], true)
|
||||||
|
|
||||||
|
|
||||||
// Handle birthdate seperately, since it can contain a timestamp -> we probably don't want to update if only the timestamp is wrong
|
// Handle birthdate seperately, since it can contain a timestamp -> we probably don't want to update if only the timestamp is wrong
|
||||||
String eidBirthdate = getDateWithoutTimestamp(session["agov.eid.User.birthDate"] ?: "")
|
String eidBirthdate = getDateWithoutTimestamp(session["agov.eid.User.birthDate"] ?: "")
|
||||||
String idmBirthdate = getDateWithoutTimestamp(session["ch.nevis.idm.User.birthDate"] ?: "")
|
String idmBirthdate = getDateWithoutTimestamp(session["ch.nevis.idm.User.birthDate"] ?: "")
|
||||||
LOG.debug("eidBirthdate: $eidBirthdate idmBirthdate: $idmBirthdate")
|
LOG.debug("eidBirthdate: $eidBirthdate idmBirthdate: $idmBirthdate")
|
||||||
if(eidBirthdate != idmBirthdate){
|
if(eidBirthdate != idmBirthdate){
|
||||||
sess.setAttribute("ch.nevis.idm.User.birthDate", eidBirthdate)
|
sess.setAttribute("ch.nevis.idm.User.birthDate", eidBirthdate)
|
||||||
// For some reson IdmGetPropertyState uses a different date format than IdmSetPropertyState?
|
// For some reson IdmGetPropertyState uses a different date format than IdmSetPropertyState?
|
||||||
//def date = new SimpleDateFormat('yyyy-MM-dd').parse(eidBirthdate)
|
//def date = new SimpleDateFormat('yyyy-MM-dd').parse(eidBirthdate)
|
||||||
//def idmFromatedBirthDate = new SimpleDateFormat('dd.MM.yyyy').format(date)
|
//def idmFromatedBirthDate = new SimpleDateFormat('dd.MM.yyyy').format(date)
|
||||||
//sess.setAttribute("ch.nevis.idm.User.birthDate.idmFormat", idmFromatedBirthDate)
|
//sess.setAttribute("ch.nevis.idm.User.birthDate.idmFormat", idmFromatedBirthDate)
|
||||||
attributesToAudit.add("birthDate")
|
attributesToAudit.add("birthDate")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to update IDM
|
// Check if we need to update IDM
|
||||||
def auditedRequired = attributesToAudit.size() > 0 || propertiesToAudit.size() > 0
|
def auditedRequired = attributesToAudit.size() > 0 || propertiesToAudit.size() > 0
|
||||||
|
|
||||||
if(auditedRequired){
|
if(auditedRequired){
|
||||||
// update attributes in idm & transition to User notification
|
// update attributes in idm & transition to User notification
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
|
|
||||||
String baseUrl = parameters.get("baseUrl")
|
String baseUrl = parameters.get("baseUrl")
|
||||||
String clientExtId = parameters.get("clientExtId")
|
String clientExtId = parameters.get("clientExtId")
|
||||||
String endPoint = "$baseUrl/api/core/v1"
|
String endPoint = "$baseUrl/api/core/v1"
|
||||||
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
|
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
|
||||||
|
|
||||||
String requestUrl = "$endPoint/$clientExtId/users/$userExtId"
|
String requestUrl = "$endPoint/$clientExtId/users/$userExtId"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def binding = [
|
def binding = [
|
||||||
"firstName": sess.getAttribute('agov.eid.User.firstName'),
|
"firstName": sess.getAttribute('agov.eid.User.firstName'),
|
||||||
"familyName": sess.getAttribute('agov.eid.User.lastName'),
|
"familyName": sess.getAttribute('agov.eid.User.lastName'),
|
||||||
"svnr": sess.getAttribute('agov.eid.User.svnr'),
|
"svnr": sess.getAttribute('agov.eid.User.svnr'),
|
||||||
"placeOfBirth": sess.getAttribute('agov.eid.User.placeOfBirth'),
|
"placeOfBirth": sess.getAttribute('agov.eid.User.placeOfBirth'),
|
||||||
"nationality": sess.getAttribute('agov.eid.User.nationality'),
|
"nationality": sess.getAttribute('agov.eid.User.nationality'),
|
||||||
"eIdNumber": sess.getAttribute('agov.eid.User.eIdNumber'),
|
"eIdNumber": sess.getAttribute('agov.eid.User.eIdNumber'),
|
||||||
"gender": sess.getAttribute('agov.eid.User.gender').toLowerCase(),
|
"gender": sess.getAttribute('agov.eid.User.gender').toLowerCase(),
|
||||||
"birthDate": sess.getAttribute('agov.eid.User.birthDate'),
|
"birthDate": sess.getAttribute('agov.eid.User.birthDate'),
|
||||||
"request": requestId
|
"request": requestId
|
||||||
]
|
]
|
||||||
|
|
||||||
def templateEngine = new SimpleTemplateEngine()
|
def templateEngine = new SimpleTemplateEngine()
|
||||||
def userUpdateDto = templateEngine.createTemplate(user_update_dto_template).make(binding).toString()
|
def userUpdateDto = templateEngine.createTemplate(user_update_dto_template).make(binding).toString()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
idmRestClient.patch(requestUrl, userUpdateDto)
|
idmRestClient.patch(requestUrl, userUpdateDto)
|
||||||
|
|
||||||
}catch(Exception e) {
|
}catch(Exception e) {
|
||||||
LOG.error("Failed to update User data in IDM: ${e}")
|
LOG.error("Failed to update User data in IDM: ${e}")
|
||||||
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to update User data in IDM'")
|
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to update User data in IDM'")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
String printKeys = attributesToAudit.toListString()
|
String printKeys = attributesToAudit.toListString()
|
||||||
LOG.debug("AuditedAttributes: $printKeys")
|
LOG.debug("AuditedAttributes: $printKeys")
|
||||||
|
|
||||||
// Transform gender back to number
|
// Transform gender back to number
|
||||||
if(sess.get('ch.nevis.idm.User.gender') == 'MALE'){
|
if(sess.get('ch.nevis.idm.User.gender') == 'MALE'){
|
||||||
sess.setAttribute('ch.nevis.idm.User.gender', '1')
|
sess.setAttribute('ch.nevis.idm.User.gender', '1')
|
||||||
}
|
}
|
||||||
if(sess.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
if(sess.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
||||||
sess.setAttribute('ch.nevis.idm.User.gender', '2')
|
sess.setAttribute('ch.nevis.idm.User.gender', '2')
|
||||||
}
|
}
|
||||||
if(sess.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
if(sess.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
||||||
sess.setAttribute('ch.nevis.idm.User.gender', '3')
|
sess.setAttribute('ch.nevis.idm.User.gender', '3')
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('audited')
|
response.setResult('audited')
|
||||||
}else{
|
}else{
|
||||||
// Attributes match & no notification needed => continue by updating the linking credential and sending the saml assertion
|
// Attributes match & no notification needed => continue by updating the linking credential and sending the saml assertion
|
||||||
// NOTE/aca/2025/06/19: We skip checking the account state, recovery code, mobile number and LoA
|
// NOTE/aca/2025/06/19: We skip checking the account state, recovery code, mobile number and LoA
|
||||||
LOG.debug("No Audit Required: Logging user in")
|
LOG.debug("No Audit Required: Logging user in")
|
||||||
response.setResult('noChange')
|
response.setResult('noChange')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,200 +1,201 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
import ch.nevis.idm.client.HTTPRequestWrapper
|
import ch.nevis.idm.client.HTTPRequestWrapper
|
||||||
|
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import groovy.json.JsonBuilder
|
import groovy.json.JsonBuilder
|
||||||
|
|
||||||
def getHeader(String name) {
|
def getHeader(String name) {
|
||||||
def inctx = request.getLoginContext()
|
def inctx = request.getLoginContext()
|
||||||
// case-insensitive lookup of HTTP headers
|
// case-insensitive lookup of HTTP headers
|
||||||
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
||||||
map.putAll(inctx)
|
map.putAll(inctx)
|
||||||
return map['connection.HttpHeader.' + name]
|
return map['connection.HttpHeader.' + name]
|
||||||
}
|
}
|
||||||
|
|
||||||
def clearEidSession(){
|
def clearEidSession(){
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
s.removeAttribute('agov.eid.verification')
|
s.removeAttribute('agov.eid.verification')
|
||||||
s.removeAttribute('agov.eid.verification.id')
|
s.removeAttribute('agov.eid.verification.id')
|
||||||
s.removeAttribute('agov.eid.verification.link')
|
s.removeAttribute('agov.eid.verification.link')
|
||||||
s.removeAttribute('agov.eid.linkedAccountsDto')
|
s.removeAttribute('agov.eid.linkedAccountsDto')
|
||||||
s.removeAttribute('agov.eid.User.birthDate')
|
s.removeAttribute('agov.eid.User.birthDate')
|
||||||
s.removeAttribute('agov.eid.User.eIdNumber')
|
s.removeAttribute('agov.eid.User.eIdNumber')
|
||||||
s.removeAttribute('agov.eid.User.firstName')
|
s.removeAttribute('agov.eid.User.firstName')
|
||||||
s.removeAttribute('agov.eid.User.lastName')
|
s.removeAttribute('agov.eid.User.lastName')
|
||||||
s.removeAttribute('agov.eid.User.gender')
|
s.removeAttribute('agov.eid.User.gender')
|
||||||
s.removeAttribute('agov.eid.User.nationality')
|
s.removeAttribute('agov.eid.User.nationality')
|
||||||
s.removeAttribute('agov.eid.User.placeOfBirth')
|
s.removeAttribute('agov.eid.User.placeOfBirth')
|
||||||
s.removeAttribute('agov.eid.User.svnr')
|
s.removeAttribute('agov.eid.User.svnr')
|
||||||
s.removeAttribute('agov.eid.User.origin')
|
s.removeAttribute('agov.eid.User.origin')
|
||||||
}
|
}
|
||||||
|
|
||||||
def getAccounts(json, String svnr) {
|
def getAccounts(json, String svnr) {
|
||||||
String svnrWithPrefix = "urn:ch-agov-eid:$svnr"
|
String svnrWithPrefix = "urn:ch-agov-eid:$svnr"
|
||||||
def idm_users_dto = json["Resources"]
|
def idm_users_dto = json["Resources"]
|
||||||
def accounts = [:]
|
def accounts = [:]
|
||||||
def frontend_dto = []
|
def frontend_dto = []
|
||||||
|
|
||||||
for(user in idm_users_dto){
|
for(user in idm_users_dto){
|
||||||
|
|
||||||
def credentials_dto = user["urn:nevis:idm:scim:schemas:v1:extension:User"]["credentials"]
|
def credentials_dto = user["urn:nevis:idm:scim:schemas:v1:extension:User"]["credentials"]
|
||||||
if(!credentials_dto){
|
if(!credentials_dto){
|
||||||
LOG.warn("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${extId}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='AGOV account has no credentials'")
|
LOG.warn("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${extId}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='AGOV account has no credentials'")
|
||||||
}
|
}
|
||||||
|
|
||||||
for(cred in credentials_dto){
|
for(cred in credentials_dto){
|
||||||
def foundCredential = false
|
def foundCredential = false
|
||||||
def extId = user["externalId"]
|
def extId = user["externalId"]
|
||||||
//TODO/aca/2025/06/11: Can we have multiple email adresses? -> if yes search for primary
|
//TODO/aca/2025/06/11: Can we have multiple email adresses? -> if yes search for primary
|
||||||
String email = user["emails"][0]["value"]
|
String email = user["emails"][0]["value"]
|
||||||
if(cred["type"] == "SAMLFEDERATION" && ( cred["issuerNameId"] == svnr || cred["issuerNameId"] == svnrWithPrefix )){
|
if(cred["type"] == "SAMLFEDERATION" && ( cred["issuerNameId"] == svnr || cred["issuerNameId"] == svnrWithPrefix )){
|
||||||
// we found more than one federation credential in one AGOV account -> Throw data error
|
// we found more than one federation credential in one AGOV account -> Throw data error
|
||||||
if(foundCredential){
|
if(foundCredential){
|
||||||
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${extId}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Multiple EId linking credentials found in one AGOV account'")
|
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${extId}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Multiple EId linking credentials found in one AGOV account'")
|
||||||
return [null,null]
|
return [null,null]
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract login info
|
// extract login info
|
||||||
def firstLogin = true
|
def firstLogin = true
|
||||||
if(cred["credentialLoginInfo"]){
|
if(cred["credentialLoginInfo"]){
|
||||||
if(cred["credentialLoginInfo"]["lastLogin"] && cred["credentialLoginInfo"]["lastLogin"] != ""){
|
if(cred["credentialLoginInfo"]["lastLogin"] && cred["credentialLoginInfo"]["lastLogin"] != ""){
|
||||||
firstLogin = false
|
firstLogin = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE/aca/2025/06/11: Assume that this is sanitized when registered.
|
//NOTE/aca/2025/06/11: Assume that this is sanitized when registered.
|
||||||
def accountName = cred['subjectNameId']
|
def accountName = cred['subjectNameId']
|
||||||
def credentialExtId = cred['extId']
|
def credentialExtId = cred['extId']
|
||||||
|
|
||||||
accounts.put(email, [ "extId": extId, "credentialExtId": cred['extId'], "firstLogin": firstLogin ] )
|
accounts.put(email, [ "extId": extId, "credentialExtId": cred['extId'], "firstLogin": firstLogin ] )
|
||||||
frontend_dto.add(["email": email, "description": accountName])
|
frontend_dto.add(["email": email, "description": accountName])
|
||||||
|
|
||||||
foundCredential=true
|
foundCredential=true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [ accounts, [ "accounts": frontend_dto ] ]
|
return [ accounts, [ "accounts": frontend_dto ] ]
|
||||||
}
|
}
|
||||||
|
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
|
|
||||||
// Accounting
|
// Accounting
|
||||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
|
|
||||||
if(inargs['submit'] && inargs['login'] && inargs['login'] != ''){
|
if(inargs['submit'] && inargs['login'] && inargs['login'] != ''){
|
||||||
LOG.debug("Account with email: ${inargs['login']} was selceted -> Continuing")
|
LOG.debug("Account with email: ${inargs['login']} was selceted -> Continuing")
|
||||||
|
|
||||||
def accounts = new JsonSlurper().parseText(session['agov.eid.linkedAccountsDto'])
|
def accounts = new JsonSlurper().parseText(session['agov.eid.linkedAccountsDto'])
|
||||||
def account = accounts.get( inargs['login'].trim() )
|
def account = accounts.get( inargs['login'].trim() )
|
||||||
|
|
||||||
sess.setAttribute('agov.eid.linkingCredentialExtId', account["credentialExtId"])
|
sess.setAttribute('agov.eid.linkingCredentialExtId', account["credentialExtId"])
|
||||||
sess.setAttribute('agov.eid.linkedAccountExtId', account["extId"])
|
sess.setAttribute('agov.eid.linkedAccountExtId', account["extId"])
|
||||||
|
|
||||||
if(account["firstLogin"]){
|
if(account["firstLogin"]){
|
||||||
response.setResult('firstLogin')
|
response.setResult('firstLogin')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inargs['cancelEid'] && inargs['cancelEid'] == 'cancel'){
|
if(inargs['cancelEid'] && inargs['cancelEid'] == 'cancel'){
|
||||||
LOG.debug("Account selection was canceled: back to initial login screen")
|
LOG.debug("Account selection was canceled: back to initial login screen")
|
||||||
clearEidSession()
|
clearEidSession()
|
||||||
response.setResult('backToVerification')
|
response.setResult('backToVerification')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(getHeader('Content-Type') == 'application/json'){
|
if(getHeader('Content-Type') == 'application/json'){
|
||||||
String account_selection_dto = session['agov.eid.linkedAccountsFrontendDto']
|
String account_selection_dto = session['agov.eid.linkedAccountsFrontendDto']
|
||||||
|
|
||||||
response.setContent(account_selection_dto.toString())
|
response.setContent(account_selection_dto.toString())
|
||||||
response.setContentType('application/json')
|
response.setContentType('application/json')
|
||||||
response.setHttpStatusCode(200)
|
response.setHttpStatusCode(200)
|
||||||
response.setIsDirectResponse(true)
|
response.setIsDirectResponse(true)
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
String baseUrl = parameters.get("baseUrl")
|
String baseUrl = parameters.get("baseUrl")
|
||||||
String clientExtId = parameters.get("clientExtId")
|
String clientExtId = parameters.get("clientExtId")
|
||||||
String endPoint = "$baseUrl/api/scim/v1/$clientExtId/Users"
|
String endPoint = "$baseUrl/api/scim/v1/$clientExtId/Users"
|
||||||
|
|
||||||
// Fetch account identifier
|
// Fetch account identifier
|
||||||
String svnr = sess.getAttribute("agov.eid.User.svnr")
|
String svnr = sess.getAttribute("agov.eid.User.svnr")
|
||||||
LOG.debug("search for accounts with SVNR: $svnr")
|
LOG.debug("search for accounts with SVNR: $svnr")
|
||||||
|
|
||||||
// Pepare GET request
|
// Pepare GET request
|
||||||
String attributes = "externalId,emails,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.type,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.issuerNameId,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.subjectNameId,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.extId,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.credentialLoginInfo.lastLogin"
|
String attributes = "externalId,emails,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.type,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.issuerNameId,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.subjectNameId,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.extId,urn:nevis:idm:scim:schemas:v1:extension:User.credentials.credentialLoginInfo.lastLogin"
|
||||||
String filter = "urn:nevis:idm:scim:schemas:v1:extension:User.credentials.type=='SAMLFEDERATION'%20AND%20%28%20urn:nevis:idm:scim:schemas:v1:extension:User.credentials.issuerNameId%20==%20'$svnr'%20OR%20urn:nevis:idm:scim:schemas:v1:extension:User.credentials.issuerNameId%20==%20'urn:ch-agov-eid:$svnr'%29"
|
String filter = "urn:nevis:idm:scim:schemas:v1:extension:User.credentials.type=='SAMLFEDERATION'%20AND%20%28%20urn:nevis:idm:scim:schemas:v1:extension:User.credentials.issuerNameId%20==%20'$svnr'%20OR%20urn:nevis:idm:scim:schemas:v1:extension:User.credentials.issuerNameId%20==%20'urn:ch-agov-eid:$svnr'%29"
|
||||||
|
|
||||||
String requestUrl = "$endPoint?count=20&attributes=$attributes&filter=$filter"
|
String requestUrl = "$endPoint?count=20&attributes=$attributes&filter=$filter"
|
||||||
|
|
||||||
String scimResponse
|
String scimResponse
|
||||||
try {
|
try {
|
||||||
|
|
||||||
scimResponse = idmRestClient.get(requestUrl)
|
scimResponse = idmRestClient.get(requestUrl)
|
||||||
|
|
||||||
//TODO/aca/2025/06/11: Fetch more pages if more than 20 entries have been found
|
//TODO/aca/2025/06/11: Fetch more pages if more than 20 entries have been found
|
||||||
|
|
||||||
LOG.debug("SCIM Response: $scimResponse")
|
LOG.debug("SCIM Response: $scimResponse")
|
||||||
|
|
||||||
def json = new JsonSlurper().parseText(scimResponse)
|
def json = new JsonSlurper().parseText(scimResponse)
|
||||||
def (accounts, frontend_dto) = getAccounts(json, svnr)
|
def (accounts, frontend_dto) = getAccounts(json, svnr)
|
||||||
|
|
||||||
// unrecoverable DATA ERROR happend
|
// unrecoverable DATA ERROR happend
|
||||||
if(accounts == null){
|
if(accounts == null){
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def numAccounts = accounts.size()
|
def numAccounts = accounts.size()
|
||||||
|
|
||||||
LOG.debug("Linked accounts found: " + frontend_dto.toString())
|
LOG.debug("Linked accounts found: " + frontend_dto.toString())
|
||||||
|
|
||||||
if(numAccounts == 0){
|
if(numAccounts == 0){
|
||||||
// No account found => show account linking dialog options
|
// No account found => show account linking dialog options
|
||||||
response.setResult('noAccount')
|
response.setResult('noAccount')
|
||||||
return
|
return
|
||||||
}else if(numAccounts == 1){
|
}else if(numAccounts == 1){
|
||||||
// One account found -> continue with loading attributes from idm (+ notification if it is the first login)
|
// One account found -> continue with loading attributes from idm (+ notification if it is the first login)
|
||||||
def account = accounts.values().first()
|
def account = accounts.values().first()
|
||||||
sess.setAttribute('agov.eid.linkingCredentialExtId', account["credentialExtId"])
|
sess.setAttribute('agov.eid.linkingCredentialExtId', account["credentialExtId"])
|
||||||
sess.setAttribute('agov.eid.linkedAccountExtId', account["extId"])
|
sess.setAttribute('agov.eid.linkedAccountExtId', account["extId"])
|
||||||
|
|
||||||
if(account["firstLogin"]){
|
if(account["firstLogin"]){
|
||||||
response.setResult('firstLogin')
|
response.setResult('firstLogin')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}else{
|
}else{
|
||||||
// Multiple accounts found -> Dispatch the account selection screen
|
// Multiple accounts found -> Dispatch the account selection screen
|
||||||
sess.setAttribute('agov.eid.linkedAccountsDto', new JsonBuilder(accounts).toString())
|
sess.setAttribute('agov.eid.linkedAccountsDto', new JsonBuilder(accounts).toString())
|
||||||
sess.setAttribute('agov.eid.linkedAccountsFrontendDto', new JsonBuilder(frontend_dto).toString())
|
sess.setAttribute('agov.eid.linkedAccountsFrontendDto', new JsonBuilder(frontend_dto).toString())
|
||||||
|
|
||||||
LOG.debug("Show GUI")
|
LOG.debug("Show GUI")
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
LOG.error("Fetching Agov Accounts Failed: ${e}")
|
LOG.error("Fetching Agov Accounts Failed: ${e}")
|
||||||
sess.setAttribute("eid.placeholder.text", "EId: An exception occured while fetching the AGOV accounts\n: ${e}")
|
sess.setAttribute("eid.placeholder.text", "EId: An exception occured while fetching the AGOV accounts\n: ${e}")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
|
|
||||||
|
|
||||||
String user_notification_dto = '''
|
String user_notification_dto = '''
|
||||||
{
|
{
|
||||||
"clientExtId": "{{clientExtId}}",
|
"clientExtId": "{{clientExtId}}",
|
||||||
"userExtId": "{{userExtId}}",
|
"userExtId": "{{userExtId}}",
|
||||||
"notificationType": "userNotification4",
|
"notificationType": "userNotification4",
|
||||||
"sendingMethod": [
|
"sendingMethod": [
|
||||||
"Email"
|
"Email"
|
||||||
],
|
],
|
||||||
"async": false
|
"async": false
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
String baseUrl = parameters.get("baseUrl")
|
String baseUrl = parameters.get("baseUrl")
|
||||||
String clientExtId = parameters.get("clientExtId")
|
String clientExtId = parameters.get("clientExtId")
|
||||||
String endPoint = "$baseUrl/api/notification/v1/"
|
String endPoint = "$baseUrl/api/notification/v1/"
|
||||||
|
|
||||||
String userExtId = sess.getAttribute("agov.eid.linkedAccountExtId")
|
String userExtId = sess.getAttribute("agov.eid.linkedAccountExtId")
|
||||||
|
|
||||||
String restRequest = user_notification_dto.replaceAll("\\{\\{clientExtId}}", clientExtId).replaceAll("\\{\\{userExtId}}", userExtId)
|
String restRequest = user_notification_dto.replaceAll("\\{\\{clientExtId}}", clientExtId).replaceAll("\\{\\{userExtId}}", userExtId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
idmRestClient.post(endPoint, restRequest)
|
idmRestClient.post(endPoint, restRequest)
|
||||||
|
|
||||||
}catch(Exception e) {
|
}catch(Exception e) {
|
||||||
LOG.error("Failed to send User Notification: First Login: ${e}")
|
LOG.error("Failed to send User Notification: First Login: ${e}")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
|
@ -1,38 +1,38 @@
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
|
|
||||||
|
|
||||||
String user_notification_dto = '''
|
String user_notification_dto = '''
|
||||||
{
|
{
|
||||||
"clientExtId": "{{clientExtId}}",
|
"clientExtId": "{{clientExtId}}",
|
||||||
"userExtId": "{{userExtId}}",
|
"userExtId": "{{userExtId}}",
|
||||||
"notificationType": "userNotification3",
|
"notificationType": "userNotification3",
|
||||||
"sendingMethod": [
|
"sendingMethod": [
|
||||||
"Email"
|
"Email"
|
||||||
],
|
],
|
||||||
"async": false
|
"async": false
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
String baseUrl = parameters.get("baseUrl")
|
String baseUrl = parameters.get("baseUrl")
|
||||||
String clientExtId = parameters.get("clientExtId")
|
String clientExtId = parameters.get("clientExtId")
|
||||||
String endPoint = "$baseUrl/api/notification/v1/"
|
String endPoint = "$baseUrl/api/notification/v1/"
|
||||||
|
|
||||||
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
|
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
|
||||||
|
|
||||||
String restRequest = user_notification_dto.replaceAll("\\{\\{clientExtId}}", clientExtId).replaceAll("\\{\\{userExtId}}", userExtId)
|
String restRequest = user_notification_dto.replaceAll("\\{\\{clientExtId}}", clientExtId).replaceAll("\\{\\{userExtId}}", userExtId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
idmRestClient.post(endPoint, restRequest)
|
idmRestClient.post(endPoint, restRequest)
|
||||||
|
|
||||||
}catch(Exception e) {
|
}catch(Exception e) {
|
||||||
LOG.error("Failed to send User Notification: Idm Update with EId data: ${e}")
|
LOG.error("Failed to send User Notification: Idm Update with EId data: ${e}")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
|
@ -1,17 +1,17 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
|
|
||||||
if(inargs['cancel']){
|
if(inargs['cancel']){
|
||||||
LOG.debug("Account registration canceled: Send response with error")
|
LOG.debug("Account registration canceled: Send response with error")
|
||||||
response.setResult('back')
|
response.setResult('back')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inargs['register'] == "agov"){
|
if(inargs['register'] == "agov"){
|
||||||
LOG.debug("AGOV account registration was selected")
|
LOG.debug("AGOV account registration was selected")
|
||||||
response.setResult('register')
|
response.setResult('register')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.debug("Show GUI")
|
LOG.debug("Show GUI")
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
|
@ -1,27 +1,27 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
|
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
if(inargs['cancelEid']){
|
if(inargs['cancelEid']){
|
||||||
LOG.debug("Account registration canceled: Send response with error")
|
LOG.debug("Account registration canceled: Send response with error")
|
||||||
response.setResult('cancel')
|
response.setResult('cancel')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inargs['continue'] == 'link_account'){
|
if(inargs['continue'] == 'link_account'){
|
||||||
LOG.debug("AGOV account linking")
|
LOG.debug("AGOV account linking")
|
||||||
//sess.setAttribute("eid.placeholder.text", "EId: Implicit account linking not implemented yet")
|
//sess.setAttribute("eid.placeholder.text", "EId: Implicit account linking not implemented yet")
|
||||||
response.setResult('link')
|
response.setResult('link')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inargs['continue'] == 'register_account'){
|
if(inargs['continue'] == 'register_account'){
|
||||||
LOG.debug("AGOV account registration was selected")
|
LOG.debug("AGOV account registration was selected")
|
||||||
sess.setAttribute("eid.placeholder.text", "EId: Account registration with implicit linking not implemented yet")
|
sess.setAttribute("eid.placeholder.text", "EId: Account registration with implicit linking not implemented yet")
|
||||||
response.setResult('register')
|
response.setResult('register')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.debug("Show GUI")
|
LOG.debug("Show GUI")
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
|
@ -1,36 +1,36 @@
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
|
|
||||||
|
|
||||||
String login_info_update_dto = '''
|
String login_info_update_dto = '''
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"credentialExtId": "{{credentialExtId}}"
|
"credentialExtId": "{{credentialExtId}}"
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
String baseUrl = parameters.get("baseUrl")
|
String baseUrl = parameters.get("baseUrl")
|
||||||
String clientExtId = parameters.get("clientExtId")
|
String clientExtId = parameters.get("clientExtId")
|
||||||
String endPoint = "$baseUrl/api/core/v1"
|
String endPoint = "$baseUrl/api/core/v1"
|
||||||
|
|
||||||
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
|
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
|
||||||
String linkingCredentialExtId = sess.getAttribute("agov.eid.linkingCredentialExtId")
|
String linkingCredentialExtId = sess.getAttribute("agov.eid.linkingCredentialExtId")
|
||||||
|
|
||||||
String requestUrl = "$endPoint/$clientExtId/users/$userExtId/login-info"
|
String requestUrl = "$endPoint/$clientExtId/users/$userExtId/login-info"
|
||||||
|
|
||||||
String restRequest = login_info_update_dto.replaceAll("\\{\\{credentialExtId}}", linkingCredentialExtId)
|
String restRequest = login_info_update_dto.replaceAll("\\{\\{credentialExtId}}", linkingCredentialExtId)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
idmRestClient.post(requestUrl, restRequest)
|
idmRestClient.post(requestUrl, restRequest)
|
||||||
|
|
||||||
}catch(Exception e) {
|
}catch(Exception e) {
|
||||||
LOG.error("Failed to Update Linking Credential info: ${e}")
|
LOG.error("Failed to Update Linking Credential info: ${e}")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
|
@ -1,455 +1,456 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
import ch.nevis.esauth.sess.Session
|
import ch.nevis.esauth.sess.Session
|
||||||
import ch.nevis.esauth.util.httpclient.api.HttpClient
|
import ch.nevis.esauth.util.httpclient.api.HttpClient
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import io.opentelemetry.api.trace.Span
|
import io.opentelemetry.api.trace.Span
|
||||||
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.time.ZoneOffset
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
import com.fasterxml.uuid.Generators
|
import com.fasterxml.uuid.Generators
|
||||||
|
|
||||||
def getHeader(String name) {
|
def getHeader(String name) {
|
||||||
def inctx = request.getLoginContext()
|
def inctx = request.getLoginContext()
|
||||||
// case-insensitive lookup of HTTP headers
|
// case-insensitive lookup of HTTP headers
|
||||||
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
||||||
map.putAll(inctx)
|
map.putAll(inctx)
|
||||||
return map['connection.HttpHeader.' + name]
|
return map['connection.HttpHeader.' + name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true on success and false on failure
|
// returns true on success and false on failure
|
||||||
def getNewVerification(Session sess, HttpClient httpClient, String verification_request_template, String traceparent){
|
def getNewVerification(Session sess, HttpClient httpClient, String verification_request_template, String traceparent){
|
||||||
// Initialize the verification session on the verifier
|
// Initialize the verification session on the verifier
|
||||||
def endPoint = "${parameters.get('eidVerifierBaseUrl')}/api/v1/verifications"
|
def endPoint = "${parameters.get('eidVerifierBaseUrl')}/api/v1/verifications"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
def httpResponse = Http.post()
|
def httpResponse = Http.post()
|
||||||
.url(endPoint)
|
.url(endPoint)
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.header("traceparent", traceparent)
|
.header("traceparent", traceparent)
|
||||||
.entity(Http.entity()
|
.entity(Http.entity()
|
||||||
.content(verification_request_template.replaceAll("\\{\\{UUID}}", UUID.randomUUID().toString()))
|
.content(verification_request_template.replaceAll("\\{\\{UUID}}", UUID.randomUUID().toString()))
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.build())
|
.build())
|
||||||
.build()
|
.build()
|
||||||
.send(httpClient)
|
.send(httpClient)
|
||||||
|
|
||||||
|
|
||||||
if (httpResponse.code() != 200) {
|
if (httpResponse.code() != 200) {
|
||||||
LOG.debug("Result: ${httpResponse}")
|
LOG.debug("Result: ${httpResponse}")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
def json = new JsonSlurper().parseText(httpResponse.bodyAsString())
|
def json = new JsonSlurper().parseText(httpResponse.bodyAsString())
|
||||||
LOG.debug("Result: ${json}")
|
LOG.debug("Result: ${json}")
|
||||||
|
|
||||||
sess.setAttribute('agov.eid.verification', 'true')
|
sess.setAttribute('agov.eid.verification', 'true')
|
||||||
sess.setAttribute('agov.eid.verification.id', json.id)
|
sess.setAttribute('agov.eid.verification.id', json.id)
|
||||||
sess.setAttribute('agov.eid.verification.link', json.verification_url)
|
sess.setAttribute('agov.eid.verification.link', json.verification_url)
|
||||||
|
|
||||||
|
|
||||||
// TODO/aca/2025-04-04:This could probably also be INITIATED, once the verifier supports this status
|
// TODO/aca/2025-04-04:This could probably also be INITIATED, once the verifier supports this status
|
||||||
if (json.state != 'PENDING') {
|
if (json.state != 'PENDING') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
LOG.error("Eid verification failed: $e")
|
LOG.error("Eid verification failed: $e")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
def clearEidSession(){
|
def clearEidSession(){
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
s.removeAttribute('agov.eid.verification')
|
s.removeAttribute('agov.eid.verification')
|
||||||
s.removeAttribute('agov.eid.verification.id')
|
s.removeAttribute('agov.eid.verification.id')
|
||||||
s.removeAttribute('agov.eid.verification.link')
|
s.removeAttribute('agov.eid.verification.link')
|
||||||
}
|
}
|
||||||
|
|
||||||
def verification_request_template = '''
|
def verification_request_template = '''
|
||||||
{ "presentation_definition": {
|
{ "presentation_definition": {
|
||||||
"id": "{{UUID}}",
|
"id": "{{UUID}}",
|
||||||
"name": "AGOV Verification",
|
"name": "AGOV Verification",
|
||||||
"purpose": "AGOV Login",
|
"purpose": "AGOV Login",
|
||||||
"format": {
|
"format": {
|
||||||
"vc+sd-jwt": {
|
"vc+sd-jwt": {
|
||||||
"sd-jwt_alg_values": [
|
"sd-jwt_alg_values": [
|
||||||
"ES256"
|
"ES256"
|
||||||
],
|
],
|
||||||
"kb-jwt_alg_values": [
|
"kb-jwt_alg_values": [
|
||||||
"ES256"
|
"ES256"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"input_descriptors": [
|
"input_descriptors": [
|
||||||
{
|
{
|
||||||
"id": "agov-all-attributes",
|
"id": "agov-all-attributes",
|
||||||
"name": "AGOV Identity Verification",
|
"name": "AGOV Identity Verification",
|
||||||
"purpose": "verification and authentication",
|
"purpose": "verification and authentication",
|
||||||
"format": {
|
"format": {
|
||||||
"vc+sd-jwt": {
|
"vc+sd-jwt": {
|
||||||
"sd-jwt_alg_values": [
|
"sd-jwt_alg_values": [
|
||||||
"ES256"
|
"ES256"
|
||||||
],
|
],
|
||||||
"kb-jwt_alg_values": [
|
"kb-jwt_alg_values": [
|
||||||
"ES256"
|
"ES256"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"constraints": {
|
"constraints": {
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.family_name"
|
"$.family_name"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.given_name"
|
"$.given_name"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.birth_date"
|
"$.birth_date"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.sex"
|
"$.sex"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.place_of_origin"
|
"$.place_of_origin"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.birth_place"
|
"$.birth_place"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.nationality"
|
"$.nationality"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.personal_administrative_number"
|
"$.personal_administrative_number"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.document_number"
|
"$.document_number"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.issuance_date"
|
"$.issuance_date"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.expiry_date"
|
"$.expiry_date"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.issuing_authority"
|
"$.issuing_authority"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
"$.issuing_country"
|
"$.issuing_country"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def ERROR_CODE_TO_STATUS_MAPPER = [
|
def ERROR_CODE_TO_STATUS_MAPPER = [
|
||||||
'CREDENTIAL_INVALID' : 'FAILED',
|
'CREDENTIAL_INVALID' : 'FAILED',
|
||||||
'JWT_EXPIRED' : 'ERROR',
|
'JWT_EXPIRED' : 'ERROR',
|
||||||
'INVALID_FORMAT' : 'ERROR',
|
'INVALID_FORMAT' : 'ERROR',
|
||||||
'CREDENTIAL_EXPIRED' : 'FAILED',
|
'CREDENTIAL_EXPIRED' : 'FAILED',
|
||||||
'MISSING_NONCE' : 'ERROR',
|
'MISSING_NONCE' : 'ERROR',
|
||||||
'UNSUPPORTED_FORMAT' : 'ERROR',
|
'UNSUPPORTED_FORMAT' : 'ERROR',
|
||||||
'CREDENTIAL_REVOKED' : 'FAILED',
|
'CREDENTIAL_REVOKED' : 'FAILED',
|
||||||
'CREDENTIAL_SUSPENDED' : 'FAILED',
|
'CREDENTIAL_SUSPENDED' : 'FAILED',
|
||||||
'HOLDER_BINDING_MISMATCH' : 'ERROR',
|
'HOLDER_BINDING_MISMATCH' : 'ERROR',
|
||||||
'CREDENTIAL_MISSING_DATA' : 'FAILED',
|
'CREDENTIAL_MISSING_DATA' : 'FAILED',
|
||||||
'UNRESOLVABLE_STATUS_LIST' : 'ERROR',
|
'UNRESOLVABLE_STATUS_LIST' : 'ERROR',
|
||||||
'PUBLIC_KEY_OF_ISSUER_UNRESOLVABLE': 'ERROR',
|
'PUBLIC_KEY_OF_ISSUER_UNRESOLVABLE': 'ERROR',
|
||||||
'CLIENT_REJECTED' : 'CANCELED',
|
'CLIENT_REJECTED' : 'CANCELED',
|
||||||
'ISSUER_NOT_ACCEPTED' : 'ERROR'
|
'ISSUER_NOT_ACCEPTED' : 'ERROR'
|
||||||
]
|
]
|
||||||
|
|
||||||
// ---------------
|
// ---------------
|
||||||
// check, whether we are still processing the correct AuthnRequest
|
// check, whether we are still processing the correct AuthnRequest
|
||||||
// or if the frontend requested a timeout
|
// or if the frontend requested a timeout
|
||||||
if ( (inargs.containsKey('authRequestId') && (inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) || inargs['oid4vp'] == 'TIMEOUT') {
|
if ( (inargs.containsKey('authRequestId') && (inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) || inargs['oid4vp'] == 'TIMEOUT') {
|
||||||
// wrong request, "force" a timeout
|
// wrong request, "force" a timeout
|
||||||
LOG.debug('authentication timeout enforced, due to concurrent requests (authRequestId missmatch) -> return a 408')
|
LOG.debug('authentication timeout enforced, due to concurrent requests (authRequestId missmatch) -> return a 408')
|
||||||
|
|
||||||
response.setIsDirectResponse(true)
|
response.setIsDirectResponse(true)
|
||||||
response.setContentType('text/html; charset=UTF-8')
|
response.setContentType('text/html; charset=UTF-8')
|
||||||
response.setContent('Timeout')
|
response.setContent('Timeout')
|
||||||
response.setHttpStatusCode(205)
|
response.setHttpStatusCode(205)
|
||||||
response.setHeader('IDP-AUTH', 'Timeout')
|
response.setHeader('IDP-AUTH', 'Timeout')
|
||||||
|
|
||||||
// CONTINUE to keep the other request beeing processed
|
// CONTINUE to keep the other request beeing processed
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
if (inargs['oid4vp'] == 'ERROR') {
|
if (inargs['oid4vp'] == 'ERROR') {
|
||||||
LOG.debug("oid4vp error")
|
LOG.debug("oid4vp error")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['oid4vp'] == 'SUCCEEDED') {
|
if (inargs['oid4vp'] == 'SUCCEEDED') {
|
||||||
LOG.debug("oid4vp succeeded")
|
LOG.debug("oid4vp succeeded")
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch to access App
|
// switch to access App
|
||||||
if (inargs['accessApp'] == 'accessApp') {
|
if (inargs['accessApp'] == 'accessApp') {
|
||||||
//TODO/aca/2025/06/19: In theory we could also land here when we send 'SUCCESS' to the frontend -> would be better to clear all session vaiables that can be set in this Authstate
|
//TODO/aca/2025/06/19: In theory we could also land here when we send 'SUCCESS' to the frontend -> would be better to clear all session vaiables that can be set in this Authstate
|
||||||
//TODO/aca/2025/06/19: Should we here rather set the LOGINMETHOD cookie and send an error assertion, since otherwise we might swich states too often and Nevis will kill the session?
|
//TODO/aca/2025/06/19: Should we here rather set the LOGINMETHOD cookie and send an error assertion, since otherwise we might swich states too often and Nevis will kill the session?
|
||||||
clearEidSession()
|
clearEidSession()
|
||||||
LOG.debug("Switch to Access App")
|
LOG.debug("Switch to Access App")
|
||||||
sess.setAttribute('agov.lastLoginMethod', 'accessApp')
|
sess.setAttribute('agov.lastLoginMethod', 'accessApp')
|
||||||
response.setResult('agovLogin')
|
response.setResult('agovLogin')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch to fido2
|
// switch to fido2
|
||||||
if (inargs['securityKey'] == 'securityKey') {
|
if (inargs['securityKey'] == 'securityKey') {
|
||||||
clearEidSession()
|
clearEidSession()
|
||||||
LOG.debug("Switch to Security Key")
|
LOG.debug("Switch to Security Key")
|
||||||
sess.setAttribute('agov.lastLoginMethod', 'securityKey')
|
sess.setAttribute('agov.lastLoginMethod', 'securityKey')
|
||||||
response.setResult('agovLogin')
|
response.setResult('agovLogin')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// switch to registration
|
// switch to registration
|
||||||
if (inargs['fallback'] == 'register') {
|
if (inargs['fallback'] == 'register') {
|
||||||
clearEidSession()
|
clearEidSession()
|
||||||
LOG.debug("Switch to registration")
|
LOG.debug("Switch to registration")
|
||||||
response.setResult('register')
|
response.setResult('register')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
HttpClient httpClient = HttpClients.create(parameters)
|
HttpClient httpClient = HttpClients.create(parameters)
|
||||||
def spanCtxt = Span.current().getSpanContext()
|
def spanCtxt = Span.current().getSpanContext()
|
||||||
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
||||||
|
|
||||||
|
|
||||||
if (getHeader('Content-Type') == 'application/json' && inargs.containsKey('o.id.v')) {
|
if (getHeader('Content-Type') == 'application/json' && inargs.containsKey('o.id.v')) {
|
||||||
LOG.debug("Request Status Update")
|
LOG.debug("Request Status Update")
|
||||||
// request for a status update from the verifier
|
// request for a status update from the verifier
|
||||||
def result
|
def result
|
||||||
|
|
||||||
// FE requested a new verification
|
// FE requested a new verification
|
||||||
if (inargs['o.id.v'] == 'NEW' || inargs['o.id.v'] == 'RESET') {
|
if (inargs['o.id.v'] == 'NEW' || inargs['o.id.v'] == 'RESET') {
|
||||||
LOG.debug("Initializing new verification")
|
LOG.debug("Initializing new verification")
|
||||||
if(!getNewVerification(sess, httpClient, verification_request_template, traceparent)){
|
if(!getNewVerification(sess, httpClient, verification_request_template, traceparent)){
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def idvalue = (!inargs['o.id.v'] || inargs['o.id.v'] == 'NEW' || inargs['o.id.v'] == 'RESET') ? session['agov.eid.verification.id'] : inargs['o.id.v']
|
def idvalue = (!inargs['o.id.v'] || inargs['o.id.v'] == 'NEW' || inargs['o.id.v'] == 'RESET') ? session['agov.eid.verification.id'] : inargs['o.id.v']
|
||||||
|
|
||||||
LOG.error("IDValSent: " + idvalue)
|
LOG.error("IDValSent: " + idvalue)
|
||||||
|
|
||||||
// check, whether we are still processing the same verification request or if a new one was generated in e.g. another Tab
|
// check, whether we are still processing the same verification request or if a new one was generated in e.g. another Tab
|
||||||
if(inargs['o.id.v'] && inargs['o.id.v'] != 'NEW' && inargs['o.id.v'] != 'RESET' && inargs['o.id.v'] != session['agov.eid.verification.id']){
|
if(inargs['o.id.v'] && inargs['o.id.v'] != 'NEW' && inargs['o.id.v'] != 'RESET' && inargs['o.id.v'] != session['agov.eid.verification.id']){
|
||||||
// wrong request, tell fe to stop polling and request a timeout
|
// wrong request, tell fe to stop polling and request a timeout
|
||||||
LOG.debug('authentication timeout enforced, due to concurrent requests (verificationRequest missmatch) -> Notify FE & then return a 408')
|
LOG.debug('authentication timeout enforced, due to concurrent requests (verificationRequest missmatch) -> Notify FE & then return a 408')
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "TIMEOUT",
|
"status": "TIMEOUT",
|
||||||
"verification_url": "${session['agov.eid.verification.link']}",
|
"verification_url": "${session['agov.eid.verification.link']}",
|
||||||
"id": "${idvalue}",
|
"id": "${idvalue}",
|
||||||
"error_code": "REQUEST-MISMATCH",
|
"error_code": "REQUEST-MISMATCH",
|
||||||
"error_message": "Request Mismatch Detected: Forcing Timeout"
|
"error_message": "Request Mismatch Detected: Forcing Timeout"
|
||||||
}}"""
|
}}"""
|
||||||
|
|
||||||
response.setContent(result.toString())
|
response.setContent(result.toString())
|
||||||
response.setContentType('application/json')
|
response.setContentType('application/json')
|
||||||
response.setHttpStatusCode(200)
|
response.setHttpStatusCode(200)
|
||||||
response.setIsDirectResponse(true)
|
response.setIsDirectResponse(true)
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
def endPoint = "${parameters.get('eidVerifierBaseUrl')}/api/v1/verifications/${idvalue}"
|
def endPoint = "${parameters.get('eidVerifierBaseUrl')}/api/v1/verifications/${idvalue}"
|
||||||
|
|
||||||
def httpResponse = Http.get()
|
def httpResponse = Http.get()
|
||||||
.url(endPoint)
|
.url(endPoint)
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.header("traceparent", traceparent)
|
.header("traceparent", traceparent)
|
||||||
.build()
|
.build()
|
||||||
.send(httpClient)
|
.send(httpClient)
|
||||||
|
|
||||||
|
|
||||||
// 404 -> request a new verification
|
// 404 -> request a new verification
|
||||||
if(httpResponse.code() == 404){
|
if(httpResponse.code() == 404){
|
||||||
// Frontend should know that we are starting a new request and not recieve an error
|
// Frontend should know that we are starting a new request and not recieve an error
|
||||||
def status = "FAILED"
|
def status = "FAILED"
|
||||||
// Delete session variable to start a new verification
|
// Delete session variable to start a new verification
|
||||||
sess.removeAttribute('agov.eid.verification')
|
sess.removeAttribute('agov.eid.verification')
|
||||||
|
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "${status}",
|
"status": "${status}",
|
||||||
"verification_url": "",
|
"verification_url": "",
|
||||||
"id": "",
|
"id": "",
|
||||||
"error_code": "HTTP-ERROR",
|
"error_code": "HTTP-ERROR",
|
||||||
"error_message": "Faild to verify status of verification, http status: ${httpResponse.code()}"
|
"error_message": "Faild to verify status of verification, http status: ${httpResponse.code()}"
|
||||||
}}"""
|
}}"""
|
||||||
LOG.warn("<== Response: ${httpResponse.code()}")
|
LOG.warn("<== Response: ${httpResponse.code()}")
|
||||||
}
|
}
|
||||||
else if (httpResponse.code() != 200) {
|
else if (httpResponse.code() != 200) {
|
||||||
LOG.debug("Result: ${httpResponse}")
|
LOG.debug("Result: ${httpResponse}")
|
||||||
|
|
||||||
def status = "ERROR"
|
def status = "ERROR"
|
||||||
|
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "${status}",
|
"status": "${status}",
|
||||||
"verification_url": "${session['agov.eid.verification.link']}",
|
"verification_url": "${session['agov.eid.verification.link']}",
|
||||||
"id": "${idvalue}",
|
"id": "${idvalue}",
|
||||||
"error_code": "HTTP-ERROR",
|
"error_code": "HTTP-ERROR",
|
||||||
"error_message": "failed to verify status of verification ${idvalue}, http status: ${httpResponse.code()}"
|
"error_message": "failed to verify status of verification ${idvalue}, http status: ${httpResponse.code()}"
|
||||||
}}"""
|
}}"""
|
||||||
LOG.warn("<== Response: ${httpResponse.code()}")
|
LOG.warn("<== Response: ${httpResponse.code()}")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
def json = new JsonSlurper().parseText(httpResponse.bodyAsString())
|
def json = new JsonSlurper().parseText(httpResponse.bodyAsString())
|
||||||
LOG.debug(httpResponse.bodyAsString())
|
LOG.debug(httpResponse.bodyAsString())
|
||||||
if (json.state == 'SUCCESS') {
|
if (json.state == 'SUCCESS') {
|
||||||
def claims = json.wallet_response.credential_subject_data
|
def claims = json.wallet_response.credential_subject_data
|
||||||
LOG.debug("Store user data in session")
|
LOG.debug("Store user data in session")
|
||||||
|
|
||||||
def validFrom = LocalDate.parse(claims.issuance_date, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
def validFrom = LocalDate.parse(claims.issuance_date, DateTimeFormatter.ISO_LOCAL_DATE).atStartOfDay(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||||
def validTo = LocalDate.parse(claims.expiry_date, DateTimeFormatter.ISO_LOCAL_DATE).atTime(23,59,59).atOffset(ZoneOffset.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
def validTo = LocalDate.parse(claims.expiry_date, DateTimeFormatter.ISO_LOCAL_DATE).atTime(23,59,59).atOffset(ZoneOffset.systemDefault()).format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)
|
||||||
|
|
||||||
sess.setAttribute('agov.eid.User.firstName', claims.given_name)
|
sess.setAttribute('agov.eid.User.firstName', claims.given_name)
|
||||||
sess.setAttribute('agov.eid.User.lastName', claims.family_name)
|
sess.setAttribute('agov.eid.User.lastName', claims.family_name)
|
||||||
sess.setAttribute('agov.eid.User.birthDate', claims.birth_date)
|
sess.setAttribute('agov.eid.User.birthDate', claims.birth_date)
|
||||||
sess.setAttribute('agov.eid.User.gender', claims.sex)
|
sess.setAttribute('agov.eid.User.gender', claims.sex)
|
||||||
sess.setAttribute('agov.eid.User.svnr', claims.personal_administrative_number.replace('.',''))
|
sess.setAttribute('agov.eid.User.svnr', claims.personal_administrative_number.replace('.',''))
|
||||||
sess.setAttribute('agov.eid.User.placeOfBirth', claims.birth_place)
|
sess.setAttribute('agov.eid.User.placeOfBirth', claims.birth_place)
|
||||||
sess.setAttribute('agov.eid.User.placeOfOrigin', claims.place_of_origin)
|
sess.setAttribute('agov.eid.User.placeOfOrigin', claims.place_of_origin)
|
||||||
sess.setAttribute('agov.eid.User.eIdNumber', claims.document_number)
|
sess.setAttribute('agov.eid.User.eIdNumber', claims.document_number)
|
||||||
// Simpler for later comparison -> Is converted again to upper case in the saml assertion
|
// Simpler for later comparison -> Is converted again to upper case in the saml assertion
|
||||||
sess.setAttribute('agov.eid.User.nationality', claims.nationality.toString().toLowerCase())
|
sess.setAttribute('agov.eid.User.nationality', claims.nationality.toString().toLowerCase())
|
||||||
|
|
||||||
sess.setAttribute('ValidFrom', validFrom)
|
sess.setAttribute('ValidFrom', validFrom)
|
||||||
sess.setAttribute('ValidTo', validTo)
|
sess.setAttribute('ValidTo', validTo)
|
||||||
sess.setAttribute('authenticatedWith', "urn:qa.agov.ch:names:tc:authfactor:eid")
|
sess.setAttribute('authenticatedWith', "urn:qa.agov.ch:names:tc:authfactor:eid")
|
||||||
sess.setAttribute('idVerification', "Eid")
|
sess.setAttribute('idVerification', "Eid")
|
||||||
|
|
||||||
// BUNDBITBK-5203 Dynamic aq levels
|
// BUNDBITBK-5203 Dynamic aq levels
|
||||||
def requestedRoleLevel = session['agov.requestedRoleLevel']
|
def requestedRoleLevel = session['agov.requestedRoleLevel']
|
||||||
if(requestedRoleLevel == "600"){
|
if(requestedRoleLevel == "600"){
|
||||||
sess.setAttribute('contextClassRefToSet', "urn:qa.agov.ch:names:tc:ac:classes:600")
|
sess.setAttribute('contextClassRefToSet', "urn:qa.agov.ch:names:tc:ac:classes:600")
|
||||||
}else{
|
}else{
|
||||||
sess.setAttribute('contextClassRefToSet', "urn:qa.agov.ch:names:tc:ac:classes:500")
|
sess.setAttribute('contextClassRefToSet', "urn:qa.agov.ch:names:tc:ac:classes:500")
|
||||||
}
|
}
|
||||||
|
|
||||||
// subjectUUID v5
|
// subjectUUID v5
|
||||||
def namespace = UUID.fromString(parameters.get('eidUUIDNamespace'))
|
def namespace = UUID.fromString(parameters.get('eidUUIDNamespace'))
|
||||||
def uuid = Generators.nameBasedGenerator(namespace).generate(claims.personal_administrative_number)
|
def uuid = Generators.nameBasedGenerator(namespace).generate(claims.personal_administrative_number)
|
||||||
LOG.debug("UUID derived from svnr: ${uuid}")
|
LOG.debug("UUID derived from svnr: ${uuid}")
|
||||||
String uuidString = uuid.toString()
|
String uuidString = uuid.toString()
|
||||||
sess.setAttribute('agov.subjectUUID', '' + uuidString)
|
sess.setAttribute('agov.subjectUUID', '' + uuidString)
|
||||||
|
|
||||||
response.setUserId(uuidString)
|
response.setUserId(uuidString)
|
||||||
sess.setAttribute('ch.adnovum.nevisidm.user.extId', uuidString)
|
sess.setAttribute('ch.adnovum.nevisidm.user.extId', uuidString)
|
||||||
response.setLoginId(claims.document_number)
|
response.setLoginId(claims.document_number)
|
||||||
response.setAuthLevel("EID")
|
response.setAuthLevel("EID")
|
||||||
|
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "SUCCEEDED",
|
"status": "SUCCEEDED",
|
||||||
"verification_url": "${session['agov.eid.verification.link']}",
|
"verification_url": "${session['agov.eid.verification.link']}",
|
||||||
"id": "${idvalue}",
|
"id": "${idvalue}",
|
||||||
"error_code": "NONE"
|
"error_code": "NONE"
|
||||||
}}"""
|
}}"""
|
||||||
}
|
}
|
||||||
else if (json.state == 'FAILED') {
|
else if (json.state == 'FAILED') {
|
||||||
LOG
|
LOG
|
||||||
.error("Eid verification failed: ${json.wallet_response.error_code} (${json.wallet_response.error_description})")
|
.error("Eid verification failed: ${json.wallet_response.error_code} (${json.wallet_response.error_description})")
|
||||||
|
|
||||||
def status = ERROR_CODE_TO_STATUS_MAPPER[json.wallet_response.error_code] ?: 'ERROR'
|
def status = ERROR_CODE_TO_STATUS_MAPPER[json.wallet_response.error_code] ?: 'ERROR'
|
||||||
|
|
||||||
// Send new request & return variables with new id and url
|
// Send new request & return variables with new id and url
|
||||||
if(status == 'FAILED' || status == 'CANCELED'){
|
if(status == 'FAILED' || status == 'CANCELED'){
|
||||||
// Delete session variable to start a new verification
|
// Delete session variable to start a new verification
|
||||||
sess.removeAttribute('agov.eid.verification')
|
sess.removeAttribute('agov.eid.verification')
|
||||||
|
|
||||||
// Clear variables for for a cleaner result
|
// Clear variables for for a cleaner result
|
||||||
sess.removeAttribute('agov.eid.verification.link')
|
sess.removeAttribute('agov.eid.verification.link')
|
||||||
}
|
}
|
||||||
|
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "${status}",
|
"status": "${status}",
|
||||||
"verification_url": "${session['agov.eid.verification.link']}",
|
"verification_url": "${session['agov.eid.verification.link']}",
|
||||||
"id": "${idvalue}",
|
"id": "${idvalue}",
|
||||||
"error_code": "${json.wallet_response.error_code}",
|
"error_code": "${json.wallet_response.error_code}",
|
||||||
"error_message": "${json.wallet_response.error_description}"
|
"error_message": "${json.wallet_response.error_description}"
|
||||||
}}"""
|
}}"""
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "${inargs['o.id.v'] == 'NEW' || inargs['o.id.v'] == 'RESET' ? 'INITIATED' : 'PENDING'}",
|
"status": "${inargs['o.id.v'] == 'NEW' || inargs['o.id.v'] == 'RESET' ? 'INITIATED' : 'PENDING'}",
|
||||||
"verification_url": "${session['agov.eid.verification.link']}",
|
"verification_url": "${session['agov.eid.verification.link']}",
|
||||||
"id": "${idvalue}",
|
"id": "${idvalue}",
|
||||||
"error_code": "NONE"
|
"error_code": "NONE"
|
||||||
}}"""
|
}}"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
LOG.error("Eid verification failed: ${e}")
|
LOG.error("Eid verification failed: ${e}")
|
||||||
result = """{
|
result = """{
|
||||||
"oid4vp": {
|
"oid4vp": {
|
||||||
"status": "ERROR",
|
"status": "ERROR",
|
||||||
"verification_url": "${session['agov.eid.verification.link']}",
|
"verification_url": "${session['agov.eid.verification.link']}",
|
||||||
"id": "${idvalue}",
|
"id": "${idvalue}",
|
||||||
"error_code": "HTTP-ERROR",
|
"error_code": "HTTP-ERROR",
|
||||||
"error_message": "failed to verify status of verification ${idvalue}, http exception"
|
"error_message": "failed to verify status of verification ${idvalue}, http exception"
|
||||||
}}"""
|
}}"""
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setContent(result.toString())
|
response.setContent(result.toString())
|
||||||
response.setContentType('application/json')
|
response.setContentType('application/json')
|
||||||
response.setHttpStatusCode(200)
|
response.setHttpStatusCode(200)
|
||||||
response.setIsDirectResponse(true)
|
response.setIsDirectResponse(true)
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach this place, display GUI
|
// if we reach this place, display GUI
|
||||||
LOG.debug("Show GUI")
|
LOG.debug("Show GUI")
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -122,4 +122,4 @@ if (inargs['submit']) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// show the GUI
|
// show the GUI
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
|
|
|
@ -13,8 +13,9 @@ JAVA_OPTS=(
|
||||||
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
||||||
"-Dotel.javaagent.logging=application"
|
"-Dotel.javaagent.logging=application"
|
||||||
"-Dotel.javaagent.configuration-file=/var/opt/nevisauth/default/conf/otel.properties"
|
"-Dotel.javaagent.configuration-file=/var/opt/nevisauth/default/conf/otel.properties"
|
||||||
"-Dotel.resource.attributes=service.version=8.2411.3,service.instance.id=$HOSTNAME"
|
"-Dotel.resource.attributes=service.version=8.2505.5,service.instance.id=$HOSTNAME"
|
||||||
"-Djavax.net.ssl.trustStore=/var/opt/keys/trust/auth-idp-extended-truststore/truststore.p12"
|
"-Djavax.net.ssl.trustStore=/var/opt/keys/trust/auth-idp-extended-truststore/truststore.p12"
|
||||||
"-Djavax.net.ssl.trustStorePassword=\${exec:/var/opt/keys/trust/auth-idp-extended-truststore/keypass}"
|
"-Djavax.net.ssl.trustStorePassword=\${exec:/var/opt/keys/trust/auth-idp-extended-truststore/keypass}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<SessionCoordinator sessionInitialInactivityTimeout="600" sessionInactivityTimeout="28800" sessionMaxLifetime="28800" sessionIdPreGenerate="true">
|
<SessionCoordinator sessionInitialInactivityTimeout="600" sessionInactivityTimeout="28800" sessionMaxLifetime="28800" sessionIdPreGenerate="true">
|
||||||
<!-- source: pattern://7022472ae407577ae604bbb8 -->
|
<!-- source: pattern://7022472ae407577ae604bbb8 -->
|
||||||
<LocalSessionStore maxSessions="100000"/>
|
<LocalSessionStore maxSessions="100000"/>
|
||||||
|
<!-- source: pattern://b7b59e97b3fd18bb60178573 -->
|
||||||
|
<RemoteSessionStore connectionUser="pipe:///var/opt/nevisauth/default/conf/credentials/dbUser" connectionPassword="pipe:///var/opt/nevisauth/default/conf/credentials/dbPassword" connectionUrl="jdbc:mariadb://mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat:3306/nevisauth?serverTimezone=UTC&sslMode=disable&autocommit=true" connectionMaxLifeTime="1800000" connectionMaxIdleTime="600000" connectionMinPoolSize="10" connectionMaxPoolSize="10" connectionAutomaticDbSchemaSetup="false" storeUnauthenticatedSessions="true"/>
|
||||||
<!-- source: pattern://7022472ae407577ae604bbb8 -->
|
<!-- source: pattern://7022472ae407577ae604bbb8 -->
|
||||||
<TokenAssembler name="DefaultTokenAssembler">
|
<TokenAssembler name="DefaultTokenAssembler">
|
||||||
<Selector default="true"/>
|
<Selector default="true"/>
|
||||||
|
@ -45,6 +47,8 @@
|
||||||
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
<field src="session" key="ch.adnovum.nevisidm.clientId" as="clientId"/>
|
<field src="session" key="ch.adnovum.nevisidm.clientId" as="clientId"/>
|
||||||
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
|
<field src="session" key="ch.nevis.session.domain" as="domain"/>
|
||||||
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
<field src="request" key="ActualRoles" as="roles"/>
|
<field src="request" key="ActualRoles" as="roles"/>
|
||||||
</TokenSpec>
|
</TokenSpec>
|
||||||
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
|
@ -65,6 +69,8 @@
|
||||||
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
<field src="session" key="ch.adnovum.nevisidm.clientId" as="clientId"/>
|
<field src="session" key="ch.adnovum.nevisidm.clientId" as="clientId"/>
|
||||||
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
|
<field src="session" key="ch.nevis.session.domain" as="domain"/>
|
||||||
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
<field src="request" key="ActualRoles" as="roles"/>
|
<field src="request" key="ActualRoles" as="roles"/>
|
||||||
</TokenSpec>
|
</TokenSpec>
|
||||||
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
<!-- source: pattern://94e0b7b92ff2593f958c1eec -->
|
||||||
|
@ -128,6 +134,11 @@
|
||||||
<!-- source: pattern://8dbec5bb024707d73fca93ef -->
|
<!-- source: pattern://8dbec5bb024707d73fca93ef -->
|
||||||
<KeyObject name="https://trustbroker-idp.agov-w.azure.adnovum.net" certificate="/var/opt/keys/trust/idp-pem-atb/truststore.jks"/>
|
<KeyObject name="https://trustbroker-idp.agov-w.azure.adnovum.net" certificate="/var/opt/keys/trust/idp-pem-atb/truststore.jks"/>
|
||||||
</KeyStore>
|
</KeyStore>
|
||||||
|
<!-- source: pattern://b09a3092a59797b317c06ae4 -->
|
||||||
|
<KeyStore name="EncryptionKeys">
|
||||||
|
<!-- source: pattern://b09a3092a59797b317c06ae4 -->
|
||||||
|
<KeyObject name="DefaultEncryptionKey" certificate="/var/opt/keys/trust/idp-pem-atb-enc/truststore.jks"/>
|
||||||
|
</KeyStore>
|
||||||
<!-- source: pattern://cb8c63274fe346280de0ffd5 -->
|
<!-- source: pattern://cb8c63274fe346280de0ffd5 -->
|
||||||
<KeyStore name="Auth_Realm_Mobile_FIDO_UAFKeyStore">
|
<KeyStore name="Auth_Realm_Mobile_FIDO_UAFKeyStore">
|
||||||
<!-- source: pattern://cb8c63274fe346280de0ffd5 -->
|
<!-- source: pattern://cb8c63274fe346280de0ffd5 -->
|
||||||
|
@ -146,8 +157,8 @@
|
||||||
<KeyObject name="internal_tls_Truststore" certificate="/var/opt/keys/trust/env-ca/truststore.jks"/>
|
<KeyObject name="internal_tls_Truststore" certificate="/var/opt/keys/trust/env-ca/truststore.jks"/>
|
||||||
</KeyStore>
|
</KeyStore>
|
||||||
</SessionCoordinator>
|
</SessionCoordinator>
|
||||||
<!-- source: pattern://7022472ae407577ae604bbb8 -->
|
<!-- source: pattern://b7b59e97b3fd18bb60178573 -->
|
||||||
<LocalOutOfContextDataStore reaperPeriod="60"/>
|
<RemoteOutOfContextDataStore connectionUser="pipe:///var/opt/nevisauth/default/conf/credentials/dbUser" connectionPassword="pipe:///var/opt/nevisauth/default/conf/credentials/dbPassword" connectionUrl="jdbc:mariadb://mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat:3306/nevisauth?serverTimezone=UTC&sslMode=disable&autocommit=true" connectionMaxLifeTime="1800000" connectionMaxIdleTime="600000" connectionMinPoolSize="10" connectionMaxPoolSize="10" connectionAutomaticDbSchemaSetup="false"/>
|
||||||
<!-- source: pattern://204c22beaccdfd22727af378, pattern://06aeae2d799e492f5580d03b, pattern://7022472ae407577ae604bbb8, pattern://7022472ae407577ae604bbb8, pattern://9a8294b080ea769d22924af0, pattern://f393012a278e525956a362d3, pattern://c686c1bdd5355351f7f98cc8, pattern://7fb39bfd6c34685866a22180, pattern://b8bdab6e4634a1d81f20e5bb, pattern://cb8c63274fe346280de0ffd5, pattern://9a1d3c6052019748d3510261, pattern://ae023be7e097522c74e31d17, pattern://81ae3547acc02160f787a546, pattern://0327ca909dfcaf2d332da104, pattern://584964c837512845d7940809, pattern://e0fda9336be9c69dafc9b69e, pattern://7022472ae407577ae604bbb8, pattern://cb8c63274fe346280de0ffd5, pattern://204c22beaccdfd22727af378, pattern://06aeae2d799e492f5580d03b, pattern://7022472ae407577ae604bbb8 -->
|
<!-- source: pattern://204c22beaccdfd22727af378, pattern://06aeae2d799e492f5580d03b, pattern://7022472ae407577ae604bbb8, pattern://7022472ae407577ae604bbb8, pattern://9a8294b080ea769d22924af0, pattern://f393012a278e525956a362d3, pattern://c686c1bdd5355351f7f98cc8, pattern://7fb39bfd6c34685866a22180, pattern://b8bdab6e4634a1d81f20e5bb, pattern://cb8c63274fe346280de0ffd5, pattern://9a1d3c6052019748d3510261, pattern://ae023be7e097522c74e31d17, pattern://81ae3547acc02160f787a546, pattern://0327ca909dfcaf2d332da104, pattern://584964c837512845d7940809, pattern://e0fda9336be9c69dafc9b69e, pattern://7022472ae407577ae604bbb8, pattern://cb8c63274fe346280de0ffd5, pattern://204c22beaccdfd22727af378, pattern://06aeae2d799e492f5580d03b, pattern://7022472ae407577ae604bbb8 -->
|
||||||
<AuthEngine useLiteralDictionary="true" literalDictionaryLanguages="en,de,fr,it" inputLanguageCookie="LANG" compatLevel="none" addAutheLevelToSecRoles="true" classPath="/var/opt/nevisauth/default/plugin:/opt/nevisidmcl/nevisauth/lib:/opt/nevisfidocl/nevisauth/lib:/opt/nevisauth/plugin" propagateSession="false">
|
<AuthEngine useLiteralDictionary="true" literalDictionaryLanguages="en,de,fr,it" inputLanguageCookie="LANG" compatLevel="none" addAutheLevelToSecRoles="true" classPath="/var/opt/nevisauth/default/plugin:/opt/nevisidmcl/nevisauth/lib:/opt/nevisfidocl/nevisauth/lib:/opt/nevisauth/plugin" propagateSession="false">
|
||||||
<!-- source: pattern://4fcfadb4a5c946ead7e6e995 -->
|
<!-- source: pattern://4fcfadb4a5c946ead7e6e995 -->
|
||||||
|
@ -420,6 +431,8 @@
|
||||||
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
||||||
<ResultCond name="main" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP"/>
|
<ResultCond name="main" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP"/>
|
||||||
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
||||||
|
<ResultCond name="main_secure" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC"/>
|
||||||
|
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
||||||
<Response value="AUTH_CONTINUE">
|
<Response value="AUTH_CONTINUE">
|
||||||
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
<!-- source: pattern://73efd00d67082ff1eb927922 -->
|
||||||
<Gui name="saml_dispatcher" label="title.saml.failed">
|
<Gui name="saml_dispatcher" label="title.saml.failed">
|
||||||
|
@ -847,6 +860,10 @@
|
||||||
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
||||||
<property name="logoutTrigger" value="#{request['currentResource'].contains('logout') || inargs.containsKey('logout') || inargs.containsKey('SAMLLogout')}"/>
|
<property name="logoutTrigger" value="#{request['currentResource'].contains('logout') || inargs.containsKey('logout') || inargs.containsKey('SAMLLogout')}"/>
|
||||||
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
||||||
|
<property name="in.verify" value="Assertion, AuthnRequest, ArtifactResolve, ArtifactResponse"/>
|
||||||
|
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
||||||
|
<property name="in.prospectVerification" value="ArtifactResolve"/>
|
||||||
|
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
||||||
<property name="out.binding" value="http-post"/>
|
<property name="out.binding" value="http-post"/>
|
||||||
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
||||||
<property name="out.post.relayStateEncoding" value="HTML"/>
|
<property name="out.post.relayStateEncoding" value="HTML"/>
|
||||||
|
@ -933,6 +950,19 @@
|
||||||
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
<!-- source: pattern://92cb6d5256008a32f12ceb93 -->
|
||||||
<property name="out.attribute.http://schemas.agov.ch/ws/2025/07/identity/claims/op/conversationId" value="${inctx:connection.HttpHeader.traceparent:^([0-9a-f]+)-([0-9a-f]+)-([0-9a-f]+)-([0-9a-f]+)$:$2}"/>
|
<property name="out.attribute.http://schemas.agov.ch/ws/2025/07/identity/claims/op/conversationId" value="${inctx:connection.HttpHeader.traceparent:^([0-9a-f]+)-([0-9a-f]+)-([0-9a-f]+)-([0-9a-f]+)$:$2}"/>
|
||||||
</AuthState>
|
</AuthState>
|
||||||
|
<AuthState name="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="false" resumeState="false">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="default" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_post"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="useArtifact" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_artifact"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<Response value="AUTH_ERROR">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<Gui name="AuthErrorDialog"/>
|
||||||
|
</Response>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="condition:useArtifact" value="${sess:agov.idp.use.artifact:^true$}"/>
|
||||||
|
</AuthState>
|
||||||
<AuthState name="Auth_Realm_Main_IDP_ReturnTimeoutButKeepSession" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="true">
|
<AuthState name="Auth_Realm_Main_IDP_ReturnTimeoutButKeepSession" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="true">
|
||||||
<!-- source: pattern://826166d230a6a4849f2837ae -->
|
<!-- source: pattern://826166d230a6a4849f2837ae -->
|
||||||
<Response value="AUTH_CONTINUE">
|
<Response value="AUTH_CONTINUE">
|
||||||
|
@ -1188,6 +1218,100 @@
|
||||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||||
</Response>
|
</Response>
|
||||||
</AuthState>
|
</AuthState>
|
||||||
|
<AuthState name="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_post" class="ch.nevis.esauth.auth.states.saml.IdentityProviderState" final="false" resumeState="true">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="IDP-initiated-ConcurrentLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Concurrent_Logout"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="IDP-initiated-SingleLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Prepare_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="LogoutCompleted" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Logout_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="LogoutFailed" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Logout_Fail"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="SP-initiated-ConcurrentLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Concurrent_Logout"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="SP-initiated-SingleLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Prepare_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="authenticate:IDP-initiated-SSO" next="Auth_Realm_Main_IDP_RequestedRoleLevel"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="authenticate:SP-initiated-SSO" next="Auth_Realm_Main_IDP_RequestedRoleLevel"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="invalidAssertionConsumerUrl" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="ok" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Prepare_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="stepup:IDP-initiated-SSO" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Selector"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="stepup:SP-initiated-SSO" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Selector"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<Response value="AUTH_ERROR">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<Gui name="saml_idp" label="title.saml.failed">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<GuiElem name="lasterror" type="error" label="error.saml.failed"/>
|
||||||
|
</Gui>
|
||||||
|
</Response>
|
||||||
|
<propertyRef name="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.issuer" value="https://auth.agov-w.azure.adnovum.net/SAML2SEC/"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.binding" value="http-post"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.post.relayStateEncoding" value="HTML"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.encrypt" value="none"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.encrypt.keystoreref" value="EncryptionKeys"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.encrypt.keyobjectref" value="DefaultEncryptionKey"/>
|
||||||
|
</AuthState>
|
||||||
|
<AuthState name="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_artifact" class="ch.nevis.esauth.auth.states.saml.IdentityProviderState" final="false" resumeState="true">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="IDP-initiated-ConcurrentLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Concurrent_Logout"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="IDP-initiated-SingleLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Prepare_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="LogoutCompleted" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Logout_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="LogoutFailed" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Logout_Fail"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="SP-initiated-ConcurrentLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Concurrent_Logout"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="SP-initiated-SingleLogout" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Prepare_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="authenticate:IDP-initiated-SSO" next="Auth_Realm_Main_IDP_RequestedRoleLevel"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="authenticate:SP-initiated-SSO" next="Auth_Realm_Main_IDP_RequestedRoleLevel"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="invalidAssertionConsumerUrl" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="ok" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Prepare_Done"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="stepup:IDP-initiated-SSO" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Selector"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<ResultCond name="stepup:SP-initiated-SSO" next="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_Selector"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<Response value="AUTH_ERROR">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<Gui name="saml_idp" label="title.saml.failed">
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<GuiElem name="lasterror" type="error" label="error.saml.failed"/>
|
||||||
|
</Gui>
|
||||||
|
</Response>
|
||||||
|
<propertyRef name="Auth_Realm_Main_IDP_Auth_Realm_Main_IDP_Custom_AGOV_IDP"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.issuer" value="https://auth.agov-w.azure.adnovum.net/SAML2SEC/"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.binding" value="http-artifact"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.post.relayStateEncoding" value="HTML"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.encrypt" value="none"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.encrypt.keystoreref" value="EncryptionKeys"/>
|
||||||
|
<!-- source: pattern://bb9e7806a04578e0ad468829 -->
|
||||||
|
<property name="out.encrypt.keyobjectref" value="DefaultEncryptionKey"/>
|
||||||
|
</AuthState>
|
||||||
<AuthState name="Auth_Realm_Main_IDP_Fido_Email_Verify" class="ch.nevis.idm.authstate.IdmUserVerifyState" final="false" resumeState="false">
|
<AuthState name="Auth_Realm_Main_IDP_Fido_Email_Verify" class="ch.nevis.idm.authstate.IdmUserVerifyState" final="false" resumeState="false">
|
||||||
<!-- source: pattern://7fb39bfd6c34685866a22180 -->
|
<!-- source: pattern://7fb39bfd6c34685866a22180 -->
|
||||||
<ResultCond name="clientNotFound" next="Auth_Realm_Main_IDP_AuthnFailed_Client_NotFound"/>
|
<ResultCond name="clientNotFound" next="Auth_Realm_Main_IDP_AuthnFailed_Client_NotFound"/>
|
||||||
|
@ -3282,8 +3406,6 @@
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
<ResultCond name="SOAP:showGui" next="NotUsed_Auth_Realm_Prepare_Done"/>
|
<ResultCond name="SOAP:showGui" next="NotUsed_Auth_Realm_Prepare_Done"/>
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
<ResultCond name="default" next="NotUsed_Auth_Realm_Prepare_Done"/>
|
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
|
||||||
<ResultCond name="ok" next="NotUsed_Auth_Realm_Prepare_Done" startOver="true"/>
|
<ResultCond name="ok" next="NotUsed_Auth_Realm_Prepare_Done" startOver="true"/>
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
<ResultCond name="showGui" next="NotUsed_Auth_Realm_NotUsed_Pwd_Login-IdmPostProcessing"/>
|
<ResultCond name="showGui" next="NotUsed_Auth_Realm_NotUsed_Pwd_Login-IdmPostProcessing"/>
|
||||||
|
@ -3302,6 +3424,12 @@
|
||||||
<property name="detaillevel.default" value="EXCLUDE"/>
|
<property name="detaillevel.default" value="EXCLUDE"/>
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
<property name="detaillevel.user" value="MEDIUM"/>
|
<property name="detaillevel.user" value="MEDIUM"/>
|
||||||
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
|
<property name="detaillevel.profile" value="MEDIUM"/>
|
||||||
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
|
<property name="detaillevel.role" value="LOW"/>
|
||||||
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
|
<property name="forceDataReload" value="true"/>
|
||||||
</AuthState>
|
</AuthState>
|
||||||
<AuthState name="NotUsed_Auth_Realm_NotUsed_Pwd_Login-IdmPasswordChange" class="ch.nevis.idm.authstate.IdmChangePasswordState" final="false">
|
<AuthState name="NotUsed_Auth_Realm_NotUsed_Pwd_Login-IdmPasswordChange" class="ch.nevis.idm.authstate.IdmChangePasswordState" final="false">
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
|
@ -3379,7 +3507,7 @@
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
<GuiElem name="isiwebnewpw2" type="pw-text" label="prompt.newpassword.confirm"/>
|
<GuiElem name="isiwebnewpw2" type="pw-text" label="prompt.newpassword.confirm"/>
|
||||||
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
<!-- source: pattern://e0fda9336be9c69dafc9b69e -->
|
||||||
<GuiElem name="submit" type="submit" label="button.submit"/>
|
<GuiElem name="submit" type="submit" label="submit.button.label"/>
|
||||||
</Gui>
|
</Gui>
|
||||||
</Response>
|
</Response>
|
||||||
<propertyRef name="nevisIDM_Connector"/>
|
<propertyRef name="nevisIDM_Connector"/>
|
||||||
|
@ -3442,4 +3570,21 @@
|
||||||
<!-- source: pattern://ab5a82719993921822e95751 -->
|
<!-- source: pattern://ab5a82719993921822e95751 -->
|
||||||
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
|
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
|
||||||
</WebService>
|
</WebService>
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<WebService name="IDP_AGOV_SEC_ARS" class="ch.nevis.esauth.auth.adapter.saml.ArtifactResolutionService" uri="/nevisauth/services/ars/sec" SSODomain="Auth_Realm_Main_IDP">
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<property name="issuer" value="https://auth.agov-w.azure.adnovum.net/SAML2SEC/"/>
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<property name="out.keystoreref" value="Store_IDP_AGOV"/>
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<property name="in.keystoreref" value="Store_IDP_AGOV"/>
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<property name="in.verify" value="ArtifactResolve"/>
|
||||||
|
<!-- source: pattern://14efdcb489f3f295fcbdf811 -->
|
||||||
|
<property name="in.prospectVerification" value=""/>
|
||||||
|
</WebService>
|
||||||
|
<!-- source: pattern://7022472ae407577ae604bbb8 -->
|
||||||
|
<RESTService name="ManagementService" class="ch.nevis.esauth.rest.service.session.ManagementService"/>
|
||||||
</esauth-server>
|
</esauth-server>
|
||||||
|
|
|
@ -1,39 +1,39 @@
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import io.opentelemetry.api.trace.Span
|
import io.opentelemetry.api.trace.Span
|
||||||
|
|
||||||
def sess = request.getAuthSession(true)
|
def sess = request.getAuthSession(true)
|
||||||
|
|
||||||
def spanCtxt = Span.current().getSpanContext()
|
def spanCtxt = Span.current().getSpanContext()
|
||||||
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
||||||
def jsonSlurper = new JsonSlurper()
|
def jsonSlurper = new JsonSlurper()
|
||||||
|
|
||||||
|
|
||||||
def lang = (session['ch.nevis.idm.User.language']?:'DE').trim()
|
def lang = (session['ch.nevis.idm.User.language']?:'DE').trim()
|
||||||
def endppoint = "${parameters.get('baseurl')}/api/v1/countries?lang=${lang.toUpperCase()}"
|
def endppoint = "${parameters.get('baseurl')}/api/v1/countries?lang=${lang.toUpperCase()}"
|
||||||
def countryCode = (session['ch.nevis.idm.User.country']?:'CH').trim().toLowerCase()
|
def countryCode = (session['ch.nevis.idm.User.country']?:'CH').trim().toLowerCase()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LOG.debug("UTILITY: Countries: Request url: ${endppoint}")
|
LOG.debug("UTILITY: Countries: Request url: ${endppoint}")
|
||||||
|
|
||||||
def httpClient = HttpClients.create(parameters)
|
def httpClient = HttpClients.create(parameters)
|
||||||
def httpResponse = Http.get().url(endppoint).header('traceparent', traceparent).build().send(httpClient)
|
def httpResponse = Http.get().url(endppoint).header('traceparent', traceparent).build().send(httpClient)
|
||||||
|
|
||||||
LOG.debug('UTILITY: Countries: Response Message: ' + httpResponse.reasonPhrase())
|
LOG.debug('UTILITY: Countries: Response Message: ' + httpResponse.reasonPhrase())
|
||||||
LOG.debug('UTILITY: Countries: Response Status Code: ' + httpResponse.code())
|
LOG.debug('UTILITY: Countries: Response Status Code: ' + httpResponse.code())
|
||||||
LOG.debug('UTILITY: Countries: Response: ' + httpResponse.bodyAsString())
|
LOG.debug('UTILITY: Countries: Response: ' + httpResponse.bodyAsString())
|
||||||
|
|
||||||
if (httpResponse.code() == 200) {
|
if (httpResponse.code() == 200) {
|
||||||
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
||||||
// {"country.af":"Afghanistan","country.al":"Albanie"... }
|
// {"country.af":"Afghanistan","country.al":"Albanie"... }
|
||||||
def countryName = json["country.${countryCode}"]
|
def countryName = json["country.${countryCode}"]
|
||||||
LOG.debug("UTILITY: Countries: countryName for ${countryCode}: ${countryName}")
|
LOG.debug("UTILITY: Countries: countryName for ${countryCode}: ${countryName}")
|
||||||
if (countryName) {
|
if (countryName) {
|
||||||
sess.setAttribute('agov.countryName', countryName)
|
sess.setAttribute('agov.countryName', countryName)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("UTILITY: Countries: Failed to fetch country translations. (httpResponse.code: ${httpResponse.code()})")
|
LOG.warn("UTILITY: Countries: Failed to fetch country translations. (httpResponse.code: ${httpResponse.code()})")
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("UTILITY: Countries: Failed to fetch country translations. (${e})")
|
LOG.warn("UTILITY: Countries: Failed to fetch country translations. (${e})")
|
||||||
}
|
}
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
|
@ -1,38 +1,38 @@
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import io.opentelemetry.api.trace.Span
|
import io.opentelemetry.api.trace.Span
|
||||||
|
|
||||||
def url = parameters.get('url')
|
def url = parameters.get('url')
|
||||||
def realIpHttpHeaderName = parameters.get('realIpHttpHeaderName') ?: 'X-Real-IP'
|
def realIpHttpHeaderName = parameters.get('realIpHttpHeaderName') ?: 'X-Real-IP'
|
||||||
def ip = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def ip = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
def spanCtxt = Span.current().getSpanContext()
|
def spanCtxt = Span.current().getSpanContext()
|
||||||
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
||||||
|
|
||||||
def jsonSlurper = new JsonSlurper()
|
def jsonSlurper = new JsonSlurper()
|
||||||
def httpClient = HttpClients.create(parameters)
|
def httpClient = HttpClients.create(parameters)
|
||||||
def httpResponse = Http.get().url(url).header('traceparent', traceparent)
|
def httpResponse = Http.get().url(url).header('traceparent', traceparent)
|
||||||
.header(realIpHttpHeaderName, ip).build().send(httpClient)
|
.header(realIpHttpHeaderName, ip).build().send(httpClient)
|
||||||
|
|
||||||
LOG.debug('Response Status Code: ' + httpResponse.code())
|
LOG.debug('Response Status Code: ' + httpResponse.code())
|
||||||
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
||||||
|
|
||||||
if (httpResponse.code() == 200) {
|
if (httpResponse.code() == 200) {
|
||||||
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
||||||
|
|
||||||
response.setSessionAttribute('agov.fido2.captchaSettings.enabled', String.valueOf(json.friendlyCaptureClientSettings.enabled))
|
response.setSessionAttribute('agov.fido2.captchaSettings.enabled', String.valueOf(json.friendlyCaptureClientSettings.enabled))
|
||||||
response.setSessionAttribute('agov.fido2.captchaSettings.siteKey', json.friendlyCaptureClientSettings.siteKey)
|
response.setSessionAttribute('agov.fido2.captchaSettings.siteKey', json.friendlyCaptureClientSettings.siteKey)
|
||||||
response.setSessionAttribute('agov.fido2.captchaSettings.puzzleUrl', json.friendlyCaptureClientSettings.puzzleUrl)
|
response.setSessionAttribute('agov.fido2.captchaSettings.puzzleUrl', json.friendlyCaptureClientSettings.puzzleUrl)
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
} else {
|
} else {
|
||||||
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Unexpected HTTP reponse')
|
response.setError(1, 'Unexpected HTTP reponse')
|
||||||
}
|
}
|
||||||
} catch (all) {
|
} catch (all) {
|
||||||
// Handle exception and set the transition
|
// Handle exception and set the transition
|
||||||
LOG.error('error: ' + all, all)
|
LOG.error('error: ' + all, all)
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Exception during HTTP call')
|
response.setError(1, 'Exception during HTTP call')
|
||||||
}
|
}
|
|
@ -1,63 +1,63 @@
|
||||||
import io.opentelemetry.api.trace.Span
|
import io.opentelemetry.api.trace.Span
|
||||||
|
|
||||||
def url = parameters.get('url')
|
def url = parameters.get('url')
|
||||||
|
|
||||||
def email = inargs['userInputValue_prompt.email']
|
def email = inargs['userInputValue_prompt.email']
|
||||||
def token = inargs['captcha_response']?: 'MISSING'
|
def token = inargs['captcha_response']?: 'MISSING'
|
||||||
def enabled = (session['agov.fido2.captchaSettings.enabled']?:'true').toBoolean()
|
def enabled = (session['agov.fido2.captchaSettings.enabled']?:'true').toBoolean()
|
||||||
|
|
||||||
def ip = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def ip = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
def payload = "{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }"
|
def payload = "{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }"
|
||||||
|
|
||||||
LOG.debug('Token: ' + token)
|
LOG.debug('Token: ' + token)
|
||||||
LOG.debug('Payload: ' + payload)
|
LOG.debug('Payload: ' + payload)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
LOG.info("FriendlyCAPTCHA is disabled, allowing operation for ${payload}")
|
LOG.info("FriendlyCAPTCHA is disabled, allowing operation for ${payload}")
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def spanCtxt = Span.current().getSpanContext()
|
def spanCtxt = Span.current().getSpanContext()
|
||||||
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
||||||
|
|
||||||
def httpClient = HttpClients.create(parameters)
|
def httpClient = HttpClients.create(parameters)
|
||||||
def httpResponse = Http.post()
|
def httpResponse = Http.post()
|
||||||
.url(url)
|
.url(url)
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.header("X-FriendlyCAPTCHA-Token", token)
|
.header("X-FriendlyCAPTCHA-Token", token)
|
||||||
.header("traceparent", traceparent)
|
.header("traceparent", traceparent)
|
||||||
.entity(Http.entity()
|
.entity(Http.entity()
|
||||||
.content(payload)
|
.content(payload)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
.build())
|
.build())
|
||||||
.build()
|
.build()
|
||||||
.send(httpClient)
|
.send(httpClient)
|
||||||
|
|
||||||
LOG.debug('Response Status Code: ' + httpResponse.code())
|
LOG.debug('Response Status Code: ' + httpResponse.code())
|
||||||
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
||||||
|
|
||||||
if (httpResponse.code() == 200) {
|
if (httpResponse.code() == 200) {
|
||||||
if (httpResponse.bodyAsString().contains('SUCCESSFUL')) {
|
if (httpResponse.bodyAsString().contains('SUCCESSFUL')) {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("Friendly captcha not successful for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
LOG.warn("Friendly captcha not successful for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
||||||
response.setResult('exit.1')
|
response.setResult('exit.1')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Friendly captcha failed with statuscode ${httpResponse.code()} for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
LOG.error("Friendly captcha failed with statuscode ${httpResponse.code()} for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Unexpected HTTP reponse')
|
response.setError(1, 'Unexpected HTTP reponse')
|
||||||
}
|
}
|
||||||
} catch (all) {
|
} catch (all) {
|
||||||
// Handle exception and set the transition
|
// Handle exception and set the transition
|
||||||
LOG.error("Friendly captcha failed with a general error '${all}' for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }', service-url: ${url}")
|
LOG.error("Friendly captcha failed with a general error '${all}' for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }', service-url: ${url}")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Exception during HTTP call')
|
response.setError(1, 'Exception during HTTP call')
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,3 +24,4 @@ else {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,4 +20,4 @@ if(outargs.containsKey('saml.SAMLResponse')) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,3 +32,4 @@ else {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,168 +1,197 @@
|
||||||
import groovy.xml.XmlSlurper
|
import groovy.xml.XmlSlurper
|
||||||
import groovy.xml.slurpersupport.GPathResult
|
import groovy.xml.slurpersupport.GPathResult
|
||||||
import groovy.xml.slurpersupport.NodeChild
|
import groovy.xml.slurpersupport.NodeChild
|
||||||
|
|
||||||
import java.util.zip.Inflater
|
import java.util.zip.Inflater
|
||||||
import java.util.zip.InflaterInputStream
|
import java.util.zip.InflaterInputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the Referer header.
|
* Gets the value of the Referer header.
|
||||||
* If the header is missing the fallback is returned
|
* If the header is missing the fallback is returned
|
||||||
*
|
*
|
||||||
* This method is used when SAML IDP / Dispatch Error Redirect is not set
|
* This method is used when SAML IDP / Dispatch Error Redirect is not set
|
||||||
*
|
*
|
||||||
* @param fallback - value to return if the Referer header is missing
|
* @param fallback - value to return if the Referer header is missing
|
||||||
* @return value of header or fallback
|
* @return value of header or fallback
|
||||||
*/
|
*/
|
||||||
def getReferer(String fallback) {
|
def getReferer(String fallback) {
|
||||||
return request.getHttpHeader('Referer') ?: fallback
|
return request.getHttpHeader('Referer') ?: fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
def redirect(String url) {
|
def redirect(String url) {
|
||||||
outargs.put('nevis.transfer.type', 'redirect')
|
outargs.put('nevis.transfer.type', 'redirect')
|
||||||
outargs.put('nevis.transfer.destination', url)
|
outargs.put('nevis.transfer.destination', url)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
String getNormalisedSamlMessage(String parameter) {
|
||||||
* Extracts the content of the Issuer element from a parsed SAML message.
|
if (parameter == null) {
|
||||||
* The Issuer is optional according to SAML specification but we need it for dispatching.
|
return
|
||||||
*
|
}
|
||||||
* @param xml - as parsed by Groovy XmlSlurper
|
String text
|
||||||
* @return text content of Issuer element converted or null
|
byte[] decoded
|
||||||
*/
|
|
||||||
String getIssuer(GPathResult xml) {
|
// if parameter is raw xml then continue otherwise try to parse the base64 encoding
|
||||||
return xml.depthFirst().find { GPathResult node -> {
|
if (parameter.startsWith("<")) {
|
||||||
node.name().endsWith(":Issuer") || node.name().equalsIgnoreCase("Issuer")
|
text = new String(parameter)
|
||||||
}
|
}
|
||||||
}?.text()
|
else {
|
||||||
}
|
decoded = parameter.decodeBase64()
|
||||||
|
text = new String(decoded)
|
||||||
String getIssuer(String value) {
|
}
|
||||||
if (value == null) {
|
return text
|
||||||
return
|
}
|
||||||
}
|
|
||||||
String text
|
|
||||||
byte[] decoded
|
String getNodeText(GPathResult xml, String nodeName) {
|
||||||
def parser = new XmlSlurper()
|
return xml.depthFirst().find { GPathResult node -> {
|
||||||
// if value is raw xml then continue otherwise try to parse the base64 encoding
|
node.name().endsWith(":${nodeName}") || node.name().equalsIgnoreCase(nodeName)
|
||||||
if (value.startsWith("<")) {
|
}
|
||||||
text = new String(value)
|
}?.text()?.trim()
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
decoded = value.decodeBase64()
|
String getAttribute(GPathResult xml, String attributeName) {
|
||||||
text = new String(decoded)
|
return xml.depthFirst().find { GPathResult node -> {
|
||||||
LOG.info("received SAML request $value")
|
node.attributes().containsKey(attributeName)
|
||||||
}
|
}
|
||||||
|
}?.attributes()?.get(attributeName)
|
||||||
// after decoded, if redirect binding, we need to parse string to xml
|
}
|
||||||
if (text.startsWith("<")) {
|
|
||||||
LOG.debug("assuming POST/SOAP binding")
|
String getNodeText(String parameter, String nodeName) {
|
||||||
// plain String (POST/SOAP parameter)
|
String samlMessage = getNormalisedSamlMessage(parameter)
|
||||||
def xml = parser.parseText(text)
|
if (samlMessage == null) {
|
||||||
return getIssuer(xml)
|
return
|
||||||
}
|
}
|
||||||
else {
|
def parser = new XmlSlurper()
|
||||||
LOG.debug("assuming redirect binding")
|
def xml = parser.parseText(samlMessage)
|
||||||
// should be deflate encoded (query parameter)
|
return getNodeText(xml, nodeName)
|
||||||
def is = new InflaterInputStream(new ByteArrayInputStream(decoded), new Inflater(true))
|
}
|
||||||
def xml = parser.parse(is)
|
|
||||||
return getIssuer(xml)
|
String getAttribute(String parameter, String attributeName) {
|
||||||
}
|
String samlMessage = getNormalisedSamlMessage(parameter)
|
||||||
}
|
if (samlMessage == null) {
|
||||||
|
return
|
||||||
def dispatchIssuer(i2s, String issuer) {
|
}
|
||||||
def result = i2s.get(issuer)
|
def parser = new XmlSlurper()
|
||||||
if (result == null) {
|
def xml = parser.parseText(samlMessage)
|
||||||
LOG.info("No SP found for issuer '$issuer'. Hint: check SAML SP Connector patterns.")
|
return getAttribute(xml, attributeName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch different idp if artifact binding is enabled
|
String getIssuer(String value) {
|
||||||
if(parameters.get('epdMode') == 'artifact' && result == 'epd'){
|
return getNodeText(value, 'Issuer')
|
||||||
LOG.debug("EPD: Artifact mode")
|
}
|
||||||
result = result + "_artifact"
|
|
||||||
}else{
|
String getAttributeConsumingServiceIndex(String value) {
|
||||||
LOG.debug("EPD: POST mode")
|
return getAttribute(value, 'AttributeConsumingServiceIndex')
|
||||||
}
|
}
|
||||||
response.setResult(result)
|
|
||||||
session.put("saml.inbound.issuer", issuer)
|
String getProtocolBinding(String value) {
|
||||||
session.put('saml.idp.result', result) // remember decision for sub-sequent requests without a SAML message
|
return getAttribute(value, 'ProtocolBinding')
|
||||||
|
}
|
||||||
}
|
|
||||||
|
def dispatchIssuer(i2s, String issuer, boolean secureMode) {
|
||||||
def dispatchMessage(i2s, String message) {
|
def result = i2s.get(issuer)
|
||||||
def issuer = getIssuer(message)
|
if (result == null) {
|
||||||
if (issuer == null) {
|
LOG.info("No SP found for issuer '$issuer'. Hint: check SAML SP Connector patterns.")
|
||||||
LOG.info("No issuer found in incoming SAML message. Giving up.")
|
}
|
||||||
}
|
|
||||||
session.put("saml.inbound.issuer", issuer)
|
// dispatch different idp if artifact binding is enabled
|
||||||
dispatchIssuer(i2s, issuer)
|
if(parameters.get('epdMode') == 'artifact' && result == 'epd'){
|
||||||
}
|
LOG.debug("EPD: Artifact mode")
|
||||||
|
result = result + "_artifact"
|
||||||
if (parameters.get('logoutConfirmation') == 'true' && "stepup" == request.getMethod()) {
|
} else if (result == 'main' && secureMode) {
|
||||||
String url = request.currentResource
|
LOG.debug("AGOV: Secure mode requested")
|
||||||
def path = new URL(url).getPath()
|
result = result + "_secure"
|
||||||
if (path.endsWith("/logout")) {
|
}
|
||||||
// next AuthState will show a logout confirmation GUI
|
response.setResult(result)
|
||||||
response.setResult('confirm')
|
session.put('saml.inbound.issuer', issuer)
|
||||||
return
|
session.put('saml.idp.result', result) // remember decision for sub-sequent requests without a SAML message
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure session exists
|
def dispatchIssuer(i2s, String issuer) {
|
||||||
if (request.getSession(false) == null) {
|
dispatchIssuer(i2s, issuer, false)
|
||||||
session = request.getSession(true).getData()
|
}
|
||||||
}
|
|
||||||
|
def dispatchMessage(i2s, String message) {
|
||||||
// issuer (any case) -> ResultCond name
|
def issuer = getIssuer(message)
|
||||||
def i2s = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER)
|
def secureMode = (getAttributeConsumingServiceIndex(message) == '10101')
|
||||||
|
def useArtifact = ('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' == getProtocolBinding(message))
|
||||||
|
|
||||||
i2s.put(parameters.get('atb'), 'main')
|
LOG.info("secureMode requested: ${secureMode}")
|
||||||
i2s.put(parameters.get('epd_atb'), 'epd')
|
|
||||||
|
if (issuer == null) {
|
||||||
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('SAMLRequest')) { // SP-initiated authentication
|
LOG.info("No issuer found in incoming SAML message. Giving up.")
|
||||||
LOG.debug("found SAMLRequest parameter for SP-initiated authentication")
|
}
|
||||||
String message = inargs.get('SAMLRequest')
|
session.put('saml.inbound.issuer', issuer)
|
||||||
dispatchMessage(i2s, message)
|
session.put('agov.idp.use.artifact', '' + useArtifact)
|
||||||
return
|
dispatchIssuer(i2s, issuer, secureMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs.containsKey('SAMLResponse')) { // response to IDP-initiated SAML Logout
|
if (parameters.get('logoutConfirmation') == 'true' && "stepup" == request.getMethod()) {
|
||||||
LOG.debug("found SAMLResponse parameter")
|
String url = request.currentResource
|
||||||
String message = inargs.get('SAMLResponse')
|
def path = new URL(url).getPath()
|
||||||
dispatchMessage(i2s, message)
|
if (path.endsWith("/logout")) {
|
||||||
return
|
// next AuthState will show a logout confirmation GUI
|
||||||
}
|
response.setResult('confirm')
|
||||||
|
return
|
||||||
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('soapheader')) { // SP-initiated SOAP with soapheader
|
}
|
||||||
LOG.debug("found soapheader parameter for SP-initiated")
|
}
|
||||||
String message = inargs.get('soapheader')
|
|
||||||
dispatchMessage(i2s, message)
|
// ensure session exists
|
||||||
return
|
if (request.getSession(false) == null) {
|
||||||
}
|
session = request.getSession(true).getData()
|
||||||
|
}
|
||||||
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('')) { // SP-initiated SOAP with empty
|
|
||||||
LOG.debug("found empty parameter for SP-initiated SOAP message")
|
// issuer (any case) -> ResultCond name
|
||||||
String message = inargs.get('')
|
def i2s = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER)
|
||||||
dispatchMessage(i2s, message)
|
|
||||||
return
|
|
||||||
}
|
i2s.put(parameters.get('atb'), 'main')
|
||||||
|
i2s.put(parameters.get('epd_atb'), 'epd')
|
||||||
String issuer = inargs['Issuer'] ?: inargs['issuer']
|
|
||||||
if (parameters.get('idpInitiated') == 'true' && issuer != null) { // IDP-initiated authentication
|
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('SAMLRequest')) { // SP-initiated authentication
|
||||||
LOG.debug("found Issuer parameter for IDP-initiated authentication")
|
LOG.debug("found SAMLRequest parameter for SP-initiated authentication")
|
||||||
dispatchIssuer(i2s, issuer)
|
String message = inargs.get('SAMLRequest')
|
||||||
return
|
dispatchMessage(i2s, message)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
// used as fallback in case of ?logout (we need an IdentityProviderState)
|
|
||||||
if (inargs.containsKey("logout") && session.containsKey('saml.idp.result')) {
|
if (inargs.containsKey('SAMLResponse')) { // response to IDP-initiated SAML Logout
|
||||||
def result = session.get('saml.idp.result')
|
LOG.debug("found SAMLResponse parameter")
|
||||||
LOG.debug("dispatching to last used ResultCond: $result")
|
String message = inargs.get('SAMLResponse')
|
||||||
response.setResult(result)
|
dispatchMessage(i2s, message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def location = getReferer('/')
|
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('soapheader')) { // SP-initiated SOAP with soapheader
|
||||||
LOG.info("Unable to dispatch request. Giving up and redirecting (back) to $location")
|
LOG.debug("found soapheader parameter for SP-initiated")
|
||||||
|
String message = inargs.get('soapheader')
|
||||||
|
dispatchMessage(i2s, message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('')) { // SP-initiated SOAP with empty
|
||||||
|
LOG.debug("found empty parameter for SP-initiated SOAP message")
|
||||||
|
String message = inargs.get('')
|
||||||
|
dispatchMessage(i2s, message)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
String issuer = inargs['Issuer'] ?: inargs['issuer']
|
||||||
|
if (parameters.get('idpInitiated') == 'true' && issuer != null) { // IDP-initiated authentication
|
||||||
|
LOG.debug("found Issuer parameter for IDP-initiated authentication")
|
||||||
|
dispatchIssuer(i2s, issuer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// used as fallback in case of ?logout (we need an IdentityProviderState)
|
||||||
|
if (inargs.containsKey("logout") && session.containsKey('saml.idp.result')) {
|
||||||
|
def result = session.get('saml.idp.result')
|
||||||
|
LOG.debug("dispatching to last used ResultCond: $result")
|
||||||
|
response.setResult(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
def location = getReferer('/')
|
||||||
|
LOG.info("Unable to dispatch request. Giving up and redirecting (back) to $location")
|
||||||
redirect(location)
|
redirect(location)
|
|
@ -1,33 +1,33 @@
|
||||||
if (inargs['authRequestId'] && (!session['ch.nevis.auth.saml.request.id'] || inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) {
|
if (inargs['authRequestId'] && (!session['ch.nevis.auth.saml.request.id'] || inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) {
|
||||||
// make sure we start from scratch
|
// make sure we start from scratch
|
||||||
def mInargs = request.getInArgs()
|
def mInargs = request.getInArgs()
|
||||||
mInargs.remove('email')
|
mInargs.remove('email')
|
||||||
mInargs.remove('recaptcha_sitekey')
|
mInargs.remove('recaptcha_sitekey')
|
||||||
mInargs.remove('recaptcha_response')
|
mInargs.remove('recaptcha_response')
|
||||||
mInargs.remove('continue')
|
mInargs.remove('continue')
|
||||||
mInargs.remove('authRequestId')
|
mInargs.remove('authRequestId')
|
||||||
mInargs.remove('cancel')
|
mInargs.remove('cancel')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['cd'] && session['agov.recovery.code']) {
|
if (inargs['cd'] && session['agov.recovery.code']) {
|
||||||
// we are called with a new URL --> make sure we start from scratch
|
// we are called with a new URL --> make sure we start from scratch
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
def sessionKeySet = new HashSet(session.keySet())
|
def sessionKeySet = new HashSet(session.keySet())
|
||||||
sessionKeySet.each { key ->
|
sessionKeySet.each { key ->
|
||||||
if ( key ==~ /ch.nevis.idm.*/ || key ==~ /ch.adnovum.nevisidm.*/ || key ==~ /agov.recovery.*/ ) {
|
if ( key ==~ /ch.nevis.idm.*/ || key ==~ /ch.adnovum.nevisidm.*/ || key ==~ /agov.recovery.*/ ) {
|
||||||
s.removeAttribute(key)
|
s.removeAttribute(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!session['ch.nevis.auth.saml.request.id']) {
|
if (!session['ch.nevis.auth.saml.request.id']) {
|
||||||
response.setSessionAttribute('ch.nevis.auth.saml.request.id', java.util.UUID.randomUUID().toString())
|
response.setSessionAttribute('ch.nevis.auth.saml.request.id', java.util.UUID.randomUUID().toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
|
|
||||||
response.setSessionAttribute('agov.recovery.ip', '' + sourceIp)
|
response.setSessionAttribute('agov.recovery.ip', '' + sourceIp)
|
||||||
response.setSessionAttribute('agov.recovery.userAgent', '' + userAgent)
|
response.setSessionAttribute('agov.recovery.userAgent', '' + userAgent)
|
||||||
|
|
||||||
response.setResult('default')
|
response.setResult('default')
|
|
@ -1,10 +1,10 @@
|
||||||
def requester = 'unknown'
|
def requester = 'unknown'
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
def reason = session['agov.recovery.reason'] ?: 'unknown'
|
def reason = session['agov.recovery.reason'] ?: 'unknown'
|
||||||
|
|
||||||
LOG.info("Event='RECOVERY-REASON', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', Reason='${reason}'")
|
LOG.info("Event='RECOVERY-REASON', Requester='${requester}', RequestId='${requestId}', User=${user}, SourceIp=${sourceIp}, UserAgent='${userAgent}', Reason='${reason}'")
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
|
@ -16,16 +16,12 @@ Configuration:
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
- name: "EsAuthStart"
|
- name: "EsAuthStart"
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
- name: "org.apache.catalina.loader.WebappClassLoader"
|
|
||||||
level: "FATAL"
|
|
||||||
- name: "org.apache.catalina.startup.HostConfig"
|
|
||||||
level: "ERROR"
|
|
||||||
- name: "ch.nevis.esauth.events"
|
|
||||||
level: "FATAL"
|
|
||||||
- name: "AGOV-ACCT"
|
- name: "AGOV-ACCT"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
- name: "AgovCaptcha"
|
- name: "AgovCaptcha"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
|
- name: "ArtifactResolutionService"
|
||||||
|
level: "DEBUG"
|
||||||
- name: "AuthEngine"
|
- name: "AuthEngine"
|
||||||
level: "INFO"
|
level: "INFO"
|
||||||
- name: "AuthPerf"
|
- name: "AuthPerf"
|
||||||
|
@ -33,9 +29,11 @@ Configuration:
|
||||||
- name: "IdmAuth"
|
- name: "IdmAuth"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
- name: "OpTrace"
|
- name: "OpTrace"
|
||||||
level: "DEBUG"
|
level: "INFO"
|
||||||
- name: "Recovery"
|
- name: "Recovery"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
|
- name: "Saml"
|
||||||
|
level: "DEBUG"
|
||||||
- name: "Script"
|
- name: "Script"
|
||||||
level: "DEBUG"
|
level: "DEBUG"
|
||||||
- name: "SessCoord"
|
- name: "SessCoord"
|
||||||
|
|
|
@ -1,64 +1,64 @@
|
||||||
def redirect(location) {
|
def redirect(location) {
|
||||||
outargs.put('nevis.transfer.type', 'redirect')
|
outargs.put('nevis.transfer.type', 'redirect')
|
||||||
outargs.put('nevis.transfer.destination', location)
|
outargs.put('nevis.transfer.destination', location)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getReturnURL() {
|
def getReturnURL() {
|
||||||
if (inargs.containsKey('return')) {
|
if (inargs.containsKey('return')) {
|
||||||
return inargs.get('return')
|
return inargs.get('return')
|
||||||
}
|
}
|
||||||
// determine returnURL based on Referer header (if present and not pointing to this page)
|
// determine returnURL based on Referer header (if present and not pointing to this page)
|
||||||
def referer = request.getHttpHeader('Referer')
|
def referer = request.getHttpHeader('Referer')
|
||||||
if (referer == null) {
|
if (referer == null) {
|
||||||
LOG.debug('no Referer header found')
|
LOG.debug('no Referer header found')
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// strip query String for comparison
|
// strip query String for comparison
|
||||||
String previous = referer.contains('?') ? referer.substring(0, referer.indexOf("?")) : referer
|
String previous = referer.contains('?') ? referer.substring(0, referer.indexOf("?")) : referer
|
||||||
def current = request.getCurrentResource()
|
def current = request.getCurrentResource()
|
||||||
if (current.startsWith(previous)) {
|
if (current.startsWith(previous)) {
|
||||||
LOG.debug("Referer header $referer cannot be used as return URL - cyclic redirect")
|
LOG.debug("Referer header $referer cannot be used as return URL - cyclic redirect")
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return referer
|
return referer
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs.containsKey('logout-confirm')) {
|
if (inargs.containsKey('logout-confirm')) {
|
||||||
def current = request.getCurrentResource()
|
def current = request.getCurrentResource()
|
||||||
// user has confirmed logout -> replace /logout with /?logout
|
// user has confirmed logout -> replace /logout with /?logout
|
||||||
String location
|
String location
|
||||||
if (current.contains('?')) {
|
if (current.contains('?')) {
|
||||||
location = current.replace("/logout?", "/?logout&")
|
location = current.replace("/logout?", "/?logout&")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
location = current.replace("/logout", "/?logout")
|
location = current.replace("/logout", "/?logout")
|
||||||
}
|
}
|
||||||
redirect(location)
|
redirect(location)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs.containsKey('logout-abort')) {
|
if (inargs.containsKey('logout-abort')) {
|
||||||
// user has aborted logout -> redirect to stored return URL
|
// user has aborted logout -> redirect to stored return URL
|
||||||
def location = session.get('logout-abort-url')
|
def location = session.get('logout-abort-url')
|
||||||
redirect(location)
|
redirect(location)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// user has not clicked any button -> render GUI
|
// user has not clicked any button -> render GUI
|
||||||
response.setGuiName('saml_logout_confirm')
|
response.setGuiName('saml_logout_confirm')
|
||||||
response.setGuiLabel('title.logout.confirmation')
|
response.setGuiLabel('title.logout.confirmation')
|
||||||
// not setting a target as the API has been removed
|
// not setting a target as the API has been removed
|
||||||
response.addInfoGuiField('info', 'info.logout.confirmation', null)
|
response.addInfoGuiField('info', 'info.logout.confirmation', null)
|
||||||
response.addButtonGuiField('logout-confirm', 'continue.button.label', 'true')
|
response.addButtonGuiField('logout-confirm', 'continue.button.label', 'true')
|
||||||
|
|
||||||
def returnURL = getReturnURL()
|
def returnURL = getReturnURL()
|
||||||
|
|
||||||
if (returnURL != null) {
|
if (returnURL != null) {
|
||||||
// store return URL in session
|
// store return URL in session
|
||||||
session.put('logout-abort-url', returnURL)
|
session.put('logout-abort-url', returnURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session.containsKey('logout-abort-url')) {
|
if (session.containsKey('logout-abort-url')) {
|
||||||
// add cancel button to go back
|
// add cancel button to go back
|
||||||
response.addButtonGuiField('logout-abort', 'cancel.button.label', 'true')
|
response.addButtonGuiField('logout-abort', 'cancel.button.label', 'true')
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
otel.service.name = auth
|
otel.service.name = auth
|
||||||
|
otel.traces.sampler = always_on
|
||||||
otel.traces.exporter = none
|
otel.traces.exporter = none
|
||||||
otel.metrics.exporter = none
|
otel.metrics.exporter = none
|
||||||
otel.logs.exporter = none
|
otel.logs.exporter = none
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
|
|
||||||
if (inargs['cancel'] && inargs['cancel'] == 'cancel') {
|
if (inargs['cancel'] && inargs['cancel'] == 'cancel') {
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
s.removeAttribute('agov.recovery.moreThanOneLf')
|
s.removeAttribute('agov.recovery.moreThanOneLf')
|
||||||
|
|
||||||
response.setResult('doCancel')
|
response.setResult('doCancel')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['continue'] && inargs['continue'] == 'yes') {
|
if (inargs['continue'] && inargs['continue'] == 'yes') {
|
||||||
response.setSessionAttribute('agov.recovery.moreThanOneLf', 'yes')
|
response.setSessionAttribute('agov.recovery.moreThanOneLf', 'yes')
|
||||||
response.setResult('loginFactorYes')
|
response.setResult('loginFactorYes')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['continue'] && inargs['continue'] == 'no') {
|
if (inargs['continue'] && inargs['continue'] == 'no') {
|
||||||
response.setSessionAttribute('agov.recovery.moreThanOneLf', 'no')
|
response.setSessionAttribute('agov.recovery.moreThanOneLf', 'no')
|
||||||
response.setResult('loginFactorNo')
|
response.setResult('loginFactorNo')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach this, display the GUI again
|
// if we reach this, display the GUI again
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
|
@ -1,28 +1,28 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
|
|
||||||
if (inargs['reason']) {
|
if (inargs['reason']) {
|
||||||
response.setSessionAttribute('agov.recovery.reason', '' + inargs['reason'])
|
response.setSessionAttribute('agov.recovery.reason', '' + inargs['reason'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['cancel'] && inargs['cancel'] == 'cancel') {
|
if (inargs['cancel'] && inargs['cancel'] == 'cancel') {
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
s.removeAttribute('agov.recovery.moreThanOneLf')
|
s.removeAttribute('agov.recovery.moreThanOneLf')
|
||||||
s.removeAttribute('agov.recovery.reason')
|
s.removeAttribute('agov.recovery.reason')
|
||||||
|
|
||||||
response.setResult('doCancel')
|
response.setResult('doCancel')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['continue'] && inargs['continue'] == 'yes') {
|
if (inargs['continue'] && inargs['continue'] == 'yes') {
|
||||||
response.setResult('validReasons')
|
response.setResult('validReasons')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inargs['continue'] && inargs['continue'] == 'no') {
|
if (inargs['continue'] && inargs['continue'] == 'no') {
|
||||||
response.setResult('invalidReasons')
|
response.setResult('invalidReasons')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach this, display the GUI again
|
// if we reach this, display the GUI again
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
|
@ -76,4 +76,4 @@ if (session['ch.adnovum.nevisidm.userDto'] != null && notes['lasterror'] == null
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
|
|
||||||
// new
|
// new
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
if (session['agov.recovery.redirectDone']) {
|
if (session['agov.recovery.redirectDone']) {
|
||||||
// user navigated back from AGOV.me, go again for the code
|
// user navigated back from AGOV.me, go again for the code
|
||||||
|
|
||||||
// clean up SAML state first,
|
// clean up SAML state first,
|
||||||
// IdentityProviderState sets session attributes as follows
|
// IdentityProviderState sets session attributes as follows
|
||||||
// <IDP-State-Name>-session-participants.<SAML-RP-ISSUER> = <ACS-URL>
|
// <IDP-State-Name>-session-participants.<SAML-RP-ISSUER> = <ACS-URL>
|
||||||
// State name contains the name of the pattern 'Recovery_redirectAgovMe'
|
// State name contains the name of the pattern 'Recovery_redirectAgovMe'
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
def sessionKeySet = new HashSet(session.keySet())
|
def sessionKeySet = new HashSet(session.keySet())
|
||||||
sessionKeySet.each { key ->
|
sessionKeySet.each { key ->
|
||||||
if ( key ==~ /.*Recovery_redirectAgovMe-session-participants.*/ ) {
|
if ( key ==~ /.*Recovery_redirectAgovMe-session-participants.*/ ) {
|
||||||
LOG.debug("Deleted session attribute '${key}'")
|
LOG.debug("Deleted session attribute '${key}'")
|
||||||
s.removeAttribute(key)
|
s.removeAttribute(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.removeAttribute('agov.recovery.redirectDone')
|
s.removeAttribute('agov.recovery.redirectDone')
|
||||||
response.setResult('back')
|
response.setResult('back')
|
||||||
} else {
|
} else {
|
||||||
// redirect
|
// redirect
|
||||||
response.setSessionAttribute('agov.recovery.redirectDone', 'true')
|
response.setSessionAttribute('agov.recovery.redirectDone', 'true')
|
||||||
response.setResult('redirect')
|
response.setResult('redirect')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,267 +1,267 @@
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import groovy.xml.XmlSlurper
|
import groovy.xml.XmlSlurper
|
||||||
import org.codehaus.groovy.runtime.StackTraceUtils
|
import org.codehaus.groovy.runtime.StackTraceUtils
|
||||||
|
|
||||||
import ch.nevis.idm.client.IdmRestClient
|
import ch.nevis.idm.client.IdmRestClient
|
||||||
import ch.nevis.idm.client.IdmRestClientFactory
|
import ch.nevis.idm.client.IdmRestClientFactory
|
||||||
|
|
||||||
|
|
||||||
// AGOVaq conversion
|
// AGOVaq conversion
|
||||||
def maxLoiRoleToCtxClssConvertorMap = [
|
def maxLoiRoleToCtxClssConvertorMap = [
|
||||||
"level100": "urn:qa.agov.ch:names:tc:ac:classes:100",
|
"level100": "urn:qa.agov.ch:names:tc:ac:classes:100",
|
||||||
"level200": "urn:qa.agov.ch:names:tc:ac:classes:200",
|
"level200": "urn:qa.agov.ch:names:tc:ac:classes:200",
|
||||||
"level300": "urn:qa.agov.ch:names:tc:ac:classes:300",
|
"level300": "urn:qa.agov.ch:names:tc:ac:classes:300",
|
||||||
"level400": "urn:qa.agov.ch:names:tc:ac:classes:400",
|
"level400": "urn:qa.agov.ch:names:tc:ac:classes:400",
|
||||||
"level500": "urn:qa.agov.ch:names:tc:ac:classes:500"
|
"level500": "urn:qa.agov.ch:names:tc:ac:classes:500"
|
||||||
]
|
]
|
||||||
|
|
||||||
// https://docs.nevis.net/nevisidm/Developer-Guide/SOAP-Interface/Interface-specification/Value-types#enum-value-types
|
// https://docs.nevis.net/nevisidm/Developer-Guide/SOAP-Interface/Interface-specification/Value-types#enum-value-types
|
||||||
def blockingCredentialStates = ['DISABLED', 'EXPIRED', 'LOCKED', 'ARCHIVED', 'RESET_CODE']
|
def blockingCredentialStates = ['DISABLED', 'EXPIRED', 'LOCKED', 'ARCHIVED', 'RESET_CODE']
|
||||||
|
|
||||||
def getUserIdVerificationForRecovery(currentLoaRole) {
|
def getUserIdVerificationForRecovery(currentLoaRole) {
|
||||||
// application is AGOV-AccountStatus
|
// application is AGOV-AccountStatus
|
||||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
def result = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-AccountStatus,mustRecover'}?.value?.text()
|
def result = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-AccountStatus,mustRecover'}?.value?.text()
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// fallback if not explicitly set
|
// fallback if not explicitly set
|
||||||
def chDomicile = list.country.text() == 'ch'
|
def chDomicile = list.country.text() == 'ch'
|
||||||
def lastIdVerification = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + currentLoaRole}?.value?.text() ?: 'missing'
|
def lastIdVerification = list.'**'.find {node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + currentLoaRole}?.value?.text() ?: 'missing'
|
||||||
switch (currentLoaRole) {
|
switch (currentLoaRole) {
|
||||||
case 'level100':
|
case 'level100':
|
||||||
result = chDomicile ? 'SimpleLetter' : 'Video'
|
result = chDomicile ? 'SimpleLetter' : 'Video'
|
||||||
break
|
break
|
||||||
case 'level200':
|
case 'level200':
|
||||||
result = chDomicile ? 'Bmid' : 'Video'
|
result = chDomicile ? 'Bmid' : 'Video'
|
||||||
break
|
break
|
||||||
case 'level300':
|
case 'level300':
|
||||||
case 'level400':
|
case 'level400':
|
||||||
result = chDomicile ? lastIdVerification : 'Video'
|
result = chDomicile ? lastIdVerification : 'Video'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
LOG.warn("unexpected loa on account: ${currentLoaRole}")
|
LOG.warn("unexpected loa on account: ${currentLoaRole}")
|
||||||
// safest default, should work in any case
|
// safest default, should work in any case
|
||||||
result = 'Video'
|
result = 'Video'
|
||||||
}
|
}
|
||||||
LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
|
LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevel) {
|
def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevel) {
|
||||||
def result = 'level'
|
def result = 'level'
|
||||||
|
|
||||||
switch (idVerification) {
|
switch (idVerification) {
|
||||||
case 'None':
|
case 'None':
|
||||||
result = result.concat('100')
|
result = result.concat('100')
|
||||||
break
|
break
|
||||||
case 'SimpleLetter':
|
case 'SimpleLetter':
|
||||||
result = result.concat('200')
|
result = result.concat('200')
|
||||||
break
|
break
|
||||||
case 'Video':
|
case 'Video':
|
||||||
case 'VideoSelfPaid':
|
case 'VideoSelfPaid':
|
||||||
case 'Bmid':
|
case 'Bmid':
|
||||||
case 'BmidSelfPaid':
|
case 'BmidSelfPaid':
|
||||||
case 'Counter':
|
case 'Counter':
|
||||||
result = result.concat((highestRoleLevel == 'level400') ? '400' : '300')
|
result = result.concat((highestRoleLevel == 'level400') ? '400' : '300')
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
||||||
// safest default, should work in any case
|
// safest default, should work in any case
|
||||||
result = highestRoleLevel
|
result = highestRoleLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserMustRecoverValidFrom() {
|
def getUserMustRecoverValidFrom() {
|
||||||
// set attibutes from DTO: -> validFrom
|
// set attibutes from DTO: -> validFrom
|
||||||
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
def authzNode = payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == 'mustRecover'}
|
def authzNode = payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == 'mustRecover'}
|
||||||
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
def userHasNewLoginFactor() {
|
def userHasNewLoginFactor() {
|
||||||
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
|
||||||
|
|
||||||
String baseUrl = parameters.get('baseUrl')
|
String baseUrl = parameters.get('baseUrl')
|
||||||
String clientExtId = session.get('ch.adnovum.nevisidm.user.clientExtId')
|
String clientExtId = session.get('ch.adnovum.nevisidm.user.clientExtId')
|
||||||
String userExtId = session.get('ch.adnovum.nevisidm.user.extId')
|
String userExtId = session.get('ch.adnovum.nevisidm.user.extId')
|
||||||
String baseEndPoint = "$baseUrl/api/core/v1/$clientExtId/users/$userExtId"
|
String baseEndPoint = "$baseUrl/api/core/v1/$clientExtId/users/$userExtId"
|
||||||
|
|
||||||
response.setSessionAttribute('agov.recovery.newLoginFactor', 'NONE')
|
response.setSessionAttribute('agov.recovery.newLoginFactor', 'NONE')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
def credInfoArray = new JsonSlurper().parseText(idmRestClient.get("$baseEndPoint/generic-credentials"))
|
def credInfoArray = new JsonSlurper().parseText(idmRestClient.get("$baseEndPoint/generic-credentials"))
|
||||||
|
|
||||||
def accessApp = credInfoArray['items'].find( it -> it.stateName == "active")
|
def accessApp = credInfoArray['items'].find( it -> it.stateName == "active")
|
||||||
if (accessApp) {
|
if (accessApp) {
|
||||||
response.setSessionAttribute('agov.recovery.accessapp', accessApp.properties.fidouaf_name)
|
response.setSessionAttribute('agov.recovery.accessapp', accessApp.properties.fidouaf_name)
|
||||||
response.setSessionAttribute('agov.recovery.accessapp.dispatchTargetId', accessApp.identification.replaceAll('dispatch_target_', ''))
|
response.setSessionAttribute('agov.recovery.accessapp.dispatchTargetId', accessApp.identification.replaceAll('dispatch_target_', ''))
|
||||||
response.setSessionAttribute('agov.recovery.newLoginFactor', 'ACCESS_APP')
|
response.setSessionAttribute('agov.recovery.newLoginFactor', 'ACCESS_APP')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
credInfoArray = new JsonSlurper().parseText(idmRestClient.get("$baseEndPoint/fido2"))
|
credInfoArray = new JsonSlurper().parseText(idmRestClient.get("$baseEndPoint/fido2"))
|
||||||
|
|
||||||
def fido2Key = credInfoArray['items'].find( it -> it.stateName == "active")
|
def fido2Key = credInfoArray['items'].find( it -> it.stateName == "active")
|
||||||
if (fido2Key) {
|
if (fido2Key) {
|
||||||
response.setSessionAttribute('agov.recovery.securityKey', fido2Key.userFriendlyName)
|
response.setSessionAttribute('agov.recovery.securityKey', fido2Key.userFriendlyName)
|
||||||
response.setSessionAttribute('agov.recovery.newLoginFactor', 'FIDO2')
|
response.setSessionAttribute('agov.recovery.newLoginFactor', 'FIDO2')
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
LOG.error(e.toString())
|
LOG.error(e.toString())
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// for autditing
|
// for autditing
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
def maxLoi = null
|
def maxLoi = null
|
||||||
|
|
||||||
|
|
||||||
// new
|
// new
|
||||||
if (session['ch.adnovum.nevisidm.userDto'] != null && notes['lasterror'] == null) {
|
if (session['ch.adnovum.nevisidm.userDto'] != null && notes['lasterror'] == null) {
|
||||||
try {
|
try {
|
||||||
def userDto = new XmlSlurper().parseText(session['ch.adnovum.nevisidm.userDto'])
|
def userDto = new XmlSlurper().parseText(session['ch.adnovum.nevisidm.userDto'])
|
||||||
def userState = userDto.state
|
def userState = userDto.state
|
||||||
def recoveryCode = userDto.'**'.find {node -> node.name() == 'credentials' && node.type.text() == 'CONTEXT_PASSWORD' && node.context.text() == 'RECOVERY'}
|
def recoveryCode = userDto.'**'.find {node -> node.name() == 'credentials' && node.type.text() == 'CONTEXT_PASSWORD' && node.context.text() == 'RECOVERY'}
|
||||||
|
|
||||||
LOG.debug("Recovery: Dto is '${userDto}")
|
LOG.debug("Recovery: Dto is '${userDto}")
|
||||||
LOG.debug("Recovery: state is '${userState}")
|
LOG.debug("Recovery: state is '${userState}")
|
||||||
LOG.debug("Recovery: RecoveryCode is '${recoveryCode ? recoveryCode : 'none'}'")
|
LOG.debug("Recovery: RecoveryCode is '${recoveryCode ? recoveryCode : 'none'}'")
|
||||||
|
|
||||||
if (userState == 'ACTIVE') {
|
if (userState == 'ACTIVE') {
|
||||||
|
|
||||||
response.setSessionAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
response.setSessionAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
||||||
response.setSessionAttribute('agov.recovery.authenticatedWith', 'urn:qa.agov.ch:names:tc:authfactor:email')
|
response.setSessionAttribute('agov.recovery.authenticatedWith', 'urn:qa.agov.ch:names:tc:authfactor:email')
|
||||||
response.setSessionAttribute('agov.recovery.codeStatus', 'notNeeded')
|
response.setSessionAttribute('agov.recovery.codeStatus', 'notNeeded')
|
||||||
response.setSessionAttribute('agov.recovery.codeDetailStatus', 'n/a')
|
response.setSessionAttribute('agov.recovery.codeDetailStatus', 'n/a')
|
||||||
response.setSessionAttribute('agov.recovery.newLoginFactor', 'NONE')
|
response.setSessionAttribute('agov.recovery.newLoginFactor', 'NONE')
|
||||||
|
|
||||||
def maxLoiList = userDto.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-Loi' }.collect({ node -> node.name.text() })
|
def maxLoiList = userDto.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-Loi' }.collect({ node -> node.name.text() })
|
||||||
maxLoi = (maxLoiList == null || maxLoiList.isEmpty()) ? null : maxLoiList.sort().last()
|
maxLoi = (maxLoiList == null || maxLoiList.isEmpty()) ? null : maxLoiList.sort().last()
|
||||||
|
|
||||||
def idVerification = null
|
def idVerification = null
|
||||||
def agovAqValidFrom = null
|
def agovAqValidFrom = null
|
||||||
if (maxLoi) {
|
if (maxLoi) {
|
||||||
if (maxLoi != 'level100') {
|
if (maxLoi != 'level100') {
|
||||||
response.setSessionAttribute('agov.recovery.codeDetailStatus', '' + maxLoi)
|
response.setSessionAttribute('agov.recovery.codeDetailStatus', '' + maxLoi)
|
||||||
}
|
}
|
||||||
|
|
||||||
idVerification = userDto.'**'.find { node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + maxLoi}?.value?.text()
|
idVerification = userDto.'**'.find { node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + maxLoi}?.value?.text()
|
||||||
idVerification = idVerification ?: 'None'
|
idVerification = idVerification ?: 'None'
|
||||||
agovAqValidFrom = userDto.'**'.find { node -> node.name() == 'authorizations' && node.role.name.text() == maxLoi}?.validFrom?.text()
|
agovAqValidFrom = userDto.'**'.find { node -> node.name() == 'authorizations' && node.role.name.text() == maxLoi}?.validFrom?.text()
|
||||||
agovAqValidFrom = agovAqValidFrom?: userDto.'**'.find { node -> node.name() == 'authorizations' && node.role.name.text() == maxLoi}?.ctlCreDat?.text()
|
agovAqValidFrom = agovAqValidFrom?: userDto.'**'.find { node -> node.name() == 'authorizations' && node.role.name.text() == maxLoi}?.ctlCreDat?.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
def mustRecover = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'mustRecover' }
|
def mustRecover = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'mustRecover' }
|
||||||
|
|
||||||
def hasRecoveryRole = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'recovery' }
|
def hasRecoveryRole = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'recovery' }
|
||||||
|
|
||||||
def hasRecoveryCascadeRole = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'recoveryCascade' }
|
def hasRecoveryCascadeRole = userDto.'**'.find { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' && node.name.text() == 'recoveryCascade' }
|
||||||
|
|
||||||
def hasNewLoginFactor = hasRecoveryRole && userHasNewLoginFactor()
|
def hasNewLoginFactor = hasRecoveryRole && userHasNewLoginFactor()
|
||||||
|
|
||||||
if (mustRecover) {
|
if (mustRecover) {
|
||||||
// attributes are defined over the mustRecover authorization
|
// attributes are defined over the mustRecover authorization
|
||||||
response.setSessionAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
response.setSessionAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
||||||
response.setSessionAttribute('agov.recovery.codeDetailStatus', 'mustRecover')
|
response.setSessionAttribute('agov.recovery.codeDetailStatus', 'mustRecover')
|
||||||
|
|
||||||
idVerification = getUserIdVerificationForRecovery(maxLoi ?: 'level100') ?: idVerification
|
idVerification = getUserIdVerificationForRecovery(maxLoi ?: 'level100') ?: idVerification
|
||||||
|
|
||||||
agovAqValidFrom = getUserMustRecoverValidFrom()
|
agovAqValidFrom = getUserMustRecoverValidFrom()
|
||||||
|
|
||||||
maxLoi = getAqLevelBasedOnIdVerificationForRecovery(idVerification, maxLoi)
|
maxLoi = getAqLevelBasedOnIdVerificationForRecovery(idVerification, maxLoi)
|
||||||
} else if (hasRecoveryCascadeRole && hasNewLoginFactor) {
|
} else if (hasRecoveryCascadeRole && hasNewLoginFactor) {
|
||||||
response.setSessionAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recoveryCascade')
|
response.setSessionAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recoveryCascade')
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.debug("Recovery: MaxLoi is '${maxLoi}'")
|
LOG.debug("Recovery: MaxLoi is '${maxLoi}'")
|
||||||
LOG.debug("Recovery: IdVerification is ${idVerification}")
|
LOG.debug("Recovery: IdVerification is ${idVerification}")
|
||||||
LOG.debug("Recovery: agovAqValidFrom is ${agovAqValidFrom}")
|
LOG.debug("Recovery: agovAqValidFrom is ${agovAqValidFrom}")
|
||||||
LOG.debug("Recovery: mustRecover is '${mustRecover}'")
|
LOG.debug("Recovery: mustRecover is '${mustRecover}'")
|
||||||
LOG.debug("Recovery: hasRecoveryRole is '${hasRecoveryRole}'")
|
LOG.debug("Recovery: hasRecoveryRole is '${hasRecoveryRole}'")
|
||||||
LOG.debug("Recovery: hasNewLoginFactor is '${hasNewLoginFactor}'")
|
LOG.debug("Recovery: hasNewLoginFactor is '${hasNewLoginFactor}'")
|
||||||
|
|
||||||
if (maxLoi != null) {
|
if (maxLoi != null) {
|
||||||
if (maxLoiRoleToCtxClssConvertorMap.containsKey(maxLoi)) {
|
if (maxLoiRoleToCtxClssConvertorMap.containsKey(maxLoi)) {
|
||||||
LOG.debug("Recovery: MaxLoiMapping is " + maxLoiRoleToCtxClssConvertorMap[maxLoi])
|
LOG.debug("Recovery: MaxLoiMapping is " + maxLoiRoleToCtxClssConvertorMap[maxLoi])
|
||||||
response.setSessionAttribute('agov.recovery.currentAgovAq', '' + maxLoiRoleToCtxClssConvertorMap[maxLoi])
|
response.setSessionAttribute('agov.recovery.currentAgovAq', '' + maxLoiRoleToCtxClssConvertorMap[maxLoi])
|
||||||
response.setSessionAttribute('agov.recovery.currentIdVerification', '' + idVerification)
|
response.setSessionAttribute('agov.recovery.currentIdVerification', '' + idVerification)
|
||||||
response.setSessionAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + agovAqValidFrom)
|
response.setSessionAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + agovAqValidFrom)
|
||||||
|
|
||||||
if ((maxLoi == 'level100') && (mustRecover == null) && !hasNewLoginFactor) {
|
if ((maxLoi == 'level100') && (mustRecover == null) && !hasNewLoginFactor) {
|
||||||
// AQ100 accounts need to use the recovery code, if they can
|
// AQ100 accounts need to use the recovery code, if they can
|
||||||
// check the status of recoveryCode credential
|
// check the status of recoveryCode credential
|
||||||
if (recoveryCode && !blockingCredentialStates.contains(recoveryCode.state.text())) {
|
if (recoveryCode && !blockingCredentialStates.contains(recoveryCode.state.text())) {
|
||||||
LOG.debug("Recovery: emailAndCode")
|
LOG.debug("Recovery: emailAndCode")
|
||||||
response.setResult('needCode')
|
response.setResult('needCode')
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
LOG.warn("AGOVaq100 recovery: skipped Recovery-Code check '${recoveryCode ? recoveryCode.state.text() : 'MISSING'}'")
|
LOG.warn("AGOVaq100 recovery: skipped Recovery-Code check '${recoveryCode ? recoveryCode.state.text() : 'MISSING'}'")
|
||||||
response.setSessionAttribute('agov.recovery.codeStatus', 'skipped')
|
response.setSessionAttribute('agov.recovery.codeStatus', 'skipped')
|
||||||
response.setSessionAttribute('agov.recovery.codeDetailStatus', "unusable (state: ${recoveryCode ? recoveryCode.state.text() : 'MISSING'})")
|
response.setSessionAttribute('agov.recovery.codeDetailStatus', "unusable (state: ${recoveryCode ? recoveryCode.state.text() : 'MISSING'})")
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ((maxLoi == 'level100') && hasNewLoginFactor) {
|
} else if ((maxLoi == 'level100') && hasNewLoginFactor) {
|
||||||
LOG.debug("Recovery: new Login Factor")
|
LOG.debug("Recovery: new Login Factor")
|
||||||
response.setSessionAttribute('agov.recovery.codeStatus', 'skipped')
|
response.setSessionAttribute('agov.recovery.codeStatus', 'skipped')
|
||||||
response.setSessionAttribute('agov.recovery.codeDetailStatus', "new login factor already registered (${session['agov.recovery.newLoginFactor']})")
|
response.setSessionAttribute('agov.recovery.codeDetailStatus', "new login factor already registered (${session['agov.recovery.newLoginFactor']})")
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LOG.debug("Recovery: email")
|
LOG.debug("Recovery: email")
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Recovery: Failed to convert '${maxLoi}' to AGOVaq")
|
LOG.error("Recovery: Failed to convert '${maxLoi}' to AGOVaq")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// maxLoi is null
|
// maxLoi is null
|
||||||
LOG.debug("Recovery: no 'AGOV-Loi'-role assigned to user ${user}")
|
LOG.debug("Recovery: no 'AGOV-Loi'-role assigned to user ${user}")
|
||||||
if ((hasRecoveryRole != null) && (mustRecover == null)) {
|
if ((hasRecoveryRole != null) && (mustRecover == null)) {
|
||||||
response.setResult('notFullyRegistered')
|
response.setResult('notFullyRegistered')
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Recovery: no 'AGOV-Loi'-role assigned to user ${user} and no recovery role ")
|
LOG.error("Recovery: no 'AGOV-Loi'-role assigned to user ${user} and no recovery role ")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// state != ACTIVE and no lasterror should not happen
|
// state != ACTIVE and no lasterror should not happen
|
||||||
LOG.error("Recovery: state='${userState}' but not lasterror set")
|
LOG.error("Recovery: state='${userState}' but not lasterror set")
|
||||||
response.setNote('lasterror', '9909')
|
response.setNote('lasterror', '9909')
|
||||||
response.setNote('lasterrorinfo', 'internal error')
|
response.setNote('lasterrorinfo', 'internal error')
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e = StackTraceUtils.sanitize(e)
|
e = StackTraceUtils.sanitize(e)
|
||||||
def affectedLines = e.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
def affectedLines = e.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
||||||
LOG.error("FATAL: Recovery processing failed (at lines: ${affectedLines})", e)
|
LOG.error("FATAL: Recovery processing failed (at lines: ${affectedLines})", e)
|
||||||
response.setNote('lasterror', '9909')
|
response.setNote('lasterror', '9909')
|
||||||
response.setNote('lasterrorinfo', 'internal error')
|
response.setNote('lasterrorinfo', 'internal error')
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.error("Recovery: userDto missing or failure before (lasterror='${notes.getProperty('lasterror', '-')}')")
|
LOG.error("Recovery: userDto missing or failure before (lasterror='${notes.getProperty('lasterror', '-')}')")
|
||||||
response.setNote('lasterror', '9909')
|
response.setNote('lasterror', '9909')
|
||||||
response.setNote('lasterrorinfo', 'internal error')
|
response.setNote('lasterrorinfo', 'internal error')
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,39 +1,39 @@
|
||||||
import groovy.json.JsonSlurper
|
import groovy.json.JsonSlurper
|
||||||
import io.opentelemetry.api.trace.Span
|
import io.opentelemetry.api.trace.Span
|
||||||
|
|
||||||
def url = parameters.get('url')
|
def url = parameters.get('url')
|
||||||
def realIpHttpHeaderName = parameters.get('realIpHttpHeaderName') ?: 'X-Real-IP'
|
def realIpHttpHeaderName = parameters.get('realIpHttpHeaderName') ?: 'X-Real-IP'
|
||||||
def ip = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def ip = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
def spanCtxt = Span.current().getSpanContext()
|
def spanCtxt = Span.current().getSpanContext()
|
||||||
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
||||||
|
|
||||||
def jsonSlurper = new JsonSlurper()
|
def jsonSlurper = new JsonSlurper()
|
||||||
def httpClient = HttpClients.create(parameters)
|
def httpClient = HttpClients.create(parameters)
|
||||||
def httpResponse = Http.get().url(url).header('traceparent', traceparent)
|
def httpResponse = Http.get().url(url).header('traceparent', traceparent)
|
||||||
.header(realIpHttpHeaderName, ip).build().send(httpClient)
|
.header(realIpHttpHeaderName, ip).build().send(httpClient)
|
||||||
|
|
||||||
LOG.debug('Response Status Code: ' + httpResponse.code())
|
LOG.debug('Response Status Code: ' + httpResponse.code())
|
||||||
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
||||||
|
|
||||||
if (httpResponse.code() == 200) {
|
if (httpResponse.code() == 200) {
|
||||||
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
||||||
|
|
||||||
response.setSessionAttribute('agov.recovery.captchaSettings.enabled', String.valueOf(json.friendlyCaptureClientSettings.enabled))
|
response.setSessionAttribute('agov.recovery.captchaSettings.enabled', String.valueOf(json.friendlyCaptureClientSettings.enabled))
|
||||||
response.setSessionAttribute('agov.recovery.captchaSettings.siteKey', json.friendlyCaptureClientSettings.siteKey)
|
response.setSessionAttribute('agov.recovery.captchaSettings.siteKey', json.friendlyCaptureClientSettings.siteKey)
|
||||||
response.setSessionAttribute('agov.recovery.captchaSettings.puzzleUrl', json.friendlyCaptureClientSettings.puzzleUrl)
|
response.setSessionAttribute('agov.recovery.captchaSettings.puzzleUrl', json.friendlyCaptureClientSettings.puzzleUrl)
|
||||||
|
|
||||||
|
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
} else {
|
} else {
|
||||||
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Unexpected HTTP reponse')
|
response.setError(1, 'Unexpected HTTP reponse')
|
||||||
}
|
}
|
||||||
} catch (all) {
|
} catch (all) {
|
||||||
// Handle exception and set the transition
|
// Handle exception and set the transition
|
||||||
LOG.error('error: ' + all, all)
|
LOG.error('error: ' + all, all)
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Exception during HTTP call')
|
response.setError(1, 'Exception during HTTP call')
|
||||||
}
|
}
|
|
@ -60,4 +60,4 @@ try {
|
||||||
LOG.error("Friendly captcha failed with a general error '${all}' for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }', service-url: ${url}")
|
LOG.error("Friendly captcha failed with a general error '${all}' for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }', service-url: ${url}")
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Exception during HTTP call')
|
response.setError(1, 'Exception during HTTP call')
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,4 @@ if (inargs['cd'] != null) {
|
||||||
if (inargs['cd'] == null && session['agov.recovery.code'] != null) {
|
if (inargs['cd'] == null && session['agov.recovery.code'] != null) {
|
||||||
response.setResult('exit.1')
|
response.setResult('exit.1')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||||
|
|
||||||
if (inargs['recovery'] != null && inargs['recovery'] == 'recovery' ) {
|
if (inargs['recovery'] != null && inargs['recovery'] == 'recovery' ) {
|
||||||
// clean up SAML state, to make sure the redirect will really be processed
|
// clean up SAML state, to make sure the redirect will really be processed
|
||||||
// IdentityProviderState sets session attributes as follows
|
// IdentityProviderState sets session attributes as follows
|
||||||
// <IDP-State-Name>-session-participants.<SAML-RP-ISSUER> = <ACS-URL>
|
// <IDP-State-Name>-session-participants.<SAML-RP-ISSUER> = <ACS-URL>
|
||||||
// State name contains the name of the pattern 'Recovery_redirectAgovMe'
|
// State name contains the name of the pattern 'Recovery_redirectAgovMe'
|
||||||
def s = request.getAuthSession(true)
|
def s = request.getAuthSession(true)
|
||||||
def sessionKeySet = new HashSet(session.keySet())
|
def sessionKeySet = new HashSet(session.keySet())
|
||||||
sessionKeySet.each { key ->
|
sessionKeySet.each { key ->
|
||||||
if ( key ==~ /.*Recovery_redirectAgovMe-session-participants.*/ ) {
|
if ( key ==~ /.*Recovery_redirectAgovMe-session-participants.*/ ) {
|
||||||
LOG.debug("Deleted session attribute '${key}'")
|
LOG.debug("Deleted session attribute '${key}'")
|
||||||
s.removeAttribute(key)
|
s.removeAttribute(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach this, display the GUI again
|
// if we reach this, display the GUI again
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
|
@ -1,41 +1,41 @@
|
||||||
import io.opentelemetry.api.trace.Span
|
import io.opentelemetry.api.trace.Span
|
||||||
|
|
||||||
def url = parameters.get('url')
|
def url = parameters.get('url')
|
||||||
def email = inargs['email']
|
def email = inargs['email']
|
||||||
def language = session['ch.nevis.session.user.language'] ?: 'en'
|
def language = session['ch.nevis.session.user.language'] ?: 'en'
|
||||||
def payload = '{ "email": "' + email + '", "language": "' + language + '"}'
|
def payload = '{ "email": "' + email + '", "language": "' + language + '"}'
|
||||||
|
|
||||||
try {
|
try {
|
||||||
def spanCtxt = Span.current().getSpanContext()
|
def spanCtxt = Span.current().getSpanContext()
|
||||||
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
def traceparent = "00-${spanCtxt.getTraceId()}-${spanCtxt.getSpanId()}-${spanCtxt.getTraceFlags().asHex()}"
|
||||||
|
|
||||||
def httpClient = HttpClients.create(parameters)
|
def httpClient = HttpClients.create(parameters)
|
||||||
def httpResponse = Http.post()
|
def httpResponse = Http.post()
|
||||||
.url(url)
|
.url(url)
|
||||||
.header("Accept", "application/json")
|
.header("Accept", "application/json")
|
||||||
.header("traceparent", traceparent)
|
.header("traceparent", traceparent)
|
||||||
.entity(Http.entity()
|
.entity(Http.entity()
|
||||||
.content(payload)
|
.content(payload)
|
||||||
.contentType("application/json")
|
.contentType("application/json")
|
||||||
// .charSet("utf-8")
|
// .charSet("utf-8")
|
||||||
.build())
|
.build())
|
||||||
.build()
|
.build()
|
||||||
.send(httpClient)
|
.send(httpClient)
|
||||||
|
|
||||||
LOG.info('Response Message: ' + httpResponse.reasonPhrase())
|
LOG.info('Response Message: ' + httpResponse.reasonPhrase())
|
||||||
LOG.info('Response Status Code: ' + httpResponse.code())
|
LOG.info('Response Status Code: ' + httpResponse.code())
|
||||||
LOG.info('Response: ' + httpResponse.bodyAsString())
|
LOG.info('Response: ' + httpResponse.bodyAsString())
|
||||||
|
|
||||||
if (httpResponse.code() == 200) {
|
if (httpResponse.code() == 200) {
|
||||||
response.setResult('ok')
|
response.setResult('ok')
|
||||||
} else {
|
} else {
|
||||||
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Unexpected HTTP reponse')
|
response.setError(1, 'Unexpected HTTP reponse')
|
||||||
}
|
}
|
||||||
} catch (all) {
|
} catch (all) {
|
||||||
// Handle exception and set the transition
|
// Handle exception and set the transition
|
||||||
LOG.error('error: ' + all, all)
|
LOG.error('error: ' + all, all)
|
||||||
response.setResult('error')
|
response.setResult('error')
|
||||||
response.setError(1, 'Exception during HTTP call')
|
response.setError(1, 'Exception during HTTP call')
|
||||||
}
|
}
|
|
@ -8,4 +8,4 @@ response.setHeader('IDP-AUTH', 'Timeout')
|
||||||
|
|
||||||
// CONTINUE to keep the other request beeing processed
|
// CONTINUE to keep the other request beeing processed
|
||||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||||
return
|
return
|
||||||
|
|
|
@ -29,3 +29,4 @@ if ( inargs['submit'] && inargs['submit'] == 'submit' ) {
|
||||||
|
|
||||||
response.setResult('stay')
|
response.setResult('stay')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -22,4 +22,4 @@ if ( inargs['continue'] && inargs['continue'] == 'continue' ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
response.setResult('stay')
|
response.setResult('stay')
|
||||||
return
|
return
|
||||||
|
|
|
@ -11,8 +11,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
type: "NevisFIDO"
|
type: "NevisFIDO"
|
||||||
replicas: 1
|
replicas: 1
|
||||||
version: "8.2411.2"
|
version: "8.2505.5"
|
||||||
gitInitVersion: "1.3.0"
|
gitInitVersion: "1.4.0"
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
ports:
|
ports:
|
||||||
rest: 9443
|
rest: 9443
|
||||||
|
@ -40,18 +40,19 @@ spec:
|
||||||
management:
|
management:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/nevisfido/health"
|
path: "/nevisfido/health"
|
||||||
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
timeoutSeconds: 6
|
timeoutSeconds: 6
|
||||||
failureThreshold: 50
|
failureThreshold: 30
|
||||||
podDisruptionBudget:
|
podDisruptionBudget:
|
||||||
maxUnavailable: "50%"
|
maxUnavailable: "50%"
|
||||||
git:
|
git:
|
||||||
tag: "r-8c160b6ed06647cec021e38b8bc8f4dffaab04c1"
|
tag: "r-484395a405f9f7123da379fa8df82e197d2dbd71"
|
||||||
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/fido-uaf"
|
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/fido-uaf"
|
||||||
credentials: "git-credentials"
|
credentials: "git-credentials"
|
||||||
database:
|
database:
|
||||||
name: "fido-uaf"
|
name: "fido-uaf"
|
||||||
requiredVersion: "8.2411.1"
|
requiredVersion: "8.2505.5"
|
||||||
keystores:
|
keystores:
|
||||||
- "fido-uaf-default-server-identity"
|
- "fido-uaf-default-server-identity"
|
||||||
- "fido-uaf-default-client-identity"
|
- "fido-uaf-default-client-identity"
|
||||||
|
|
|
@ -11,7 +11,7 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
type: "NevisFIDO"
|
type: "NevisFIDO"
|
||||||
databaseType: "MariaDB"
|
databaseType: "MariaDB"
|
||||||
version: "8.2411.2"
|
version: "8.2505.5"
|
||||||
url: "mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat"
|
url: "mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat"
|
||||||
port: 3306
|
port: 3306
|
||||||
database: "nevisfido_uaf"
|
database: "nevisfido_uaf"
|
||||||
|
|
|
@ -9,4 +9,4 @@
|
||||||
"token_uri": "https://oauth2.googleapis.com/token",
|
"token_uri": "https://oauth2.googleapis.com/token",
|
||||||
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
||||||
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/agov-dev%40agov-test.iam.gserviceaccount.com"
|
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/agov-dev%40agov-test.iam.gserviceaccount.com"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,5 @@ JAVA_OPTS=(
|
||||||
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
||||||
"-Dotel.javaagent.logging=application"
|
"-Dotel.javaagent.logging=application"
|
||||||
"-Dotel.javaagent.configuration-file=/var/opt/nevisfido/default/conf/otel.properties"
|
"-Dotel.javaagent.configuration-file=/var/opt/nevisfido/default/conf/otel.properties"
|
||||||
"-Dotel.resource.attributes=service.version=8.2411.2,service.instance.id=$HOSTNAME"
|
"-Dotel.resource.attributes=service.version=8.2505.5,service.instance.id=$HOSTNAME"
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,14 +3,13 @@
|
||||||
"aaid" : "F1D0#0001",
|
"aaid" : "F1D0#0001",
|
||||||
"description" : "Android NEVIS Mobile Authentication PIN Authenticator",
|
"description" : "Android NEVIS Mobile Authentication PIN Authenticator",
|
||||||
"assertionScheme" : "UAFV1TLV",
|
"assertionScheme" : "UAFV1TLV",
|
||||||
"attestationRootCertificates" : [
|
"attestationRootCertificates" : [],
|
||||||
"MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfBPb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk",
|
"supportedExtensions" : [
|
||||||
"MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAzNzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnuXKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83Uh6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cnoL/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2okQBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vAD32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAImMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoWFua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09ojm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUBZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCHex0SdDrx+tWUDqG8At2JHA==",
|
{
|
||||||
"MIIFHDCCAwSgAwIBAgIJAMNrfES5rhgxMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjExMTE3MjMxMDQyWhcNMzYxMTEzMjMxMDQyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBTNNZe5cuf8oiq+jV0itTGzWVhSTjOBEk2FQvh11J3o3lna0o7rd8RFHnN00q4hi6TapFhh4qaw/iG6Xg+xOan63niLWIC5GOPFgPeYXM9+nBb3zZzC8ABypYuCusWCmt6Tn3+Pjbz3MTVhRGXuT/TQH4KGFY4PhvzAyXwdjTOCXID+aHud4RLcSySr0Fq/L+R8TWalvM1wJJPhyRjqRCJerGtfBagiALzvhnmY7U1qFcS0NCnKjoO7oFedKdWlZz0YAfu3aGCJd4KHT0MsGiLZez9WP81xYSrKMNEsDK+zK5fVzw6jA7cxmpXcARTnmAuGUeI7VVDhDzKeVOctf3a0qQLwC+d0+xrETZ4r2fRGNw2YEs2W8Qj6oDcfPvq9JySe7pJ6wcHnl5EZ0lwc4xH7Y4Dx9RA1JlfooLMw3tOdJZH0enxPXaydfAD3YifeZpFaUzicHeLzVJLt9dvGB0bHQLE4+EqKFgOZv2EoP686DQqbVS1u+9k0p2xbMA105TBIk7npraa8VM0fnrRKi7wlZKwdH+aNAyhbXRW9xsnODJ+g8eF452zvbiKKngEKirK5LGieoXBX7tZ9D1GNBH2Ob3bKOwwIWdEFle/YF/h6zWgdeoaNGDqVBrLr2+0DtWoiB1aDEjLWl9FmyIUyUm7mD/vFDkzF+wm7cyWpQpCVQ==",
|
"id" : "ch.nevis.auth.fido.uaf.google-attestation-root-keys",
|
||||||
"MIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgwNzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGICW/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2GtkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkxoSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mFmr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPzlHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVwn6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1EuzbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHovaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHnw1IdYIg2Wxg7yHcQZemFQg==",
|
"fail_if_unknown" : false,
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc=",
|
"data" : "[ \"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAQ==\" ]"
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrJblQMA0GCSqGSIb3DQEBCwUAMDoxDTALBgNVBAMMBHRlc3QxCzAJBgNVBAYTAkNIMRwwGgYJKoZIhvcNAQkBFg1mYWtlQGFjbWUuY29tMB4XDTI0MDgxOTE0NTg0MFoXDTI1MDgxOTE0NTg0MFowOjENMAsGA1UEAwwEdGVzdDELMAkGA1UEBhMCQ0gxHDAaBgkqhkiG9w0BCQEWDWZha2VAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcWDBNmdq13fYHnhsmLndAW+MfbI6PeU4OenqfbrTtQUxqpyqhP6QccPYKX2SK3JeQo5uuF1jRD/9i9vAXI9NyiMMHSItjt9LjRs7bWnY4lokYGCAcSZooR9fGZX63dBSQo73V7MC8LDFGy5rw6dGDOmh0ktKxFzaT/nav8/Mx8FyG7M9+b5OPIBo2yze5Rd5cdErGJuUYa9No93BBr5tq+JfnmR/gwgCOke97ovhNj+sMu5bt946AxC6t00wNyPNVlJHKi1os0c/pWztTQkoRAx/w0JYKS9Afl0ZnGWQQ5PNLHHecp2GzriBpQAPXq81QTbOh5H7SzvhkaFQ4oxstAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD8GOaeMDqj2mzMmCqR6Cr3ChkbDAkdsBa5lOAikMKs7/tJyaw8iA5yH0nyobC58Jb61IATuxABPUALhP3RiNsUhnQQF/Dh+6CnCTD/2wsZmr8vUvNqyCLom+xkMT6Wayd9LYW4UONARv1qCLVI4RhiAr5kcomwqZnuj2DRF697lbSQDoz3iuKrCyBYSCBhS+k7UXpqpMyB2D6quRuPqh7JNtMjGSeMiNpMXhx5f4kl1YWb8NU93LDwHFR2kwnGmPA3M272VitcJC4dz3itGRKm9EYGd6d5D7kdC6lqpZPSIopChvXDyVrXjQgckvgtSGKscs6AvYgjthJGsR2z3Eao=",
|
}
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc="
|
|
||||||
],
|
],
|
||||||
"attestationTypes" : [ 15879, 15880 ],
|
"attestationTypes" : [ 15879, 15880 ],
|
||||||
"upv" : [ {
|
"upv" : [ {
|
||||||
|
@ -34,14 +33,13 @@
|
||||||
"aaid" : "F1D0#0002",
|
"aaid" : "F1D0#0002",
|
||||||
"description" : "Android NEVIS Mobile Authentication Fingerprint Authenticator",
|
"description" : "Android NEVIS Mobile Authentication Fingerprint Authenticator",
|
||||||
"assertionScheme" : "UAFV1TLV",
|
"assertionScheme" : "UAFV1TLV",
|
||||||
"attestationRootCertificates" : [
|
"attestationRootCertificates" : [],
|
||||||
"MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfBPb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk",
|
"supportedExtensions" : [
|
||||||
"MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAzNzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnuXKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83Uh6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cnoL/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2okQBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vAD32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAImMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoWFua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09ojm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUBZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCHex0SdDrx+tWUDqG8At2JHA==",
|
{
|
||||||
"MIIFHDCCAwSgAwIBAgIJAMNrfES5rhgxMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjExMTE3MjMxMDQyWhcNMzYxMTEzMjMxMDQyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBTNNZe5cuf8oiq+jV0itTGzWVhSTjOBEk2FQvh11J3o3lna0o7rd8RFHnN00q4hi6TapFhh4qaw/iG6Xg+xOan63niLWIC5GOPFgPeYXM9+nBb3zZzC8ABypYuCusWCmt6Tn3+Pjbz3MTVhRGXuT/TQH4KGFY4PhvzAyXwdjTOCXID+aHud4RLcSySr0Fq/L+R8TWalvM1wJJPhyRjqRCJerGtfBagiALzvhnmY7U1qFcS0NCnKjoO7oFedKdWlZz0YAfu3aGCJd4KHT0MsGiLZez9WP81xYSrKMNEsDK+zK5fVzw6jA7cxmpXcARTnmAuGUeI7VVDhDzKeVOctf3a0qQLwC+d0+xrETZ4r2fRGNw2YEs2W8Qj6oDcfPvq9JySe7pJ6wcHnl5EZ0lwc4xH7Y4Dx9RA1JlfooLMw3tOdJZH0enxPXaydfAD3YifeZpFaUzicHeLzVJLt9dvGB0bHQLE4+EqKFgOZv2EoP686DQqbVS1u+9k0p2xbMA105TBIk7npraa8VM0fnrRKi7wlZKwdH+aNAyhbXRW9xsnODJ+g8eF452zvbiKKngEKirK5LGieoXBX7tZ9D1GNBH2Ob3bKOwwIWdEFle/YF/h6zWgdeoaNGDqVBrLr2+0DtWoiB1aDEjLWl9FmyIUyUm7mD/vFDkzF+wm7cyWpQpCVQ==",
|
"id" : "ch.nevis.auth.fido.uaf.google-attestation-root-keys",
|
||||||
"MIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgwNzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGICW/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2GtkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkxoSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mFmr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPzlHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVwn6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1EuzbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHovaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHnw1IdYIg2Wxg7yHcQZemFQg==",
|
"fail_if_unknown" : false,
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc=",
|
"data" : "[ \"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAQ==\" ]"
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrJblQMA0GCSqGSIb3DQEBCwUAMDoxDTALBgNVBAMMBHRlc3QxCzAJBgNVBAYTAkNIMRwwGgYJKoZIhvcNAQkBFg1mYWtlQGFjbWUuY29tMB4XDTI0MDgxOTE0NTg0MFoXDTI1MDgxOTE0NTg0MFowOjENMAsGA1UEAwwEdGVzdDELMAkGA1UEBhMCQ0gxHDAaBgkqhkiG9w0BCQEWDWZha2VAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcWDBNmdq13fYHnhsmLndAW+MfbI6PeU4OenqfbrTtQUxqpyqhP6QccPYKX2SK3JeQo5uuF1jRD/9i9vAXI9NyiMMHSItjt9LjRs7bWnY4lokYGCAcSZooR9fGZX63dBSQo73V7MC8LDFGy5rw6dGDOmh0ktKxFzaT/nav8/Mx8FyG7M9+b5OPIBo2yze5Rd5cdErGJuUYa9No93BBr5tq+JfnmR/gwgCOke97ovhNj+sMu5bt946AxC6t00wNyPNVlJHKi1os0c/pWztTQkoRAx/w0JYKS9Afl0ZnGWQQ5PNLHHecp2GzriBpQAPXq81QTbOh5H7SzvhkaFQ4oxstAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD8GOaeMDqj2mzMmCqR6Cr3ChkbDAkdsBa5lOAikMKs7/tJyaw8iA5yH0nyobC58Jb61IATuxABPUALhP3RiNsUhnQQF/Dh+6CnCTD/2wsZmr8vUvNqyCLom+xkMT6Wayd9LYW4UONARv1qCLVI4RhiAr5kcomwqZnuj2DRF697lbSQDoz3iuKrCyBYSCBhS+k7UXpqpMyB2D6quRuPqh7JNtMjGSeMiNpMXhx5f4kl1YWb8NU93LDwHFR2kwnGmPA3M272VitcJC4dz3itGRKm9EYGd6d5D7kdC6lqpZPSIopChvXDyVrXjQgckvgtSGKscs6AvYgjthJGsR2z3Eao=",
|
}
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc="
|
|
||||||
],
|
],
|
||||||
"attestationTypes" : [ 15879, 15880 ],
|
"attestationTypes" : [ 15879, 15880 ],
|
||||||
"upv" : [ {
|
"upv" : [ {
|
||||||
|
@ -65,14 +63,13 @@
|
||||||
"aaid" : "F1D0#0003",
|
"aaid" : "F1D0#0003",
|
||||||
"description" : "Android NEVIS Mobile Authentication Biometric Authenticator",
|
"description" : "Android NEVIS Mobile Authentication Biometric Authenticator",
|
||||||
"assertionScheme" : "UAFV1TLV",
|
"assertionScheme" : "UAFV1TLV",
|
||||||
"attestationRootCertificates" : [
|
"attestationRootCertificates" : [],
|
||||||
"MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfBPb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk",
|
"supportedExtensions" : [
|
||||||
"MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAzNzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnuXKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83Uh6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cnoL/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2okQBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vAD32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAImMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoWFua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09ojm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUBZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCHex0SdDrx+tWUDqG8At2JHA==",
|
{
|
||||||
"MIIFHDCCAwSgAwIBAgIJAMNrfES5rhgxMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjExMTE3MjMxMDQyWhcNMzYxMTEzMjMxMDQyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBTNNZe5cuf8oiq+jV0itTGzWVhSTjOBEk2FQvh11J3o3lna0o7rd8RFHnN00q4hi6TapFhh4qaw/iG6Xg+xOan63niLWIC5GOPFgPeYXM9+nBb3zZzC8ABypYuCusWCmt6Tn3+Pjbz3MTVhRGXuT/TQH4KGFY4PhvzAyXwdjTOCXID+aHud4RLcSySr0Fq/L+R8TWalvM1wJJPhyRjqRCJerGtfBagiALzvhnmY7U1qFcS0NCnKjoO7oFedKdWlZz0YAfu3aGCJd4KHT0MsGiLZez9WP81xYSrKMNEsDK+zK5fVzw6jA7cxmpXcARTnmAuGUeI7VVDhDzKeVOctf3a0qQLwC+d0+xrETZ4r2fRGNw2YEs2W8Qj6oDcfPvq9JySe7pJ6wcHnl5EZ0lwc4xH7Y4Dx9RA1JlfooLMw3tOdJZH0enxPXaydfAD3YifeZpFaUzicHeLzVJLt9dvGB0bHQLE4+EqKFgOZv2EoP686DQqbVS1u+9k0p2xbMA105TBIk7npraa8VM0fnrRKi7wlZKwdH+aNAyhbXRW9xsnODJ+g8eF452zvbiKKngEKirK5LGieoXBX7tZ9D1GNBH2Ob3bKOwwIWdEFle/YF/h6zWgdeoaNGDqVBrLr2+0DtWoiB1aDEjLWl9FmyIUyUm7mD/vFDkzF+wm7cyWpQpCVQ==",
|
"id" : "ch.nevis.auth.fido.uaf.google-attestation-root-keys",
|
||||||
"MIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgwNzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGICW/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2GtkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkxoSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mFmr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPzlHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVwn6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1EuzbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHovaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHnw1IdYIg2Wxg7yHcQZemFQg==",
|
"fail_if_unknown" : false,
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc=",
|
"data" : "[ \"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAQ==\" ]"
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrJblQMA0GCSqGSIb3DQEBCwUAMDoxDTALBgNVBAMMBHRlc3QxCzAJBgNVBAYTAkNIMRwwGgYJKoZIhvcNAQkBFg1mYWtlQGFjbWUuY29tMB4XDTI0MDgxOTE0NTg0MFoXDTI1MDgxOTE0NTg0MFowOjENMAsGA1UEAwwEdGVzdDELMAkGA1UEBhMCQ0gxHDAaBgkqhkiG9w0BCQEWDWZha2VAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcWDBNmdq13fYHnhsmLndAW+MfbI6PeU4OenqfbrTtQUxqpyqhP6QccPYKX2SK3JeQo5uuF1jRD/9i9vAXI9NyiMMHSItjt9LjRs7bWnY4lokYGCAcSZooR9fGZX63dBSQo73V7MC8LDFGy5rw6dGDOmh0ktKxFzaT/nav8/Mx8FyG7M9+b5OPIBo2yze5Rd5cdErGJuUYa9No93BBr5tq+JfnmR/gwgCOke97ovhNj+sMu5bt946AxC6t00wNyPNVlJHKi1os0c/pWztTQkoRAx/w0JYKS9Afl0ZnGWQQ5PNLHHecp2GzriBpQAPXq81QTbOh5H7SzvhkaFQ4oxstAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD8GOaeMDqj2mzMmCqR6Cr3ChkbDAkdsBa5lOAikMKs7/tJyaw8iA5yH0nyobC58Jb61IATuxABPUALhP3RiNsUhnQQF/Dh+6CnCTD/2wsZmr8vUvNqyCLom+xkMT6Wayd9LYW4UONARv1qCLVI4RhiAr5kcomwqZnuj2DRF697lbSQDoz3iuKrCyBYSCBhS+k7UXpqpMyB2D6quRuPqh7JNtMjGSeMiNpMXhx5f4kl1YWb8NU93LDwHFR2kwnGmPA3M272VitcJC4dz3itGRKm9EYGd6d5D7kdC6lqpZPSIopChvXDyVrXjQgckvgtSGKscs6AvYgjthJGsR2z3Eao=",
|
}
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc="
|
|
||||||
],
|
],
|
||||||
"attestationTypes" : [ 15879, 15880 ],
|
"attestationTypes" : [ 15879, 15880 ],
|
||||||
"upv" : [ {
|
"upv" : [ {
|
||||||
|
@ -96,14 +93,13 @@
|
||||||
"aaid" : "F1D0#0004",
|
"aaid" : "F1D0#0004",
|
||||||
"description" : "Android NEVIS Mobile Authentication Device Passcode Authenticator",
|
"description" : "Android NEVIS Mobile Authentication Device Passcode Authenticator",
|
||||||
"assertionScheme" : "UAFV1TLV",
|
"assertionScheme" : "UAFV1TLV",
|
||||||
"attestationRootCertificates" : [
|
"attestationRootCertificates" : [],
|
||||||
"MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfBPb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk",
|
"supportedExtensions" : [
|
||||||
"MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAzNzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnuXKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83Uh6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cnoL/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2okQBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vAD32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAImMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoWFua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09ojm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUBZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCHex0SdDrx+tWUDqG8At2JHA==",
|
{
|
||||||
"MIIFHDCCAwSgAwIBAgIJAMNrfES5rhgxMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjExMTE3MjMxMDQyWhcNMzYxMTEzMjMxMDQyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBTNNZe5cuf8oiq+jV0itTGzWVhSTjOBEk2FQvh11J3o3lna0o7rd8RFHnN00q4hi6TapFhh4qaw/iG6Xg+xOan63niLWIC5GOPFgPeYXM9+nBb3zZzC8ABypYuCusWCmt6Tn3+Pjbz3MTVhRGXuT/TQH4KGFY4PhvzAyXwdjTOCXID+aHud4RLcSySr0Fq/L+R8TWalvM1wJJPhyRjqRCJerGtfBagiALzvhnmY7U1qFcS0NCnKjoO7oFedKdWlZz0YAfu3aGCJd4KHT0MsGiLZez9WP81xYSrKMNEsDK+zK5fVzw6jA7cxmpXcARTnmAuGUeI7VVDhDzKeVOctf3a0qQLwC+d0+xrETZ4r2fRGNw2YEs2W8Qj6oDcfPvq9JySe7pJ6wcHnl5EZ0lwc4xH7Y4Dx9RA1JlfooLMw3tOdJZH0enxPXaydfAD3YifeZpFaUzicHeLzVJLt9dvGB0bHQLE4+EqKFgOZv2EoP686DQqbVS1u+9k0p2xbMA105TBIk7npraa8VM0fnrRKi7wlZKwdH+aNAyhbXRW9xsnODJ+g8eF452zvbiKKngEKirK5LGieoXBX7tZ9D1GNBH2Ob3bKOwwIWdEFle/YF/h6zWgdeoaNGDqVBrLr2+0DtWoiB1aDEjLWl9FmyIUyUm7mD/vFDkzF+wm7cyWpQpCVQ==",
|
"id" : "ch.nevis.auth.fido.uaf.google-attestation-root-keys",
|
||||||
"MIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgwNzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGICW/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2GtkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkxoSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mFmr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPzlHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVwn6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1EuzbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHovaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHnw1IdYIg2Wxg7yHcQZemFQg==",
|
"fail_if_unknown" : false,
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc=",
|
"data" : "[ \"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAQ==\" ]"
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrJblQMA0GCSqGSIb3DQEBCwUAMDoxDTALBgNVBAMMBHRlc3QxCzAJBgNVBAYTAkNIMRwwGgYJKoZIhvcNAQkBFg1mYWtlQGFjbWUuY29tMB4XDTI0MDgxOTE0NTg0MFoXDTI1MDgxOTE0NTg0MFowOjENMAsGA1UEAwwEdGVzdDELMAkGA1UEBhMCQ0gxHDAaBgkqhkiG9w0BCQEWDWZha2VAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcWDBNmdq13fYHnhsmLndAW+MfbI6PeU4OenqfbrTtQUxqpyqhP6QccPYKX2SK3JeQo5uuF1jRD/9i9vAXI9NyiMMHSItjt9LjRs7bWnY4lokYGCAcSZooR9fGZX63dBSQo73V7MC8LDFGy5rw6dGDOmh0ktKxFzaT/nav8/Mx8FyG7M9+b5OPIBo2yze5Rd5cdErGJuUYa9No93BBr5tq+JfnmR/gwgCOke97ovhNj+sMu5bt946AxC6t00wNyPNVlJHKi1os0c/pWztTQkoRAx/w0JYKS9Afl0ZnGWQQ5PNLHHecp2GzriBpQAPXq81QTbOh5H7SzvhkaFQ4oxstAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD8GOaeMDqj2mzMmCqR6Cr3ChkbDAkdsBa5lOAikMKs7/tJyaw8iA5yH0nyobC58Jb61IATuxABPUALhP3RiNsUhnQQF/Dh+6CnCTD/2wsZmr8vUvNqyCLom+xkMT6Wayd9LYW4UONARv1qCLVI4RhiAr5kcomwqZnuj2DRF697lbSQDoz3iuKrCyBYSCBhS+k7UXpqpMyB2D6quRuPqh7JNtMjGSeMiNpMXhx5f4kl1YWb8NU93LDwHFR2kwnGmPA3M272VitcJC4dz3itGRKm9EYGd6d5D7kdC6lqpZPSIopChvXDyVrXjQgckvgtSGKscs6AvYgjthJGsR2z3Eao=",
|
}
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc="
|
|
||||||
],
|
],
|
||||||
"attestationTypes" : [ 15879, 15880 ],
|
"attestationTypes" : [ 15879, 15880 ],
|
||||||
"upv" : [ {
|
"upv" : [ {
|
||||||
|
@ -127,14 +123,13 @@
|
||||||
"aaid" : "F1D0#0005",
|
"aaid" : "F1D0#0005",
|
||||||
"description" : "Android NEVIS Mobile Authentication Password Authenticator",
|
"description" : "Android NEVIS Mobile Authentication Password Authenticator",
|
||||||
"assertionScheme" : "UAFV1TLV",
|
"assertionScheme" : "UAFV1TLV",
|
||||||
"attestationRootCertificates" : [
|
"attestationRootCertificates" : [],
|
||||||
"MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYyODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYDVR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lkLmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQADggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfBPb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00mqC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rYDBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPmQUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4uJU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyDCdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79IyZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxDqwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23UaicMDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk",
|
"supportedExtensions" : [
|
||||||
"MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAzNzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnuXKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83Uh6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cnoL/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2okQBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vAD32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAImMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoWFua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09ojm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUBZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCHex0SdDrx+tWUDqG8At2JHA==",
|
{
|
||||||
"MIIFHDCCAwSgAwIBAgIJAMNrfES5rhgxMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjExMTE3MjMxMDQyWhcNMzYxMTEzMjMxMDQyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBTNNZe5cuf8oiq+jV0itTGzWVhSTjOBEk2FQvh11J3o3lna0o7rd8RFHnN00q4hi6TapFhh4qaw/iG6Xg+xOan63niLWIC5GOPFgPeYXM9+nBb3zZzC8ABypYuCusWCmt6Tn3+Pjbz3MTVhRGXuT/TQH4KGFY4PhvzAyXwdjTOCXID+aHud4RLcSySr0Fq/L+R8TWalvM1wJJPhyRjqRCJerGtfBagiALzvhnmY7U1qFcS0NCnKjoO7oFedKdWlZz0YAfu3aGCJd4KHT0MsGiLZez9WP81xYSrKMNEsDK+zK5fVzw6jA7cxmpXcARTnmAuGUeI7VVDhDzKeVOctf3a0qQLwC+d0+xrETZ4r2fRGNw2YEs2W8Qj6oDcfPvq9JySe7pJ6wcHnl5EZ0lwc4xH7Y4Dx9RA1JlfooLMw3tOdJZH0enxPXaydfAD3YifeZpFaUzicHeLzVJLt9dvGB0bHQLE4+EqKFgOZv2EoP686DQqbVS1u+9k0p2xbMA105TBIk7npraa8VM0fnrRKi7wlZKwdH+aNAyhbXRW9xsnODJ+g8eF452zvbiKKngEKirK5LGieoXBX7tZ9D1GNBH2Ob3bKOwwIWdEFle/YF/h6zWgdeoaNGDqVBrLr2+0DtWoiB1aDEjLWl9FmyIUyUm7mD/vFDkzF+wm7cyWpQpCVQ==",
|
"id" : "ch.nevis.auth.fido.uaf.google-attestation-root-keys",
|
||||||
"MIIFHDCCAwSgAwIBAgIJAPHBcqaZ6vUdMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMjIwMzIwMTgwNzQ4WhcNNDIwMzE1MTgwNzQ4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1UdIwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQB8cMqTllHc8U+qCrOlg3H7174lmaCsbo/bJ0C17JEgMLb4kvrqsXZs01U3mB/qABg/1t5Pd5AORHARs1hhqGICW/nKMav574f9rZN4PC2ZlufGXb7sIdJpGiO9ctRhiLuYuly10JccUZGEHpHSYM2GtkgYbZba6lsCPYAAP83cyDV+1aOkTf1RCp/lM0PKvmxYN10RYsK631jrleGdcdkxoSK//mSQbgcWnmAEZrzHoF1/0gso1HZgIn0YLzVhLSA/iXCX4QT2h3J5z3znluKG1nv8NQdxei2DIIhASWfu804CA96cQKTTlaae2fweqXjdN1/v2nqOhngNyz1361mFmr4XmaKH/ItTwOe72NI9ZcwS1lVaCvsIkTDCEXdm9rCNPAY10iTunIHFXRh+7KPzlHGewCq/8TOohBRn0/NNfh7uRslOSZ/xKbN9tMBtw37Z8d2vvnXq/YWdsm1+JLVwn6yYD/yacNJBlwpddla8eaVMjsF6nBnIgQOf9zKSe06nSTqvgwUHosgOECZJZ1EuzbH4yswbt02tKtKEFhx+v+OTge/06V+jGsqTWLsfrOCNLuA8H++z+pUENmpqnnHovaI47gC+TNpkgYGkkBT6B/m/U01BuOBBTzhIlMEZq9qkDWuM2cA5kW5V3FJUcfHnw1IdYIg2Wxg7yHcQZemFQg==",
|
"fail_if_unknown" : false,
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc=",
|
"data" : "[ \"MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdSSxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggjnar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGqC4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQoVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+OJtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/EgsTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRiigHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+MRPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9EaDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5UmAGMCAwEAAQ==\" ]"
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrJblQMA0GCSqGSIb3DQEBCwUAMDoxDTALBgNVBAMMBHRlc3QxCzAJBgNVBAYTAkNIMRwwGgYJKoZIhvcNAQkBFg1mYWtlQGFjbWUuY29tMB4XDTI0MDgxOTE0NTg0MFoXDTI1MDgxOTE0NTg0MFowOjENMAsGA1UEAwwEdGVzdDELMAkGA1UEBhMCQ0gxHDAaBgkqhkiG9w0BCQEWDWZha2VAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcWDBNmdq13fYHnhsmLndAW+MfbI6PeU4OenqfbrTtQUxqpyqhP6QccPYKX2SK3JeQo5uuF1jRD/9i9vAXI9NyiMMHSItjt9LjRs7bWnY4lokYGCAcSZooR9fGZX63dBSQo73V7MC8LDFGy5rw6dGDOmh0ktKxFzaT/nav8/Mx8FyG7M9+b5OPIBo2yze5Rd5cdErGJuUYa9No93BBr5tq+JfnmR/gwgCOke97ovhNj+sMu5bt946AxC6t00wNyPNVlJHKi1os0c/pWztTQkoRAx/w0JYKS9Afl0ZnGWQQ5PNLHHecp2GzriBpQAPXq81QTbOh5H7SzvhkaFQ4oxstAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAD8GOaeMDqj2mzMmCqR6Cr3ChkbDAkdsBa5lOAikMKs7/tJyaw8iA5yH0nyobC58Jb61IATuxABPUALhP3RiNsUhnQQF/Dh+6CnCTD/2wsZmr8vUvNqyCLom+xkMT6Wayd9LYW4UONARv1qCLVI4RhiAr5kcomwqZnuj2DRF697lbSQDoz3iuKrCyBYSCBhS+k7UXpqpMyB2D6quRuPqh7JNtMjGSeMiNpMXhx5f4kl1YWb8NU93LDwHFR2kwnGmPA3M272VitcJC4dz3itGRKm9EYGd6d5D7kdC6lqpZPSIopChvXDyVrXjQgckvgtSGKscs6AvYgjthJGsR2z3Eao=",
|
}
|
||||||
"MIIC8jCCAdqgAwIBAgIGAZFrLh2fMA0GCSqGSIb3DQEBCwUAMDoxDjAMBgNVBAMMBXRlc3R5MQswCQYDVQQGEwJVUzEbMBkGCSqGSIb3DQEJARYMYWJjQGFjbWUuY29tMB4XDTI0MDgxOTE1MDc1MFoXDTI1MDgxOTE1MDc1MFowOjEOMAwGA1UEAwwFdGVzdHkxCzAJBgNVBAYTAlVTMRswGQYJKoZIhvcNAQkBFgxhYmNAYWNtZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDqitlYBzaxbPF389ZT5xkSS9Le1qdIOuc+dLVpBSWP9PEJhVZROgdOHs5f666iAcBedQm73sew3rpl+02J4fSgGmPkIYm1G2vkIrpt0eB9KzSc0AiLZbrPcFZOLHcOLoqVTfoRhnmAksHDC2f8euNKhCyriK8xlJb/xPfAfCn4r58ZGsQPUS7cJL6FLYh7FjrqfYDS10VOrQvGOALrG5NUj1DdqRq0M+klgs+6oJdUZTtY62BKkWh3N+7moNvrqykpv+ydFUJltgezDcb4Br8Nkw/breSPnomRfyHIcAcfATZcOPJlI8pO0zFZDIz8r7ESMnBhAxNaZgsUhR2XbaqbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGw5XLY6GeFJMP350+djhcVqAw+E4HZqCJu1BMpYC0qS2D85fFi3gNuV0TnqB52abX1WBDDJK1CA0SPdyo/nX+qQzP6Dba1AVRKpRzdcsDsMDN3eMC08tajHgIIf5tNDv+HGE/MT2br4o5oducmQMOfV1NTJO1xhXYVqbsUnyrq3S6kD9WS8zRl6ruY1rT26eCQ4hTLHPaAiVsoXh5TBRXYCvGlAw7o2d9cmsbySforZ2wgdZwmu43B5eHNnt4NlDxZRyz6iEDP0nT877aB2ffsOKHAkJNuTvF5JSfnVzLmiyfa/7NI1ujfzcpA2UUXoWa7WN0wACiZQot8Zmswonjc="
|
|
||||||
],
|
],
|
||||||
"attestationTypes" : [ 15879, 15880 ],
|
"attestationTypes" : [ 15879, 15880 ],
|
||||||
"upv" : [ {
|
"upv" : [ {
|
||||||
|
@ -268,4 +263,5 @@
|
||||||
"publicKeyAlgAndEncodings" : [ 257 ],
|
"publicKeyAlgAndEncodings" : [ 257 ],
|
||||||
"tcDisplay" : 1,
|
"tcDisplay" : 1,
|
||||||
"tcDisplayContentType" : "text/plain"
|
"tcDisplayContentType" : "text/plain"
|
||||||
}]
|
}
|
||||||
|
]
|
||||||
|
|
|
@ -37,7 +37,7 @@ fido-uaf:
|
||||||
max-text-length: 2000
|
max-text-length: 2000
|
||||||
metadata:
|
metadata:
|
||||||
path: "conf/metadata/metadata.json"
|
path: "conf/metadata/metadata.json"
|
||||||
idm-connection-type: "soap"
|
idm-connection-type: "rest"
|
||||||
dispatchers:
|
dispatchers:
|
||||||
- type: "firebase-cloud-messaging"
|
- type: "firebase-cloud-messaging"
|
||||||
dry-run: false
|
dry-run: false
|
||||||
|
@ -45,6 +45,7 @@ fido-uaf:
|
||||||
registration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/registration"
|
registration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/registration"
|
||||||
authentication-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/authentication"
|
authentication-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/authentication"
|
||||||
deregistration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/deregistration"
|
deregistration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/deregistration"
|
||||||
|
message-ttl: "180s"
|
||||||
- type: "png-qr-code"
|
- type: "png-qr-code"
|
||||||
registration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/registration"
|
registration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/registration"
|
||||||
authentication-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/authentication"
|
authentication-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/authentication"
|
||||||
|
@ -54,8 +55,11 @@ fido-uaf:
|
||||||
authentication-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/authentication"
|
authentication-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/authentication"
|
||||||
deregistration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/deregistration"
|
deregistration-redeem-url: "https://auth.agov-w.azure.adnovum.net/nevisfido/token/redeem/deregistration"
|
||||||
base-url: "ch.agov.access-t://x-callback-url/authenticate"
|
base-url: "ch.agov.access-t://x-callback-url/authenticate"
|
||||||
basic-full-attestation:
|
full-basic-attestation:
|
||||||
android-verification-level: "strict"
|
android-verification-level: "default"
|
||||||
|
android-permissive-mode-enabled: true
|
||||||
|
android-attestation-key-revocation:
|
||||||
|
reload-interval: "21600s"
|
||||||
authorization:
|
authorization:
|
||||||
registration:
|
registration:
|
||||||
type: "sectoken"
|
type: "sectoken"
|
||||||
|
@ -95,18 +99,18 @@ fido-uaf:
|
||||||
session-repository:
|
session-repository:
|
||||||
type: "sql"
|
type: "sql"
|
||||||
jdbc-url: "jdbc:mariadb://mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat:3306/nevisfido_uaf?sslMode=disable&autocommit=true"
|
jdbc-url: "jdbc:mariadb://mariadb-session-store-service.adn-agov-nevisidm-ob-01-uat:3306/nevisfido_uaf?sslMode=disable&autocommit=true"
|
||||||
max-connection-lifetime: "10m"
|
|
||||||
user: "${exec:/var/opt/nevisfido/default/conf/credentials/dbUser}"
|
user: "${exec:/var/opt/nevisfido/default/conf/credentials/dbUser}"
|
||||||
password: "${exec:/var/opt/nevisfido/default/conf/credentials/dbPassword}"
|
password: "${exec:/var/opt/nevisfido/default/conf/credentials/dbPassword}"
|
||||||
schema-user: ""
|
|
||||||
schema-user-password: ""
|
|
||||||
automatic-db-schema-setup: false
|
automatic-db-schema-setup: false
|
||||||
|
max-connection-lifetime: "1800s"
|
||||||
|
connection-timeout: "30s"
|
||||||
|
min-connection-pool-size: 10
|
||||||
|
max-connection-pool-size: 10
|
||||||
|
max-connection-idle-time: "600s"
|
||||||
credential-repository:
|
credential-repository:
|
||||||
type: "nevisidm"
|
type: "nevisidm"
|
||||||
client-id: "cfa9c9b9-119f-4dff-9bb8-86d7c0cf2720"
|
client-id: "cfa9c9b9-119f-4dff-9bb8-86d7c0cf2720"
|
||||||
user-attribute: "extId"
|
user-attribute: "extId"
|
||||||
administration-url: "https://idm.adn-agov-nevisidm-admin-01-uat:8989/nevisidm/services/v1_46/AdminService"
|
|
||||||
admin-service-version: "v1_46"
|
|
||||||
rest-url: "https://idm.adn-agov-nevisidm-admin-01-uat:8989/nevisidm"
|
rest-url: "https://idm.adn-agov-nevisidm-admin-01-uat:8989/nevisidm"
|
||||||
keystore: "/var/opt/keys/own/fido-uaf-default-client-identity/keystore.p12"
|
keystore: "/var/opt/keys/own/fido-uaf-default-client-identity/keystore.p12"
|
||||||
keystore-type: "pkcs12"
|
keystore-type: "pkcs12"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
otel.service.name = fido-uaf
|
otel.service.name = fido-uaf
|
||||||
|
otel.traces.sampler = always_on
|
||||||
otel.traces.exporter = none
|
otel.traces.exporter = none
|
||||||
otel.metrics.exporter = none
|
otel.metrics.exporter = none
|
||||||
otel.logs.exporter = none
|
otel.logs.exporter = none
|
||||||
|
|
|
@ -21,4 +21,4 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,4 +41,4 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,4 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,4 +44,4 @@ if is_nevisfido_healthy():
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
raise_last_error_in_log()
|
raise_last_error_in_log()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -11,8 +11,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
type: "NevisFIDO"
|
type: "NevisFIDO"
|
||||||
replicas: 1
|
replicas: 1
|
||||||
version: "8.2411.2"
|
version: "8.2505.5"
|
||||||
gitInitVersion: "1.3.0"
|
gitInitVersion: "1.4.0"
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
ports:
|
ports:
|
||||||
management: 9089
|
management: 9089
|
||||||
|
@ -40,13 +40,14 @@ spec:
|
||||||
management:
|
management:
|
||||||
httpGet:
|
httpGet:
|
||||||
path: "/nevisfido/health"
|
path: "/nevisfido/health"
|
||||||
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
timeoutSeconds: 6
|
timeoutSeconds: 6
|
||||||
failureThreshold: 50
|
failureThreshold: 30
|
||||||
podDisruptionBudget:
|
podDisruptionBudget:
|
||||||
maxUnavailable: "50%"
|
maxUnavailable: "50%"
|
||||||
git:
|
git:
|
||||||
tag: "r-317ed268556b37656f27fb58fcffd4797cea27e4"
|
tag: "r-484395a405f9f7123da379fa8df82e197d2dbd71"
|
||||||
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/fido2"
|
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/fido2"
|
||||||
credentials: "git-credentials"
|
credentials: "git-credentials"
|
||||||
keystores:
|
keystores:
|
||||||
|
|
|
@ -6,5 +6,5 @@ JAVA_OPTS=(
|
||||||
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
||||||
"-Dotel.javaagent.logging=application"
|
"-Dotel.javaagent.logging=application"
|
||||||
"-Dotel.javaagent.configuration-file=/var/opt/nevisfido/default/conf/otel.properties"
|
"-Dotel.javaagent.configuration-file=/var/opt/nevisfido/default/conf/otel.properties"
|
||||||
"-Dotel.resource.attributes=service.version=8.2411.2,service.instance.id=$HOSTNAME"
|
"-Dotel.resource.attributes=service.version=8.2505.5,service.instance.id=$HOSTNAME"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,21 @@
|
||||||
|
fido2:
|
||||||
|
enabled: true
|
||||||
|
user-presence-requirement: "always"
|
||||||
|
rp-name: "AGOV-RelPartName"
|
||||||
|
rp-id: "adnovum.net"
|
||||||
|
origins:
|
||||||
|
- "https://ob.agov-w.azure.adnovum.net"
|
||||||
|
- "https://auth.agov-w.azure.adnovum.net"
|
||||||
|
- "https://nevisidm.agov-w.azure.adnovum.net"
|
||||||
|
signature-algorithms:
|
||||||
|
- "ES256"
|
||||||
|
- "EdDSA"
|
||||||
|
display-name-source: "email"
|
||||||
|
metadata:
|
||||||
|
allow-listing-enabled: false
|
||||||
|
timeout:
|
||||||
|
user-verification: "300s"
|
||||||
|
no-user-verification: "120s"
|
||||||
server:
|
server:
|
||||||
port: 9443
|
port: 9443
|
||||||
protocol: "https"
|
protocol: "https"
|
||||||
|
@ -24,27 +42,5 @@ credential-repository:
|
||||||
truststore-passphrase: "${exec:/var/opt/keys/trust/fido2-idp-extended-truststore/keypass}"
|
truststore-passphrase: "${exec:/var/opt/keys/trust/fido2-idp-extended-truststore/keypass}"
|
||||||
truststore-type: "pkcs12"
|
truststore-type: "pkcs12"
|
||||||
user-attribute: "extId"
|
user-attribute: "extId"
|
||||||
fido2:
|
|
||||||
enabled: true
|
|
||||||
rp-name: "AGOV-RelPartName"
|
|
||||||
rp-id: "adnovum.net"
|
|
||||||
origins:
|
|
||||||
- "https://ob.agov-w.azure.adnovum.net"
|
|
||||||
- "https://auth.agov-w.azure.adnovum.net"
|
|
||||||
- "https://nevisidm.agov-w.azure.adnovum.net"
|
|
||||||
signature-algorithms:
|
|
||||||
- "RS1"
|
|
||||||
- "RS256"
|
|
||||||
- "RS384"
|
|
||||||
- "RS512"
|
|
||||||
- "ES256"
|
|
||||||
- "ES384"
|
|
||||||
- "ES512"
|
|
||||||
display-name-source: "email"
|
|
||||||
metadata:
|
|
||||||
allow-listing-enabled: false
|
|
||||||
timeout:
|
|
||||||
user-verification: "300s"
|
|
||||||
no-user-verification: "120s"
|
|
||||||
session-repository:
|
session-repository:
|
||||||
type: "in-memory"
|
type: "in-memory"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
otel.service.name = fido2
|
otel.service.name = fido2
|
||||||
|
otel.traces.sampler = always_on
|
||||||
otel.traces.exporter = none
|
otel.traces.exporter = none
|
||||||
otel.metrics.exporter = none
|
otel.metrics.exporter = none
|
||||||
otel.logs.exporter = none
|
otel.logs.exporter = none
|
||||||
|
|
|
@ -44,4 +44,4 @@ if is_nevisfido_healthy():
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
raise_last_error_in_log()
|
raise_last_error_in_log()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -11,8 +11,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
type: "NevisLogrend"
|
type: "NevisLogrend"
|
||||||
replicas: 1
|
replicas: 1
|
||||||
version: "8.2411.2"
|
version: "8.2505.5"
|
||||||
gitInitVersion: "1.3.0"
|
gitInitVersion: "1.4.0"
|
||||||
runAsNonRoot: true
|
runAsNonRoot: true
|
||||||
ports:
|
ports:
|
||||||
server: 8988
|
server: 8988
|
||||||
|
@ -38,13 +38,14 @@ spec:
|
||||||
startupProbe:
|
startupProbe:
|
||||||
server:
|
server:
|
||||||
tcpSocket: true
|
tcpSocket: true
|
||||||
|
initialDelaySeconds: 30
|
||||||
periodSeconds: 5
|
periodSeconds: 5
|
||||||
timeoutSeconds: 4
|
timeoutSeconds: 4
|
||||||
failureThreshold: 50
|
failureThreshold: 30
|
||||||
podDisruptionBudget:
|
podDisruptionBudget:
|
||||||
maxUnavailable: "50%"
|
maxUnavailable: "50%"
|
||||||
git:
|
git:
|
||||||
tag: "r-e157935e7f17a778cb613627a645fe400a85af4d"
|
tag: "r-5e17b7ae74eadb8800587a4f4db74406a7e21e95"
|
||||||
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/logrend"
|
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/logrend"
|
||||||
credentials: "git-credentials"
|
credentials: "git-credentials"
|
||||||
podSecurity:
|
podSecurity:
|
||||||
|
|
|
@ -10,5 +10,5 @@ JAVA_OPTS=(
|
||||||
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
"-javaagent:/opt/agent/opentelemetry-javaagent.jar"
|
||||||
"-Dotel.javaagent.logging=application"
|
"-Dotel.javaagent.logging=application"
|
||||||
"-Dotel.javaagent.configuration-file=/var/opt/nevislogrend/default/conf/otel.properties"
|
"-Dotel.javaagent.configuration-file=/var/opt/nevislogrend/default/conf/otel.properties"
|
||||||
"-Dotel.resource.attributes=service.version=8.2411.2,service.instance.id=$HOSTNAME"
|
"-Dotel.resource.attributes=service.version=8.2505.5,service.instance.id=$HOSTNAME"
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
|
||||||
ico=image/x-icon
|
ico=image/x-icon
|
||||||
|
json=application/json
|
||||||
woff=font/woff
|
woff=font/woff
|
||||||
woff2=font/woff2
|
woff2=font/woff2
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
otel.service.name = logrend
|
otel.service.name = logrend
|
||||||
|
otel.traces.sampler = always_on
|
||||||
otel.traces.exporter = none
|
otel.traces.exporter = none
|
||||||
otel.metrics.exporter = none
|
otel.metrics.exporter = none
|
||||||
otel.logs.exporter = none
|
otel.logs.exporter = none
|
||||||
|
|
|
@ -9,7 +9,6 @@ agov-ident.invalid-url.message=Link kann nicht verarbeitet werden
|
||||||
agov-ident.invalid-url.title=Ungültiger Link
|
agov-ident.invalid-url.title=Ungültiger Link
|
||||||
agov-ident.onboarding=Registrierung & Verifikation
|
agov-ident.onboarding=Registrierung & Verifikation
|
||||||
agov-ident.retry=Versuchen Sie es erneut
|
agov-ident.retry=Versuchen Sie es erneut
|
||||||
button.submit=Senden
|
|
||||||
darkModeSwitch.aria.label=Dark-Mode-Schalter
|
darkModeSwitch.aria.label=Dark-Mode-Schalter
|
||||||
error.policy.failed=Das neue Passwort stimmt nicht mit der Richtlinie überein.
|
error.policy.failed=Das neue Passwort stimmt nicht mit der Richtlinie überein.
|
||||||
error_1=Bitte überprüfen Sie Ihre Eingaben.
|
error_1=Bitte überprüfen Sie Ihre Eingaben.
|
||||||
|
@ -246,6 +245,7 @@ recovery_questionnaire_reason_selection.instruction=Bitte wählen Sie einen
|
||||||
recovery_start_info.banner.warning=Sie können Ihr Konto nicht nutzen, bis der Wiederherstellungsprozess abgeschlossen ist.
|
recovery_start_info.banner.warning=Sie können Ihr Konto nicht nutzen, bis der Wiederherstellungsprozess abgeschlossen ist.
|
||||||
recovery_start_info.instruction=Während des Wiederherstellungsprozesses werden Sie einen neuen Login-Faktor registrieren. Wenn Ihr Konto verifizierte Informationen enthält, müssen Sie zum Abschluss des Wiederherstellungsprozesses möglicherweise auch einen Verifikationsprozess durchlaufen.
|
recovery_start_info.instruction=Während des Wiederherstellungsprozesses werden Sie einen neuen Login-Faktor registrieren. Wenn Ihr Konto verifizierte Informationen enthält, müssen Sie zum Abschluss des Wiederherstellungsprozesses möglicherweise auch einen Verifikationsprozess durchlaufen.
|
||||||
recovery_start_info.title=Sie sind dabei, den Wiederherstellungsprozess zu starten
|
recovery_start_info.title=Sie sind dabei, den Wiederherstellungsprozess zu starten
|
||||||
|
submit.button.label=Senden
|
||||||
title=NEVIS SSO Portal
|
title=NEVIS SSO Portal
|
||||||
title.login=Login
|
title.login=Login
|
||||||
title.pwchange.label=Passwort ändern
|
title.pwchange.label=Passwort ändern
|
||||||
|
|
|
@ -9,7 +9,6 @@ agov-ident.invalid-url.message=Link kann nicht verarbeitet werden
|
||||||
agov-ident.invalid-url.title=Ungültiger Link
|
agov-ident.invalid-url.title=Ungültiger Link
|
||||||
agov-ident.onboarding=Registrierung & Verifikation
|
agov-ident.onboarding=Registrierung & Verifikation
|
||||||
agov-ident.retry=Versuchen Sie es erneut
|
agov-ident.retry=Versuchen Sie es erneut
|
||||||
button.submit=Senden
|
|
||||||
darkModeSwitch.aria.label=Dark-Mode-Schalter
|
darkModeSwitch.aria.label=Dark-Mode-Schalter
|
||||||
error.policy.failed=Das neue Passwort stimmt nicht mit der Richtlinie überein.
|
error.policy.failed=Das neue Passwort stimmt nicht mit der Richtlinie überein.
|
||||||
error_1=Bitte überprüfen Sie Ihre Eingaben.
|
error_1=Bitte überprüfen Sie Ihre Eingaben.
|
||||||
|
@ -246,6 +245,7 @@ recovery_questionnaire_reason_selection.instruction=Bitte wählen Sie einen
|
||||||
recovery_start_info.banner.warning=Sie können Ihr Konto nicht nutzen, bis der Wiederherstellungsprozess abgeschlossen ist.
|
recovery_start_info.banner.warning=Sie können Ihr Konto nicht nutzen, bis der Wiederherstellungsprozess abgeschlossen ist.
|
||||||
recovery_start_info.instruction=Während des Wiederherstellungsprozesses werden Sie einen neuen Login-Faktor registrieren. Wenn Ihr Konto verifizierte Informationen enthält, müssen Sie zum Abschluss des Wiederherstellungsprozesses möglicherweise auch einen Verifikationsprozess durchlaufen.
|
recovery_start_info.instruction=Während des Wiederherstellungsprozesses werden Sie einen neuen Login-Faktor registrieren. Wenn Ihr Konto verifizierte Informationen enthält, müssen Sie zum Abschluss des Wiederherstellungsprozesses möglicherweise auch einen Verifikationsprozess durchlaufen.
|
||||||
recovery_start_info.title=Sie sind dabei, den Wiederherstellungsprozess zu starten
|
recovery_start_info.title=Sie sind dabei, den Wiederherstellungsprozess zu starten
|
||||||
|
submit.button.label=Senden
|
||||||
title=NEVIS SSO Portal
|
title=NEVIS SSO Portal
|
||||||
title.login=Login
|
title.login=Login
|
||||||
title.pwchange.label=Passwort ändern
|
title.pwchange.label=Passwort ändern
|
||||||
|
|
|
@ -9,7 +9,6 @@ agov-ident.invalid-url.message=Link can't be processed
|
||||||
agov-ident.invalid-url.title=Invalid Link
|
agov-ident.invalid-url.title=Invalid Link
|
||||||
agov-ident.onboarding=Registration & Verification
|
agov-ident.onboarding=Registration & Verification
|
||||||
agov-ident.retry=Try again
|
agov-ident.retry=Try again
|
||||||
button.submit=Submit
|
|
||||||
darkModeSwitch.aria.label=Dark mode toggle
|
darkModeSwitch.aria.label=Dark mode toggle
|
||||||
error.policy.failed=The new password does not comply with the policy.
|
error.policy.failed=The new password does not comply with the policy.
|
||||||
error_1=Please check your input.
|
error_1=Please check your input.
|
||||||
|
@ -246,6 +245,7 @@ recovery_questionnaire_reason_selection.instruction=Please select the reason you
|
||||||
recovery_start_info.banner.warning=You will not be able to use your account until the recovery process has been concluded.
|
recovery_start_info.banner.warning=You will not be able to use your account until the recovery process has been concluded.
|
||||||
recovery_start_info.instruction=During the recovery process you will register a new login factor. If your account contains any verified information you might also have to go through a verification process to finish the recovery.
|
recovery_start_info.instruction=During the recovery process you will register a new login factor. If your account contains any verified information you might also have to go through a verification process to finish the recovery.
|
||||||
recovery_start_info.title=You are about to start the recovery process
|
recovery_start_info.title=You are about to start the recovery process
|
||||||
|
submit.button.label=Submit
|
||||||
title=NEVIS SSO Portal
|
title=NEVIS SSO Portal
|
||||||
title.login=Login
|
title.login=Login
|
||||||
title.pwchange.label=Password Change
|
title.pwchange.label=Password Change
|
||||||
|
|
|
@ -9,7 +9,6 @@ agov-ident.invalid-url.message=Le lien ne peut pas être traité
|
||||||
agov-ident.invalid-url.title=Lien non valide
|
agov-ident.invalid-url.title=Lien non valide
|
||||||
agov-ident.onboarding=Enregistrement et vérification
|
agov-ident.onboarding=Enregistrement et vérification
|
||||||
agov-ident.retry=Essayez à nouveau
|
agov-ident.retry=Essayez à nouveau
|
||||||
button.submit=Envoyer
|
|
||||||
darkModeSwitch.aria.label=Activer l'apparence sombre
|
darkModeSwitch.aria.label=Activer l'apparence sombre
|
||||||
error.policy.failed=Votre nouveau mot de passe ne conforme pas aux mesures de sécurité
|
error.policy.failed=Votre nouveau mot de passe ne conforme pas aux mesures de sécurité
|
||||||
error_1=Veuillez vérifier votre saisie.
|
error_1=Veuillez vérifier votre saisie.
|
||||||
|
@ -246,6 +245,7 @@ recovery_questionnaire_reason_selection.instruction=Veuillez sélectionner
|
||||||
recovery_start_info.banner.warning=Vous ne pourrez pas utiliser votre compte tant que le processus de récupération n'aura pas été terminé.
|
recovery_start_info.banner.warning=Vous ne pourrez pas utiliser votre compte tant que le processus de récupération n'aura pas été terminé.
|
||||||
recovery_start_info.instruction=Le processus de récupération nécessitera l’enregistrement d’un nouveau facteur d’authentification. Si votre compte contient des informations ayant déjà été vérifiées, il se peut que vous deviez les faire vérifier à nouveau pour terminer la récupération.
|
recovery_start_info.instruction=Le processus de récupération nécessitera l’enregistrement d’un nouveau facteur d’authentification. Si votre compte contient des informations ayant déjà été vérifiées, il se peut que vous deviez les faire vérifier à nouveau pour terminer la récupération.
|
||||||
recovery_start_info.title=Vous êtes sur le point de démarrer le processus de récupération.
|
recovery_start_info.title=Vous êtes sur le point de démarrer le processus de récupération.
|
||||||
|
submit.button.label=Envoyer
|
||||||
title=NEVIS SSO Portal
|
title=NEVIS SSO Portal
|
||||||
title.login=Login
|
title.login=Login
|
||||||
title.pwchange.label=Changer mot de passe
|
title.pwchange.label=Changer mot de passe
|
||||||
|
|
|
@ -9,7 +9,6 @@ agov-ident.invalid-url.message=Il link non può essere elaborato
|
||||||
agov-ident.invalid-url.title=Link non valido
|
agov-ident.invalid-url.title=Link non valido
|
||||||
agov-ident.onboarding=Registrazione e verifica
|
agov-ident.onboarding=Registrazione e verifica
|
||||||
agov-ident.retry=Riprova
|
agov-ident.retry=Riprova
|
||||||
button.submit=Continua
|
|
||||||
darkModeSwitch.aria.label=Attivare la modalità scura
|
darkModeSwitch.aria.label=Attivare la modalità scura
|
||||||
error.policy.failed=La nuova password non è stata accettata. Scegliere una password che sia conforme ai criteri di password.
|
error.policy.failed=La nuova password non è stata accettata. Scegliere una password che sia conforme ai criteri di password.
|
||||||
error_1=Verificare i dati inseriti.
|
error_1=Verificare i dati inseriti.
|
||||||
|
@ -246,6 +245,7 @@ recovery_questionnaire_reason_selection.instruction=Selezioni il motivo per cui
|
||||||
recovery_start_info.banner.warning=Non è possibile utilizzare l’account finché il processo di ripristino non sarà concluso.
|
recovery_start_info.banner.warning=Non è possibile utilizzare l’account finché il processo di ripristino non sarà concluso.
|
||||||
recovery_start_info.instruction=Durante il processo di ripristino registrerà un nuovo fattore di login. Se il suo account contiene informazioni verificate, potrebbe dover effettuare anche un processo di verificazione per completare il ripristino.
|
recovery_start_info.instruction=Durante il processo di ripristino registrerà un nuovo fattore di login. Se il suo account contiene informazioni verificate, potrebbe dover effettuare anche un processo di verificazione per completare il ripristino.
|
||||||
recovery_start_info.title=Sta per iniziare il processo di ripristino
|
recovery_start_info.title=Sta per iniziare il processo di ripristino
|
||||||
|
submit.button.label=Continua
|
||||||
title=NEVIS SSO Portal
|
title=NEVIS SSO Portal
|
||||||
title.login=Login
|
title.login=Login
|
||||||
title.pwchange.label=Cambiare Password
|
title.pwchange.label=Cambiare Password
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0)">
|
||||||
|
<path d="M13.9697 17.2808C12.9941 18.2276 11.9177 18.08 10.8917 17.6336C9.80091 17.1782 8.80371 17.1494 7.65171 17.6336C6.21711 18.2528 5.45571 18.0728 4.59171 17.2808C-0.28628 12.2588 0.433719 4.60879 5.97771 4.32079C7.32231 4.39279 8.26371 5.06419 9.05571 5.11999C10.2329 4.88059 11.3597 4.19479 12.6197 4.28479C14.1335 4.40719 15.2657 5.00479 16.0217 6.07938C12.9077 7.95138 13.6457 12.0554 16.5059 13.2074C15.9335 14.7104 15.1991 16.1954 13.9679 17.2934L13.9697 17.2808ZM8.94771 4.26679C8.80191 2.03479 10.6109 0.198798 12.6917 0.0187988C12.9779 2.59279 10.3517 4.51879 8.94771 4.26679Z" fill="#1F2F33"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0">
|
||||||
|
<rect width="15.156" height="18" fill="white" transform="translate(1.3335)"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 872 B |
|
@ -66,7 +66,7 @@ const Status = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function setDeepLinkLabel(button) {
|
function setDeepLinkLabel(button) {
|
||||||
const text = document.getElementsByName('info.deeplink')[0].value;
|
const text = document.getElementById('info.login.access_app').innerText;
|
||||||
button.innerHTML = text;
|
button.innerHTML = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,13 @@ function messageCheckPhone() {
|
||||||
infoElement.innerHTML = text;
|
infoElement.innerHTML = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Element = {
|
function showError() {
|
||||||
|
const text = document.getElementsByName('error.authcloud.login')[0].value;
|
||||||
|
errorElement.innerHTML = text;
|
||||||
|
infoElement.style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccessAppElement = {
|
||||||
|
|
||||||
_elem: null, // QR code or deep link depending on device
|
_elem: null, // QR code or deep link depending on device
|
||||||
|
|
||||||
|
@ -91,8 +97,11 @@ const Element = {
|
||||||
if (isAndroid || isIphone) {
|
if (isAndroid || isIphone) {
|
||||||
this._elem = document.createElement('a');
|
this._elem = document.createElement('a');
|
||||||
this._elem.setAttribute('href', appLink);
|
this._elem.setAttribute('href', appLink);
|
||||||
this._elem.setAttribute('class', 'btn btn-primary');
|
this._elem.setAttribute('class', 'btn btn-primary w-100 mt-4');
|
||||||
this._elem.setAttribute('target', '_blank');
|
this._elem.setAttribute('target', '_blank');
|
||||||
|
// distinguishes style for platforms
|
||||||
|
dispatcherElement.classList.add('mobile-platform');
|
||||||
|
|
||||||
dispatcherElement.appendChild(this._elem);
|
dispatcherElement.appendChild(this._elem);
|
||||||
setDeepLinkLabel(this._elem);
|
setDeepLinkLabel(this._elem);
|
||||||
}
|
}
|
||||||
|
@ -103,13 +112,23 @@ const Element = {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
messageScanQR();
|
messageScanQR();
|
||||||
|
const qrSize = 280;
|
||||||
|
// Element to render the QR code
|
||||||
this._elem = document.createElement('canvas');
|
this._elem = document.createElement('canvas');
|
||||||
dispatcherElement.appendChild(this._elem);
|
// Wrapper div to render corners
|
||||||
var qrcode = new QRious({
|
const qrCodeWrapper = document.createElement('div');
|
||||||
|
qrCodeWrapper.setAttribute('id','qr-code-wrapper');
|
||||||
|
qrCodeWrapper.style.width = `${qrSize}px`;
|
||||||
|
qrCodeWrapper.style.height = `${qrSize}px`;
|
||||||
|
qrCodeWrapper.appendChild(this._elem)
|
||||||
|
dispatcherElement.style.height = `${qrSize}px`;
|
||||||
|
dispatcherElement.appendChild(qrCodeWrapper);
|
||||||
|
const qrcode = new QRious({
|
||||||
element: this._elem,
|
element: this._elem,
|
||||||
foreground: "#168CA9",
|
// use --nevis-gray-900 CSS variable value
|
||||||
|
foreground: getComputedStyle(document.body).getPropertyValue('--nevis-gray-900'),
|
||||||
level: "M",
|
level: "M",
|
||||||
size: 280,
|
size: qrSize,
|
||||||
value: appLink
|
value: appLink
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -125,20 +144,31 @@ const Element = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function authenticateUser(appLink) {
|
function authenticateUser(appLink) {
|
||||||
Element.show(appLink);
|
|
||||||
console.log('Starting Authentication Cloud status polling...');
|
AccessAppElement.show(appLink);
|
||||||
|
|
||||||
|
console.log('Starting Auth Cloud status polling...');
|
||||||
|
|
||||||
Status.startPolling(statusToken, (st, done) => {
|
Status.startPolling(statusToken, (st, done) => {
|
||||||
|
|
||||||
if (st.status === 'succeeded') {
|
if (st.status === 'succeeded') {
|
||||||
console.log('Authentication Cloud login done.');
|
|
||||||
|
console.log('Auth Cloud success.');
|
||||||
|
|
||||||
|
// auto submit form with outcome
|
||||||
submitStatus('succeeded')
|
submitStatus('succeeded')
|
||||||
}
|
}
|
||||||
else if (st.status === 'failed') {
|
else if (st.status === 'failed') {
|
||||||
|
|
||||||
// failed: The transaction failed, either by timeout or because the user did not accept.
|
// failed: The transaction failed, either by timeout or because the user did not accept.
|
||||||
console.warn('Authentication Cloud login failed. User abort or timeout.');
|
console.warn('Auth Cloud login failed. User abort or timeout.');
|
||||||
|
|
||||||
submitStatus('failed')
|
submitStatus('failed')
|
||||||
}
|
}
|
||||||
else if (st.status === 'unknown') {
|
else if (st.status === 'unknown') {
|
||||||
console.error('Authentication Cloud login failed. Unknown status.');
|
|
||||||
|
console.error('Auth Cloud login failed. Unknown status.');
|
||||||
|
|
||||||
submitStatus('unknown')
|
submitStatus('unknown')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -75,7 +75,12 @@ function messageScanQR() {
|
||||||
infoElement.innerHTML = text;
|
infoElement.innerHTML = text;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Element = {
|
function messageInstalledAccessApp() {
|
||||||
|
const text = document.getElementById('info.access_app.installed').innerText;
|
||||||
|
infoElement.innerHTML = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AccessAppElement = {
|
||||||
|
|
||||||
_elem: null, // QR code or deep link depending on device
|
_elem: null, // QR code or deep link depending on device
|
||||||
|
|
||||||
|
@ -84,22 +89,47 @@ const Element = {
|
||||||
const isIphone = 'iPhone' === navigator.platform;
|
const isIphone = 'iPhone' === navigator.platform;
|
||||||
const isAndroid = /android/i.test(userAgent) && /mobile/i.test(userAgent);
|
const isAndroid = /android/i.test(userAgent) && /mobile/i.test(userAgent);
|
||||||
if (isAndroid || isIphone) {
|
if (isAndroid || isIphone) {
|
||||||
|
if (isAndroid) {
|
||||||
|
document.getElementById('install_apple').style.display = 'none';
|
||||||
|
}
|
||||||
|
if (isIphone) {
|
||||||
|
document.getElementById('install_google').style.display = 'none';
|
||||||
|
}
|
||||||
this._elem = document.createElement('a');
|
this._elem = document.createElement('a');
|
||||||
this._elem.setAttribute('href', appLink);
|
this._elem.setAttribute('href', appLink);
|
||||||
this._elem.setAttribute('class', 'btn btn-primary');
|
this._elem.setAttribute('class', 'btn btn-primary w-100');
|
||||||
this._elem.setAttribute('target', '_blank');
|
this._elem.setAttribute('target', '_blank');
|
||||||
|
// distinguishes style for platforms
|
||||||
|
dispatcherElement.classList.add('mobile-platform');
|
||||||
|
const accessApplinks = document.getElementById('access-app-download-link');
|
||||||
|
accessApplinks.classList.add('access-app-download-link-mobile-spacing');
|
||||||
|
|
||||||
dispatcherElement.appendChild(this._elem);
|
dispatcherElement.appendChild(this._elem);
|
||||||
setDeepLinkLabel(this._elem);
|
setDeepLinkLabel(this._elem);
|
||||||
|
|
||||||
|
// info text is displayed before access app links
|
||||||
|
accessApplinks.parentNode.insertBefore(infoElement.parentNode, accessApplinks);
|
||||||
|
messageInstalledAccessApp();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
messageScanQR();
|
messageScanQR();
|
||||||
|
const qrSize = 280;
|
||||||
|
// Element to render the QR code
|
||||||
this._elem = document.createElement('canvas');
|
this._elem = document.createElement('canvas');
|
||||||
dispatcherElement.appendChild(this._elem);
|
// Wrapper div to render corners
|
||||||
var qrcode = new QRious({
|
const qrCodeWrapper = document.createElement('div');
|
||||||
|
qrCodeWrapper.setAttribute('id','qr-code-wrapper');
|
||||||
|
qrCodeWrapper.style.width = `${qrSize}px`;
|
||||||
|
qrCodeWrapper.style.height = `${qrSize}px`;
|
||||||
|
qrCodeWrapper.appendChild(this._elem)
|
||||||
|
dispatcherElement.style.height = `${qrSize}px`;
|
||||||
|
dispatcherElement.appendChild(qrCodeWrapper);
|
||||||
|
const qrcode = new QRious({
|
||||||
element: this._elem,
|
element: this._elem,
|
||||||
foreground: "#168CA9",
|
// use --nevis-gray-900 CSS variable value
|
||||||
|
foreground: getComputedStyle(document.body).getPropertyValue('--nevis-gray-900'),
|
||||||
level: "M",
|
level: "M",
|
||||||
size: 280,
|
size: qrSize,
|
||||||
value: appLink
|
value: appLink
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -114,25 +144,47 @@ const Element = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function onboardUser(appLink) {
|
function onboardUser(appLink) {
|
||||||
Element.show(appLink);
|
|
||||||
console.log('Starting Authentication Cloud status polling...');
|
AccessAppElement.show(appLink);
|
||||||
|
|
||||||
|
console.log('Starting Auth Cloud status polling...');
|
||||||
|
|
||||||
Status.startPolling(statusToken, (st, done) => {
|
Status.startPolling(statusToken, (st, done) => {
|
||||||
|
|
||||||
if (st.status === 'succeeded') {
|
if (st.status === 'succeeded') {
|
||||||
console.log('Authentication Cloud onboarding done.');
|
|
||||||
|
console.log('Auth Cloud success.');
|
||||||
|
|
||||||
|
// auto submit form with outcome
|
||||||
submitStatus('succeeded')
|
submitStatus('succeeded')
|
||||||
}
|
}
|
||||||
else if (st.status === 'failed') {
|
else if (st.status === 'failed') {
|
||||||
|
|
||||||
// failed: The transaction failed, either by timeout or because the user did not accept.
|
// failed: The transaction failed, either by timeout or because the user did not accept.
|
||||||
console.warn('Authentication Cloud onboarding failed. User abort or timeout.');
|
console.warn('Authentication Cloud onboarding failed. User abort or timeout.');
|
||||||
|
|
||||||
submitStatus('failed')
|
submitStatus('failed')
|
||||||
}
|
}
|
||||||
else if (st.status === 'unknown') {
|
else if (st.status === 'unknown') {
|
||||||
|
|
||||||
console.error('Authentication Cloud onboarding failed. Unknown status.');
|
console.error('Authentication Cloud onboarding failed. Unknown status.');
|
||||||
|
|
||||||
submitStatus('unknown')
|
submitStatus('unknown')
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const swap = function (nodeA, nodeB) {
|
||||||
|
const parentA = nodeA.parentNode;
|
||||||
|
const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;
|
||||||
|
|
||||||
|
// Move `nodeA` to before the `nodeB`
|
||||||
|
nodeB.parentNode.insertBefore(nodeA, nodeB);
|
||||||
|
|
||||||
|
// Move `nodeB` to before the sibling of `nodeA`
|
||||||
|
parentA.insertBefore(nodeB, siblingA);
|
||||||
|
};
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
|
|
||||||
const form = document.getElementById('authcloud_onboard');
|
const form = document.getElementById('authcloud_onboard');
|
||||||
|
@ -145,6 +197,9 @@ function init() {
|
||||||
|
|
||||||
dispatcherElement = document.getElementById('authcloud_dispatch');
|
dispatcherElement = document.getElementById('authcloud_dispatch');
|
||||||
|
|
||||||
|
// info texts are displayed underneath QR code
|
||||||
|
swap(infoElement.parentNode, dispatcherElement.parentNode);
|
||||||
|
|
||||||
const appLink = form.appLink.value;
|
const appLink = form.appLink.value;
|
||||||
onboardUser(appLink);
|
onboardUser(appLink);
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue