mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
external-idp: add support to manage external IdP objects
Fixes: https://pagure.io/freeipa/issue/8804 Fixes: https://pagure.io/freeipa/issue/8803 Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com> Signed-off-by: Florence Blanc-Renaud <flo@redhat.com> Reviewed-By: Francisco Trivino <ftrivino@redhat.com> Reviewed-By: Sumit Bose <sbose@redhat.com>
This commit is contained in:
10
ACI.txt
10
ACI.txt
@@ -180,6 +180,16 @@ dn: cn=views,cn=accounts,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || description || entryusn || gidnumber || ipaanchoruuid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaGroupOverride)")(version 3.0;acl "permission:System: Read Group ID Overrides";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
dn: cn=views,cn=accounts,dc=ipa,dc=example
|
||||
aci: (targetattr = "createtimestamp || description || entryusn || gecos || gidnumber || homedirectory || ipaanchoruuid || ipaoriginaluid || ipasshpubkey || loginshell || memberof || modifytimestamp || objectclass || uid || uidnumber || usercertificate")(targetfilter = "(objectclass=ipaUserOverride)")(version 3.0;acl "permission:System: Read User ID Overrides";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
dn: cn=idp,dc=ipa,dc=example
|
||||
aci: (targetfilter = "(objectclass=ipaidp)")(version 3.0;acl "permission:System: Add External IdP server";allow (add) groupdn = "ldap:///cn=System: Add External IdP server,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=idp,dc=ipa,dc=example
|
||||
aci: (targetfilter = "(objectclass=ipaidp)")(version 3.0;acl "permission:System: Delete External IdP server";allow (delete) groupdn = "ldap:///cn=System: Delete External IdP server,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=idp,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || ipaidpauthendpoint || ipaidpclientid || ipaidpclientsecret || ipaidpdevauthendpoint || ipaidpissuerurl || ipaidpkeysendpoint || ipaidpscope || ipaidpsub || ipaidptokenendpoint || ipaidpuserinfoendpoint || objectclass")(targetfilter = "(objectclass=ipaidp)")(version 3.0;acl "permission:System: Modify External IdP server";allow (write) groupdn = "ldap:///cn=System: Modify External IdP server,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=idp,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || entryusn || ipaidpauthendpoint || ipaidpclientid || ipaidpdevauthendpoint || ipaidpissuerurl || ipaidpkeysendpoint || ipaidpscope || ipaidpsub || ipaidptokenendpoint || ipaidpuserinfoendpoint || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaidp)")(version 3.0;acl "permission:System: Read External IdP server";allow (compare,read,search) groupdn = "ldap:///cn=System: Read External IdP server,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=idp,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || entryusn || ipaidpauthendpoint || ipaidpclientid || ipaidpclientsecret || ipaidpdevauthendpoint || ipaidpissuerurl || ipaidpkeysendpoint || ipaidpscope || ipaidpsub || ipaidptokenendpoint || ipaidpuserinfoendpoint || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaidp)")(version 3.0;acl "permission:System: Read External IdP server client secret";allow (compare,read,search) groupdn = "ldap:///cn=System: Read External IdP server client secret,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||
dn: cn=ranges,cn=etc,dc=ipa,dc=example
|
||||
aci: (targetattr = "cn || createtimestamp || entryusn || ipaautoprivategroups || ipabaseid || ipabaserid || ipaidrangesize || ipanttrusteddomainsid || iparangetype || ipasecondarybaserid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaidrange)")(version 3.0;acl "permission:System: Read ID Ranges";allow (compare,read,search) userdn = "ldap:///all";)
|
||||
dn: cn=views,cn=accounts,dc=ipa,dc=example
|
||||
|
152
API.txt
152
API.txt
@@ -1098,7 +1098,7 @@ option: Int('ipasearchrecordslimit?', autofill=False, cli_name='searchrecordslim
|
||||
option: Int('ipasearchtimelimit?', autofill=False, cli_name='searchtimelimit')
|
||||
option: Str('ipaselinuxusermapdefault?', autofill=False)
|
||||
option: Str('ipaselinuxusermaporder?', autofill=False)
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'disabled'])
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp', u'disabled'])
|
||||
option: Bool('ipauserdefaultsubordinateid?', autofill=False, cli_name='user_default_subid')
|
||||
option: Str('ipauserobjectclasses*', autofill=False, cli_name='userobjectclasses')
|
||||
option: IA5Str('ipausersearchfields?', autofill=False, cli_name='usersearch')
|
||||
@@ -2477,7 +2477,7 @@ option: Bool('ipakrbokasdelegate?', cli_name='ok_as_delegate')
|
||||
option: Bool('ipakrboktoauthasdelegate?', cli_name='ok_to_auth_as_delegate')
|
||||
option: Bool('ipakrbrequirespreauth?', cli_name='requires_pre_auth')
|
||||
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
||||
option: StrEnum('krbprincipalauthind*', cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('krbprincipalauthind*', cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: Str('l?', cli_name='locality')
|
||||
option: Str('macaddress*')
|
||||
option: Flag('no_members', autofill=True, default=False)
|
||||
@@ -2613,7 +2613,7 @@ option: Str('in_netgroup*', cli_name='in_netgroups')
|
||||
option: Str('in_role*', cli_name='in_roles')
|
||||
option: Str('in_sudorule*', cli_name='in_sudorules')
|
||||
option: Str('ipaassignedidview?', autofill=False)
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: Str('l?', autofill=False, cli_name='locality')
|
||||
option: Str('macaddress*', autofill=False)
|
||||
option: Str('man_by_host*', cli_name='man_by_hosts')
|
||||
@@ -2653,7 +2653,7 @@ option: Bool('ipakrbokasdelegate?', autofill=False, cli_name='ok_as_delegate')
|
||||
option: Bool('ipakrboktoauthasdelegate?', autofill=False, cli_name='ok_to_auth_as_delegate')
|
||||
option: Bool('ipakrbrequirespreauth?', autofill=False, cli_name='requires_pre_auth')
|
||||
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: Principal('krbprincipalname*', autofill=False)
|
||||
option: Str('l?', autofill=False, cli_name='locality')
|
||||
option: Str('macaddress*', autofill=False)
|
||||
@@ -3051,6 +3051,96 @@ option: Str('version?')
|
||||
output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: idp_add/1
|
||||
args: 1,18,3
|
||||
arg: Str('cn', cli_name='name')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('ipaidpauthendpoint?', alwaysask=False, cli_name='auth_uri')
|
||||
option: Str('ipaidpbaseurl?', cli_name='base_url')
|
||||
option: Str('ipaidpclientid', cli_name='client_id')
|
||||
option: Password('ipaidpclientsecret?', cli_name='secret', confirm=True)
|
||||
option: Str('ipaidpdevauthendpoint?', alwaysask=False, cli_name='dev_auth_uri')
|
||||
option: Str('ipaidpissuerurl?', cli_name='issuer_url')
|
||||
option: Str('ipaidpkeysendpoint?', alwaysask=False, cli_name='keys_uri')
|
||||
option: Str('ipaidporg?', cli_name='organization')
|
||||
option: StrEnum('ipaidpprovider?', cli_name='provider', values=[u'google', u'github', u'microsoft', u'okta', u'keycloak'])
|
||||
option: Str('ipaidpscope?', cli_name='scope')
|
||||
option: Str('ipaidpsub?', cli_name='idp_user_id')
|
||||
option: Str('ipaidptokenendpoint?', alwaysask=False, cli_name='token_uri')
|
||||
option: Str('ipaidpuserinfoendpoint?', alwaysask=False, cli_name='userinfo_uri')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||
option: Str('setattr*', cli_name='setattr')
|
||||
option: Str('version?')
|
||||
output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: idp_del/1
|
||||
args: 1,2,3
|
||||
arg: Str('cn+', cli_name='name')
|
||||
option: Flag('continue', autofill=True, cli_name='continue', default=False)
|
||||
option: Str('version?')
|
||||
output: Output('result', type=[<type 'dict'>])
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: ListOfPrimaryKeys('value')
|
||||
command: idp_find/1
|
||||
args: 1,17,4
|
||||
arg: Str('criteria?')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('cn?', autofill=False, cli_name='name')
|
||||
option: Str('ipaidpauthendpoint?', autofill=False, cli_name='auth_uri')
|
||||
option: Str('ipaidpclientid?', autofill=False, cli_name='client_id')
|
||||
option: Password('ipaidpclientsecret?', autofill=False, cli_name='secret', confirm=True)
|
||||
option: Str('ipaidpdevauthendpoint?', autofill=False, cli_name='dev_auth_uri')
|
||||
option: Str('ipaidpissuerurl?', autofill=False, cli_name='issuer_url')
|
||||
option: Str('ipaidpkeysendpoint?', autofill=False, cli_name='keys_uri')
|
||||
option: Str('ipaidpscope?', autofill=False, cli_name='scope')
|
||||
option: Str('ipaidpsub?', autofill=False, cli_name='idp_user_id')
|
||||
option: Str('ipaidptokenendpoint?', autofill=False, cli_name='token_uri')
|
||||
option: Str('ipaidpuserinfoendpoint?', autofill=False, cli_name='userinfo_uri')
|
||||
option: Flag('pkey_only?', autofill=True, default=False)
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||
option: Int('sizelimit?', autofill=False)
|
||||
option: Int('timelimit?', autofill=False)
|
||||
option: Str('version?')
|
||||
output: Output('count', type=[<type 'int'>])
|
||||
output: ListOfEntries('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: Output('truncated', type=[<type 'bool'>])
|
||||
command: idp_mod/1
|
||||
args: 1,18,3
|
||||
arg: Str('cn', cli_name='name')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('delattr*', cli_name='delattr')
|
||||
option: Str('ipaidpauthendpoint?', autofill=False, cli_name='auth_uri')
|
||||
option: Str('ipaidpclientid?', autofill=False, cli_name='client_id')
|
||||
option: Password('ipaidpclientsecret?', autofill=False, cli_name='secret', confirm=True)
|
||||
option: Str('ipaidpdevauthendpoint?', autofill=False, cli_name='dev_auth_uri')
|
||||
option: Str('ipaidpissuerurl?', autofill=False, cli_name='issuer_url')
|
||||
option: Str('ipaidpkeysendpoint?', autofill=False, cli_name='keys_uri')
|
||||
option: Str('ipaidpscope?', autofill=False, cli_name='scope')
|
||||
option: Str('ipaidpsub?', autofill=False, cli_name='idp_user_id')
|
||||
option: Str('ipaidptokenendpoint?', autofill=False, cli_name='token_uri')
|
||||
option: Str('ipaidpuserinfoendpoint?', autofill=False, cli_name='userinfo_uri')
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||
option: Str('rename?', cli_name='rename')
|
||||
option: Flag('rights', autofill=True, default=False)
|
||||
option: Str('setattr*', cli_name='setattr')
|
||||
option: Str('version?')
|
||||
output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: idp_show/1
|
||||
args: 1,4,3
|
||||
arg: Str('cn', cli_name='name')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||
option: Flag('rights', autofill=True, default=False)
|
||||
option: Str('version?')
|
||||
output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: idrange_add/1
|
||||
args: 1,13,3
|
||||
arg: Str('cn', cli_name='name')
|
||||
@@ -3236,16 +3326,18 @@ output: Output('result', type=[<type 'bool'>])
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: krbtpolicy_mod/1
|
||||
args: 1,17,3
|
||||
args: 1,19,3
|
||||
arg: Str('uid?', cli_name='user')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('delattr*', cli_name='delattr')
|
||||
option: Int('krbauthindmaxrenewableage_hardened?', autofill=False, cli_name='hardened_maxrenew')
|
||||
option: Int('krbauthindmaxrenewableage_idp?', autofill=False, cli_name='idp_maxrenew')
|
||||
option: Int('krbauthindmaxrenewableage_otp?', autofill=False, cli_name='otp_maxrenew')
|
||||
option: Int('krbauthindmaxrenewableage_pkinit?', autofill=False, cli_name='pkinit_maxrenew')
|
||||
option: Int('krbauthindmaxrenewableage_radius?', autofill=False, cli_name='radius_maxrenew')
|
||||
option: Int('krbauthindmaxticketlife_hardened?', autofill=False, cli_name='hardened_maxlife')
|
||||
option: Int('krbauthindmaxticketlife_idp?', autofill=False, cli_name='idp_maxlife')
|
||||
option: Int('krbauthindmaxticketlife_otp?', autofill=False, cli_name='otp_maxlife')
|
||||
option: Int('krbauthindmaxticketlife_pkinit?', autofill=False, cli_name='pkinit_maxlife')
|
||||
option: Int('krbauthindmaxticketlife_radius?', autofill=False, cli_name='radius_maxlife')
|
||||
@@ -4588,7 +4680,7 @@ option: StrEnum('ipakrbauthzdata*', cli_name='pac_type', values=[u'MS-PAC', u'PA
|
||||
option: Bool('ipakrbokasdelegate?', cli_name='ok_as_delegate')
|
||||
option: Bool('ipakrboktoauthasdelegate?', cli_name='ok_to_auth_as_delegate')
|
||||
option: Bool('ipakrbrequirespreauth?', cli_name='requires_pre_auth')
|
||||
option: StrEnum('krbprincipalauthind*', cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('krbprincipalauthind*', cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: Flag('no_members', autofill=True, default=False)
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||
option: Str('setattr*', cli_name='setattr')
|
||||
@@ -4724,7 +4816,7 @@ arg: Str('criteria?')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: StrEnum('ipakrbauthzdata*', autofill=False, cli_name='pac_type', values=[u'MS-PAC', u'PAD', u'NONE'])
|
||||
option: Principal('krbcanonicalname?', autofill=False, cli_name='canonical_principal')
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: Principal('krbprincipalname*', autofill=False, cli_name='principal')
|
||||
option: Str('man_by_host*', cli_name='man_by_hosts')
|
||||
option: Flag('no_members', autofill=True, default=True)
|
||||
@@ -4748,7 +4840,7 @@ option: StrEnum('ipakrbauthzdata*', autofill=False, cli_name='pac_type', values=
|
||||
option: Bool('ipakrbokasdelegate?', autofill=False, cli_name='ok_as_delegate')
|
||||
option: Bool('ipakrboktoauthasdelegate?', autofill=False, cli_name='ok_to_auth_as_delegate')
|
||||
option: Bool('ipakrbrequirespreauth?', autofill=False, cli_name='requires_pre_auth')
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('krbprincipalauthind*', autofill=False, cli_name='auth_ind', values=[u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: Principal('krbprincipalname*', autofill=False, cli_name='principal')
|
||||
option: Flag('no_members', autofill=True, default=False)
|
||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||
@@ -4976,7 +5068,7 @@ output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: stageuser_add/1
|
||||
args: 1,45,3
|
||||
args: 1,47,3
|
||||
arg: Str('uid', cli_name='login')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
@@ -4993,10 +5085,12 @@ option: Int('gidnumber?')
|
||||
option: Str('givenname', cli_name='first')
|
||||
option: Str('homedirectory?', cli_name='homedir')
|
||||
option: Str('initials?', autofill=True)
|
||||
option: Str('ipaidpconfiglink?', cli_name='idp')
|
||||
option: Str('ipaidpsub?', cli_name='idp_user_id')
|
||||
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
||||
option: Str('ipatokenradiusconfiglink?', cli_name='radius')
|
||||
option: Str('ipatokenradiususername?', cli_name='radius_username')
|
||||
option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: DateTime('krbpasswordexpiration?', cli_name='password_expiration')
|
||||
option: DateTime('krbprincipalexpiration?', cli_name='principal_expiration')
|
||||
option: Principal('krbprincipalname*', autofill=True, cli_name='principal')
|
||||
@@ -5082,7 +5176,7 @@ output: Output('result', type=[<type 'dict'>])
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: ListOfPrimaryKeys('value')
|
||||
command: stageuser_find/1
|
||||
args: 1,60,4
|
||||
args: 1,62,4
|
||||
arg: Str('criteria?')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('carlicense*', autofill=False)
|
||||
@@ -5103,13 +5197,15 @@ option: Str('in_role*', cli_name='in_roles')
|
||||
option: Str('in_subid*', cli_name='in_subids')
|
||||
option: Str('in_sudorule*', cli_name='in_sudorules')
|
||||
option: Str('initials?', autofill=False)
|
||||
option: Str('ipaidpconfiglink?', autofill=False, cli_name='idp')
|
||||
option: Str('ipaidpsub?', autofill=False, cli_name='idp_user_id')
|
||||
option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir')
|
||||
option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:'])
|
||||
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: DateTime('krbpasswordexpiration?', autofill=False, cli_name='password_expiration')
|
||||
option: DateTime('krbprincipalexpiration?', autofill=False, cli_name='principal_expiration')
|
||||
option: Principal('krbprincipalname*', autofill=False, cli_name='principal')
|
||||
@@ -5149,7 +5245,7 @@ output: ListOfEntries('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: Output('truncated', type=[<type 'bool'>])
|
||||
command: stageuser_mod/1
|
||||
args: 1,51,3
|
||||
args: 1,53,3
|
||||
arg: Str('uid', cli_name='login')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
@@ -5166,6 +5262,8 @@ option: Int('gidnumber?', autofill=False)
|
||||
option: Str('givenname?', autofill=False, cli_name='first')
|
||||
option: Str('homedirectory?', autofill=False, cli_name='homedir')
|
||||
option: Str('initials?', autofill=False)
|
||||
option: Str('ipaidpconfiglink?', autofill=False, cli_name='idp')
|
||||
option: Str('ipaidpsub?', autofill=False, cli_name='idp_user_id')
|
||||
option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir')
|
||||
option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:'])
|
||||
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||
@@ -5173,7 +5271,7 @@ option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: DateTime('krbpasswordexpiration?', autofill=False, cli_name='password_expiration')
|
||||
option: DateTime('krbprincipalexpiration?', autofill=False, cli_name='principal_expiration')
|
||||
option: Principal('krbprincipalname*', autofill=False, cli_name='principal')
|
||||
@@ -6156,7 +6254,7 @@ output: Entry('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: user_add/1
|
||||
args: 1,46,3
|
||||
args: 1,48,3
|
||||
arg: Str('uid', cli_name='login')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
@@ -6172,10 +6270,12 @@ option: Int('gidnumber?')
|
||||
option: Str('givenname', cli_name='first')
|
||||
option: Str('homedirectory?', cli_name='homedir')
|
||||
option: Str('initials?', autofill=True)
|
||||
option: Str('ipaidpconfiglink?', cli_name='idp')
|
||||
option: Str('ipaidpsub?', cli_name='idp_user_id')
|
||||
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
||||
option: Str('ipatokenradiusconfiglink?', cli_name='radius')
|
||||
option: Str('ipatokenradiususername?', cli_name='radius_username')
|
||||
option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: DateTime('krbpasswordexpiration?', cli_name='password_expiration')
|
||||
option: DateTime('krbprincipalexpiration?', cli_name='principal_expiration')
|
||||
option: Principal('krbprincipalname*', autofill=True, cli_name='principal')
|
||||
@@ -6278,7 +6378,7 @@ output: Output('result', type=[<type 'bool'>])
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: PrimaryKey('value')
|
||||
command: user_find/1
|
||||
args: 1,63,4
|
||||
args: 1,65,4
|
||||
arg: Str('criteria?')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
option: Str('carlicense*', autofill=False)
|
||||
@@ -6299,13 +6399,15 @@ option: Str('in_role*', cli_name='in_roles')
|
||||
option: Str('in_subid*', cli_name='in_subids')
|
||||
option: Str('in_sudorule*', cli_name='in_sudorules')
|
||||
option: Str('initials?', autofill=False)
|
||||
option: Str('ipaidpconfiglink?', autofill=False, cli_name='idp')
|
||||
option: Str('ipaidpsub?', autofill=False, cli_name='idp_user_id')
|
||||
option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir')
|
||||
option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:'])
|
||||
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: DateTime('krbpasswordexpiration?', autofill=False, cli_name='password_expiration')
|
||||
option: DateTime('krbprincipalexpiration?', autofill=False, cli_name='principal_expiration')
|
||||
option: Principal('krbprincipalname*', autofill=False, cli_name='principal')
|
||||
@@ -6348,7 +6450,7 @@ output: ListOfEntries('result')
|
||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||
output: Output('truncated', type=[<type 'bool'>])
|
||||
command: user_mod/1
|
||||
args: 1,52,3
|
||||
args: 1,54,3
|
||||
arg: Str('uid', cli_name='login')
|
||||
option: Str('addattr*', cli_name='addattr')
|
||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||
@@ -6365,6 +6467,8 @@ option: Int('gidnumber?', autofill=False)
|
||||
option: Str('givenname?', autofill=False, cli_name='first')
|
||||
option: Str('homedirectory?', autofill=False, cli_name='homedir')
|
||||
option: Str('initials?', autofill=False)
|
||||
option: Str('ipaidpconfiglink?', autofill=False, cli_name='idp')
|
||||
option: Str('ipaidpsub?', autofill=False, cli_name='idp_user_id')
|
||||
option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir')
|
||||
option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:'])
|
||||
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||
@@ -6372,7 +6476,7 @@ option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened'])
|
||||
option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'idp'])
|
||||
option: DateTime('krbpasswordexpiration?', autofill=False, cli_name='password_expiration')
|
||||
option: DateTime('krbprincipalexpiration?', autofill=False, cli_name='principal_expiration')
|
||||
option: Principal('krbprincipalname*', autofill=False, cli_name='principal')
|
||||
@@ -7023,6 +7127,12 @@ default: idoverrideuser_find/1
|
||||
default: idoverrideuser_mod/1
|
||||
default: idoverrideuser_remove_cert/1
|
||||
default: idoverrideuser_show/1
|
||||
default: idp/1
|
||||
default: idp_add/1
|
||||
default: idp_del/1
|
||||
default: idp_find/1
|
||||
default: idp_mod/1
|
||||
default: idp_show/1
|
||||
default: idrange/1
|
||||
default: idrange_add/1
|
||||
default: idrange_del/1
|
||||
@@ -7329,10 +7439,10 @@ default: vaultcontainer_del/1
|
||||
default: vaultcontainer_remove_owner/1
|
||||
default: vaultcontainer_show/1
|
||||
default: whoami/1
|
||||
capability: vault_aes_keywrap 2.246
|
||||
capability: messages 2.52
|
||||
capability: optional_uid_params 2.54
|
||||
capability: permissions2 2.69
|
||||
capability: primary_key_types 2.83
|
||||
capability: datetime_values 2.84
|
||||
capability: dns_name_values 2.88
|
||||
capability: vault_aes_keywrap 2.246
|
||||
|
@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
|
||||
# #
|
||||
########################################################
|
||||
define(IPA_API_VERSION_MAJOR, 2)
|
||||
# Last change: Add wrapping algorithm to vault archive/retrieve
|
||||
define(IPA_API_VERSION_MINOR, 246)
|
||||
# Last change: add idp API
|
||||
define(IPA_API_VERSION_MINOR, 247)
|
||||
|
||||
########################################################
|
||||
# Following values are auto-generated from values above
|
||||
|
@@ -155,6 +155,7 @@ DEFAULT_CONFIG = (
|
||||
('container_ca_renewal',
|
||||
DN(('cn', 'ca_renewal'), ('cn', 'ipa'), ('cn', 'etc'))),
|
||||
('container_subids', DN(('cn', 'subids'), ('cn', 'accounts'))),
|
||||
('container_idp', DN(('cn', 'idp'))),
|
||||
|
||||
# Ports, hosts, and URIs:
|
||||
# Following values do not have any reasonable default.
|
||||
|
@@ -87,12 +87,21 @@ def validate_nsaccountlock(entry_attrs):
|
||||
raise errors.ValidationError(name='nsaccountlock',
|
||||
error=_('must be TRUE or FALSE'))
|
||||
|
||||
|
||||
def radius_dn2pk(api, entry_attrs):
|
||||
cl = entry_attrs.get('ipatokenradiusconfiglink', None)
|
||||
if cl:
|
||||
pk = api.Object['radiusproxy'].get_primary_key_from_dn(cl[0])
|
||||
entry_attrs['ipatokenradiusconfiglink'] = [pk]
|
||||
|
||||
|
||||
def idp_dn2pk(api, entry_attrs):
|
||||
cl = entry_attrs.get('ipaidpconfiglink', None)
|
||||
if cl:
|
||||
pk = api.Object['idp'].get_primary_key_from_dn(cl[0])
|
||||
entry_attrs['ipaidpconfiglink'] = [pk]
|
||||
|
||||
|
||||
def convert_nsaccountlock(entry_attrs):
|
||||
if 'nsaccountlock' not in entry_attrs:
|
||||
entry_attrs['nsaccountlock'] = False
|
||||
@@ -161,7 +170,7 @@ class baseuser(LDAPObject):
|
||||
possible_objectclasses = [
|
||||
'meporiginentry', 'ipauserauthtypeclass', 'ipauser',
|
||||
'ipatokenradiusproxyuser', 'ipacertmapobject',
|
||||
'ipantuserattrs',
|
||||
'ipantuserattrs', 'ipaidpuser',
|
||||
]
|
||||
disallow_object_classes = ['krbticketpolicyaux']
|
||||
permission_filter_objectclasses = ['posixaccount']
|
||||
@@ -172,6 +181,7 @@ class baseuser(LDAPObject):
|
||||
'telephonenumber', 'title', 'memberof', 'nsaccountlock',
|
||||
'memberofindirect', 'ipauserauthtype', 'userclass',
|
||||
'ipatokenradiusconfiglink', 'ipatokenradiususername',
|
||||
'ipaidpconfiglink', 'ipaidpsub',
|
||||
'krbprincipalexpiration', 'usercertificate;binary',
|
||||
'krbprincipalname', 'krbcanonicalname',
|
||||
'ipacertmapdata', 'ipantlogonscript', 'ipantprofilepath',
|
||||
@@ -360,7 +370,8 @@ class baseuser(LDAPObject):
|
||||
cli_name='user_auth_type',
|
||||
label=_('User authentication types'),
|
||||
doc=_('Types of supported user authentication'),
|
||||
values=(u'password', u'radius', u'otp', u'pkinit', u'hardened'),
|
||||
values=(u'password', u'radius', u'otp', u'pkinit', u'hardened',
|
||||
u'idp'),
|
||||
),
|
||||
Str('userclass*',
|
||||
cli_name='class',
|
||||
@@ -376,6 +387,15 @@ class baseuser(LDAPObject):
|
||||
cli_name='radius_username',
|
||||
label=_('RADIUS proxy username'),
|
||||
),
|
||||
Str('ipaidpconfiglink?',
|
||||
cli_name='idp',
|
||||
label=_('External IdP configuration'),
|
||||
),
|
||||
Str('ipaidpsub?',
|
||||
cli_name='idp_user_id',
|
||||
label=_('External IdP user identifier'),
|
||||
doc=_('A string that identifies the user at external IdP'),
|
||||
),
|
||||
Str('departmentnumber*',
|
||||
label=_('Department Number'),
|
||||
),
|
||||
@@ -544,6 +564,9 @@ class baseuser_add(LDAPCreate):
|
||||
if entry_attrs.get('ipauserauthtype', None):
|
||||
add_missing_object_class(ldap, u'ipauserauthtypeclass', dn,
|
||||
entry_attrs, update=False)
|
||||
if entry_attrs.get('ipaidpconfiglink', None):
|
||||
add_missing_object_class(ldap, 'ipaidpuser', dn,
|
||||
entry_attrs, update=False)
|
||||
|
||||
def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||
assert isinstance(dn, DN)
|
||||
@@ -553,6 +576,7 @@ class baseuser_add(LDAPCreate):
|
||||
if 'nsaccountlock' in entry_attrs:
|
||||
convert_nsaccountlock(entry_attrs)
|
||||
radius_dn2pk(self.api, entry_attrs)
|
||||
idp_dn2pk(self.api, entry_attrs)
|
||||
|
||||
|
||||
class baseuser_del(LDAPDelete):
|
||||
@@ -637,7 +661,8 @@ class baseuser_mod(LDAPUpdate):
|
||||
def check_objectclass(self, ldap, dn, entry_attrs):
|
||||
# Some attributes may require additional object classes
|
||||
special_attrs = {'ipasshpubkey', 'ipauserauthtype', 'userclass',
|
||||
'ipatokenradiusconfiglink', 'ipatokenradiususername'}
|
||||
'ipatokenradiusconfiglink', 'ipatokenradiususername',
|
||||
'ipaidpconfiglink'}
|
||||
if special_attrs.intersection(entry_attrs):
|
||||
if 'objectclass' in entry_attrs:
|
||||
obj_classes = entry_attrs['objectclass']
|
||||
@@ -666,6 +691,15 @@ class baseuser_mod(LDAPUpdate):
|
||||
answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl)
|
||||
entry_attrs['ipatokenradiusconfiglink'] = answer
|
||||
|
||||
if 'ipaidpconfiglink' in entry_attrs:
|
||||
cl = entry_attrs['ipaidpconfiglink']
|
||||
if cl:
|
||||
if 'ipaidpuser' not in obj_classes:
|
||||
entry_attrs['objectclass'].append('ipaidpuser')
|
||||
|
||||
answer = self.api.Object['idp'].get_dn_if_exists(cl)
|
||||
entry_attrs['ipaidpconfiglink'] = answer
|
||||
|
||||
# Note: we could have used the method add_missing_object_class
|
||||
# but since the data is already fetched and lowercased in
|
||||
# obj_classes, it is more efficient to use the same approach
|
||||
@@ -708,6 +742,7 @@ class baseuser_mod(LDAPUpdate):
|
||||
convert_sshpubkey_post(entry_attrs)
|
||||
remove_sshpubkey_from_output_post(self.context, entry_attrs)
|
||||
radius_dn2pk(self.api, entry_attrs)
|
||||
idp_dn2pk(self.api, entry_attrs)
|
||||
|
||||
class baseuser_find(LDAPSearch):
|
||||
"""
|
||||
@@ -732,6 +767,11 @@ class baseuser_find(LDAPSearch):
|
||||
if cl in options:
|
||||
newoptions[cl] = self.api.Object['radiusproxy'].get_dn(options[cl])
|
||||
|
||||
# Ensure that the IdP config link is a dn, not just the name
|
||||
cl = 'ipaidpconfiglink'
|
||||
if cl in options:
|
||||
newoptions[cl] = self.api.Object['idp'].get_dn(options[cl])
|
||||
|
||||
def pre_common_callback(self, ldap, filters, attrs_list, base_dn, scope,
|
||||
*args, **options):
|
||||
add_sshpubkey_to_attrs_pre(self.context, attrs_list)
|
||||
@@ -761,6 +801,7 @@ class baseuser_show(LDAPRetrieve):
|
||||
convert_sshpubkey_post(entry_attrs)
|
||||
remove_sshpubkey_from_output_post(self.context, entry_attrs)
|
||||
radius_dn2pk(self.api, entry_attrs)
|
||||
idp_dn2pk(self.api, entry_attrs)
|
||||
|
||||
|
||||
class baseuser_add_manager(LDAPAddMember):
|
||||
|
@@ -269,7 +269,7 @@ class config(LDAPObject):
|
||||
label=_('Default user authentication types'),
|
||||
doc=_('Default types of supported user authentication'),
|
||||
values=(u'password', u'radius', u'otp',
|
||||
u'pkinit', u'hardened', u'disabled'),
|
||||
u'pkinit', u'hardened', u'idp', u'disabled'),
|
||||
),
|
||||
Bool('ipauserdefaultsubordinateid?',
|
||||
cli_name='user_default_subid',
|
||||
|
@@ -601,9 +601,11 @@ class host(LDAPObject):
|
||||
" Use 'pkinit' to allow PKINIT-based 2FA authentications."
|
||||
" Use 'hardened' to allow brute-force hardened password"
|
||||
" authentication by SPAKE or FAST."
|
||||
" Use 'idp' to allow External Identity Provider"
|
||||
" authentications."
|
||||
" With no indicator specified,"
|
||||
" all authentication mechanisms are allowed."),
|
||||
values=(u'radius', u'otp', u'pkinit', u'hardened'),
|
||||
values=(u'radius', u'otp', u'pkinit', u'hardened', u'idp'),
|
||||
),
|
||||
) + ticket_flags_params
|
||||
|
||||
|
442
ipaserver/plugins/idp.py
Normal file
442
ipaserver/plugins/idp.py
Normal file
@@ -0,0 +1,442 @@
|
||||
#
|
||||
# Copyright (C) 2021 FreeIPA Contributors see COPYING for license
|
||||
#
|
||||
import logging
|
||||
import string
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from .baseldap import (
|
||||
LDAPObject,
|
||||
LDAPCreate,
|
||||
LDAPDelete,
|
||||
LDAPUpdate,
|
||||
LDAPSearch,
|
||||
LDAPRetrieve)
|
||||
from ipalib import api, errors, Password, Str, StrEnum, _, ngettext
|
||||
from ipalib.plugable import Registry
|
||||
from ipapython.dn import DN
|
||||
from ipapython.ipautil import template_str
|
||||
from copy import deepcopy
|
||||
from itertools import chain
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
__doc__ = _("""
|
||||
External Identity Provider Servers
|
||||
""") + _("""
|
||||
Manage External Identity Provider Servers.
|
||||
""") + _("""
|
||||
IPA supports the use of an external Identity Provider for Oauth2.0 Device Flow
|
||||
authentication.
|
||||
""") + _("""
|
||||
EXAMPLES:
|
||||
""") + _("""
|
||||
Add a new external Identity Provider server:
|
||||
ipa idp-add MyIdP --client-id jhkQty13 \
|
||||
--auth-uri https://oauth2.idp.com/auth \
|
||||
--token-uri https://oauth2.idp.com/token --secret
|
||||
""") + _("""
|
||||
Add a new external Identity Provider server using github predefined endpoints:
|
||||
ipa idp-add MyIdp --client-id jhkQty13 --provider github --secret
|
||||
""") + _("""
|
||||
Find all external Identity Provider servers whose entries include the string
|
||||
"test.com":
|
||||
ipa idp-find test.com
|
||||
""") + _("""
|
||||
Examine the configuration of an external Identity Provider server:
|
||||
ipa idp-show MyIdP
|
||||
""") + _("""
|
||||
Change the secret:
|
||||
ipa idp-mod MyIdP --secret
|
||||
""") + _("""
|
||||
Delete an external Identity Provider server:
|
||||
ipa idp-del MyIdP
|
||||
""")
|
||||
|
||||
register = Registry()
|
||||
|
||||
|
||||
def normalize_baseurl(url):
|
||||
if url.startswith('https://'):
|
||||
return url[len('https://'):]
|
||||
return url
|
||||
|
||||
|
||||
def validate_uri(ugettext, uri):
|
||||
try:
|
||||
parsed = urlparse(uri, 'https')
|
||||
except Exception:
|
||||
return _('Invalid URI: not an https scheme')
|
||||
|
||||
if not parsed.netloc:
|
||||
return _('Invalid URI: missing netloc')
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@register()
|
||||
class idp(LDAPObject):
|
||||
"""
|
||||
Identity Provider object.
|
||||
"""
|
||||
container_dn = api.env.container_idp
|
||||
object_name = _('Identity Provider server')
|
||||
object_name_plural = _('Identity Provider servers')
|
||||
object_class = ['ipaidp']
|
||||
default_attributes = [
|
||||
'cn', 'ipaidpauthendpoint', 'ipaidpdevauthendpoint',
|
||||
'ipaidpuserinfoendpoint', 'ipaidpkeysendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpissuerurl',
|
||||
'ipaidpclientid', 'ipaidpscope', 'ipaidpsub',
|
||||
]
|
||||
search_attributes = [
|
||||
'cn', 'ipaidpauthendpoint', 'ipaidpdevauthendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpuserinfoendpoint',
|
||||
'ipaidpkeysendpoint', 'ipaidpscope', 'ipaidpsub',
|
||||
]
|
||||
allow_rename = True
|
||||
label = _('Identity Provider servers')
|
||||
label_singular = _('Identity Provider server')
|
||||
|
||||
takes_params = (
|
||||
Str('cn',
|
||||
cli_name='name',
|
||||
label=_('Identity Provider server name'),
|
||||
primary_key=True,
|
||||
),
|
||||
Str('ipaidpauthendpoint?',
|
||||
validate_uri,
|
||||
cli_name='auth_uri',
|
||||
label=_('Authorization URI'),
|
||||
doc=_('OAuth 2.0 authorization endpoint'),
|
||||
),
|
||||
Str('ipaidpdevauthendpoint?',
|
||||
validate_uri,
|
||||
cli_name='dev_auth_uri',
|
||||
label=_('Device authorization URI'),
|
||||
doc=_('Device authorization endpoint'),
|
||||
),
|
||||
Str('ipaidptokenendpoint?',
|
||||
validate_uri,
|
||||
cli_name='token_uri',
|
||||
label=_('Token URI'),
|
||||
doc=_('Token endpoint'),
|
||||
),
|
||||
Str('ipaidpuserinfoendpoint?',
|
||||
validate_uri,
|
||||
cli_name='userinfo_uri',
|
||||
label=_('User info URI'),
|
||||
doc=_('User information endpoint'),
|
||||
),
|
||||
Str('ipaidpkeysendpoint?',
|
||||
validate_uri,
|
||||
cli_name='keys_uri',
|
||||
label=_('JWKS URI'),
|
||||
doc=_('JWKS endpoint'),
|
||||
),
|
||||
Str('ipaidpissuerurl?',
|
||||
cli_name='issuer_url',
|
||||
label=_('OIDC URL'),
|
||||
doc=_(
|
||||
'The Identity Provider OIDC URL'),
|
||||
),
|
||||
Str('ipaidpclientid',
|
||||
cli_name='client_id',
|
||||
label=_('Client identifier'),
|
||||
doc=_(
|
||||
'OAuth 2.0 client identifier'),
|
||||
),
|
||||
Password('ipaidpclientsecret?',
|
||||
cli_name='secret',
|
||||
label=_('Secret'),
|
||||
doc=_('OAuth 2.0 client secret'),
|
||||
confirm=True,
|
||||
flags={'no_display'},
|
||||
),
|
||||
Str('ipaidpscope?',
|
||||
cli_name='scope',
|
||||
label=_('Scope'),
|
||||
doc=_('OAuth 2.0 scope. Multiple scopes separated by space'),
|
||||
),
|
||||
Str('ipaidpsub?',
|
||||
cli_name='idp_user_id',
|
||||
label=_('External IdP user identifier attribute'),
|
||||
doc=_('Attribute for user identity in OAuth 2.0 userinfo'),
|
||||
),
|
||||
)
|
||||
|
||||
permission_filter_objectclasses = ['ipaidp']
|
||||
managed_permissions = {
|
||||
'System: Add External IdP server': {
|
||||
'ipapermright': {'add'},
|
||||
'ipapermlocation': DN(container_dn, api.env.basedn),
|
||||
'ipapermtargetfilter': {
|
||||
'(objectclass=ipaidp)'},
|
||||
'default_privileges': {'External IdP server Administrators'}
|
||||
},
|
||||
'System: Read External IdP server': {
|
||||
'ipapermright': {'read', 'search', 'compare'},
|
||||
'ipapermdefaultattr': {
|
||||
'cn', 'objectclass', 'ipaidpauthendpoint',
|
||||
'ipaidpdevauthendpoint', 'ipaidpuserinfoendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpkeysendpoint',
|
||||
'ipaidpissuerurl', 'ipaidpclientid', 'ipaidpscope',
|
||||
'ipaidpsub',
|
||||
},
|
||||
'ipapermlocation': DN(container_dn, api.env.basedn),
|
||||
'ipapermtargetfilter': {
|
||||
'(objectclass=ipaidp)'},
|
||||
'default_privileges': {'External IdP server Administrators'}
|
||||
},
|
||||
'System: Modify External IdP server': {
|
||||
'ipapermright': {'write'},
|
||||
'ipapermlocation': DN(container_dn, api.env.basedn),
|
||||
'ipapermdefaultattr': {
|
||||
'cn', 'objectclass', 'ipaidpauthendpoint',
|
||||
'ipaidpdevauthendpoint', 'ipaidpuserinfoendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpkeysendpoint',
|
||||
'ipaidpissuerurl', 'ipaidpclientid', 'ipaidpscope',
|
||||
'ipaidpclientsecret', 'ipaidpsub',
|
||||
},
|
||||
'default_privileges': {'External IdP server Administrators'}
|
||||
},
|
||||
'System: Delete External IdP server': {
|
||||
'ipapermright': {'delete'},
|
||||
'ipapermlocation': DN(container_dn, api.env.basedn),
|
||||
'ipapermtargetfilter': {
|
||||
'(objectclass=ipaidp)'},
|
||||
'default_privileges': {'External IdP server Administrators'}
|
||||
},
|
||||
'System: Read External IdP server client secret': {
|
||||
'ipapermright': {'read', 'search', 'compare'},
|
||||
'ipapermlocation': DN(container_dn, api.env.basedn),
|
||||
'ipapermdefaultattr': {
|
||||
'cn', 'objectclass', 'ipaidpauthendpoint',
|
||||
'ipaidpdevauthendpoint', 'ipaidpuserinfoendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpissuerurl',
|
||||
'ipaidpkeysendpoint', 'ipaidpclientid', 'ipaidpscope',
|
||||
'ipaidpclientsecret', 'ipaidpsub',
|
||||
},
|
||||
'ipapermtargetfilter': {
|
||||
'(objectclass=ipaidp)'},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@register()
|
||||
class idp_add(LDAPCreate):
|
||||
__doc__ = _('Add a new Identity Provider server.')
|
||||
msg_summary = _('Added Identity Provider server "%(value)s"')
|
||||
|
||||
# List of pre-populated idp endpoints
|
||||
# key = provider,
|
||||
# value = dictionary of overidden attributes
|
||||
idp_providers = {
|
||||
'google': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://accounts.google.com/o/oauth2/auth',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://oauth2.googleapis.com/device/code',
|
||||
'ipaidptokenendpoint':
|
||||
'https://oauth2.googleapis.com/token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://openidconnect.googleapis.com/v1/userinfo',
|
||||
'ipaidpkeysendpoint':
|
||||
'https://www.googleapis.com/oauth2/v3/certs',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email'},
|
||||
'github': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://github.com/login/oauth/authorize',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://github.com/login/device/code',
|
||||
'ipaidptokenendpoint':
|
||||
'https://github.com/login/oauth/access_token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://api.github.com/user',
|
||||
'ipaidpscope': 'user',
|
||||
'ipaidpsub': 'login'},
|
||||
'microsoft': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/'
|
||||
'authorize',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/'
|
||||
'devicecode',
|
||||
'ipaidptokenendpoint':
|
||||
'https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/'
|
||||
'token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://graph.microsoft.com/oidc/userinfo',
|
||||
'ipaidpkeysendpoint':
|
||||
'https://login.microsoftonline.com/common/discovery/v2.0/keys',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email',
|
||||
},
|
||||
'okta': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/authorize',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/device/authorize',
|
||||
'ipaidptokenendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://${ipaidpbaseurl}/oauth2/v1/userinfo',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email'},
|
||||
'keycloak': {
|
||||
'ipaidpauthendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/auth',
|
||||
'ipaidpdevauthendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/auth/device',
|
||||
'ipaidptokenendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/token',
|
||||
'ipaidpuserinfoendpoint':
|
||||
'https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/'
|
||||
'openid-connect/userinfo',
|
||||
'ipaidpscope': 'openid email',
|
||||
'ipaidpsub': 'email'},
|
||||
}
|
||||
|
||||
takes_options = LDAPCreate.takes_options + (
|
||||
StrEnum(
|
||||
'ipaidpprovider?',
|
||||
cli_name='provider',
|
||||
label=_('IdP provider template'),
|
||||
doc=_('Choose a pre-defined template to use'),
|
||||
flags={'virtual_attribute', 'no_create', 'no_update', 'nosearch'},
|
||||
values=tuple(idp_providers.keys()),
|
||||
),
|
||||
Str('ipaidporg?',
|
||||
cli_name='organization',
|
||||
label=_('Organization'),
|
||||
doc=_('Organization ID or Realm name for IdP provider templates'),
|
||||
flags={'virtual_attribute', 'no_create', 'no_update', 'nosearch'}),
|
||||
Str('ipaidpbaseurl?',
|
||||
cli_name='base_url',
|
||||
label=_('Base URL'),
|
||||
doc=_('Base URL for IdP provider templates'),
|
||||
normalizer=normalize_baseurl,
|
||||
flags={'virtual_attribute', 'no_create', 'no_update', 'nosearch'}),
|
||||
)
|
||||
|
||||
def _convert_provider_to_endpoints(self, entry_attrs,
|
||||
provider=None, elements=None):
|
||||
"""
|
||||
Converts provider options to auth-uri and token-uri
|
||||
"""
|
||||
if provider:
|
||||
if provider not in self.idp_providers:
|
||||
raise errors.ValidationError(
|
||||
name='provider',
|
||||
error=_('unknown provider')
|
||||
)
|
||||
# For each string in the template check if a variable
|
||||
# is required, it is provided as an option
|
||||
points = deepcopy(self.idp_providers[provider])
|
||||
r = string.Template.pattern
|
||||
for (k,v) in points.items():
|
||||
# build list of variables to be replaced
|
||||
subs = list(chain.from_iterable(
|
||||
(filter(None, s) for s in r.findall(v))))
|
||||
if subs:
|
||||
for s in subs:
|
||||
if s not in elements:
|
||||
raise errors.ValidationError(
|
||||
name=self.options[s].cli_name,
|
||||
error=_('value is missing'))
|
||||
points[k] = template_str(v, elements)
|
||||
entry_attrs.update(points)
|
||||
|
||||
def get_options(self):
|
||||
# Some URIs are not mandatory as they can be built from the value of
|
||||
# provider.
|
||||
for option in super(idp_add, self).get_options():
|
||||
if option.name in ('ipaidpauthendpoint', 'ipaidpdevauthendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpuserinfoendpoint',
|
||||
'ipaidpkeysendpoint'):
|
||||
yield option.clone(required=False, alwaysask=False)
|
||||
else:
|
||||
yield option
|
||||
|
||||
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
|
||||
**options):
|
||||
# The valid calls are
|
||||
# ipa idp-add --provider provider IDP [client details]
|
||||
# ipa idp-add --dev-auth-uri auth --token-uri token-uri IDP
|
||||
auth = options.get('ipaidpauthendpoint')
|
||||
devauth = options.get('ipaidpdevauthendpoint')
|
||||
token = options.get('ipaidptokenendpoint')
|
||||
userinfo = options.get('ipaidpuserinfoendpoint')
|
||||
jwks = options.get('ipaidpkeysendpoint')
|
||||
provider = options.get('ipaidpprovider')
|
||||
|
||||
# If the provider is supplied, reject individual endpoints
|
||||
if any([devauth, auth, token, userinfo, jwks]):
|
||||
if provider:
|
||||
raise errors.MutuallyExclusiveError(
|
||||
reason=_('cannot specify both individual endpoints '
|
||||
'and IdP provider'))
|
||||
|
||||
# If there is no --provider, individual endpoints required
|
||||
if not provider and not devauth:
|
||||
raise errors.RequirementError(name='dev-auth-uri or provider')
|
||||
if not provider and not auth:
|
||||
raise errors.RequirementError(name='auth-uri or provider')
|
||||
if not provider and not token:
|
||||
raise errors.RequirementError(name='token-uri or provider')
|
||||
if not provider and not userinfo:
|
||||
raise errors.RequirementError(name='userinfo-uri or provider')
|
||||
|
||||
# if the command is called with --provider we need to add
|
||||
# ipaidpdevauthendpoint, ipaidpauthendpoint, and ipaidptokenendpoint
|
||||
# to the attrs list in order to display the resulting value in
|
||||
# the command output
|
||||
for endpoint in ['ipaidpauthendpoint', 'ipaidpdevauthendpoint',
|
||||
'ipaidptokenendpoint', 'ipaidpuserinfoendpoint',
|
||||
'ipaidpkeysendpoint']:
|
||||
if endpoint not in attrs_list:
|
||||
attrs_list.append(endpoint)
|
||||
|
||||
self._convert_provider_to_endpoints(entry_attrs,
|
||||
provider=provider,
|
||||
elements=options)
|
||||
return dn
|
||||
|
||||
|
||||
@register()
|
||||
class idp_del(LDAPDelete):
|
||||
__doc__ = _('Delete an Identity Provider server.')
|
||||
msg_summary = _('Deleted Identity Provider server "%(value)s"')
|
||||
|
||||
|
||||
@register()
|
||||
class idp_mod(LDAPUpdate):
|
||||
__doc__ = _('Modify an Identity Provider server.')
|
||||
msg_summary = _('Modified Identity Provider server "%(value)s"')
|
||||
|
||||
|
||||
@register()
|
||||
class idp_find(LDAPSearch):
|
||||
__doc__ = _('Search for Identity Provider servers.')
|
||||
msg_summary = ngettext(
|
||||
'%(count)d Identity Provider server matched',
|
||||
'%(count)d Identity Provider servers matched', 0
|
||||
)
|
||||
|
||||
def get_options(self):
|
||||
# do not propose --client-id or --secret in ipa idp-find
|
||||
for option in super(idp_find, self).get_options():
|
||||
if option.name in ('ipaidpclientsecret', 'ipaidpclientid'):
|
||||
option = option.clone(flags={'no_option'})
|
||||
|
||||
yield option
|
||||
|
||||
|
||||
@register()
|
||||
class idp_show(LDAPRetrieve):
|
||||
__doc__ = _('Display information about an Identity Provider '
|
||||
'server.')
|
@@ -75,7 +75,7 @@ _default_values = {
|
||||
# These attributes never have non-optional values, so they should be
|
||||
# ignored in post callbacks
|
||||
_option_based_attrs = ('krbauthindmaxticketlife', 'krbauthindmaxrenewableage')
|
||||
_supported_options = ('otp', 'radius', 'pkinit', 'hardened')
|
||||
_supported_options = ('otp', 'radius', 'pkinit', 'hardened', 'idp')
|
||||
|
||||
@register()
|
||||
class krbtpolicy(baseldap.LDAPObject):
|
||||
@@ -187,6 +187,18 @@ class krbtpolicy(baseldap.LDAPObject):
|
||||
label=_('Hardened max renew'),
|
||||
doc=_('Hardened ticket maximum renewable age (seconds)'),
|
||||
minvalue=1),
|
||||
Int('krbauthindmaxticketlife_idp?',
|
||||
cli_name='idp_maxlife',
|
||||
label=_('IdP max life'),
|
||||
doc=_('External Identity Provider ticket maximum ticket life '
|
||||
'(seconds)'),
|
||||
minvalue=1),
|
||||
Int('krbauthindmaxrenewableage_idp?',
|
||||
cli_name='idp_maxrenew',
|
||||
label=_('IdP max renew'),
|
||||
doc=_('External Identity Provider ticket maximum renewable '
|
||||
'age (seconds)'),
|
||||
minvalue=1),
|
||||
)
|
||||
|
||||
def get_dn(self, *keys, **kwargs):
|
||||
|
@@ -576,7 +576,7 @@ class service(LDAPObject):
|
||||
" authentication by SPAKE or FAST."
|
||||
" With no indicator specified,"
|
||||
" all authentication mechanisms are allowed."),
|
||||
values=(u'radius', u'otp', u'pkinit', u'hardened'),
|
||||
values=(u'radius', u'otp', u'pkinit', u'hardened', u'idp'),
|
||||
),
|
||||
) + ticket_flags_params
|
||||
|
||||
|
@@ -388,6 +388,19 @@ class stageuser_add(baseuser_add):
|
||||
answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl)
|
||||
entry_attrs['ipatokenradiusconfiglink'] = answer
|
||||
|
||||
if 'ipaidpconfiglink' in entry_attrs:
|
||||
cl = entry_attrs['ipaidpconfiglink']
|
||||
if cl:
|
||||
if 'objectclass' not in entry_attrs:
|
||||
_entry = ldap.get_entry(dn, ['objectclass'])
|
||||
entry_attrs['objectclass'] = _entry['objectclass']
|
||||
|
||||
if 'ipaidpuser' not in entry_attrs['objectclass']:
|
||||
entry_attrs['objectclass'].append('ipaidpuser')
|
||||
|
||||
answer = self.api.Object['idp'].get_dn_if_exists(cl)
|
||||
entry_attrs['ipaidpconfiglink'] = answer
|
||||
|
||||
self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys,
|
||||
**options)
|
||||
|
||||
|
@@ -626,6 +626,14 @@ class user_add(baseuser_add):
|
||||
answer = self.api.Object['radiusproxy'].get_dn_if_exists(rcl)
|
||||
entry_attrs['ipatokenradiusconfiglink'] = answer
|
||||
|
||||
rcl = entry_attrs.get('ipaidpconfiglink', None)
|
||||
if rcl:
|
||||
if 'ipaidpuser' not in entry_attrs['objectclass']:
|
||||
entry_attrs['objectclass'].append('ipaidpuser')
|
||||
|
||||
answer = self.api.Object['idp'].get_dn_if_exists(rcl)
|
||||
entry_attrs['ipaidpconfiglink'] = answer
|
||||
|
||||
self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys,
|
||||
**options)
|
||||
|
||||
|
@@ -425,6 +425,7 @@ AstroidBuilder(MANAGER).string_build(textwrap.dedent(
|
||||
api.env.container_hbacservicegroup = DN()
|
||||
api.env.container_host = DN()
|
||||
api.env.container_hostgroup = DN()
|
||||
api.env.container_idp = DN()
|
||||
api.env.container_locations = DN()
|
||||
api.env.container_masters = DN()
|
||||
api.env.container_netgroup = DN()
|
||||
|
Reference in New Issue
Block a user