Migration is usually a one-time operation. This probably is not worth a dedicated feature since setups and prior authentication methods might vary significantly. IMHO a script is the most flexible and easiest way to go.
The current implementation of OpenID Connect Authenticator only adds XWiki.OIDC.UserClass when creating new users. As far as I can tell it always uses the profile where the correspoding XWiki.OIDC.UserClass exists from then on. So basically you need to add an XWiki.OIDC.UserClass object to your existing LDAP users (or whatever method of authentication you've been using so far).
I solved it by parsing for users with an XWiki.LDAPProfileClass object and adding an XWiki.OIDC.UserClass object to their profiles. Here is the script I used:
Hint: Do steps 2–4 before users start logging in through OIDC, so no new duplicates appear in between # Back up the database # Fill in {{ISSUER}} and {{{}CSV{}}}, then view the page with {{{}DRY_RUN = true{}}}. # Fix any red rows: missing UPNs, or conflicts with profiles from earlier test logins # Set {{DRY_RUN = false}} and view the page once more. # Set the OIDC configuration according to the format of your usernames, then restart: oidc.user.subjectFormater=${oidc.idtoken.upn._lowerCase} # Delete duplicate profiles after checking they hold nothing # If you use the Microsoft Entra v2.0 endpoint, make sure {{upn}} is in the ID token: add it as an optional claim under token configuration in the app registration
{code:java} {{groovy}} import org.xwiki.model.reference.LocalDocumentReference def DRY_RUN = true // Exactly the "iss" claim of the ID token (copy it from an OIDC-created profile) def ISSUER = 'https://login.microsoftonline.com/<tenant-id>/v2.0' // Contents of upn.csv: <LDAP uid>;<userPrincipalName> def CSV = ''' user;user@example.com ''' def upnByUid = [:] CSV.trim().eachLine { line -> def parts = line.split(';') if (parts.length == 2) { upnByUid[parts[0].trim().toLowerCase()] = parts[1].trim().toLowerCase() } } def oidcClass = new LocalDocumentReference(['XWiki', 'OIDC'], 'UserClass') def context = xcontext.context def wiki = context.wiki def linkedSubjects = [:] services.query.xwql('select oidc.subject, doc.fullName from Document doc, doc.object(XWiki.OIDC.UserClass) as oidc where oidc.issuer = :issuer') .bindValue('issuer', ISSUER).execute().each { linkedSubjects[it[0]] = it[1] } def profiles = services.query.xwql('select doc.fullName, ldap.uid from Document doc, doc.object(XWiki.LDAPProfileClass) as ldap order by doc.fullName').execute() println "|=Profile|=LDAP uid|=Subject|=Result" profiles.each { row -> def (fullName, uid) = [row[0], row[1]] def upn = uid ? upnByUid[uid.toLowerCase()] : null def doc = wiki.getDocument(services.model.resolveDocument(fullName), context) def result if (doc.getXObject(oidcClass) != null) { result = 'skipped: already linked' } else if (upn == null) { result = '(% style="color:red" %)skipped: no UPN in CSV' } else if (linkedSubjects.containsKey(upn)) { result = "(% style=\"color:red\" %)conflict: subject already on ${linkedSubjects[upn]}" } else if (DRY_RUN) { result = 'would link' } else { doc = doc.clone() def obj = doc.newXObject(oidcClass, context) obj.setStringValue('issuer', ISSUER) obj.setStringValue('subject', upn) wiki.saveDocument(doc, 'Link LDAP profile to OIDC identity', true, context) result = 'linked' } println "|[[${fullName}]]|${uid}|${upn ?: ''}|${result}" } {{/groovy}}{code}
Patrick Koska on 04/Nov/26 11:20
Migration is usually a one-time operation. This probably is not worth a dedicated feature since setups and prior authentication methods might vary significantly. IMHO a script is the most flexible and easiest way to go.
The current implementation of OpenID Connect Authenticator only adds XWiki.OIDC.UserClass when creating new users. As far as I can tell it always uses the profile where the correspoding XWiki.OIDC.UserClass exists from then on. So basically you need to add an XWiki.OIDC.UserClass object to your existing LDAP users (or whatever method of authentication you've been using so far).
I solved it by parsing for users with an XWiki.LDAPProfileClass object and adding an XWiki.OIDC.UserClass object to their profiles. Here is the script I used:
Hint: Do steps 2–4 before users start logging in through OIDC, so no new duplicates appear in between # Back up the database # Fill in {{ISSUER}} and {{{}CSV{}}}, then view the page with {{{}DRY_RUN = true{}}}. # Fix any red rows: missing UPNs, or conflicts with profiles from earlier test logins # Set {{DRY_RUN = false}} and view the page once more. # Set the OIDC configuration according to the format of your usernames, then restart: oidc.user.subjectFormater=${oidc.idtoken.upn._lowerCase} # Delete duplicate profiles after checking they hold nothing # If you use the Microsoft Entra v2.0 endpoint, make sure {{upn}} is in the ID token: add it as an optional claim under token configuration in the app registration
{code:java} {{groovy}} import org.xwiki.model.reference.LocalDocumentReference def DRY_RUN = true // Exactly the "iss" claim of the ID token (copy it from an OIDC-created profile) def ISSUER = 'https://login.microsoftonline.com/<tenant-id>/v2.0' // Contents of upn.csv: <LDAP uid>;<userPrincipalName> def CSV = ''' user;user@example.com ''' def upnByUid = [:] CSV.trim().eachLine { line -> def parts = line.split(';') if (parts.length == 2) { upnByUid[parts[0].trim().toLowerCase()] = parts[1].trim().toLowerCase() } } def oidcClass = new LocalDocumentReference(['XWiki', 'OIDC'], 'UserClass') def context = xcontext.context def wiki = context.wiki def linkedSubjects = [:] services.query.xwql('select oidc.subject, doc.fullName from Document doc, doc.object(XWiki.OIDC.UserClass) as oidc where oidc.issuer = :issuer') .bindValue('issuer', ISSUER).execute().each { linkedSubjects[it[0]] = it[1] } def profiles = services.query.xwql('select doc.fullName, ldap.uid from Document doc, doc.object(XWiki.LDAPProfileClass) as ldap order by doc.fullName').execute() println "|=Profile|=LDAP uid|=Subject|=Result" profiles.each { row -> def (fullName, uid) = [row[0], row[1]] def upn = uid ? upnByUid[uid.toLowerCase()] : null def doc = wiki.getDocument(services.model.resolveDocument(fullName), context) def result if (doc.getXObject(oidcClass) != null) { result = 'skipped: already linked' } else if (upn == null) { result = '(% style="color:red" %)skipped: no UPN in CSV' } else if (linkedSubjects.containsKey(upn)) { result = "(% style=\"color:red\" %)conflict: subject already on ${linkedSubjects[upn]}" } else if (DRY_RUN) { result = 'would link' } else { doc = doc.clone() def obj = doc.newXObject(oidcClass, context) obj.setStringValue('issuer', ISSUER) obj.setStringValue('subject', upn) wiki.saveDocument(doc, 'Link LDAP profile to OIDC identity', true, context) result = 'linked' } println "|[[${fullName}]]|${uid}|${upn ?: ''}|${result}" } {{/groovy}}{code}
Patrick Koska on 04/Nov/26 11:21
Migration is usually a one-time operation. This probably is not worth a dedicated feature since setups and prior authentication methods might vary significantly. IMHO a script is the most flexible and easiest way to go.
The current implementation of OpenID Connect Authenticator only adds XWiki.OIDC.UserClass when creating new users. As far as I can tell it always uses the profile where the correspoding XWiki.OIDC.UserClass exists from then on. So basically you need to add an XWiki.OIDC.UserClass object to your existing LDAP users (or whatever authentication auth method you've been using so far).
I solved it by parsing for users with an XWiki.LDAPProfileClass object and adding an XWiki.OIDC.UserClass object to their profiles. Here is the script I used:
Hint: Do steps 2–4 before users start logging in through OIDC, so no new duplicates appear in between # Back up the database # Fill in {{ISSUER}} and {{{}CSV{}}}, then view the page with {{{}DRY_RUN = true{}}}. # Fix any red rows: missing UPNs, or conflicts with profiles from earlier test logins # Set {{DRY_RUN = false}} and view the page once more. # Set the OIDC configuration according to the format of your usernames, then restart: oidc.user.subjectFormater=${oidc.idtoken.upn._lowerCase} # Delete duplicate profiles after checking they hold nothing # If you use the Microsoft Entra v2.0 endpoint, make sure {{upn}} is in the ID token: add it as an optional claim under token configuration in the app registration
{code:java} {{groovy}} import org.xwiki.model.reference.LocalDocumentReference def DRY_RUN = true // Exactly the "iss" claim of the ID token (copy it from an OIDC-created profile) def ISSUER = 'https://login.microsoftonline.com/<tenant-id>/v2.0' // Contents of upn.csv: <LDAP uid>;<userPrincipalName> def CSV = ''' user;user@example.com ''' def upnByUid = [:] CSV.trim().eachLine { line -> def parts = line.split(';') if (parts.length == 2) { upnByUid[parts[0].trim().toLowerCase()] = parts[1].trim().toLowerCase() } } def oidcClass = new LocalDocumentReference(['XWiki', 'OIDC'], 'UserClass') def context = xcontext.context def wiki = context.wiki def linkedSubjects = [:] services.query.xwql('select oidc.subject, doc.fullName from Document doc, doc.object(XWiki.OIDC.UserClass) as oidc where oidc.issuer = :issuer') .bindValue('issuer', ISSUER).execute().each { linkedSubjects[it[0]] = it[1] } def profiles = services.query.xwql('select doc.fullName, ldap.uid from Document doc, doc.object(XWiki.LDAPProfileClass) as ldap order by doc.fullName').execute() println "|=Profile|=LDAP uid|=Subject|=Result" profiles.each { row -> def (fullName, uid) = [row[0], row[1]] def upn = uid ? upnByUid[uid.toLowerCase()] : null def doc = wiki.getDocument(services.model.resolveDocument(fullName), context) def result if (doc.getXObject(oidcClass) != null) { result = 'skipped: already linked' } else if (upn == null) { result = '(% style="color:red" %)skipped: no UPN in CSV' } else if (linkedSubjects.containsKey(upn)) { result = "(% style=\"color:red\" %)conflict: subject already on ${linkedSubjects[upn]}" } else if (DRY_RUN) { result = 'would link' } else { doc = doc.clone() def obj = doc.newXObject(oidcClass, context) obj.setStringValue('issuer', ISSUER) obj.setStringValue('subject', upn) wiki.saveDocument(doc, 'Link LDAP profile to OIDC identity', true, context) result = 'linked' } println "|[[${fullName}]]|${uid}|${upn ?: ''}|${result}" } {{/groovy}}{code}
Patrick Koska on 04/Nov/26 11:22
Migration is usually a one-time operation. This probably is not worth a dedicated feature since setups and prior authentication methods might vary significantly. IMHO a script is the most flexible and easiest way to go.
The current implementation of OpenID Connect Authenticator only adds XWiki.OIDC.UserClass when creating new users. As far as I can tell it always uses the profile where the correspoding XWiki.OIDC.UserClass exists from then on. So basically you need to add an XWiki.OIDC.UserClass object to your existing LDAP users (or whatever auth method you've been using so far).
I solved it by parsing for users with an XWiki.LDAPProfileClass object and adding an XWiki.OIDC.UserClass object to their profiles. Here is Below you'll find the script I used:.
Hint: Do steps 2–4 before users start logging in through OIDC, so no new duplicates appear in between # Back up the database # Fill in {{ISSUER}} and {{{}CSV{}}}, then view the page with {{{}DRY_RUN = true{}}}. # Fix any red rows: missing UPNs, or conflicts with profiles from earlier test logins # Set {{DRY_RUN = false}} and view the page once more. # Set the OIDC configuration according to the format of your usernames, then restart: oidc.user.subjectFormater=${oidc.idtoken.upn._lowerCase} # Delete duplicate profiles after checking they hold nothing # If you use the Microsoft Entra v2.0 endpoint, make sure {{upn}} is in the ID token: add it as an optional claim under token configuration in the app registration
{code:java} {{groovy}} import org.xwiki.model.reference.LocalDocumentReference def DRY_RUN = true // Exactly the "iss" claim of the ID token (copy it from an OIDC-created profile) def ISSUER = 'https://login.microsoftonline.com/<tenant-id>/v2.0' // Contents of upn.csv: <LDAP uid>;<userPrincipalName> def CSV = ''' user;user@example.com ''' def upnByUid = [:] CSV.trim().eachLine { line -> def parts = line.split(';') if (parts.length == 2) { upnByUid[parts[0].trim().toLowerCase()] = parts[1].trim().toLowerCase() } } def oidcClass = new LocalDocumentReference(['XWiki', 'OIDC'], 'UserClass') def context = xcontext.context def wiki = context.wiki def linkedSubjects = [:] services.query.xwql('select oidc.subject, doc.fullName from Document doc, doc.object(XWiki.OIDC.UserClass) as oidc where oidc.issuer = :issuer') .bindValue('issuer', ISSUER).execute().each { linkedSubjects[it[0]] = it[1] } def profiles = services.query.xwql('select doc.fullName, ldap.uid from Document doc, doc.object(XWiki.LDAPProfileClass) as ldap order by doc.fullName').execute() println "|=Profile|=LDAP uid|=Subject|=Result" profiles.each { row -> def (fullName, uid) = [row[0], row[1]] def upn = uid ? upnByUid[uid.toLowerCase()] : null def doc = wiki.getDocument(services.model.resolveDocument(fullName), context) def result if (doc.getXObject(oidcClass) != null) { result = 'skipped: already linked' } else if (upn == null) { result = '(% style="color:red" %)skipped: no UPN in CSV' } else if (linkedSubjects.containsKey(upn)) { result = "(% style=\"color:red\" %)conflict: subject already on ${linkedSubjects[upn]}" } else if (DRY_RUN) { result = 'would link' } else { doc = doc.clone() def obj = doc.newXObject(oidcClass, context) obj.setStringValue('issuer', ISSUER) obj.setStringValue('subject', upn) wiki.saveDocument(doc, 'Link LDAP profile to OIDC identity', true, context) result = 'linked' } println "|[[${fullName}]]|${uid}|${upn ?: ''}|${result}" } {{/groovy}}{code}
Patrick Koska on 04/Nov/26 11:23
Migration is usually a one-time operation. This probably is not worth a dedicated feature since setups and prior authentication methods might vary significantly. IMHO a script is the most flexible and easiest way to go.
The current implementation of OpenID Connect Authenticator only adds XWiki.OIDC.UserClass when creating new users. As far as I can tell it always uses the profile where the correspoding XWiki.OIDC.UserClass exists from then on. So basically you need to add an XWiki.OIDC.UserClass object to your existing LDAP users (or whatever auth method you've been using so far).
I solved it by parsing for users with an XWiki.LDAPProfileClass object and adding an XWiki.OIDC.UserClass object to their profiles. Below you'll find See below for the script I used.
Hint: Do steps 2–4 before users start logging in through OIDC, so no new duplicates appear in between # Back up the database # Fill in {{ISSUER}} and {{{}CSV{}}}, then view the page with {{{}DRY_RUN = true{}}}. # Fix any red rows: missing UPNs, or conflicts with profiles from earlier test logins # Set {{DRY_RUN = false}} and view the page once more. # Set the OIDC configuration according to the format of your usernames, then restart: oidc.user.subjectFormater=${oidc.idtoken.upn._lowerCase} # Delete duplicate profiles after checking they hold nothing # If you use the Microsoft Entra v2.0 endpoint, make sure {{upn}} is in the ID token: add it as an optional claim under token configuration in the app registration
{code:java} {{groovy}} import org.xwiki.model.reference.LocalDocumentReference def DRY_RUN = true // Exactly the "iss" claim of the ID token (copy it from an OIDC-created profile) def ISSUER = 'https://login.microsoftonline.com/<tenant-id>/v2.0' // Contents of upn.csv: <LDAP uid>;<userPrincipalName> def CSV = ''' user;user@example.com ''' def upnByUid = [:] CSV.trim().eachLine { line -> def parts = line.split(';') if (parts.length == 2) { upnByUid[parts[0].trim().toLowerCase()] = parts[1].trim().toLowerCase() } } def oidcClass = new LocalDocumentReference(['XWiki', 'OIDC'], 'UserClass') def context = xcontext.context def wiki = context.wiki def linkedSubjects = [:] services.query.xwql('select oidc.subject, doc.fullName from Document doc, doc.object(XWiki.OIDC.UserClass) as oidc where oidc.issuer = :issuer') .bindValue('issuer', ISSUER).execute().each { linkedSubjects[it[0]] = it[1] } def profiles = services.query.xwql('select doc.fullName, ldap.uid from Document doc, doc.object(XWiki.LDAPProfileClass) as ldap order by doc.fullName').execute() println "|=Profile|=LDAP uid|=Subject|=Result" profiles.each { row -> def (fullName, uid) = [row[0], row[1]] def upn = uid ? upnByUid[uid.toLowerCase()] : null def doc = wiki.getDocument(services.model.resolveDocument(fullName), context) def result if (doc.getXObject(oidcClass) != null) { result = 'skipped: already linked' } else if (upn == null) { result = '(% style="color:red" %)skipped: no UPN in CSV' } else if (linkedSubjects.containsKey(upn)) { result = "(% style=\"color:red\" %)conflict: subject already on ${linkedSubjects[upn]}" } else if (DRY_RUN) { result = 'would link' } else { doc = doc.clone() def obj = doc.newXObject(oidcClass, context) obj.setStringValue('issuer', ISSUER) obj.setStringValue('subject', upn) wiki.saveDocument(doc, 'Link LDAP profile to OIDC identity', true, context) result = 'linked' } println "|[[${fullName}]]|${uid}|${upn ?: ''}|${result}" } {{/groovy}}{code}
This message was sent by Atlassian Jira (v9.3.0#930000-sha1:287aeb6)
If image attachments aren't displayed, see this article.