8 files updated
This commit is contained in:
parent
991bfbd7c3
commit
1e3b12f009
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,264 +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
|
||||||
default:
|
case 'Eid':
|
||||||
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
result = result.concat('400')
|
||||||
// safest default, should work in any case
|
break
|
||||||
result = result.concat('' + highestRoleLevelNumber)
|
default:
|
||||||
}
|
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
||||||
|
// safest default, should work in any case
|
||||||
return result
|
result = result.concat('' + highestRoleLevelNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getUserMustRecoverValidFrom() {
|
return result
|
||||||
// set attibutes from DTO: -> validFrom
|
}
|
||||||
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 getUserMustRecoverValidFrom() {
|
||||||
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
// set attibutes from DTO: -> validFrom
|
||||||
}
|
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||||
|
def authzNode = payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == 'mustRecover'}
|
||||||
// Accounting
|
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
||||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
}
|
||||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
|
||||||
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
// Accounting
|
||||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||||
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
||||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||||
|
def credentialType = session['authenticatedWith'] ?: 'unknown'
|
||||||
try {
|
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||||
// beef
|
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: request.getLoginContext()['connection.HttpHeader.User-Agent'] ?: 'unknown'
|
||||||
def session = request.getAuthSession(true)
|
|
||||||
def highestRoleLevelNumber = 0
|
try {
|
||||||
def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
|
// beef
|
||||||
def adressVerificationList = getUserAGOVLoiIdVerification('200')
|
def s = request.getAuthSession(true)
|
||||||
def adressVerification = 'None'
|
def highestRoleLevelNumber = 0
|
||||||
if (adressVerificationList && !adressVerificationList.isEmpty()) {
|
|
||||||
adressVerification = adressVerificationList[0]
|
if (!session.get('agov.requestedRoleLevel')) {
|
||||||
}
|
LOG.error("IDP: internal error: agov.requestedRoleLevel not set in session")
|
||||||
def authenticationMethod = session.get('authenticatedWith')
|
response.setResult('error');
|
||||||
|
return
|
||||||
LOG.debug('CheckLoa: Requested role level '+ requestedRoleLevelNumber)
|
}
|
||||||
LOG.debug('CheckLoa: idVerification: ' + getUserAGOVLoiIdVerification())
|
def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
|
||||||
LOG.debug('CheckLoa: adressVerification : ' + adressVerification)
|
|
||||||
|
def authenticationMethod = session.get('authenticatedWith')
|
||||||
def idVerificationMethodList = getUserAGOVLoiIdVerification()
|
if (!authenticationMethod) {
|
||||||
|
LOG.error("IDP: internal error: authenticationMethod not set in session")
|
||||||
session.setAttribute('idVerification', idVerificationMethodList.isEmpty() ? 'None' : idVerificationMethodList.last())
|
response.setResult('error');
|
||||||
session.setAttribute('agov.adressVerification', '' + adressVerification)
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (requestedRoleLevelNumber == 0) {
|
// data transformations needed for SAML and OIDC
|
||||||
// AuthnFailed_Zero_RoleLvl
|
// Transform sex to number
|
||||||
response.setResult('error');
|
if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
|
||||||
return
|
s.setAttribute('ch.nevis.idm.User.gender', '1')
|
||||||
}
|
}
|
||||||
|
if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
||||||
if (session.get('ch.adnovum.nevisidm.profileExtId') == '') {
|
s.setAttribute('ch.nevis.idm.User.gender', '2')
|
||||||
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='Account without Profile', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
}
|
||||||
|
if(s.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
||||||
session.setAttribute('contextClassRefToSet', 'urn:qa.agov.ch:names:tc:ac:classes:100')
|
session.setAttribute('ch.nevis.idm.User.gender', '3')
|
||||||
|
}
|
||||||
// if the account has no profile, we must not return address or svnr
|
|
||||||
session.setAttribute('agov.appAddressRequired', 'false')
|
|
||||||
session.setAttribute('agov.appSvnrAllowed', 'false')
|
// handle accounts qa attributes, and set them in session
|
||||||
|
// account itself, only needed if not authenticated with e-ID
|
||||||
response.setResult('ok')
|
if (!'urn:qa.agov.ch:names:tc:authfactor:eid'.equalsIgnoreCase(authenticationMethod)) {
|
||||||
return
|
def idVerificationList = getUserAGOVLoiIdVerification()
|
||||||
}
|
def idVerification = 'None'
|
||||||
|
if (idVerificationList && !idVerificationList.isEmpty()) {
|
||||||
// Transform sex to number
|
idVerification = idVerificationList.last()
|
||||||
if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
|
}
|
||||||
session.setAttribute('ch.nevis.idm.User.gender', '1')
|
s.setAttribute('idVerification', idVerification)
|
||||||
}
|
|
||||||
if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
// contextClassRefToSet based on highest level-role assigned to default profile
|
||||||
session.setAttribute('ch.nevis.idm.User.gender', '2')
|
for (String role : getUserAGOVLoiRoles()) {
|
||||||
}
|
if (role.startsWith('level')) {
|
||||||
if(session.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
def roleLevel = role.substring(5)
|
||||||
session.setAttribute('ch.nevis.idm.User.gender', '3')
|
int roleLevelNumber = Integer.parseInt(roleLevel)
|
||||||
}
|
|
||||||
|
if (highestRoleLevelNumber< roleLevelNumber) {
|
||||||
for (String role : getUserAGOVLoiRoles()) {
|
highestRoleLevelNumber=roleLevelNumber
|
||||||
if (role.startsWith('level')) {
|
}
|
||||||
def roleLevel = role.substring(5)
|
}
|
||||||
int roleLevelNumber = Integer.parseInt(roleLevel)
|
}
|
||||||
|
|
||||||
if (highestRoleLevelNumber< roleLevelNumber) {
|
LOG.debug('CheckLoa: Highest role Level ' + highestRoleLevelNumber.toString() +' contextclassref ' + requestedRoleLevelNumber.toString())
|
||||||
highestRoleLevelNumber=roleLevelNumber
|
LOG.debug('CheckLoa: Compare ' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
|
||||||
}
|
|
||||||
}
|
//set attribute Actual Role Level
|
||||||
}
|
s.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
||||||
|
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
||||||
LOG.debug('CheckLoa: Highest role Level ' + highestRoleLevelNumber.toString() +' contextclassref ' + requestedRoleLevelNumber.toString())
|
|
||||||
LOG.debug('CheckLoa: Compare ' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
|
// set attribute ValidFrom and ValidTo (only for higher than 100)
|
||||||
|
if (highestRoleLevelNumber > 100) {
|
||||||
//set attribute Actual Role Level
|
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
|
||||||
session.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
|
||||||
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
|
||||||
|
LOG.debug('CheckLoa: ValidFrom :' + validFrom)
|
||||||
if (highestRoleLevelNumber > 0) {
|
LOG.debug('CheckLoa: ValidTo :' + validTo)
|
||||||
// set attribute contextClassRefToSet
|
|
||||||
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
|
if(validFrom != '') {
|
||||||
} else {
|
s.setAttribute('ValidFrom', '' + validFrom)
|
||||||
// by default 100
|
}
|
||||||
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' )
|
if(validTo != '') {
|
||||||
}
|
s.setAttribute('ValidTo', '' + validTo)
|
||||||
|
}
|
||||||
// no login for users with a recovery role
|
}
|
||||||
def recoveryRoleList = getUserAGOVRecoveryRoles()
|
if (highestRoleLevelNumber > 0) {
|
||||||
|
// set attribute contextClassRefToSet
|
||||||
if (recoveryRoleList.contains('mustRecover')) {
|
s.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
|
||||||
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
} else {
|
||||||
session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown' )
|
// by default 100
|
||||||
|
s.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' )
|
||||||
def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'
|
}
|
||||||
def idVerification = getUserIdVerificationForRecovery() ?: origIdVerification
|
}
|
||||||
session.setAttribute('agov.recovery.currentIdVerification', '' + idVerification )
|
// address related, needed in any case (also e-ID)
|
||||||
|
def adressVerificationList = getUserAGOVLoiIdVerification('200')
|
||||||
// align currentAgovAq with the method selected for idVerification
|
def adressVerification = 'None'
|
||||||
def currentAgovAqForRecovery = getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber)
|
if (adressVerificationList && !adressVerificationList.isEmpty()) {
|
||||||
session.setAttribute('agov.recovery.currentAgovAq', '' + currentAgovAqForRecovery)
|
adressVerification = adressVerificationList[0]
|
||||||
|
}
|
||||||
def validFrom = getUserMustRecoverValidFrom() ?: ''
|
s.setAttribute('agov.adressVerification', '' + adressVerification)
|
||||||
session.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + validFrom )
|
|
||||||
|
if (!session.get('ch.adnovum.nevisidm.profileExtId')) {
|
||||||
LOG.debug("CheckLoa: mustRecover: origIdVerification=${origIdVerification}, idVerification=${idVerification}, currentAgovAqForRecovery=${currentAgovAqForRecovery}")
|
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', errorMessage='Account without Profile', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||||
|
|
||||||
response.setResult('exit.2')
|
// if the account has no profile, we must not return address or svnr
|
||||||
return
|
s.setAttribute('agov.appAddressRequired', 'false')
|
||||||
|
s.setAttribute('agov.appSvnrAllowed', 'false')
|
||||||
} else if (recoveryRoleList.contains('recovery')) {
|
|
||||||
if (recoveryRoleList.contains('recoveryCascade')) {
|
response.setResult('ok')
|
||||||
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recoveryCascade')
|
return
|
||||||
} else {
|
}
|
||||||
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
|
||||||
}
|
// no login for users with a recovery role (but onyl when not logging in with e-Id)
|
||||||
session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown')
|
// TODO/haburger/2025-07-01: automatic recovery if logging in with e-Id
|
||||||
session.setAttribute('agov.recovery.currentAgovAq', session.getAttribute('contextClassRefToSet') ?: 'urn:qa.agov.ch:names:tc:ac:classes:100' )
|
if (!'urn:qa.agov.ch:names:tc:authfactor:eid'.equalsIgnoreCase(authenticationMethod)) {
|
||||||
LOG.debug('CheckLoa: idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
|
// no login for users with a recovery role
|
||||||
def idVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString())
|
def recoveryRoleList = getUserAGOVRecoveryRoles()
|
||||||
session.setAttribute('agov.recovery.currentIdVerification', (idVerification.isEmpty() ? 'None' : idVerification.first()))
|
|
||||||
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString())) ?: ''
|
if (recoveryRoleList.contains('mustRecover')) {
|
||||||
session.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', validFrom)
|
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
||||||
|
s.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown' )
|
||||||
response.setResult('exit.2')
|
|
||||||
return
|
def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'
|
||||||
}
|
def idVerification = getUserIdVerificationForRecovery() ?: origIdVerification
|
||||||
|
s.setAttribute('agov.recovery.currentIdVerification', '' + idVerification )
|
||||||
|
|
||||||
if (highestRoleLevelNumber>=requestedRoleLevelNumber) {
|
// align currentAgovAq with the method selected for idVerification
|
||||||
|
def currentAgovAqForRecovery = getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevelNumber)
|
||||||
// set attribute ValidFrom and ValidTo (only for higher than 100)
|
s.setAttribute('agov.recovery.currentAgovAq', '' + currentAgovAqForRecovery)
|
||||||
if (highestRoleLevelNumber > 100) {
|
|
||||||
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
|
def validFrom = getUserMustRecoverValidFrom() ?: ''
|
||||||
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
|
s.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + validFrom )
|
||||||
|
|
||||||
LOG.debug('CheckLoa: ValidFrom :' + validFrom)
|
LOG.debug("CheckLoa: mustRecover: origIdVerification=${origIdVerification}, idVerification=${idVerification}, currentAgovAqForRecovery=${currentAgovAqForRecovery}")
|
||||||
LOG.debug('CheckLoa: ValidTo :' + validTo)
|
|
||||||
|
response.setResult('exit.2')
|
||||||
if(validFrom != '') {
|
return
|
||||||
session.setAttribute('ValidFrom', '' + validFrom)
|
|
||||||
}
|
} else if (recoveryRoleList.contains('recovery')) {
|
||||||
if(validTo != '') {
|
if (recoveryRoleList.contains('recoveryCascade')) {
|
||||||
session.setAttribute('ValidTo', '' + validTo)
|
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recoveryCascade')
|
||||||
}
|
} else {
|
||||||
}
|
s.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
||||||
response.setResult('ok')
|
}
|
||||||
return;
|
s.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown')
|
||||||
} else {
|
s.setAttribute('agov.recovery.currentAgovAq', session.getAttribute('contextClassRefToSet') ?: 'urn:qa.agov.ch:names:tc:ac:classes:100' )
|
||||||
// Insufficient_LoaInfo
|
LOG.debug('CheckLoa: idVerification2= '+ getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()))
|
||||||
response.setResult('exit.1');
|
def idVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString())
|
||||||
return;
|
s.setAttribute('agov.recovery.currentIdVerification', (idVerification.isEmpty() ? 'None' : idVerification.first()))
|
||||||
}
|
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString())) ?: ''
|
||||||
} catch (Exception ex) {
|
s.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', validFrom)
|
||||||
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)
|
response.setResult('exit.2')
|
||||||
def affectedLines = ex.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
return
|
||||||
LOG.error("FATAL: Script failure (at lines: ${affectedLines})", ex)
|
}
|
||||||
// AuthnFailed_Zero_RoleLvl
|
} else {
|
||||||
response.setResult('error');
|
// authenticated with e-ID, we adjust highestRoleLevelNumber to e-ID login
|
||||||
return;
|
highestRoleLevelNumber = 500
|
||||||
}
|
s.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
||||||
|
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifiy that AQ level is high enough
|
||||||
|
if (highestRoleLevelNumber>=requestedRoleLevelNumber) {
|
||||||
|
response.setResult('ok')
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Insufficient_LoaInfo
|
||||||
|
response.setResult('exit.1');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} 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}'")
|
||||||
|
ex = StackTraceUtils.sanitize(ex)
|
||||||
|
def affectedLines = ex.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
||||||
|
LOG.error("FATAL: Script failure (at lines: ${affectedLines})", ex)
|
||||||
|
// AuthnFailed_Zero_RoleLvl
|
||||||
|
response.setResult('error');
|
||||||
|
return;
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ def compareAndUpdateSessionVariables(sess, keys, isProperty){
|
||||||
return updatedKeys
|
return updatedKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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": {
|
||||||
|
|
|
@ -33,31 +33,6 @@ def clearEidSession(){
|
||||||
s.removeAttribute('agov.eid.User.origin')
|
s.removeAttribute('agov.eid.User.origin')
|
||||||
}
|
}
|
||||||
|
|
||||||
def updateLoginHistory(idmRestClient, userExtId, credentialExtId) {
|
|
||||||
try {
|
|
||||||
def baseUrl = parameters.get("baseUrl")
|
|
||||||
def clientExtId = parameters.get("clientExtId")
|
|
||||||
def endpoint = "$baseUrl/api/core/v1/$clientExtId/users/$userExtId/login-info"
|
|
||||||
def dto = "{\"success\": true,\"credentialExtId\": \"${credentialExtId}\"}"
|
|
||||||
|
|
||||||
def postRequest = new HTTPRequestWrapper()
|
|
||||||
postRequest.addToHeaders('Content-Type', ['application/json'])
|
|
||||||
postRequest.setPayLoad(dto.getBytes('UTF-8'))
|
|
||||||
postRequest.setPayLoad(dto.getBytes('UTF-8'))
|
|
||||||
|
|
||||||
def result = idmRestClient.postWithResponse(endpoint, postRequest)
|
|
||||||
if (result.getStatusCode() != 200) {
|
|
||||||
// best effort, we log only
|
|
||||||
// TODO/haburger/2025-06-24: context parameters are missing here (also in getAccounts)
|
|
||||||
LOG.warn("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${userExtId}, CredentialType='E-ID Link', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='failed to update login history for credential ${credentialExtId} (http status: ${result.getStatusCode()})'")
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// best effort, we log only
|
|
||||||
// TODO/haburger/2025-06-24: context parameters are missing here (also in getAccounts)
|
|
||||||
LOG.warn("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${userExtId}, CredentialType='E-ID Link', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='failed to update login history for credential ${credentialExtId} (${e})'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def getAccounts(json, String svnr) {
|
def getAccounts(json, String svnr) {
|
||||||
def idm_users_dto = json["Resources"]
|
def idm_users_dto = json["Resources"]
|
||||||
def accounts = [:]
|
def accounts = [:]
|
||||||
|
@ -126,9 +101,6 @@ if(inargs['submit'] && inargs['login'] && inargs['login'] != ''){
|
||||||
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"])
|
||||||
|
|
||||||
// update login history
|
|
||||||
updateLoginHistory(idmRestClient, account["extId"], account["credentialExtId"])
|
|
||||||
|
|
||||||
if(account["firstLogin"]){
|
if(account["firstLogin"]){
|
||||||
response.setResult('firstLogin')
|
response.setResult('firstLogin')
|
||||||
return
|
return
|
||||||
|
@ -206,9 +178,6 @@ try {
|
||||||
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"])
|
||||||
|
|
||||||
// update login history
|
|
||||||
updateLoginHistory(idmRestClient, account["extId"], account["credentialExtId"])
|
|
||||||
|
|
||||||
if(account["firstLogin"]){
|
if(account["firstLogin"]){
|
||||||
response.setResult('firstLogin')
|
response.setResult('firstLogin')
|
||||||
return
|
return
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -5,6 +5,7 @@ pattern:
|
||||||
name: "Ask_Mobile_Number"
|
name: "Ask_Mobile_Number"
|
||||||
properties:
|
properties:
|
||||||
authStatesFile: "res://6d83506dfcc430c12d81dfa3#authStatesFile"
|
authStatesFile: "res://6d83506dfcc430c12d81dfa3#authStatesFile"
|
||||||
|
parameters: "var://ask_mobile_number-template-parameters"
|
||||||
onSuccess:
|
onSuccess:
|
||||||
- "pattern://2cdd910036aa06b102863a4f"
|
- "pattern://2cdd910036aa06b102863a4f"
|
||||||
onFailure:
|
onFailure:
|
||||||
|
|
|
@ -8,7 +8,7 @@ pattern:
|
||||||
properties:
|
properties:
|
||||||
authStatesFile: "res://306ce091fd87bad6174d9e8b#authStatesFile"
|
authStatesFile: "res://306ce091fd87bad6174d9e8b#authStatesFile"
|
||||||
onSuccess:
|
onSuccess:
|
||||||
- "pattern://b87d0d2b640e8e545ad70234"
|
- "pattern://2cdd910036aa06b102863a4f"
|
||||||
onFailure:
|
onFailure:
|
||||||
- "pattern://4c65de021d362462324a3a5f"
|
- "pattern://4c65de021d362462324a3a5f"
|
||||||
nextSteps:
|
nextSteps:
|
||||||
|
|
|
@ -7,6 +7,13 @@ variables:
|
||||||
maxAllowed: 1
|
maxAllowed: 1
|
||||||
value: ".agov-d.azure.adnovum.net"
|
value: ".agov-d.azure.adnovum.net"
|
||||||
requireOverloading: true
|
requireOverloading: true
|
||||||
|
ask_mobile_number-template-parameters:
|
||||||
|
className: "ch.nevis.admin.v4.plugin.base.generation.property.TextProperty"
|
||||||
|
parameters:
|
||||||
|
required: false
|
||||||
|
syntax: "YAML"
|
||||||
|
value: null
|
||||||
|
requireOverloading: true
|
||||||
auth-session-store-database-host:
|
auth-session-store-database-host:
|
||||||
className: "ch.nevis.admin.v4.plugin.base.generation.property.HostPortProperty"
|
className: "ch.nevis.admin.v4.plugin.base.generation.property.HostPortProperty"
|
||||||
parameters:
|
parameters:
|
||||||
|
|
Loading…
Reference in New Issue