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