115 files added
This commit is contained in:
commit
4243be829d
|
@ -0,0 +1,12 @@
|
|||
schemaVersion: "1.0"
|
||||
bundles:
|
||||
- "nevisadmin-plugin-base-generation:7.2402.1.3"
|
||||
- "nevisadmin-plugin-oauth:7.2402.1.3"
|
||||
- "nevisadmin-plugin-nevisdetect:7.2402.1.3"
|
||||
- "nevisadmin-plugin-nevisauth:7.2402.1.3"
|
||||
- "nevisadmin-plugin-nevisdp:7.2402.1.3"
|
||||
- "nevisadmin-plugin-nevisproxy:7.2402.1.3"
|
||||
- "nevisadmin-plugin-mobile-auth:7.2402.1.3"
|
||||
- "nevisadmin-plugin-nevisidm:7.2402.1.3"
|
||||
- "nevisadmin-plugin-fido2:7.2402.1.3"
|
||||
- "nevisadmin-plugin-authcloud:7.2402.1.3"
|
|
@ -0,0 +1,21 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.idm.authstate.IdmUserVerifyState" final="false" resumeState="false">
|
||||
<ResultCond name="prospect" next="${state.done}"/>
|
||||
<!-- Security issue : goes to next state if client not found -->
|
||||
<ResultCond name="default" next="${state.failed}"/>
|
||||
<ResultCond name="failed" next="${state.failed}"/>
|
||||
<ResultCond name="clientNotFound" next="${state.failed}"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${request:traceId}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.loginId" value="${inargs:email}"/>
|
||||
<property name="user.loginType" value="EMAIL"/>
|
||||
<property name="client.name" value="${param.client.name}"/>
|
||||
<property name="presetNoteValues" value="false"/>
|
||||
<property name="detaillevel.profile" value="HIGH"/>
|
||||
<property name="detaillevel.role" value="HIGH"/>
|
||||
<property name="detaillevel.authorization" value="HIGH"/>
|
||||
<property name="detaillevel.dataroom" value="HIGH"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,85 @@
|
|||
#################### general parameters ####################
|
||||
|
||||
# parameter for providing the resource path (see application.webdata.path)
|
||||
application.webdata.pathparam=logrendresourcepath
|
||||
|
||||
# default value for the resource path (nevisProxy needs a forward connector with MappingType=pathinfo and URIPrefix=/login/resources)
|
||||
application.webdata.pathparam.default=/login/resources
|
||||
|
||||
# path to webdata directory for http requests (relative to context)
|
||||
# isiweb must redirect all requests within this path to logrend (used for images, css, js)
|
||||
# {0} is replaced with the query parameter defined via "application.webdata.pathparam" (to be provided by isiweb)
|
||||
# {1} is replaced with the context path of the webapp (without the leading /)
|
||||
# {2} is replaced by a slash if both {0} and {1} are not empty
|
||||
# a tailing slash is added if the resulting path is not empty
|
||||
application.webdata.path={0}{2}{1}
|
||||
|
||||
# obfuscate urls
|
||||
application.url.obfuscate=no
|
||||
|
||||
# charset of incoming GuiDescriptor XML (default: <file.encoding> - default for -Dfile.encoding is ISO-8859-1)
|
||||
application.input.charset=UTF-8
|
||||
|
||||
# HTML-encode any inputs before putting them in the output
|
||||
# (*only* set to "no" if isiweb already encodes the user inputs)
|
||||
application.inputs.htmlencode=yes
|
||||
# HTML-encode any GUI element values before putting them in the output - velocity only (default: no, for backwards compatibility)
|
||||
#application.inputs.htmlencode.guielems=yes
|
||||
|
||||
# content type (default: text/html; charset=<file.encoding> - default for file.encoding is ISO-8859-1)
|
||||
application.render.content.type=text/html; charset=UTF-8
|
||||
|
||||
# name of the package
|
||||
application.package.name=nevislogrend
|
||||
|
||||
# name of the current login application (if not empty, this is always used)
|
||||
application.loginapp.current=
|
||||
|
||||
# name of the default login application (used if login application is not defined by other means)
|
||||
application.loginapp.default=${default-app}
|
||||
|
||||
# allow overriding the login application via a configurable header
|
||||
# the resulting login application name is _<header><headerValue> for historical reasons
|
||||
application.loginapp.override=header:channel
|
||||
|
||||
# time in minutes after which a cache-entry should be checked against the source (0 to turn revalidation off completely)
|
||||
cache.revalidate.delay=-1
|
||||
|
||||
# read cached data from files (file) or from classloader (class)
|
||||
# if both are given, try to find the file in the given order
|
||||
cache.source=file
|
||||
|
||||
# tags for variable substitution in GUI labels
|
||||
keytag.start=${
|
||||
keytag.end=}
|
||||
|
||||
# perform litdict mapping on GUI labels
|
||||
application.gui.litdict=yes
|
||||
|
||||
# perform ${bean.<name>} variable substitution on GUI labels
|
||||
application.gui.substitution=yes
|
||||
|
||||
# allow extraction of loginapplicationid from path info (first part until /):
|
||||
application.accept.loginapplicationidfromuri=no
|
||||
|
||||
# Container related configuration
|
||||
server.name=default
|
||||
|
||||
# The protocol that can be used to connect to this instance
|
||||
# Allowed values: http, https
|
||||
server.protocol=${protocol}
|
||||
|
||||
# the hostname on which the component listener will be installed
|
||||
server.host=${host}
|
||||
|
||||
# the port on which the components listener binds to.
|
||||
server.port=${port}
|
||||
|
||||
# the path section contains all the items lying around in the filesystem
|
||||
# this points to the instance's configuration folder
|
||||
path.config=/var/opt/nevislogrend/${instance}/conf
|
||||
path.instance=/var/opt/nevislogrend/${instance}
|
||||
|
||||
${keystore}${keystore-password}${truststore}${truststore-password}
|
||||
|
||||
management.healthchecks.enabled=${healthchecks}
|
|
@ -0,0 +1,10 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="true" resumeState="true">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_on_going" label="not.used.label">
|
||||
<GuiElem name="authRequestId" type="hidden" label="not.used.label" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="recovery" type="button" label="not.used" value="recovery" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/recovery_ongoing.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,4 @@
|
|||
if (inargs['recovery'] != null && inargs['recovery'] == 'recovery' ) {
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,11 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="true">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<ResultCond name="error" next="${state.failed}"/>
|
||||
<ResultCond name="notFullyRegistered" next="${state.exit.1}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="NoGui">
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="Recovery"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/recovery-checkAccount.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,79 @@
|
|||
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||
import groovy.xml.XmlSlurper
|
||||
|
||||
|
||||
// AGOVaq conversion
|
||||
def maxLoiRoleToCtxClssConvertorMap = [
|
||||
"level100": "urn:qa.agov.ch:names:tc:ac:classes:100",
|
||||
"level200": "urn:qa.agov.ch:names:tc:ac:classes:200",
|
||||
"level300": "urn:qa.agov.ch:names:tc:ac:classes:300",
|
||||
"level400": "urn:qa.agov.ch:names:tc:ac:classes:400",
|
||||
"level500": "urn:qa.agov.ch:names:tc:ac:classes:500"
|
||||
]
|
||||
|
||||
def cleanSession() {
|
||||
def s = request.getAuthSession(true)
|
||||
|
||||
s.removeAttribute('agov.op.onboarding.ctxClass')
|
||||
s.removeAttribute('agov.op.onboarding.minLoi')
|
||||
s.removeAttribute('agov.op.onboarding.homeName')
|
||||
s.removeAttribute('agov.op.onboarding.subject')
|
||||
s.removeAttribute('agov.op.onboarding.process.state')
|
||||
s.removeAttribute('ch.adnovum.nevisidm.userDto')
|
||||
s.removeAttribute('saml.response.statusCode')
|
||||
if (response.getActualRoles().length > 0) {
|
||||
def actualRoles = Arrays.copyOf(response.getActualRoles(), response.getActualRoles().length)
|
||||
actualRoles.each{ role -> response.removeActualRole(role) }
|
||||
}
|
||||
}
|
||||
|
||||
// for autditing
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
def sourceIp = request.getLoginContext()['connection.HttpHeader.X-Real-IP'] ?: 'unknown'
|
||||
def userAgent = request.getLoginContext()['connection.HttpHeader.user-agent'] ?: 'unknown'
|
||||
def maxLoi = 'unknown'
|
||||
|
||||
|
||||
// new
|
||||
if (session['ch.adnovum.nevisidm.userDto'] != null && notes['lasterror'] == null) {
|
||||
try {
|
||||
def userDto = new XmlSlurper().parseText(session['ch.adnovum.nevisidm.userDto'])
|
||||
def userState = userDto.state
|
||||
LOG.debug("Recovery: Dto is '${userDto}")
|
||||
LOG.debug("Recovery: state is '${userState}")
|
||||
if (userState == 'ACTIVE') {
|
||||
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()
|
||||
def accountStatusRoles = userDto.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-AccountStatus' }.collect({ node -> node.name.text() })
|
||||
def hasRecoveryRole = accountStatusRoles.isEmpty() ? null : accountStatusRoles.sort().first()
|
||||
LOG.debug("Recovery: MaxLoi is '${maxLoi}'")
|
||||
LOG.debug("Recovery: hasRecoveryRole is '${hasRecoveryRole}'")
|
||||
if (maxLoi != null && maxLoiRoleToCtxClssConvertorMap.containsKey(maxLoi)) {
|
||||
response.setResult('ok')
|
||||
return
|
||||
} else {
|
||||
LOG.debug("Recovery: no 'AGOV-Loi'-role assigned to user ${user}")
|
||||
response.setResult('notFullyRegistered')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// state != ACTIVE and no lasterror should not happen
|
||||
LOG.error("Recovery: state='${userState}' but not lasterror set")
|
||||
response.setNote('lasterror', '9909')
|
||||
response.setNote('lasterrorinfo', 'internal error')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Recovery processing failed: Exception " + e)
|
||||
response.setNote('lasterror', '9909')
|
||||
response.setNote('lasterrorinfo', 'internal error')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
response.setResult('error')
|
||||
return
|
||||
|
||||
// new
|
|
@ -0,0 +1,257 @@
|
|||
import org.codehaus.groovy.runtime.StackTraceUtils
|
||||
import groovy.xml.XmlSlurper
|
||||
|
||||
def getUserAGOVLoiRoles() {
|
||||
// set attibutes from DTO: -> AGOVaq
|
||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||
return list.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-Loi' }.collect({ node -> node.name.text() })
|
||||
}
|
||||
|
||||
def getUserAGOVRecoveryRoles() {
|
||||
// set attibutes from DTO: -> AGOV
|
||||
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() })
|
||||
}
|
||||
|
||||
def getUserAGOVLoiIdVerification() {
|
||||
// set attibutes from DTO: -> idVerification
|
||||
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()})
|
||||
}
|
||||
|
||||
def getUserAGOVLoiIdVerification(level) {
|
||||
// set attibutes from DTO: -> idVerification
|
||||
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()})
|
||||
}
|
||||
|
||||
def getUserAGOVLoiValidFrom(level) {
|
||||
// set attibutes from DTO: -> validFrom
|
||||
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()
|
||||
}
|
||||
|
||||
def getUserAGOVLoiValidTo(level) {
|
||||
// set attibutes from DTO: -> validTo
|
||||
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()
|
||||
}
|
||||
|
||||
def getUserIdVerificationForRecovery() {
|
||||
// application is AGOV-AccountStatus
|
||||
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()
|
||||
|
||||
if (!result) {
|
||||
// fallback if not explicitly set
|
||||
def currentLoaRole = getUserAGOVLoiRoles()?.sort()?.last() ?: 'level100'
|
||||
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()
|
||||
switch (currentLoaRole) {
|
||||
case 'level100':
|
||||
result = chDomicile ? 'SimpleLetter' : 'Video'
|
||||
break
|
||||
case 'level200':
|
||||
result = chDomicile ? 'Bmid' : 'Video'
|
||||
break
|
||||
case 'level300':
|
||||
case 'level400':
|
||||
result = chDomicile ? lastIdVerification : 'Video'
|
||||
break
|
||||
default:
|
||||
LOG.warn("unexpected loa on account: ${currentLoaRole}")
|
||||
// safest default, should work in any case
|
||||
result = 'Video'
|
||||
}
|
||||
LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
|
||||
}
|
||||
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'))
|
||||
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()) : ''
|
||||
}
|
||||
|
||||
// Accounting
|
||||
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'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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'
|
||||
|
||||
try {
|
||||
// beef
|
||||
def session = request.getAuthSession(true)
|
||||
def highestRoleLevelNumber = 0
|
||||
def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
|
||||
def adressVerificationList = getUserAGOVLoiIdVerification('200')
|
||||
def adressVerification = 'None'
|
||||
if (adressVerificationList && !adressVerificationList.isEmpty()) {
|
||||
adressVerification = adressVerificationList[0]
|
||||
}
|
||||
|
||||
LOG.debug('CheckLoa: Requested role level '+ requestedRoleLevelNumber)
|
||||
LOG.debug('CheckLoa: idVerification: ' + getUserAGOVLoiIdVerification())
|
||||
LOG.debug('CheckLoa: adressVerification : ' + adressVerification)
|
||||
|
||||
def idVerificationMethodList = getUserAGOVLoiIdVerification()
|
||||
|
||||
session.setAttribute('idVerification', idVerificationMethodList.isEmpty() ? 'None' : idVerificationMethodList.last())
|
||||
session.setAttribute('agov.adressVerification', '' + adressVerification)
|
||||
|
||||
|
||||
if (requestedRoleLevelNumber == 0) {
|
||||
// AuthnFailed_Zero_RoleLvl
|
||||
response.setResult('error');
|
||||
return
|
||||
}
|
||||
|
||||
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}'")
|
||||
|
||||
session.setAttribute('contextClassRefToSet', 'urn:qa.agov.ch:names:tc:ac:classes:100')
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
||||
|
||||
// Transform sex to number
|
||||
if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
|
||||
session.setAttribute('ch.nevis.idm.User.gender', '1')
|
||||
}
|
||||
if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
||||
session.setAttribute('ch.nevis.idm.User.gender', '2')
|
||||
}
|
||||
if(session.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
||||
session.setAttribute('ch.nevis.idm.User.gender', '3')
|
||||
}
|
||||
|
||||
|
||||
for (String role : getUserAGOVLoiRoles()) {
|
||||
if (role.startsWith('level')) {
|
||||
def roleLevel = role.substring(5)
|
||||
int roleLevelNumber = Integer.parseInt(roleLevel)
|
||||
if (highestRoleLevelNumber == 0) {
|
||||
highestRoleLevelNumber = roleLevelNumber
|
||||
}
|
||||
if (highestRoleLevelNumber< roleLevelNumber) {
|
||||
highestRoleLevelNumber=roleLevelNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
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('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
|
||||
|
||||
if (highestRoleLevelNumber > 0) {
|
||||
// set attribute contextClassRefToSet
|
||||
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
|
||||
} else {
|
||||
// by default 100
|
||||
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' )
|
||||
}
|
||||
|
||||
// no login for users with a recovery role
|
||||
for (String role : getUserAGOVRecoveryRoles()) {
|
||||
if (role == 'mustRecover') {
|
||||
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
||||
session.setAttribute('agov.recovery.authenticatedWith', session.getAttribute('authenticatedWith') ?: 'unknown' )
|
||||
|
||||
def origIdVerification = getUserAGOVLoiIdVerification(highestRoleLevelNumber.toString()) ?: 'None'
|
||||
|
||||
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
|
||||
|
||||
} else if (role == 'recovery') {
|
||||
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('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())) ?: ''
|
||||
session.setAttribute('agov.recovery.currentAgovAqRoleValidFrom', validFrom)
|
||||
|
||||
response.setResult('exit.2')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (highestRoleLevelNumber>=requestedRoleLevelNumber) {
|
||||
|
||||
// set attribute ValidFrom and ValidTo (only for higher than 100)
|
||||
if (highestRoleLevelNumber > 100) {
|
||||
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
|
||||
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
|
||||
|
||||
LOG.debug('CheckLoa: ValidFrom :' + validFrom)
|
||||
LOG.debug('CheckLoa: ValidTo :' + validTo)
|
||||
|
||||
if(validFrom != '') {
|
||||
session.setAttribute('ValidFrom', '' + validFrom)
|
||||
}
|
||||
if(validTo != '') {
|
||||
session.setAttribute('ValidTo', '' + validTo)
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
|
||||
<ResultCond name="error" next="${state.exit.1}"/>
|
||||
<ResultCond name="cancel" next="${state.failed}"/>
|
||||
<ResultCond name="ok" next="${state.done}" />
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="fido2_auth" label="title.login.fido2">
|
||||
</Gui>
|
||||
<!-- does this realy do something ? -->
|
||||
<Arg name="fido2UserVerification" value="required"/>
|
||||
</Response>
|
||||
<property name="parameter.cancel" value="OnCancel_Dispatch"/>
|
||||
<property name="parameter.fido" value="${param.fido2.serviceAndPort:fido2:9443}"/>
|
||||
<property name="parameter.rpId" value="${param.rpId:auth.agov.admin.ch}"/>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/fido2_auth.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,202 @@
|
|||
import groovy.json.JsonBuilder
|
||||
import groovy.json.JsonSlurper
|
||||
import java.util.UUID
|
||||
|
||||
if (inargs.containsKey('cancel_fido2')) {
|
||||
response.setResult('cancel')
|
||||
LOG.debug("Fido2Auth: authentication cancelled by user")
|
||||
return
|
||||
}
|
||||
|
||||
def base64url(uuid) {
|
||||
def msb = uuid.getMostSignificantBits()
|
||||
def lsb = uuid.getLeastSignificantBits()
|
||||
return new byte[] {
|
||||
(byte) msb,
|
||||
(byte) (msb >> 8),
|
||||
(byte) (msb >> 16),
|
||||
(byte) (msb >> 24),
|
||||
(byte) (msb >> 32),
|
||||
(byte) (msb >> 40),
|
||||
(byte) (msb >> 48),
|
||||
(byte) (msb >> 56),
|
||||
(byte) lsb,
|
||||
(byte) (lsb >> 8),
|
||||
(byte) (lsb >> 16),
|
||||
(byte) (lsb >> 24),
|
||||
(byte) (lsb >> 32),
|
||||
(byte) (lsb >> 40),
|
||||
(byte) (lsb >> 48),
|
||||
(byte) (lsb >> 56)
|
||||
}.encodeBase64Url().toString()
|
||||
}
|
||||
|
||||
def showGui() {
|
||||
response.setGuiName('fido2_auth') // name is the trigger for including the JS
|
||||
response.setGuiLabel('title.login.fido2')
|
||||
response.addInfoGuiField('info', 'info.login.fido2', null)
|
||||
response.addHiddenGuiField('authRequestId', 'not used', session['ch.nevis.auth.saml.request.id'])
|
||||
response.addTextGuiField('email', 'email', session['ch.nevis.idm.User.email'])
|
||||
if (notes.containsKey('lasterrorinfo') || notes.containsKey('lasterror')) {
|
||||
response.addErrorGuiField('lasterror', notes['lasterrorinfo'], notes['lasterror'])
|
||||
}
|
||||
if (parameters.containsKey('cancel')) {
|
||||
response.addButtonGuiField('cancel_fido2', 'cancel.login.fido2.button.label', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
def getPath() {
|
||||
if (inargs.containsKey('path')) { // form POST
|
||||
return inargs['path']
|
||||
}
|
||||
if (inargs.containsKey('o.path.v')) { // AJAX POST
|
||||
return inargs['o.path.v']
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
def post(connection, json) {
|
||||
connection.setRequestMethod("POST")
|
||||
connection.setRequestProperty("Content-Type", "application/json")
|
||||
connection.setDoOutput(true) // required to write body
|
||||
String body = json.toString()
|
||||
LOG.debug("Fido2Auth: ==> Request: '${body}'")
|
||||
connection.getOutputStream().write(body.getBytes())
|
||||
}
|
||||
|
||||
String userExtId = session['ch.adnovum.nevisidm.user.extId'] ?: session['ch.nevis.idm.User.extId'] ?: request.getUserId() ?: notes['userid']
|
||||
if (userExtId == null) {
|
||||
LOG.error("Fido2Auth: missing extId of nevisIDM user. check your authentication flow.")
|
||||
}
|
||||
// without the user extId this script won't work and we can fail with a System Error
|
||||
Objects.requireNonNull(userExtId)
|
||||
|
||||
def path = getPath()
|
||||
if (path == null) {
|
||||
showGui() // POST from JavaScript not received
|
||||
return
|
||||
}
|
||||
|
||||
def connection = null
|
||||
try {
|
||||
def fullPath = "https://${parameters.get('fido')}${path}"
|
||||
LOG.debug("Fido2Auth: opening connection to '${fullPath}'")
|
||||
connection = new URL(fullPath).openConnection()
|
||||
} catch (Exception e) {
|
||||
LOG.error("Fido2Auth: opening connection failed", e)
|
||||
notes.setProperty('lasterrorinfo', 'FIDO2 authentication failed')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
|
||||
def json = new JsonBuilder()
|
||||
|
||||
if (path == '/nevisfido/fido2/attestation/options') {
|
||||
json {
|
||||
"username" userExtId
|
||||
"userVerification" "required"
|
||||
}
|
||||
post(connection, json)
|
||||
def responseCode = connection.responseCode
|
||||
|
||||
// non existing account, or account without FIDO2 key case
|
||||
if (responseCode == 404 || responseCode == 400) {
|
||||
|
||||
LOG.debug("Fido2Auth: <== Response: ${responseCode}")
|
||||
|
||||
// Accounting
|
||||
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'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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'
|
||||
def tAuth = System.currentTimeMillis() - (request.getSession(true).getCreationTime().getEpochSecond() * 1000)
|
||||
|
||||
LOG.info("Event='NOACCOUNT', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${session['ch.nevis.idm.User.email']}, CredentialType='${credentialType}', tAuth=${tAuth}ms, SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
// returning a fake options structure, which shouldn't leak whether the user account exists or not
|
||||
// keyId is unique per environment and email, fido2SessionId and challenge are renewed each time
|
||||
def keyId = UUID.nameUUIDFromBytes("${parameters['rpId']}.${session['ch.nevis.idm.User.email']}".getBytes())
|
||||
def responseText = """{"status": "ok",
|
||||
"errorMessage": "",
|
||||
"fido2SessionId": "${UUID.randomUUID()}",
|
||||
"challenge": "${base64url(UUID.randomUUID())}",
|
||||
"timeout": 300000,
|
||||
"rpId": "${parameters['rpId']}",
|
||||
"allowCredentials": [
|
||||
{
|
||||
"type": "public-key",
|
||||
"id": "${base64url(keyId)}",
|
||||
"transports": []
|
||||
}
|
||||
],
|
||||
"userVerification": "required"}"""
|
||||
|
||||
response.setContent(responseText) // return response from nevisFIDO "as-is"
|
||||
response.setContentType('application/json')
|
||||
response.setHttpStatusCode(200)
|
||||
response.setIsDirectResponse(true)
|
||||
return
|
||||
}
|
||||
|
||||
def responseText = connection.inputStream.text
|
||||
LOG.debug("Fido2Auth: <== Response: ${responseCode} : ${responseText}")
|
||||
response.setContent(responseText) // return response from nevisFIDO "as-is"
|
||||
response.setContentType('application/json')
|
||||
response.setHttpStatusCode(200)
|
||||
response.setIsDirectResponse(true)
|
||||
return
|
||||
}
|
||||
|
||||
if (path == '/nevisfido/fido2/assertion/result') {
|
||||
|
||||
if (inargs.containsKey('authRequestId') && (inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) {
|
||||
// wrong request, "force" a timeout
|
||||
LOG.debug('Fido2Auth: authentication timeout enforced, due to concurrent requests')
|
||||
|
||||
response.setIsDirectResponse(true)
|
||||
response.setContentType('text/html; charset=UTF-8')
|
||||
response.setContent('Timeout')
|
||||
response.setHttpStatusCode(205)
|
||||
response.setHeader('IDP-AUTH', 'Timeout')
|
||||
|
||||
// CONTINUE to keep the other request beeing processed
|
||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||
return
|
||||
}
|
||||
|
||||
def userHandleValue = userExtId.getBytes().encodeBase64Url().toString()
|
||||
LOG.debug("Fido2Auth: encoded userHandle: ${userHandleValue}")
|
||||
json {
|
||||
"id" inargs['id']
|
||||
"type" inargs['type']
|
||||
response {
|
||||
"clientDataJSON" inargs['response.clientDataJSON']
|
||||
"authenticatorData" inargs['response.authenticatorData']
|
||||
"signature" inargs['response.signature']
|
||||
"userHandle" userHandleValue
|
||||
}
|
||||
}
|
||||
post(connection, json)
|
||||
def responseCode = connection.responseCode
|
||||
// test if credentials exist
|
||||
if (responseCode != 400) {
|
||||
def responseText = connection.inputStream.text
|
||||
LOG.debug("Fido2Auth: <== Response: ${responseCode} : ${responseText}")
|
||||
if (responseCode == 200 && new JsonSlurper().parseText(responseText).status == 'ok') {
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
||||
}
|
||||
//response.setHttpStatusCode(400)
|
||||
//response.setIsDirectResponse(true)
|
||||
// DEFINE how to handel error
|
||||
notes.setProperty('lasterror', '1')
|
||||
notes.setProperty('lasterrorinfo', 'FIDO2 authentication failed')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
|
||||
response.setError(1, "FIDO2 authentication failed")
|
||||
showGui()
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
|
||||
RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
|
||||
VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
|
||||
DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
|
||||
ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
|
||||
VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
|
||||
mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
|
||||
IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
|
||||
mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
|
||||
XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
|
||||
dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
|
||||
jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
|
||||
BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
|
||||
DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
|
||||
9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
|
||||
jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
|
||||
Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
|
||||
ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
|
||||
R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
|
||||
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
|
||||
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
|
||||
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
|
||||
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
|
||||
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
|
||||
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
|
||||
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
|
||||
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
|
||||
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
|
||||
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
|
||||
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
|
||||
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
|
||||
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
|
||||
MrY=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="true">
|
||||
<ResultCond name="ok" next="${state.entry}_Processing"/>
|
||||
<ResultCond name="default" next="${state.entry}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_accessapp_auth">
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="fallback" type="button" label="mobile_auth.cancel.button.label" value="true" optional="true"/>
|
||||
<GuiElem name="accessApp" type="hidden" value="${sess:agov.recovery.accessapp}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/Recovery_mobile_nless_auth.groovy"/>
|
||||
<property name="parameter.agovmeregistrationurl" value="${var.agovmeregistrationurl}"/>
|
||||
<property name="parameter.recoveryurl" value="${var.recoveryurl}"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="${state.entry}_Processing" class="ch.nevis.auth.fido.uaf.authstate.OutOfBandFidoUafAuthState" final="false" resumeState="false">
|
||||
<ResultCond name="error" next="${state.entry}_Processing"/>
|
||||
<ResultCond name="failed" next="${state.entry}"/>
|
||||
<ResultCond name="ok" next="${state.done}" authLevel="2"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||
</Response>
|
||||
<property name="fidoUafServerUrl" value="https://fido-uaf:9443/nevisfido"/>
|
||||
<property name="dispatcher" value="link"/>
|
||||
<property name="httpclient.tls.trustStoreRef" value="${keystore}"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,53 @@
|
|||
import groovy.json.JsonBuilder
|
||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||
|
||||
|
||||
def getHeader(String name) {
|
||||
def inctx = request.getLoginContext()
|
||||
// case-insensitive lookup of HTTP headers
|
||||
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
||||
map.putAll(inctx)
|
||||
return map['connection.HttpHeader.' + name]
|
||||
}
|
||||
|
||||
def clearFidoUAFSession() {
|
||||
def s = request.getAuthSession(true)
|
||||
s.removeAttribute('ch.nevis.auth.fido.uaf.fidouafsessionid')
|
||||
inargs.remove('fallback')
|
||||
}
|
||||
|
||||
|
||||
// dispatch AJAX calls and form POST when operation is done
|
||||
if (inargs['fidoUafDone'] == 'true' ||
|
||||
inargs.containsKey('o.fidoUafSessionId.v') ||
|
||||
getHeader('Content-Type') == 'application/json') {
|
||||
|
||||
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
|
||||
LOG.debug("received polling for wrong fido session ${inargs['o.fidoUafSessionId.v']} (correct: ${session['ch.nevis.auth.fido.uaf.fidouafsessionid']})")
|
||||
def json = new JsonBuilder()
|
||||
json {
|
||||
"status" "unknown"
|
||||
"timestamp" org.joda.time.DateTime.now().toString()
|
||||
}
|
||||
String body = json.toString()
|
||||
|
||||
response.setContent(body)
|
||||
response.setContentType('application/json')
|
||||
response.setHttpStatusCode(200)
|
||||
response.setIsDirectResponse(true)
|
||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||
return
|
||||
}
|
||||
|
||||
// continue with OutOfBandFidoUafAuthState
|
||||
response.setResult('ok')
|
||||
}
|
||||
|
||||
|
||||
// dispatch form post with onReload input field : refresh QR-code FIDO UAF
|
||||
if (inargs.containsKey('onReload')) {
|
||||
clearFidoUAFSession()
|
||||
response.setResult('default')
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="NotUsed"/>
|
||||
</Response>
|
||||
<property name="parameter.cookie.domain" value="${param.cookie.domain}"/>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/SendSamlResponseWithError.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,24 @@
|
|||
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||
|
||||
// Accounting
|
||||
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'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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'
|
||||
def tAuth = System.currentTimeMillis() - (request.getSession(true).getCreationTime().getEpochSecond() * 1000)
|
||||
|
||||
def errorCode = notes['saml.errorCode'] ?: 'unknown'
|
||||
def errorMessage = notes['saml.errorMessage'] ?: 'unknown'
|
||||
|
||||
LOG.info("Event='SAMLERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', tAuth=${tAuth}ms, errorCode='${errorCode}', errorMessage='${errorMessage}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
|
||||
|
||||
// delete the login cookie
|
||||
def agovLoginCookie = "agovLogin=deleted; Domain=${parameters.get('cookie.domain')}; Path=/; Max-Age=0; SameSite=Strict; Secure; HttpOnly"
|
||||
response.setHeader('Set-Cookie', agovLoginCookie)
|
||||
|
||||
response.setStatus(AuthResponse.AUTH_ERROR)
|
||||
return
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,16 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
|
||||
<ResultCond name="error" next="${state.exit.1}"/>
|
||||
<ResultCond name="cancel" next="${state.failed}"/>
|
||||
<ResultCond name="ok" next="${state.done}" />
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_fidokey_auth" label="title.login.fido2">
|
||||
<!-- <Gui name="fido2_auth" label="title.login.fido2">-->
|
||||
<GuiElem name="securityKey" type="hidden" value="${sess:agov.recovery.securityKey}" optional="true"/>
|
||||
</Gui>
|
||||
<!-- does this realy do something ? -->
|
||||
<Arg name="fido2UserVerification" value="required"/>
|
||||
</Response>
|
||||
<property name="parameter.cancel" value="OnCancel_Dispatch"/>
|
||||
<property name="parameter.fido" value="fido2:9443"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/recovery_fido2_auth.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,151 @@
|
|||
import groovy.json.JsonBuilder
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
if (inargs.containsKey('cancel_fido2')) {
|
||||
response.setResult('cancel')
|
||||
return
|
||||
}
|
||||
|
||||
def showGui() {
|
||||
response.setGuiName('recovery_fidokey_auth') // name is the trigger for including the JS
|
||||
//response.setGuiName('fido2_auth') // name is the trigger for including the JS
|
||||
response.setGuiLabel('title.login.fido2')
|
||||
response.addInfoGuiField('info', 'info.login.fido2', null)
|
||||
response.addHiddenGuiField('authRequestId', 'not used', session['ch.nevis.auth.saml.request.id'])
|
||||
response.addHiddenGuiField('securityKey', 'not used', session['agov.recovery.securityKey'])
|
||||
response.addTextGuiField('email', 'email', session['ch.nevis.idm.User.email'])
|
||||
if (notes.containsKey('lasterrorinfo') || notes.containsKey('lasterror')) {
|
||||
response.addErrorGuiField('lasterror', notes['lasterrorinfo'], notes['lasterror'])
|
||||
}
|
||||
if (parameters.containsKey('cancel')) {
|
||||
// TODO koenig 20221021: replace with specific label
|
||||
response.addButtonGuiField('cancel_fido2', 'cancel.login.fido2.button.label', 'true')
|
||||
}
|
||||
}
|
||||
|
||||
def getPath() {
|
||||
if (inargs.containsKey('path')) { // form POST
|
||||
return inargs['path']
|
||||
}
|
||||
if (inargs.containsKey('o.path.v')) { // AJAX POST
|
||||
return inargs['o.path.v']
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
def post(connection, json) {
|
||||
connection.setRequestMethod("POST")
|
||||
connection.setRequestProperty("Content-Type", "application/json")
|
||||
connection.setDoOutput(true) // required to write body
|
||||
String body = json.toString()
|
||||
LOG.info("==> Request: ${body}")
|
||||
connection.getOutputStream().write(body.getBytes())
|
||||
}
|
||||
|
||||
String userExtId = session['ch.adnovum.nevisidm.user.extId'] ?: session['ch.nevis.idm.User.extId'] ?: request.getUserId() ?: notes['userid']
|
||||
if (userExtId == null) {
|
||||
LOG.error("missing extId of nevisIDM user. check your authentication flow.")
|
||||
}
|
||||
// without the user extId this script won't work and we can fail with a System Error
|
||||
Objects.requireNonNull(userExtId)
|
||||
|
||||
def path = getPath()
|
||||
if (path == null) {
|
||||
showGui() // POST from JavaScript not received
|
||||
return
|
||||
}
|
||||
|
||||
def connection = new URL("https://${parameters.get('fido')}${path}").openConnection()
|
||||
def json = new JsonBuilder()
|
||||
|
||||
if (path == '/nevisfido/fido2/attestation/options') {
|
||||
json {
|
||||
"username" userExtId
|
||||
"userVerification" "required"
|
||||
}
|
||||
post(connection, json)
|
||||
def responseCode = connection.responseCode
|
||||
// account without FIDO2 case
|
||||
if (responseCode == 400) {
|
||||
def responseText = '''{"status": "ok",
|
||||
"errorMessage": "",
|
||||
"fido2SessionId": "270312ae-8d74-4ded-ad89-5310da2d2e6f",
|
||||
"challenge": "tKCqUM6URnykri1ZFz-3ww",
|
||||
"timeout": 300000,
|
||||
"rpId": "agov-d.azure.adnovum.net",
|
||||
"allowCredentials": [
|
||||
{
|
||||
"type": "public-key",
|
||||
"id": "WVzzUwxOf-1doTGkrdRHWPDbETTawkULLPsEiwiQwA2AFC4_YgL5OVmJJOT2OulAZSq_tvOfNlMSRKRXyXH2kw",
|
||||
"transports": []
|
||||
}
|
||||
],
|
||||
"userVerification": "preferred"}'''
|
||||
LOG.info("<== Response: ${responseCode}")
|
||||
response.setContent(responseText) // return response from nevisFIDO "as-is"
|
||||
response.setContentType('application/json')
|
||||
response.setHttpStatusCode(200)
|
||||
response.setIsDirectResponse(true)
|
||||
return
|
||||
}
|
||||
|
||||
def responseText = connection.inputStream.text
|
||||
LOG.info("<== Response: ${responseCode} : ${responseText}")
|
||||
response.setContent(responseText) // return response from nevisFIDO "as-is"
|
||||
response.setContentType('application/json')
|
||||
response.setHttpStatusCode(200)
|
||||
response.setIsDirectResponse(true)
|
||||
return
|
||||
}
|
||||
|
||||
if (path == '/nevisfido/fido2/assertion/result') {
|
||||
|
||||
if (inargs.containsKey('authRequestId') && (inargs['authRequestId'] != session['ch.nevis.auth.saml.request.id'])) {
|
||||
// wrong request, "force" a timeout
|
||||
LOG.info('authentication timeout enforced, due to concurrent requests')
|
||||
|
||||
response.setIsDirectResponse(true)
|
||||
response.setContentType('text/html; charset=UTF-8')
|
||||
response.setContent('Timeout')
|
||||
response.setHttpStatusCode(205)
|
||||
response.setHeader('IDP-AUTH', 'Timeout')
|
||||
|
||||
// CONTINUE to keep the other request beeing processed
|
||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||
return
|
||||
}
|
||||
|
||||
def userHandleValue = userExtId.getBytes().encodeBase64Url().toString()
|
||||
LOG.info("encoded userHandle: ${userHandleValue}")
|
||||
json {
|
||||
"id" inargs['id']
|
||||
"type" inargs['type']
|
||||
response {
|
||||
"clientDataJSON" inargs['response.clientDataJSON']
|
||||
"authenticatorData" inargs['response.authenticatorData']
|
||||
"signature" inargs['response.signature']
|
||||
"userHandle" userHandleValue
|
||||
}
|
||||
}
|
||||
post(connection, json)
|
||||
def responseCode = connection.responseCode
|
||||
// test if credentials exist
|
||||
if (responseCode != 400) {
|
||||
def responseText = connection.inputStream.text
|
||||
LOG.info("<== Response: ${responseCode} : ${responseText}")
|
||||
if (responseCode == 200 && new JsonSlurper().parseText(responseText).status == 'ok') {
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
||||
}
|
||||
//response.setHttpStatusCode(400)
|
||||
//response.setIsDirectResponse(true)
|
||||
// DEFINE how to handel error
|
||||
notes.setProperty('lasterror', '1')
|
||||
notes.setProperty('lasterrorinfo', 'FIDO2 authentication failed')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
|
||||
response.setError(1, "FIDO2 authentication failed")
|
||||
showGui()
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlDCCAnwCCQC4xKJxfbSLBzANBgkqhkiG9w0BAQsFADCBizELMAkGA1UEBhMC
|
||||
Y2gxEDAOBgNVBAoMB2Fkbm92dW0xDTALBgNVBAsMBGFnb3YxJzAlBgNVBAMMHmph
|
||||
a29iLmFnb3YtZC5henVyZS5hZG5vdnVtLm5ldDEyMDAGCSqGSIb3DQEJARYjaW5m
|
||||
b0BqYWtvYi5hZ292LWQuYXp1cmUuYWRub3Z1bS5uZXQwHhcNMjMwMzIxMTUyMjI0
|
||||
WhcNMjgwMzE5MTUyMjI0WjCBizELMAkGA1UEBhMCY2gxEDAOBgNVBAoMB2Fkbm92
|
||||
dW0xDTALBgNVBAsMBGFnb3YxJzAlBgNVBAMMHmpha29iLmFnb3YtZC5henVyZS5h
|
||||
ZG5vdnVtLm5ldDEyMDAGCSqGSIb3DQEJARYjaW5mb0BqYWtvYi5hZ292LWQuYXp1
|
||||
cmUuYWRub3Z1bS5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB
|
||||
8LPO6Nack2z1whTratLxRD9KraO8QGrVTEa0p+23EKJH4WYE0QGzXbX4oFl2maXE
|
||||
QDeCBofTnrl8sL3yVxBy56qO8T3VxYtt9akjty6PujO48bgJr2VGjGgtPYPUeOEk
|
||||
lzCS616732Bnxc2iqo267G/tGooRIOOqefSyhEGmbI3KGv/zYZn/qxQo/A+5f+6y
|
||||
zEoKdmnBF6vnowvffKfdFKI8udd5eKmfyrc5iNYHXoVP/HmqKbkyrBw1U0ysihRJ
|
||||
3vyJVDtirQ5chLn0jOZ4UZ5SBck9+784yrVqpNbsWAe3NU+Vfx4wCk/rPWRDa1E2
|
||||
fk+gEVvEMUFt4UvU25BdAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIxToc9muu9z
|
||||
d4yycZCgZbDtzrq2Un+m/m3TrBNzLV7UBbGt7HW9/kxvdFJTKgNEj+ZD1cY6O6D7
|
||||
vrWV0Xb1XPgkaAfypc4Y7IOUTFDR/ib4siP9gPkHvr5WSIip3mFgX9yIV910N/hh
|
||||
ImE9/Jtf/q9MopBu8J6zRmL/J8mVewVdcU3xqz27OVMMSht0Du8FcpIrNQwqc1LN
|
||||
tCgdj+pw5vl7NH546WlyYNpLEkAeBpJ3XCBDDwcQftC+/cQ7GKJGtOJ4ODdxWMyX
|
||||
ThsHJ7MCTiN+MoW5CeioGSmngezh4Gs5SpGAGVvwNvXW2jiGdRDdHmyxDPjCQvYA
|
||||
wwZrmgtpYE0=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,30 @@
|
|||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIrK+HT9oczr4CAggA
|
||||
MB0GCWCGSAFlAwQBKgQQTod/pSdiG2RFbQP2Nd05NASCBNB7YpOzsJLORdEhmk3M
|
||||
0Wk3qJDZU6RNXh80oEQP/nlr4Suikuw3zOxKUYT1M78+2DaCCu3bLUVzziTsjhdO
|
||||
QBMYYAwvIMz/InjsEevdyggTQ1C/xbx45zHb7Ho8uYH/Epnk/9T9EQ5lPy0eOI6S
|
||||
rkX0SeMl018/+YyykoMTg3/eCbnzxmoMymolvPIqz1keeBAY/Dyu565S+BK4wnMq
|
||||
NnfAgU/4HzD+xTuGVCxbxmL4c+d4BZa4eG01Abn4rX0AjoPx35Yi+ES24zVbRSlR
|
||||
WaJT5zcI4m2P2Y9n9KKxUcznXGt7GKV+IQ3cApU5eufhMEai5VPODunXDRlZsXVF
|
||||
+reMLFXSYyIXSXQ6TjCCqyLtxQmwd0xmld0YDobuQEeU1Y30UNj2X+VOED91/6my
|
||||
xe0KlnLenkiuVjyHFbKsV3yEaAYSBwHej4LuJzb3cCLUjh7Jdxh/cRLnbNzsc1rw
|
||||
FofMEMdl5Lyqhje3AxHy95+a2cumk3BMmTdJ2l230nyFVDX/Fbf7hJgF9SPBCvF1
|
||||
nS6bm6QOdJHNhEC+Te+GMAFzXWbnvYr/zdYLv75Vcs9Lho/HfPWU1WgPDk1H4xTF
|
||||
U9cKCRBpf//SpVX8mizQJBrhIbp/Tew3LvC3h9DLqRXmVqQF4G7+7oz5bKWgtxMV
|
||||
r5m5/6XcFLCZp9zyDt7SMGasdELub82S1BE1YHHREYqybR2koBp2x1nF8N1P/u28
|
||||
QXtyVI+wiDXZ9l9zls7HPY6cAMS/nE+fpvjaLmcPxNj5uw6GfYCQblPxacBuMc8a
|
||||
I8LAVprYqWLi3WZ/5HQIsAINt7G60P++avvvMA/bIkON9T5eNJuEeqo9gt/iVR6E
|
||||
n9SyVovJUtLC6faBYNwjffNNU9pUPP+fft6Y9iE7OF4L6CQf3tp4KQR9681MBp0v
|
||||
nxZnzadarXohWObxXVgBm4jSNIwUexm1vouxFrA3Lh+ofGpIpSrm/A54aKyv/S77
|
||||
NXC6zHX2cDyY4ZIfDQ/mRiMvuR/4sNw5MiR04/DItNV2wORIFIbpTp3tEyfCINBr
|
||||
7b7nayBWgrJYxu5ixD05bDpjqwgV6ajIuTvLG8sMFoQnS9e0rVzD73jHy3vsmJ2P
|
||||
GS0Nj8u6f0njKc3Cr/i1/l/hXX/JtqhvZZTQiSVygqFhHi3Eo91hfGLR3fk+tUTs
|
||||
ktOc53BVgbN1LzKJKqgyBddp25PxTy2y9qJL2iTTScUv98kSM017XBSZ+BdOhl1A
|
||||
Qfgxv7u7zlj195X+vVaFVq0VMrfhhDDuDajKjA0zhO5sMPL/tH/2FPsPlnDuwS4b
|
||||
9vSJH2qybeTcRfZEdu7vIbQdmS8JNTKvDE27YA+zur3yE/dc5qg6NZH2B68CRXLc
|
||||
oKkyGrcKhpPry0mL9xUMM53iGcTV21KnsOdn/F/XKgZqhc8Y/80z2pifJ9w8DWLn
|
||||
XSfKBJKmj/59HD0S6bdRddgMxrhkOR4uhkf5fedgIF7lkL+ovA3xek1j1z1X4fPM
|
||||
ov46RsskJhclqbADip5qJ/IErNjlac/0dTELdxjLTnGJ3lM0/WbCbPpEP+/nLtCd
|
||||
dgvmAnhykEiW2WpaEzP/K6T+OSKsNFmhrw6kX+XHDqqgjglJ/0XI4gwC1BDvn927
|
||||
J8qw1PEvnK6TfEH3PrEDK8DwJQ==
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
|
@ -0,0 +1,243 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="false">
|
||||
<ResultCond name="default" next="${state.entry}_dispatch"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/initializeRecovery.groovy"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_dispatch" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="false" resumeState="true">
|
||||
<ResultCond name="default" next="${state.exit.5}"/>
|
||||
|
||||
<!-- TODO/haburger/2024-AUG-20: remove Google after successfull migration to Friendly Captcha -->
|
||||
<!-- <ResultCond name="cancel, hasCaptchaInfos, visible" next="${state.exit.2}"/> -->
|
||||
<!-- <ResultCond name="hasCode, hasCaptchaInfos, visible" next="${state.exit.2}"/> -->
|
||||
<!-- <ResultCond name="hasCaptchaInfos, invalidUrlTicket, visible" next="${state.entry}_enterEmail"/> -->
|
||||
<!-- <ResultCond name="hasCaptchaInfos, continue, visible" next="${state.entry}_enterEmail"/> -->
|
||||
<!-- <ResultCond name="invalidUrl, hasCaptchaInfos, visible" next="${state.entry}_ticketInvalid"/> -->
|
||||
|
||||
<ResultCond name="invalidUrlTicket" next="${state.exit.5}"/>
|
||||
<ResultCond name="hasCode" next="${state.exit.2}"/>
|
||||
<ResultCond name="cancel" next="${state.exit.2}"/>
|
||||
<ResultCond name="cancel, hasCaptchaInfos" next="${state.exit.2}"/>
|
||||
<ResultCond name="hasCode, hasCaptchaInfos" next="${state.exit.2}"/>
|
||||
<ResultCond name="hasCaptchaInfos" next="${state.entry}_loginFactorQuestion"/>
|
||||
<ResultCond name="hasCaptchaInfos, invalidUrlTicket" next="${state.entry}_enterEmail"/>
|
||||
<ResultCond name="hasSessionCode, hasCaptchaInfos" next="${state.entry}_verifyUrlTicketIntro"/>
|
||||
<ResultCond name="hasCaptchaInfos, continue" next="${state.exit.6}"/>
|
||||
<ResultCond name="hasSessionCode" next="${state.entry}_verifyUrlTicketIntro"/>
|
||||
<ResultCond name="hasCode, hasSessionCode" next="${state.exit.2}"/>
|
||||
<ResultCond name="invalidUrl" next="${state.entry}_ticketInvalid"/>
|
||||
<ResultCond name="invalidUrl, hasCaptchaInfos" next="${state.entry}_ticketInvalid"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="NoGui">
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:cancel" value="#{inargs.containsKey('cancel')}"/>
|
||||
<property name="condition:hasCode" value="#{inargs.containsKey('cd')}"/>
|
||||
<property name="condition:hasSessionCode" value="#{sess.get('agov.recovery.code')}"/>
|
||||
<property name="condition:invalidUrl" value="#{!inctx.getProperty('connection.actualURL').matches('((https://.*/AUTH/RECOVERY/\\?$)|(https://.*/AUTH/RECOVERY/$)|(https://.*/AUTH/RECOVERY/\\?language=(de|fr|it|en))|(https://.*/AUTH/RECOVERY/\\?cd=.*))')}"/>
|
||||
<property name="condition:invalidUrlTicket" value="${notes:invalidUrlTicket}"/>
|
||||
<property name="condition:hasCaptchaInfos" value="#{sess.get('agov.recovery.captchaSettings.puzzleUrl')}"/>
|
||||
|
||||
<!-- TODO/haburger/2024-AUG-20: remove Google after successfull migration to Friendly Captcha -->
|
||||
<!-- <property name="condition:hasCaptchaInfos" value="#{sess.get('agov.recovery.json.accountUrl')}"/> -->
|
||||
<!-- <property name="condition:continue" value="#{inargs.containsKey('continue')}"/> -->
|
||||
<!-- <property name="condition:visible" value="#{sess.get('agov.recovery.X-ReCAPTCHA-Integration') eq 'VISIBLE'}"/> -->
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_loginFactorQuestion" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="true" resumeState="true">
|
||||
<ResultCond name="cancel" next="${state.exit.2}"/>
|
||||
<ResultCond name="loginFactorYes" next="${state.entry}_reasonSelection"/>
|
||||
<ResultCond name="loginFactorNo" next="${state.entry}_reasonSelection"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_questionnaire_loginfactor">
|
||||
<GuiElem name="intro" type="info" label="recovery.intro.message"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="cancel" type="submit" label="cancel.button.label" value="cancel"/>
|
||||
<GuiElem name="submit" type="submit" label="submit.button.label" value="submit"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:cancel" value="${inargs:cancel}==cancel"/>
|
||||
<property name="condition:loginFactorYes" value="${inargs:continue}==yes"/>
|
||||
<property name="condition:loginFactorNo" value="${inargs:continue}==no"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_reasonSelection" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="true" resumeState="true">
|
||||
<ResultCond name="cancel" next="${state.entry}_loginFactorQuestion"/>
|
||||
<ResultCond name="validReasons" next="${state.entry}_instructions"/>
|
||||
<ResultCond name="invalidReasons" next="${state.entry}_noRecovery"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_questionnaire_reason_selection">
|
||||
<GuiElem name="intro" type="info" label="recovery.intro.message"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="question" type="hidden" value="${inargs:continue}" optional="true"/>
|
||||
<GuiElem name="cancel" type="submit" label="cancel.button.label" value="cancel"/>
|
||||
<GuiElem name="submit" type="submit" label="submit.button.label" value="submit"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:cancel" value="${inargs:cancel}==cancel"/>
|
||||
<property name="condition:validReasons" value="${inargs:continue}==yes"/>
|
||||
<property name="condition:invalidReasons" value="${inargs:continue}==no"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_instructions" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="true" resumeState="true">
|
||||
<ResultCond name="cancel" next="${state.entry}_loginFactorQuestion"/>
|
||||
<ResultCond name="continue" next="${state.entry}_enterEmail"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_questionnaire_instructions">
|
||||
<GuiElem name="intro" type="info" label="recovery.intro.message"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="cancel" type="submit" label="cancel.button.label" value="cancel"/>
|
||||
<GuiElem name="submit" type="submit" label="submit.button.label" value="submit"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:cancel" value="${inargs:cancel}==cancel"/>
|
||||
<property name="condition:continue" value="${inargs:continue}==continue"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_noRecovery" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="true" resumeState="true">
|
||||
<ResultCond name="cancel" next="${state.exit.2}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_questionnaire_no_recovery">
|
||||
<GuiElem name="intro" type="info" label="recovery.intro.message"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="cancel" type="submit" label="cancel.button.label" value="cancel"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:cancel" value="${inargs:cancel}==cancel"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_enterEmail" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="true" resumeState="true">
|
||||
<ResultCond name="cancel" next="${state.exit.2}"/>
|
||||
<ResultCond name="verifyEmail" next="${state.entry}_saveEmail"/>
|
||||
<ResultCond name="stay" next="${state.entry}_enterEmail"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_intro_email">
|
||||
<GuiElem name="intro" type="info" label="recovery.intro.message"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
|
||||
<GuiElem name="email" type="text" label="prompt.email" value="#{(sess.containsKey('agov.recovery.email'))?sess.get('agov.recovery.email'):inargs.getProperty('email', '')}" optional="true"/>
|
||||
<GuiElem name="captchaSettings.enabled" type="hidden" value="${sess:agov.recovery.captchaSettings.enabled}" optional="true"/>
|
||||
<GuiElem name="friendlyCaptchaSettings.siteKey" type="hidden" value="${sess:agov.recovery.captchaSettings.siteKey}" optional="true"/>
|
||||
<GuiElem name="friendlyCaptchaSettings.puzzleUrl" type="hidden" value="${sess:agov.recovery.captchaSettings.puzzleUrl}" optional="true"/>
|
||||
|
||||
|
||||
<!-- TODO/haburger/2024-AUG-20: remove Google after successfull migration to Friendly Captcha -->
|
||||
<!-- <GuiElem name="captchaSettings.reCaptchaInvisibleSiteKey" type="hidden" value="${sess:agov.recovery.json.captchaSettings.reCaptchaInvisibleSiteKey}" optional="true"/> -->
|
||||
<!-- <GuiElem name="captchaSettings.reCaptchaVisibleSiteKey" type="hidden" value="${sess:agov.recovery.json.captchaSettings.reCaptchaVisibleSiteKey}" optional="true"/> -->
|
||||
<!-- <GuiElem name="X-ReCAPTCHA-Integration" type="hidden" value="${sess:agov.recovery.X-ReCAPTCHA-Integration}" optional="true"/> -->
|
||||
|
||||
<GuiElem name="cancel" type="submit" label="cancel.button.label" value="cancel"/>
|
||||
<GuiElem name="submit" type="submit" label="submit.button.label" value="submit"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/sanitizeAndDispatchRecoveryEmailInput.groovy"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_saveEmail" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false">
|
||||
<ResultCond name="default" next="${state.exit.6}"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="sess:agov.recovery.email" value="${inargs:email}"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_verifyUrlTicketIntro" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="true" resumeState="true">
|
||||
<ResultCond name="cancel" next="${state.failed}"/>
|
||||
<ResultCond name="confirm" next="${state.entry}_verifyUrlTicket"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_start_info">
|
||||
<GuiElem name="intro" type="info" label="recovery.intro.message"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="email" type="text" label="prompt.email" value="${sess:ch.nevis.session.loginid}" optional="true"/>
|
||||
<GuiElem name="cancel" type="submit" label="cancel.button.label" value="cancel"/>
|
||||
<GuiElem name="submit" type="submit" label="submit.button.label" value="submit"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:confirm" value="#{inargs.containsKey('confirm') && inargs.getProperty('confirm') eq 'confirm'}"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_verifyUrlTicket" final="false" class="ch.nevis.idm.authstate.IdmURLTicketVerifyState">
|
||||
<ResultCond name="ok" next="${state.entry}_IdmGetPropertiesStateTicket" authLevel="auth.weak"/>
|
||||
<ResultCond name="tmpLocked" next="${state.entry}_invalidateCode"/>
|
||||
<ResultCond name="lockWarn" next="${state.entry}_invalidateCode"/>
|
||||
<ResultCond name="nowLocked" next="${state.entry}_invalidateCode"/>
|
||||
<ResultCond name="locked" next="${state.entry}_invalidateCode"/>
|
||||
<ResultCond name="failed" next="${state.entry}_invalidateCode"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="NoGui"/>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.ticket" value="${session:agov.recovery.code}"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_invalidateCode" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false" resumeState="false">
|
||||
<ResultCond name="default" next="${state.entry}"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="sess:agov.recovery.code" value=""/>
|
||||
<property name="removeOnEmptyValue" value="true"/>
|
||||
<property name="notes:invalidUrlTicket" value="was invalid"/>
|
||||
</AuthState>
|
||||
<AuthState class="ch.nevis.esauth.auth.states.standard.AuthGeneric" final="true" name="${state.entry}_ticketInvalid">
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="nevis.transfer.type" value="redirect"/>
|
||||
<Arg name="nevis.transfer.destination" value="/AUTH/RECOVERY/"/>
|
||||
</Response>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_IdmGetPropertiesStateTicket" final="false" class="ch.nevis.idm.authstate.IdmGetPropertiesState" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.entry}_verifyUser"/>
|
||||
<ResultCond name="clientNotFound" next="${state.failed}"/>
|
||||
<ResultCond name="default" next="${state.failed}"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${request:traceId}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="forceDataReload" value="true"/>
|
||||
<!-- Returned Attributes in SecToken -->
|
||||
<property name="user.attributes" value="${param.attributes}"/>
|
||||
<property name="user.properties" value="${param.properties}"/>
|
||||
<property name="userExtId" value="${request:userid}"/>
|
||||
<property name="chooseDefaultProfile" value="true"/>
|
||||
<property name="client.name" value="${param.client.name}"/>
|
||||
<property name="detaillevel.profile" value="HIGH"/>
|
||||
<property name="detaillevel.role" value="HIGH"/>
|
||||
<property name="detaillevel.authorization" value="HIGH"/>
|
||||
<property name="detaillevel.dataroom" value="HIGH"/>
|
||||
<property name="detaillevel.credential" value="HIGH"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_verifyUser" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="true">
|
||||
<ResultCond name="ok" next="${state.exit.1}"/>
|
||||
<ResultCond name="needCode" next="${state.entry}_IdmUserIdPasswordLogin"/>
|
||||
<ResultCond name="error" next="${state.failed}"/>
|
||||
<ResultCond name="alreadyInRecovery" next="${state.exit.3}"/>
|
||||
<ResultCond name="notFullyRegistered" next="${state.exit.7}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="${state.entry}Dialog" label="op-onboarding.intro.title">
|
||||
<GuiElem name="info" type="info" label="op-onboarding.intro.message"/>
|
||||
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
|
||||
<GuiElem name="submit" type="button" label="continue.button.label" value="go"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="Recovery"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/recovery-preprocessing.groovy"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_IdmUserIdPasswordLogin" final="true" resumeState="true" class="ch.nevis.idm.authstate.IdmPasswordVerifyState">
|
||||
<ResultCond name="ok" next="${state.exit.1}" authLevel="auth.weak"/>
|
||||
<ResultCond name="pwChange" next="${state.entry}_IdmUserIdPasswordLogin" authLevel="auth.weak"/>
|
||||
<ResultCond name="lockWarn" next="${state.entry}_IdmUserIdPasswordLogin"/>
|
||||
<ResultCond name="nowLocked" next="${state.entry}_codeLocked"/>
|
||||
<ResultCond name="locked" next="${state.entry}_codeLocked"/>
|
||||
<ResultCond name="tmpLocked" next="${state.entry}_codeLocked"/>
|
||||
<ResultCond name="failed" next="${state.entry}_IdmUserIdPasswordLogin"/>
|
||||
<ResultCond name="clientNotFound" next="${state.entry}_IdmUserIdPasswordLogin"/>
|
||||
<ResultCond name="disabled" next="${state.entry}_codeLocked"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_check_code">
|
||||
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
|
||||
<GuiElem name="code" type="pw-text" label="not-used" value="hide-input-in-logs" optional="true"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.loginType" value="LOGINID"/>
|
||||
<property name="credential.type" value="contextPassword"/>
|
||||
<property name="credential.context" value="RECOVERY"/>
|
||||
<property name="user.password" value="#{inargs.getProperty('code').replace('-', '')}"/>
|
||||
<property name="user.loginid" value="${sess:ch.adnovum.nevisidm.user.loginId}"/>
|
||||
<property name="client.name" value="agov"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_codeLocked" class="ch.nevis.esauth.auth.states.standard.AuthGeneric" final="true" resumeState="false">
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="recovery_check_noCode">
|
||||
</Gui>
|
||||
</Response>
|
||||
</AuthState>
|
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
def mInargs = request.getInArgs()
|
||||
mInargs.remove('email')
|
||||
mInargs.remove('recaptcha_sitekey')
|
||||
mInargs.remove('recaptcha_response')
|
||||
mInargs.remove('continue')
|
||||
mInargs.remove('authRequestId')
|
||||
mInargs.remove('cancel')
|
||||
}
|
||||
|
||||
if (inargs['cd'] && session['agov.recovery.code']) {
|
||||
// we are called with a new URL --> make sure we start from scratch
|
||||
def s = request.getAuthSession(true)
|
||||
def sessionKeySet = new HashSet(session.keySet())
|
||||
sessionKeySet.each { key ->
|
||||
if ( key ==~ /ch.nevis.idm.*/ || key ==~ /ch.adnovum.nevisidm.*/ || key ==~ /agov.recovery.*/ ) {
|
||||
s.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!session['ch.nevis.auth.saml.request.id']) {
|
||||
response.setSessionAttribute('ch.nevis.auth.saml.request.id', java.util.UUID.randomUUID().toString())
|
||||
}
|
||||
|
||||
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'
|
||||
|
||||
response.setSessionAttribute('agov.recovery.ip', '' + sourceIp)
|
||||
response.setSessionAttribute('agov.recovery.userAgent', '' + userAgent)
|
||||
|
||||
response.setResult('default')
|
|
@ -0,0 +1,192 @@
|
|||
import org.codehaus.groovy.runtime.StackTraceUtils
|
||||
import groovy.xml.XmlSlurper
|
||||
|
||||
|
||||
// AGOVaq conversion
|
||||
def maxLoiRoleToCtxClssConvertorMap = [
|
||||
"level100": "urn:qa.agov.ch:names:tc:ac:classes:100",
|
||||
"level200": "urn:qa.agov.ch:names:tc:ac:classes:200",
|
||||
"level300": "urn:qa.agov.ch:names:tc:ac:classes:300",
|
||||
"level400": "urn:qa.agov.ch:names:tc:ac:classes:400",
|
||||
"level500": "urn:qa.agov.ch:names:tc:ac:classes:500"
|
||||
]
|
||||
|
||||
def getUserIdVerificationForRecovery(currentLoaRole) {
|
||||
// application is AGOV-AccountStatus
|
||||
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()
|
||||
|
||||
if (!result) {
|
||||
// fallback if not explicitly set
|
||||
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'
|
||||
switch (currentLoaRole) {
|
||||
case 'level100':
|
||||
result = chDomicile ? 'SimpleLetter' : 'Video'
|
||||
break
|
||||
case 'level200':
|
||||
result = chDomicile ? 'Bmid' : 'Video'
|
||||
break
|
||||
case 'level300':
|
||||
case 'level400':
|
||||
result = chDomicile ? lastIdVerification : 'Video'
|
||||
break
|
||||
default:
|
||||
LOG.warn("unexpected loa on account: ${currentLoaRole}")
|
||||
// safest default, should work in any case
|
||||
result = 'Video'
|
||||
}
|
||||
LOG.warn("Recovery method not set, choosing ${result} (based on currentLoad: ${currentLoaRole}, CH-domicile: ${chDomicile}, last verification method: ${lastIdVerification})")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def getAqLevelBasedOnIdVerificationForRecovery(idVerification, highestRoleLevel) {
|
||||
def result = 'level'
|
||||
|
||||
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((highestRoleLevel == 'level400') ? '400' : '300')
|
||||
break
|
||||
default:
|
||||
LOG.warn("unexpected idVerification for recovery on account: ${idVerification}")
|
||||
// safest default, should work in any case
|
||||
result = highestRoleLevel
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
def getUserMustRecoverValidFrom() {
|
||||
// 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'}
|
||||
return (authzNode) ? ((authzNode.validFrom && !authzNode.validFrom.text().isEmpty()) ? authzNode.validFrom?.text() : authzNode.ctlCreDat?.text()) : ''
|
||||
}
|
||||
|
||||
|
||||
// for autditing
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: '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 maxLoi = null
|
||||
|
||||
|
||||
// new
|
||||
if (session['ch.adnovum.nevisidm.userDto'] != null && notes['lasterror'] == null) {
|
||||
try {
|
||||
def userDto = new XmlSlurper().parseText(session['ch.adnovum.nevisidm.userDto'])
|
||||
def userState = userDto.state
|
||||
LOG.debug("Recovery: Dto is '${userDto}")
|
||||
LOG.debug("Recovery: state is '${userState}")
|
||||
def session = request.getAuthSession(true)
|
||||
|
||||
if (userState == 'ACTIVE') {
|
||||
|
||||
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:recovery')
|
||||
|
||||
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()
|
||||
|
||||
def idVerification = null
|
||||
def agovAqValidFrom = null
|
||||
if (maxLoi) {
|
||||
idVerification = userDto.'**'.find { node -> node.name() == 'properties' && node.name.text() == 'idVerification' && node.scopeName.text() == 'AGOV-Loi,' + maxLoi}?.value?.text()
|
||||
idVerification = idVerification ?: 'None'
|
||||
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()
|
||||
}
|
||||
|
||||
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' }
|
||||
|
||||
|
||||
if (mustRecover) {
|
||||
// attributes are defined over the mustRecover authorization
|
||||
session.setAttribute('agov.recovery.authnContextClassRef', 'urn:qa.agov.ch:names:tc:ac:classes:mustRecover')
|
||||
|
||||
idVerification = getUserIdVerificationForRecovery(maxLoi ?: 'level100') ?: idVerification
|
||||
|
||||
agovAqValidFrom = getUserMustRecoverValidFrom()
|
||||
|
||||
maxLoi = getAqLevelBasedOnIdVerificationForRecovery(idVerification, maxLoi)
|
||||
}
|
||||
|
||||
LOG.debug("Recovery: MaxLoi is '${maxLoi}'")
|
||||
LOG.debug("Recovery: IdVerification is ${idVerification}")
|
||||
LOG.debug("Recovery: agovAqValidFrom is ${agovAqValidFrom}")
|
||||
LOG.debug("Recovery: mustRecover is '${mustRecover}'")
|
||||
LOG.debug("Recovery: hasRecoveryRole is '${hasRecoveryRole}'")
|
||||
|
||||
if (maxLoi != null) {
|
||||
if (maxLoiRoleToCtxClssConvertorMap.containsKey(maxLoi)) {
|
||||
LOG.debug("Recovery: MaxLoiMapping is " + maxLoiRoleToCtxClssConvertorMap[maxLoi])
|
||||
response.setSessionAttribute('agov.recovery.currentAgovAq', '' + maxLoiRoleToCtxClssConvertorMap[maxLoi])
|
||||
response.setSessionAttribute('agov.recovery.currentIdVerification', '' + idVerification)
|
||||
response.setSessionAttribute('agov.recovery.currentAgovAqRoleValidFrom', '' + agovAqValidFrom)
|
||||
|
||||
if ((maxLoi == 'level100') && (mustRecover == null)) {
|
||||
// mustRecover role not set, so code needs to be checked
|
||||
LOG.debug("Recovery: emailAndCode")
|
||||
response.setSessionAttribute('agov.recovery.authenticatedWith', 'urn:qa.agov.ch:names:tc:authfactor:emailAndCode')
|
||||
response.setResult('needCode')
|
||||
return
|
||||
} else {
|
||||
LOG.debug("Recovery: email")
|
||||
response.setSessionAttribute('agov.recovery.authenticatedWith', 'urn:qa.agov.ch:names:tc:authfactor:email')
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
LOG.error("Recovery: Failed to convert '${maxLoi}' to AGOVaq")
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// maxLoi is null
|
||||
LOG.debug("Recovery: no 'AGOV-Loi'-role assigned to user ${user}")
|
||||
if ((hasRecoveryRole != null) && (mustRecover == null)) {
|
||||
response.setResult('notFullyRegistered')
|
||||
return
|
||||
} else {
|
||||
LOG.error("Recovery: no 'AGOV-Loi'-role assigned to user ${user} and no recovery role ")
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// state != ACTIVE and no lasterror should not happen
|
||||
LOG.error("Recovery: state='${userState}' but not lasterror set")
|
||||
response.setNote('lasterror', '9909')
|
||||
response.setNote('lasterrorinfo', 'internal error')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e = StackTraceUtils.sanitize(e)
|
||||
def affectedLines = e.stackTrace.findAll { it.className.startsWith('Script') }.collect { "${it.methodName}:${it.lineNumber}" }
|
||||
LOG.error("FATAL: Recovery processing failed (at lines: ${affectedLines})", e)
|
||||
response.setNote('lasterror', '9909')
|
||||
response.setNote('lasterrorinfo', 'internal error')
|
||||
response.setResult('error')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
LOG.error("Recovery: userDto missing or failure before (lasterror='${notes.getProperty('lasterror', '-')}')")
|
||||
response.setNote('lasterror', '9909')
|
||||
response.setNote('lasterrorinfo', 'internal error')
|
||||
response.setResult('error')
|
||||
return
|
|
@ -0,0 +1,25 @@
|
|||
def EMAIL_REGEXP = '^(([^<>()\\[\\]\\\\\\.,;:\\s@"]+(\\.[^<>()\\[\\]\\\\\\.,;:\\s@"]+)*)|(\\.\\+))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$'
|
||||
|
||||
|
||||
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'
|
||||
|
||||
if (inargs['cancel'] && inargs['cancel'] == 'cancel') {
|
||||
response.setResult('cancel')
|
||||
return
|
||||
}
|
||||
|
||||
if ( inargs['continue'] && inargs['continue'] == 'continue' ) {
|
||||
if (inargs['email'] && inargs['email'].matches(EMAIL_REGEXP)) {
|
||||
response.setResult('verifyEmail')
|
||||
return
|
||||
} else {
|
||||
LOG.warn("User attempted to bypass frontend emailvalidation with inavlid email: '${inargs['email']}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
request.getInArgs().setProperty('email', 'inavalid@email.org')
|
||||
response.setResult('stay')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
response.setResult('stay')
|
||||
return
|
|
@ -0,0 +1,20 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.xml.DocumentProcessor" final="false" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
|
||||
<!-- errors ignored, we transition anyway to done -->
|
||||
<ResultCond name="nomatch" next="${state.done}"/>
|
||||
<ResultCond name="validation-failed" next="${state.done}"/>
|
||||
<ResultCond name="default" next="${state.done}"/>
|
||||
|
||||
<Response value="AUTH_ERROR"/>
|
||||
|
||||
<property name="sess:agov.countryName" value="xpath:/country-names/country[@code='${sess:ch.nevis.idm.User.country}']/@${sess:ch.nevis.idm.User.language}"/>
|
||||
<property name="optional" value="sess:agov.countryName"/>
|
||||
|
||||
<property name="documentSourceType" value="RESOURCE"/>
|
||||
<property name="documentSource" value="file:/var/opt/nevisauth/default/conf/countries.xml"/>
|
||||
|
||||
<!-- scheduler interval: only load it once, then we use it -->
|
||||
<property name="scheduler.interval" value="-1"/>
|
||||
<property name="maxAge" value="-1"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,250 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<country-names>
|
||||
<country code="af" en="Afghanistan" de="Afghanistan" fr="Afghanistan" it="Afghanistan"/>
|
||||
<country code="al" en="Albania" de="Albanien" fr="Albanie" it="Albania"/>
|
||||
<country code="dz" en="Algeria" de="Algerien" fr="Algérie" it="Algeria"/>
|
||||
<country code="as" en="American Samoa" de="Amerikanisch-Samoa" fr="Samoa américaines" it="Samoa Americane"/>
|
||||
<country code="ad" en="Andorra" de="Andorra" fr="Andorre" it="Andorra"/>
|
||||
<country code="ao" en="Angola" de="Angola" fr="Angola" it="Angola"/>
|
||||
<country code="ai" en="Anguilla" de="Anguilla" fr="Anguilla" it="Anguilla"/>
|
||||
<country code="aq" en="Antarctica" de="Antarktis" fr="Antarctique" it="Antartide"/>
|
||||
<country code="ag" en="Antigua and Barbuda" de="Antigua und Barbuda" fr="Antigua-et-Barbuda" it="Antigua e Barbuda"/>
|
||||
<country code="ar" en="Argentina" de="Argentinien" fr="Argentine" it="Argentina"/>
|
||||
<country code="am" en="Armenia" de="Armenien" fr="Arménie" it="Armenia"/>
|
||||
<country code="aw" en="Aruba" de="Aruba" fr="Aruba" it="Aruba"/>
|
||||
<country code="au" en="Australia" de="Australien" fr="Australie" it="Australia"/>
|
||||
<country code="at" en="Austria" de="Österreich" fr="Autriche" it="Austria"/>
|
||||
<country code="az" en="Azerbaijan" de="Aserbaidschan" fr="Azerbaïdjan" it="Azerbaigian"/>
|
||||
<country code="bs" en="Bahamas" de="Bahamas" fr="Bahamas" it="Bahamas"/>
|
||||
<country code="bh" en="Bahrain" de="Bahrain" fr="Bahreïn" it="Bahrein"/>
|
||||
<country code="bd" en="Bangladesh" de="Bangladesch" fr="Bangladesh" it="Bangladesh"/>
|
||||
<country code="bb" en="Barbados" de="Barbados" fr="Barbade" it="Barbados"/>
|
||||
<country code="by" en="Belarus" de="Belarus" fr="Bélarus" it="Bielorussia"/>
|
||||
<country code="be" en="Belgium" de="Belgien" fr="Belgique" it="Belgio"/>
|
||||
<country code="bz" en="Belize" de="Belize" fr="Belize" it="Belize"/>
|
||||
<country code="bj" en="Benin" de="Benin" fr="Bénin" it="Benin"/>
|
||||
<country code="bm" en="Bermuda" de="Bermudas" fr="Bermudes" it="Bermuda"/>
|
||||
<country code="bt" en="Bhutan" de="Bhutan" fr="Bhoutan" it="Bhutan"/>
|
||||
<country code="bo" en="Bolivia" de="Bolivien" fr="Bolivie" it="Bolivia"/>
|
||||
<country code="ba" en="Bosnia-Herzegovina" de="Bosnien-Herzegowina" fr="Bosnie et Herzégovine" it="Bosnia ed Erzegovina"/>
|
||||
<country code="bw" en="Botswana" de="Botsuana" fr="Botswana" it="Botswana"/>
|
||||
<country code="bv" en="Bouvet Island" de="Bouvetinsel" fr="Île Bouvet" it="Isola Bouvet"/>
|
||||
<country code="br" en="Brazil" de="Brasilien" fr="Brésil" it="Brasile"/>
|
||||
<country code="io" en="British Indian Ocean Territory" de="Britisches Territorium im Indischen Ozean" fr="Territoire britannique de l’océan Indien" it="Territorio Britannico dell’Oceano Indiano"/>
|
||||
<country code="bn" en="Brunei" de="Brunei" fr="Brunei" it="Brunei"/>
|
||||
<country code="bg" en="Bulgaria" de="Bulgarien" fr="Bulgarie" it="Bulgaria"/>
|
||||
<country code="bf" en="Burkina Faso" de="Burkina Faso" fr="Burkina Faso " it="Burkina Faso"/>
|
||||
<country code="bi" en="Burundi" de="Burundi" fr="Burundi" it="Burundi"/>
|
||||
<country code="kh" en="Cambodia" de="Kambodscha" fr="Cambodge" it="Cambogia"/>
|
||||
<country code="cm" en="Cameroon" de="Kamerun" fr="Cameroun" it="Camerun"/>
|
||||
<country code="ca" en="Canada" de="Kanada" fr="Canada" it="Canada"/>
|
||||
<country code="cv" en="Cape Verde" de="Cabo Verde" fr="Cabo Verde" it="Capo Verde"/>
|
||||
<country code="ky" en="Cayman Islands" de="Kaiman-Inseln" fr="Îles Caïmans" it="Isole Cayman"/>
|
||||
<country code="cf" en="Central African Republic" de="Zentralafrikanische Republik" fr="République centrafricaine" it="Repubblica Centrafricana"/>
|
||||
<country code="td" en="Chad" de="Tschad" fr="Tchad" it="Ciad"/>
|
||||
<country code="cl" en="Chile" de="Chile" fr="Chili" it="Cile"/>
|
||||
<country code="cn" en="China (People's Republic OF)" de="China (Volksrepublik)" fr="Chine (République populaire de Chine)" it="Cina, Repubblica popolare cinese"/>
|
||||
<country code="cx" en="Christmas Island (Indian Ocean)" de="Weihnachtsinsel (Indischer Ozean)" fr="Île Christmas (océan Indien)" it="Isola di Natale"/>
|
||||
<country code="cc" en="Cocos (Keeling) Island" de="Kokosinseln (Keeling)" fr="Îles Cocos" it="Isole Cocos (Keeling)"/>
|
||||
<country code="co" en="Colombia" de="Kolumbien" fr="Colombie" it="Colombia"/>
|
||||
<country code="km" en="Comoros" de="Komoren" fr="Comores" it="Comore"/>
|
||||
<country code="cg" en="Congo (Republic)" de="Kongo (Republik)" fr="République du Congo" it="Repubblica del Congo"/>
|
||||
<country code="cd" en="Congo, Democratic Republic" de="Kongo, Demokratische Republik" fr="République démocratique du Congo" it="Repubblica democratica del Congo"/>
|
||||
<country code="ck" en="Cook Islands" de="Cookinseln" fr="Îles Cook" it="Isole Cook"/>
|
||||
<country code="cr" en="Costa Rica" de="Costa Rica" fr="Costa Rica" it="Costa Rica"/>
|
||||
<country code="hr" en="Croatia" de="Kroatien" fr="Croatie" it="Croazia"/>
|
||||
<country code="cu" en="Cuba" de="Kuba" fr="Cuba" it="Cuba"/>
|
||||
<country code="cw" en="Curaçao" de="Curaçao" fr="Curaçao" it="Curaçao"/>
|
||||
<country code="cy" en="Cyprus" de="Zypern" fr="Chypre" it="Cipro"/>
|
||||
<country code="cz" en="Czech Republic" de="Tschechische Republik" fr="Tchéquie" it="Repubblica Ceca"/>
|
||||
<country code="dk" en="Denmark" de="Dänemark" fr="Danemark" it="Danimarca"/>
|
||||
<country code="dj" en="Djibouti" de="Dschibuti" fr="Djibouti" it="Gibuti"/>
|
||||
<country code="dm" en="Dominica" de="Dominica" fr="Dominique" it="Dominica"/>
|
||||
<country code="do" en="Dominican Republic" de="Dominikanische Republik" fr="République dominicaine" it="Repubblica Dominicana"/>
|
||||
<country code="ec" en="Ecuador" de="Ecuador" fr="Équateur" it="Ecuador"/>
|
||||
<country code="eg" en="Egypt" de="Ägypten" fr="Égypte" it="Egitto"/>
|
||||
<country code="sv" en="El Salvador" de="El Salvador" fr="El Salvador" it="El Salvador"/>
|
||||
<country code="gq" en="Equatorial Guinea" de="Äquatorialguinea" fr="Guinée équatoriale" it="Guinea equatoriale"/>
|
||||
<country code="er" en="Eritrea" de="Eritrea" fr="Érythrée" it="Eritrea"/>
|
||||
<country code="ee" en="Estonia" de="Estland" fr="Estonie" it="Estonia"/>
|
||||
<country code="et" en="Ethiopia" de="Äthiopien" fr="Éthiopie" it="Etiopia"/>
|
||||
<country code="fk" en="Falkland Islands" de="Falklandinseln" fr="Îles Falkland" it="Isole Falkland"/>
|
||||
<country code="fo" en="Faroe Islands" de="Färöerinseln" fr="Îles Féroé" it="Isole Faroe"/>
|
||||
<country code="fj" en="Fiji" de="Fidschi" fr="Fidji" it="Figi"/>
|
||||
<country code="fi" en="Finland" de="Finnland" fr="Finlande" it="Finlandia"/>
|
||||
<country code="fr" en="France" de="Frankreich" fr="France" it="Francia"/>
|
||||
<country code="gf" en="French Guiana" de="Französisch-Guayana" fr="Guyane française" it="Guyana francese"/>
|
||||
<country code="pf" en="French Polynesia" de="Französisch-Polynesien" fr="Polynésie française" it="Polinesia francese"/>
|
||||
<country code="ga" en="Gabon" de="Gabun" fr="Gabon" it="Gabon"/>
|
||||
<country code="gm" en="Gambia" de="Gambia" fr="Gambie" it="Gambia"/>
|
||||
<country code="ge" en="Georgia" de="Georgien" fr="Géorgie" it="Georgia"/>
|
||||
<country code="de" en="Germany" de="Deutschland" fr="Allemagne" it="Germania"/>
|
||||
<country code="gh" en="Ghana" de="Ghana" fr="Ghana" it="Ghana"/>
|
||||
<country code="gi" en="Gibraltar" de="Gibraltar" fr="Gibraltar" it="Gibilterra"/>
|
||||
<country code="gb" en="Great Britain and Northern Ireland" de="Grossbritannien und Nordirland" fr="Royaume-Uni" it="Regno Unito"/>
|
||||
<country code="gr" en="Greece" de="Griechenland" fr="Grèce" it="Grecia"/>
|
||||
<country code="gl" en="Greenland" de="Grönland" fr="Groenland" it="Groenlandia"/>
|
||||
<country code="gd" en="Grenada" de="Grenada" fr="Grenade" it="Grenada"/>
|
||||
<country code="gp" en="Guadeloupe" de="Guadeloupe" fr="Guadeloupe" it="Guadalupa"/>
|
||||
<country code="gu" en="Guam" de="Guam" fr="Guam" it="Guam"/>
|
||||
<country code="gt" en="Guatemala" de="Guatemala" fr="Guatemala" it="Guatemala"/>
|
||||
<country code="gg" en="Guernsey" de="Guernsey" fr="Guernesey" it="Guernsey"/>
|
||||
<country code="gn" en="Guinea (Republic)" de="Guinea (Republik)" fr="République de Guinée" it="Guinea"/>
|
||||
<country code="gw" en="Guinea-Bissau" de="Guinea-Bissau" fr="Guinée-Bissau" it="Guinea-Bissau"/>
|
||||
<country code="gy" en="Guyana" de="Guyana" fr="Guyana" it="Guyana"/>
|
||||
<country code="ht" en="Haiti" de="Haiti" fr="Haïti" it="Haiti"/>
|
||||
<country code="hm" en="Heard AND McDonald Islands" de="Heard- und McDonald-Inseln" fr="Îles Heard et McDonald" it="Isola Heard e Isole McDonald"/>
|
||||
<country code="hn" en="Honduras" de="Honduras" fr="Honduras" it="Honduras"/>
|
||||
<country code="hk" en="Hong Kong" de="Hongkong" fr="Hong Kong" it="Hong Kong"/>
|
||||
<country code="hu" en="Hungary" de="Ungarn" fr="Hongrie" it="Ungheria"/>
|
||||
<country code="is" en="Iceland" de="Island" fr="Islande" it="Islanda"/>
|
||||
<country code="in" en="India" de="Indien" fr="Inde" it="India"/>
|
||||
<country code="id" en="Indonesia" de="Indonesien" fr="Indonésie" it="Indonesia"/>
|
||||
<country code="ir" en="Iran" de="Iran" fr="Iran" it="Iran"/>
|
||||
<country code="iq" en="Iraq" de="Irak" fr="Irak" it="Iraq"/>
|
||||
<country code="ie" en="Ireland" de="Irland" fr="Irlande" it="Irlanda"/>
|
||||
<country code="im" en="Island OF Man" de="Isle of Man" fr="Île de Man" it="Isola di Man"/>
|
||||
<country code="il" en="Israel" de="Israel" fr="Israël" it="Israele"/>
|
||||
<country code="it" en="Italy" de="Italien" fr="Italie" it="Italia"/>
|
||||
<country code="ci" en="Ivory Coast" de="Côte d'Ivoire" fr="Côte d’Ivoire" it="Costa d’Avorio"/>
|
||||
<country code="jm" en="Jamaica" de="Jamaika" fr="Jamaïque" it="Giamaica"/>
|
||||
<country code="jp" en="Japan" de="Japan" fr="Japon" it="Giappone"/>
|
||||
<country code="je" en="Jersey" de="Jersey" fr="Jersey" it="Jersey"/>
|
||||
<country code="jo" en="Jordan" de="Jordanien" fr="Jordanie" it="Giordania"/>
|
||||
<country code="kz" en="Kazakhstan" de="Kasachstan" fr="Kazakhstan" it="Kazakstan"/>
|
||||
<country code="ke" en="Kenya" de="Kenia" fr="Kenya" it="Kenya"/>
|
||||
<country code="ki" en="Kiribati" de="Kiribati" fr="Kiribati" it="Kiribati"/>
|
||||
<country code="kp" en="Korea, Democratic People's Republic of (North Korea)" de="Korea, Demokratische Volksrepublik (Nordkorea)" fr="République populaire démocratique de Corée (Corée du Nord)" it="Repubblica popolare democratica di Corea (Corea del Nord)"/>
|
||||
<country code="kr" en="Korea, Republic of (South Korea)" de="Korea, Republik (Südkorea)" fr="République de Corée (Corée du Sud)" it="Repubblica di Corea (Corea del Sud)"/>
|
||||
<country code="xk" en="Kosovo / Unmik" de="Kosovo / UNMIK" fr="Kosovo" it="Kosovo / UNMIK"/>
|
||||
<country code="kw" en="Kuwait" de="Kuwait" fr="Koweït" it="Kuwait"/>
|
||||
<country code="kg" en="Kyrgyzstan" de="Kirgisistan" fr="Kirghizistan" it="Kirghizistan"/>
|
||||
<country code="la" en="Laos" de="Laos" fr="Laos" it="Laos"/>
|
||||
<country code="lv" en="Latvia" de="Lettland" fr="Lettonie" it="Lettonia"/>
|
||||
<country code="lb" en="Lebanon" de="Libanon" fr="Liban" it="Libano"/>
|
||||
<country code="ls" en="Lesotho" de="Lesotho" fr="Lesotho" it="Lesotho"/>
|
||||
<country code="lr" en="Liberia" de="Liberia" fr="Libéria" it="Liberia"/>
|
||||
<country code="ly" en="Libya" de="Libyen" fr="Libye" it="Libia"/>
|
||||
<country code="li" en="Liechtenstein" de="Liechtenstein" fr="Liechtenstein" it="Liechtenstein"/>
|
||||
<country code="lt" en="Lithuania" de="Litauen" fr="Lituanie" it="Lituania"/>
|
||||
<country code="lu" en="Luxembourg" de="Luxemburg" fr="Luxembourg" it="Lussemburgo"/>
|
||||
<country code="mo" en="Macao" de="Macao" fr="Macao" it="Macao"/>
|
||||
<country code="mk" en="Macedonia, the Former Yugoslav Republic of" de="Mazedonien, ehemalige jugoslawische Republik" fr="Macédoine du Nord" it="Macedonia del Nord"/>
|
||||
<country code="mg" en="Madagascar" de="Madagaskar" fr="Madagascar" it="Madagascar"/>
|
||||
<country code="mw" en="Malawi" de="Malawi" fr="Malawi" it="Malawi"/>
|
||||
<country code="my" en="Malaysia" de="Malaysia" fr="Malaisie" it="Malaysia"/>
|
||||
<country code="mv" en="Maldives" de="Malediven" fr="Maldives" it="Maldive"/>
|
||||
<country code="ml" en="Mali" de="Mali" fr="Mali" it="Mali"/>
|
||||
<country code="mt" en="Malta" de="Malta" fr="Malte" it="Malta"/>
|
||||
<country code="mp" en="Mariana Islands" de="Marianen" fr="Îles Mariannes" it="Isole Marianne"/>
|
||||
<country code="mh" en="Marshall Islands" de="Marshallinseln" fr="Îles Marshall" it="Isole Marshall"/>
|
||||
<country code="mq" en="Martinique" de="Martinique" fr="Martinique" it="Martinica"/>
|
||||
<country code="mr" en="Mauritania" de="Mauretanien" fr="Mauritanie" it="Mauritania"/>
|
||||
<country code="mu" en="Mauritius Island" de="Mauritius" fr="Île Maurice" it="Maurizio"/>
|
||||
<country code="yt" en="Mayotte" de="Mayotte" fr="Mayotte" it="Mayotte"/>
|
||||
<country code="mx" en="Mexico" de="Mexiko" fr="Mexique" it="Messico"/>
|
||||
<country code="fm" en="Micronesia (Federated States OF)" de="Mikronesien (Föderierte Staaten von)" fr="États fédérés de Micronésie" it="Stati Federati di Micronesia"/>
|
||||
<country code="md" en="Moldova" de="Moldau" fr="Moldavie" it="Moldova"/>
|
||||
<country code="mc" en="Monaco" de="Monaco" fr="Monaco" it="Monaco"/>
|
||||
<country code="mn" en="Mongolia" de="Mongolei" fr="Mongolie" it="Mongolia"/>
|
||||
<country code="me" en="Montenegro, Republic" de="Montenegro, Republik" fr="Monténégro" it="Montenegro"/>
|
||||
<country code="ms" en="Montserrat" de="Montserrat" fr="Montserrat" it="Montserrat"/>
|
||||
<country code="ma" en="Morocco" de="Marokko" fr="Maroc" it="Marocco"/>
|
||||
<country code="mz" en="Mozambique" de="Mosambik" fr="Mozambique" it="Mozambico"/>
|
||||
<country code="mm" en="Myanmar (Union of)" de="Myanmar (Union)" fr="Myanmar" it="Myanmar"/>
|
||||
<country code="na" en="Namibia" de="Namibia" fr="Namibie" it="Namibia"/>
|
||||
<country code="nr" en="Nauru" de="Nauru" fr="Nauru" it="Nauru"/>
|
||||
<country code="np" en="Nepal" de="Nepal" fr="Népal" it="Nepal"/>
|
||||
<country code="nl" en="Netherlands" de="Niederlande" fr="Pays-Bas" it="Paesi Bassi"/>
|
||||
<country code="nc" en="New Caledonia" de="Neukaledonien" fr="Nouvelle-Calédonie" it="Nuova Caledonia"/>
|
||||
<country code="nz" en="New Zealand" de="Neuseeland" fr="Nouvelle-Zélande" it="Nuova Zelanda"/>
|
||||
<country code="ni" en="Nicaragua" de="Nicaragua" fr="Nicaragua" it="Nicaragua"/>
|
||||
<country code="ne" en="Niger" de="Niger" fr="Niger" it="Niger"/>
|
||||
<country code="ng" en="Nigeria" de="Nigeria" fr="Nigéria" it="Nigeria"/>
|
||||
<country code="nu" en="Niua" de="Niue" fr="Nioué" it="Isole Niua"/>
|
||||
<country code="nf" en="Norfolk Island" de="Norfolkinsel" fr="Île Norfolk" it="Isola Norfolk"/>
|
||||
<country code="no" en="Norway" de="Norwegen" fr="Norvège" it="Norvegia"/>
|
||||
<country code="om" en="Oman" de="Oman" fr="Oman" it="Oman"/>
|
||||
<country code="pk" en="Pakistan" de="Pakistan" fr="Pakistan" it="Pakistan"/>
|
||||
<country code="pw" en="Palau" de="Palau" fr="Palaos" it="Palau"/>
|
||||
<country code="ps" en="Palestine" de="Palästina" fr="Palestine" it="Palestina"/>
|
||||
<country code="pa" en="Panama" de="Panama" fr="Panama" it="Panama"/>
|
||||
<country code="pg" en="Papua New Guinea" de="Papua-Neuguinea" fr="Papouasie-Nouvelle-Guinée" it="Papua Nuova Guinea"/>
|
||||
<country code="py" en="Paraguay" de="Paraguay" fr="Paraguay" it="Paraguay"/>
|
||||
<country code="pe" en="Peru" de="Peru" fr="Pérou" it="Perù"/>
|
||||
<country code="ph" en="Philippines" de="Philippinen" fr="Philippines" it="Filippine"/>
|
||||
<country code="pn" en="Pitcairn" de="Pitcairn" fr="Îles Pitcairn" it="Isole Pitcairn"/>
|
||||
<country code="pl" en="Poland" de="Polen" fr="Pologne" it="Polonia"/>
|
||||
<country code="pt" en="Portugal" de="Portugal" fr="Portugal" it="Portogallo"/>
|
||||
<country code="pr" en="Puerto Rico" de="Puerto Rico" fr="Porto Rico" it="Porto Rico"/>
|
||||
<country code="qa" en="Qatar" de="Katar" fr="Qatar" it="Qatar"/>
|
||||
<country code="re" en="Réunion" de="Réunion" fr="La Réunion" it="Isola della Riunione"/>
|
||||
<country code="ro" en="Romania" de="Rumänien" fr="Roumanie" it="Romania"/>
|
||||
<country code="ru" en="Russian Federation" de="Russische Föderation" fr="Russie" it="Russia"/>
|
||||
<country code="rw" en="Rwanda" de="Ruanda" fr="Rwanda" it="Ruanda"/>
|
||||
<country code="sb" en="Salomon Islands" de="Salomoninseln" fr="Îles Salomon" it="Isole Salomone"/>
|
||||
<country code="sm" en="San Marino" de="San Marino" fr="Saint-Marin" it="San Marino"/>
|
||||
<country code="sa" en="Saudi Arabia" de="Saudi-Arabien" fr="Arabie saoudite" it="Arabia Saudita"/>
|
||||
<country code="sn" en="Senegal" de="Senegal" fr="Sénégal" it="Senegal"/>
|
||||
<country code="rs" en="Serbia, Republic" de="Serbien, Republik" fr="Serbie" it="Serbia"/>
|
||||
<country code="sc" en="Seychelles" de="Seychellen" fr="Seychelles" it="Seychelles"/>
|
||||
<country code="sl" en="Sierra Leone" de="Sierra Leone" fr="Sierra Leone" it="Sierra Leone"/>
|
||||
<country code="sg" en="Singapore" de="Singapur" fr="Singapour" it="Singapore"/>
|
||||
<country code="sk" en="Slovak Republic" de="Slowakei" fr="Slovaquie" it="Slovacchia"/>
|
||||
<country code="si" en="Slovenia" de="Slowenien" fr="Slovénie" it="Slovenia"/>
|
||||
<country code="so" en="Somalia" de="Somalia" fr="Somalie" it="Somalia"/>
|
||||
<country code="za" en="South Africa" de="Südafrika" fr="Afrique du Sud" it="Sudafrica"/>
|
||||
<country code="gs" en="South Georgia AND the south Sandwich Islands" de="Südgeorgien und die Südlichen Sandwichinseln" fr="Îles Géorgie du Sud et Sandwich du Sud" it="Georgia del Sud e Sandwich Australi"/>
|
||||
<country code="ss" en="South Sudan" de="Südsudan" fr="Soudan du Sud" it="Sudan del Sud"/>
|
||||
<country code="es" en="Spain" de="Spanien" fr="Espagne" it="Spagna"/>
|
||||
<country code="lk" en="Sri Lanka" de="Sri Lanka" fr="Sri Lanka" it="Sri Lanka"/>
|
||||
<country code="bl" en="St. Barthélemy" de="St. Barthélemy" fr="Saint-Barthélemy" it="Saint Barthélemy"/>
|
||||
<country code="kn" en="St. Christopher (St. Kitts) and Nevis" de="St. Kitts und Nevis" fr="Saint-Christophe-et-Niévès" it="Saint Kitts e Nevis"/>
|
||||
<country code="sh" en="St. Helena, Ascension and Tristan da Cunha" de="St. Helena, Ascension und Tristan da Cunha" fr="Sainte-Hélène, Ascension et Tristan da Cunha" it="Sant’Elena, Ascensione e Tristan da Cunha"/>
|
||||
<country code="lc" en="St. Lucia" de="St. Lucia" fr="Sainte-Lucie" it="Santa Lucia"/>
|
||||
<country code="sx" en="St. Maarten" de="Sint Maarten" fr="Sint-Maarten" it="Sint Maarten"/>
|
||||
<country code="mf" en="St. Martin" de="St. Martin" fr="Saint-Martin" it="Saint Martin"/>
|
||||
<country code="pm" en="St. Pierre and Miquelon" de="St. Pierre und Miquelon" fr="Saint-Pierre-et-Miquelon" it="Saint-Pierre e Miquelon"/>
|
||||
<country code="st" en="St. Tome and Principe" de="São Tomé und Príncipe" fr="Sao Tomé-et-Principe" it="São Tomé e Príncipe"/>
|
||||
<country code="vc" en="St. Vincent and the Grenadines" de="St. Vincent und die Grenadinen" fr="Saint-Vincent-et-les-Grenadines" it="Saint Vincent e Grenadine"/>
|
||||
<country code="sd" en="Sudan" de="Sudan" fr="Soudan" it="Sudan"/>
|
||||
<country code="sr" en="Suriname" de="Suriname" fr="Suriname" it="Suriname"/>
|
||||
<country code="sj" en="Svalbard and Jan Mayen Island" de="Svalbard und Jan Mayen-Insel" fr="Svalbard et Jan Mayen" it="Svalbard e Jan Mayen"/>
|
||||
<country code="sz" en="Swaziland" de="Eswatini" fr="Swaziland" it="Eswatini"/>
|
||||
<country code="se" en="Sweden" de="Schweden" fr="Suède" it="Svezia"/>
|
||||
<country code="ch" en="Switzerland" de="Schweiz" fr="Suisse" it="Svizzera"/>
|
||||
<country code="sy" en="Syria" de="Syrien" fr="Syrie" it="Siria"/>
|
||||
<country code="tw" en="Taiwan (Chinese Taipei)" de="Taiwan (Chinesisches Taipei)" fr="Taïwan (Taipei chinois)" it="Taiwan (Taipei cinese)"/>
|
||||
<country code="tj" en="Tajikistan" de="Tadschikistan" fr="Tadjikistan" it="Tagikistan"/>
|
||||
<country code="tz" en="Tanzania" de="Tansania" fr="Tanzanie" it="Tanzania"/>
|
||||
<country code="th" en="Thailand" de="Thailand" fr="Thaïlande" it="Thailandia"/>
|
||||
<country code="tl" en="Timor-Leste" de="Timor-Leste" fr="Timor-Leste" it="Timor-Leste"/>
|
||||
<country code="tg" en="Togo" de="Togo" fr="Togo" it="Togo"/>
|
||||
<country code="tk" en="Tokelau" de="Tokelau" fr="Tokélaou" it="Tokelau"/>
|
||||
<country code="to" en="Tonga" de="Tonga" fr="Tonga" it="Tonga"/>
|
||||
<country code="tt" en="Trinidad and Tobago" de="Trinidad und Tobago" fr="Trinité-et-Tobago" it="Trinidad e Tobago"/>
|
||||
<country code="tn" en="Tunisia" de="Tunesien" fr="Tunisie" it="Tunisia"/>
|
||||
<country code="tr" en="Turkey" de="Türkiye" fr="Turquie" it="Turchia"/>
|
||||
<country code="tm" en="Turkmenistan" de="Turkmenistan" fr="Turkménistan" it="Turkmenistan"/>
|
||||
<country code="tc" en="Turks and Caicos" de="Turks- und Caicosinseln" fr="Turks-et-Caïcos" it="Isole Turks e Caicos"/>
|
||||
<country code="tv" en="Tuvalu" de="Tuvalu" fr="Tuvalu" it="Tuvalu"/>
|
||||
<country code="ug" en="Uganda" de="Uganda" fr="Ouganda" it="Uganda"/>
|
||||
<country code="ua" en="Ukraine" de="Ukraine" fr="Ukraine" it="Ucraina"/>
|
||||
<country code="ae" en="United Arab Emirates" de="Vereinigte Arabische Emirate" fr="Émirats arabes unis" it="Emirati Arabi Uniti"/>
|
||||
<country code="um" en="United States Minor Outlying Islands" de="United States Minor Outlying Islands" fr="Îles mineures éloignées des États-Unis" it="Isole Minori Esterne degli Stati Uniti"/>
|
||||
<country code="us" en="United States of America" de="Vereinigte Staaten von Amerika" fr="États-Unis d’Amérique" it="Stati Uniti"/>
|
||||
<country code="uy" en="Uruguay" de="Uruguay" fr="Uruguay" it="Uruguay"/>
|
||||
<country code="uz" en="Uzbekistan" de="Usbekistan" fr="Ouzbékistan" it="Uzbekistan"/>
|
||||
<country code="vu" en="Vanuatu" de="Vanuatu" fr="Vanuatu" it="Vanuatu"/>
|
||||
<country code="va" en="Vatican City State" de="Vatikanstadt" fr="Saint-Siège (Cité du Vatican)" it="Città del Vaticano"/>
|
||||
<country code="ve" en="Venezuela" de="Venezuela" fr="Venezuela" it="Venezuela"/>
|
||||
<country code="vn" en="Vietnam" de="Vietnam" fr="Vietnam" it="Vietnam"/>
|
||||
<country code="vi" en="Virgin Islands (USA)" de="Jungferninseln (USA)" fr="Îles Vierges américaines" it="Isole Vergini"/>
|
||||
<country code="vg" en="Virgin Islands, British (Tortola)" de="British Virgin Islands (Tortola)" fr="Îles Vierges britanniques (Tortola)" it="Isole Vergini britanniche"/>
|
||||
<country code="wf" en="Wallis and Futuna Islands" de="Wallis und Futuna" fr="Îles Wallis-et-Futuna" it="Wallis e Futuna"/>
|
||||
<country code="eh" en="Western Sahara" de="Westsahara" fr="Sahara occidental" it="Sahara occidentale"/>
|
||||
<country code="ws" en="Western Samoa" de="Samoa" fr="Samoa" it="Samoa occidentale"/>
|
||||
<country code="ye" en="Yemen" de="Jemen" fr="Yémen" it="Yemen"/>
|
||||
<country code="zm" en="Zambia" de="Sambia" fr="Zambie" it="Zambia"/>
|
||||
<country code="zw" en="Zimbabwe" de="Simbabwe" fr="Zimbabwe" it="Zimbabwe" />
|
||||
</country-names>
|
|
@ -0,0 +1,225 @@
|
|||
<Domain inactiveInterval="30" reauthInterval="0" statelessAuth="true">
|
||||
<Entry method="authenticate" state="Check_Trusted_Caller"/>
|
||||
<Entry method="stepup" state="STS_Audit_Failure"/>
|
||||
</Domain>
|
||||
|
||||
<!-- ***** Authenticate Caller ***** -->
|
||||
<AuthState name="Check_Trusted_Caller" class="ch.nevis.esauth.auth.states.cache.ReadFromCacheState" final="false">
|
||||
<ResultCond name="ok" next="Dispatcher_TokenType"/>
|
||||
<ResultCond name="miss" next="Validation_Client_Cert"/>
|
||||
<Response value="AUTH_ERROR" />
|
||||
<property name="cacheSpace" value="TechAuthCache"/>
|
||||
<property name="hashAlgorithm" value="SHA-512"/>
|
||||
<property name="sess:agov.techuser.extId" value="${param.cert.source}"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Validation_Client_Cert" class="ch.nevis.idm.authstate.IdmX509State" final="false" resumeState="true">
|
||||
<ResultCond name="default" next="STS_Audit_Failure"/>
|
||||
<ResultCond name="ok" next="Validation_Client_Cert_PostProcessing"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="AuthErrorDialog">
|
||||
<GuiElem name="lasterror" type="error" label="#{notes.containsKey('lasterror') ? 'error.login.cert.' : ''}#{notes['lasterror']}"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.certificate" value="${param.cert.source}"/>
|
||||
<property name="client.name" value="${param.techuser.client.name}"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Validation_Client_Cert_PostProcessing" class="ch.nevis.idm.authstate.IdmGetPropertiesState" final="false" resumeState="true">
|
||||
<ResultCond name="default" next="STS_Audit_Failure"/>
|
||||
<ResultCond name="ok" next="Check_Impersonator"/>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="detaillevel.default" value="EXCLUDE"/>
|
||||
<property name="chooseDefaultProfile" value="true"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Check_Impersonator" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="false">
|
||||
<ResultCond name="isImpersonator" next="Clear_Session"/>
|
||||
<ResultCond name="default" next="STS_Audit_Failure"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||
</Response>
|
||||
<property name="condition:isImpersonator" value="${response/actualRoles/^.*(nevisIdm\.Impersonator).*$}"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Clear_Session" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false">
|
||||
<ResultCond name="ok" next="Cache_Trusted_Caller"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||
</Response>
|
||||
<property name="sess:agov.techuser.extId" value="${sess:ch.adnovum.nevisidm.user.extId}"/>
|
||||
<property name="sess:ch.adnovum.nevisidm.clientExtId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.clientId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.clientName" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.profileExtId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.profileId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.profileName" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.user.clientExtId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.user.extId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.user.loginId" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.userDto" value=""/>
|
||||
<property name="sess:ch.adnovum.nevisidm.userExtId" value=""/>
|
||||
<property name="sess:ch.nevis.idm.User.extId" value=""/>
|
||||
<property name="removeOnEmptyValue" value="true"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Cache_Trusted_Caller" class="ch.nevis.esauth.auth.states.cache.WriteToCacheState" final="false">
|
||||
<ResultCond name="ok" next="Dispatcher_TokenType"/>
|
||||
<ResultCond name="failed" next="STS_Audit_Failure"/>
|
||||
<Response value="AUTH_ERROR" />
|
||||
<property name="cacheSpace" value="TechAuthCache"/>
|
||||
<property name="hashAlgorithm" value="SHA-512"/>
|
||||
<!-- maxAge: 1 hour -->
|
||||
<property name="maxAge" value="3600"/>
|
||||
<!-- maxEntries: 2 as we have only 1 tech user, which should use that service -->
|
||||
<property name="maxEntries" value="2"/>
|
||||
<property name="overwriteOldEntries" value="false"/>
|
||||
<property name="${param.cert.source}" value="${sess:agov.techuser.extId}"/>
|
||||
</AuthState>
|
||||
|
||||
<!-- ***** Dispatch Requests ***** -->
|
||||
<AuthState name="Dispatcher_TokenType" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="false">
|
||||
<ResultCond name="SamlAssertion" next="Service_Provider_State"/>
|
||||
<ResultCond name="checkOblCode" next="Verify_Shadow_User"/>
|
||||
<ResultCond name="usernameToken" next="Verify_User_extID"/>
|
||||
<ResultCond name="default" next="STS_Audit_Failure"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||
</Response>
|
||||
<property name="condition:SamlAssertion" value="${request:currentResource:/nevisauth/services/sts/saml:true}"/>
|
||||
<property name="condition:checkOblCode" value="${request:currentResource:/nevisauth/services/sts/check:true}"/>
|
||||
<property name="condition:usernameToken" value="${request:currentResource:/nevisauth/services/sts/username:true}"/>
|
||||
</AuthState>
|
||||
|
||||
<!-- ***** SAML Assertion to Token, and usernameToken ***** -->
|
||||
<AuthState name="Service_Provider_State" class="ch.nevis.esauth.auth.states.saml.ServiceProviderState" final="false" resumeState="true">
|
||||
<ResultCond name="default" next="STS_Audit_Failure"/>
|
||||
<ResultCond name="ok" next="Verify_User_extID" authLevel="auth.weak"/>
|
||||
<property name="consumerURL" value="${param.saml.assertion.acsurl}"/>
|
||||
<property name="in.verify" value="Assertion"/>
|
||||
<property name="in.internalBindingSource" value="${inargs:SAMLAssertion}"/>
|
||||
<property name="in.binding" value="internal-assertion"/>
|
||||
<property name="in.max_age" value="${param.saml.assertion.max_age}"/>
|
||||
<property name="in.audience" value="${param.saml.assertion.audience}"/>
|
||||
<property name="in.keystoreref" value="${keystore}"/>
|
||||
<property name="in.prospectVerification" value="SubjectConfirmation"/>
|
||||
<property name="out.sign" value="none"/>
|
||||
<property name="out.binding" value="none"/>
|
||||
<property name="out.ttl" value="30"/>
|
||||
<property name="out.issuer" value="not-used"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Verify_User_extID" class="ch.nevis.idm.authstate.IdmUserVerifyState" final="false" resumeState="true">
|
||||
<ResultCond name="clientNotFound" next="STS_Audit_Failure"/>
|
||||
<ResultCond name="failed" next="STS_Audit_Failure"/>
|
||||
<ResultCond name="prospect" next="Verify_User_extID_IdmGetPropertiesState"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="AuthFailDialog"/>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="userExtId" value="${inargs:UserID}"/>
|
||||
<property name="client.name" value="${param.accounts.client.name}"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Verify_User_extID_IdmGetPropertiesState" class="ch.nevis.idm.authstate.IdmGetPropertiesState" final="false" resumeState="true">
|
||||
<ResultCond name="SOAP:showGui" next="STS_Audit_Success"/>
|
||||
<ResultCond name="default" next="STS_Audit_Failure"/>
|
||||
<ResultCond name="ok" next="STS_Audit_Success"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="AuthProfileSelectionDialog">
|
||||
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.attributes" value="loginId,extId,firstName,name,email,gender,birthDate,language,sex,addressLine1,postalCode,city,country,street,houseNumber,locality"/>
|
||||
<property name="chooseDefaultProfile" value="true"/>
|
||||
</AuthState>
|
||||
|
||||
<!-- ***** Ceck Obl Code ***** -->
|
||||
<AuthState name="Verify_Shadow_User" class="ch.nevis.idm.authstate.IdmPasswordVerifyState" final="false" resumeState="false">
|
||||
<ResultCond name="cancel" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="clientNotFound" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="disabled" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="failed" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="lockWarn" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="locked" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="nowLocked" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="ok" next="Verify_Shadow_User_DeleteCredential" authLevel="auth.weak"/>
|
||||
<ResultCond name="pwChange" next="Verify_Shadow_User_Error"/>
|
||||
<ResultCond name="tmpLocked" next="Verify_Shadow_User_Error"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="ErrorDialog" label="error">
|
||||
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
|
||||
<GuiElem name="isiwebpasswd" type="pw-text" label="not-used" value="just-ot-hide-it-in-logs" optional="true" />
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.loginType" value="LOGINID"/>
|
||||
<property name="credential.type" value="contextPassword"/>
|
||||
<property name="credential.context" value="AGOV"/>
|
||||
<property name="client.name" value="${param.shadow-accounts.client.name}"/>
|
||||
<property name="user.loginId" value="${inargs:isiwebuserid}"/>
|
||||
<property name="user.password" value="${inargs:isiwebpasswd}"/>
|
||||
<property name="detaillevel.user" value="MEDIUM"/>
|
||||
<property name="detaillevel.profile" value="LOW"/>
|
||||
<property name="detaillevel.property" value="MEDIUM"/>
|
||||
<property name="detaillevel.credential" value="MEDIUM"/>
|
||||
<property name="detaillevel.certificate" value="MEDIUM"/>
|
||||
<property name="detaillevel.default" value="EXCLUDE"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Verify_Shadow_User_Error" class="ch.nevis.esauth.auth.states.standard.AuthLogout" final="true" resumeState="true">
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="ErrorDialog" label="error">
|
||||
<GuiElem name="lasterror" type="error" label="${notes:lasterrorinfo}" value="${notes:lasterror}"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Verify_Shadow_User_DeleteCredential" class="ch.nevis.idm.authstate.IdmDeleteCredentialState" final="false" resumeState="true">
|
||||
<ResultCond name="failed" next="STS_Audit_Success"/>
|
||||
<ResultCond name="noCredential" next="STS_Audit_Success"/>
|
||||
<ResultCond name="ok" next="STS_Audit_Success"/>
|
||||
<Response value="AUTH_ERROR"/>
|
||||
<propertyRef name="${realm}_Verify_Shadow_User"/>
|
||||
<property name="cred.context" value="AGOV"/>
|
||||
<property name="cred.type" value="CONTEXT_PASSWORD"/>
|
||||
</AuthState>
|
||||
|
||||
<!-- ***** Terminal States ***** -->
|
||||
<AuthState name="STS_Audit_Success" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
|
||||
<ResultCond name="error" next="Authentication_Failed"/>
|
||||
<ResultCond name="ok" next="Auth_Done"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/sts_audit_success.groovy"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Auth_Done" class="ch.nevis.esauth.auth.states.standard.AuthDone" final="false">
|
||||
<Response value="AUTH_DONE">
|
||||
<Gui name="ContinueResponse"/>
|
||||
</Response>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="STS_Audit_Failure" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
|
||||
<ResultCond name="error" next="Authentication_Failed"/>
|
||||
<ResultCond name="ok" next="Authentication_Failed"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/sts_audit_failure.groovy"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="Authentication_Failed" class="ch.nevis.esauth.auth.states.standard.AuthError" final="false">
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="Error">
|
||||
<GuiElem name="info" type="error" label="error_99"/>
|
||||
<GuiElem name="submit" type="button" label="continue.button.label"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
</AuthState>
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
try {
|
||||
def user = inargs['UserID'] ?: session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
def techuser = session['agov.techuser.extId'] ?: 'unknown'
|
||||
def sourceIp = request.getTransportLayerInformation().getRemoteIP() ?: 'unknown'
|
||||
def credentialType = request.getResource().replaceAll("\\/nevisauth\\/services\\/sts\\/(.+)\\/", "\$1").toUpperCase()
|
||||
def lasterrorinfo = notes.getProperty('lasterrorinfo', '-')
|
||||
def lasterror = notes.getProperty('lasterror', '-')
|
||||
|
||||
if (credentialType=='SAML') {
|
||||
credentialType = 'PASSWORD'
|
||||
}
|
||||
LOG.warn("Event='TKNFAILED', Techuser=${techuser}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, lasterrorinfo='${lasterrorinfo}', lasterror=${lasterror}")
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Exception in Script: ${e}")
|
||||
} finally {
|
||||
response.setResult('ok')
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
try {
|
||||
def user = inargs['UserID'] ?: session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
def techuser = session['agov.techuser.extId'] ?: 'unknown'
|
||||
def sourceIp = request.getTransportLayerInformation().getRemoteIP() ?: 'unknown'
|
||||
def credentialType = request.getResource().replaceAll("\\/nevisauth\\/services\\/sts\\/(.+)\\/", "\$1").toUpperCase()
|
||||
|
||||
if (credentialType=='SAML') {
|
||||
credentialType = 'PASSWORD'
|
||||
}
|
||||
LOG.info("Event='TKNISSUED', Techuser=${techuser}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}")
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.warn("Exception in Script: ${e}")
|
||||
} finally {
|
||||
response.setResult('ok')
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.saml.IdentityProviderState" final="false" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.entry}_Handle_Redirect"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${request:traceId}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="in.binding" value="none"/>
|
||||
<property name="out.binding" value="internal"/>
|
||||
<property name="out.sign" value="Response Assertion"/>
|
||||
<property name="out.signatureKeyInfo" value="Certificate"/>
|
||||
<!-- assertion validity time -->
|
||||
<property name="out.ttl" value="${param.assertionValidityTime}"/>
|
||||
<!-- subject confirmation: Bearer -->
|
||||
<property name="out.extension.Bearer" value="ch.nevis.esauth.auth.states.saml.extensions.SubjectConfirmationExtender"/>
|
||||
<property name="Bearer.ttl" value="${param.assertionValidityTime}"/>
|
||||
<property name="out.keystoreref" value="Store_IDP_AGOV"/>
|
||||
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
|
||||
<property name="spURL" value="${param.agovmedirecturl}"/>
|
||||
<property name="acsUrlWhitelist.uris" value="not used"/>
|
||||
<!-- adttributes -->
|
||||
<property name="out.authnContextClassRef" value="${sess:agov.recovery.authnContextClassRef}"/>
|
||||
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/authenticatedWith" value="${sess:agov.recovery.authenticatedWith}"/>
|
||||
<property name="out.attribute.http://schemas.agov.ch/ws/2023/11/identity/claims/currentAgovAq" value="${sess:agov.recovery.currentAgovAq}"/>
|
||||
<property name="out.attribute.http://schemas.agov.ch/ws/2024/01/identity/claims/currentIdVerification" value="${sess:agov.recovery.currentIdVerification}"/>
|
||||
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/qa/dateOfVerification"
|
||||
value="${sess:agov.recovery.currentAgovAqRoleValidFrom}"/>
|
||||
<property name="out.attributeDelimiter" value=",\s*" />
|
||||
<property name="out.subject" value="${sess:ch.adnovum.nevisidm.user.extId}"/>
|
||||
<property name="out.subject.format" value="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
|
||||
<property name="out.issuer" value="${param.issuer}"/>
|
||||
<property name="out.audienceRestriction" value="${param.directAudience}"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="${state.entry}_Handle_Redirect" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="true">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="not_used"/>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="parameter.agovmedirecturl" value="${param.agovmedirecturl}"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/handleRedirectRecovery.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,23 @@
|
|||
if(outargs.containsKey('saml.SAMLResponse')) {
|
||||
// Accounting
|
||||
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'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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'
|
||||
|
||||
LOG.info("Event='GOTORECOVERY', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
|
||||
// Redirect
|
||||
response.addOutArg('nevis.transfer.destination', parameters.get('agovmedirecturl'))
|
||||
response.addOutArg('nevis.transfer.field.SAMLResponse', outargs.getProperty('saml.SAMLResponse').bytes.encodeBase64().toString())
|
||||
response.setStatus(ch.nevis.esauth.auth.engine.AuthResponse.AUTH_CONTINUE)
|
||||
response.setIsRedirectTransfer(false)
|
||||
|
||||
response.removeOutArg('saml.SAMLResponse')
|
||||
}
|
||||
else {
|
||||
response.setResult('ok')
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="true" resumeState="true">
|
||||
<ResultCond name="default" next="${state.entry}"/>
|
||||
<ResultCond name="noEmail" next="${state.entry}_noEmail"/>
|
||||
<ResultCond name="hasCode" next="${state.done}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_intro_email_sent">
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="noEmail" type="submit" label="not used" value="not used" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="condition:noEmail" value="#{inargs.containsKey('noEmail')}"/>
|
||||
<property name="condition:hasCode" value="#{inargs.containsKey('cd')}"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_noEmail" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false" resumeState="false">
|
||||
<ResultCond name="default" next="${state.failed}"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="sess:agov.recovery.code" value=""/>
|
||||
<property name="removeOnEmptyValue" value="true"/>
|
||||
<property name="notes:invalidUrlTicket" value="not received"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,17 @@
|
|||
function outputHeader(request, response)
|
||||
trace = request:getTracer()
|
||||
-- after successful authentication neviauth returns the SecToken as attribute to the proxy
|
||||
secToken = request:getAttribute("ch.nevis.isiweb4.auth.SecToken")
|
||||
if secToken then
|
||||
trace:debug("SessionInvalidationFilter: SecToken part of the attributes returned from nevisAuth: "..secToken)
|
||||
session = request:getSession(false)
|
||||
if session then
|
||||
session:invalidate()
|
||||
trace:info("SessionInvalidationFilter: Session invalidated after successful authentication")
|
||||
else
|
||||
trace:debug("SessionInvalidationFilter: SecToken but no session, nothing to do")
|
||||
end
|
||||
else
|
||||
trace:debug("SessionInvalidationFilter: No SecToken, nothing to do")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,129 @@
|
|||
import groovy.xml.XmlSlurper
|
||||
import groovy.json.JsonSlurper
|
||||
//import ch.nevis.esauth.util.httpclient.api.HttpClients
|
||||
//import ch.nevis.esauth.util.httpclient.api.Http
|
||||
|
||||
|
||||
int getRequestedLevel(String authnContextClassRef, def roleList){
|
||||
if (!authnContextClassRef) {
|
||||
return 100
|
||||
}
|
||||
if (authnContextClassRef && authnContextClassRef.startsWith('urn:qa.agov.ch:names:tc:ac:classes:')) {
|
||||
def requestedLevel = authnContextClassRef.substring(35)
|
||||
LOG.debug('authnContextClassRef agov found: ' + requestedLevel)
|
||||
if (requestedLevel.isNumber()) {
|
||||
int requestedLevelNumber = Integer.parseInt(requestedLevel)
|
||||
LOG.debug('contains ' + roleList.contains(requestedLevelNumber))
|
||||
if (requestedLevel.isNumber() && roleList.contains(requestedLevelNumber)) {
|
||||
LOG.debug('Requested role number: ' + requestedLevel)
|
||||
return requestedLevelNumber
|
||||
}
|
||||
}
|
||||
else return 0
|
||||
}
|
||||
else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
def session = request.getAuthSession(true)
|
||||
def context = session.get('ch.nevis.auth.saml.request.authnContextClassRef')
|
||||
def roleLevels = [100,200,300,400]
|
||||
def requestedRoleLevelNumber = getRequestedLevel(context, roleLevels)
|
||||
|
||||
//set attribute Requested Role Level
|
||||
session.setAttribute('agov.requestedRoleLevel', '' + requestedRoleLevelNumber)
|
||||
LOG.debug('Requested role level (agov) '+ requestedRoleLevelNumber)
|
||||
|
||||
// SAML finisherstate is now available, we can backup it
|
||||
session.setAttribute('agov.backup.finishers', '' + session.getAttribute('ch.nevis.session.finishers'))
|
||||
|
||||
// Accounting
|
||||
def requester = session['ch.nevis.auth.saml.request.scoping.requesterId'] ?: 'unknown'
|
||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||
def replacedRequestId = session['agov.replacedRequestId'] ?: '-'
|
||||
def requestedAq = session['agov.requestedRoleLevel'] ?: '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'
|
||||
|
||||
LOG.info("Event='AUTHREQUEST', Requester='${requester}', RequestId='${requestId}', ReplacedRequestId='${replacedRequestId}', RequestedAq=${requestedAq}, SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
|
||||
|
||||
def appAddressRequiredWhitelist = ',' + (parameters.get('appAddressRequired.whitelist') ?: '').replaceAll('\\s','') + ','
|
||||
def appIsOnappAddressRequiredWhitelist = appAddressRequiredWhitelist.contains(','+requester+',')
|
||||
|
||||
if (requestedRoleLevelNumber == 0 || session.get('ch.nevis.auth.saml.request.scoping.requesterId') == null) {
|
||||
response.setResult('error');
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
def jsonSlurper = new JsonSlurper()
|
||||
def url = parameters.get('url') + '?entity-id=' + session.get('ch.nevis.auth.saml.request.scoping.requesterId')
|
||||
LOG.debug('Request url: ' + url)
|
||||
def httpClient = HttpClients.create(parameters)
|
||||
def httpResponse = Http.get().url(url).build().send(httpClient)
|
||||
LOG.debug('Response Message: ' + httpResponse.reasonPhrase())
|
||||
LOG.debug('Response Status Code: ' + httpResponse.code())
|
||||
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
||||
|
||||
if (httpResponse.code() == 200) {
|
||||
def json = jsonSlurper.parseText(httpResponse.bodyAsString())
|
||||
LOG.debug('AdressRequired: ' + json.addrRequired)
|
||||
LOG.debug('SvnrAllowed: ' + json.svnrAllowed)
|
||||
LOG.debug('appAddressRequiredWhitelist applies: ' + appIsOnappAddressRequiredWhitelist)
|
||||
|
||||
// address will be returned to the application if allowed by connect (json.addrRequired)
|
||||
// and the authRequest was done with at least AGOVaq 200
|
||||
// BITBKAGOVSUP-362: or whitelisted to receive the address
|
||||
session.setAttribute('agov.appAddressRequired', '' + (json.addrRequired && ((requestedRoleLevelNumber >= 200) || appIsOnappAddressRequiredWhitelist)))
|
||||
|
||||
// address will be returned to the application if allowed by connect (json.svnrAllowed)
|
||||
// and the authRequest was done with at least AGOVaq 300
|
||||
session.setAttribute('agov.appSvnrAllowed', '' + (json.svnrAllowed && requestedRoleLevelNumber >= 300))
|
||||
|
||||
session.setAttribute('agov.appDisplayNameDE', '' + json.displayNameDe)
|
||||
session.setAttribute('agov.appDisplayNameFR', '' + json.displayNameFr)
|
||||
session.setAttribute('agov.appDisplayNameIT', '' + json.displayNameIt)
|
||||
session.setAttribute('agov.appDisplayNameEN', '' + json.displayNameEn)
|
||||
response.setResult('ok')
|
||||
return
|
||||
} else {
|
||||
LOG.warn("Failed to fetch connect meta data for relying party '${session.get('ch.nevis.auth.saml.request.scoping.requesterId')}'")
|
||||
LOG.warn('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||
|
||||
if ( requestedRoleLevelNumber == 100) {
|
||||
session.setAttribute('agov.appAddressRequired', '' + appIsOnappAddressRequiredWhitelist)
|
||||
session.setAttribute('agov.appSvnrAllowed', 'false')
|
||||
response.setResult('ok')
|
||||
}
|
||||
else if ( requestedRoleLevelNumber == 200) {
|
||||
session.setAttribute('agov.appAddressRequired', 'true')
|
||||
session.setAttribute('agov.appSvnrAllowed', 'false')
|
||||
response.setResult('ok')
|
||||
}
|
||||
else {
|
||||
response.setResult('error')
|
||||
response.setError(9071, "Missing meta data for relying party, can't process request")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to fetch connect meta data for relying party '${session.get('ch.nevis.auth.saml.request.scoping.requesterId')}'", e)
|
||||
if ( requestedRoleLevelNumber == 100) {
|
||||
session.setAttribute('agov.appAddressRequired', '' + appIsOnappAddressRequiredWhitelist)
|
||||
session.setAttribute('agov.appSvnrAllowed', 'false')
|
||||
response.setResult('ok')
|
||||
}
|
||||
else if ( requestedRoleLevelNumber == 200) {
|
||||
session.setAttribute('agov.appAddressRequired', 'true')
|
||||
session.setAttribute('agov.appSvnrAllowed', 'false')
|
||||
response.setResult('ok')
|
||||
}
|
||||
else {
|
||||
response.setResult('error')
|
||||
response.setError(9072, "Failure while processing meta data for relying party, can't continue processing request")
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
def url = parameters.get('url')
|
||||
|
||||
def email = inargs['userInputValue_prompt.email']
|
||||
def token = inargs['captcha_response']?: 'MISSING'
|
||||
|
||||
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 payload = "{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }"
|
||||
|
||||
LOG.debug('Token: ' + token)
|
||||
LOG.debug('Payload: ' + payload)
|
||||
|
||||
try {
|
||||
|
||||
def httpClient = HttpClients.create(parameters)
|
||||
def httpResponse = Http.post()
|
||||
.url(url)
|
||||
.header("Accept", "application/json")
|
||||
.header("X-FriendlyCAPTCHA-Token", token)
|
||||
.entity(Http.entity()
|
||||
.content(payload)
|
||||
.contentType("application/json")
|
||||
.build())
|
||||
.build()
|
||||
.send(httpClient)
|
||||
|
||||
LOG.debug('Response Message: ' + httpResponse.reasonPhrase())
|
||||
LOG.debug('Response Status Code: ' + httpResponse.code())
|
||||
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
||||
|
||||
if (httpResponse.code() == 200) {
|
||||
if (httpResponse.bodyAsString().contains('SUCCESSFUL')) {
|
||||
response.setResult('ok')
|
||||
return
|
||||
} else {
|
||||
LOG.warn("Friendly captcha not successful for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
||||
response.setResult('exit.1')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
LOG.error("Friendly captcha failed with statuscode ${httpResponse.code()} for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
||||
response.setResult('error')
|
||||
response.setError(1, 'Unexpected HTTP reponse')
|
||||
}
|
||||
} catch (all) {
|
||||
// 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}")
|
||||
response.setResult('error')
|
||||
response.setError(1, 'Exception during HTTP call')
|
||||
}
|
||||
|
||||
|
||||
// TODO/haburger/2024-AUG-20: remove if reCaptcha is not needed anymore
|
||||
//
|
||||
// def payload = '{ "email": "' + inargs['userInputValue_prompt.email'] + '", "action": "LOGIN", "userIp": "' + ip + '", "userAgent": "' + userAgent + '"}'
|
||||
//
|
||||
// LOG.info('Token: ' + inargs['recaptcha_response'])
|
||||
// LOG.info('Integration: ' + session['agov.fido2.X-ReCAPTCHA-Integration'])
|
||||
// LOG.info('Payload: ' + payload)
|
||||
//
|
||||
// try {
|
||||
//
|
||||
// def httpClient = HttpClients.create(parameters)
|
||||
// def httpResponse = Http.post()
|
||||
// .url(url)
|
||||
// .header("Accept", "application/json")
|
||||
// .header("X-ReCAPTCHA-Token", inargs['recaptcha_response'])
|
||||
// .header("X-ReCAPTCHA-Integration", session['agov.fido2.X-ReCAPTCHA-Integration'])
|
||||
// .entity(Http.entity()
|
||||
// .content(payload)
|
||||
// .contentType("application/json")
|
||||
// .build())
|
||||
// .build()
|
||||
// .send(httpClient)
|
||||
//
|
||||
// LOG.info('Response Message: ' + httpResponse.reasonPhrase())
|
||||
// LOG.info('Response Status Code: ' + httpResponse.code())
|
||||
// LOG.info('Response: ' + httpResponse.bodyAsString())
|
||||
//
|
||||
// if (httpResponse.code() == 200) {
|
||||
// if (httpResponse.bodyAsString().contains('SUCCESSFUL')) {
|
||||
// response.setResult('ok')
|
||||
// return
|
||||
// } else {
|
||||
//
|
||||
// response.setSessionAttribute('agov.fido2.X-ReCAPTCHA-Integration', 'VISIBLE')
|
||||
// response.setResult('exit.1')
|
||||
// return
|
||||
// }
|
||||
// } else {
|
||||
// LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||
// response.setResult('error')
|
||||
// response.setError(1, 'Unexpected HTTP reponse')
|
||||
// }
|
||||
// } catch (all) {
|
||||
// // Handle exception and set the transition
|
||||
// LOG.error('error: ' + all, all)
|
||||
// response.setResult('error')
|
||||
// response.setError(1, 'Exception during HTTP call')
|
||||
// }
|
|
@ -0,0 +1,102 @@
|
|||
def url = parameters.get('url')
|
||||
|
||||
def email = inargs['email']
|
||||
def token = inargs['captcha_response']?: 'MISSING'
|
||||
|
||||
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 payload = "{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }"
|
||||
|
||||
LOG.debug('Token: ' + token)
|
||||
LOG.debug('Payload: ' + payload)
|
||||
|
||||
try {
|
||||
|
||||
def httpClient = HttpClients.create(parameters)
|
||||
def httpResponse = Http.post()
|
||||
.url(url)
|
||||
.header("Accept", "application/json")
|
||||
.header("X-FriendlyCAPTCHA-Token", token)
|
||||
.entity(Http.entity()
|
||||
.content(payload)
|
||||
.contentType("application/json")
|
||||
.build())
|
||||
.build()
|
||||
.send(httpClient)
|
||||
|
||||
LOG.debug('Response Message: ' + httpResponse.reasonPhrase())
|
||||
LOG.debug('Response Status Code: ' + httpResponse.code())
|
||||
LOG.debug('Response: ' + httpResponse.bodyAsString())
|
||||
|
||||
if (httpResponse.code() == 200) {
|
||||
if (httpResponse.bodyAsString().contains('SUCCESSFUL')) {
|
||||
response.setResult('ok')
|
||||
return
|
||||
} else {
|
||||
LOG.warn("Friendly captcha not successful for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
||||
response.setResult('exit.1')
|
||||
return
|
||||
}
|
||||
} else {
|
||||
LOG.error("Friendly captcha failed with statuscode ${httpResponse.code()} for '{ \"userIp\": \"${ip}\", \"email\": \"${email}\", \"userAgent\": \"${userAgent}\" }'")
|
||||
response.setResult('error')
|
||||
response.setError(1, 'Unexpected HTTP reponse')
|
||||
}
|
||||
} catch (all) {
|
||||
// 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}")
|
||||
response.setResult('error')
|
||||
response.setError(1, 'Exception during HTTP call')
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO/haburger/2024-AUG-20: remove if reCaptcha is not needed anymore
|
||||
// def payload = '{ "email": "' + inargs['email'] + '", "action": "LOGIN", "userIp": "' + session.get('agov.recovery.ip') + '", "userAgent": "' + session.get('agov.recovery.userAgent') + '"}'
|
||||
//
|
||||
// LOG.info('Token: ' + inargs['recaptcha_response'])
|
||||
// LOG.info('Integration: ' + session['agov.recovery.X-ReCAPTCHA-Integration'])
|
||||
// LOG.info('Payload: ' + payload)
|
||||
//
|
||||
// try {
|
||||
//
|
||||
// def httpClient = HttpClients.create(parameters)
|
||||
// def httpResponse = Http.post()
|
||||
// .url(url)
|
||||
// .header("Accept", "application/json")
|
||||
// .header("X-ReCAPTCHA-Token", inargs['recaptcha_response'])
|
||||
// .header("X-ReCAPTCHA-Integration", session['agov.recovery.X-ReCAPTCHA-Integration'])
|
||||
// .entity(Http.entity()
|
||||
// .content(payload)
|
||||
// .contentType("application/json")
|
||||
// // .charSet("utf-8")
|
||||
// .build())
|
||||
// .build()
|
||||
// .send(httpClient)
|
||||
//
|
||||
// LOG.info('Response Message: ' + httpResponse.reasonPhrase())
|
||||
// LOG.info('Response Status Code: ' + httpResponse.code())
|
||||
// LOG.info('Response: ' + httpResponse.bodyAsString())
|
||||
//
|
||||
// if (httpResponse.code() == 200) {
|
||||
// if (httpResponse.bodyAsString().contains('SUCCESSFUL')) {
|
||||
// response.setResult('ok')
|
||||
// return
|
||||
// } else {
|
||||
//
|
||||
// response.setSessionAttribute('agov.recovery.X-ReCAPTCHA-Integration', 'VISIBLE')
|
||||
// response.setResult('exit.1')
|
||||
// return
|
||||
// }
|
||||
// } else {
|
||||
// LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||
// response.setResult('error')
|
||||
// response.setError(1, 'Unexpected HTTP reponse')
|
||||
// }
|
||||
// } catch (all) {
|
||||
// // Handle exception and set the transition
|
||||
// LOG.error('error: ' + all, all)
|
||||
// response.setResult('error')
|
||||
// response.setError(1, 'Exception during HTTP call')
|
||||
// }
|
|
@ -0,0 +1,9 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<ResultCond name="continueAfterRepost" next="${state.exit.1}"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="parameter.cookie.domain" value="${param.cookie.domain}"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/idp_status_check.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,145 @@
|
|||
import groovy.json.JsonBuilder
|
||||
import java.security.MessageDigest
|
||||
import java.util.HashSet
|
||||
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||
|
||||
def getHeader(String name) {
|
||||
def inctx = request.getLoginContext()
|
||||
// case-insensitive lookup of HTTP headers
|
||||
def map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER)
|
||||
map.putAll(inctx)
|
||||
return map['connection.HttpHeader.' + name]
|
||||
}
|
||||
|
||||
def sha256(String input) {
|
||||
// we do not catch NoSuchAlgorithmException, as every implementation of the Java platform is required to support SHA-256
|
||||
def digestBytes = MessageDigest.getInstance('SHA-256').digest(input.getBytes())
|
||||
return digestBytes.encodeBase64().toString()
|
||||
}
|
||||
|
||||
|
||||
def clearCurrentAuthenticationSession() {
|
||||
|
||||
// clean up session attributes
|
||||
def s = request.getAuthSession(true)
|
||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||
|
||||
// we backup the replaced requestId
|
||||
if (requestId != 'unknown') {
|
||||
s.setAttribute('agov.replacedRequestId', '' + requestId)
|
||||
}
|
||||
|
||||
// fido
|
||||
s.removeAttribute('ch.nevis.auth.fido.uaf.fidouafsessionid')
|
||||
// SAML
|
||||
s.removeAttribute('finisherState-DeferredResponse')
|
||||
s.removeAttribute('saml.idp.result')
|
||||
s.removeAttribute('saml.inbound.issuer')
|
||||
|
||||
def sessionKeySet = new HashSet(session.keySet())
|
||||
sessionKeySet.each { key ->
|
||||
if ( key ==~ /ch.nevis.auth.saml.request.*/ ) {
|
||||
s.removeAttribute(key)
|
||||
}
|
||||
}
|
||||
// agov
|
||||
s.removeAttribute('agov.requestedRoleLevel')
|
||||
|
||||
}
|
||||
|
||||
|
||||
// context: script is executed, thus we are in the initial dispatching of the state engine
|
||||
// due to the resetAuthenticationCondition it will be called for sure after each SAMLRequest received
|
||||
if (inargs['SAMLRequest'] != null) {
|
||||
|
||||
if (session['ch.nevis.auth.saml.request.id'] != null) {
|
||||
|
||||
// Accounting
|
||||
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'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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'
|
||||
|
||||
// check if we receive a repost of the ongoing request
|
||||
if (session['agov.currentSamlRequestHash'] != null && session['agov.currentSamlRequestHash'] == sha256(inargs['SAMLRequest'])) {
|
||||
LOG.info("Event='AUTHCONTINUE', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
|
||||
request.getInArgs().remove('SAMLRequest')
|
||||
request.getInArgs().remove('RelayState')
|
||||
|
||||
// restore the finisher again (was removed by resetAuthenticationCondition)
|
||||
def s = request.getAuthSession(true)
|
||||
s.setAttribute('ch.nevis.session.finishers', '' + session['agov.backup.finishers'])
|
||||
|
||||
// process it the same way, as if frontend triggered a reload
|
||||
request.getInArgs().setProperty('onReload', 'now')
|
||||
|
||||
response.setResult('continueAfterRepost')
|
||||
return
|
||||
}
|
||||
// else, the new replaces the on-going one
|
||||
LOG.info("Event='AUTHREPL', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}'")
|
||||
clearCurrentAuthenticationSession()
|
||||
}
|
||||
|
||||
// we track the SAML Request we received
|
||||
def s = request.getAuthSession(true)
|
||||
s.setAttribute('agov.currentSamlRequestHash', '' + sha256(inargs['SAMLRequest']))
|
||||
|
||||
// we set/update a login Cookie
|
||||
def agovLoginCookie = "agovLogin=${System.currentTimeMillis()}; Domain=${parameters.get('cookie.domain')}; Path=/; SameSite=Strict; Secure; HttpOnly"
|
||||
response.setHeader('Set-Cookie', agovLoginCookie)
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// from here on, corner cases //
|
||||
// =============================
|
||||
def json = new JsonBuilder()
|
||||
|
||||
if (inargs.containsKey('o.fidoUafSessionId.v')) {
|
||||
|
||||
// timeout, and script in login page is still polling -> send fake response
|
||||
LOG.debug('authentication timeout reached, login script is still polling access app status')
|
||||
json {
|
||||
"status" "unknown"
|
||||
"timestamp" org.joda.time.DateTime.now().toString()
|
||||
}
|
||||
String body = json.toString()
|
||||
|
||||
response.setContent(body)
|
||||
response.setContentType('application/json')
|
||||
response.setHttpStatusCode(200)
|
||||
response.setIsDirectResponse(true)
|
||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||
return
|
||||
}
|
||||
else {
|
||||
// authentication timeout reached, or SSO-Endpoint bookmarked -> return a 404
|
||||
def agovLoginCookie = 'missing'
|
||||
|
||||
if (getHeader('cookie') != null) {
|
||||
def cookies = getHeader('cookie')
|
||||
if (cookies.matches('^.*agovLogin=([^;]+).*$')) {
|
||||
agovLoginCookie = cookies.replaceAll('^.*agovLogin=([^;]+).*$', '$1')
|
||||
}
|
||||
}
|
||||
LOG.debug("agovLoginCookie: ${agovLoginCookie}")
|
||||
if (agovLoginCookie == 'missing' || agovLoginCookie == 'deleted') {
|
||||
LOG.debug('SSO-Endpoint bookmarked -> return a 404')
|
||||
response.setHttpStatusCode(404)
|
||||
response.setIsDirectResponse(true)
|
||||
response.setStatus(AuthResponse.AUTH_ERROR)
|
||||
}
|
||||
else {
|
||||
LOG.debug('authentication timeout reached -> return a 408')
|
||||
response.setHttpStatusCode(408)
|
||||
response.setIsDirectResponse(true)
|
||||
response.setStatus(AuthResponse.AUTH_ERROR)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.idm.authstate.IdmUserVerifyState" final="false" resumeState="false">
|
||||
<ResultCond name="prospect" next="${state.entry}_IdmGetPropertiesState"/>
|
||||
<!-- Security issue : goes to next state if client not found -->
|
||||
<ResultCond name="failed" next="${state.entry}_FailedEmailState"/>
|
||||
<ResultCond name="clientNotFound" next="${state.exit.1}"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${system:random.bytes.16}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="user.loginId" value="${inargs:userInputValue_prompt.email}"/>
|
||||
<property name="user.loginType" value="EMAIL"/>
|
||||
<property name="client.name" value="${param.client.name}"/>
|
||||
<property name="presetNoteValues" value="false"/>
|
||||
<property name="detaillevel.user" value="HIGH"/>
|
||||
<property name="detaillevel.profile" value="HIGH"/>
|
||||
<property name="detaillevel.role" value="MEDIUM"/>
|
||||
<property name="detaillevel.authorization" value="HIGH"/>
|
||||
<property name="detaillevel.dataroom" value="LOW"/>
|
||||
<property name="detaillevel.credential" value="HIGH"/>
|
||||
<property name="detaillevel.property" value="HIGH"/>
|
||||
<property name="detaillevel.unit" value="LOW"/>
|
||||
<property name="detaillevel.default" value="EXCLUDE"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="${state.entry}_IdmGetPropertiesState" final="false" class="ch.nevis.idm.authstate.IdmGetPropertiesState" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<ResultCond name="clientNotFound" next="${state.exit.1}"/>
|
||||
<ResultCond name="default" next="${state.failed}"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${request:traceId}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="forceDataReload" value="true"/>
|
||||
<!-- Returned Attributes in SecToken -->
|
||||
<property name="user.attributes" value="${param.attributes}"/>
|
||||
<property name="user.properties" value="${param.properties}"/>
|
||||
<property name="user.cred.context_password1.state" value="true"/>
|
||||
<property name="user.cred.context_password1.context" value="true"/>
|
||||
<property name="userExtId" value="${sess:ch.nevis.session.userid}"/>
|
||||
<property name="chooseDefaultProfile" value="true"/>
|
||||
<property name="client.name" value="${param.client.name}"/>
|
||||
<property name="detaillevel.user" value="HIGH"/>
|
||||
<property name="detaillevel.profile" value="HIGH"/>
|
||||
<property name="detaillevel.role" value="MEDIUM"/>
|
||||
<property name="detaillevel.authorization" value="HIGH"/>
|
||||
<property name="detaillevel.dataroom" value="LOW"/>
|
||||
<property name="detaillevel.credential" value="HIGH"/>
|
||||
<property name="detaillevel.property" value="HIGH"/>
|
||||
<property name="detaillevel.unit" value="LOW"/>
|
||||
<property name="detaillevel.default" value="EXCLUDE"/>
|
||||
</AuthState>
|
||||
|
||||
<!-- Dummy authstate for failed email -->
|
||||
<AuthState name="${state.entry}_FailedEmailState" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false">
|
||||
<ResultCond name="default" next="${state.done}"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="sess:ch.adnovum.nevisidm.user.extId" value="4a9b6cf8-0093-3416-8db8-0f063aa85d14"/>
|
||||
<property name="sess:ch.nevis.idm.User.email" value="${inargs:userInputValue_prompt.email}"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,24 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEGDCCAwCgAwIBAgIBBDANBgkqhkiG9w0BAQsFADA4MQswCQYDVQQGEwJjaDEQ
|
||||
MA4GA1UEChMHQWRub3Z1bTEXMBUGA1UEAxMOYml0ZWlhbS1yb290Q0EwHhcNMjAw
|
||||
MzA1MTYzMDAwWhcNMzAwMzAzMTYzMDAwWjA0MQswCQYDVQQGEwJjaDEQMA4GA1UE
|
||||
ChMHQWRub3Z1bTETMBEGA1UEAxMKc2lnbmVyRkVEUzCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAJoWqbsYhNXW0mDsDJPAiTN896e4QML9qnt7FIhVKKe3
|
||||
T66lT/nfOkFPUZuKejgbjFFDEDChRJf0Achq7lWGKPrNPnrTxZmU7Bcu86BER76L
|
||||
4kDcGF/x03W9fgUgQ7X45CXYeq4vqfpzNC+lkZA1OxbpcXZA/4Z39Z3pm7CWXnAg
|
||||
v6nFABKJ9kVAyhuPyb5yIuGHcdLL+068aVp5sxY/6HoXf889+iVFDgTwSXVYKMyZ
|
||||
nZbvvd/IIod4WuiXsOspPS9yj+E9yMvtsUtChghcQ17ubo7S1P8JxAQWXngopH8Y
|
||||
nDeOiesJfR2APDdg7EXWYewARSFr10GxuXoKDjLe148CAwEAAaOCAS8wggErMAkG
|
||||
A1UdEwQCMAAwPwYJYIZIAYb4QgENBDIWME5ldmlzIEtleUJveCBHZW5lcmF0ZWQg
|
||||
Q2VydGlmaWNhdGUgdXNpbmcgT3BlblNTTDALBgNVHQ8EBAMCA6gwHQYDVR0lBBYw
|
||||
FAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBQ4zYpzY1lB5/bKeg3z1kJO
|
||||
kkdYgDBoBgNVHSMEYTBfgBRRdKau0TH9VQ0E8ob0J+WyYkcs4aE8pDowODELMAkG
|
||||
A1UEBhMCY2gxEDAOBgNVBAoTB0Fkbm92dW0xFzAVBgNVBAMTDmJpdGVpYW0tcm9v
|
||||
dENBggkA+97eIJWmttcwEQYJYIZIAYb4QgEBBAQDAgbAMBUGA1UdEQQOMAyCCnNp
|
||||
Z25lckZFRFMwDQYJKoZIhvcNAQELBQADggEBAHGHJ7DzRNdPl6Kiy4rCoQR/nhTa
|
||||
VbBsAeB070NpWma2iun3Wf5zIoefbSlPoofP4tOVYUoKtMHTWCYAUnHIEg5H985y
|
||||
Ym2MFY0vwgMZ+Jvcs7NCHzK9O/tN+uUjkFNLSCfzTb+K9vyF6lj4L4lQWa5++DZ6
|
||||
kWPaDWvwY/NOSoIehmJupmcJlA1qxzlTc+659xoOk1WyhusNkuiOUjFrLQ+tgRnD
|
||||
7dGuzJQyBV1Iy/A4IhpN2ootVgrI7NMJ2YetCq7yuipRZka3RoeVhUs8CWFfYRtc
|
||||
saTCck7atYyMVlPUf03EppC18ILBmbNzYJ58KT2oQywa7+Sdsqx4+5cOOOU=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,22 @@
|
|||
<AuthState name="${state.entry}" final="false" class="ch.nevis.idm.authstate.IdmGetPropertiesState" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<ResultCond name="clientNotFound" next="${state.failed}"/>
|
||||
<ResultCond name="default" next="${state.failed}"/>
|
||||
<Response value="AUTH_ERROR">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${request:traceId}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<property name="forceDataReload" value="true"/>
|
||||
<!-- Returned Attributes in SecToken -->
|
||||
<property name="user.attributes" value="${param.attributes}"/>
|
||||
<property name="user.properties" value="${param.properties}"/>
|
||||
<property name="userExtId" value="${sess:ch.nevis.session.userid}"/>
|
||||
<property name="chooseDefaultProfile" value="true"/>
|
||||
<property name="client.name" value="${param.client.name}"/>
|
||||
<property name="detaillevel.profile" value="HIGH"/>
|
||||
<property name="detaillevel.role" value="HIGH"/>
|
||||
<property name="detaillevel.authorization" value="HIGH"/>
|
||||
<property name="detaillevel.dataroom" value="HIGH"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,7 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="NotUsed"/>
|
||||
</Response>
|
||||
<property name="parameter.cookie.domain" value="${param.cookie.domain}"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/returnTimeoutButKeepSession.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,11 @@
|
|||
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||
|
||||
response.setIsDirectResponse(true)
|
||||
response.setContentType('text/html; charset=UTF-8')
|
||||
response.setContent('Timeout')
|
||||
response.setHttpStatusCode(205)
|
||||
response.setHeader('IDP-AUTH', 'Timeout')
|
||||
|
||||
// CONTINUE to keep the other request beeing processed
|
||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
||||
return
|
|
@ -0,0 +1,11 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.idm.authstate.IdmCreateCredentialState" final="false">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<ResultCond name="failed" next="${state.failed}"/>
|
||||
<ResultCond name="credentialExists" next="${state.done}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="NoGui"/>
|
||||
</Response>
|
||||
<property name="cred.type" value="url_ticket"/>
|
||||
<property name="cred.modificationComment" value="New ticket ordered"/>
|
||||
<property name="recreateIfExists" value="true"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,37 @@
|
|||
<AuthState name="${state.entry}" final="false" class="ch.nevis.idm.authstate.IdmGetPropertiesState" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.entry}_Check_new_LOA"/>
|
||||
<ResultCond name="insufficientLoa" next="${state.failed}"/>
|
||||
<ResultCond name="noRoleLevel" next="${state.exit.1}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="internal_error">
|
||||
<GuiElem name="transferId" type="hidden" value="${request:traceId}" optional="true"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<propertyRef name="nevisIDM_Connector"/>
|
||||
<!-- Returned Attributes in SecToken -->
|
||||
<property name="client.name" value="${param.client.name}"/>
|
||||
<property name="user.attributes" value="${param.attributes}"/>
|
||||
<property name="user.properties" value="${param.properties}"/>
|
||||
<property name="user.cred.context_password1.state" value="true"/>
|
||||
<property name="chooseDefaultProfile" value="true"/>
|
||||
<property name="forceDataReload" value="true"/>
|
||||
<property name="userExtId" value="${sess:ch.nevis.session.userid}"/>
|
||||
<property name="detaillevel.user" value="HIGH"/>
|
||||
<property name="detaillevel.profile" value="HIGH"/>
|
||||
<property name="detaillevel.role" value="MEDIUM"/>
|
||||
<property name="detaillevel.authorization" value="HIGH"/>
|
||||
<property name="detaillevel.dataroom" value="LOW"/>
|
||||
<property name="detaillevel.credential" value="HIGH"/>
|
||||
<property name="detaillevel.property" value="HIGH"/>
|
||||
<property name="detaillevel.unit" value="LOW"/>
|
||||
<property name="detaillevel.default" value="EXCLUDE"/>
|
||||
</AuthState>
|
||||
|
||||
<AuthState name="${state.entry}_Check_new_LOA" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.done}"/>
|
||||
<ResultCond name="insufficientLoa" next="${state.failed}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="not_used"/>
|
||||
</Response>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/checkInsufficientLoa.groovy"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,133 @@
|
|||
import groovy.xml.XmlSlurper
|
||||
|
||||
def getUserAGOVLoiRoles() {
|
||||
// set attibutes from DTO: -> AGOVaq
|
||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||
return list.'**'.findAll { node -> node.name() == 'roles' && node.applicationName.text() == 'AGOV-Loi' }.collect({ node -> node.name.text() })
|
||||
}
|
||||
|
||||
def getUserAGOVLoiIdVerification() {
|
||||
// set attibutes from DTO: -> idVerification
|
||||
def list = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||
return list.'**'.findAll {node -> node.name() == 'properties' && node.name.text() == 'idVerification' }.collect({ node -> node.value.text()})
|
||||
}
|
||||
|
||||
def getUserAGOVLoiValidFrom(level) {
|
||||
// set attibutes from DTO: -> validFrom
|
||||
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||
return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}.getProperty("validFrom")
|
||||
}
|
||||
|
||||
def getUserAGOVLoiValidTo(level) {
|
||||
// set attibutes from DTO: -> validTo
|
||||
def payload = new XmlSlurper().parseText(session.get('ch.adnovum.nevisidm.userDto'))
|
||||
return payload.'**'.find {node -> node.name() == 'authorizations' && node.role.name.text() == level}.getProperty("validTo")
|
||||
}
|
||||
|
||||
// Accounting
|
||||
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'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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'
|
||||
|
||||
try {
|
||||
// beef
|
||||
def session = request.getAuthSession(true)
|
||||
def highestRoleLevelNumber = 0
|
||||
def requestedRoleLevelNumber = session.get('agov.requestedRoleLevel').toInteger()
|
||||
def hasValidatedAddress = Arrays.stream(response.getActualRoles()).filter(s -> s == 'AGOV-Loi.level200').findAny().isPresent()
|
||||
|
||||
LOG.debug('Requested role level '+ requestedRoleLevelNumber)
|
||||
LOG.debug('idVerification: ' + getUserAGOVLoiIdVerification())
|
||||
LOG.debug('hasValidatedAddress : ' + hasValidatedAddress)
|
||||
|
||||
session.setAttribute('idVerification', getUserAGOVLoiIdVerification().last())
|
||||
session.setAttribute('agov.hasValidatedAddress', '' + hasValidatedAddress)
|
||||
|
||||
|
||||
if (requestedRoleLevelNumber == 0) {
|
||||
// AuthnFailed_Zero_RoleLvl
|
||||
response.setResult('noRoleLevel');
|
||||
return
|
||||
}
|
||||
|
||||
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}'")
|
||||
|
||||
session.setAttribute('contextClassRefToSet', 'urn:qa.agov.ch:names:tc:ac:classes:100')
|
||||
response.setResult('ok')
|
||||
return
|
||||
}
|
||||
|
||||
// Transform sex to number
|
||||
if(session.get('ch.nevis.idm.User.gender') == 'MALE'){
|
||||
session.setAttribute('ch.nevis.idm.User.gender', '1')
|
||||
}
|
||||
if(session.get('ch.nevis.idm.User.gender') == 'FEMALE'){
|
||||
session.setAttribute('ch.nevis.idm.User.gender', '2')
|
||||
}
|
||||
if(session.get('ch.nevis.idm.User.gender') == 'OTHER'){
|
||||
session.setAttribute('ch.nevis.idm.User.gender', '3')
|
||||
}
|
||||
|
||||
|
||||
for (String role : getUserAGOVLoiRoles()) {
|
||||
if (role.startsWith('level')) {
|
||||
def roleLevel = role.substring(5)
|
||||
int roleLevelNumber = Integer.parseInt(roleLevel)
|
||||
if (highestRoleLevelNumber == 0) {
|
||||
highestRoleLevelNumber = roleLevelNumber
|
||||
}
|
||||
if (highestRoleLevelNumber< roleLevelNumber) {
|
||||
highestRoleLevelNumber=roleLevelNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG.debug('Highest role Level' + highestRoleLevelNumber.toString() +' contextclassref' + requestedRoleLevelNumber.toString())
|
||||
LOG.debug(' Compare' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
|
||||
|
||||
//set attribute Actual Role Level
|
||||
session.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
|
||||
LOG.info('actual role level (agov) '+ highestRoleLevelNumber)
|
||||
|
||||
if (highestRoleLevelNumber > 0) {
|
||||
// set attribute contextClassRefToSet
|
||||
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))
|
||||
} else {
|
||||
// by default 100
|
||||
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:100' )
|
||||
}
|
||||
|
||||
if (highestRoleLevelNumber>=requestedRoleLevelNumber) {
|
||||
|
||||
// set attribute ValidFrom and ValidTo (only for higher than 100)
|
||||
if (highestRoleLevelNumber > 100) {
|
||||
def validFrom = getUserAGOVLoiValidFrom('level'.concat(highestRoleLevelNumber.toString()))
|
||||
def validTo = getUserAGOVLoiValidTo('level'.concat(highestRoleLevelNumber.toString()))
|
||||
|
||||
LOG.debug('ValidFrom :' + validFrom)
|
||||
LOG.debug('ValidTo :' + validTo)
|
||||
|
||||
if(validFrom != '') {
|
||||
session.setAttribute('ValidFrom', '' + validFrom)
|
||||
}
|
||||
if(validTo != '') {
|
||||
session.setAttribute('ValidTo', '' + validTo)
|
||||
}
|
||||
}
|
||||
response.setResult('ok')
|
||||
return;
|
||||
} else {
|
||||
// Insufficient_LoaInfo
|
||||
response.setResult('insufficientLoa');
|
||||
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}'")
|
||||
// AuthnFailed_Zero_RoleLvl
|
||||
response.setResult('noRoleLevel');
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//import ch.nevis.esauth.util.httpclient.api.HttpClient;
|
||||
//import ch.nevis.esauth.util.httpclient.api.HttpClients;
|
||||
//import ch.nevis.esauth.util.httpclient.api.Http;
|
||||
|
||||
def url = parameters.get('url')
|
||||
//def payload = parameters.get('json')
|
||||
//def url = "https://me.agov-d.azure.adnovum.net:48081/utility/api/v1/email/031"
|
||||
def email = inargs['email']
|
||||
def language = session['ch.nevis.session.user.language'] ?: 'en'
|
||||
def payload = '{ "email": "' + email + '", "language": "' + language + '"}'
|
||||
|
||||
try {
|
||||
def httpClient = HttpClients.create(parameters)
|
||||
def httpResponse = Http.post()
|
||||
.url(url)
|
||||
.header("Accept", "application/json")
|
||||
.entity(Http.entity()
|
||||
.content(payload)
|
||||
.contentType("application/json")
|
||||
// .charSet("utf-8")
|
||||
.build())
|
||||
.build()
|
||||
.send(httpClient)
|
||||
|
||||
LOG.info('Response Message: ' + httpResponse.reasonPhrase())
|
||||
LOG.info('Response Status Code: ' + httpResponse.code())
|
||||
LOG.info('Response: ' + httpResponse.bodyAsString())
|
||||
|
||||
if (httpResponse.code() == 200) {
|
||||
response.setResult('ok')
|
||||
} else {
|
||||
LOG.error('Unexcpected HTTP response code: ' + httpResponse.code())
|
||||
response.setResult('error')
|
||||
response.setError(1, 'Unexpected HTTP reponse')
|
||||
}
|
||||
} catch (all) {
|
||||
// Handle exception and set the transition
|
||||
LOG.error('error: ' + all, all)
|
||||
response.setResult('error')
|
||||
response.setError(1, 'Exception during HTTP call')
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.jwt.JWTToken" final="false" resumeState="false">
|
||||
<ResultCond name="ok" next="${state.entry}_decryptCode"/>
|
||||
<Response value="AUTH_ERROR"/>
|
||||
<property name="token.algorithm" value="${param.token.algorithm}"/>
|
||||
<property name="out.issuer" value="ag"/>
|
||||
<property name="out.subject" value="${sess:ch.adnovum.nevisidm.user.extId}"/>
|
||||
<property name="out.custom.sessionId" value="${sess:ch.nevis.session.conversationId}"/>
|
||||
<property name="out.include.not_before" value="true"/>
|
||||
<property name="out.time_to_live" value="${param.token.time_to_live}"/>
|
||||
<property name="keystoreref" value="${param.token.keystoreref}"/>
|
||||
<property name="keyobjectref" value="${param.token.keyobjectref }"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_decryptCode" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false" resumeState="true">
|
||||
<ResultCond name="default" next="${state.entry}_Process"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="cryptoKey" value="secret://8jzQ1+F4HHvx7/tKFYRZb2/hFmyXjzt1HXgMJz+Tb16qSMh5Yv2QNnDH0JqsXHAoqtvZu1Nlo5A="/>
|
||||
<property name="cryptoAlgorithm" value="AES/CTR/PKCS5Padding"/>
|
||||
<property name="${sess:agov.new.recovery.code.cipher}?notes:agov.new.recovery.code:decrypt-b64" value="${sess:agov.new.recovery.code.cipher}"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_Process" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false" resumeState="false">
|
||||
<ResultCond name="done" next="${state.done}"/>
|
||||
<ResultCond name="encryptCode" next="${state.entry}_encryptCode"/>
|
||||
<ResultCond name="failed" next="${state.failed}"/>
|
||||
<Response value="AUTH_CONTINUE">
|
||||
<Gui name="recovery_code" label="general.entryCode">
|
||||
<GuiElem name="isiwebpasswd" type="hidden" value="#{ notes.getProperty('agov.new.recovery.code', inargs.getProperty('isiwebpasswd')) }" optional="true"/>
|
||||
<GuiElem name="validTil" type="hidden" value="${sess:agov.new.recovery.code.validTil}" optional="true"/>
|
||||
<GuiElem name="pdfAuthToken" type="hidden" value="${sess:agov.new.recovery.code.pdfAuthToken}" length="4096" optional="true"/>
|
||||
<GuiElem name="authRequestId" type="hidden" value="${sess:ch.nevis.auth.saml.request.id}" optional="true"/>
|
||||
<GuiElem name="submit" type="button" label="continue.button.label" value="submit"/>
|
||||
</Gui>
|
||||
</Response>
|
||||
<property name="scriptTraceGroup" value="AGOV-ACCT"/>
|
||||
<property name="script" value="file:///var/opt/nevisauth/default/conf/ensureRecoveryCode.groovy"/>
|
||||
<property name="parameter.utility-service.baseUrl" value="${param.utility-service.baseUrl}"/>
|
||||
<!--property name="parameter.idm.baseUrl" value="https://${param.idm-service:idm.adn-agov-nevisidm-01-dev}:8989/nevisidm/api"/-->
|
||||
<property name="parameter.idm.httpclient.tls.trustStoreRef" value="Ensure_Recovery_Code"/>
|
||||
<property name="parameter.cookie.domain" value="${param.cookie.domain}"/>
|
||||
</AuthState>
|
||||
<AuthState name="${state.entry}_encryptCode" class="ch.nevis.esauth.auth.states.standard.TransformAttributes" final="false" resumeState="false">
|
||||
<ResultCond name="default" next="${state.entry}_Process"/>
|
||||
<Response value="AUTH_CONTINUE"/>
|
||||
<property name="cryptoKey" value="secret://8jzQ1+F4HHvx7/tKFYRZb2/hFmyXjzt1HXgMJz+Tb16qSMh5Yv2QNnDH0JqsXHAoqtvZu1Nlo5A="/>
|
||||
<property name="cryptoAlgorithm" value="AES/CTR/PKCS5Padding"/>
|
||||
<property name="!${sess:agov.new.recovery.code.cipher}?sess:agov.new.recovery.code.cipher:encrypt-b64" value="${notes:agov.new.recovery.code}"/>
|
||||
</AuthState>
|
|
@ -0,0 +1,105 @@
|
|||
import ch.nevis.esauth.auth.engine.AuthResponse
|
||||
import ch.nevis.idm.client.IdmRestClient
|
||||
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'
|
||||
def requestId = session['ch.nevis.auth.saml.request.id'] ?: 'unknown'
|
||||
def requestedAq = session['agov.requestedRoleLevel'] ?: 'unknown'
|
||||
def user = session['ch.adnovum.nevisidm.user.extId'] ?: 'unknown'
|
||||
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 endPoint = "${parameters.get('utility-service.baseUrl')}/api/v1/recovery/code"
|
||||
|
||||
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.context.text() == 'RECOVERY'}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
// 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')
|
||||
}
|
||||
|
||||
// 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'])
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
response.setResult('encryptCode')
|
||||
return
|
||||
}
|
||||
|
||||
if (inargs['submit']) {
|
||||
def agovRecoveryCodeCookie = "agovRecoveryCode=deleted; Domain=${parameters.get('cookie.domain')}; Path=/; Max-Age=0; SameSite=Strict; Secure; HttpOnly"
|
||||
response.setHeader('Set-Cookie', agovRecoveryCodeCookie)
|
||||
response.setResult('done')
|
||||
return
|
||||
}
|
||||
|
||||
// show the GUI
|
||||
response.setStatus(AuthResponse.AUTH_CONTINUE)
|
|
@ -0,0 +1,15 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "632ae3e34c70513c4d5ae882"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
|
||||
name: "ATB_Key_Signer"
|
||||
label: "STS"
|
||||
notes: "need to add truststore"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-FLORIAN"
|
||||
sourcePatternId: "632ae3e34c70513c4d5ae882"
|
||||
author: "florip"
|
||||
lastCopied: "2023-03-30T11:10:57Z"
|
||||
properties:
|
||||
trustStore:
|
||||
- "pattern://8052fd68f4a663629d651f7b"
|
|
@ -0,0 +1,11 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "cdbb957d49fdc6695a978265"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.WebApplicationAccess"
|
||||
name: "App_Icon_Application"
|
||||
properties:
|
||||
host:
|
||||
- "pattern://1f0702aaabef60a615abf41f"
|
||||
path: "/app-info/"
|
||||
backends: "var://backendAppIconUrl"
|
||||
allowedMethods: "GET"
|
|
@ -0,0 +1,7 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "0eb5c0c45d7239987a22435a"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.AuthenticationDone"
|
||||
name: "Auth_Done"
|
||||
label: "UTILS"
|
||||
properties: {}
|
|
@ -0,0 +1,7 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "473f9d6b4ab9d61c1eb8c689"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.AuthenticationFailed"
|
||||
name: "Auth_Failed"
|
||||
label: "UTILS"
|
||||
properties: {}
|
|
@ -0,0 +1,29 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "4fcfadb4a5c946ead7e6e995"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.NevisAuthRealm"
|
||||
name: "Auth_Realm_Main_IDP"
|
||||
label: "AUTH"
|
||||
notes: "Every normal authentication on the IdP starts with a SAMLRequest.\nIf such\
|
||||
\ a request is present in the ianrgs, and another still ongoing (request.id in\
|
||||
\ the session), we reset the state engine and start by processing that request."
|
||||
properties:
|
||||
authenticate:
|
||||
- "pattern://68665057549fd887ea09fb86"
|
||||
auth:
|
||||
- "pattern://7022472ae407577ae604bbb8"
|
||||
authParams:
|
||||
- RecheckAuthentication: "On"
|
||||
- RenewIdentification: "false"
|
||||
- RenegotiateCookieOnAuthContinue: "false"
|
||||
logrend:
|
||||
- "pattern://097929211988398a87bcbb0c"
|
||||
template: "res://4fcfadb4a5c946ead7e6e995#template"
|
||||
labels: "res://4fcfadb4a5c946ead7e6e995#labels"
|
||||
sessionTracking: "COOKIE"
|
||||
cookieName: "agov"
|
||||
initialSessionTimeout: "var://idp-authentication-session-timeout"
|
||||
sessionTimeout: "30m"
|
||||
langCookieDomain: "var://auth_realm_main_idp-language-cookie-domain"
|
||||
resetAuthenticationCondition: "#{ (inargs.containsKey('SAMLRequest') and session.containsKey('ch.nevis.auth.saml.request.id'))\
|
||||
\ ? 'restart' : '' }"
|
|
@ -0,0 +1,19 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "5d7dc3d51416356293a239f7"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.GenericAuthRealm"
|
||||
name: "Auth_Realm_Main_STS"
|
||||
label: "STS"
|
||||
properties:
|
||||
authStatesFile: "res://5d7dc3d51416356293a239f7#authStatesFile"
|
||||
parameters: "var://auth_realm_main_sts_parameters"
|
||||
resources: "res://5d7dc3d51416356293a239f7#resources"
|
||||
keyObjects:
|
||||
- "pattern://632ae3e34c70513c4d5ae882"
|
||||
auth:
|
||||
- "pattern://4bad2fe3ccc54716cc87138f"
|
||||
logrend:
|
||||
- "pattern://d19fe773e8f9ae00504352da"
|
||||
initialSessionTimeout: "30s"
|
||||
sessionTimeout: "30s"
|
||||
maxSessionLifetime: "60m"
|
|
@ -0,0 +1,21 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "cb8c63274fe346280de0ffd5"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisfido.inband.patterns.InBandMobileAuthenticationRealm"
|
||||
name: "Auth_Realm_Mobile_FIDO_UAF "
|
||||
label: "AUTH"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-FLORIAN"
|
||||
sourcePatternId: "cb8c63274fe346280de0ffd5"
|
||||
author: "florip"
|
||||
lastCopied: "2023-04-27T13:03:12Z"
|
||||
properties:
|
||||
tokens:
|
||||
- "pattern://94e0b7b92ff2593f958c1eec"
|
||||
nevisfido:
|
||||
- "pattern://ca92034f995b39fde562293c"
|
||||
auth:
|
||||
- "pattern://7022472ae407577ae604bbb8"
|
||||
logrend:
|
||||
- "pattern://097929211988398a87bcbb0c"
|
||||
initialSessionTimeout: "var://idp-authentication-session-timeout"
|
|
@ -0,0 +1,15 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "0b3ce3ceec7bfca3ea524983"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.TransformVariablesStep"
|
||||
name: "AuthnFailed_Client_NotFound"
|
||||
label: "UTILS"
|
||||
notes: "Display screen : informing the user that a problem blocking his account\
|
||||
\ has been encountered\n"
|
||||
properties:
|
||||
variables:
|
||||
- notes:saml.errorCode: "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"
|
||||
- notes:saml.errorMessage: "permanent error, not linked to user, but to system\
|
||||
\ , Request ID: ${request:traceId}"
|
||||
onSuccess:
|
||||
- "pattern://4c65de021d362462324a3a5f"
|
|
@ -0,0 +1,18 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "3cc9ad9d0cc771665881abcc"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.TransformVariablesStep"
|
||||
name: "AuthnFailed_LockedAccount"
|
||||
label: "UTILS"
|
||||
notes: "Display screen : informing the user that a problem blocking his account\
|
||||
\ has been encountered\n\nmissing info : if you didn't lock the account then go\
|
||||
\ consult the support page for unlocking the account\n\nErrors : 1: user verification\
|
||||
\ failed (user not found); 98: account disabled or archived (98 not in use yet)"
|
||||
properties:
|
||||
variables:
|
||||
- notes:saml.errorCode: "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"
|
||||
- notes:saml.errorMessage: "Your account is locked , Request ID: ${request:transferId}"
|
||||
- notes:saml.errorInfo: "If you didn't lock the account then go consult the support\
|
||||
\ page for unlocking the account"
|
||||
onSuccess:
|
||||
- "pattern://473f9d6b4ab9d61c1eb8c689"
|
|
@ -0,0 +1,18 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "50b861438e79c2332862d3ca"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.TransformVariablesStep"
|
||||
name: "AuthnFailed_Zero_RoleLvl"
|
||||
label: "UTILS"
|
||||
notes: "Display screen : informing the user that a problem blocking his account\
|
||||
\ has been encountered\n\nmissing info : if you didn't lock the account then go\
|
||||
\ consult the support page for unlocking the account"
|
||||
properties:
|
||||
variables:
|
||||
- notes:saml.errorCode: "urn:oasis:names:tc:SAML:2.0:status:AuthnFailed"
|
||||
- notes:saml.errorMessage: "Your account verification status is expired or invalid\
|
||||
\ , Request ID: ${request:traceId}"
|
||||
- notes:saml.errorInfo: "Please consult the support page on how to check your\
|
||||
\ account status"
|
||||
onSuccess:
|
||||
- "pattern://4c65de021d362462324a3a5f"
|
|
@ -0,0 +1,16 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "2cdd910036aa06b102863a4f"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GroovyScriptStep"
|
||||
name: "CheckLoa"
|
||||
label: "LOA"
|
||||
properties:
|
||||
scriptFile: "res://2cdd910036aa06b102863a4f#scriptFile"
|
||||
onSuccess:
|
||||
- "pattern://594764b3b866d7855f6990a1"
|
||||
onFailure:
|
||||
- "pattern://50b861438e79c2332862d3ca"
|
||||
customSteps:
|
||||
- "pattern://d1298ac82e8bab66583d5571"
|
||||
- "pattern://1d38203c48e017b5b3812385"
|
||||
scriptTraceGroup: "AGOV-ACCT"
|
|
@ -0,0 +1,10 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "bcfe78c02cbe0588528bc3cb"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
|
||||
name: "ClusterInternalTlsTrustObject"
|
||||
properties:
|
||||
keyObjectId: "TlsTrustStore"
|
||||
type: "truststore"
|
||||
trustStore:
|
||||
- "pattern://ff188ae9f50527ef19eccd2c"
|
|
@ -0,0 +1,11 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "bcca48cd422668aa2f78ea42"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.URLHandler"
|
||||
name: "Correct_Path_to_static_Ressources"
|
||||
label: "UTILS"
|
||||
properties:
|
||||
redirects:
|
||||
- /SAML2/SSO/undefined/(.*): "/nevislogrend/nevislogrend/applications/Auth_Realm_Main_IDP/webdata/$1"
|
||||
subPaths: "SAML2/SSO/"
|
||||
phase: "AFTER_SANITATION"
|
|
@ -0,0 +1,22 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "ecf4381f4653b0aa9a69b417"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.GenericHostContextSettings"
|
||||
name: "DefaulErrorPages"
|
||||
label: "UTILS"
|
||||
properties:
|
||||
filters: "<filter>\n <filter-name>DefaultErrorFilter</filter-name>\n <filter-class>ch::nevis::isiweb4::filter::error::ErrorFilter</filter-class>\n\
|
||||
\ <init-param>\n <param-name>StatusCode</param-name>\n <param-value>\n\
|
||||
\ 400:file:/resources/errorPages/404.html:reset-header:reset-status-code\n\
|
||||
\ 403:file:/resources/errorPages/403.html:reset-header:reset-status-code\n\
|
||||
\t 404:file:/resources/errorPages/404.html:reset-header:reset-status-code\n\
|
||||
\ 408:file:/resources/errorPages/timeout.html:reset-header:reset-status-code\n\
|
||||
\ 500:file:/resources/errorPages/500.html:reset-header:reset-status-code\n\
|
||||
\ 502:file:/resources/errorPages/502.html:reset-header:reset-status-code\n\
|
||||
\ </param-value>\n </init-param>\n <init-param>\n <param-name>CheckAcceptHeader</param-name>\n\
|
||||
\ <param-value>true</param-value>\n </init-param>\n <init-param>\n\
|
||||
\ <param-name>PlaceHolders</param-name>\n <param-value>\n \
|
||||
\ TransferIdHolder:TRANSFER_ID\n TimestampHolder:TIMESTAMP\n\
|
||||
\ </param-value>\n </init-param>\n</filter>"
|
||||
filterMappings: "automatic"
|
||||
phase: "START"
|
|
@ -0,0 +1,8 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "a086e62f29aabe721b8b9e3b"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.GenericServiceSettings"
|
||||
name: "Default_CSRF_Protection"
|
||||
properties:
|
||||
filters: "<filter-mapping>\n <filter-name>CSRF_Default</filter-name>\n <url-pattern>${service.path}</url-pattern>\n\
|
||||
</filter-mapping>"
|
|
@ -0,0 +1,8 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "ff188ae9f50527ef19eccd2c"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.PemTrustStoreProvider"
|
||||
name: "ENV_CA"
|
||||
label: "STORE"
|
||||
properties:
|
||||
truststoreFile: "var://env_ca-trusted-certificates"
|
|
@ -0,0 +1,15 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "e3cac41e75980361d7d26bde"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Email Input"
|
||||
label: "FIDO2"
|
||||
properties:
|
||||
authStatesFile: "res://e3cac41e75980361d7d26bde#authStatesFile"
|
||||
onSuccess:
|
||||
- "pattern://699f22cf1cd4ad08bd973f31"
|
||||
onFailure:
|
||||
- "pattern://f63c475c35b616b7c6c1901c"
|
||||
nextSteps:
|
||||
- "pattern://826166d230a6a4849f2837ae"
|
||||
resources: "res://e3cac41e75980361d7d26bde#resources"
|
|
@ -0,0 +1,17 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "f393012a278e525956a362d3"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Ensure_Account_State"
|
||||
notes: "NOTE/haburger/2023-11-29: we continue, even if there was an error during\
|
||||
\ the recovery code generation"
|
||||
properties:
|
||||
authStatesFile: "res://f393012a278e525956a362d3#authStatesFile"
|
||||
parameters: "var://extid_user_verify-template-parameters"
|
||||
onSuccess:
|
||||
- "pattern://9ff0369f3cf662f95d94ff09"
|
||||
onFailure:
|
||||
- "pattern://4c65de021d362462324a3a5f"
|
||||
resources: "res://f393012a278e525956a362d3#resources"
|
||||
keyObjects:
|
||||
- "pattern://bcfe78c02cbe0588528bc3cb"
|
|
@ -0,0 +1,17 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "9ff0369f3cf662f95d94ff09"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Ensure_Recovery_Code"
|
||||
notes: "NOTE/haburger/2023-11-29: we continue, even if there was an error during\
|
||||
\ the recovery code generation"
|
||||
properties:
|
||||
authStatesFile: "res://9ff0369f3cf662f95d94ff09#authStatesFile"
|
||||
parameters: "var://ensure_recovery_code-parameters"
|
||||
onSuccess:
|
||||
- "pattern://2cdd910036aa06b102863a4f"
|
||||
onFailure:
|
||||
- "pattern://2cdd910036aa06b102863a4f"
|
||||
resources: "res://9ff0369f3cf662f95d94ff09#resources"
|
||||
keyObjects:
|
||||
- "pattern://bcfe78c02cbe0588528bc3cb"
|
|
@ -0,0 +1,36 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "302b0fa3c5c3d1d17e9b1004"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "FIDO2_Authentication"
|
||||
label: "FIDO2"
|
||||
notes: "Taken from the pattern FIDO2 Authentication (SAML IDP FIDO2 Authentication)\
|
||||
\ and changed only one line in the fido2_auth.groovy script.\n\n<AuthState name=\"\
|
||||
Auth_Realm_Main_IDP_TEMP_FIDO2AUTH\" class=\"ch.nevis.auth.fido.fido2.authstate.Fido2AuthState\"\
|
||||
\ final=\"false\">\n <ResultCond name=\"cancelled\" next=\"Auth_Realm_Main_IDP_Auth_Failed\"\
|
||||
/>\n <ResultCond name=\"ok\" next=\"Auth_Realm_Main_IDP_CheckLoa\"\
|
||||
/>\n <Response value=\"AUTH_CONTINUE\">\n <Gui name=\"\
|
||||
fido2_auth_std\" label=\"title.login.fido2\">\n <GuiElem name=\"\
|
||||
info\" type=\"info\" label=\"info.login.fido2\"/>\n <GuiElem\
|
||||
\ name=\"username\" type=\"hidden\" value=\"#{session['ch.adnovum.nevisidm.user.extId']\
|
||||
\ != null ? session['ch.adnovum.nevisidm.user.extId'] : session['ch.nevis.idm.User.extId']\
|
||||
\ != null ? session['ch.nevis.idm.User.extId'] : request.getUserId() != null ?\
|
||||
\ request.getUserId() : notes['userid']}\"/>\n </Gui>\n \
|
||||
\ <Arg name=\"fido2AuthenticationOptionsPath\" value=\"/nevisfido/fido2/attestation/options\"\
|
||||
/>\n <Arg name=\"fido2AuthenticationPath\" value=\"/nevisfido/fido2/assertion/result\"\
|
||||
/>\n <Arg name=\"fido2StatusServicePath\" value=\"/nevisfido/fido2/status\"\
|
||||
/>\n <Arg name=\"fido2UserVerification\" value=\"required\"/>\n\
|
||||
\ </Response>\n <property name=\"clientResult\" value=\"\
|
||||
${inargs:cancel_fido2:^true$:cancelled}\"/>\n <property name=\"fido2UserName\"\
|
||||
\ value=\"${inargs:o.username.v}\"/>\n <property name=\"fido2ServerUrl\"\
|
||||
\ value=\"https://fido2:9443/nevisfido/\"/>\n </AuthState>"
|
||||
properties:
|
||||
authStatesFile: "res://302b0fa3c5c3d1d17e9b1004#authStatesFile"
|
||||
parameters: "var://authentication-parameters"
|
||||
onSuccess:
|
||||
- "pattern://1a7583c6caa3b5c36599b25e"
|
||||
onFailure:
|
||||
- "pattern://af4ec934e8efbef422f03926"
|
||||
nextSteps:
|
||||
- "pattern://e3cac41e75980361d7d26bde"
|
||||
resources: "res://302b0fa3c5c3d1d17e9b1004#resources"
|
|
@ -0,0 +1,13 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "887ada57500885703a4a9408"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.TransformVariablesStep"
|
||||
name: "FIDO2_ResetSessionInfos"
|
||||
notes: "TODO/haburger/2024-AUG-20: remove after migration to Friendly Captcha is\
|
||||
\ done"
|
||||
properties:
|
||||
variables:
|
||||
- sess:agov.fido2.X-ReCAPTCHA-Integration: ""
|
||||
emptyValue: "remove-variable"
|
||||
onSuccess:
|
||||
- "pattern://f39352769cb2a1c88e1a176d"
|
|
@ -0,0 +1,36 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "ca92034f995b39fde562293c"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisfido.deployable.patterns.NevisFIDODeployable"
|
||||
name: "FIDO_UAF_Instance"
|
||||
deploymentHosts: "fido-uaf"
|
||||
label: "UAF"
|
||||
notes: "/!\\ client name needs to be the name and not the ID\n\n"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-FLORIAN"
|
||||
sourcePatternId: "ca92034f995b39fde562293c"
|
||||
author: "florip"
|
||||
lastCopied: "2023-04-27T13:03:12Z"
|
||||
properties:
|
||||
logging:
|
||||
- "pattern://af4f8f438a9955bc7abec1f3"
|
||||
frontendAddress: "var://new-nevisfido-uaf-instance-frontend-address"
|
||||
frontendTrustStore:
|
||||
- "pattern://69948a66429d85d971608411"
|
||||
signerTrustStore:
|
||||
- "pattern://55bf63a1b1716e9631f7080d"
|
||||
database:
|
||||
- "pattern://9385d1b33aefe975fb1c5914"
|
||||
facets: "var://fido_uaf_instance-facets"
|
||||
firebaseServiceAccount: "var://fido_uaf_instance-firebase-configuration"
|
||||
firebaseProxyAddress: "var://fido_uaf_instance-firebase-proxy-url"
|
||||
link: "Custom URI"
|
||||
customURILink: "var://fido_uaf_instance-custom-uri-link"
|
||||
nevisidm:
|
||||
- "pattern://b8a36646f81c3247cdb5d90b"
|
||||
client: "var://fido_uaf_instance-client-id"
|
||||
registrationTokenTimeout: "var://fido-uaf-out-of-band-timeout"
|
||||
authenticationTokenTimeout: "var://fido-uaf-out-of-band-timeout"
|
||||
addons:
|
||||
- "pattern://6c7076da1508f186394a3bd2"
|
||||
- "pattern://90af8358cc587f5c5aa79fec"
|
|
@ -0,0 +1,20 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "4e8cb939780070d6dd185204"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisfido.devicederegistration.patterns.MobileDeviceDeregistration"
|
||||
name: "FIDO_UAF_Mobile_Deregistration"
|
||||
label: "UAF"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-FLORIAN"
|
||||
sourcePatternId: "4e8cb939780070d6dd185204"
|
||||
author: "florip"
|
||||
lastCopied: "2023-04-27T13:03:12Z"
|
||||
properties:
|
||||
host:
|
||||
- "pattern://1f0702aaabef60a615abf41f"
|
||||
realm:
|
||||
- "pattern://cb8c63274fe346280de0ffd5"
|
||||
token:
|
||||
- "pattern://94e0b7b92ff2593f958c1eec"
|
||||
nevisfido:
|
||||
- "pattern://ca92034f995b39fde562293c"
|
|
@ -0,0 +1,20 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "116760946160c3cc30abfcab"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisfido.outofband.patterns.OutOfBandMobileDeviceRegistration"
|
||||
name: "FIDO_UAF_Mobile_Registration"
|
||||
label: "UAF"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-FLORIAN"
|
||||
sourcePatternId: "116760946160c3cc30abfcab"
|
||||
author: "florip"
|
||||
lastCopied: "2023-04-27T13:03:12Z"
|
||||
properties:
|
||||
host:
|
||||
- "pattern://1f0702aaabef60a615abf41f"
|
||||
realm:
|
||||
- "pattern://06aeae2d799e492f5580d03b"
|
||||
token:
|
||||
- "pattern://94e0b7b92ff2593f958c1eec"
|
||||
nevisfido:
|
||||
- "pattern://ca92034f995b39fde562293c"
|
|
@ -0,0 +1,10 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "95220b3005deb118adeb01aa"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
|
||||
name: "FIDO_UAF_Truststore"
|
||||
label: "STORE"
|
||||
properties:
|
||||
type: "truststore"
|
||||
trustStore:
|
||||
- "pattern://ff188ae9f50527ef19eccd2c"
|
|
@ -0,0 +1,10 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "69948a66429d85d971608411"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.AutomaticTrustStoreProvider"
|
||||
name: "FIDO_UAF_extended_Frontent_Truststore"
|
||||
label: "UAF"
|
||||
notes: "Used to also as trusstore for the firebase outgoing connection (i.e. trust\
|
||||
\ forward proxy CA if necessary)"
|
||||
properties:
|
||||
truststoreFile: "var://fido_uaf_extended_frontent_truststore-fw_proxy_ca_cert"
|
|
@ -0,0 +1,17 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "9a8294b080ea769d22924af0"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Fetch_Attributes"
|
||||
label: "LOA"
|
||||
notes: "fetch new user attributes and check the new loa level"
|
||||
properties:
|
||||
authStatesFile: "res://9a8294b080ea769d22924af0#authStatesFile"
|
||||
parameters: "var://extid_user_verify-template-parameters"
|
||||
onSuccess:
|
||||
- "pattern://b87d0d2b640e8e545ad70234"
|
||||
onFailure:
|
||||
- "pattern://d1298ac82e8bab66583d5571"
|
||||
nextSteps:
|
||||
- "pattern://50b861438e79c2332862d3ca"
|
||||
resources: "res://9a8294b080ea769d22924af0#resources"
|
|
@ -0,0 +1,10 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "594764b3b866d7855f6990a1"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Fetch_Country_Name"
|
||||
properties:
|
||||
authStatesFile: "res://594764b3b866d7855f6990a1#authStatesFile"
|
||||
onSuccess:
|
||||
- "pattern://b87d0d2b640e8e545ad70234"
|
||||
resources: "res://594764b3b866d7855f6990a1#resources"
|
|
@ -0,0 +1,11 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "1a7583c6caa3b5c36599b25e"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.TransformVariablesStep"
|
||||
name: "Fido2_VariableStep"
|
||||
label: "FIDO2"
|
||||
properties:
|
||||
variables:
|
||||
- sess:authenticatedWith: "urn:qa.agov.ch:names:tc:authfactor:fido"
|
||||
onSuccess:
|
||||
- "pattern://f393012a278e525956a362d3"
|
|
@ -0,0 +1,11 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "56c67433c7a47b6cb06f011a"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.TransformVariablesStep"
|
||||
name: "FidoUAF_VariableStep"
|
||||
label: "UAF"
|
||||
properties:
|
||||
variables:
|
||||
- sess:authenticatedWith: "urn:qa.agov.ch:names:tc:authfactor:accessapp"
|
||||
onSuccess:
|
||||
- "pattern://c686c1bdd5355351f7f98cc8"
|
|
@ -0,0 +1,16 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "7fb39bfd6c34685866a22180"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Fido_Email_Verify"
|
||||
label: "FIDO2"
|
||||
notes: "force next authstate\ndummy state for failed state (hardcoded extId)"
|
||||
properties:
|
||||
authStatesFile: "res://7fb39bfd6c34685866a22180#authStatesFile"
|
||||
parameters: "var://extid_user_verify-template-parameters"
|
||||
onSuccess:
|
||||
- "pattern://302b0fa3c5c3d1d17e9b1004"
|
||||
onFailure:
|
||||
- "pattern://4c65de021d362462324a3a5f"
|
||||
nextSteps:
|
||||
- "pattern://0b3ce3ceec7bfca3ea524983"
|
|
@ -0,0 +1,21 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "2951ead44a7a9362a4545094"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisidm.patterns.NevisIDMDatabase"
|
||||
name: "IDM_DB"
|
||||
label: "IDM"
|
||||
properties:
|
||||
type: "var://idm_db-database-type"
|
||||
hosts: "var://idm_db-database-host"
|
||||
database: "var://idm_db-database-name"
|
||||
rootCredential: "var://idm_db-root-credential"
|
||||
rootCredentialNamespace: "var://idm_db-root-credential-namespace"
|
||||
user: "var://idm_db-database-user"
|
||||
password: "var://idm_db-database-password"
|
||||
encryption: "var://idm_db-tls-encryption"
|
||||
trustStore:
|
||||
- "pattern://326adce95ad1a0761f2259b7"
|
||||
jdbcDriver: "var://idm_db-database-jdbc-driver"
|
||||
oracleVolumeClaimName: "var://idm_db-database-volume-claim"
|
||||
databaseManagement: "var://agov_dev_idm-db-management"
|
||||
connectionUrl: "var://idm_db-database-connection-url"
|
|
@ -0,0 +1,8 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "326adce95ad1a0761f2259b7"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.PemTrustStoreProvider"
|
||||
name: "IDM_DB_TLS_TrustStore"
|
||||
label: "IDM"
|
||||
properties:
|
||||
truststoreFile: "var://idm_db_tls_truststore-trusted-certificates"
|
|
@ -0,0 +1,9 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "93739706b170b426534b8bd5"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisidm.patterns2.NevisIDMPasswordLogin"
|
||||
name: "IDM_Login"
|
||||
label: "IDM"
|
||||
properties:
|
||||
nevisIDM:
|
||||
- "pattern://b8a36646f81c3247cdb5d90b"
|
|
@ -0,0 +1,15 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "71411a755a625f9b850c6cf5"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisidm.patterns.NevisIDMAdvancedSettings"
|
||||
name: "IDM_Settings"
|
||||
label: "IDM"
|
||||
notes: "used for SAMP IDP\n---\nbatch, event, audit processing disabled on that\
|
||||
\ instance"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-JAKOB"
|
||||
sourcePatternId: "71411a755a625f9b850c6cf5"
|
||||
author: "florip"
|
||||
lastCopied: "2023-03-30T08:57:06Z"
|
||||
properties:
|
||||
properties: "var://idm-standard-settings"
|
|
@ -0,0 +1,28 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "c642107fde6b2e07f16bfedb"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.SamlIdp"
|
||||
name: "IDP_AGOV"
|
||||
label: "IDP"
|
||||
notes: "ATB -> mod issuer\nIssuer -> Jakob"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-JAKOB"
|
||||
sourcePatternId: "c642107fde6b2e07f16bfedb"
|
||||
author: "florip"
|
||||
lastCopied: "2023-03-30T08:57:06Z"
|
||||
properties:
|
||||
issuer: "var://idp_agov-saml-issuer"
|
||||
host:
|
||||
- "pattern://1f0702aaabef60a615abf41f"
|
||||
path: "/SAML2/SSO/"
|
||||
realm:
|
||||
- "pattern://4fcfadb4a5c946ead7e6e995"
|
||||
sp:
|
||||
- "pattern://27cefc3861bce987f6766342"
|
||||
samlSigner:
|
||||
- "pattern://56d6268c95f766c86c6aaae9"
|
||||
metadataService: "disabled"
|
||||
authenticationType: "sp-initiated"
|
||||
logoutConfirmation: "disabled"
|
||||
preProcess:
|
||||
- "pattern://7a913eec7f78ce674cd87854"
|
|
@ -0,0 +1,13 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "8052fd68f4a663629d651f7b"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.PemTrustStoreProvider"
|
||||
name: "IDP_PEM_ATB"
|
||||
label: "IDP"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-JAKOB"
|
||||
sourcePatternId: "8052fd68f4a663629d651f7b"
|
||||
author: "florip"
|
||||
lastCopied: "2023-03-30T08:57:06Z"
|
||||
properties:
|
||||
truststoreFile: "var://idp_pem_atb-trusted-certificates"
|
|
@ -0,0 +1,14 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "56d6268c95f766c86c6aaae9"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.PemKeyStoreProvider"
|
||||
name: "IDP_PEM_Signer"
|
||||
label: "IDP"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-JAKOB"
|
||||
sourcePatternId: "56d6268c95f766c86c6aaae9"
|
||||
author: "florip"
|
||||
lastCopied: "2023-03-30T08:57:06Z"
|
||||
properties:
|
||||
keystoreFiles: "var://idp_pem_signer-key-store-content"
|
||||
keyPass: "var://saml-idp-signer-pem-key-store-private-key-passphrase"
|
|
@ -0,0 +1,67 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "27cefc3861bce987f6766342"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.SamlSpConnector"
|
||||
name: "IDP_SP_Connector"
|
||||
label: "IDP"
|
||||
notes: "- Subject NameID Format -> urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n\
|
||||
- dateOfBirth: to have a date suitable for SAML and OIDC, we remove the TimeZone\
|
||||
\ charachter ('1993-03-03Z' --> '1993-03-03')\n- verificationMethod: BUNDBITBK-2892\
|
||||
\ SelfPaid is only for internal use, we remove this from the public assertion\n\
|
||||
- address.verificationMethod: BUNDBITBK-2921 avoid interface change for hotfix"
|
||||
link:
|
||||
sourceProjectKey: "DEFAULT-IAM-JAKOB"
|
||||
sourcePatternId: "27cefc3861bce987f6766342"
|
||||
author: "florip"
|
||||
lastCopied: "2023-03-30T08:57:06Z"
|
||||
properties:
|
||||
issuer: "var://idp_sp_connector-sp-issuer"
|
||||
url: "var://idp_sp_connector-sp-url---assertion-consumer-services"
|
||||
signerTrust:
|
||||
- "pattern://8052fd68f4a663629d651f7b"
|
||||
subjectFormat: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
|
||||
subjectConfirmation: "bearer"
|
||||
attributes:
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: "${sess:ch.nevis.idm.User.email}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/languageOfCorrespondance: "${sess:ch.nevis.idm.User.language}"
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname: "${sess:ch.nevis.idm.User.firstName}"
|
||||
- http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname: "${sess:ch.nevis.idm.User.lastName}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/dateOfBirth: "${sess:ch.nevis.idm.User.birthDate:^(\\\
|
||||
d\\d\\d\\d-\\d\\d-\\d\\d).*$}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/sex: "${sess:ch.nevis.idm.User.gender}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/socialSecurityNumber: "#{\
|
||||
\ (sess['agov.appSvnrAllowed'] == 'true') ? sess['ch.nevis.idm.User.prop.svnr']\
|
||||
\ : ''}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/placeOfBirth: "#{ (sess['agov.appSvnrAllowed']\
|
||||
\ == 'true') ? sess['ch.nevis.idm.User.prop.placeOfBirth'] : ''}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/eIdNumber: "${sess:ch.nevis.idm.User.prop.eIdNumber}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/qa/dateOfVerification: "${sess:ValidFrom}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/qa/validTillDate: "${sess:ValidTo}"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/qa/verificationMethod: "#{\
|
||||
\ ''.concat(sess.get('idVerification')).replace('SelfPaid', '') }"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/nationality: "#{ sess.containsKey('ch.nevis.idm.User.prop.nationality')\
|
||||
\ ? sess['ch.nevis.idm.User.prop.nationality'].toUpperCase(): '' }"
|
||||
- http://schemas.agov.ch/ws/2023/05/identity/claims/authenticatedWith: "${sess:authenticatedWith}"
|
||||
- http://schemas.agov.ch/ws/2023/08/identity/claims/emailVerified: "true"
|
||||
- http://schemas.agov.ch/ws/2023/08/identity/claims/address/street: "#{ (sess['agov.appAddressRequired']\
|
||||
\ == 'true') ? sess['ch.nevis.idm.User.street'] : '' }"
|
||||
- http://schemas.agov.ch/ws/2023/08/identity/claims/address/houseNumber: "#{ (sess['agov.appAddressRequired']\
|
||||
\ == 'true') ? sess['ch.nevis.idm.User.houseNumber'] : '' }"
|
||||
- http://schemas.agov.ch/ws/2023/08/identity/claims/address/zipCode: "#{ (sess['agov.appAddressRequired']\
|
||||
\ == 'true') ? sess['ch.nevis.idm.User.postalCode'] : '' }"
|
||||
- http://schemas.agov.ch/ws/2023/08/identity/claims/address/town: "#{ (sess['agov.appAddressRequired']\
|
||||
\ == 'true') ? sess['ch.nevis.idm.User.city'] : '' }"
|
||||
- http://schemas.agov.ch/ws/2024/02/identity/claims/address/country: "#{ (sess['agov.appAddressRequired']\
|
||||
\ == 'true') ? sess['ch.nevis.idm.User.country'].toUpperCase() : '' }"
|
||||
- http://schemas.agov.ch/ws/2024/02/identity/claims/address/qa/verificationMethod: "#{\
|
||||
\ (sess['agov.appAddressRequired'] == 'true') ? ''.concat(sess.get('agov.adressVerification')).replace('Location',\
|
||||
\ 'Domicile') : '' }"
|
||||
- http://schemas.agov.ch/ws/2024/02/identity/claims/address/countryName: "#{ (sess['agov.appAddressRequired']\
|
||||
\ == 'true') ? sess['agov.countryName'] : ''}"
|
||||
context: "PasswordProtectedTransport"
|
||||
assertionLifetime: "30s"
|
||||
sign:
|
||||
- "Response"
|
||||
- "Assertion"
|
||||
keyInfo: "Certificate"
|
||||
properties: "var://idp-sp-connector-properties"
|
|
@ -0,0 +1,12 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "7a913eec7f78ce674cd87854"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "IDP_Status_Check"
|
||||
label: "IDP"
|
||||
properties:
|
||||
authStatesFile: "res://7a913eec7f78ce674cd87854#authStatesFile"
|
||||
parameters: "var://idp_domain_settings"
|
||||
nextSteps:
|
||||
- "pattern://f63c475c35b616b7c6c1901c"
|
||||
resources: "res://7a913eec7f78ce674cd87854#resources"
|
|
@ -0,0 +1,36 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "8b8167e5de0e69dedb81cacb"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.GenericHostContextSettings"
|
||||
name: "IdP-Cors-Filter"
|
||||
properties:
|
||||
filters: "<filter>\n <filter-name>Lua_${name}</filter-name>\n <filter-class>ch::nevis::isiweb4::filter::lua::LuaFilter</filter-class>\n\
|
||||
\ <init-param>\n <param-name>Script.OutputHeaderFunctionName</param-name>\n\
|
||||
\ <param-value>outputHeader</param-value>\n </init-param>\n <init-param>\n\
|
||||
\ <param-name>Script</param-name>\n <param-value>\n\nfunction\
|
||||
\ outputHeader(request, response)\n trace = request:getTracer()\n if request:getHeader(\"\
|
||||
Origin\") then\n if not response:getHeader(\"Access-Control-Allow-Origin\"\
|
||||
) then\n domains = ${param.cors.allowed.fqdns}\n\n for k,\
|
||||
\ v in pairs(domains) do\n trace:info(\"Accepted domains=\"..v)\n\
|
||||
\ end\n\n domainContainsOrigin = false\n origin\
|
||||
\ = request:getHeader(\"Origin\")\n if (origin ~= nil) then\n \
|
||||
\ origin = origin:match('^%w+://([^/:]+)')\n for index,\
|
||||
\ value in ipairs(domains) do\n if (origin == value) then\n \
|
||||
\ response:setHeader(\"Access-Control-Allow-Origin\", origin)\n\
|
||||
\ response:setHeader(\"Vary\", \"Origin\")\n \
|
||||
\ domainContainsOrigin = true\n end\n end\n \
|
||||
\ if (not domainContainsOrigin) then\n request:getTracer():notice(\"\
|
||||
VA01\", \"HTTP Origin header \" .. origin .. \" does not match one in the domain\
|
||||
\ list \")\n response:setHeader(\"Content-Type\", \"text/plain\"\
|
||||
)\n response:setBody(\"403 Denied\")\n response:send(403)\n\
|
||||
\ return\n end\n end\n trace:info(\"\
|
||||
Proxy allowed CORS for '\"..response:getHeader(\"Access-Control-Allow-Origin\"\
|
||||
)..\"' after receiving a call from origin \"..request:getHeader(\"Origin\"))\n\
|
||||
\ else\n trace:info(\"Backend allowed CORS for '\"..response:getHeader(\"\
|
||||
Access-Control-Allow-Origin\")..\"' after receiving a call from origin \"..request:getHeader(\"\
|
||||
Origin\"))\n end\n else\n trace:info(\"Origin header is null\"\
|
||||
)\n end\nend\n\n </param-value>\n </init-param>\n</filter>\n\n<filter-mapping>\n\
|
||||
\ <filter-name>Lua_${name}</filter-name>\n <url-pattern>/SAML2/SSO/*</url-pattern>\n\
|
||||
</filter-mapping>\n"
|
||||
phase: "SANITATION"
|
||||
parameters: "var://idp-cors-filter-template-parameters"
|
|
@ -0,0 +1,14 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "d1298ac82e8bab66583d5571"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
|
||||
name: "Insufficient_LoaInfo"
|
||||
label: "LOA"
|
||||
properties:
|
||||
authStatesFile: "res://d1298ac82e8bab66583d5571#authStatesFile"
|
||||
onSuccess:
|
||||
- "pattern://cdb228eccc12b4b1dea20d9d"
|
||||
onFailure:
|
||||
- "pattern://1793638a6715537e8f491f25"
|
||||
nextSteps:
|
||||
- "pattern://826166d230a6a4849f2837ae"
|
|
@ -0,0 +1,8 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "55bf63a1b1716e9631f7080d"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.AutomaticTrustStoreProvider"
|
||||
name: "Internal-IdP-Auth-Signer-Trust"
|
||||
properties:
|
||||
keystore:
|
||||
- "pattern://aeb2fed9962dcd5f7893db51"
|
|
@ -0,0 +1,9 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "aeb2fed9962dcd5f7893db51"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.AutomaticKeyStoreProvider"
|
||||
name: "Internal-IdP-Auth-Signer"
|
||||
properties:
|
||||
owner:
|
||||
- "pattern://7022472ae407577ae604bbb8"
|
||||
- "pattern://4bad2fe3ccc54716cc87138f"
|
|
@ -0,0 +1,9 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "aec56cb572434a42d55de30c"
|
||||
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.CustomAuthLogFile"
|
||||
name: "Log_Auth"
|
||||
label: "LOGS"
|
||||
properties:
|
||||
logLevel: "var://log_auth-default-log-level"
|
||||
levels: "var://log_auth-log-levels"
|
|
@ -0,0 +1,9 @@
|
|||
schemaVersion: "1.0"
|
||||
pattern:
|
||||
id: "10b2e9ba76058c1dadb3935a"
|
||||
className: "ch.nevis.admin.v4.plugin.fido2.patterns.NevisFIDOLogSettings"
|
||||
name: "Log_FIDO2"
|
||||
label: "LOGS"
|
||||
properties:
|
||||
logLevel: "var://log_fido2-default-log-level"
|
||||
levels: "var://log_fido2-log-levels"
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue