adn-agov-iam-project/patterns/306ce091fd87bad6174d9e8b_re.../eid_compare_and_update_idm_...

159 lines
6.5 KiB
Groovy

import java.text.SimpleDateFormat
import groovy.text.SimpleTemplateEngine
import ch.nevis.idm.client.IdmRestClient
import ch.nevis.idm.client.IdmRestClientFactory
def getDateWithoutTimestamp(String date){
def result = date
if(date.matches('^[0-9-]+[+]{1}.*')){
result = date.replaceAll('[+]{1}.*', "")
}
return result
}
// NOTE/aca/2025/06/19: We could also reload the data from idm after the update instead of updating the session variables manualy -> probably better and less error-prone
def compareAndUpdateSessionVariables(sess, keys, isProperty){
def updatedKeys = []
for(key in keys){
def idmkey = isProperty ? "ch.nevis.idm.User.prop.$key" : "ch.nevis.idm.User.$key"
def eidValue = session["agov.eid.User.$key"] ?: ""
def idmValue = session[idmkey] ?: ""
if(!idmValue || eidValue != idmValue){
sess.setAttribute(idmkey, eidValue)
updatedKeys.add(key)
}
}
return updatedKeys
}
String user_update_dto_template = '''
{
"name": {
"firstName": "$firstName",
"familyName": "$familyName"
},
"properties": {
"svnr": "$svnr",
"placeOfBirth": "$placeOfBirth",
"nationality": "$nationality",
"eIdNumber": "$eIdNumber"
},
"gender": "$gender",
"birthDate": "$birthDate",
"modificationComment": "updated user information with eid attributes during request $request"
}
'''
// 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 sess = request.getAuthSession(true)
// Convert EID gender format to IDM
if(sess.get('agov.eid.User.gender') == '1'){
sess.setAttribute('agov.eid.User.gender', 'MALE')
}
if(sess.get('agov.eid.User.gender') == '2'){
sess.setAttribute('agov.eid.User.gender', 'FEMALE')
}
if(sess.get('agov.eid.User.gender') == '3'){
sess.setAttribute('agov.eid.User.gender', 'OTHER')
}
// Compare eid and idm attributes + update idm session variables if they differ
def attributesToAudit = compareAndUpdateSessionVariables(sess, ["firstName", "lastName", "gender"], false)
// NOTE/aca/2025/06/14/: Potentally Throw a DATA ERROR if the properties are different? -> should the svnr number ever change?
def propertiesToAudit = compareAndUpdateSessionVariables(sess, ["svnr", "eIdNumber", "nationality", "placeOfBirth"], true)
// Handle birthdate seperately, since it can contain a timestamp -> we probably don't want to update if only the timestamp is wrong
String eidBirthdate = getDateWithoutTimestamp(session["agov.eid.User.birthDate"] ?: "")
String idmBirthdate = getDateWithoutTimestamp(session["ch.nevis.idm.User.birthDate"] ?: "")
LOG.debug("eidBirthdate: $eidBirthdate idmBirthdate: $idmBirthdate")
if(eidBirthdate != idmBirthdate){
sess.setAttribute("ch.nevis.idm.User.birthDate", eidBirthdate)
// For some reson IdmGetPropertyState uses a different date format than IdmSetPropertyState?
//def date = new SimpleDateFormat('yyyy-MM-dd').parse(eidBirthdate)
//def idmFromatedBirthDate = new SimpleDateFormat('dd.MM.yyyy').format(date)
//sess.setAttribute("ch.nevis.idm.User.birthDate.idmFormat", idmFromatedBirthDate)
attributesToAudit.add("birthDate")
}
// Check if we need to update IDM
def auditedRequired = attributesToAudit.size() > 0 || propertiesToAudit.size() > 0
if(auditedRequired){
// update attributes in idm & transition to User notification
IdmRestClient idmRestClient = IdmRestClientFactory.get(parameters)
String baseUrl = parameters.get("baseUrl")
String clientExtId = parameters.get("clientExtId")
String endPoint = "$baseUrl/api/core/v1"
String userExtId = sess.getAttribute("ch.nevis.idm.User.extId")
String requestUrl = "$endPoint/$clientExtId/users/$userExtId"
def binding = [
"firstName": sess.getAttribute('agov.eid.User.firstName'),
"familyName": sess.getAttribute('agov.eid.User.lastName'),
"svnr": sess.getAttribute('agov.eid.User.svnr'),
"placeOfBirth": sess.getAttribute('agov.eid.User.placeOfBirth'),
"nationality": sess.getAttribute('agov.eid.User.nationality'),
"eIdNumber": sess.getAttribute('agov.eid.User.eIdNumber'),
"gender": sess.getAttribute('agov.eid.User.gender').toLowerCase(),
"birthDate": sess.getAttribute('agov.eid.User.birthDate'),
"request": requestId
]
def templateEngine = new SimpleTemplateEngine()
def userUpdateDto = templateEngine.createTemplate(user_update_dto_template).make(binding).toString()
try {
idmRestClient.patch(requestUrl, userUpdateDto)
}catch(Exception e) {
LOG.error("Failed to update User data in IDM: ${e}")
LOG.error("Event='DATAERROR', Requester='${requester}', RequestId='${requestId}', RequestedAq=${requestedAq}, User=${user}, CredentialType='${credentialType}', SourceIp=${sourceIp}, UserAgent='${userAgent}', reason='Failed to update User data in IDM'")
response.setResult('error')
return
}
String printKeys = attributesToAudit.toListString()
LOG.debug("AuditedAttributes: $printKeys")
// Transform gender back to number
if(sess.get('ch.nevis.idm.User.gender') == 'MALE'){
sess.setAttribute('ch.nevis.idm.User.gender', '1')
}
if(sess.get('ch.nevis.idm.User.gender') == 'FEMALE'){
sess.setAttribute('ch.nevis.idm.User.gender', '2')
}
if(sess.get('ch.nevis.idm.User.gender') == 'OTHER'){
sess.setAttribute('ch.nevis.idm.User.gender', '3')
}
response.setResult('audited')
}else{
// Attributes match & no notification needed => continue by updating the linking credential and sending the saml assertion
// NOTE/aca/2025/06/19: We skip checking the account state, recovery code, mobile number and LoA
LOG.debug("No Audit Required: Logging user in")
response.setResult('noChange')
}