mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Redesign subid feature
Subordinate ids are now handled by a new plugin class and stored in separate entries in the cn=subids,cn=accounts subtree. Signed-off-by: Christian Heimes <cheimes@redhat.com> Reviewed-By: Francois Cami <fcami@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
committed by
Rob Crittenden
parent
1c4ae37293
commit
c78d1341ad
12
ACI.txt
12
ACI.txt
@@ -61,7 +61,7 @@ aci: (targetattr = "cn || description || ipacertprofilestoreissued")(targetfilte
|
|||||||
dn: cn=certprofiles,cn=ca,dc=ipa,dc=example
|
dn: cn=certprofiles,cn=ca,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacertprofilestoreissued || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipacertprofile)")(version 3.0;acl "permission:System: Read Certificate Profiles";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacertprofilestoreissued || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipacertprofile)")(version 3.0;acl "permission:System: Read Certificate Profiles";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=ipaconfig,cn=etc,dc=ipa,dc=example
|
dn: cn=ipaconfig,cn=etc,dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxhostnamelength || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxhostnamelength || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserdefaultsubordinateid || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
|
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Add Group Password Policy costemplate";allow (add) groupdn = "ldap:///cn=System: Add Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Add Group Password Policy costemplate";allow (add) groupdn = "ldap:///cn=System: Add Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
|
dn: cn=costemplates,cn=accounts,dc=ipa,dc=example
|
||||||
@@ -318,6 +318,14 @@ dn: cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example
|
|||||||
aci: (targetattr = "krblastpwdchange || krbpasswordexpiration || krbprincipalkey || userpassword")(target = "ldap:///uid=*,cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Reset Preserved User password";allow (read,search,write) groupdn = "ldap:///cn=System: Reset Preserved User password,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetattr = "krblastpwdchange || krbpasswordexpiration || krbprincipalkey || userpassword")(target = "ldap:///uid=*,cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Reset Preserved User password";allow (read,search,write) groupdn = "ldap:///cn=System: Reset Preserved User password,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: dc=ipa,dc=example
|
dn: dc=ipa,dc=example
|
||||||
aci: (target_to = "ldap:///cn=users,cn=accounts,dc=ipa,dc=example")(target_from = "ldap:///cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example")(targetfilter = "(objectclass=nsContainer)")(version 3.0;acl "permission:System: Undelete User";allow (moddn) groupdn = "ldap:///cn=System: Undelete User,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (target_to = "ldap:///cn=users,cn=accounts,dc=ipa,dc=example")(target_from = "ldap:///cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example")(targetfilter = "(objectclass=nsContainer)")(version 3.0;acl "permission:System: Undelete User";allow (moddn) groupdn = "ldap:///cn=System: Undelete User,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
|
dn: cn=subids,cn=accounts,dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "description || ipaowner")(targetfilter = "(objectclass=ipasubordinateidentry)")(version 3.0;acl "permission:System: Manage Subordinate Ids";allow (write) groupdn = "ldap:///cn=System: Manage Subordinate Ids,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
|
dn: cn=subids,cn=accounts,dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "createtimestamp || description || entryusn || ipaowner || ipasubgidcount || ipasubgidnumber || ipasubuidcount || ipasubuidnumber || ipauniqueid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipasubordinateidentry)")(version 3.0;acl "permission:System: Read Subordinate Id Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
|
dn: cn=subids,cn=accounts,dc=ipa,dc=example
|
||||||
|
aci: (targetattr = "numsubordinates")(target = "ldap:///cn=subids,cn=accounts,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Subordinate Id Count";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
|
dn: cn=subids,cn=accounts,dc=ipa,dc=example
|
||||||
|
aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(version 3.0;acl "permission:System: Remove Subordinate Ids";allow (delete) groupdn = "ldap:///cn=System: Remove Subordinate Ids,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=sudocmds,cn=sudo,dc=ipa,dc=example
|
dn: cn=sudocmds,cn=sudo,dc=ipa,dc=example
|
||||||
aci: (targetfilter = "(objectclass=ipasudocmd)")(version 3.0;acl "permission:System: Add Sudo Command";allow (add) groupdn = "ldap:///cn=System: Add Sudo Command,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
aci: (targetfilter = "(objectclass=ipasudocmd)")(version 3.0;acl "permission:System: Add Sudo Command";allow (add) groupdn = "ldap:///cn=System: Add Sudo Command,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
||||||
dn: cn=sudocmds,cn=sudo,dc=ipa,dc=example
|
dn: cn=sudocmds,cn=sudo,dc=ipa,dc=example
|
||||||
@@ -375,7 +383,7 @@ aci: (targetattr = "audio || businesscategory || carlicense || departmentnumber
|
|||||||
dn: dc=ipa,dc=example
|
dn: dc=ipa,dc=example
|
||||||
aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || loginshell || modifytimestamp || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || loginshell || modifytimestamp || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
|
||||||
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "ipasshpubkey || ipasubgidcount || ipasubgidnumber || ipasubuidcount || ipasubuidnumber || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "ipasshpubkey || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
||||||
aci: (targetattr = "krbcanonicalname || krblastpwdchange || krbpasswordexpiration || krbprincipalaliases || krbprincipalexpiration || krbprincipalname || krbprincipaltype || nsaccountlock")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Kerberos Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
aci: (targetattr = "krbcanonicalname || krblastpwdchange || krbpasswordexpiration || krbprincipalaliases || krbprincipalexpiration || krbprincipalname || krbprincipaltype || nsaccountlock")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Kerberos Attributes";allow (compare,read,search) userdn = "ldap:///all";)
|
||||||
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
dn: cn=users,cn=accounts,dc=ipa,dc=example
|
||||||
|
|||||||
153
API.txt
153
API.txt
@@ -1076,7 +1076,7 @@ args: 0,1,1
|
|||||||
option: Str('version?')
|
option: Str('version?')
|
||||||
output: Output('result')
|
output: Output('result')
|
||||||
command: config_mod/1
|
command: config_mod/1
|
||||||
args: 0,28,3
|
args: 0,29,3
|
||||||
option: Str('addattr*', cli_name='addattr')
|
option: Str('addattr*', cli_name='addattr')
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
option: Str('ca_renewal_master_server?', autofill=False)
|
option: Str('ca_renewal_master_server?', autofill=False)
|
||||||
@@ -1099,6 +1099,7 @@ option: Int('ipasearchtimelimit?', autofill=False, cli_name='searchtimelimit')
|
|||||||
option: Str('ipaselinuxusermapdefault?', autofill=False)
|
option: Str('ipaselinuxusermapdefault?', autofill=False)
|
||||||
option: Str('ipaselinuxusermaporder?', 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'disabled'])
|
||||||
|
option: Bool('ipauserdefaultsubordinateid?', autofill=False, cli_name='user_default_subid')
|
||||||
option: Str('ipauserobjectclasses*', autofill=False, cli_name='userobjectclasses')
|
option: Str('ipauserobjectclasses*', autofill=False, cli_name='userobjectclasses')
|
||||||
option: IA5Str('ipausersearchfields?', autofill=False, cli_name='usersearch')
|
option: IA5Str('ipausersearchfields?', autofill=False, cli_name='usersearch')
|
||||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||||
@@ -4974,7 +4975,7 @@ output: Entry('result')
|
|||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
output: PrimaryKey('value')
|
output: PrimaryKey('value')
|
||||||
command: stageuser_add/1
|
command: stageuser_add/1
|
||||||
args: 1,46,3
|
args: 1,45,3
|
||||||
arg: Str('uid', cli_name='login')
|
arg: Str('uid', cli_name='login')
|
||||||
option: Str('addattr*', cli_name='addattr')
|
option: Str('addattr*', cli_name='addattr')
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
@@ -4992,7 +4993,6 @@ option: Str('givenname', cli_name='first')
|
|||||||
option: Str('homedirectory?', cli_name='homedir')
|
option: Str('homedirectory?', cli_name='homedir')
|
||||||
option: Str('initials?', autofill=True)
|
option: Str('initials?', autofill=True)
|
||||||
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
||||||
option: Int('ipasubuidnumber?', cli_name='subuid')
|
|
||||||
option: Str('ipatokenradiusconfiglink?', cli_name='radius')
|
option: Str('ipatokenradiusconfiglink?', cli_name='radius')
|
||||||
option: Str('ipatokenradiususername?', cli_name='radius_username')
|
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'])
|
||||||
@@ -5099,14 +5099,13 @@ option: Str('in_group*', cli_name='in_groups')
|
|||||||
option: Str('in_hbacrule*', cli_name='in_hbacrules')
|
option: Str('in_hbacrule*', cli_name='in_hbacrules')
|
||||||
option: Str('in_netgroup*', cli_name='in_netgroups')
|
option: Str('in_netgroup*', cli_name='in_netgroups')
|
||||||
option: Str('in_role*', cli_name='in_roles')
|
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('in_sudorule*', cli_name='in_sudorules')
|
||||||
option: Str('initials?', autofill=False)
|
option: Str('initials?', autofill=False)
|
||||||
option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir')
|
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: 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('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||||
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||||
option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid')
|
|
||||||
option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid')
|
|
||||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
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'])
|
||||||
@@ -5123,6 +5122,7 @@ option: Str('not_in_group*', cli_name='not_in_groups')
|
|||||||
option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules')
|
option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules')
|
||||||
option: Str('not_in_netgroup*', cli_name='not_in_netgroups')
|
option: Str('not_in_netgroup*', cli_name='not_in_netgroups')
|
||||||
option: Str('not_in_role*', cli_name='not_in_roles')
|
option: Str('not_in_role*', cli_name='not_in_roles')
|
||||||
|
option: Str('not_in_subid*', cli_name='not_in_subids')
|
||||||
option: Str('not_in_sudorule*', cli_name='not_in_sudorules')
|
option: Str('not_in_sudorule*', cli_name='not_in_sudorules')
|
||||||
option: Str('ou?', autofill=False, cli_name='orgunit')
|
option: Str('ou?', autofill=False, cli_name='orgunit')
|
||||||
option: Str('pager*', autofill=False)
|
option: Str('pager*', autofill=False)
|
||||||
@@ -5148,7 +5148,7 @@ output: ListOfEntries('result')
|
|||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
output: Output('truncated', type=[<type 'bool'>])
|
output: Output('truncated', type=[<type 'bool'>])
|
||||||
command: stageuser_mod/1
|
command: stageuser_mod/1
|
||||||
args: 1,52,3
|
args: 1,51,3
|
||||||
arg: Str('uid', cli_name='login')
|
arg: Str('uid', cli_name='login')
|
||||||
option: Str('addattr*', cli_name='addattr')
|
option: Str('addattr*', cli_name='addattr')
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
@@ -5170,7 +5170,6 @@ option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_d
|
|||||||
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||||
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||||
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
||||||
option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid')
|
|
||||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
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'])
|
||||||
@@ -5263,6 +5262,100 @@ option: Str('version?')
|
|||||||
output: Entry('result')
|
output: Entry('result')
|
||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
output: PrimaryKey('value')
|
output: PrimaryKey('value')
|
||||||
|
command: subid_add/1
|
||||||
|
args: 1,8,3
|
||||||
|
arg: Str('ipauniqueid?', cli_name='id')
|
||||||
|
option: Str('addattr*', cli_name='addattr')
|
||||||
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
|
option: Str('description?', cli_name='desc')
|
||||||
|
option: Str('ipaowner', cli_name='owner')
|
||||||
|
option: Int('ipasubuidnumber?', cli_name='subuid')
|
||||||
|
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: subid_del/1
|
||||||
|
args: 1,2,3
|
||||||
|
arg: Str('ipauniqueid+', cli_name='id')
|
||||||
|
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: subid_find/1
|
||||||
|
args: 1,11,4
|
||||||
|
arg: Str('criteria?')
|
||||||
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
|
option: Str('description?', autofill=False, cli_name='desc')
|
||||||
|
option: Str('ipaowner?', autofill=False, cli_name='owner')
|
||||||
|
option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid')
|
||||||
|
option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid')
|
||||||
|
option: Str('ipauniqueid?', autofill=False, cli_name='id')
|
||||||
|
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: subid_generate/1
|
||||||
|
args: 0,4,3
|
||||||
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
|
option: Str('ipaowner?', cli_name='owner')
|
||||||
|
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||||
|
option: Str('version?')
|
||||||
|
output: Entry('result')
|
||||||
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
|
output: PrimaryKey('value')
|
||||||
|
command: subid_match/1
|
||||||
|
args: 1,7,4
|
||||||
|
arg: Str('criteria?')
|
||||||
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
|
option: Int('ipasubuidnumber', autofill=False, cli_name='subuid')
|
||||||
|
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: subid_mod/1
|
||||||
|
args: 1,8,3
|
||||||
|
arg: Str('ipauniqueid', cli_name='id')
|
||||||
|
option: Str('addattr*', cli_name='addattr')
|
||||||
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
|
option: Str('delattr*', cli_name='delattr')
|
||||||
|
option: Str('description?', autofill=False, cli_name='desc')
|
||||||
|
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||||
|
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: subid_show/1
|
||||||
|
args: 1,4,3
|
||||||
|
arg: Str('ipauniqueid', cli_name='id')
|
||||||
|
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: subid_stats/1
|
||||||
|
args: 0,3,2
|
||||||
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
|
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
||||||
|
option: Str('version?')
|
||||||
|
output: Entry('result')
|
||||||
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
command: sudocmd_add/1
|
command: sudocmd_add/1
|
||||||
args: 1,7,3
|
args: 1,7,3
|
||||||
arg: Str('sudocmd', cli_name='command')
|
arg: Str('sudocmd', cli_name='command')
|
||||||
@@ -6062,7 +6155,7 @@ output: Entry('result')
|
|||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
output: PrimaryKey('value')
|
output: PrimaryKey('value')
|
||||||
command: user_add/1
|
command: user_add/1
|
||||||
args: 1,47,3
|
args: 1,46,3
|
||||||
arg: Str('uid', cli_name='login')
|
arg: Str('uid', cli_name='login')
|
||||||
option: Str('addattr*', cli_name='addattr')
|
option: Str('addattr*', cli_name='addattr')
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
@@ -6079,7 +6172,6 @@ option: Str('givenname', cli_name='first')
|
|||||||
option: Str('homedirectory?', cli_name='homedir')
|
option: Str('homedirectory?', cli_name='homedir')
|
||||||
option: Str('initials?', autofill=True)
|
option: Str('initials?', autofill=True)
|
||||||
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
option: Str('ipasshpubkey*', cli_name='sshpubkey')
|
||||||
option: Int('ipasubuidnumber?', cli_name='subuid')
|
|
||||||
option: Str('ipatokenradiusconfiglink?', cli_name='radius')
|
option: Str('ipatokenradiusconfiglink?', cli_name='radius')
|
||||||
option: Str('ipatokenradiususername?', cli_name='radius_username')
|
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'])
|
||||||
@@ -6161,16 +6253,6 @@ option: Str('version?')
|
|||||||
output: Entry('result')
|
output: Entry('result')
|
||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
output: PrimaryKey('value')
|
output: PrimaryKey('value')
|
||||||
command: user_auto_subid/1
|
|
||||||
args: 1,4,3
|
|
||||||
arg: Str('uid', cli_name='login')
|
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
|
||||||
option: Flag('no_members', autofill=True, default=False)
|
|
||||||
option: Flag('raw', autofill=True, cli_name='raw', default=False)
|
|
||||||
option: Str('version?')
|
|
||||||
output: Entry('result')
|
|
||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
|
||||||
output: PrimaryKey('value')
|
|
||||||
command: user_del/1
|
command: user_del/1
|
||||||
args: 1,3,3
|
args: 1,3,3
|
||||||
arg: Str('uid+', cli_name='login')
|
arg: Str('uid+', cli_name='login')
|
||||||
@@ -6213,14 +6295,13 @@ option: Str('in_group*', cli_name='in_groups')
|
|||||||
option: Str('in_hbacrule*', cli_name='in_hbacrules')
|
option: Str('in_hbacrule*', cli_name='in_hbacrules')
|
||||||
option: Str('in_netgroup*', cli_name='in_netgroups')
|
option: Str('in_netgroup*', cli_name='in_netgroups')
|
||||||
option: Str('in_role*', cli_name='in_roles')
|
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('in_sudorule*', cli_name='in_sudorules')
|
||||||
option: Str('initials?', autofill=False)
|
option: Str('initials?', autofill=False)
|
||||||
option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir')
|
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: 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('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||||
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||||
option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid')
|
|
||||||
option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid')
|
|
||||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
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'])
|
||||||
@@ -6237,6 +6318,7 @@ option: Str('not_in_group*', cli_name='not_in_groups')
|
|||||||
option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules')
|
option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules')
|
||||||
option: Str('not_in_netgroup*', cli_name='not_in_netgroups')
|
option: Str('not_in_netgroup*', cli_name='not_in_netgroups')
|
||||||
option: Str('not_in_role*', cli_name='not_in_roles')
|
option: Str('not_in_role*', cli_name='not_in_roles')
|
||||||
|
option: Str('not_in_subid*', cli_name='not_in_subids')
|
||||||
option: Str('not_in_sudorule*', cli_name='not_in_sudorules')
|
option: Str('not_in_sudorule*', cli_name='not_in_sudorules')
|
||||||
option: Bool('nsaccountlock?', autofill=False, cli_name='disabled', default=False)
|
option: Bool('nsaccountlock?', autofill=False, cli_name='disabled', default=False)
|
||||||
option: Str('ou?', autofill=False, cli_name='orgunit')
|
option: Str('ou?', autofill=False, cli_name='orgunit')
|
||||||
@@ -6264,23 +6346,8 @@ output: Output('count', type=[<type 'int'>])
|
|||||||
output: ListOfEntries('result')
|
output: ListOfEntries('result')
|
||||||
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
|
||||||
output: Output('truncated', type=[<type 'bool'>])
|
output: Output('truncated', type=[<type 'bool'>])
|
||||||
command: user_match_subid/1
|
|
||||||
args: 1,8,4
|
|
||||||
arg: Str('criteria?')
|
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
|
||||||
option: Int('ipasubuidnumber', autofill=False, cli_name='subuid')
|
|
||||||
option: Flag('no_members', autofill=True, default=True)
|
|
||||||
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: user_mod/1
|
command: user_mod/1
|
||||||
args: 1,53,3
|
args: 1,52,3
|
||||||
arg: Str('uid', cli_name='login')
|
arg: Str('uid', cli_name='login')
|
||||||
option: Str('addattr*', cli_name='addattr')
|
option: Str('addattr*', cli_name='addattr')
|
||||||
option: Flag('all', autofill=True, cli_name='all', default=False)
|
option: Flag('all', autofill=True, cli_name='all', default=False)
|
||||||
@@ -6302,7 +6369,6 @@ option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_d
|
|||||||
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script')
|
||||||
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path')
|
||||||
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
|
||||||
option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid')
|
|
||||||
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius')
|
||||||
option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username')
|
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'])
|
||||||
@@ -7138,6 +7204,15 @@ default: stageuser_remove_certmapdata/1
|
|||||||
default: stageuser_remove_manager/1
|
default: stageuser_remove_manager/1
|
||||||
default: stageuser_remove_principal/1
|
default: stageuser_remove_principal/1
|
||||||
default: stageuser_show/1
|
default: stageuser_show/1
|
||||||
|
default: subid/1
|
||||||
|
default: subid_add/1
|
||||||
|
default: subid_del/1
|
||||||
|
default: subid_find/1
|
||||||
|
default: subid_generate/1
|
||||||
|
default: subid_match/1
|
||||||
|
default: subid_mod/1
|
||||||
|
default: subid_show/1
|
||||||
|
default: subid_stats/1
|
||||||
default: sudocmd/1
|
default: sudocmd/1
|
||||||
default: sudocmd_add/1
|
default: sudocmd_add/1
|
||||||
default: sudocmd_del/1
|
default: sudocmd_del/1
|
||||||
@@ -7216,12 +7291,10 @@ default: user_add_cert/1
|
|||||||
default: user_add_certmapdata/1
|
default: user_add_certmapdata/1
|
||||||
default: user_add_manager/1
|
default: user_add_manager/1
|
||||||
default: user_add_principal/1
|
default: user_add_principal/1
|
||||||
default: user_auto_subid/1
|
|
||||||
default: user_del/1
|
default: user_del/1
|
||||||
default: user_disable/1
|
default: user_disable/1
|
||||||
default: user_enable/1
|
default: user_enable/1
|
||||||
default: user_find/1
|
default: user_find/1
|
||||||
default: user_match_subid/1
|
|
||||||
default: user_mod/1
|
default: user_mod/1
|
||||||
default: user_remove_cert/1
|
default: user_remove_cert/1
|
||||||
default: user_remove_certmapdata/1
|
default: user_remove_certmapdata/1
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
# Central management of subordinate user and group ids
|
# Central management of subordinate user and group ids
|
||||||
|
|
||||||
|
## OUTDATED
|
||||||
|
|
||||||
|
**The design document does not reflect new implementation yet!**
|
||||||
|
|
||||||
Subordinate ids are a Linux Kernel feature to grant a user additional
|
Subordinate ids are a Linux Kernel feature to grant a user additional
|
||||||
user and group id ranges. Amongst others the feature can be used
|
user and group id ranges. Amongst others the feature can be used
|
||||||
by container runtime engies to implement rootless containers.
|
by container runtime engies to implement rootless containers.
|
||||||
@@ -74,8 +78,10 @@ basic use cases. Some restrictions may be lifted in the future.
|
|||||||
to the same value.
|
to the same value.
|
||||||
* counts are hard-coded to value 65536
|
* counts are hard-coded to value 65536
|
||||||
* once assigned subids cannot be removed
|
* once assigned subids cannot be removed
|
||||||
* IPA does not support multiple subordinate id ranges. Contrary to
|
* IPA does not support multiple subordinate id ranges, yet. Contrary to
|
||||||
``/etc/subuid``, users are limited to one set of subordinate ids.
|
``/etc/subuid``, users are limited to one set of subordinate ids. The
|
||||||
|
limitation is implemented with a unique index on owner reference and
|
||||||
|
can be lifted in the future.
|
||||||
* subids are auto-assigned. Auto-assignment is currently emulated
|
* subids are auto-assigned. Auto-assignment is currently emulated
|
||||||
until 389-DS has been extended to support DNA with step interval.
|
until 389-DS has been extended to support DNA with step interval.
|
||||||
* subids are allocated from hard-coded range
|
* subids are allocated from hard-coded range
|
||||||
@@ -118,20 +124,21 @@ to servers. The DNA plug-in guarantees uniqueness across servers.
|
|||||||
### LDAP schema extension
|
### LDAP schema extension
|
||||||
|
|
||||||
The subordinate id feature introduces a new auxiliar object class
|
The subordinate id feature introduces a new auxiliar object class
|
||||||
``ipaSubordinateId`` with four required attributes ``ipaSubUidNumber``,
|
``ipaSubordinateId`` with five required attributes ``ipaOwner``,
|
||||||
``ipaSubUidCount``, ``ipaSubGidNumber``, and ``ipaSubGidCount``. The
|
``ipaSubUidNumber``, ``ipaSubUidCount``, ``ipaSubGidNumber``, and
|
||||||
attributes with ``number`` suffix store the start value of the interval.
|
``ipaSubGidCount``. The attributes with ``number`` suffix store the
|
||||||
The ``count`` attributes contain the size of the interval including the
|
start value of the interval. The ``count`` attributes contain the
|
||||||
start value. The maximum subid is
|
size of the interval including the start value. The maximum subid is
|
||||||
``ipaSubUidNumber + ipaSubUidCount - 1``.
|
``ipaSubUidNumber + ipaSubUidCount - 1``. The ``ipaOwner`` attribute
|
||||||
|
is a reference to the owning user.
|
||||||
|
|
||||||
All four attributes are single-value ``INTEGER`` type with standard
|
All count and number attributes are single-value ``INTEGER`` type with
|
||||||
integer matching rules. OIDs ``2.16.840.1.113730.3.8.23.8`` and
|
standard integer matching rules. OIDs ``2.16.840.1.113730.3.8.23.8`` and
|
||||||
``2.16.840.1.113730.3.8.23.11`` are reserved for future use.
|
``2.16.840.1.113730.3.8.23.11`` are reserved for future use.
|
||||||
|
|
||||||
```raw
|
```raw
|
||||||
attributeTypes: (
|
attributeTypes: (
|
||||||
2.16.840.1.113730.3.8.23.6
|
2.16.840.1.113730.3.8.23.7
|
||||||
NAME 'ipaSubUidNumber'
|
NAME 'ipaSubUidNumber'
|
||||||
DESC 'Numerical subordinate user ID (range start value)'
|
DESC 'Numerical subordinate user ID (range start value)'
|
||||||
EQUALITY integerMatch ORDERING integerOrderingMatch
|
EQUALITY integerMatch ORDERING integerOrderingMatch
|
||||||
@@ -139,7 +146,7 @@ attributeTypes: (
|
|||||||
X-ORIGIN 'IPA v4.9'
|
X-ORIGIN 'IPA v4.9'
|
||||||
)
|
)
|
||||||
attributeTypes: (
|
attributeTypes: (
|
||||||
2.16.840.1.113730.3.8.23.7
|
2.16.840.1.113730.3.8.23.8
|
||||||
NAME 'ipaSubUidCount'
|
NAME 'ipaSubUidCount'
|
||||||
DESC 'Subordinate user ID count (range size)'
|
DESC 'Subordinate user ID count (range size)'
|
||||||
EQUALITY integerMatch ORDERING integerOrderingMatch
|
EQUALITY integerMatch ORDERING integerOrderingMatch
|
||||||
@@ -147,7 +154,7 @@ attributeTypes: (
|
|||||||
X-ORIGIN 'IPA v4.9'
|
X-ORIGIN 'IPA v4.9'
|
||||||
)
|
)
|
||||||
attributeTypes: (
|
attributeTypes: (
|
||||||
2.16.840.1.113730.3.8.23.9
|
2.16.840.1.113730.3.8.23.10
|
||||||
NAME 'ipaSubGidNumber'
|
NAME 'ipaSubGidNumber'
|
||||||
DESC 'Numerical subordinate group ID (range start value)'
|
DESC 'Numerical subordinate group ID (range start value)'
|
||||||
EQUALITY integerMatch ORDERING integerOrderingMatch
|
EQUALITY integerMatch ORDERING integerOrderingMatch
|
||||||
@@ -155,7 +162,7 @@ attributeTypes: (
|
|||||||
X-ORIGIN 'IPA v4.9'
|
X-ORIGIN 'IPA v4.9'
|
||||||
)
|
)
|
||||||
attributeTypes: (
|
attributeTypes: (
|
||||||
2.16.840.1.113730.3.8.23.10
|
2.16.840.1.113730.3.8.23.11
|
||||||
NAME 'ipaSubGidCount'
|
NAME 'ipaSubGidCount'
|
||||||
DESC 'Subordinate group ID count (range size)'
|
DESC 'Subordinate group ID count (range size)'
|
||||||
EQUALITY integerMatch ORDERING integerOrderingMatch
|
EQUALITY integerMatch ORDERING integerOrderingMatch
|
||||||
@@ -164,51 +171,96 @@ attributeTypes: (
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
The ``ipaSubordinateId`` object class is an auxiliar subclass of
|
The ``ipaOwner`` attribute is a single-value DN attribute that refers
|
||||||
|
to user entry that owns the subordinate ID entry. The proposal does not
|
||||||
|
reuse any of the existing attributes like ``owner`` or ``member``,
|
||||||
|
because they are all multi-valued.
|
||||||
|
|
||||||
|
```
|
||||||
|
attributeTypes: (
|
||||||
|
2.16.840.1.113730.3.8.23.13
|
||||||
|
NAME 'ipaOwner'
|
||||||
|
DESC 'Owner of an entry'
|
||||||
|
SUP distinguishedName
|
||||||
|
EQUALITY distinguishedNameMatch
|
||||||
|
SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
|
||||||
|
SINGLE-VALUE
|
||||||
|
X-ORIGIN 'IPA v4.9')
|
||||||
|
```
|
||||||
|
|
||||||
|
The ``ipaSubordinateId`` object class is an auxiliary subclass of
|
||||||
``top`` and requires all four subordinate id attributes as well as
|
``top`` and requires all four subordinate id attributes as well as
|
||||||
``uidNumber``. It does not subclass ``posixAccount`` to make
|
``ipaOwner`` attribute``
|
||||||
the class reusable in idview overrides later.
|
|
||||||
|
|
||||||
```raw
|
```raw
|
||||||
objectClasses: (
|
objectClasses: (
|
||||||
2.16.840.1.113730.3.8.24.4
|
2.16.840.1.113730.3.8.24.4
|
||||||
NAME 'ipaSubordinateId'
|
NAME 'ipaSubordinateId'
|
||||||
DESC 'Subordinate uid and gid for users'
|
DESC 'Subordinate uid and gid for users'
|
||||||
SUP top AUXILIARY
|
SUP top
|
||||||
MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount )
|
AUXILIARY
|
||||||
|
MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount )
|
||||||
X-ORIGIN 'IPA v4.9'
|
X-ORIGIN 'IPA v4.9'
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
The ``ipaSubordinateGid`` and ``ipaSubordinateUid`` are defined for
|
The ``ipaSubordinateGid`` and ``ipaSubordinateUid`` are defined for
|
||||||
future use. IPA always assumes the presence of ``ipaSubordinateId`` and
|
future use. IPA always assumes the presence of ``ipaSubordinateId``.
|
||||||
does not use these object classes.
|
|
||||||
|
|
||||||
```raw
|
```raw
|
||||||
objectClasses: (
|
objectClasses: (
|
||||||
2.16.840.1.113730.3.8.24.2
|
2.16.840.1.113730.3.8.24.2
|
||||||
NAME 'ipaSubordinateUid'
|
NAME 'ipaSubordinateUid'
|
||||||
DESC 'Subordinate uids for users, see subuid(5)'
|
DESC 'Subordinate uids for users, see subuid(5)'
|
||||||
SUP top AUXILIARY
|
SUP top
|
||||||
MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount )
|
AUXILIARY
|
||||||
|
MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount )
|
||||||
X-ORIGIN 'IPA v4.9'
|
X-ORIGIN 'IPA v4.9'
|
||||||
)
|
)
|
||||||
objectClasses: (
|
objectClasses: (
|
||||||
2.16.840.1.113730.3.8.24.3
|
2.16.840.1.113730.3.8.24.3
|
||||||
NAME 'ipaSubordinateGid'
|
NAME 'ipaSubordinateGid'
|
||||||
DESC 'Subordinate gids for users, see subgid(5)'
|
DESC 'Subordinate gids for users, see subgid(5)'
|
||||||
SUP top AUXILIARY
|
SUP top
|
||||||
MUST ( uidNumber $ ipaSubGidNumber $ ipaSubGidCount )
|
AUXILIARY
|
||||||
|
MUST ( ipaOwner $ ipaSubGidNumber $ ipaSubGidCount )
|
||||||
X-ORIGIN 'IPA v4.9'
|
X-ORIGIN 'IPA v4.9'
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Index
|
Subordinate id entries have the structural object class
|
||||||
|
``ipaSubordinateIdEntry`` and one or more of the auxiliary object
|
||||||
|
classes ``ipaSubordinateId``, ``ipaSubordinateGid``, or
|
||||||
|
``ipaSubordinateUid``. ``ipaUniqueId`` is used as a primary key (RDN).
|
||||||
|
|
||||||
|
```raw
|
||||||
|
objectClasses: (
|
||||||
|
2.16.840.1.113730.3.8.24.5
|
||||||
|
NAME 'ipaSubordinateIdEntry'
|
||||||
|
DESC 'Subordinate uid and gid entry'
|
||||||
|
SUP top
|
||||||
|
STRUCTURAL
|
||||||
|
MUST ( ipaUniqueId ) MAY ( description ) X-ORIGIN 'IPA v4.9'
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### cn=subids,cn=accounts,$SUFFIX
|
||||||
|
|
||||||
|
Subordiante ids and ACIs are stored in the new subtree
|
||||||
|
``cn=subids,cn=accounts,$SUFFIX``.
|
||||||
|
|
||||||
|
### Index, integrity, memberOf
|
||||||
|
|
||||||
The attributes ``ipaSubUidNumber`` and ``ipaSubGidNumber`` are index
|
The attributes ``ipaSubUidNumber`` and ``ipaSubGidNumber`` are index
|
||||||
for ``pres`` and ``eq`` with ``nsMatchingRule: integerOrderingMatch``
|
for ``pres`` and ``eq`` with ``nsMatchingRule: integerOrderingMatch``
|
||||||
to enable efficient ``=``, ``>=``, and ``<=`` searches.
|
to enable efficient ``=``, ``>=``, and ``<=`` searches.
|
||||||
|
|
||||||
|
The attribute ``ipaOwner`` is indexed for ``pres`` and ``eq``. This DN
|
||||||
|
attribute is also checked for referential integrity and uniqueness
|
||||||
|
within the ``cn=subids,cn=accounts,$SUFFIX`` subtree. The memberOf
|
||||||
|
plugin creates back-references for ``ipaOwner`` references.
|
||||||
|
|
||||||
|
|
||||||
### Distributed numeric assignment (DNA) plug-in extension
|
### Distributed numeric assignment (DNA) plug-in extension
|
||||||
|
|
||||||
Subordinate id auto-assignment requires an extension of 389-DS'
|
Subordinate id auto-assignment requires an extension of 389-DS'
|
||||||
@@ -221,6 +273,85 @@ option ``dnaStepAttr`` (name is tentative) will tell the DNA plug-in
|
|||||||
to use the value of entry attributes as step size.
|
to use the value of entry attributes as step size.
|
||||||
|
|
||||||
|
|
||||||
|
## IPA plugins and commands
|
||||||
|
|
||||||
|
The config plugin has a new option to enable or disable generation of
|
||||||
|
subordinate id entries for new users:
|
||||||
|
|
||||||
|
```raw
|
||||||
|
$ ipa config-mod --user-default-subid=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Subordinate ids are managed by a new plugin class. The ``subid-add``
|
||||||
|
and ``subid-del`` commands are hidden from command line. New subordinate
|
||||||
|
ids are generated and auto-assigned with ``subid-generate``.
|
||||||
|
|
||||||
|
```raw
|
||||||
|
$ ipa help subid
|
||||||
|
Topic commands:
|
||||||
|
subid-find Search for subordinate id.
|
||||||
|
subid-generate Generate and auto-assign subuid and subgid range to user entry
|
||||||
|
subid-match Match users by any subordinate uid in their range
|
||||||
|
subid-mod Modify a subordinate id.
|
||||||
|
subid-show Display information about a subordinate id.
|
||||||
|
subid-stats Subordinate id statistics
|
||||||
|
```
|
||||||
|
|
||||||
|
```raw
|
||||||
|
$ ipa subid-generate --owner testuser9
|
||||||
|
-----------------------------------------------------------
|
||||||
|
Added subordinate id "aa28f132-457c-488b-82e1-d123727e4f81"
|
||||||
|
-----------------------------------------------------------
|
||||||
|
Unique ID: aa28f132-457c-488b-82e1-d123727e4f81
|
||||||
|
Description: auto-assigned subid
|
||||||
|
Owner: testuser9
|
||||||
|
SubUID range start: 3922132992
|
||||||
|
SubUID range size: 65536
|
||||||
|
SubGID range start: 3922132992
|
||||||
|
SubGID range size: 65536
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```raw
|
||||||
|
$ ipa subid-find --owner testuser9
|
||||||
|
------------------------
|
||||||
|
1 subordinate id matched
|
||||||
|
------------------------
|
||||||
|
Unique ID: aa28f132-457c-488b-82e1-d123727e4f81
|
||||||
|
Owner: testuser9
|
||||||
|
SubUID range start: 3922132992
|
||||||
|
SubUID range size: 65536
|
||||||
|
SubGID range start: 3922132992
|
||||||
|
SubGID range size: 65536
|
||||||
|
----------------------------
|
||||||
|
Number of entries returned 1
|
||||||
|
----------------------------
|
||||||
|
```
|
||||||
|
|
||||||
|
```raw
|
||||||
|
$ ipa -vv subid-stats
|
||||||
|
...
|
||||||
|
ipa: INFO: Response: {
|
||||||
|
"error": null,
|
||||||
|
"id": 0,
|
||||||
|
"principal": "admin@IPASUBID.TEST",
|
||||||
|
"result": {
|
||||||
|
"result": {
|
||||||
|
"assigned_subids": 20,
|
||||||
|
"baseid": 2147483648,
|
||||||
|
"dna_remaining": 4293394434,
|
||||||
|
"rangesize": 2147352576,
|
||||||
|
"remaining_subids": 65512
|
||||||
|
},
|
||||||
|
"summary": "65532 remaining subordinate id ranges"
|
||||||
|
},
|
||||||
|
"version": "4.10.0.dev"
|
||||||
|
}
|
||||||
|
-------------------------------------
|
||||||
|
65532 remaining subordinate id ranges
|
||||||
|
-------------------------------------
|
||||||
|
```
|
||||||
|
|
||||||
## Permissions, Privileges, Roles
|
## Permissions, Privileges, Roles
|
||||||
|
|
||||||
### Self-servive RBAC
|
### Self-servive RBAC
|
||||||
@@ -246,6 +377,13 @@ be modified or deleted.
|
|||||||
* Privilege: *Subordinate ID Administrators*
|
* Privilege: *Subordinate ID Administrators*
|
||||||
* default privilege role: *User Administrator*
|
* default privilege role: *User Administrator*
|
||||||
|
|
||||||
|
### Managed permissions
|
||||||
|
|
||||||
|
* *System: Read Subordinate Id Attributes* (all authenticated users)
|
||||||
|
* *System: Read Subordinate Id Count* (all authenticated usrs)
|
||||||
|
* *System: Manage Subordinate Ids* (User Administrators)
|
||||||
|
* *System: Remove Subordinate Ids* (User Administrators)
|
||||||
|
|
||||||
|
|
||||||
## Workflows
|
## Workflows
|
||||||
|
|
||||||
@@ -257,12 +395,12 @@ to assign subordinate ids to users.
|
|||||||
|
|
||||||
Users with *User Administrator* role and members of the *admins* group
|
Users with *User Administrator* role and members of the *admins* group
|
||||||
have permission to auto-assign new subordinate ids to any user. Auto
|
have permission to auto-assign new subordinate ids to any user. Auto
|
||||||
assignment can be performed with new ``user-auto-subid`` command on the
|
assignment can be performed with new ``subid-generate`` command on the
|
||||||
command line or with the *Auto assign subordinate ids* action in the
|
command line or with the *Auto assign subordinate ids* action in the
|
||||||
*Actions* drop-down menu in the web UI.
|
*Actions* drop-down menu in the web UI.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ipa user-auto-subid someusername
|
$ ipa subid-generate --owner myusername
|
||||||
```
|
```
|
||||||
|
|
||||||
### Self-service for group members
|
### Self-service for group members
|
||||||
@@ -279,27 +417,14 @@ $ ipa role-add-member "Subordinate ID Selfservice User" --groups=ipausers
|
|||||||
```
|
```
|
||||||
|
|
||||||
This allows members of ``ipausers`` to request subordinate ids with
|
This allows members of ``ipausers`` to request subordinate ids with
|
||||||
the ``user-auto-subid`` command or the *Auto assign subordinate ids*
|
the ``subid-generate`` command or the *Auto assign subordinate ids*
|
||||||
action in the web UI.
|
action in the web UI (**TODO** not implemented yet). The command picks
|
||||||
|
the name of the current user principal automatically.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ipa user-auto-subid myusername
|
$ ipa subid-generate
|
||||||
```
|
```
|
||||||
|
|
||||||
### Auto assignment with user default object class
|
|
||||||
|
|
||||||
Admins can also enable auto-assignment of subordinate ids for all new
|
|
||||||
users by adding ``ipasubordinateid`` as a default user objectclass.
|
|
||||||
This can be accomplished in the web UI under "IPA Server" /
|
|
||||||
"Configuration" / "Default user objectclasses" or on the command line
|
|
||||||
with:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ ipa config-mod --addattr="ipaUserObjectClasses=ipasubordinateid"
|
|
||||||
```
|
|
||||||
|
|
||||||
**NOTE:** The objectclass must be written all lower case.
|
|
||||||
|
|
||||||
### ipa-subid tool
|
### ipa-subid tool
|
||||||
|
|
||||||
Finally IPA includes a new tool for mass-assignment of subordinate ids.
|
Finally IPA includes a new tool for mass-assignment of subordinate ids.
|
||||||
@@ -355,26 +480,25 @@ gid range. The new command ``user-match-subid`` can be used to find a
|
|||||||
user by any subordinate id in their range.
|
user by any subordinate id in their range.
|
||||||
|
|
||||||
```raw
|
```raw
|
||||||
$ ipa user-match-subid --subuid=2153185287
|
$ ipa subid-match --subuid=2147549183
|
||||||
User login: asmith
|
------------------------
|
||||||
First name: Alice
|
1 subordinate id matched
|
||||||
Last name: Smith
|
------------------------
|
||||||
...
|
Name: asmith-auto
|
||||||
SubUID range start: 2153185280
|
Owner: asmith
|
||||||
|
SubUID range start: 2147483648
|
||||||
SubUID range size: 65536
|
SubUID range size: 65536
|
||||||
SubGID range start: 2153185280
|
SubGID range start: 2147483648
|
||||||
SubGID range size: 65536
|
SubGID range size: 65536
|
||||||
----------------------------
|
----------------------------
|
||||||
Number of entries returned 1
|
Number of entries returned 1
|
||||||
----------------------------
|
----------------------------
|
||||||
$ ipa user-match-subid --subuid=2153185279
|
$ ipa user-match-subid --subuid=2147549184
|
||||||
User login: bjones
|
Name: bjones-auto
|
||||||
First name: Bob
|
Owner: bjones
|
||||||
Last name: Jones
|
SubUID range start: 2147549184
|
||||||
...
|
|
||||||
SubUID range start: 2153119744
|
|
||||||
SubUID range size: 65536
|
SubUID range size: 65536
|
||||||
SubGID range start: 2153119744
|
SubGID range start: 2147549184
|
||||||
SubGID range size: 65536
|
SubGID range size: 65536
|
||||||
----------------------------
|
----------------------------
|
||||||
Number of entries returned 1
|
Number of entries returned 1
|
||||||
@@ -383,61 +507,54 @@ Number of entries returned 1
|
|||||||
|
|
||||||
## SSSD integration
|
## SSSD integration
|
||||||
|
|
||||||
* base: ``cn=accounts,$SUFFIX`` / ``cn=users,cn=accounts,$SUFFIX``
|
* search base: ``cn=subids,cn=accounts,$SUFFIX``
|
||||||
* scope: ``SCOPE_SUBTREE`` (2) / ``SCOPE_ONELEVEL`` (1)
|
* scope: ``SCOPE_ONELEVEL`` (1)
|
||||||
* user filter: should include ``(objectClass=posixAccount)``
|
* filter: ``(objectClass=ipaSubordinateId)``
|
||||||
* attributes: ``uidNumber ipaSubUidNumber ipaSubUidCount ipaSubGidNumber ipaSubGidCount``
|
* attributes: ``ipaOwner ipaSubUidNumber ipaSubUidCount ipaSubGidNumber ipaSubGidCount``
|
||||||
|
|
||||||
SSSD can safely assume that only *user accounts* of type ``posixAccount``
|
|
||||||
have subordinate ids. In the first revision there are no other entries
|
|
||||||
with subordinate ids. The ``posixAccount`` object class has ``uid``
|
|
||||||
(user login name) and ``uidNumber`` (numeric user id) as mandatory
|
|
||||||
attributes. The ``uid`` attribute is guaranteed to be unique across
|
|
||||||
all user accounts in an IPA domain.
|
|
||||||
|
|
||||||
The ``uidNumber`` attribute is commonly unique, too. However it's
|
|
||||||
technically possible that an administrator has assigned the same
|
|
||||||
numeric user id to multiple users. Automatically assigned uid numbers
|
|
||||||
don't conflict. SSSD should treat multiple users with same numeric
|
|
||||||
user id as an error.
|
|
||||||
|
|
||||||
The attribute ``ipaSubUidNumber`` is always accompanied by
|
The attribute ``ipaSubUidNumber`` is always accompanied by
|
||||||
``ipaSubUidCount`` and ``ipaSubGidNumber`` is always accompanied
|
``ipaSubUidCount`` and ``ipaSubGidNumber`` is always accompanied
|
||||||
by ``ipaSubGidCount``. In revision 1 the presence of
|
by ``ipaSubGidCount``. In revision 1 the presence of
|
||||||
``ipaSubUidNumber`` implies presence of the other three attributes.
|
``ipaSubUidNumber`` implies presence of the other three attributes.
|
||||||
All four subordinate id attributes and ``uidNumber`` are single-value
|
All four subordinate id attributes are single-value ``INTEGER`` types.
|
||||||
``INTEGER`` types. Any value outside of range of ``uint32_t`` must
|
Any value outside of range of ``uint32_t`` must treated as invalid.
|
||||||
treated as invalid. SSSD will never see the DNA magic value ``-1``
|
SSSD will never see the DNA magic value ``-1`` in
|
||||||
in ``cn=accounts,$SUFFIX`` subtree.
|
``cn=accounts,$SUFFIX`` subtree. In revision 1 each user subordinate
|
||||||
|
id entry is assigned to exactly one user and each user has either 0
|
||||||
|
or 1 subid.
|
||||||
|
|
||||||
IPA recommends that SSSD simply extends its existing query for user
|
IPA recommends that SSSD uses LDAP deref controls for ``ipaOwner``
|
||||||
accounts and requests the four subordinate attributes additionally to
|
attribute to fetch ``uidNumber`` from the user object.
|
||||||
RFC 2307 attributes ``rfc2307_user_map``. SSSD can directly take the
|
|
||||||
values and return them without further processing, e.g.
|
|
||||||
``uidNumber:ipaSubUidNumber:ipaSubUidCount`` for ``/etc/subuid``.
|
|
||||||
|
|
||||||
Filters for additional cases:
|
### Deref control example
|
||||||
|
|
||||||
* subuid filter (find user with subuid by numeric uid):
|
```raw
|
||||||
``&((objectClass=posixAccount)(ipaSubUidNumber=*)(uidNumber=$UID))``,
|
$ ldapsearch -L -E '!deref=ipaOwner:uid,uidNumber' \
|
||||||
``(&(objectClass=ipaSubordinateId)(uidNumber=$UID))``, or similar
|
-b 'cn=subids,cn=accounts,dc=ipasubid,dc=test' \
|
||||||
* subuid enumeration filter:
|
'(ipaOwner=uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test)'
|
||||||
``&((objectClass=posixAccount)(ipaSubUidNumber=*)(uidNumber=*))``,
|
|
||||||
``(objectClass=ipaSubordinateId)``, or similar
|
dn: ipauniqueid=35c02c93-3799-4551-a355-ebbf042e431c,cn=subids,cn=accounts,dc=ipasubid,dc=test
|
||||||
* subgid filter (find user with subgid by numeric uid):
|
# control: 1.3.6.1.4.1.4203.666.5.16 false MIQAAABxMIQAAABrBAhpcGFPd25lcgQ3dWlk
|
||||||
``&((objectClass=posixAccount)(ipaSubGidNumber=*)(uidNumber=$UID))``,
|
PXRlc3R1c2VyMTAsY249dXNlcnMsY249YWNjb3VudHMsZGM9aXBhc3ViaWQsZGM9dGVzdKCEAAAAIj
|
||||||
``(&(objectClass=ipaSubordinateId)(uidNumber=$UID))``, or similar
|
CEAAAAHAQJdWlkTnVtYmVyMYQAAAALBAk2MjgwMDAwMTE=
|
||||||
* subgid enumeration filter:
|
# ipaOwner: <uidNumber=628000011>;uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test
|
||||||
``&((objectClass=posixAccount)(ipaSubGidNumber=*)(uidNumber=*))``,
|
|
||||||
``(objectClass=ipaSubordinateId)``, or similar
|
ipaOwner: uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test
|
||||||
|
ipaUniqueID: 35c02c93-3799-4551-a355-ebbf042e431c
|
||||||
|
description: auto-assigned subid
|
||||||
|
objectClass: ipasubordinateidentry
|
||||||
|
objectClass: ipasubordinategid
|
||||||
|
objectClass: ipasubordinateuid
|
||||||
|
objectClass: ipasubordinateid
|
||||||
|
objectClass: top
|
||||||
|
ipaSubUidNumber: 3434020864
|
||||||
|
ipaSubGidNumber: 3434020864
|
||||||
|
ipaSubUidCount: 65536
|
||||||
|
ipaSubGidCount: 65536
|
||||||
|
```
|
||||||
|
|
||||||
## Implementation details
|
## Implementation details
|
||||||
|
|
||||||
* The four subid attributes are not included in
|
|
||||||
``baseuser.default_attributes`` on purpose. The ``config-mod``
|
|
||||||
command does not permit removal of a user default objectclasses
|
|
||||||
when the class is the last provider of an attribute in
|
|
||||||
``default_attributes``.
|
|
||||||
* ``ipaSubordinateId`` object class does not subclass the other two
|
* ``ipaSubordinateId`` object class does not subclass the other two
|
||||||
object classes. LDAP supports
|
object classes. LDAP supports
|
||||||
``SUP ( ipaSubordinateGid $ ipaSubordinateUid )`` but 389-DS only
|
``SUP ( ipaSubordinateGid $ ipaSubordinateUid )`` but 389-DS only
|
||||||
@@ -459,6 +576,13 @@ Filters for additional cases:
|
|||||||
* Shared DNA configuration entries in ``cn=dna,cn=ipa,cn=etc,$SUFFIX``
|
* Shared DNA configuration entries in ``cn=dna,cn=ipa,cn=etc,$SUFFIX``
|
||||||
are automatically removed by existing code. Server and replication
|
are automatically removed by existing code. Server and replication
|
||||||
plug-ins search and delete entries by ``dnaHostname`` attribute.
|
plug-ins search and delete entries by ``dnaHostname`` attribute.
|
||||||
|
* ``ipaSubordinateId`` entries no longer contains ``uidNumber``
|
||||||
|
attribute. I considered to use CoS plugin to provide ``uidNumber``
|
||||||
|
as virtual attribute. However it's not possible to
|
||||||
|
``objectClass: cosIndirectDefinition`` with
|
||||||
|
``cosIndirectSpecifier: ipaOwner`` and
|
||||||
|
``cosAttribute: uidNumber override`` for the task. Indexes and
|
||||||
|
searches don't work with virtual attributes.
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
@@ -466,3 +590,23 @@ Filters for additional cases:
|
|||||||
* remove ``fake_dna_plugin`` hack from ``baseuser`` plug-in.
|
* remove ``fake_dna_plugin`` hack from ``baseuser`` plug-in.
|
||||||
* add custom range type for idranges and teach AD trust, sidgen, and
|
* add custom range type for idranges and teach AD trust, sidgen, and
|
||||||
range overlap check code to deal with new range type.
|
range overlap check code to deal with new range type.
|
||||||
|
|
||||||
|
#### user-del --preserve
|
||||||
|
|
||||||
|
Preserving a user with ``ipa user-del --preserve`` currently fails with
|
||||||
|
an ObjectclassViolation error (err=65). The problem is caused by
|
||||||
|
configuration of referential integrity postoperation plug-in. The
|
||||||
|
plug-in excludes the subtree
|
||||||
|
``nsslapd-pluginexcludeentryscope: cn=provisioning,$SUFFX``. Preserved
|
||||||
|
users are moved into the staging area of the provisioning subtree.
|
||||||
|
Since the ``ipaOwner`` DN target is now out of scope, the plug-in
|
||||||
|
attempts to delete references. However ``ipaOwner`` is a required
|
||||||
|
attribute, which triggers the objectclass violation.
|
||||||
|
|
||||||
|
Possible solutions
|
||||||
|
|
||||||
|
* Don't preserve subid entries
|
||||||
|
* Implement preserve feature for subid entries and move subids of
|
||||||
|
preserved users into
|
||||||
|
``cn=deleted subids,cn=accounts,cn=provisioning,$SUFFIX`` subtree.
|
||||||
|
* Change ``nsslapd-pluginexcludeentryscope`` setting
|
||||||
|
|||||||
@@ -5,15 +5,16 @@
|
|||||||
##
|
##
|
||||||
dn: cn=schema
|
dn: cn=schema
|
||||||
# subordinate ids
|
# subordinate ids
|
||||||
# range ceiling OIDs are reserved for future use (operational attribute?)
|
# range ceiling OIDs are reserved for future use
|
||||||
# object class requires uidNumber but does not subclass posixAccount so we
|
attributeTypes: ( 2.16.840.1.113730.3.8.23.7 NAME 'ipaSubUidNumber' DESC 'Numerical subordinate user ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
# can re-use the object class in idview overrides later.
|
attributeTypes: ( 2.16.840.1.113730.3.8.23.8 NAME 'ipaSubUidCount' DESC 'Subordinate user ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
attributeTypes: ( 2.16.840.1.113730.3.8.23.6 NAME 'ipaSubUidNumber' DESC 'Numerical subordinate user ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
# attributeTypes: ( 2.16.840.1.113730.3.8.23.9 NAME 'ipaSubUidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
attributeTypes: ( 2.16.840.1.113730.3.8.23.7 NAME 'ipaSubUidCount' DESC 'Subordinate user ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
attributeTypes: ( 2.16.840.1.113730.3.8.23.10 NAME 'ipaSubGidNumber' DESC 'Numerical subordinate group ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
# attributeTypes: ( 2.16.840.1.113730.3.8.23.8 NAME 'ipaSubUidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
attributeTypes: ( 2.16.840.1.113730.3.8.23.11 NAME 'ipaSubGidCount' DESC 'Subordinate group ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
attributeTypes: ( 2.16.840.1.113730.3.8.23.9 NAME 'ipaSubGidNumber' DESC 'Numerical subordinate group ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
# attributeTypes: ( 2.16.840.1.113730.3.8.23.12 NAME 'ipaSubGidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
attributeTypes: ( 2.16.840.1.113730.3.8.23.10 NAME 'ipaSubGidCount' DESC 'Subordinate group ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
attributeTypes: ( 2.16.840.1.113730.3.8.23.13 NAME 'ipaOwner' DESC 'Owner of an entry' SUP distinguishedName EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
# attributeTypes: ( 2.16.840.1.113730.3.8.23.11 NAME 'ipaSubGidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
# attribute 2.16.840.1.113730.3.8.23.14 'ipaUserDefaultSubordinateId' is defined in 60ipaconfig.ldif
|
||||||
objectClasses: (2.16.840.1.113730.3.8.24.2 NAME 'ipaSubordinateUid' DESC 'Subordinate uids for users, see subuid(5)' SUP top AUXILIARY MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount ) X-ORIGIN 'IPA v4.9')
|
objectClasses: (2.16.840.1.113730.3.8.24.2 NAME 'ipaSubordinateUid' DESC 'Subordinate uids for users, see subuid(5)' SUP top AUXILIARY MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount ) X-ORIGIN 'IPA v4.9')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.24.3 NAME 'ipaSubordinateGid' DESC 'Subordinate gids for users, see subgid(5)' SUP top AUXILIARY MUST ( uidNumber $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9')
|
objectClasses: (2.16.840.1.113730.3.8.24.3 NAME 'ipaSubordinateGid' DESC 'Subordinate gids for users, see subgid(5)' SUP top AUXILIARY MUST ( ipaOwner $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9')
|
||||||
objectClasses: (2.16.840.1.113730.3.8.24.4 NAME 'ipaSubordinateId' DESC 'Subordinate uid and gid for users' SUP top AUXILIARY MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9')
|
objectClasses: (2.16.840.1.113730.3.8.24.4 NAME 'ipaSubordinateId' DESC 'Subordinate uid and gid for users' SUP top AUXILIARY MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9')
|
||||||
|
objectClasses: (2.16.840.1.113730.3.8.24.5 NAME 'ipaSubordinateIdEntry' DESC 'Subordinate uid and gid entry' SUP top STRUCTURAL MUST ( ipaUniqueId ) MAY ( description ) X-ORIGIN 'IPA v4.9')
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
## ObjectClasses: 2.16.840.1.113730.3.8.2 - V1
|
## ObjectClasses: 2.16.840.1.113730.3.8.2 - V1
|
||||||
## Attributes: 2.16.840.1.113730.3.8.3 - V2
|
## Attributes: 2.16.840.1.113730.3.8.3 - V2
|
||||||
## ObjectClasses: 2.16.840.1.113730.3.8.4 - V2
|
## ObjectClasses: 2.16.840.1.113730.3.8.4 - V2
|
||||||
|
## Attributes: 2.16.840.1.113730.3.8.23 - V4 base attributes
|
||||||
dn: cn=schema
|
dn: cn=schema
|
||||||
###############################################
|
###############################################
|
||||||
##
|
##
|
||||||
@@ -45,11 +46,13 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.26 NAME 'ipaSELinuxUserMapDefault' DES
|
|||||||
attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC 'Available SELinux user context ordering' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
|
attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC 'Available SELinux user context ordering' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
|
||||||
## ipaMaxHostnameLength - maximum hostname length to allow
|
## ipaMaxHostnameLength - maximum hostname length to allow
|
||||||
attributeTypes: ( 2.16.840.1.113730.3.8.1.28 NAME 'ipaMaxHostnameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
|
attributeTypes: ( 2.16.840.1.113730.3.8.1.28 NAME 'ipaMaxHostnameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE)
|
||||||
|
# ipaUserDefaultSubordinateId - if TRUE new user entries gain subordinate id by default
|
||||||
|
attributeTypes: ( 2.16.840.1.113730.3.8.3.23.14 NAME 'ipaUserDefaultSubordinateId' DESC 'Enable adding user entries with subordinate id' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
||||||
###############################################
|
###############################################
|
||||||
##
|
##
|
||||||
## ObjectClasses
|
## ObjectClasses
|
||||||
##
|
##
|
||||||
## ipaGuiConfig - GUI config parameters objectclass
|
## ipaGuiConfig - GUI config parameters objectclass
|
||||||
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData $ ipaMaxHostnameLength) )
|
objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData $ ipaMaxHostnameLength $ ipaUserDefaultSubordinateId) )
|
||||||
## ipaConfigObject - Generic config strings object holder
|
## ipaConfigObject - Generic config strings object holder
|
||||||
objectClasses: (2.16.840.1.113730.3.8.4.13 NAME 'ipaConfigObject' DESC 'generic config object for IPA' AUXILIARY MAY ( ipaConfigString ) X-ORIGIN 'IPA v2' )
|
objectClasses: (2.16.840.1.113730.3.8.4.13 NAME 'ipaConfigObject' DESC 'generic config object for IPA' AUXILIARY MAY ( ipaConfigString ) X-ORIGIN 'IPA v2' )
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ dnaMagicRegen: -1
|
|||||||
dnaFilter: (objectClass=ipaSubordinateId)
|
dnaFilter: (objectClass=ipaSubordinateId)
|
||||||
dnaScope: $SUFFIX
|
dnaScope: $SUFFIX
|
||||||
dnaThreshold: eval($SUBID_DNA_THRESHOLD)
|
dnaThreshold: eval($SUBID_DNA_THRESHOLD)
|
||||||
# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr
|
|
||||||
# dnaStepAttr: ipaSubUidCount
|
|
||||||
# dnaStepAttr: ipaSubGidCount
|
|
||||||
# dnaStepAllowedValues: eval($SUBID_COUNT)
|
|
||||||
dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
|
dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
|
||||||
dnaExcludeScope: cn=provisioning,$SUFFIX
|
dnaExcludeScope: cn=provisioning,$SUFFIX
|
||||||
|
# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr
|
||||||
|
# dnaIntervalAttr: ipasubuidcount
|
||||||
|
# dnaIntervalAttr: ipasubgidcount
|
||||||
|
# dnaMaxInterval: eval($SUBID_COUNT)
|
||||||
|
|
||||||
# Enable the DNA plugin
|
# Enable the DNA plugin
|
||||||
dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
|
dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ memberofgroupattr: memberUser
|
|||||||
-
|
-
|
||||||
add: memberofgroupattr
|
add: memberofgroupattr
|
||||||
memberofgroupattr: memberHost
|
memberofgroupattr: memberHost
|
||||||
|
-
|
||||||
|
add: memberofgroupattr
|
||||||
|
memberofgroupattr: ipaOwner
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ define([
|
|||||||
'./serverconfig',
|
'./serverconfig',
|
||||||
'./service',
|
'./service',
|
||||||
'./stageuser',
|
'./stageuser',
|
||||||
|
'./subid',
|
||||||
'./sudo',
|
'./sudo',
|
||||||
'./trust',
|
'./trust',
|
||||||
'./topology',
|
'./topology',
|
||||||
|
|||||||
@@ -103,7 +103,8 @@ var nav = {};
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{ entity: 'subid' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -123,6 +123,10 @@ return {
|
|||||||
$type: 'checkbox',
|
$type: 'checkbox',
|
||||||
name: 'ipamigrationenabled'
|
name: 'ipamigrationenabled'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
$type: 'checkbox',
|
||||||
|
name: 'ipauserdefaultsubordinateid'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
$type: 'multivalued',
|
$type: 'multivalued',
|
||||||
name: 'ipauserobjectclasses'
|
name: 'ipauserobjectclasses'
|
||||||
|
|||||||
92
install/ui/src/freeipa/subid.js
Normal file
92
install/ui/src/freeipa/subid.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 FreeIPA Contributors see COPYING for license
|
||||||
|
*/
|
||||||
|
|
||||||
|
define([
|
||||||
|
'dojo/on',
|
||||||
|
'./ipa',
|
||||||
|
'./jquery',
|
||||||
|
'./phases',
|
||||||
|
'./reg',
|
||||||
|
'./details',
|
||||||
|
'./search',
|
||||||
|
'./association',
|
||||||
|
'./entity'],
|
||||||
|
function(on, IPA, $, phases, reg) {
|
||||||
|
|
||||||
|
var exp = IPA.subid = {};
|
||||||
|
|
||||||
|
var make_spec = function() {
|
||||||
|
return {
|
||||||
|
name: 'subid',
|
||||||
|
facets: [
|
||||||
|
{
|
||||||
|
$type: 'search',
|
||||||
|
columns: [
|
||||||
|
'ipauniqueid',
|
||||||
|
'ipaowner',
|
||||||
|
'ipasubgidnumber',
|
||||||
|
'ipasubuidnumber'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$type: 'details',
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
name: 'details',
|
||||||
|
fields: [
|
||||||
|
'ipauniqueid',
|
||||||
|
'description',
|
||||||
|
{
|
||||||
|
name: 'ipaowner',
|
||||||
|
label: '@i18n:objects.subid.ipaowner',
|
||||||
|
title: '@mo-param:subid:ipaowner:label'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ipasubgidnumber',
|
||||||
|
label: '@i18n:objects.subid.ipasubgidnumber',
|
||||||
|
title: '@mo-param:subid:ipasubgidnumber:label'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ipasubgidcount',
|
||||||
|
label: '@i18n:objects.subid.ipasubgidcount',
|
||||||
|
title: '@mo-param:subid:ipasubgidcount:label'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ipasubuidnumber',
|
||||||
|
label: '@i18n:objects.subid.ipasubuidnumber',
|
||||||
|
title: '@mo-param:subid:ipasubuidnumber:label'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ipasubuidcount',
|
||||||
|
label: '@i18n:objects.subid.ipasubuidcount',
|
||||||
|
title: '@mo-param:subid:ipasubuidcount:label'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
adder_dialog: {
|
||||||
|
title: '@i18n:objects.subid.add',
|
||||||
|
method: 'generate',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
$type: 'entity_select',
|
||||||
|
name: 'ipaowner',
|
||||||
|
other_entity: 'user',
|
||||||
|
other_field: 'uid'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};};
|
||||||
|
|
||||||
|
exp.entity_spec = make_spec();
|
||||||
|
exp.register = function() {
|
||||||
|
var e = reg.entity;
|
||||||
|
e.register({type: 'subid', spec: exp.entity_spec});
|
||||||
|
};
|
||||||
|
phases.on('registration', exp.register);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
@@ -259,33 +259,6 @@ return {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'subordinate',
|
|
||||||
label: '@i18n:objects.subordinate.identity',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'ipasubuidnumber',
|
|
||||||
label: '@i18n:objects.subordinate.subuidnumber',
|
|
||||||
read_only: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ipasubuidcount',
|
|
||||||
label: '@i18n:objects.subordinate.subuidcount',
|
|
||||||
read_only: true
|
|
||||||
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ipasubgidnumber',
|
|
||||||
label: '@i18n:objects.subordinate.subgidnumber',
|
|
||||||
read_only: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'ipasubgidcount',
|
|
||||||
label: '@i18n:objects.subordinate.subgidcount',
|
|
||||||
read_only: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'pwpolicy',
|
name: 'pwpolicy',
|
||||||
label: '@i18n:objects.pwpolicy.identity',
|
label: '@i18n:objects.pwpolicy.identity',
|
||||||
@@ -478,16 +451,6 @@ return {
|
|||||||
enable_cond: ['is-locked'],
|
enable_cond: ['is-locked'],
|
||||||
confirm_msg: '@i18n:objects.user.unlock_confirm'
|
confirm_msg: '@i18n:objects.user.unlock_confirm'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
$factory: IPA.object_action,
|
|
||||||
name: 'auto_subid',
|
|
||||||
method: 'auto_subid',
|
|
||||||
label: '@i18n:objects.user.auto_subid',
|
|
||||||
needs_confirm: true,
|
|
||||||
hide_cond: ['preserved-user'],
|
|
||||||
enable_cond: ['no-subid'],
|
|
||||||
confirm_msg: '@i18n:objects.user.auto_subid_confirm'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
$type: 'automember_rebuild',
|
$type: 'automember_rebuild',
|
||||||
name: 'automember_rebuild',
|
name: 'automember_rebuild',
|
||||||
@@ -500,20 +463,15 @@ return {
|
|||||||
title: '@i18n:objects.cert.issue_for_user'
|
title: '@i18n:objects.cert.issue_for_user'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$factory: IPA.object_action,
|
$type: 'subid_generate',
|
||||||
name: 'auto_subid',
|
|
||||||
method: 'auto_subid',
|
|
||||||
label: '@i18n:objects.user.auto_subid',
|
|
||||||
needs_confirm: true,
|
|
||||||
hide_cond: ['preserved-user'],
|
hide_cond: ['preserved-user'],
|
||||||
enable_cond: ['no-subid'],
|
enable_cond: ['no-subid']
|
||||||
confirm_msg: '@i18n:objects.user.auto_subid_confirm'
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
header_actions: [
|
header_actions: [
|
||||||
'reset_password', 'enable', 'disable', 'stage', 'undel',
|
'reset_password', 'enable', 'disable', 'stage', 'undel',
|
||||||
'delete_active_user', 'delete', 'unlock', 'add_otptoken',
|
'delete_active_user', 'delete', 'unlock', 'add_otptoken',
|
||||||
'automember_rebuild', 'request_cert', 'auto_subid'
|
'automember_rebuild', 'request_cert', 'subid_generate'
|
||||||
],
|
],
|
||||||
state: {
|
state: {
|
||||||
evaluators: [
|
evaluators: [
|
||||||
@@ -532,7 +490,8 @@ return {
|
|||||||
IPA.user.self_service_other_user_evaluator,
|
IPA.user.self_service_other_user_evaluator,
|
||||||
IPA.user.preserved_user_evaluator,
|
IPA.user.preserved_user_evaluator,
|
||||||
IPA.user.is_locked_evaluator,
|
IPA.user.is_locked_evaluator,
|
||||||
IPA.cert.certificate_evaluator
|
IPA.cert.certificate_evaluator,
|
||||||
|
IPA.user.has_subid_evaluator
|
||||||
],
|
],
|
||||||
summary_conditions: [
|
summary_conditions: [
|
||||||
{
|
{
|
||||||
@@ -593,6 +552,12 @@ return {
|
|||||||
add_title: '@i18n:objects.user.add_into_sudo',
|
add_title: '@i18n:objects.user.add_into_sudo',
|
||||||
remove_method: 'remove_user',
|
remove_method: 'remove_user',
|
||||||
remove_title: '@i18n:objects.user.remove_from_sudo'
|
remove_title: '@i18n:objects.user.remove_from_sudo'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$type: 'association',
|
||||||
|
name: 'memberof_subid',
|
||||||
|
associator: IPA.serial_associator,
|
||||||
|
read_only: true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
standard_association_facets: {
|
standard_association_facets: {
|
||||||
@@ -1206,7 +1171,31 @@ IPA.user.is_locked_evaluator = function(spec) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user.ipasubuidnumber) {
|
that.notify_on_change(old_state);
|
||||||
|
};
|
||||||
|
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
|
IPA.user.has_subid_evaluator = function(spec) {
|
||||||
|
|
||||||
|
spec = spec || {};
|
||||||
|
spec.event = spec.event || 'post_load';
|
||||||
|
|
||||||
|
var that = IPA.state_evaluator(spec);
|
||||||
|
that.name = spec.name || 'has_subid_evaluator';
|
||||||
|
that.param = spec.param || 'memberof_subid';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates if user already has a subid
|
||||||
|
*/
|
||||||
|
that.on_event = function(data) {
|
||||||
|
|
||||||
|
var old_state = that.state;
|
||||||
|
that.state = [];
|
||||||
|
|
||||||
|
var value = that.adapter.load(data);
|
||||||
|
if (value.length === 0) {
|
||||||
that.state.push('no-subid');
|
that.state.push('no-subid');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1216,6 +1205,30 @@ IPA.user.is_locked_evaluator = function(spec) {
|
|||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
IPA.user.subid_generate_action = function(spec) {
|
||||||
|
|
||||||
|
spec = spec || {};
|
||||||
|
spec.name = spec.name || 'subid_generate';
|
||||||
|
spec.label = spec.label || '@i18n:objects.user.auto_subid';
|
||||||
|
spec.hide_cond = spec.hide_cond || ['preserved-user'];
|
||||||
|
spec.confirm_msg = spec.confirm_msg || '@i18n:objects.user.auto_subid_confirm';
|
||||||
|
|
||||||
|
var that = IPA.action(spec);
|
||||||
|
|
||||||
|
that.execute_action = function(facet) {
|
||||||
|
|
||||||
|
var subid_e = reg.entity.get('subid');
|
||||||
|
var dialog = subid_e.get_dialog('add');
|
||||||
|
dialog.open();
|
||||||
|
if (!IPA.is_selfservice) {
|
||||||
|
var owner = facet.get_pkey();
|
||||||
|
dialog.get_field('ipaowner').set_value([owner]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return that;
|
||||||
|
};
|
||||||
|
|
||||||
exp.entity_spec = make_spec();
|
exp.entity_spec = make_spec();
|
||||||
exp.register = function() {
|
exp.register = function() {
|
||||||
var e = reg.entity;
|
var e = reg.entity;
|
||||||
@@ -1225,6 +1238,7 @@ exp.register = function() {
|
|||||||
a.register('reset_password', IPA.user.reset_password_action);
|
a.register('reset_password', IPA.user.reset_password_action);
|
||||||
a.register('add_otptoken', IPA.user.add_otptoken_action);
|
a.register('add_otptoken', IPA.user.add_otptoken_action);
|
||||||
a.register('delete_active_user', IPA.user.delete_active_user_action);
|
a.register('delete_active_user', IPA.user.delete_active_user_action);
|
||||||
|
a.register('subid_generate', IPA.user.subid_generate_action);
|
||||||
d.copy('password', 'user_password', {
|
d.copy('password', 'user_password', {
|
||||||
factory: IPA.user.password_dialog,
|
factory: IPA.user.password_dialog,
|
||||||
pre_ops: [IPA.user.password_dialog_pre_op]
|
pre_ops: [IPA.user.password_dialog_pre_op]
|
||||||
|
|||||||
@@ -109,3 +109,22 @@ default:nsslapd-plugin-depends-on-type: database
|
|||||||
default:nsslapd-pluginId: NSUniqueAttr
|
default:nsslapd-pluginId: NSUniqueAttr
|
||||||
default:nsslapd-pluginVersion: 1.1.0
|
default:nsslapd-pluginVersion: 1.1.0
|
||||||
default:nsslapd-pluginVendor: Fedora Project
|
default:nsslapd-pluginVendor: Fedora Project
|
||||||
|
|
||||||
|
dn: cn=ipaSubordinateIdEntry ipaOwner uniqueness,cn=plugins,cn=config
|
||||||
|
default:objectClass: top
|
||||||
|
default:objectClass: nsSlapdPlugin
|
||||||
|
default:objectClass: extensibleObject
|
||||||
|
default:cn: ipaSubordinateIdEntry ipaOwner uniqueness
|
||||||
|
default:nsslapd-pluginDescription: Enforce unique attribute values of ipaOwner
|
||||||
|
default:nsslapd-pluginPath: libattr-unique-plugin
|
||||||
|
default:nsslapd-pluginInitfunc: NSUniqueAttr_Init
|
||||||
|
default:nsslapd-pluginType: preoperation
|
||||||
|
default:nsslapd-pluginEnabled: on
|
||||||
|
default:uniqueness-attribute-name: ipaOwner
|
||||||
|
default:uniqueness-subtrees: cn=subids,cn=accounts,$SUFFIX
|
||||||
|
default:uniqueness-across-all-subtrees: on
|
||||||
|
default:uniqueness-subtree-entries-oc: ipaSubordinateIdEntry
|
||||||
|
default:nsslapd-plugin-depends-on-type: database
|
||||||
|
default:nsslapd-pluginId: NSUniqueAttr
|
||||||
|
default:nsslapd-pluginVersion: 1.1.0
|
||||||
|
default:nsslapd-pluginVendor: Fedora Project
|
||||||
|
|||||||
@@ -263,6 +263,14 @@ default:nsSystemIndex: false
|
|||||||
add:nsIndexType: eq
|
add:nsIndexType: eq
|
||||||
add:nsIndexType: pres
|
add:nsIndexType: pres
|
||||||
|
|
||||||
|
dn: cn=ipaOwner,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
|
||||||
|
only:cn: ipaOwner
|
||||||
|
default:objectClass: nsIndex
|
||||||
|
default:objectClass: top
|
||||||
|
default:nsSystemIndex: false
|
||||||
|
add:nsIndexType: eq
|
||||||
|
add:nsIndexType: pres
|
||||||
|
|
||||||
dn: cn=ipasudorunas,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
|
dn: cn=ipasudorunas,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
|
||||||
only:cn: ipasudorunas
|
only:cn: ipasudorunas
|
||||||
default:objectClass: nsIndex
|
default:objectClass: nsIndex
|
||||||
@@ -425,6 +433,10 @@ default:nsSystemIndex: false
|
|||||||
add:nsIndexType: eq
|
add:nsIndexType: eq
|
||||||
add:nsIndexType: pres
|
add:nsIndexType: pres
|
||||||
|
|
||||||
|
dn: cn=memberOf,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
|
||||||
|
only:cn: member
|
||||||
|
add:nsIndexType: sub
|
||||||
|
|
||||||
dn: cn=memberPrincipal,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
|
dn: cn=memberPrincipal,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
|
||||||
only:cn: memberPrincipal
|
only:cn: memberPrincipal
|
||||||
default:objectClass: nsIndex
|
default:objectClass: nsIndex
|
||||||
|
|||||||
@@ -21,3 +21,4 @@ add: referint-membership-attr: ipamemberca
|
|||||||
add: referint-membership-attr: ipamembercertprofile
|
add: referint-membership-attr: ipamembercertprofile
|
||||||
add: referint-membership-attr: ipalocation
|
add: referint-membership-attr: ipalocation
|
||||||
add: referint-membership-attr: membermanager
|
add: referint-membership-attr: membermanager
|
||||||
|
add: referint-membership-attr: ipaowner
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
# subordinate ids
|
# subordinate ids
|
||||||
|
|
||||||
|
# create memberOf attributes for ipaOwner
|
||||||
|
dn: cn=MemberOf Plugin,cn=plugins,cn=config
|
||||||
|
add: memberofgroupattr: ipaOwner
|
||||||
|
|
||||||
|
# container
|
||||||
|
dn: cn=subids,cn=accounts,$SUFFIX
|
||||||
|
default: objectClass: top
|
||||||
|
default: objectClass: nsContainer
|
||||||
|
default: cn: subids
|
||||||
|
|
||||||
# self-service RBAC
|
# self-service RBAC
|
||||||
dn: cn=Subordinate ID Selfservice User,cn=roles,cn=accounts,$SUFFIX
|
dn: cn=Subordinate ID Selfservice User,cn=roles,cn=accounts,$SUFFIX
|
||||||
default:objectClass: groupofnames
|
default:objectClass: groupofnames
|
||||||
@@ -56,9 +66,9 @@ default:member: cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX
|
|||||||
# self-service permission when 389-DS' DNA plugin supports dnaStepAttr and
|
# self-service permission when 389-DS' DNA plugin supports dnaStepAttr and
|
||||||
# fake_dna_plugin hack has been removed.
|
# fake_dna_plugin hack has been removed.
|
||||||
#
|
#
|
||||||
dn: $SUFFIX
|
dn: cn=subids,cn=accounts,$SUFFIX
|
||||||
add: aci: (targetfilter = "(objectclass=posixaccount)")(targattrfilters = "add=objectClass:(|(objectClass=ipasubordinateid)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=eval($SUBID_RANGE_START))(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=eval($SUBID_RANGE_START))(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (write) userdn = "ldap:///self" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";)
|
add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=eval($SUBID_RANGE_START))(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=eval($SUBID_RANGE_START))(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (add, write) userattr = "ipaowner#SELFDN" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";)
|
||||||
add: aci: (targetfilter = "(objectclass=posixaccount)")(targattrfilters = "add=objectClass:(|(objectClass=ipasubordinateid)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";)
|
add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (add, write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";)
|
||||||
|
|
||||||
# DNA plugin and idrange configuration
|
# DNA plugin and idrange configuration
|
||||||
dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
|
dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
|
||||||
@@ -78,14 +88,14 @@ default: dnaMagicRegen: -1
|
|||||||
default: dnaFilter: (objectClass=ipaSubordinateId)
|
default: dnaFilter: (objectClass=ipaSubordinateId)
|
||||||
default: dnaScope: $SUFFIX
|
default: dnaScope: $SUFFIX
|
||||||
default: dnaThreshold: eval($SUBID_DNA_THRESHOLD)
|
default: dnaThreshold: eval($SUBID_DNA_THRESHOLD)
|
||||||
# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr
|
|
||||||
# default: dnaStepAttr: ipaSubUidCount
|
|
||||||
# default: dnaStepAttr: ipaSubGidCount
|
|
||||||
# default: dnaStepAllowedValues: eval($SUBID_COUNT)
|
|
||||||
default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
|
default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX
|
||||||
default: dnaExcludeScope: cn=provisioning,$SUFFIX
|
default: dnaExcludeScope: cn=provisioning,$SUFFIX
|
||||||
default: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
|
# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr
|
||||||
default: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
|
# add: dnaIntervalAttr: ipasubuidcount
|
||||||
|
# add: dnaIntervalAttr: ipasubgidcount
|
||||||
|
# addifnew: dnaMaxInterval: eval($SUBID_COUNT)
|
||||||
|
add: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
|
||||||
|
add: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";)
|
||||||
|
|
||||||
dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX
|
dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX
|
||||||
default: objectClass: top
|
default: objectClass: top
|
||||||
|
|||||||
@@ -131,6 +131,9 @@ DEFAULT_CONFIG = (
|
|||||||
('container_ranges', DN(('cn', 'ranges'), ('cn', 'etc'))),
|
('container_ranges', DN(('cn', 'ranges'), ('cn', 'etc'))),
|
||||||
('container_dna', DN(('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))),
|
('container_dna', DN(('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))),
|
||||||
('container_dna_posix_ids', DN(('cn', 'posix-ids'), ('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))),
|
('container_dna_posix_ids', DN(('cn', 'posix-ids'), ('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))),
|
||||||
|
('container_dna_subordinate_ids', DN(
|
||||||
|
('cn', 'subordinate-ids'), ('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc')
|
||||||
|
)),
|
||||||
('container_realm_domains', DN(('cn', 'Realm Domains'), ('cn', 'ipa'), ('cn', 'etc'))),
|
('container_realm_domains', DN(('cn', 'Realm Domains'), ('cn', 'ipa'), ('cn', 'etc'))),
|
||||||
('container_otp', DN(('cn', 'otp'))),
|
('container_otp', DN(('cn', 'otp'))),
|
||||||
('container_radiusproxy', DN(('cn', 'radiusproxy'))),
|
('container_radiusproxy', DN(('cn', 'radiusproxy'))),
|
||||||
@@ -148,6 +151,7 @@ DEFAULT_CONFIG = (
|
|||||||
('container_certmaprules', DN(('cn', 'certmaprules'), ('cn', 'certmap'))),
|
('container_certmaprules', DN(('cn', 'certmaprules'), ('cn', 'certmap'))),
|
||||||
('container_ca_renewal',
|
('container_ca_renewal',
|
||||||
DN(('cn', 'ca_renewal'), ('cn', 'ipa'), ('cn', 'etc'))),
|
DN(('cn', 'ca_renewal'), ('cn', 'ipa'), ('cn', 'etc'))),
|
||||||
|
('container_subids', DN(('cn', 'subids'), ('cn', 'accounts'))),
|
||||||
|
|
||||||
# Ports, hosts, and URIs:
|
# Ports, hosts, and URIs:
|
||||||
# Following values do not have any reasonable default.
|
# Following values do not have any reasonable default.
|
||||||
@@ -355,4 +359,4 @@ SUBID_RANGE_START = 2 ** 31
|
|||||||
SUBID_RANGE_MAX = (2 ** 32) - (2 * SUBID_COUNT)
|
SUBID_RANGE_MAX = (2 ** 32) - (2 * SUBID_COUNT)
|
||||||
SUBID_RANGE_SIZE = SUBID_RANGE_MAX - SUBID_RANGE_START
|
SUBID_RANGE_SIZE = SUBID_RANGE_MAX - SUBID_RANGE_START
|
||||||
# threshold before DNA plugin requests a new range
|
# threshold before DNA plugin requests a new range
|
||||||
SUBID_DNA_THRESHOLD = 500 * SUBID_COUNT
|
SUBID_DNA_THRESHOLD = 500
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from ipalib.facts import is_ipa_configured
|
|||||||
from ipaplatform.paths import paths
|
from ipaplatform.paths import paths
|
||||||
from ipapython.admintool import AdminTool, ScriptError
|
from ipapython.admintool import AdminTool, ScriptError
|
||||||
from ipapython.dn import DN
|
from ipapython.dn import DN
|
||||||
from ipaserver.plugins.baseldap import DNA_MAGIC
|
from ipapython.version import API_VERSION
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ class IPASubids(AdminTool):
|
|||||||
# only users with posixAccount
|
# only users with posixAccount
|
||||||
"(objectClass=posixAccount)",
|
"(objectClass=posixAccount)",
|
||||||
# without subordinate ids
|
# without subordinate ids
|
||||||
"(!(objectClass=ipaSubordinateId))",
|
f"(!(memberOf=*,cn=subids,cn=accounts,{api.env.basedn}))",
|
||||||
]
|
]
|
||||||
if groupinfo is not None:
|
if groupinfo is not None:
|
||||||
filters.append(
|
filters.append(
|
||||||
@@ -89,7 +89,7 @@ class IPASubids(AdminTool):
|
|||||||
|
|
||||||
def search_users(self, filters):
|
def search_users(self, filters):
|
||||||
users_dn = DN(api.env.container_user, api.env.basedn)
|
users_dn = DN(api.env.container_user, api.env.basedn)
|
||||||
attrs = ["objectclass", "uid", "uidnumber"]
|
attrs = ["objectclass", "uid"]
|
||||||
|
|
||||||
logger.debug("basedn: %s", users_dn)
|
logger.debug("basedn: %s", users_dn)
|
||||||
logger.debug("attrs: %s", attrs)
|
logger.debug("attrs: %s", attrs)
|
||||||
@@ -116,7 +116,7 @@ class IPASubids(AdminTool):
|
|||||||
api.finalize()
|
api.finalize()
|
||||||
api.Backend.ldap2.connect()
|
api.Backend.ldap2.connect()
|
||||||
self.ldap2 = api.Backend.ldap2
|
self.ldap2 = api.Backend.ldap2
|
||||||
user_obj = api.Object["user"]
|
subid_generate = api.Command.subid_generate
|
||||||
|
|
||||||
dry_run = self.safe_options.dry_run
|
dry_run = self.safe_options.dry_run
|
||||||
group_info = self.get_group_info()
|
group_info = self.get_group_info()
|
||||||
@@ -136,11 +136,13 @@ class IPASubids(AdminTool):
|
|||||||
i,
|
i,
|
||||||
total
|
total
|
||||||
)
|
)
|
||||||
user_obj.set_subordinate_ids(
|
|
||||||
self.ldap2, entry.dn, entry, DNA_MAGIC
|
|
||||||
)
|
|
||||||
if not dry_run:
|
if not dry_run:
|
||||||
self.ldap2.update_entry(entry)
|
# TODO: check for duplicate entry (race condition)
|
||||||
|
# TODO: log new subid
|
||||||
|
subid_generate(
|
||||||
|
ipaowner=entry.single_value["uid"],
|
||||||
|
version=API_VERSION
|
||||||
|
)
|
||||||
|
|
||||||
if dry_run:
|
if dry_run:
|
||||||
logger.info("Dry run mode, no user was modified")
|
logger.info("Dry run mode, no user was modified")
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ register = Registry()
|
|||||||
|
|
||||||
@register()
|
@register()
|
||||||
class update_dna_shared_config(Updater):
|
class update_dna_shared_config(Updater):
|
||||||
dna_plugin_names = ('posix IDs',)
|
dna_plugin_names = ('posix IDs', 'Subordinate IDs')
|
||||||
|
|
||||||
dna_plugin_dn = DN(
|
dna_plugin_dn = DN(
|
||||||
('cn', 'Distributed Numeric Assignment Plugin'),
|
('cn', 'Distributed Numeric Assignment Plugin'),
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ global_output_params = (
|
|||||||
Str('memberof_hbacrule?',
|
Str('memberof_hbacrule?',
|
||||||
label='Member of HBAC rule',
|
label='Member of HBAC rule',
|
||||||
),
|
),
|
||||||
|
Str('memberof_subid?',
|
||||||
|
label='Subordinate ids',),
|
||||||
Str('member_idoverrideuser?',
|
Str('member_idoverrideuser?',
|
||||||
label=_('Member ID user overrides'),),
|
label=_('Member ID user overrides'),),
|
||||||
Str('memberindirect_idoverrideuser?',
|
Str('memberindirect_idoverrideuser?',
|
||||||
@@ -795,7 +797,13 @@ class LDAPObject(Object):
|
|||||||
|
|
||||||
dn = entry.dn
|
dn = entry.dn
|
||||||
filter = self.backend.make_filter(
|
filter = self.backend.make_filter(
|
||||||
{'member': dn, 'memberuser': dn, 'memberhost': dn})
|
{
|
||||||
|
'member': dn,
|
||||||
|
'memberuser': dn,
|
||||||
|
'memberhost': dn,
|
||||||
|
'ipaowner': dn
|
||||||
|
}
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
result = self.backend.get_entries(
|
result = self.backend.get_entries(
|
||||||
self.api.env.basedn,
|
self.api.env.basedn,
|
||||||
|
|||||||
@@ -17,10 +17,9 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import random
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from ipalib import api, errors, output, constants
|
from ipalib import api, errors, constants
|
||||||
from ipalib import (
|
from ipalib import (
|
||||||
Flag, Int, Password, Str, Bool, StrEnum, DateTime, DNParam)
|
Flag, Int, Password, Str, Bool, StrEnum, DateTime, DNParam)
|
||||||
from ipalib.parameters import Principal, Certificate
|
from ipalib.parameters import Principal, Certificate
|
||||||
@@ -28,9 +27,9 @@ from ipalib.plugable import Registry
|
|||||||
from .baseldap import (
|
from .baseldap import (
|
||||||
DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete,
|
DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete,
|
||||||
LDAPRetrieve, LDAPAddAttribute, LDAPModAttribute, LDAPRemoveAttribute,
|
LDAPRetrieve, LDAPAddAttribute, LDAPModAttribute, LDAPRemoveAttribute,
|
||||||
LDAPQuery, LDAPAddMember, LDAPRemoveMember,
|
LDAPAddMember, LDAPRemoveMember,
|
||||||
LDAPAddAttributeViaOption, LDAPRemoveAttributeViaOption,
|
LDAPAddAttributeViaOption, LDAPRemoveAttributeViaOption,
|
||||||
add_missing_object_class, DNA_MAGIC, pkey_to_value, entry_to_dict
|
add_missing_object_class
|
||||||
)
|
)
|
||||||
from ipaserver.plugins.service import (validate_realm, normalize_principal)
|
from ipaserver.plugins.service import (validate_realm, normalize_principal)
|
||||||
from ipalib.request import context
|
from ipalib.request import context
|
||||||
@@ -162,7 +161,7 @@ class baseuser(LDAPObject):
|
|||||||
possible_objectclasses = [
|
possible_objectclasses = [
|
||||||
'meporiginentry', 'ipauserauthtypeclass', 'ipauser',
|
'meporiginentry', 'ipauserauthtypeclass', 'ipauser',
|
||||||
'ipatokenradiusproxyuser', 'ipacertmapobject',
|
'ipatokenradiusproxyuser', 'ipacertmapobject',
|
||||||
'ipantuserattrs', 'ipasubordinateid',
|
'ipantuserattrs',
|
||||||
]
|
]
|
||||||
disallow_object_classes = ['krbticketpolicyaux']
|
disallow_object_classes = ['krbticketpolicyaux']
|
||||||
permission_filter_objectclasses = ['posixaccount']
|
permission_filter_objectclasses = ['posixaccount']
|
||||||
@@ -183,13 +182,13 @@ class baseuser(LDAPObject):
|
|||||||
'krbprincipalname', 'loginshell',
|
'krbprincipalname', 'loginshell',
|
||||||
'mail', 'telephonenumber', 'title', 'nsaccountlock',
|
'mail', 'telephonenumber', 'title', 'nsaccountlock',
|
||||||
'uidnumber', 'gidnumber', 'sshpubkeyfp',
|
'uidnumber', 'gidnumber', 'sshpubkeyfp',
|
||||||
'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber',
|
|
||||||
'ipasubgidcount',
|
|
||||||
]
|
]
|
||||||
uuid_attribute = 'ipauniqueid'
|
uuid_attribute = 'ipauniqueid'
|
||||||
attribute_members = {
|
attribute_members = {
|
||||||
'manager': ['user'],
|
'manager': ['user'],
|
||||||
'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
|
'memberof': [
|
||||||
|
'group', 'netgroup', 'role', 'hbacrule', 'sudorule', 'subid'
|
||||||
|
],
|
||||||
'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
|
'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
|
||||||
}
|
}
|
||||||
allow_rename = True
|
allow_rename = True
|
||||||
@@ -432,41 +431,6 @@ class baseuser(LDAPObject):
|
|||||||
'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:',
|
'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:',
|
||||||
'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'),
|
'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'),
|
||||||
),
|
),
|
||||||
Int(
|
|
||||||
'ipasubuidnumber?',
|
|
||||||
label=_('SubUID range start'),
|
|
||||||
cli_name='subuid',
|
|
||||||
doc=_('Start value for subordinate user ID (subuid) range'),
|
|
||||||
minvalue=constants.SUBID_RANGE_START,
|
|
||||||
maxvalue=constants.SUBID_RANGE_MAX,
|
|
||||||
),
|
|
||||||
Int(
|
|
||||||
'ipasubuidcount?',
|
|
||||||
label=_('SubUID range size'),
|
|
||||||
cli_name='subuidcount',
|
|
||||||
doc=_('Subordinate user ID count'),
|
|
||||||
flags={'no_create', 'no_update', 'no_search'},
|
|
||||||
minvalue=constants.SUBID_COUNT,
|
|
||||||
maxvalue=constants.SUBID_COUNT,
|
|
||||||
),
|
|
||||||
Int(
|
|
||||||
'ipasubgidnumber?',
|
|
||||||
label=_('SubGID range start'),
|
|
||||||
cli_name='subgid',
|
|
||||||
doc=_('Start value for subordinate group ID (subgid) range'),
|
|
||||||
flags={'no_create', 'no_update'},
|
|
||||||
minvalue=constants.SUBID_RANGE_START,
|
|
||||||
maxvalue=constants.SUBID_RANGE_MAX,
|
|
||||||
),
|
|
||||||
Int(
|
|
||||||
'ipasubgidcount?',
|
|
||||||
label=_('SubGID range size'),
|
|
||||||
cli_name='subgidcount',
|
|
||||||
doc=_('Subordinate group ID count'),
|
|
||||||
flags={'no_create', 'no_update', 'no_search'},
|
|
||||||
minvalue=constants.SUBID_COUNT,
|
|
||||||
maxvalue=constants.SUBID_COUNT,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def normalize_and_validate_email(self, email, config=None):
|
def normalize_and_validate_email(self, email, config=None):
|
||||||
@@ -564,131 +528,6 @@ class baseuser(LDAPObject):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def handle_subordinate_ids(self, ldap, dn, entry_attrs):
|
|
||||||
"""Handle ipaSubordinateId object class
|
|
||||||
"""
|
|
||||||
obj_classes = entry_attrs.get("objectclass")
|
|
||||||
new_subuid = entry_attrs.single_value.get("ipasubuidnumber")
|
|
||||||
new_subgid = entry_attrs.single_value.get("ipasubgidnumber")
|
|
||||||
|
|
||||||
# entry has object class ipaSubordinateId
|
|
||||||
# default to auto-assigment of subuids
|
|
||||||
if (
|
|
||||||
new_subuid is None
|
|
||||||
and obj_classes is not None
|
|
||||||
and self.has_objectclass(obj_classes, "ipasubordinateid")
|
|
||||||
):
|
|
||||||
new_subuid = DNA_MAGIC
|
|
||||||
|
|
||||||
# neither auto-assignment nor explicit assignment
|
|
||||||
if new_subuid is None:
|
|
||||||
# nothing to do
|
|
||||||
return False
|
|
||||||
|
|
||||||
# enforce subuid == subgid
|
|
||||||
if new_subgid is not None and new_subgid != new_subuid:
|
|
||||||
raise errors.ValidationError(
|
|
||||||
name="ipasubgidnumber",
|
|
||||||
error=_("subgidnumber must be equal to subuidnumber")
|
|
||||||
)
|
|
||||||
|
|
||||||
self.set_subordinate_ids(ldap, dn, entry_attrs, new_subuid)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def set_subordinate_ids(self, ldap, dn, entry_attrs, subuid):
|
|
||||||
"""Set subuid value of an entry
|
|
||||||
|
|
||||||
Takes care of objectclass and sibbling attributes
|
|
||||||
"""
|
|
||||||
if "objectclass" in entry_attrs:
|
|
||||||
obj_classes = entry_attrs["objectclass"]
|
|
||||||
else:
|
|
||||||
_entry_attrs = ldap.get_entry(dn, ["objectclass"])
|
|
||||||
entry_attrs["objectclass"] = _entry_attrs["objectclass"]
|
|
||||||
obj_classes = entry_attrs["objectclass"]
|
|
||||||
|
|
||||||
if not self.has_objectclass(obj_classes, "ipasubordinateid"):
|
|
||||||
# could append ipasubordinategid and ipasubordinateuid, too
|
|
||||||
obj_classes.append("ipasubordinateid")
|
|
||||||
|
|
||||||
# XXX HACK, remove later
|
|
||||||
if subuid == DNA_MAGIC:
|
|
||||||
subuid = self._fake_dna_plugin(ldap, dn, entry_attrs)
|
|
||||||
|
|
||||||
entry_attrs["ipasubuidnumber"] = subuid
|
|
||||||
# enforice subuid == subgid for now
|
|
||||||
entry_attrs["ipasubgidnumber"] = subuid
|
|
||||||
# hard-coded constants
|
|
||||||
entry_attrs["ipasubuidcount"] = constants.SUBID_COUNT
|
|
||||||
entry_attrs["ipasubgidcount"] = constants.SUBID_COUNT
|
|
||||||
|
|
||||||
def get_subid_match_candidate_filter(
|
|
||||||
self, ldap, *, subuid, subgid, extra_filters=(), offset=None,
|
|
||||||
):
|
|
||||||
"""Create LDAP filter to locate matching/overlapping subids
|
|
||||||
"""
|
|
||||||
if subuid is None and subgid is None:
|
|
||||||
raise ValueError("subuid and subgid are both None")
|
|
||||||
if offset is None:
|
|
||||||
# assumes that no subordinate count is larger than SUBID_COUNT
|
|
||||||
offset = constants.SUBID_COUNT - 1
|
|
||||||
|
|
||||||
class_filters = "(objectclass=ipasubordinateid)"
|
|
||||||
subid_filters = []
|
|
||||||
if subuid is not None:
|
|
||||||
subid_filters.append(
|
|
||||||
ldap.combine_filters(
|
|
||||||
[
|
|
||||||
f"(ipasubuidnumber>={subuid - offset})",
|
|
||||||
f"(ipasubuidnumber<={subuid + offset})",
|
|
||||||
],
|
|
||||||
rules=ldap.MATCH_ALL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if subgid is not None:
|
|
||||||
subid_filters.append(
|
|
||||||
ldap.combine_filters(
|
|
||||||
[
|
|
||||||
f"(ipasubgidnumber>={subgid - offset})",
|
|
||||||
f"(ipasubgidnumber<={subgid + offset})",
|
|
||||||
],
|
|
||||||
rules=ldap.MATCH_ALL
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
subid_filters = ldap.combine_filters(
|
|
||||||
subid_filters, rules=ldap.MATCH_ANY
|
|
||||||
)
|
|
||||||
filters = [class_filters, subid_filters]
|
|
||||||
filters.extend(extra_filters)
|
|
||||||
return ldap.combine_filters(filters, rules=ldap.MATCH_ALL)
|
|
||||||
|
|
||||||
def _fake_dna_plugin(self, ldap, dn, entry_attrs):
|
|
||||||
"""XXX HACK, remove when 389-DS DNA plugin supports steps"""
|
|
||||||
uidnumber = entry_attrs.single_value.get("uidnumber")
|
|
||||||
if uidnumber is None:
|
|
||||||
entry = ldap.get_entry(dn, ["uidnumber"])
|
|
||||||
uidnumber = entry.single_value["uidnumber"]
|
|
||||||
uidnumber = int(uidnumber)
|
|
||||||
|
|
||||||
if uidnumber == DNA_MAGIC:
|
|
||||||
return (
|
|
||||||
3221225472
|
|
||||||
+ random.randint(1, 16382) * constants.SUBID_COUNT
|
|
||||||
)
|
|
||||||
|
|
||||||
if not hasattr(context, "idrange_ipabaseid"):
|
|
||||||
range_name = f"{self.api.env.realm}_id_range"
|
|
||||||
range = self.api.Command.idrange_show(range_name)["result"]
|
|
||||||
context.idrange_ipabaseid = int(range["ipabaseid"][0])
|
|
||||||
|
|
||||||
range_start = context.idrange_ipabaseid
|
|
||||||
|
|
||||||
assert uidnumber >= range_start
|
|
||||||
assert uidnumber < range_start + 2**14
|
|
||||||
|
|
||||||
return (uidnumber - range_start) * constants.SUBID_COUNT + 2**31
|
|
||||||
|
|
||||||
|
|
||||||
class baseuser_add(LDAPCreate):
|
class baseuser_add(LDAPCreate):
|
||||||
"""
|
"""
|
||||||
@@ -699,7 +538,6 @@ class baseuser_add(LDAPCreate):
|
|||||||
assert isinstance(dn, DN)
|
assert isinstance(dn, DN)
|
||||||
set_krbcanonicalname(entry_attrs)
|
set_krbcanonicalname(entry_attrs)
|
||||||
self.obj.convert_usercertificate_pre(entry_attrs)
|
self.obj.convert_usercertificate_pre(entry_attrs)
|
||||||
self.obj.handle_subordinate_ids(ldap, dn, entry_attrs)
|
|
||||||
if entry_attrs.get('ipatokenradiususername', None):
|
if entry_attrs.get('ipatokenradiususername', None):
|
||||||
add_missing_object_class(ldap, u'ipatokenradiusproxyuser', dn,
|
add_missing_object_class(ldap, u'ipatokenradiusproxyuser', dn,
|
||||||
entry_attrs, update=False)
|
entry_attrs, update=False)
|
||||||
@@ -852,7 +690,6 @@ class baseuser_mod(LDAPUpdate):
|
|||||||
|
|
||||||
self.check_objectclass(ldap, dn, entry_attrs)
|
self.check_objectclass(ldap, dn, entry_attrs)
|
||||||
self.obj.convert_usercertificate_pre(entry_attrs)
|
self.obj.convert_usercertificate_pre(entry_attrs)
|
||||||
self.obj.handle_subordinate_ids(ldap, dn, entry_attrs)
|
|
||||||
self.preserve_krbprincipalname_pre(ldap, entry_attrs, *keys, **options)
|
self.preserve_krbprincipalname_pre(ldap, entry_attrs, *keys, **options)
|
||||||
update_samba_attrs(ldap, dn, entry_attrs, **options)
|
update_samba_attrs(ldap, dn, entry_attrs, **options)
|
||||||
|
|
||||||
@@ -1133,98 +970,3 @@ class baseuser_remove_certmapdata(ModCertMapData,
|
|||||||
LDAPRemoveAttribute):
|
LDAPRemoveAttribute):
|
||||||
__doc__ = _("Remove one or more certificate mappings from the user entry.")
|
__doc__ = _("Remove one or more certificate mappings from the user entry.")
|
||||||
msg_summary = _('Removed certificate mappings from user "%(value)s"')
|
msg_summary = _('Removed certificate mappings from user "%(value)s"')
|
||||||
|
|
||||||
|
|
||||||
class baseuser_auto_subid(LDAPQuery):
|
|
||||||
__doc__ = _("Auto-assign subuid and subgid range to user entry")
|
|
||||||
|
|
||||||
has_output = output.standard_entry
|
|
||||||
|
|
||||||
def execute(self, cn, **options):
|
|
||||||
ldap = self.obj.backend
|
|
||||||
dn = self.obj.get_dn(cn)
|
|
||||||
|
|
||||||
try:
|
|
||||||
entry_attrs = ldap.get_entry(
|
|
||||||
dn, ["objectclass", "ipasubuidnumber"]
|
|
||||||
)
|
|
||||||
except errors.NotFound:
|
|
||||||
raise self.obj.handle_not_found(cn)
|
|
||||||
|
|
||||||
if "ipasubuidnumber" in entry_attrs:
|
|
||||||
raise errors.AlreadyContainsValueError(attr="ipasubuidnumber")
|
|
||||||
|
|
||||||
self.obj.set_subordinate_ids(ldap, dn, entry_attrs, subuid=DNA_MAGIC)
|
|
||||||
ldap.update_entry(entry_attrs)
|
|
||||||
|
|
||||||
# fetch updated entry (use search display attribute to show subids)
|
|
||||||
if options.get('all', False):
|
|
||||||
attrs_list = ['*'] + self.obj.search_display_attributes
|
|
||||||
else:
|
|
||||||
attrs_list = set(self.obj.search_display_attributes)
|
|
||||||
attrs_list.update(entry_attrs.keys())
|
|
||||||
if options.get('no_members', False):
|
|
||||||
attrs_list.difference_update(self.obj.attribute_members)
|
|
||||||
attrs_list = list(attrs_list)
|
|
||||||
|
|
||||||
entry = self._exc_wrapper((cn,), options, ldap.get_entry)(
|
|
||||||
dn, attrs_list
|
|
||||||
)
|
|
||||||
entry_attrs = entry_to_dict(entry, **options)
|
|
||||||
entry_attrs['dn'] = dn
|
|
||||||
|
|
||||||
return dict(result=entry_attrs, value=pkey_to_value(cn, options))
|
|
||||||
|
|
||||||
|
|
||||||
class baseuser_match_subid(baseuser_find):
|
|
||||||
__doc__ = _("Match users by any subordinate uid in their range")
|
|
||||||
|
|
||||||
_subid_attrs = {
|
|
||||||
"ipasubuidnumber",
|
|
||||||
"ipasubuidcount",
|
|
||||||
"ipasubgidnumber",
|
|
||||||
"ipasubgidcount"
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_options(self):
|
|
||||||
base_options = {p.name for p in self.obj.takes_params}
|
|
||||||
for option in super().get_options():
|
|
||||||
if option.name == "ipasubuidnumber":
|
|
||||||
yield option.clone(
|
|
||||||
label=_('SubUID match'),
|
|
||||||
doc=_('Match value for subordinate user ID'),
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
elif option.name not in base_options:
|
|
||||||
# raw, version
|
|
||||||
yield option.clone()
|
|
||||||
|
|
||||||
def pre_callback(
|
|
||||||
self, ldap, filters, attrs_list, base_dn, scope, *args, **options
|
|
||||||
):
|
|
||||||
# search for candidates in range
|
|
||||||
# Code assumes that no subordinate count is larger than SUBID_COUNT
|
|
||||||
filters = self.obj.get_subid_match_candidate_filter(
|
|
||||||
ldap, subuid=options["ipasubuidnumber"], subgid=None,
|
|
||||||
)
|
|
||||||
# always include subid attributes
|
|
||||||
for missing in self._subid_attrs.difference(attrs_list):
|
|
||||||
attrs_list.append(missing)
|
|
||||||
|
|
||||||
return filters, base_dn, scope
|
|
||||||
|
|
||||||
def post_callback(self, ldap, entries, truncated, *args, **options):
|
|
||||||
# filter out mismatches manually
|
|
||||||
osubuid = options["ipasubuidnumber"]
|
|
||||||
new_entries = []
|
|
||||||
for entry in entries:
|
|
||||||
esubuid = int(entry.single_value["ipasubuidnumber"])
|
|
||||||
esubcount = int(entry.single_value["ipasubuidcount"])
|
|
||||||
minsubuid = esubuid
|
|
||||||
maxsubuid = esubuid + esubcount - 1
|
|
||||||
if minsubuid <= osubuid <= maxsubuid:
|
|
||||||
new_entries.append(entry)
|
|
||||||
|
|
||||||
entries[:] = new_entries
|
|
||||||
|
|
||||||
return truncated
|
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ class config(LDAPObject):
|
|||||||
'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
|
'ipapwdexpadvnotify', 'ipaselinuxusermaporder',
|
||||||
'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
|
'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata',
|
||||||
'ipauserauthtype', 'ipadomainresolutionorder', 'ipamaxhostnamelength',
|
'ipauserauthtype', 'ipadomainresolutionorder', 'ipamaxhostnamelength',
|
||||||
|
'ipauserdefaultsubordinateid',
|
||||||
]
|
]
|
||||||
container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
|
container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc'))
|
||||||
permission_filter_objectclasses = ['ipaguiconfig']
|
permission_filter_objectclasses = ['ipaguiconfig']
|
||||||
@@ -142,7 +143,7 @@ class config(LDAPObject):
|
|||||||
'ipasearchrecordslimit', 'ipasearchtimelimit',
|
'ipasearchrecordslimit', 'ipasearchtimelimit',
|
||||||
'ipauserauthtype', 'ipauserobjectclasses',
|
'ipauserauthtype', 'ipauserobjectclasses',
|
||||||
'ipausersearchfields', 'ipacustomfields',
|
'ipausersearchfields', 'ipacustomfields',
|
||||||
'ipamaxhostnamelength',
|
'ipamaxhostnamelength', 'ipauserdefaultsubordinateid',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -261,6 +262,11 @@ class config(LDAPObject):
|
|||||||
values=(u'password', u'radius', u'otp',
|
values=(u'password', u'radius', u'otp',
|
||||||
u'pkinit', u'hardened', u'disabled'),
|
u'pkinit', u'hardened', u'disabled'),
|
||||||
),
|
),
|
||||||
|
Bool('ipauserdefaultsubordinateid?',
|
||||||
|
cli_name='user_default_subid',
|
||||||
|
label=_('Enable adding subids to new users'),
|
||||||
|
doc=_('Enable adding subids to new users'),
|
||||||
|
),
|
||||||
Str(
|
Str(
|
||||||
'ipa_master_server*',
|
'ipa_master_server*',
|
||||||
label=_('IPA masters'),
|
label=_('IPA masters'),
|
||||||
|
|||||||
@@ -1547,7 +1547,7 @@ class i18n_messages(Command):
|
|||||||
"Drive to mount a home directory"
|
"Drive to mount a home directory"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
"subordinate": {
|
"subid": {
|
||||||
"identity": _("Subordinate user and group id"),
|
"identity": _("Subordinate user and group id"),
|
||||||
"subuidnumber": _("Subordinate user id"),
|
"subuidnumber": _("Subordinate user id"),
|
||||||
"subuidcount": _("Subordinate user id count"),
|
"subuidcount": _("Subordinate user id count"),
|
||||||
|
|||||||
608
ipaserver/plugins/subid.py
Normal file
608
ipaserver/plugins/subid.py
Normal file
@@ -0,0 +1,608 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2021 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
import random
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
from ipalib import api
|
||||||
|
from ipalib import constants
|
||||||
|
from ipalib import errors
|
||||||
|
from ipalib import output
|
||||||
|
from ipalib.plugable import Registry
|
||||||
|
from ipalib.parameters import Int, Str
|
||||||
|
from ipalib.request import context
|
||||||
|
from ipalib.text import _, ngettext
|
||||||
|
from ipapython.dn import DN
|
||||||
|
|
||||||
|
from .baseldap import (
|
||||||
|
LDAPObject,
|
||||||
|
LDAPCreate,
|
||||||
|
LDAPDelete,
|
||||||
|
LDAPUpdate,
|
||||||
|
LDAPSearch,
|
||||||
|
LDAPRetrieve,
|
||||||
|
LDAPQuery,
|
||||||
|
DNA_MAGIC,
|
||||||
|
)
|
||||||
|
|
||||||
|
__doc__ = _(
|
||||||
|
"""
|
||||||
|
Subordinate ids
|
||||||
|
|
||||||
|
Manage subordinate user and group ids for users
|
||||||
|
|
||||||
|
EXAMPLES:
|
||||||
|
|
||||||
|
Auto-assign a subordinate id range to current user
|
||||||
|
ipa subid-generate
|
||||||
|
|
||||||
|
Auto-assign a subordinate id range to user alice:
|
||||||
|
ipa subid-generate --owner=alice
|
||||||
|
|
||||||
|
Find subordinate ids for user alice:
|
||||||
|
ipa subid-find --owner=alice
|
||||||
|
|
||||||
|
Match entry by any subordinate uid in range:
|
||||||
|
ipa subid-match --subuid=2147483649
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
register = Registry()
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid(LDAPObject):
|
||||||
|
"""Subordinate id object."""
|
||||||
|
|
||||||
|
container_dn = api.env.container_subids
|
||||||
|
|
||||||
|
object_name = _("Subordinate id")
|
||||||
|
object_name_plural = _("Subordinate ids")
|
||||||
|
label = _("Subordinate ids")
|
||||||
|
label_singular = _("Subordinate id")
|
||||||
|
|
||||||
|
object_class = ["ipasubordinateidentry"]
|
||||||
|
possible_objectclasses = [
|
||||||
|
"ipasubordinategid",
|
||||||
|
"ipasubordinateuid",
|
||||||
|
"ipasubordinateid",
|
||||||
|
]
|
||||||
|
default_attributes = [
|
||||||
|
"ipauniqueid",
|
||||||
|
"ipaowner",
|
||||||
|
"ipasubuidnumber",
|
||||||
|
"ipasubuidcount",
|
||||||
|
"ipasubgidnumber",
|
||||||
|
"ipasubgidcount",
|
||||||
|
]
|
||||||
|
allow_rename = False
|
||||||
|
|
||||||
|
permission_filter_objectclasses_string = (
|
||||||
|
"(objectclass=ipasubordinateidentry)"
|
||||||
|
)
|
||||||
|
managed_permissions = {
|
||||||
|
# all authenticated principals can read subordinate id information
|
||||||
|
"System: Read Subordinate Id Attributes": {
|
||||||
|
"ipapermbindruletype": "all",
|
||||||
|
"ipapermright": {"read", "search", "compare"},
|
||||||
|
"ipapermtargetfilter": [
|
||||||
|
permission_filter_objectclasses_string,
|
||||||
|
],
|
||||||
|
"ipapermdefaultattr": {
|
||||||
|
"objectclass",
|
||||||
|
"ipauniqueid",
|
||||||
|
"description",
|
||||||
|
"ipaowner",
|
||||||
|
"ipasubuidnumber",
|
||||||
|
"ipasubuidcount",
|
||||||
|
"ipasubgidnumber",
|
||||||
|
"ipasubgidcount",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"System: Read Subordinate Id Count": {
|
||||||
|
"ipapermbindruletype": "all",
|
||||||
|
"ipapermright": {"read", "search", "compare"},
|
||||||
|
"ipapermtargetfilter": [],
|
||||||
|
"ipapermtarget": DN(container_dn, api.env.basedn),
|
||||||
|
"ipapermdefaultattr": {"numSubordinates"},
|
||||||
|
},
|
||||||
|
# user administrators can remove subordinate ids or update the
|
||||||
|
# ipaowner attribute. This enables user admins to remove users
|
||||||
|
# with assigned subids or move them to staging area (--preserve).
|
||||||
|
"System: Manage Subordinate Ids": {
|
||||||
|
"ipapermright": {"write"},
|
||||||
|
"ipapermtargetfilter": [
|
||||||
|
permission_filter_objectclasses_string,
|
||||||
|
],
|
||||||
|
"ipapermdefaultattr": {
|
||||||
|
"description",
|
||||||
|
"ipaowner", # allow user admins to preserve users
|
||||||
|
},
|
||||||
|
"default_privileges": {"User Administrators"},
|
||||||
|
},
|
||||||
|
"System: Remove Subordinate Ids": {
|
||||||
|
"ipapermright": {"delete"},
|
||||||
|
"ipapermtargetfilter": [
|
||||||
|
permission_filter_objectclasses_string,
|
||||||
|
],
|
||||||
|
"default_privileges": {"User Administrators"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
takes_params = (
|
||||||
|
Str(
|
||||||
|
"ipauniqueid",
|
||||||
|
cli_name="id",
|
||||||
|
label=_("Unique ID"),
|
||||||
|
primary_key=True,
|
||||||
|
flags={"optional_create"},
|
||||||
|
),
|
||||||
|
Str(
|
||||||
|
"description?",
|
||||||
|
cli_name="desc",
|
||||||
|
label=_("Description"),
|
||||||
|
doc=_("Subordinate id description"),
|
||||||
|
),
|
||||||
|
Str(
|
||||||
|
"ipaowner",
|
||||||
|
cli_name="owner",
|
||||||
|
label=_("Owner"),
|
||||||
|
doc=_("Owning user of subordinate id entry"),
|
||||||
|
flags={"no_update"},
|
||||||
|
),
|
||||||
|
Int(
|
||||||
|
"ipasubuidnumber?",
|
||||||
|
label=_("SubUID range start"),
|
||||||
|
cli_name="subuid",
|
||||||
|
doc=_("Start value for subordinate user ID (subuid) range"),
|
||||||
|
flags={"no_update"},
|
||||||
|
minvalue=constants.SUBID_RANGE_START,
|
||||||
|
maxvalue=constants.SUBID_RANGE_MAX,
|
||||||
|
),
|
||||||
|
Int(
|
||||||
|
"ipasubuidcount?",
|
||||||
|
label=_("SubUID range size"),
|
||||||
|
cli_name="subuidcount",
|
||||||
|
doc=_("Subordinate user ID count"),
|
||||||
|
flags={"no_create", "no_update", "no_search"}, # auto-assigned
|
||||||
|
minvalue=constants.SUBID_COUNT,
|
||||||
|
maxvalue=constants.SUBID_COUNT,
|
||||||
|
),
|
||||||
|
Int(
|
||||||
|
"ipasubgidnumber?",
|
||||||
|
label=_("SubGID range start"),
|
||||||
|
cli_name="subgid",
|
||||||
|
doc=_("Start value for subordinate group ID (subgid) range"),
|
||||||
|
flags={"no_create", "no_update"}, # auto-assigned
|
||||||
|
minvalue=constants.SUBID_RANGE_START,
|
||||||
|
maxvalue=constants.SUBID_RANGE_MAX,
|
||||||
|
),
|
||||||
|
Int(
|
||||||
|
"ipasubgidcount?",
|
||||||
|
label=_("SubGID range size"),
|
||||||
|
cli_name="subgidcount",
|
||||||
|
doc=_("Subordinate group ID count"),
|
||||||
|
flags={"no_create", "no_update", "no_search"}, # auto-assigned
|
||||||
|
minvalue=constants.SUBID_COUNT,
|
||||||
|
maxvalue=constants.SUBID_COUNT,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def fixup_objectclass(self, entry_attrs):
|
||||||
|
"""Add missing object classes to entry"""
|
||||||
|
has_subuid = "ipasubuidnumber" in entry_attrs
|
||||||
|
has_subgid = "ipasubgidnumber" in entry_attrs
|
||||||
|
|
||||||
|
candicates = set(self.object_class)
|
||||||
|
if has_subgid:
|
||||||
|
candicates.add("ipasubordinategid")
|
||||||
|
if has_subuid:
|
||||||
|
candicates.add("ipasubordinateuid")
|
||||||
|
if has_subgid and has_subuid:
|
||||||
|
candicates.add("ipasubordinateid")
|
||||||
|
|
||||||
|
entry_oc = entry_attrs.setdefault("objectclass", [])
|
||||||
|
current_oc = {x.lower() for x in entry_oc}
|
||||||
|
for oc in candicates.difference(current_oc):
|
||||||
|
entry_oc.append(oc)
|
||||||
|
|
||||||
|
def handle_duplicate_entry(self, *keys):
|
||||||
|
if hasattr(context, "subid_owner_dn"):
|
||||||
|
uid = context.subid_owner_dn[0].value
|
||||||
|
msg = _(
|
||||||
|
'%(oname)s with with name "%(pkey)s" or for user "%(uid)s" '
|
||||||
|
"already exists."
|
||||||
|
) % {
|
||||||
|
"uid": uid,
|
||||||
|
"pkey": keys[-1] if keys else "",
|
||||||
|
"oname": self.object_name,
|
||||||
|
}
|
||||||
|
raise errors.DuplicateEntry(message=msg) from None
|
||||||
|
else:
|
||||||
|
super().handle_duplicate_entry(*keys)
|
||||||
|
|
||||||
|
def convert_owner(self, entry_attrs, options):
|
||||||
|
"""Change owner from DN to uid string"""
|
||||||
|
if not options.get("raw", False) and "ipaowner" in entry_attrs:
|
||||||
|
userobj = self.api.Object.user
|
||||||
|
entry_attrs["ipaowner"] = [
|
||||||
|
userobj.get_primary_key_from_dn(entry_attrs["ipaowner"][0])
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_owner_dn(self, *keys, **options):
|
||||||
|
"""Get owning user entry entry (username or DN)"""
|
||||||
|
owner = keys[-1]
|
||||||
|
userobj = self.api.Object.user
|
||||||
|
if isinstance(owner, DN):
|
||||||
|
# it's already a DN, validate it's either an active or preserved
|
||||||
|
# user. Ref integrity plugin checks that it's not a dangling DN.
|
||||||
|
user_dns = (
|
||||||
|
DN(userobj.active_container_dn, self.api.env.basedn),
|
||||||
|
DN(userobj.delete_container_dn, self.api.env.basedn),
|
||||||
|
)
|
||||||
|
if not owner.endswith(user_dns):
|
||||||
|
raise errors.ValidationError(
|
||||||
|
name="ipaowner",
|
||||||
|
error=_("'%(dn)s is not a valid user") % {"dn": owner},
|
||||||
|
)
|
||||||
|
return owner
|
||||||
|
|
||||||
|
# similar to user.get_either_dn() but with error reporting and
|
||||||
|
# returns an entry
|
||||||
|
ldap = self.backend
|
||||||
|
try:
|
||||||
|
active_dn = userobj.get_dn(owner, **options)
|
||||||
|
entry = ldap.get_entry(active_dn, attrs_list=[])
|
||||||
|
return entry.dn
|
||||||
|
except errors.NotFound:
|
||||||
|
# fall back to deleted user
|
||||||
|
try:
|
||||||
|
delete_dn = userobj.get_delete_dn(owner, **options)
|
||||||
|
entry = ldap.get_entry(delete_dn, attrs_list=[])
|
||||||
|
return entry.dn
|
||||||
|
except errors.NotFound:
|
||||||
|
raise userobj.handle_not_found(owner)
|
||||||
|
|
||||||
|
def handle_subordinate_ids(self, ldap, dn, entry_attrs):
|
||||||
|
"""Handle ipaSubordinateId object class"""
|
||||||
|
new_subuid = entry_attrs.single_value.get("ipasubuidnumber")
|
||||||
|
new_subgid = entry_attrs.single_value.get("ipasubgidnumber")
|
||||||
|
|
||||||
|
if new_subuid is None:
|
||||||
|
new_subuid = DNA_MAGIC
|
||||||
|
|
||||||
|
# enforce subuid == subgid
|
||||||
|
if new_subgid is not None and new_subgid != new_subuid:
|
||||||
|
raise errors.ValidationError(
|
||||||
|
name="ipasubgidnumber",
|
||||||
|
error=_("subgidnumber must be equal to subuidnumber"),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.set_subordinate_ids(ldap, dn, entry_attrs, new_subuid)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_subordinate_ids(self, ldap, dn, entry_attrs, subuid):
|
||||||
|
"""Set subuid value of an entry
|
||||||
|
|
||||||
|
Takes care of objectclass and sibbling attributes
|
||||||
|
"""
|
||||||
|
if "objectclass" not in entry_attrs:
|
||||||
|
_entry_attrs = ldap.get_entry(dn, ["objectclass"])
|
||||||
|
entry_attrs["objectclass"] = _entry_attrs["objectclass"]
|
||||||
|
|
||||||
|
# XXX HACK, remove later
|
||||||
|
if subuid == DNA_MAGIC:
|
||||||
|
subuid = self._fake_dna_plugin(ldap, dn, entry_attrs)
|
||||||
|
|
||||||
|
entry_attrs["ipasubuidnumber"] = subuid
|
||||||
|
# enforice subuid == subgid for now
|
||||||
|
entry_attrs["ipasubgidnumber"] = subuid
|
||||||
|
# hard-coded constants
|
||||||
|
entry_attrs["ipasubuidcount"] = constants.SUBID_COUNT
|
||||||
|
entry_attrs["ipasubgidcount"] = constants.SUBID_COUNT
|
||||||
|
|
||||||
|
self.fixup_objectclass(entry_attrs)
|
||||||
|
|
||||||
|
def get_subid_match_candidate_filter(
|
||||||
|
self,
|
||||||
|
ldap,
|
||||||
|
*,
|
||||||
|
subuid,
|
||||||
|
subgid,
|
||||||
|
extra_filters=(),
|
||||||
|
offset=None,
|
||||||
|
):
|
||||||
|
"""Create LDAP filter to locate matching/overlapping subids"""
|
||||||
|
if subuid is None and subgid is None:
|
||||||
|
raise ValueError("subuid and subgid are both None")
|
||||||
|
if offset is None:
|
||||||
|
# assumes that no subordinate count is larger than SUBID_COUNT
|
||||||
|
offset = constants.SUBID_COUNT - 1
|
||||||
|
|
||||||
|
class_filters = "(objectclass=ipasubordinateid)"
|
||||||
|
subid_filters = []
|
||||||
|
if subuid is not None:
|
||||||
|
subid_filters.append(
|
||||||
|
ldap.combine_filters(
|
||||||
|
[
|
||||||
|
f"(ipasubuidnumber>={subuid - offset})",
|
||||||
|
f"(ipasubuidnumber<={subuid + offset})",
|
||||||
|
],
|
||||||
|
rules=ldap.MATCH_ALL,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if subgid is not None:
|
||||||
|
subid_filters.append(
|
||||||
|
ldap.combine_filters(
|
||||||
|
[
|
||||||
|
f"(ipasubgidnumber>={subgid - offset})",
|
||||||
|
f"(ipasubgidnumber<={subgid + offset})",
|
||||||
|
],
|
||||||
|
rules=ldap.MATCH_ALL,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
subid_filters = ldap.combine_filters(
|
||||||
|
subid_filters, rules=ldap.MATCH_ANY
|
||||||
|
)
|
||||||
|
filters = [class_filters, subid_filters]
|
||||||
|
filters.extend(extra_filters)
|
||||||
|
return ldap.combine_filters(filters, rules=ldap.MATCH_ALL)
|
||||||
|
|
||||||
|
def _fake_dna_plugin(self, ldap, dn, entry_attrs):
|
||||||
|
"""XXX HACK, remove when 389-DS DNA plugin supports steps"""
|
||||||
|
return (
|
||||||
|
constants.SUBID_RANGE_START
|
||||||
|
+ random.randint(1, 32764 - 2) * constants.SUBID_COUNT
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_add(LDAPCreate):
|
||||||
|
__doc__ = _("Add a new subordinate id.")
|
||||||
|
msg_summary = _('Added subordinate id "%(value)s"')
|
||||||
|
|
||||||
|
# internal command, use subid-auto to auto-assign subids
|
||||||
|
NO_CLI = True
|
||||||
|
|
||||||
|
def pre_callback(
|
||||||
|
self, ldap, dn, entry_attrs, attrs_list, *keys, **options
|
||||||
|
):
|
||||||
|
# XXX let ref integrity plugin validate DN?
|
||||||
|
owner_dn = self.obj.get_owner_dn(entry_attrs["ipaowner"], **options)
|
||||||
|
context.subid_owner_dn = owner_dn
|
||||||
|
entry_attrs["ipaowner"] = owner_dn
|
||||||
|
|
||||||
|
self.obj.handle_subordinate_ids(ldap, dn, entry_attrs)
|
||||||
|
attrs_list.append("objectclass")
|
||||||
|
|
||||||
|
return dn
|
||||||
|
|
||||||
|
def execute(self, ipauniqueid=None, **options):
|
||||||
|
if ipauniqueid is None:
|
||||||
|
ipauniqueid = str(uuid.uuid4())
|
||||||
|
return super().execute(ipauniqueid, **options)
|
||||||
|
|
||||||
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
|
self.obj.convert_owner(entry_attrs, options)
|
||||||
|
return super(subid_add, self).post_callback(
|
||||||
|
ldap, dn, entry_attrs, *keys, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_del(LDAPDelete):
|
||||||
|
__doc__ = _("Delete a subordinate id.")
|
||||||
|
msg_summary = _('Deleted subordinate id "%(value)s"')
|
||||||
|
|
||||||
|
# internal command, subids cannot be removed
|
||||||
|
NO_CLI = True
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_mod(LDAPUpdate):
|
||||||
|
__doc__ = _("Modify a subordinate id.")
|
||||||
|
msg_summary = _('Modified subordinate id "%(value)s"')
|
||||||
|
|
||||||
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
|
self.obj.convert_owner(entry_attrs, options)
|
||||||
|
return super(subid_mod, self).post_callback(
|
||||||
|
ldap, dn, entry_attrs, *keys, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_find(LDAPSearch):
|
||||||
|
__doc__ = _("Search for subordinate id.")
|
||||||
|
msg_summary = ngettext(
|
||||||
|
"%(count)d subordinate id matched",
|
||||||
|
"%(count)d subordinate ids matched",
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
|
||||||
|
def pre_callback(
|
||||||
|
self, ldap, filters, attrs_list, base_dn, scope, *args, **options
|
||||||
|
):
|
||||||
|
attrs_list.append("objectclass")
|
||||||
|
return super(subid_find, self).pre_callback(
|
||||||
|
ldap, filters, attrs_list, base_dn, scope, *args, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
def args_options_2_entry(self, *args, **options):
|
||||||
|
entry_attrs = super(subid_find, self).args_options_2_entry(
|
||||||
|
*args, **options
|
||||||
|
)
|
||||||
|
owner = entry_attrs.get("ipaowner")
|
||||||
|
if owner is not None:
|
||||||
|
owner_dn = self.obj.get_owner_dn(owner, **options)
|
||||||
|
entry_attrs["ipaowner"] = owner_dn
|
||||||
|
return entry_attrs
|
||||||
|
|
||||||
|
def post_callback(self, ldap, entries, truncated, *args, **options):
|
||||||
|
for entry in entries:
|
||||||
|
self.obj.convert_owner(entry, options)
|
||||||
|
return super(subid_find, self).post_callback(
|
||||||
|
ldap, entries, truncated, *args, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_show(LDAPRetrieve):
|
||||||
|
__doc__ = _("Display information about a subordinate id.")
|
||||||
|
|
||||||
|
def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
|
||||||
|
attrs_list.append("objectclass")
|
||||||
|
return super(subid_show, self).pre_callback(
|
||||||
|
ldap, dn, attrs_list, *keys, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
|
self.obj.convert_owner(entry_attrs, options)
|
||||||
|
return super(subid_show, self).post_callback(
|
||||||
|
ldap, dn, entry_attrs, *keys, **options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_generate(LDAPQuery):
|
||||||
|
__doc__ = _(
|
||||||
|
"Generate and auto-assign subuid and subgid range to user entry"
|
||||||
|
)
|
||||||
|
|
||||||
|
has_output = output.standard_entry
|
||||||
|
|
||||||
|
takes_options = LDAPQuery.takes_options + (
|
||||||
|
Str(
|
||||||
|
"ipaowner?",
|
||||||
|
cli_name="owner",
|
||||||
|
label=_("Owner"),
|
||||||
|
doc=_("Owning user of subordinate id entry"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_args(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def execute(self, *keys, **options):
|
||||||
|
owner_uid = options.get("ipaowner")
|
||||||
|
# default to current user
|
||||||
|
if owner_uid is None:
|
||||||
|
owner_dn = DN(self.api.Backend.ldap2.conn.whoami_s()[4:])
|
||||||
|
# validate it's a user and not a service or host
|
||||||
|
owner_dn = self.obj.get_owner_dn(owner_dn)
|
||||||
|
owner_uid = owner_dn[0].value
|
||||||
|
|
||||||
|
return self.api.Command.subid_add(
|
||||||
|
description="auto-assigned subid",
|
||||||
|
ipaowner=owner_uid,
|
||||||
|
version=options["version"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_match(subid_find):
|
||||||
|
__doc__ = _("Match users by any subordinate uid in their range")
|
||||||
|
|
||||||
|
def get_options(self):
|
||||||
|
base_options = {p.name for p in self.obj.takes_params}
|
||||||
|
for option in super().get_options():
|
||||||
|
if option.name == "ipasubuidnumber":
|
||||||
|
yield option.clone(
|
||||||
|
label=_("SubUID match"),
|
||||||
|
doc=_("Match value for subordinate user ID"),
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
elif option.name not in base_options:
|
||||||
|
# raw, version
|
||||||
|
yield option.clone()
|
||||||
|
|
||||||
|
def pre_callback(
|
||||||
|
self, ldap, filters, attrs_list, base_dn, scope, *args, **options
|
||||||
|
):
|
||||||
|
# search for candidates in range
|
||||||
|
# Code assumes that no subordinate count is larger than SUBID_COUNT
|
||||||
|
filters = self.obj.get_subid_match_candidate_filter(
|
||||||
|
ldap,
|
||||||
|
subuid=options["ipasubuidnumber"],
|
||||||
|
subgid=None,
|
||||||
|
)
|
||||||
|
attrs_list.extend(self.obj.default_attributes)
|
||||||
|
|
||||||
|
return filters, base_dn, scope
|
||||||
|
|
||||||
|
def post_callback(self, ldap, entries, truncated, *args, **options):
|
||||||
|
# filter out mismatches manually
|
||||||
|
osubuid = options["ipasubuidnumber"]
|
||||||
|
new_entries = []
|
||||||
|
for entry in entries:
|
||||||
|
esubuid = int(entry.single_value["ipasubuidnumber"])
|
||||||
|
esubcount = int(entry.single_value["ipasubuidcount"])
|
||||||
|
minsubuid = esubuid
|
||||||
|
maxsubuid = esubuid + esubcount - 1
|
||||||
|
if minsubuid <= osubuid <= maxsubuid:
|
||||||
|
new_entries.append(entry)
|
||||||
|
|
||||||
|
entries[:] = new_entries
|
||||||
|
|
||||||
|
return truncated
|
||||||
|
|
||||||
|
|
||||||
|
@register()
|
||||||
|
class subid_stats(LDAPQuery):
|
||||||
|
__doc__ = _("Subordinate id statistics")
|
||||||
|
|
||||||
|
takes_options = ()
|
||||||
|
has_output = (
|
||||||
|
output.summary,
|
||||||
|
output.Entry("result"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_args(self):
|
||||||
|
return ()
|
||||||
|
|
||||||
|
def get_remaining_dna(self, ldap, **options):
|
||||||
|
base_dn = DN(
|
||||||
|
self.api.env.container_dna_subordinate_ids, self.api.env.basedn
|
||||||
|
)
|
||||||
|
entries, _truncated = ldap.find_entries(
|
||||||
|
"(objectClass=dnaSharedConfig)",
|
||||||
|
attrs_list=["dnaRemainingValues"],
|
||||||
|
base_dn=base_dn,
|
||||||
|
scope=ldap.SCOPE_ONELEVEL,
|
||||||
|
)
|
||||||
|
return sum(
|
||||||
|
int(entry.single_value["dnaRemainingValues"]) for entry in entries
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_idrange(self, ldap, **options):
|
||||||
|
cn = f"{self.api.env.realm}_subid_range"
|
||||||
|
result = self.api.Command.idrange_show(cn, version=options["version"])
|
||||||
|
baseid = int(result["result"]["ipabaseid"][0])
|
||||||
|
rangesize = int(result["result"]["ipaidrangesize"][0])
|
||||||
|
return baseid, rangesize
|
||||||
|
|
||||||
|
def get_subid_assigned(self, ldap, **options):
|
||||||
|
dn = DN(self.api.env.container_subids, self.api.env.basedn)
|
||||||
|
entry = ldap.get_entry(dn=dn, attrs_list=["numSubordinates"])
|
||||||
|
return int(entry.single_value["numSubordinates"])
|
||||||
|
|
||||||
|
def execute(self, *keys, **options):
|
||||||
|
ldap = self.obj.backend
|
||||||
|
dna_remaining = self.get_remaining_dna(ldap, **options)
|
||||||
|
baseid, rangesize = self.get_idrange(ldap, **options)
|
||||||
|
assigned_subids = self.get_subid_assigned(ldap, **options)
|
||||||
|
remaining_subids = dna_remaining // constants.SUBID_COUNT
|
||||||
|
return dict(
|
||||||
|
summary=_("%(remaining)i remaining subordinate id ranges")
|
||||||
|
% {
|
||||||
|
"remaining": remaining_subids,
|
||||||
|
},
|
||||||
|
result=dict(
|
||||||
|
baseid=baseid,
|
||||||
|
rangesize=rangesize,
|
||||||
|
dna_remaining=dna_remaining,
|
||||||
|
assigned_subids=assigned_subids,
|
||||||
|
remaining_subids=remaining_subids,
|
||||||
|
),
|
||||||
|
)
|
||||||
@@ -51,8 +51,6 @@ from .baseuser import (
|
|||||||
baseuser_remove_principal,
|
baseuser_remove_principal,
|
||||||
baseuser_add_certmapdata,
|
baseuser_add_certmapdata,
|
||||||
baseuser_remove_certmapdata,
|
baseuser_remove_certmapdata,
|
||||||
baseuser_auto_subid,
|
|
||||||
baseuser_match_subid,
|
|
||||||
)
|
)
|
||||||
from .idviews import remove_ipaobject_overrides
|
from .idviews import remove_ipaobject_overrides
|
||||||
from ipalib.plugable import Registry
|
from ipalib.plugable import Registry
|
||||||
@@ -205,8 +203,6 @@ class user(baseuser):
|
|||||||
'ipapermright': {'read', 'search', 'compare'},
|
'ipapermright': {'read', 'search', 'compare'},
|
||||||
'ipapermdefaultattr': {
|
'ipapermdefaultattr': {
|
||||||
'ipauniqueid', 'ipasshpubkey', 'ipauserauthtype', 'userclass',
|
'ipauniqueid', 'ipasshpubkey', 'ipauserauthtype', 'userclass',
|
||||||
'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber',
|
|
||||||
'ipasubgidcount',
|
|
||||||
},
|
},
|
||||||
'fixup_function': fix_addressbook_permission_bindrule,
|
'fixup_function': fix_addressbook_permission_bindrule,
|
||||||
},
|
},
|
||||||
@@ -670,6 +666,17 @@ class user_add(baseuser_add):
|
|||||||
# if both randompassword and userpassword options were used
|
# if both randompassword and userpassword options were used
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# generate subid
|
||||||
|
default_subid = config.single_value.get(
|
||||||
|
'ipaUserDefaultSubordinateId', 'FALSE'
|
||||||
|
)
|
||||||
|
if default_subid == 'TRUE':
|
||||||
|
result = self.api.Command.subid_generate(
|
||||||
|
ipaowner=entry_attrs.single_value['uid'],
|
||||||
|
version=options['version']
|
||||||
|
)
|
||||||
|
entry_attrs["memberOf"].append(result['result']['dn'])
|
||||||
|
|
||||||
self.obj.get_preserved_attribute(entry_attrs, options)
|
self.obj.get_preserved_attribute(entry_attrs, options)
|
||||||
|
|
||||||
self.post_common_callback(ldap, dn, entry_attrs, *keys, **options)
|
self.post_common_callback(ldap, dn, entry_attrs, *keys, **options)
|
||||||
@@ -757,7 +764,9 @@ class user_del(baseuser_del):
|
|||||||
# of OTP tokens.
|
# of OTP tokens.
|
||||||
check_protected_member(keys[-1])
|
check_protected_member(keys[-1])
|
||||||
|
|
||||||
if not options.get('preserve', False):
|
preserve = options.get('preserve', False)
|
||||||
|
|
||||||
|
if not preserve:
|
||||||
# Remove any ID overrides tied with this user
|
# Remove any ID overrides tied with this user
|
||||||
try:
|
try:
|
||||||
remove_ipaobject_overrides(self.obj.backend, self.obj.api, dn)
|
remove_ipaobject_overrides(self.obj.backend, self.obj.api, dn)
|
||||||
@@ -780,6 +789,15 @@ class user_del(baseuser_del):
|
|||||||
else:
|
else:
|
||||||
self.api.Command.otptoken_del(token)
|
self.api.Command.otptoken_del(token)
|
||||||
|
|
||||||
|
# XXX: preserving doesn't work yet, see subordinate-ids.md
|
||||||
|
# Delete all subid entries owned by this user.
|
||||||
|
results = self.api.Command.subid_find(ipaowner=owner)["result"]
|
||||||
|
for subid_entry in results:
|
||||||
|
subid_pkey = self.api.Object.subid.get_primary_key_from_dn(
|
||||||
|
subid_entry["dn"]
|
||||||
|
)
|
||||||
|
self.api.Command.subid_del(subid_pkey)
|
||||||
|
|
||||||
return dn
|
return dn
|
||||||
|
|
||||||
def execute(self, *keys, **options):
|
def execute(self, *keys, **options):
|
||||||
@@ -829,6 +847,7 @@ class user_mod(baseuser_mod):
|
|||||||
self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys,
|
self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys,
|
||||||
**options)
|
**options)
|
||||||
validate_nsaccountlock(entry_attrs)
|
validate_nsaccountlock(entry_attrs)
|
||||||
|
# TODO: forward uidNumber changes and rename to subids
|
||||||
return dn
|
return dn
|
||||||
|
|
||||||
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
@@ -1311,13 +1330,3 @@ class user_add_principal(baseuser_add_principal):
|
|||||||
class user_remove_principal(baseuser_remove_principal):
|
class user_remove_principal(baseuser_remove_principal):
|
||||||
__doc__ = _('Remove principal alias from the user entry')
|
__doc__ = _('Remove principal alias from the user entry')
|
||||||
msg_summary = _('Removed aliases from user "%(value)s"')
|
msg_summary = _('Removed aliases from user "%(value)s"')
|
||||||
|
|
||||||
|
|
||||||
@register()
|
|
||||||
class user_auto_subid(baseuser_auto_subid):
|
|
||||||
__doc__ = baseuser_auto_subid.__doc__
|
|
||||||
|
|
||||||
|
|
||||||
@register()
|
|
||||||
class user_match_subid(baseuser_match_subid):
|
|
||||||
__doc__ = baseuser_match_subid.__doc__
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ class TestSubordinateId(IntegrationTest):
|
|||||||
topology = "star"
|
topology = "star"
|
||||||
|
|
||||||
def _parse_result(self, result):
|
def _parse_result(self, result):
|
||||||
|
# ipa CLI should get an --outform json option
|
||||||
info = {}
|
info = {}
|
||||||
for line in result.stdout_text.split("\n"):
|
for line in result.stdout_text.split("\n"):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
@@ -42,39 +43,69 @@ class TestSubordinateId(IntegrationTest):
|
|||||||
info[k] = set(v)
|
info[k] = set(v)
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def get_user(self, uid):
|
def assert_subid_info(self, uid, info):
|
||||||
cmd = ["ipa", "user-show", "--all", "--raw", uid]
|
assert info["ipauniqueid"]
|
||||||
result = self.master.run_command(cmd)
|
basedn = self.master.domain.basedn
|
||||||
return self._parse_result(result)
|
assert info["ipaowner"] == f"uid={uid},cn=users,cn=accounts,{basedn}"
|
||||||
|
assert info["ipasubuidnumber"] == info["ipasubuidnumber"]
|
||||||
|
assert info["ipasubuidnumber"] >= SUBID_RANGE_START
|
||||||
|
assert info["ipasubuidnumber"] <= SUBID_RANGE_MAX
|
||||||
|
assert info["ipasubuidcount"] == SUBID_COUNT
|
||||||
|
assert info["ipasubgidnumber"] == info["ipasubgidnumber"]
|
||||||
|
assert info["ipasubgidnumber"] == info["ipasubuidnumber"]
|
||||||
|
assert info["ipasubgidcount"] == SUBID_COUNT
|
||||||
|
|
||||||
def user_auto_subid(self, uid, **kwargs):
|
def assert_subid(self, uid, *, match):
|
||||||
cmd = ["ipa", "user-auto-subid", uid]
|
cmd = ["ipa", "subid-find", "--raw", "--owner", uid]
|
||||||
|
result = self.master.run_command(cmd, raiseonerr=False)
|
||||||
|
if not match:
|
||||||
|
assert result.returncode >= 1
|
||||||
|
if result.returncode == 1:
|
||||||
|
assert "0 subordinate ids matched" in result.stdout_text
|
||||||
|
elif result.returncode == 2:
|
||||||
|
assert "user not found" in result.stderr_text
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
assert result.returncode == 0
|
||||||
|
assert "1 subordinate id matched" in result.stdout_text
|
||||||
|
info = self._parse_result(result)
|
||||||
|
self.assert_subid_info(uid, info)
|
||||||
|
self.master.run_command(
|
||||||
|
["ipa", "subid-show", info["ipauniqueid"]]
|
||||||
|
)
|
||||||
|
return info
|
||||||
|
|
||||||
|
def subid_generate(self, uid, **kwargs):
|
||||||
|
cmd = ["ipa", "subid-generate"]
|
||||||
|
if uid is not None:
|
||||||
|
cmd.extend(("--owner", uid))
|
||||||
return self.master.run_command(cmd, **kwargs)
|
return self.master.run_command(cmd, **kwargs)
|
||||||
|
|
||||||
def test_auto_subid(self):
|
def test_auto_generate_subid(self):
|
||||||
tasks.kinit_admin(self.master)
|
|
||||||
uid = "testuser_auto1"
|
uid = "testuser_auto1"
|
||||||
tasks.user_add(self.master, uid)
|
passwd = "Secret123"
|
||||||
info = self.get_user(uid)
|
tasks.create_active_user(self.master, uid, password=passwd)
|
||||||
assert "ipasubuidcount" not in info
|
|
||||||
|
|
||||||
self.user_auto_subid(uid)
|
tasks.kinit_admin(self.master)
|
||||||
info = self.get_user(uid)
|
self.assert_subid(uid, match=False)
|
||||||
assert "ipasubuidcount" in info
|
|
||||||
|
|
||||||
|
# add subid by name
|
||||||
|
self.subid_generate(uid)
|
||||||
|
info = self.assert_subid(uid, match=True)
|
||||||
|
|
||||||
|
# second generate fails due to unique index on ipaowner
|
||||||
|
result = self.subid_generate(uid, raiseonerr=False)
|
||||||
|
assert result.returncode > 0
|
||||||
|
assert f'for user "{uid}" already exists' in result.stderr_text
|
||||||
|
|
||||||
|
# check matching
|
||||||
subuid = info["ipasubuidnumber"]
|
subuid = info["ipasubuidnumber"]
|
||||||
result = self.master.run_command(
|
for offset in (0, 1, 65535):
|
||||||
["ipa", "user-match-subid", f"--subuid={subuid}", "--raw"]
|
result = self.master.run_command(
|
||||||
)
|
["ipa", "subid-match", f"--subuid={subuid + offset}", "--raw"]
|
||||||
match = self._parse_result(result)
|
)
|
||||||
assert match["uid"] == uid
|
match = self._parse_result(result)
|
||||||
assert match["ipasubuidnumber"] == info["ipasubuidnumber"]
|
self.assert_subid_info(uid, match)
|
||||||
assert match["ipasubuidnumber"] >= SUBID_RANGE_START
|
|
||||||
assert match["ipasubuidnumber"] <= SUBID_RANGE_MAX
|
|
||||||
assert match["ipasubuidcount"] == SUBID_COUNT
|
|
||||||
assert match["ipasubgidnumber"] == info["ipasubgidnumber"]
|
|
||||||
assert match["ipasubgidnumber"] == match["ipasubuidnumber"]
|
|
||||||
assert match["ipasubgidcount"] == SUBID_COUNT
|
|
||||||
|
|
||||||
def test_ipa_subid_script(self):
|
def test_ipa_subid_script(self):
|
||||||
tasks.kinit_admin(self.master)
|
tasks.kinit_admin(self.master)
|
||||||
@@ -85,34 +116,28 @@ class TestSubordinateId(IntegrationTest):
|
|||||||
uid = f"testuser_script{i}"
|
uid = f"testuser_script{i}"
|
||||||
users.append(uid)
|
users.append(uid)
|
||||||
tasks.user_add(self.master, uid)
|
tasks.user_add(self.master, uid)
|
||||||
info = self.get_user(uid)
|
self.assert_subid(uid, match=False)
|
||||||
assert "ipasubuidcount" not in info
|
|
||||||
|
|
||||||
cmd = [tool, "--verbose", "--group", "ipausers"]
|
cmd = [tool, "--verbose", "--group", "ipausers"]
|
||||||
self.master.run_command(cmd)
|
self.master.run_command(cmd)
|
||||||
|
|
||||||
for uid in users:
|
for uid in users:
|
||||||
info = self.get_user(uid)
|
self.assert_subid(uid, match=True)
|
||||||
assert info["ipasubuidnumber"] >= SUBID_RANGE_START
|
|
||||||
assert info["ipasubuidnumber"] <= SUBID_RANGE_MAX
|
|
||||||
assert info["ipasubuidnumber"] == info["ipasubgidnumber"]
|
|
||||||
assert info["ipasubuidcount"] == SUBID_COUNT
|
|
||||||
assert info["ipasubuidcount"] == info["ipasubgidcount"]
|
|
||||||
|
|
||||||
def test_subid_selfservice(self):
|
def test_subid_selfservice(self):
|
||||||
tasks.kinit_admin(self.master)
|
uid1 = "testuser_selfservice1"
|
||||||
|
uid2 = "testuser_selfservice2"
|
||||||
uid = "testuser_selfservice1"
|
|
||||||
password = "Secret123"
|
password = "Secret123"
|
||||||
role = "Subordinate ID Selfservice User"
|
role = "Subordinate ID Selfservice User"
|
||||||
|
|
||||||
tasks.user_add(self.master, uid, password=password)
|
tasks.create_active_user(self.master, uid1, password=password)
|
||||||
tasks.kinit_user(
|
tasks.create_active_user(self.master, uid2, password=password)
|
||||||
self.master, uid, f"{password}\n{password}\n{password}\n"
|
|
||||||
)
|
tasks.kinit_user(self.master, uid1, password=password)
|
||||||
info = self.get_user(uid)
|
self.assert_subid(uid1, match=False)
|
||||||
assert "ipasubuidcount" not in info
|
result = self.subid_generate(uid1, raiseonerr=False)
|
||||||
result = self.user_auto_subid(uid, raiseonerr=False)
|
assert result.returncode > 0
|
||||||
|
result = self.subid_generate(None, raiseonerr=False)
|
||||||
assert result.returncode > 0
|
assert result.returncode > 0
|
||||||
|
|
||||||
tasks.kinit_admin(self.master)
|
tasks.kinit_admin(self.master)
|
||||||
@@ -121,10 +146,14 @@ class TestSubordinateId(IntegrationTest):
|
|||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tasks.kinit_user(self.master, uid, password)
|
tasks.kinit_user(self.master, uid1, password)
|
||||||
self.user_auto_subid(uid)
|
self.subid_generate(uid1)
|
||||||
info = self.get_user(uid)
|
self.assert_subid(uid1, match=True)
|
||||||
assert "ipasubuidcount" in info
|
|
||||||
|
# add subid from whoami
|
||||||
|
tasks.kinit_as_user(self.master, uid2, password=password)
|
||||||
|
self.subid_generate(None)
|
||||||
|
self.assert_subid(uid2, match=True)
|
||||||
finally:
|
finally:
|
||||||
tasks.kinit_admin(self.master)
|
tasks.kinit_admin(self.master)
|
||||||
self.master.run_command(
|
self.master.run_command(
|
||||||
@@ -140,45 +169,46 @@ class TestSubordinateId(IntegrationTest):
|
|||||||
password = "Secret123"
|
password = "Secret123"
|
||||||
|
|
||||||
# create user administrator
|
# create user administrator
|
||||||
tasks.user_add(self.master, uid_useradmin, password=password)
|
tasks.create_active_user(
|
||||||
|
self.master, uid_useradmin, password=password
|
||||||
|
)
|
||||||
# add user to user admin group
|
# add user to user admin group
|
||||||
tasks.kinit_admin(self.master)
|
tasks.kinit_admin(self.master)
|
||||||
self.master.run_command(
|
self.master.run_command(
|
||||||
["ipa", "role-add-member", role, f"--users={uid_useradmin}"],
|
["ipa", "role-add-member", role, f"--users={uid_useradmin}"],
|
||||||
)
|
)
|
||||||
# kinit as user admin
|
# kinit as user admin
|
||||||
tasks.kinit_user(
|
tasks.kinit_user(self.master, uid_useradmin, password)
|
||||||
self.master,
|
|
||||||
uid_useradmin,
|
|
||||||
f"{password}\n{password}\n{password}\n",
|
|
||||||
)
|
|
||||||
# create new user as user admin
|
# create new user as user admin
|
||||||
tasks.user_add(self.master, uid)
|
tasks.user_add(self.master, uid)
|
||||||
# assign new subid to user (with useradmin credentials)
|
# assign new subid to user (with useradmin credentials)
|
||||||
self.user_auto_subid(uid)
|
self.subid_generate(uid)
|
||||||
|
|
||||||
def test_subordinate_default_objclass(self):
|
# test that user admin can preserve and delete users with subids
|
||||||
|
self.master.run_command(["ipa", "user-del", "--preserve", uid])
|
||||||
|
# XXX does not work, see subordinate-ids.md
|
||||||
|
# subid should still exist
|
||||||
|
# self.assert_subid(uid, match=True)
|
||||||
|
# final delete should remove the user and subid
|
||||||
|
self.master.run_command(["ipa", "user-del", uid])
|
||||||
|
self.assert_subid(uid, match=False)
|
||||||
|
|
||||||
|
def tset_subid_auto_assign(self):
|
||||||
tasks.kinit_admin(self.master)
|
tasks.kinit_admin(self.master)
|
||||||
|
uid = "testuser_autoassign_user1"
|
||||||
|
|
||||||
result = self.master.run_command(
|
self.master.run_command(
|
||||||
["ipa", "config-show", "--raw", "--all"]
|
["ipa", "config-mod", "--user-default-subid=true"]
|
||||||
)
|
)
|
||||||
info = self._parse_result(result)
|
|
||||||
usercls = info["ipauserobjectclasses"]
|
|
||||||
assert "ipasubordinateid" not in usercls
|
|
||||||
|
|
||||||
cmd = [
|
try:
|
||||||
"ipa",
|
tasks.user_add(self.master, uid)
|
||||||
"config-mod",
|
self.assert_subid(uid, match=True)
|
||||||
"--addattr",
|
finally:
|
||||||
"ipaUserObjectClasses=ipasubordinateid",
|
self.master.run_command(
|
||||||
]
|
["ipa", "config-mod", "--user-default-subid=false"]
|
||||||
self.master.run_command(cmd)
|
)
|
||||||
|
|
||||||
uid = "testuser_usercls1"
|
|
||||||
tasks.user_add(self.master, uid)
|
|
||||||
info = self.get_user(uid)
|
|
||||||
assert "ipasubuidcount" in info
|
|
||||||
|
|
||||||
def test_idrange_subid(self):
|
def test_idrange_subid(self):
|
||||||
tasks.kinit_admin(self.master)
|
tasks.kinit_admin(self.master)
|
||||||
@@ -199,3 +229,7 @@ class TestSubordinateId(IntegrationTest):
|
|||||||
assert info["ipanttrusteddomainsid"].startswith(
|
assert info["ipanttrusteddomainsid"].startswith(
|
||||||
"S-1-5-21-738065-838566-"
|
"S-1-5-21-738065-838566-"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_subid_stats(self):
|
||||||
|
tasks.kinit_admin(self.master)
|
||||||
|
self.master.run_command(["ipa", "subid-stats"])
|
||||||
|
|||||||
Reference in New Issue
Block a user