Eid & Epr changes from EPR-LAB:

- First eid passthrough implementation
- custom idp which allows epr communication to go over artifact bindings
This commit is contained in:
aca 2025-04-01 09:20:15 +00:00
parent 8197ebd132
commit 20b0cc663b
73 changed files with 1073 additions and 57 deletions

View File

@ -0,0 +1,5 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.standard.AuthGeneric" final="true">
<Response value="AUTH_ERROR">
<Gui name="empty"/>
</Response>
</AuthState>

View File

@ -0,0 +1,68 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.saml.IdentityProviderState" final="false" resumeState="true">
<!-- Auth_Realm_Main_IDP_Concurrent_Logout -->
<ResultCond name="IDP-initiated-ConcurrentLogout" next="${state.exit.1}"/>
<ResultCond name="SP-initiated-ConcurrentLogout" next="${state.exit.1}"/>
<!-- Auth_Realm_Main_IDP_Prepare_Done -->
<ResultCond name="IDP-initiated-SingleLogout" next="${state.done}"/>
<ResultCond name="SP-initiated-SingleLogout" next="${state.done}"/>
<ResultCond name="ok" next="${state.done}"/>
<!-- Auth_Realm_Main_IDP_Logout_Done -->
<ResultCond name="LogoutCompleted" next="${state.exit.2}"/>
<!-- Auth_Realm_Main_IDP_Logout_Fail -->
<ResultCond name="LogoutFailed" next="${state.exit.3}"/>
<!-- Auth_Realm_Main_IDP_RequestedRoleLevel -->
<ResultCond name="authenticate:IDP-initiated-SSO" next="${state.exit.4}"/>
<ResultCond name="authenticate:SP-initiated-SSO" next="${state.exit.4}"/>
<ResultCond name="invalidAssertionConsumerUrl" next="${state.entry}"/>
<!-- Auth_Realm_Main_IDP_Selector -->
<ResultCond name="stepup:IDP-initiated-SSO" next="${state.failed}"/>
<ResultCond name="stepup:SP-initiated-SSO" next="${state.failed}"/>
<Response value="AUTH_ERROR">
<Gui name="saml_idp" label="title.saml.failed">
<GuiElem name="lasterror" type="error" label="error.saml.failed"/>
</Gui>
</Response>
<property name="session.participants-store.key" value="IDP_AGOV-session-participants"/>
<property name="logoutMode" value="ConcurrentLogout-Redirect"/>
<property name="logoutTrigger" value="#{request['currentResource'].contains('logout') || inargs.containsKey('logout') || inargs.containsKey('SAMLLogout')}"/>
<property name="out.binding" value="http-post"/>
<property name="out.post.relayStateEncoding" value="HTML"/>
<property name="out.sign" value="Response Assertion LogoutResponse"/>
<property name="out.signatureKeyInfo" value="Certificate"/>
<property name="out.ttl" value="30"/>
<property name="out.subject" value="${response:userId}"/>
<property name="out.subject.format" value="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
<property name="out.extension.Bearer" value="ch.nevis.esauth.auth.states.saml.extensions.SubjectConfirmationExtender"/>
<property name="out.issuer" value="${var.idp_agov-saml-issuer}"/>
<property name="out.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
<property name="spIssuer" value="${var.idp_sp_epd_connector-sp-issuer}"/>
<property name="spURL" value="${var.idp_sp_epd_connector-sp-url---assertion-consumer-services}"/>
<property name="acsUrlWhitelist.uris" value="${var.idp_sp_epd_connector-sp-url---assertion-consumer-services}"/>
<property name="in.binding" value="auto"/>
<property name="in.max_age" value="60"/>
<property name="in.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.authnContextClassRef" value="${sess:contextClassRefToSet}"/>
<property name="out.audienceRestriction" value="${var.idp_agov_epd-audience}"/>
<!-- SAML Attributes -->
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" value="${sess:ch.nevis.idm.User.firstName}"/>
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" value="${sess:ch.nevis.idm.User.lastName}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/sex" value="${sess:ch.nevis.idm.User.gender}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/dateOfBirth" value="${sess:ch.nevis.idm.User.birthDate:^(\d\d\d\d-\d\d-\d\d).*$}"/>
</AuthState>

View File

@ -162,33 +162,22 @@ try {
for (String role : getUserAGOVLoiRoles()) {
if (role.startsWith('level')) {
def roleLevel = role.substring(5)
int roleLevelNumber = Integer.parseInt(roleLevel)
if (highestRoleLevelNumber< roleLevelNumber) {
highestRoleLevelNumber=roleLevelNumber
}
if (role.startsWith('level')) {
def roleLevel = role.substring(5)
int roleLevelNumber = Integer.parseInt(roleLevel)
if (highestRoleLevelNumber< roleLevelNumber) {
highestRoleLevelNumber=roleLevelNumber
}
}
}
}
LOG.debug('CheckLoa: Highest role Level ' + highestRoleLevelNumber.toString() +' contextclassref ' + requestedRoleLevelNumber.toString())
LOG.debug('CheckLoa: Compare ' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
LOG.debug('CheckLoa: Compare ' + (highestRoleLevelNumber>=requestedRoleLevelNumber))
//set attribute Actual Role Level
session.setAttribute('agov.actualRoleLevel', '' + highestRoleLevelNumber)
LOG.debug('CheckLoa: actual role level (agov) '+ highestRoleLevelNumber)
// Best Token Available only if account's AQlevel is high enough
if ((session.getAttribute('agov.appAddressRequired') == 'true') && (highestRoleLevelNumber < 200)) {
LOG.debug("Best Token: Address requested but account has to low AQ (${highestRoleLevelNumber})")
session.setAttribute('agov.appAddressRequired', 'false')
}
if ((session.getAttribute('agov.appSvnrAllowed') == 'true') && (highestRoleLevelNumber < 400)) {
LOG.debug("Best Token: SVNr requested but account has to low AQ (${highestRoleLevelNumber})")
session.setAttribute('agov.appSvnrAllowed', 'false')
}
if (highestRoleLevelNumber > 0) {
// set attribute contextClassRefToSet
session.setAttribute('contextClassRefToSet','urn:qa.agov.ch:names:tc:ac:classes:' .concat(highestRoleLevelNumber.toString()))

View File

@ -0,0 +1,7 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
<ResultCond name="default" next="${state.done}"/>
<Response value="AUTH_DONE">
<Gui name="ContinueResponse"/>
</Response>
<property name="script" value="file:///var/opt/nevisauth/default/conf/prepare_done.groovy"/>
</AuthState>

View File

@ -0,0 +1,23 @@
// nevisProxy replaces the entire AUTH: scope when new outargs are returned by nevisAuth.
// Thus, we have to store tokens in the session (as a String) and restore them on subsequent step-ups.
// restore tokens
session.each { key, value ->
if (key.startsWith('outarg.token.')) {
def name = key.substring(7)
if (outargs.containsKey(name)) {
LOG.debug("not restoring token (outarg: $name) from session: outarg already set")
}
else {
LOG.debug("restoring token (outarg: $name) from session")
outargs.put(name, value)
}
}
}
// store tokens
outargs.each { name, value ->
if (name.startsWith('token.')) {
session.put('outarg.' + name, value)
}
}

View File

@ -0,0 +1,5 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.standard.AuthGeneric" final="true">
<Response value="AUTH_ERROR">
<Gui name="empty"/>
</Response>
</AuthState>

View File

@ -0,0 +1,13 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
<ResultCond name="ok" next="${state.done}"/>
<ResultCond name="forbidden_0" next="${state.entry}"/>
<ResultCond name="forbidden_1" next="${state.entry}"/>
<ResultCond name="stepup" next="${state.failed}"/>
<Response value="AUTH_ERROR">
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
</Response>
<!-- TODO: Put this in a variable -->
<property name="parameter.paths" value="^http[s]?\u003A//[^/]+/SAML2/SSO/.*$"/>
<property name="script" value="file:///var/opt/nevisauth/default/conf/authorization.groovy"/>
</AuthState>

View File

@ -0,0 +1,180 @@
boolean isEnabled() {
def paths = parameters.get("paths")
if (paths && !paths.isEmpty()) {
for (path in paths.split(',')) {
String url = request.currentResource
if (url.matches(path)) {
return true
}
}
}
return false
}
boolean isLevel(String role) {
if (role != null && role.isNumber()) {
def number = Integer.parseInt(role)
if (number > 0 && number <= 9) {
return true
}
}
return false
}
int getCurrentLevel() {
int level = 1 // level 1 is reached by definition on successful authentication
// levels are stored as roles once the authentication is done
for (String role : response.getActualRoles()) {
if (isLevel(role)) {
Integer number = Integer.parseInt(role)
if (number > level) {
level = number
}
}
}
LOG.debug("current level: $level")
return level
}
Integer getRequestedLevel() {
// try to determine required level based on SAML request (SP-initiated)
def context = session['ch.nevis.auth.saml.request.authnContextClassRef']
if (context == null) {
// this is expected for non-Nevis SAML partners
LOG.debug("unable to determine required authentication level: no AuthnContext")
return null
}
String prefix = 'urn:nevis:level:'
Integer level = null
if (context.contains(prefix)) {
def start = context.indexOf(prefix) // the prefix can appear anywhere in the context but only once
def remainder = context.substring(start + prefix.length())
for (String candidate : remainder.split(',')) {
if (!candidate.isNumber()) {
continue // must be an actual role
}
def number = Integer.parseInt(candidate)
if (level == null || number < level) {
level = number
}
}
}
if (level == null) {
// an AuthnContext has been sent but it does not contain the required authentication level
LOG.debug("unable to determine required authentication level from request: $context")
}
else {
LOG.info("extracted required authentication level from request: $context -> $level")
}
return level
}
Integer getRequiredLevel(levels, String issuer) {
// try to determine required level based on request
def level = getRequestedLevel()
if (level != null) {
LOG.info("required authentication level from request: $level")
return level
}
// else determine required level based on configuration (IDP-initiated or no authnContextClassRef sent)
if (issuer != null && levels.containsKey(issuer)) {
level = levels[issuer]
LOG.debug("required authentication level for issuer $issuer defined as $level")
return level
}
// else return null
LOG.debug("required authentication level for issuer $issuer is not defined")
return null
}
void setAuthnContext() {
def parts = [] as Set
def authLevel = response.getAuthLevel()
if (authLevel != null) {
if (isLevel(authLevel)) {
parts.add("urn:nevis:level:$authLevel")
}
else { // might be legacy auth.weak / auth.strong
parts.add(authLevel)
}
}
for (String role : response.getActualRoles()) {
if (isLevel(role)) { // previous authLevels might have been added to the roles already
parts.add("urn:nevis:level:$role")
}
// levels can also be normal roles so we add them always
parts.add(role)
}
def value = parts.sort().join(",")
LOG.debug("calculated AuthnContextClassRef for SAML Response: $value")
session['saml.idp.response.authncontext'] = value
}
boolean stepupRequired(levels, String issuer) {
Integer requiredLevel = getRequiredLevel(levels, issuer)
if (requiredLevel == null) {
LOG.info("unable to determine required authentication level for request from issuer $issuer")
setAuthnContext()
return false
}
Integer currentLevel = getCurrentLevel()
if (currentLevel >= requiredLevel) {
LOG.info("required authentication level $requiredLevel has been reached (current level $currentLevel)")
setAuthnContext()
return false
}
LOG.info("required authentication level $requiredLevel has not been reached (current level $currentLevel) - session upgrade needed")
request.setRequiredRoles("$requiredLevel")
return true
}
boolean hasAnyRequiredRole(i2r, issuer) {
if (issuer != null && i2r.containsKey(issuer)) {
def roles = i2r[issuer]
for (role in response.getActualRoles()) {
if (roles.contains(role)) {
return true
}
}
}
}
if (!isEnabled()) {
LOG.info("skipping SAML authorization checks.")
response.setResult('ok') // skip execution
return
}
// issuer set by IdentityProviderState (SP-initiated)
def issuer = session['ch.nevis.auth.saml.request.issuer']
// issuer to minimum required authentication level
def i2l = [:]
if (stepupRequired(i2l, issuer)) {
LOG.info("authentication level stepup required.")
response.setResult("stepup")
return // we are done for now
}
// issuer to list of required roles
def i2r = [:]
// issuer to ResultCond name
def i2e = [:]
i2e.put('https://trustbroker.agov-epr-lab.azure.adnovum.net', 'forbidden_0')
i2e.put('https://trustbroker-idp.agov-epr-lab.azure.adnovum.net', 'forbidden_1')
if (!i2r.isEmpty() && !hasAnyRequiredRole(i2r, issuer)) {
LOG.info("required roles check failed.")
response.setResult(i2e[issuer])
return // we are done
}
response.setResult('ok')

View File

@ -0,0 +1,74 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.saml.IdentityProviderState" final="false" resumeState="true">
<!-- Auth_Realm_Main_IDP_Concurrent_Logout -->
<ResultCond name="IDP-initiated-ConcurrentLogout" next="${state.exit.1}"/>
<ResultCond name="SP-initiated-ConcurrentLogout" next="${state.exit.1}"/>
<!-- Auth_Realm_Main_IDP_Prepare_Done -->
<ResultCond name="IDP-initiated-SingleLogout" next="${state.done}"/>
<ResultCond name="SP-initiated-SingleLogout" next="${state.done}"/>
<ResultCond name="ok" next="${state.done}"/>
<!-- Auth_Realm_Main_IDP_Logout_Done -->
<ResultCond name="LogoutCompleted" next="${state.exit.2}"/>
<!-- Auth_Realm_Main_IDP_Logout_Fail -->
<ResultCond name="LogoutFailed" next="${state.exit.3}"/>
<!-- Auth_Realm_Main_IDP_RequestedRoleLevel -->
<ResultCond name="authenticate:IDP-initiated-SSO" next="${state.exit.4}"/>
<ResultCond name="authenticate:SP-initiated-SSO" next="${state.exit.4}"/>
<ResultCond name="invalidAssertionConsumerUrl" next="${state.entry}"/>
<!-- Auth_Realm_Main_IDP_Selector -->
<ResultCond name="stepup:IDP-initiated-SSO" next="${state.failed}"/>
<ResultCond name="stepup:SP-initiated-SSO" next="${state.failed}"/>
<Response value="AUTH_ERROR">
<Gui name="saml_idp" label="title.saml.failed">
<GuiElem name="lasterror" type="error" label="error.saml.failed"/>
</Gui>
</Response>
<property name="session.participants-store.key" value="IDP_AGOV-session-participants"/>
<property name="logoutMode" value="ConcurrentLogout-Redirect"/>
<property name="logoutTrigger" value="#{request['currentResource'].contains('logout') || inargs.containsKey('logout') || inargs.containsKey('SAMLLogout')}"/>
<property name="out.binding" value="http-artifact"/>
<!-- SHA1 of "https://auth.agov-epr-lab.azure.adnovum.net/SAML2/" -->
<property name="out.artifactSourceId" value="0x49899452c60f53e500d7d8b221536c9745dfaf0f"/>
<property name="out.post.relayStateEncoding" value="HTML"/>
<property name="out.sign" value="Response Assertion LogoutResponse ArtifactResponse"/>
<property name="out.signatureKeyInfo" value="Certificate"/>
<property name="out.ttl" value="30"/>
<property name="out.subject" value="${response:userId}"/>
<property name="out.subject.format" value="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
<property name="out.extension.Bearer" value="ch.nevis.esauth.auth.states.saml.extensions.SubjectConfirmationExtender"/>
<!-- TODO: use var -->
<property name="out.issuer" value="${var.idp_agov-saml-issuer}"/>
<property name="out.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
<property name="spIssuer" value="${var.idp_sp_epd_connector-sp-issuer}"/>
<property name="spURL" value="${var.idp_sp_epd_connector-sp-url---assertion-consumer-services}"/>
<property name="acsUrlWhitelist.uris" value="${var.idp_sp_epd_connector-sp-url---assertion-consumer-services}"/>
<property name="in.binding" value="auto"/>
<property name="in.max_age" value="60"/>
<property name="in.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.authnContextClassRef" value="${sess:contextClassRefToSet}"/>
<property name="out.audienceRestriction" value="${var.idp_agov_epd-audience}"/>
<!-- SAML Attributes -->
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" value="${sess:ch.nevis.idm.User.firstName}"/>
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" value="${sess:ch.nevis.idm.User.lastName}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/sex" value="${sess:ch.nevis.idm.User.gender}"/>
<!--<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/socialSecurityNumber" value="#{ (sess['agov.appSvnrAllowed'] == 'true') ? sess['ch.nevis.idm.User.prop.svnr'] : ''}"/> -->
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/dateOfBirth" value="${sess:ch.nevis.idm.User.birthDate:^(\d\d\d\d-\d\d-\d\d).*$}"/>
</AuthState>

View File

@ -0,0 +1,6 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.standard.ConditionalDispatcherState" final="false">
<ResultCond name="nomatch" next="${state.done}"/>
<Response value="AUTH_ERROR">
<Arg name="ch.nevis.isiweb4.response.status" value="403"/>
</Response>
</AuthState>

View File

@ -0,0 +1,21 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
<ResultCond name="confirm" next="${state.done}"/>
<ResultCond name="main" next="${state.exit.1}"/>
<ResultCond name="epd" next="${state.exit.2}"/>
<ResultCond name="epd_artifact" next="${state.exit.3}"/>
<Response value="AUTH_CONTINUE">
<Gui name="saml_dispatcher" label="title.saml.failed">
<GuiElem name="lasterror" type="error" label="error.saml.failed"/>
</Gui>
</Response>
<!-- TODO: variables -->
<property name="parameter.logoutConfirmation" value="false"/>
<property name="parameter.spInitiated" value="true"/>
<property name="parameter.epdMode" value="${param.epd_idp_binding_mode}"/>
<property name="parameter.atb" value="${param.atb}"/>
<property name="parameter.epd_atb" value="${param.epd_atb}"/>
<property name="script" value="file:///var/opt/nevisauth/default/conf/idp_dispatcher.groovy"/>
</AuthState>

View File

@ -0,0 +1,168 @@
import groovy.xml.XmlSlurper
import groovy.xml.slurpersupport.GPathResult
import groovy.xml.slurpersupport.NodeChild
import java.util.zip.Inflater
import java.util.zip.InflaterInputStream
/**
* Gets the value of the Referer header.
* If the header is missing the fallback is returned
*
* This method is used when SAML IDP / Dispatch Error Redirect is not set
*
* @param fallback - value to return if the Referer header is missing
* @return value of header or fallback
*/
def getReferer(String fallback) {
return request.getHttpHeader('Referer') ?: fallback
}
def redirect(String url) {
outargs.put('nevis.transfer.type', 'redirect')
outargs.put('nevis.transfer.destination', url)
}
/**
* Extracts the content of the Issuer element from a parsed SAML message.
* The Issuer is optional according to SAML specification but we need it for dispatching.
*
* @param xml - as parsed by Groovy XmlSlurper
* @return text content of Issuer element converted or null
*/
String getIssuer(GPathResult xml) {
return xml.depthFirst().find { GPathResult node -> {
node.name().endsWith(":Issuer") || node.name().equalsIgnoreCase("Issuer")
}
}?.text()
}
String getIssuer(String value) {
if (value == null) {
return
}
String text
byte[] decoded
def parser = new XmlSlurper()
// if value is raw xml then continue otherwise try to parse the base64 encoding
if (value.startsWith("<")) {
text = new String(value)
}
else {
decoded = value.decodeBase64()
text = new String(decoded)
LOG.info("received SAML request $value")
}
// after decoded, if redirect binding, we need to parse string to xml
if (text.startsWith("<")) {
LOG.debug("assuming POST/SOAP binding")
// plain String (POST/SOAP parameter)
def xml = parser.parseText(text)
return getIssuer(xml)
}
else {
LOG.debug("assuming redirect binding")
// should be deflate encoded (query parameter)
def is = new InflaterInputStream(new ByteArrayInputStream(decoded), new Inflater(true))
def xml = parser.parse(is)
return getIssuer(xml)
}
}
def dispatchIssuer(i2s, String issuer) {
def result = i2s.get(issuer)
if (result == null) {
LOG.info("No SP found for issuer '$issuer'. Hint: check SAML SP Connector patterns.")
}
// dispatch different idp if artifact binding is enabled
if(parameters.get('epdMode') == 'artifact' && result == 'epd'){
LOG.debug("EPD: Artifact mode")
result = result + "_artifact"
}else{
LOG.debug("EPD: POST mode")
}
response.setResult(result)
session.put("saml.inbound.issuer", issuer)
session.put('saml.idp.result', result) // remember decision for sub-sequent requests without a SAML message
}
def dispatchMessage(i2s, String message) {
def issuer = getIssuer(message)
if (issuer == null) {
LOG.info("No issuer found in incoming SAML message. Giving up.")
}
session.put("saml.inbound.issuer", issuer)
dispatchIssuer(i2s, issuer)
}
if (parameters.get('logoutConfirmation') == 'true' && "stepup" == request.getMethod()) {
String url = request.currentResource
def path = new URL(url).getPath()
if (path.endsWith("/logout")) {
// next AuthState will show a logout confirmation GUI
response.setResult('confirm')
return
}
}
// ensure session exists
if (request.getSession(false) == null) {
session = request.getSession(true).getData()
}
// issuer (any case) -> ResultCond name
def i2s = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER)
i2s.put(parameters.get('atb'), 'main')
i2s.put(parameters.get('epd_atb'), 'epd')
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('SAMLRequest')) { // SP-initiated authentication
LOG.debug("found SAMLRequest parameter for SP-initiated authentication")
String message = inargs.get('SAMLRequest')
dispatchMessage(i2s, message)
return
}
if (inargs.containsKey('SAMLResponse')) { // response to IDP-initiated SAML Logout
LOG.debug("found SAMLResponse parameter")
String message = inargs.get('SAMLResponse')
dispatchMessage(i2s, message)
return
}
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('soapheader')) { // SP-initiated SOAP with soapheader
LOG.debug("found soapheader parameter for SP-initiated")
String message = inargs.get('soapheader')
dispatchMessage(i2s, message)
return
}
if (parameters.get('spInitiated') == 'true' && inargs.containsKey('')) { // SP-initiated SOAP with empty
LOG.debug("found empty parameter for SP-initiated SOAP message")
String message = inargs.get('')
dispatchMessage(i2s, message)
return
}
String issuer = inargs['Issuer'] ?: inargs['issuer']
if (parameters.get('idpInitiated') == 'true' && issuer != null) { // IDP-initiated authentication
LOG.debug("found Issuer parameter for IDP-initiated authentication")
dispatchIssuer(i2s, issuer)
return
}
// used as fallback in case of ?logout (we need an IdentityProviderState)
if (inargs.containsKey("logout") && session.containsKey('saml.idp.result')) {
def result = session.get('saml.idp.result')
LOG.debug("dispatching to last used ResultCond: $result")
response.setResult(result)
return
}
def location = getReferer('/')
LOG.info("Unable to dispatch request. Giving up and redirecting (back) to $location")
redirect(location)

View File

@ -0,0 +1,6 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.scripting.ScriptState" final="false">
<Response value="AUTH_CONTINUE">
<Gui name="saml_logout_confirm" label="title.logout.confirmation"/>
</Response>
<property name="script" value="file:///var/opt/nevisauth/default/conf/logout_confirm.groovy"/>
</AuthState>

View File

@ -0,0 +1,64 @@
def redirect(location) {
outargs.put('nevis.transfer.type', 'redirect')
outargs.put('nevis.transfer.destination', location)
}
def getReturnURL() {
if (inargs.containsKey('return')) {
return inargs.get('return')
}
// determine returnURL based on Referer header (if present and not pointing to this page)
def referer = request.getHttpHeader('Referer')
if (referer == null) {
LOG.debug('no Referer header found')
return null
}
// strip query String for comparison
String previous = referer.contains('?') ? referer.substring(0, referer.indexOf("?")) : referer
def current = request.getCurrentResource()
if (current.startsWith(previous)) {
LOG.debug("Referer header $referer cannot be used as return URL - cyclic redirect")
return null
}
return referer
}
if (inargs.containsKey('logout-confirm')) {
def current = request.getCurrentResource()
// user has confirmed logout -> replace /logout with /?logout
String location
if (current.contains('?')) {
location = current.replace("/logout?", "/?logout&")
}
else {
location = current.replace("/logout", "/?logout")
}
redirect(location)
return
}
if (inargs.containsKey('logout-abort')) {
// user has aborted logout -> redirect to stored return URL
def location = session.get('logout-abort-url')
redirect(location)
return
}
// user has not clicked any button -> render GUI
response.setGuiName('saml_logout_confirm')
response.setGuiLabel('title.logout.confirmation')
// not setting a target as the API has been removed
response.addInfoGuiField('info', 'info.logout.confirmation', null)
response.addButtonGuiField('logout-confirm', 'continue.button.label', 'true')
def returnURL = getReturnURL()
if (returnURL != null) {
// store return URL in session
session.put('logout-abort-url', returnURL)
}
if (session.containsKey('logout-abort-url')) {
// add cancel button to go back
response.addButtonGuiField('logout-abort', 'cancel.button.label', 'true')
}

View File

@ -0,0 +1,89 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.saml.IdentityProviderState" final="false" resumeState="true">
<!-- Auth_Realm_Main_IDP_Concurrent_Logout -->
<ResultCond name="IDP-initiated-ConcurrentLogout" next="${state.exit.1}"/>
<ResultCond name="SP-initiated-ConcurrentLogout" next="${state.exit.1}"/>
<!-- Auth_Realm_Main_IDP_Prepare_Done -->
<ResultCond name="IDP-initiated-SingleLogout" next="${state.done}"/>
<ResultCond name="SP-initiated-SingleLogout" next="${state.done}"/>
<ResultCond name="ok" next="${state.done}"/>
<!-- Auth_Realm_Main_IDP_Logout_Done -->
<ResultCond name="LogoutCompleted" next="${state.exit.2}"/>
<!-- Auth_Realm_Main_IDP_Logout_Fail -->
<ResultCond name="LogoutFailed" next="${state.exit.3}"/>
<!-- Auth_Realm_Main_IDP_RequestedRoleLevel -->
<ResultCond name="authenticate:IDP-initiated-SSO" next="${state.exit.4}"/>
<ResultCond name="authenticate:SP-initiated-SSO" next="${state.exit.4}"/>
<ResultCond name="invalidAssertionConsumerUrl" next="${state.entry}"/>
<!-- Auth_Realm_Main_IDP_Selector -->
<ResultCond name="stepup:IDP-initiated-SSO" next="${state.failed}"/>
<ResultCond name="stepup:SP-initiated-SSO" next="${state.failed}"/>
<Response value="AUTH_ERROR">
<Gui name="saml_idp" label="title.saml.failed">
<GuiElem name="lasterror" type="error" label="error.saml.failed"/>
</Gui>
</Response>
<property name="session.participants-store.key" value="IDP_AGOV-session-participants"/>
<property name="logoutMode" value="ConcurrentLogout-Redirect"/>
<property name="logoutTrigger" value="#{request['currentResource'].contains('logout') || inargs.containsKey('logout') || inargs.containsKey('SAMLLogout')}"/>
<property name="out.binding" value="http-post"/>
<property name="out.post.relayStateEncoding" value="HTML"/>
<property name="out.sign" value="Response Assertion"/>
<property name="out.signatureKeyInfo" value="Certificate"/>
<property name="out.ttl" value="30"/>
<property name="out.subject" value="${response:userId}"/>
<property name="out.subject.format" value="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
<property name="out.extension.Bearer" value="ch.nevis.esauth.auth.states.saml.extensions.SubjectConfirmationExtender"/>
<!-- TODO: use var -->
<property name="out.issuer" value="${var.idp_agov-saml-issuer}"/>
<property name="out.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
<!-- TODO: use var -->
<property name="spIssuer" value="${var.idp_sp_connector-sp-issuer}"/>
<property name="spURL" value="${var.idp_sp_connector-sp-url---assertion-consumer-services}"/>
<property name="acsUrlWhitelist.uris" value="${var.idp_sp_connector-sp-url---assertion-consumer-services}"/>
<property name="in.binding" value="auto"/>
<property name="in.max_age" value="60"/>
<property name="in.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.authnContextClassRef" value="${sess:contextClassRefToSet}"/>
<property name="out.audienceRestriction" value="${var.idp_agov-audience}"/>
<!-- SAML Attributes -->
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress" value="${sess:ch.nevis.idm.User.email}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/languageOfCorrespondance" value="${sess:ch.nevis.idm.User.language}"/>
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" value="${sess:ch.nevis.idm.User.firstName}"/>
<property name="out.attribute.http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" value="${sess:ch.nevis.idm.User.lastName}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/dateOfBirth" value="${sess:ch.nevis.idm.User.birthDate:^(\d\d\d\d-\d\d-\d\d).*$}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/sex" value="${sess:ch.nevis.idm.User.gender}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/socialSecurityNumber" value="#{ (sess['agov.appSvnrAllowed'] == 'true') ? sess['ch.nevis.idm.User.prop.svnr'] : ''}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/placeOfBirth" value="#{ (sess['agov.appSvnrAllowed'] == 'true') ? sess['ch.nevis.idm.User.prop.placeOfBirth'] : ''}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/eIdNumber" value="${sess:ch.nevis.idm.User.prop.eIdNumber}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/qa/dateOfVerification" value="${sess:ValidFrom}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/qa/validTillDate" value="${sess:ValidTo}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/qa/verificationMethod" value="#{ ''.concat(sess.get('idVerification')).replace('SelfPaid', '') }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/nationality" value="#{ sess.containsKey('ch.nevis.idm.User.prop.nationality') ? sess['ch.nevis.idm.User.prop.nationality'].toUpperCase(): '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/05/identity/claims/authenticatedWith" value="${sess:authenticatedWith}"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/08/identity/claims/emailVerified" value="true"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/08/identity/claims/address/street" value="#{ (sess['agov.appAddressRequired'] == 'true') ? sess['ch.nevis.idm.User.street'] : '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/08/identity/claims/address/houseNumber" value="#{ (sess['agov.appAddressRequired'] == 'true') ? sess['ch.nevis.idm.User.houseNumber'] : '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/08/identity/claims/address/zipCode" value="#{ (sess['agov.appAddressRequired'] == 'true') ? sess['ch.nevis.idm.User.postalCode'] : '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2023/08/identity/claims/address/town" value="#{ (sess['agov.appAddressRequired'] == 'true') ? sess['ch.nevis.idm.User.city'] : '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2024/02/identity/claims/address/country" value="#{ (sess['agov.appAddressRequired'] == 'true') ? sess['ch.nevis.idm.User.country'].toUpperCase() : '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2024/02/identity/claims/address/qa/verificationMethod" value="#{ (sess['agov.appAddressRequired'] == 'true') ? ''.concat(sess.get('agov.adressVerification')).replace('Location', 'Domicile') : '' }"/>
<property name="out.attribute.http://schemas.agov.ch/ws/2024/02/identity/claims/address/countryName" value="#{ (sess['agov.appAddressRequired'] == 'true') ? sess['agov.countryName'] : ''}"/>
</AuthState>

View File

@ -9,7 +9,9 @@ pattern:
\ the session), we reset the state engine and start by processing that request."
properties:
authenticate:
- "pattern://68665057549fd887ea09fb86"
- "pattern://7a913eec7f78ce674cd87854"
logout:
- "pattern://7a913eec7f78ce674cd87854"
auth:
- "pattern://7022472ae407577ae604bbb8"
authParams:

View File

@ -0,0 +1,21 @@
schemaVersion: "1.0"
pattern:
id: "92cb6d5256008a32f12ceb93"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_AGOV_IDP"
label: "IDP CUSTOM"
notes: "modified script taken from what Nevis generated when using a SAM IDP Pattern"
properties:
authStatesFile: "res://92cb6d5256008a32f12ceb93#authStatesFile"
onSuccess:
- "pattern://2f81f8b878ef787fc5cc284a"
onFailure:
- "pattern://5f7e44f4fb2e3f710e4a3e91"
nextSteps:
- "pattern://db4eead0bb25b03205afd79f"
- "pattern://06515d4815de4afde6f8116a"
- "pattern://3f719a1e5c1447ee46c69cb2"
- "pattern://68665057549fd887ea09fb86"
keyObjects:
- "pattern://c0f2c118a88327acce1687fe"
- "pattern://0a15213c00dec3668fb94a65"

View File

@ -0,0 +1,8 @@
schemaVersion: "1.0"
pattern:
id: "cf0e8f8de1c8ac7345c5a6bb"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Auth_Done_GUI"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://cf0e8f8de1c8ac7345c5a6bb#authStatesFile"

View File

@ -0,0 +1,13 @@
schemaVersion: "1.0"
pattern:
id: "596e3e37c4d524690ea35897"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Authorization"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://596e3e37c4d524690ea35897#authStatesFile"
onSuccess:
- "pattern://cf0e8f8de1c8ac7345c5a6bb"
onFailure:
- "pattern://5f7e44f4fb2e3f710e4a3e91"
resources: "res://596e3e37c4d524690ea35897#resources"

View File

@ -0,0 +1,21 @@
schemaVersion: "1.0"
pattern:
id: "5a75ffc73b91b88cfab6168e"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_EPD_Artifact_IDP"
label: "EPD"
notes: "modified script taken from what Nevis generated when using a SAM IDP Pattern"
properties:
authStatesFile: "res://5a75ffc73b91b88cfab6168e#authStatesFile"
onSuccess:
- "pattern://2f81f8b878ef787fc5cc284a"
onFailure:
- "pattern://5f7e44f4fb2e3f710e4a3e91"
nextSteps:
- "pattern://db4eead0bb25b03205afd79f"
- "pattern://06515d4815de4afde6f8116a"
- "pattern://3f719a1e5c1447ee46c69cb2"
- "pattern://68665057549fd887ea09fb86"
keyObjects:
- "pattern://c0f2c118a88327acce1687fe"
- "pattern://8dbec5bb024707d73fca93ef"

View File

@ -0,0 +1,21 @@
schemaVersion: "1.0"
pattern:
id: "1d81bd987455a8e1ee044ccf"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_EPD_IDP"
label: "EPD"
notes: "modified script taken from what Nevis generated when using a SAM IDP Pattern"
properties:
authStatesFile: "res://1d81bd987455a8e1ee044ccf#authStatesFile"
onSuccess:
- "pattern://2f81f8b878ef787fc5cc284a"
onFailure:
- "pattern://5f7e44f4fb2e3f710e4a3e91"
nextSteps:
- "pattern://db4eead0bb25b03205afd79f"
- "pattern://06515d4815de4afde6f8116a"
- "pattern://3f719a1e5c1447ee46c69cb2"
- "pattern://68665057549fd887ea09fb86"
keyObjects:
- "pattern://c0f2c118a88327acce1687fe"
- "pattern://8dbec5bb024707d73fca93ef"

View File

@ -0,0 +1,16 @@
schemaVersion: "1.0"
pattern:
id: "73efd00d67082ff1eb927922"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_IDP_Dispatcher"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://73efd00d67082ff1eb927922#authStatesFile"
parameters: "var://auth_realm_main_idp_custom_idp_dispatcher-template-parameters"
onSuccess:
- "pattern://9196b809b539716b03ad8565"
nextSteps:
- "pattern://92cb6d5256008a32f12ceb93"
- "pattern://1d81bd987455a8e1ee044ccf"
- "pattern://5a75ffc73b91b88cfab6168e"
resources: "res://73efd00d67082ff1eb927922#resources"

View File

@ -0,0 +1,9 @@
schemaVersion: "1.0"
pattern:
id: "9196b809b539716b03ad8565"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Logout_Confirm"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://9196b809b539716b03ad8565#authStatesFile"
resources: "res://9196b809b539716b03ad8565#resources"

View File

@ -0,0 +1,8 @@
schemaVersion: "1.0"
pattern:
id: "06515d4815de4afde6f8116a"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Logout_Done"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://06515d4815de4afde6f8116a#authStatesFile"

View File

@ -0,0 +1,8 @@
schemaVersion: "1.0"
pattern:
id: "3f719a1e5c1447ee46c69cb2"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Logout_Fail"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://3f719a1e5c1447ee46c69cb2#authStatesFile"

View File

@ -0,0 +1,11 @@
schemaVersion: "1.0"
pattern:
id: "2f81f8b878ef787fc5cc284a"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Prepare_Done"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://2f81f8b878ef787fc5cc284a#authStatesFile"
onSuccess:
- "pattern://596e3e37c4d524690ea35897"
resources: "res://2f81f8b878ef787fc5cc284a#resources"

View File

@ -0,0 +1,15 @@
schemaVersion: "1.0"
pattern:
id: "03326b180687860ffe06a58c"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.Dispatcher"
name: "Auth_Realm_Main_IDP_Custom_Preprocess_Done"
label: "IDP CUSTOM"
properties:
conditions:
- ok: "${request:currentResource:^http[s]?\\u003A//[^/]+/SAML2/SSO/.*$:true}"
transitions:
- ok: "1"
steps:
- "pattern://73efd00d67082ff1eb927922"
defaultStep:
- "pattern://473f9d6b4ab9d61c1eb8c689"

View File

@ -0,0 +1,10 @@
schemaVersion: "1.0"
pattern:
id: "5f7e44f4fb2e3f710e4a3e91"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Auth_Realm_Main_IDP_Custom_Selector"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://5f7e44f4fb2e3f710e4a3e91#authStatesFile"
onSuccess:
- "pattern://2f81f8b878ef787fc5cc284a"

View File

@ -3,6 +3,7 @@ pattern:
id: "204c22beaccdfd22727af378"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.NevisAuthRealm"
name: "Auth_Realm_Recovery"
label: "RECOVERY"
properties:
authenticate:
- "pattern://584964c837512845d7940809"

View File

@ -3,6 +3,7 @@ pattern:
id: "162d4ee18e469c146df153cc"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.LuaPattern"
name: "CSP Security Response Headers"
label: "SECURITY"
properties:
script: "res://162d4ee18e469c146df153cc#script"
phase: "BEFORE_SANITATION"

View File

@ -3,6 +3,7 @@ pattern:
id: "bcfe78c02cbe0588528bc3cb"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
name: "ClusterInternalTlsTrustObject"
label: "STORE"
properties:
keyObjectId: "TlsTrustStore"
type: "truststore"

View File

@ -1,12 +1,13 @@
schemaVersion: "1.0"
pattern:
id: "7441fca76f479e4beb5ca796"
id: "e335f57d4c64dfc97223697a"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "EId_Verification_Auth"
label: "EID"
properties:
authStatesFile: "res://7441fca76f479e4beb5ca796#authStatesFile"
authStatesFile: "res://e335f57d4c64dfc97223697a#authStatesFile"
onSuccess:
- "pattern://b87d0d2b640e8e545ad70234"
onFailure:
- "pattern://4c65de021d362462324a3a5f"
resources: "res://7441fca76f479e4beb5ca796#resources"
resources: "res://e335f57d4c64dfc97223697a#resources"

View File

@ -0,0 +1,11 @@
schemaVersion: "1.0"
pattern:
id: "ab5a82719993921822e95751"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.GenericAuthWebService"
name: "EPD_Artifact_Resolution_Service"
label: "EPD"
properties:
auth:
- "pattern://7022472ae407577ae604bbb8"
configFile: "res://ab5a82719993921822e95751#configFile"
parameters: "var://epd_artifact_resolution_service-template-parameters"

View File

@ -0,0 +1,11 @@
schemaVersion: "1.0"
pattern:
id: "8dbec5bb024707d73fca93ef"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
name: "IDP_EPD_Truststore"
label: "EPD"
properties:
keyObjectId: "var://idp_sp_epd_connector-sp-issuer"
keyStoreName: "Store_IDP_AGOV"
trustStore:
- "pattern://8052fd68f4a663629d651f7b"

View File

@ -0,0 +1,12 @@
schemaVersion: "1.0"
pattern:
id: "c0f2c118a88327acce1687fe"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
name: "IDP_Keystore"
label: "STORE"
properties:
keyObjectId: "Signer_IDP_AGOV"
keyStoreName: "Store_IDP_AGOV"
type: "keystore"
keyStore:
- "pattern://56d6268c95f766c86c6aaae9"

View File

@ -8,55 +8,29 @@ pattern:
- 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"
- address.verificationMethod: BUNDBITBK-2921 avoid interface change for hotfix\n\
\n- todo: GLN"
properties:
issuer: "var://idp_sp_epd_connector-sp-issuer"
url: "var://idp_sp_epd_connector-sp-url---assertion-consumer-services"
signerTrust:
- "pattern://8052fd68f4a663629d651f7b"
binding: "http-post"
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'] : ''}"
- http://schemas.agov.ch/ws/2023/05/identity/claims/dateOfBirth: "${sess:ch.nevis.idm.User.birthDate:^(\\\
d\\d\\d\\d-\\d\\d-\\d\\d).*$}"
context: "PasswordProtectedTransport"
assertionLifetime: "30s"
sign:
- "Response"
- "Assertion"
keyInfo: "Certificate"
properties: "var://idp-sp-connector-properties"
properties: "var://epd_idp-parameters"

View File

@ -6,6 +6,8 @@ pattern:
label: "IDP"
properties:
authStatesFile: "res://7a913eec7f78ce674cd87854#authStatesFile"
onSuccess:
- "pattern://03326b180687860ffe06a58c"
nextSteps:
- "pattern://f63c475c35b616b7c6c1901c"
resources: "res://7a913eec7f78ce674cd87854#resources"

View File

@ -0,0 +1,11 @@
schemaVersion: "1.0"
pattern:
id: "0a15213c00dec3668fb94a65"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.KeyObject"
name: "IDP_Truststore"
label: "STORE"
properties:
keyObjectId: "var://idp_sp_connector-sp-issuer"
keyStoreName: "Store_IDP_AGOV"
trustStore:
- "pattern://8052fd68f4a663629d651f7b"

View File

@ -3,6 +3,7 @@ pattern:
id: "8b8167e5de0e69dedb81cacb"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.GenericHostContextSettings"
name: "IdP-Cors-Filter"
label: "SECURITY"
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\

View File

@ -3,6 +3,7 @@ pattern:
id: "2d8151249e6734ccc072422b"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.AutomaticTrustStoreProvider"
name: "IdP-Idm-SecToken-Signer-Trust"
label: "STORE"
properties:
keystore:
- "pattern://aeb2fed9962dcd5f7893db51"

View File

@ -3,6 +3,8 @@ pattern:
id: "55bf63a1b1716e9631f7080d"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.AutomaticTrustStoreProvider"
name: "Internal-IdP-Auth-Signer-Trust"
label: "STORE"
properties:
keystore:
- "pattern://aeb2fed9962dcd5f7893db51"
truststoreFile: "var://internal-idp-auth-signer-trust-additional-trusted-certificates"

View File

@ -3,6 +3,7 @@ pattern:
id: "aeb2fed9962dcd5f7893db51"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.AutomaticKeyStoreProvider"
name: "Internal-IdP-Auth-Signer"
label: "STORE"
properties:
owner:
- "pattern://7022472ae407577ae604bbb8"

View File

@ -0,0 +1,12 @@
schemaVersion: "1.0"
pattern:
id: "a6f6dc6affdc7c692ff857b9"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.AuthenticationFlow"
name: "Main IDP Authentication Flow"
label: "IDP CUSTOM"
properties:
host:
- "pattern://1f0702aaabef60a615abf41f"
path: "/SAML2/SSO/"
realm:
- "pattern://4fcfadb4a5c946ead7e6e995"

View File

@ -3,6 +3,7 @@ pattern:
id: "584964c837512845d7940809"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_Auth"
label: "RECOVERY"
notes: "TODO/haburger/2025-01-23: the transition exit.3 (alreadyInRecovery) is never\
\ used. We should clean this up here"
properties:

View File

@ -3,6 +3,7 @@ pattern:
id: "decb9b3f88d430fb5c95f466"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns.AuthenticationFlow"
name: "Recovery Authentication Flow"
label: "RECOVERY"
properties:
host:
- "pattern://1f0702aaabef60a615abf41f"

View File

@ -3,6 +3,7 @@ pattern:
id: "3d45f250b698005a29eb58b6"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.URLHandler"
name: "Recovery_Correct_Path_to_static_Ressources"
label: "RECOVERY"
properties:
redirects:
- /AUTH/RECOVERY/undefined/(.*): "/nevislogrend/nevislogrend/applications/Auth_Realm_Recovery/webdata/$1"

View File

@ -3,6 +3,7 @@ pattern:
id: "cadefc0abd2323fdccdb7a18"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.RESTServiceAccess"
name: "Recovery_Pdf_Generation"
label: "RECOVERY"
properties:
host:
- "pattern://1f0702aaabef60a615abf41f"

View File

@ -3,6 +3,7 @@ pattern:
id: "c1c0941f54cc36340578ff5f"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_authWithNewCredentials"
label: "RECOVERY"
properties:
authStatesFile: "res://c1c0941f54cc36340578ff5f#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "249d97a8932fe97d36112ff3"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_checkAccountState"
label: "RECOVERY"
properties:
authStatesFile: "res://249d97a8932fe97d36112ff3#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "9a1d3c6052019748d3510261"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_createURLTicket"
label: "RECOVERY"
properties:
authStatesFile: "res://9a1d3c6052019748d3510261#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "6364d27d1ca954be8ef7cb46"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_emailSent_screen"
label: "RECOVERY"
properties:
authStatesFile: "res://6364d27d1ca954be8ef7cb46#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "bea3ca0c85381d07d632be52"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GroovyScriptStep"
name: "Recovery_fetchCaptchaInfos"
label: "RECOVERY"
properties:
scriptFile: "res://bea3ca0c85381d07d632be52#scriptFile"
parameters:

View File

@ -3,6 +3,7 @@ pattern:
id: "717094cbd4ddbadeab4b2cc1"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GroovyScriptStep"
name: "Recovery_fetchCaptchaResult"
label: "RECOVERY"
properties:
scriptFile: "res://717094cbd4ddbadeab4b2cc1#scriptFile"
parameters:

View File

@ -3,6 +3,7 @@ pattern:
id: "54c1b68431bc2e03b61edcaa"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_fido2Login"
label: "RECOVERY"
properties:
authStatesFile: "res://54c1b68431bc2e03b61edcaa#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "db4acd487dc7e8b82de8abb4"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GroovyScriptStep"
name: "Recovery_handleCode"
label: "RECOVERY"
properties:
scriptFile: "res://db4acd487dc7e8b82de8abb4#scriptFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "81ae3547acc02160f787a546"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_loadUser"
label: "RECOVERY"
properties:
authStatesFile: "res://81ae3547acc02160f787a546#authStatesFile"
parameters: "var://extid_user_verify-template-parameters"

View File

@ -3,6 +3,7 @@ pattern:
id: "4bc453bf68139ee87966b0c7"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_mobile_nless_auth"
label: "RECOVERY"
properties:
authStatesFile: "res://4bc453bf68139ee87966b0c7#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "1d38203c48e017b5b3812385"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_ongoing"
label: "RECOVERY"
properties:
authStatesFile: "res://1d38203c48e017b5b3812385#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "6061abea33a234fad73897b7"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_redirectAgovMe"
label: "RECOVERY"
properties:
authStatesFile: "res://6061abea33a234fad73897b7#authStatesFile"
parameters: "var://service_provider_state-template-parameters"

View File

@ -3,6 +3,7 @@ pattern:
id: "9f443ce76f9522dfae4c3aa0"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GroovyScriptStep"
name: "Recovery_sendEmail031"
label: "RECOVERY"
properties:
scriptFile: "res://9f443ce76f9522dfae4c3aa0#scriptFile"
parameters:

View File

@ -3,6 +3,7 @@ pattern:
id: "ae023be7e097522c74e31d17"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_sendEmail031b"
label: "RECOVERY"
properties:
authStatesFile: "res://ae023be7e097522c74e31d17#authStatesFile"
onSuccess:

View File

@ -3,6 +3,7 @@ pattern:
id: "0327ca909dfcaf2d332da104"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: "Recovery_verifyEmail"
label: "RECOVERY"
properties:
authStatesFile: "res://0327ca909dfcaf2d332da104#authStatesFile"
parameters: "var://extid_user_verify-template-parameters"

View File

@ -3,6 +3,7 @@ pattern:
id: "0573c2491a56e59daca47e95"
className: "ch.nevis.admin.v4.plugin.nevisproxy.patterns.RequestValidationSettings"
name: "Request Validation Settings (ModSecurity)"
label: "SECURITY"
properties:
logOnlyMode: "enabled"
level: "var://request-validation-settings-modsecurity-paranoia-level"

View File

@ -15,5 +15,5 @@ pattern:
onFailure:
- "pattern://4c65de021d362462324a3a5f"
customSteps:
- "pattern://7441fca76f479e4beb5ca796"
- "pattern://e335f57d4c64dfc97223697a"
scriptTraceGroup: "AGOV-ACCT"

View File

@ -0,0 +1,8 @@
schemaVersion: "1.0"
pattern:
id: "db4eead0bb25b03205afd79f"
className: "ch.nevis.admin.v4.plugin.nevisauth.patterns2.GenericAuthenticationStep"
name: " Auth_Realm_Main_IDP_Custom_Concurrent_Logout"
label: "IDP CUSTOM"
properties:
authStatesFile: "res://db4eead0bb25b03205afd79f#authStatesFile"

View File

@ -0,0 +1,5 @@
<WebService class="ch.nevis.esauth.auth.adapter.saml.ArtifactResolutionService" name="ArtifactResolutionService" uri="${param.endpoint}" SSODomain="Auth_Realm_Main_IDP">
<property name="issuer" value="Auth_Realm_Main_IDP_Custom_EPD_Artifact_IDP"/>
<property name="out.keystoreref" value="Store_IDP_AGOV"/>
<property name="out.keyobjectref" value="Signer_IDP_AGOV"/>
</WebService>

View File

@ -6,6 +6,9 @@
</Gui>
</Response>
<property name="in.binding" value="none"/>
<property name="in.keystoreref" value=""/>
<property name="in.keyobjectref" value=""/>
<property name="out.binding" value="internal"/>
<property name="out.sign" value="Response Assertion"/>
<property name="out.signatureKeyInfo" value="Certificate"/>

View File

@ -0,0 +1,5 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.standard.AuthDone" final="false">
<Response value="AUTH_DONE">
<Gui name="ContinueResponse"/>
</Response>
</AuthState>

View File

@ -0,0 +1,8 @@
<AuthState name="${state.entry}" class="ch.nevis.esauth.auth.states.standard.AuthLogout" final="false" resumeState="false">
<Response value="AUTH_CONTINUE">
<Gui name="saml_logout" label="title.logout">
<GuiElem name="saml.logoutURLs" type="hidden" value="${outargs:saml.logoutURLs}" optional="true"/>
<GuiElem name="saml.logoutURL" type="hidden" value="#{ session.containsKey('saml.logoutURL') ? session.get('saml.logoutURL') : '/' }" optional="true"/>
</Gui>
</Response>
</AuthState>

View File

@ -14,5 +14,7 @@ pattern:
- "pattern://c0722fc79e7314c9cdcd20ff"
signerKeyStore:
- "pattern://aeb2fed9962dcd5f7893db51"
signerTrustStore:
- "pattern://55bf63a1b1716e9631f7080d"
addons:
- "pattern://90af8358cc587f5c5aa79fec"

View File

@ -99,6 +99,14 @@ variables:
maxAllowed: 1
value: "nevisauth"
requireOverloading: true
auth_realm_main_idp_custom_idp_dispatcher-template-parameters:
className: "ch.nevis.admin.v4.plugin.base.generation.property.TextProperty"
parameters:
required: false
syntax: "YAML"
value: "epd_idp_binding_mode: post\natb: https://trustbroker.agov-epr-lab.azure.adnovum.net\n\
epd_atb: https://trustbroker-idp.agov-epr-lab.azure.adnovum.net\n \n"
requireOverloading: true
auth_realm_main_sts_parameters:
className: "ch.nevis.admin.v4.plugin.base.generation.property.TextProperty"
parameters:
@ -172,6 +180,24 @@ variables:
secretPreserving: true
value: null
requireOverloading: true
epd_artifact_resolution_service-template-parameters:
className: "ch.nevis.admin.v4.plugin.base.generation.property.TextProperty"
parameters:
required: false
syntax: "YAML"
value: "endpoint: auth.agov-epr-lab.azure.adnovum.net"
requireOverloading: true
epd_idp-parameters:
className: "ch.nevis.admin.v4.plugin.base.generation.property.AuthStateProperty"
parameters:
separators:
- "->"
- "="
switchedSeparators: []
problematicSeparator: "->"
value:
- out.authnContextClassRef: "${sess:contextClassRefToSet}"
requireOverloading: true
externalingresssettings-annotations:
className: "ch.nevis.admin.v4.plugin.base.generation.property.KeyValueProperty"
parameters:
@ -554,6 +580,16 @@ variables:
queryInputMode: "OPTIONAL"
value: "https://trustbroker.agov-d.azure.adnovum.net/adfs/ls"
requireOverloading: true
idp_sp_epd_connector-outbound-binding:
className: "ch.nevis.admin.v4.plugin.base.generation.property.SelectionProperty"
parameters:
minRequired: 1
maxAllowed: 1
options:
- "http-post"
- "http-redirect"
value: "http-post"
requireOverloading: true
idp_sp_epd_connector-sp-issuer:
className: "ch.nevis.admin.v4.plugin.base.generation.property.SimpleTextProperty"
parameters:
@ -574,6 +610,12 @@ variables:
queryInputMode: "OPTIONAL"
value: "https://trustbroker-idp.agov-d.azure.adnovum.net/adfs/ls"
requireOverloading: true
internal-idp-auth-signer-trust-additional-trusted-certificates:
className: "ch.nevis.admin.v4.plugin.base.generation.property.AttachmentProperty"
parameters:
minRequired: 0
value: null
requireOverloading: true
log_auth-default-log-level:
className: "ch.nevis.admin.v4.plugin.base.generation.property.SelectionProperty"
parameters: