diff --git a/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/etc/nevis/k8s-nevisauth-7022472ae407577ae604bbb8.yaml b/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/etc/nevis/k8s-nevisauth-7022472ae407577ae604bbb8.yaml
index f69fb9f..9e51618 100644
--- a/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/etc/nevis/k8s-nevisauth-7022472ae407577ae604bbb8.yaml
+++ b/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/etc/nevis/k8s-nevisauth-7022472ae407577ae604bbb8.yaml
@@ -45,7 +45,7 @@ spec:
podDisruptionBudget:
maxUnavailable: "50%"
git:
- tag: "r-9af6a792e2720efb1d09318c1e3f4a2ab355af31"
+ tag: "r-db9522d1110b41177e7d034419ef1fb68f95b0b8"
dir: "DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth"
credentials: "git-credentials"
keystores:
diff --git a/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/checkloa.groovy b/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/checkloa.groovy
index 87ef5fb..d86fe01 100644
--- a/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/checkloa.groovy
+++ b/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/checkloa.groovy
@@ -68,6 +68,32 @@ def getUserIdVerificationForRecovery() {
return result
}
+def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber) {
+ def result = 'urn:qa.agov.ch:names:tc:ac:classes:'
+
+ switch (idVerification) {
+ case 'None':
+ result = result.concat('100')
+ break
+ case 'SimpleLetter':
+ result = result.concat('200')
+ break
+ case 'Video':
+ case 'VideoSelfPaid':
+ case 'Bmid':
+ case 'BmidSelfPaid':
+ case 'Counter':
+ result = result.concat((highestRoleLevelNumber == 400) ? '400' : '300')
+ break
+ default:
+ LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
+ // safest default, should work in any case
+ result = result.concat('' + highestRoleLevelNumber)
+ }
+
+ return result
+}
+
def getUserMustRecoverValidFrom() {
// set attibutes from DTO: -> validFrom
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
@@ -95,9 +121,9 @@ try {
adressVerification = adressVerificationList[0]
}
- LOG.debug('Requested role level '+ requestedRoleLevelNumber)
- LOG.debug('idVerification: ' + getUserAGOVLoiIdVerification())
- LOG.debug('adressVerification : ' + adressVerification)
+ LOG.debug('CheckLoa: Requested role level '+ requestedRoleLevelNumber)
+ LOG.debug('CheckLoa: idVerification: ' + getUserAGOVLoiIdVerification())
+ LOG.debug('CheckLoa: adressVerification : ' + adressVerification)
def idVerificationMethodList = getUserAGOVLoiIdVerification()
@@ -143,12 +169,12 @@ try {
}
}
}
- LOG.debug('Highest role Level' + highestRoleLevelNumber.toString() +' contextclassref' + requestedRoleLevelNumber.toString())
- LOG.debug(' Compare' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
+ LOG.debug('CheckLoa: Highest role Level' + highestRoleLevelNumber.toString() +' contextclassref' + requestedRoleLevelNumber.toString())
+ LOG.debug('CheckLoa: Compare' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
//set attribute Actual Role Level
session.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
- LOG.debug('actual role level (agov) '+ highestRoleLevelNumber)
+ LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
if (highestRoleLevelNumber > 0) {
// set attribute contextClassRefToSet
@@ -165,18 +191,19 @@ try {
session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown' )
def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'
- if (highestRoleLevelNumber < 300) {
- // plus 100, if mustRecover
- highestRoleLevelNumber += 100
- }
- session.setAttribute('agov.recovery.currentAgovAq', 'urn:qa.agov.ch:names:tc:ac:classes:'.concat(highestRoleLevelNumber.toString()) )
def idVerification = getUserIdVerificationForRecovery() ?: origIdVerification
session.setAttribute('agov.recovery.currentIdVerification', '' + idVerification )
+ // align currentAgovAq with the method selected for idVerification
+ def currentAgovAqForRecovery = getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber)
+ session.setAttribute('agov.recovery.currentAgovAq', '' + currentAgovAqForRecovery)
+
def validFrom = getUserMustRecoverValidFrom() ?: ''
session.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + validFrom )
+ LOG.debug("CheckLoa: mustRecover: origIdVerification=${origIdVerification}, idVerification=${idVerification}, currentAgovAqForRecovery=${currentAgovAqForRecovery}")
+
response.setResult('exit.2')
return
@@ -184,7 +211,7 @@ try {
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown')
session.setAttribute('agov.recovery.currentAgovAq', session.getAttribute('contextClassRefToSet') ?: 'urn:qa.agov.ch:names:tc:ac:classes:100' )
- LOG.debug('idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
+ LOG.debug('CheckLoa: idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
def idVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString())
session.setAttribute('agov.recovery.currentIdVerification', (idVerification.isEmpty() ? 'None' : idVerification.first()))
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString())) ?: ''
@@ -202,8 +229,8 @@ try {
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
- LOG.debug('ValidFrom :' + validFrom)
- LOG.debug('ValidTo :' + validTo)
+ LOG.debug('CheckLoa: ValidFrom :' + validFrom)
+ LOG.debug('CheckLoa: ValidTo :' + validTo)
if(validFrom != '') {
session.setAttribute('ValidFrom', '' + validFrom)
diff --git a/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/ensureAccountState.groovy b/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/ensureAccountState.groovy
index 2388610..391afdb 100644
--- a/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/ensureAccountState.groovy
+++ b/DEFAULT-ADN-AGOV-PROJECT/DEFAULT-ADN-AGOV-INV/auth/var/opt/nevisauth/default/conf/ensureAccountState.groovy
@@ -4,7 +4,6 @@ import ch.nevis.idm.client.IdmRestClientFactory
import ch.nevis.idm.client.HTTPRequestWrapper
import groovy.json.JsonSlurper
-import groovy.xml.XmlSlurper
// Accounting
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
@@ -15,91 +14,112 @@ def credentialType = session['authenticatedWith'] ?: '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'
-
-
-
-
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
String clientExtId = session.get('ch.adnovum.nevisidm.user.clientExtId')
String userExtId = session.get('ch.adnovum.nevisidm.user.extId')
-String sessionId = session.get('ch.nevis.session.conversationId')
+String loginId = session.get('ch.adnovum.nevisidm.user.loginId')
+String profileExtId = session.get('ch.adnovum.nevisidm.profileExtId')
-String endPoint = "${parameters.get('utility-service.baseUrl')}/api/v1/recovery/code"
+String unitExtid= parameters.get('unitExtid')
+String level100RoleExtid = parameters.get('level100.roleExtid')
-def userDto = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
-def recoveryCredential = userDto.'**'.find {node -> node.name() == 'credentials' && node.type.text() == 'CONTEXT_PASSWORD' && node.state.text() == 'ACTIVE' && node.context.text() == 'RECOVERY'}
+String baseUrl = "${parameters.get('idm.baseUrl')}/core/v1/$clientExtId"
+boolean audited = false
+String agovAq100AuthEndpoint = null
+String endpoint = null
-// 1a) check if user has a credential
-if ( recoveryCredential != null ) {
- LOG.debug("Account '${user}' has an active recovery code, no need to create new code")
- response.setResult('done')
- return
-}
+// 1) create the profile if needed
+if (profileExtId == null || profileExtId.isEmpty()) {
-// 1b) check if a recovery is ongoing (nothing to do)
-if (Arrays.stream(response.getActualRoles()).filter( r -> r.contains('AGOV-AccountStatus.recovery')).findAny().isPresent()) {
- LOG.debug("Account '${user}' is in recovery, no need to create new code")
- response.setResult('done')
- return
-}
+ endpoint = "${baseUrl}/users/${userExtId}/profiles"
+ profileExtId = UUID.randomUUID().toString()
+ def postRequest = new HTTPRequestWrapper()
+ postRequest.addToHeaders('Content-Type', ['application/json'])
-// 2) set cookie for recoveryCode
-if (outargs.containsKey('out.JWTToken')) {
- def token = outargs.getProperty('out.JWTToken').bytes.encodeBase64().toString()
- def agovRecoveryCodeCookie = "agovRecoveryCode=${token }; Domain=${parameters.get('cookie.domain')}; Path=/; SameSite=Strict; Secure; HttpOnly"
- response.setHeader('Set-Cookie', agovRecoveryCodeCookie)
- outargs.remove('out.JWTToken')
-}
+ def dto = "{\"extId\":\"${profileExtId}\",\"unitExtId\":\"${unitExtid}\",\"profileState\":\"active\",\"name\":\"Profile-${loginId}\",\"isDefaultProfile\":true,\"modificationComment\":\"Repaired for request ${requestId}\"}"
+ postRequest.setPayLoad(dto.getBytes('UTF-8'))
-// 3) generate code if not yet done
-if (!session['agov.new.recovery.code.generated']) {
- inargs.remove('submit')
- try {
- def postRequest = new HTTPRequestWrapper()
- postRequest.addToHeaders('Content-Type', ['application/json'])
+ def result = idmRestClient.postWithResponse(endpoint, postRequest)
+ if (result.getStatusCode() != 201) {
+ LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to create the missing profile (http status code ${result.getStatusCode()})'")
- postRequest.setPayLoad("{\"userExtId\":\"$userExtId\",\"userSessionId\": \"$sessionId\"}".getBytes('UTF-8'))
-
- def result = idmRestClient.postWithResponse(endPoint, postRequest)
- if (result.getStatusCode() != 200) {
- LOG.debug("Payload: ${new String(postRequest.getPayLoad())}")
- LOG.debug("Result: ${result}")
- LOG.warn("Event='RCVRY-CODE', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to create code (http status code ${result.getStatusCode()})")
- response.setResult('failed')
- return
- }
+ response.setNote('saml.errorCode', 'Responder')
+ response.setNote('saml.errorMessage', "account of the user with agovId ${userExtId} is in a corrupt state, should contact agov help")
- def json = new JsonSlurper().parseText(new String(result.getPayLoad(), 'UTF-8'))
-
- notes.setProperty('agov.new.recovery.code', json['recoveryCode']['code'].replaceAll('^(....)(....)(.*)$', '$1-$2-$3'))
- LOG.debug("agov.new.recovery.code: ${notes['agov.new.recovery.code']}")
-
- response.setSessionAttribute('agov.new.recovery.code.generated', 'true')
- def validTil = "${json['recoveryCode']['validUntil'][2]}.${json['recoveryCode']['validUntil'][1]}.${json['recoveryCode']['validUntil'][0]}"
- response.setSessionAttribute('agov.new.recovery.code.validTil', validTil)
- response.setSessionAttribute('agov.new.recovery.code.pdfAuthToken', json['authToken'])
-
- LOG.info("Event='RCVRY-CODE', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
-
- } catch(Exception e) {
- LOG.warn("Event='RCVRY-CODE', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to create code (http status code ${e.getMessage()})")
- LOG.error("Recoverycode processing failed: $e")
response.setResult('failed')
return
+ } else {
+ LOG.warn("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='created missing profile'")
+ audited = true
+ }
+}
+
+
+// 2) add level 100 role if needed
+if (!Arrays.stream(response.getActualRoles()).filter( r -> r.contains('AGOV-Loi.level100')).findAny().isPresent()) {
+ endpoint = "${baseUrl}/profiles/${profileExtId}/authorizations"
+ def postRequest = new HTTPRequestWrapper()
+ postRequest.addToHeaders('Content-Type', ['application/json'])
+
+ def dto = "{\"extId\":\"${UUID.randomUUID().toString()}\",\"roleExtId\":\"${level100RoleExtid}\"}"
+ postRequest.setPayLoad(dto.getBytes('UTF-8'))
+
+ def result = idmRestClient.postWithResponse(endpoint, postRequest)
+ if (result.getStatusCode() != 201) {
+ LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to create the missing AGOVaq 100 role (http status code ${result.getStatusCode()})'")
+
+ response.setNote('saml.errorCode', 'Responder')
+ response.setNote('saml.errorMessage', "account of the user with agovId ${userExtId} is in a corrupt state, should contact agov help")
+
+ response.setResult('failed')
+ return
+ } else if (!audited) {
+ LOG.warn("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='created missing AGOVaq 100 role'")
+ audited = true
+ }
+ agovAq100AuthEndpoint = result.getLocation()
+}
+
+
+// 3) set the AQ level 100 verification to None
+if (!session['ch.adnovum.nevisidm.userDto'].contains("
1.5.0.1980-20240604T133737Z
+1.5.3.51-20240709T140654Z