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:
Alexander Bokovoy
2022-05-03 11:40:24 +03:00
parent fd19bdfd54
commit 10e18c3dc7
13 changed files with 670 additions and 30 deletions

10
ACI.txt
View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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):

View File

@@ -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',

View File

@@ -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
View 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.')

View File

@@ -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):

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()