Support adding user ID overrides as group and role members

Second part of adding support to manage IPA as a user from a trusted
Active Directory forest.

Treat user ID overrides as members of groups and roles.

For example, adding an Active Directory user ID override as a member of
'admins' group would make it equivalent to built-in FreeIPA 'admin'
user.

We already support self-service operations by Active Directory users if
their user ID override does exist. When Active Directory user
authenticates with GSSAPI against the FreeIPA LDAP server, its Kerberos
principal is automatically mapped to the user's ID override in the
Default Trust View. LDAP server's access control plugin uses membership
information of the corresponding LDAP entry to decide how access can be
allowed.

With the change, users from trusted Active Directory forests can
manage FreeIPA resources if the groups are part of appropriate roles or
their ID overrides are members of the roles themselves.

Fixes: https://pagure.io/freeipa/issue/7255

Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
Alexander Bokovoy 2020-06-03 13:04:46 +03:00 committed by Rob Crittenden
parent 973e0c04e4
commit bee4204039
6 changed files with 49 additions and 18 deletions

View File

@ -179,7 +179,7 @@ aci: (targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:S
dn: cn=views,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || description || entryusn || gidnumber || ipaanchoruuid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaGroupOverride)")(version 3.0;acl "permission:System: Read Group ID Overrides";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=views,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "createtimestamp || description || entryusn || gecos || gidnumber || homedirectory || ipaanchoruuid || ipaoriginaluid || ipasshpubkey || loginshell || modifytimestamp || objectclass || uid || uidnumber || usercertificate")(targetfilter = "(objectclass=ipaUserOverride)")(version 3.0;acl "permission:System: Read User ID Overrides";allow (compare,read,search) userdn = "ldap:///all";)
aci: (targetattr = "createtimestamp || description || entryusn || gecos || gidnumber || homedirectory || ipaanchoruuid || ipaoriginaluid || ipasshpubkey || loginshell || memberof || modifytimestamp || objectclass || uid || uidnumber || usercertificate")(targetfilter = "(objectclass=ipaUserOverride)")(version 3.0;acl "permission:System: Read User ID Overrides";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=ranges,cn=etc,dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || entryusn || ipabaseid || ipabaserid || ipaidrangesize || ipanttrusteddomainsid || iparangetype || ipasecondarybaserid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaidrange)")(version 3.0;acl "permission:System: Read ID Ranges";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=views,cn=accounts,dc=ipa,dc=example

34
API.txt
View File

@ -1959,10 +1959,11 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: group_add_member/1
args: 1,8,3
args: 1,9,3
arg: Str('cn', cli_name='group_name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('group*', alwaysask=True, cli_name='groups')
option: Str('idoverrideuser*', alwaysask=True, cli_name='idoverrideusers')
option: Str('ipaexternalmember*', cli_name='external')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
@ -2000,7 +2001,7 @@ output: Output('result', type=[<type 'bool'>])
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: group_find/1
args: 1,34,4
args: 1,36,4
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('cn?', autofill=False, cli_name='group_name')
@ -2008,6 +2009,7 @@ option: Str('description?', autofill=False, cli_name='desc')
option: Flag('external', autofill=True, cli_name='external', default=False)
option: Int('gidnumber?', autofill=False, cli_name='gid')
option: Str('group*', cli_name='groups')
option: Str('idoverrideuser*', cli_name='idoverrideusers')
option: Str('in_group*', cli_name='in_groups')
option: Str('in_hbacrule*', cli_name='in_hbacrules')
option: Str('in_netgroup*', cli_name='in_netgroups')
@ -2016,6 +2018,7 @@ option: Str('in_sudorule*', cli_name='in_sudorules')
option: Str('membermanager_group*', cli_name='membermanager_groups')
option: Str('membermanager_user*', cli_name='membermanager_users')
option: Str('no_group*', cli_name='no_groups')
option: Str('no_idoverrideuser*', cli_name='no_idoverrideusers')
option: Flag('no_members', autofill=True, default=True)
option: Principal('no_service*', cli_name='no_services')
option: Str('no_user*', cli_name='no_users')
@ -2060,10 +2063,11 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: group_remove_member/1
args: 1,8,3
args: 1,9,3
arg: Str('cn', cli_name='group_name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('group*', alwaysask=True, cli_name='groups')
option: Str('idoverrideuser*', alwaysask=True, cli_name='idoverrideusers')
option: Str('ipaexternalmember*', cli_name='external')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
@ -2920,7 +2924,7 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: idoverrideuser_add/1
args: 2,16,3
args: 2,17,3
arg: Str('idviewcn', cli_name='idview')
arg: Str('ipaanchoruuid', cli_name='anchor')
option: Str('addattr*', cli_name='addattr')
@ -2933,6 +2937,7 @@ option: Str('homedirectory?', cli_name='homedir')
option: Str('ipaoriginaluid?')
option: Str('ipasshpubkey*', cli_name='sshpubkey')
option: Str('loginshell?', cli_name='shell')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('setattr*', cli_name='setattr')
option: Str('uid?', cli_name='login')
@ -2943,11 +2948,12 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: idoverrideuser_add_cert/1
args: 2,5,3
args: 2,6,3
arg: Str('idviewcn', cli_name='idview')
arg: Str('ipaanchoruuid', cli_name='anchor')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('fallback_to_ldap?', autofill=True, default=False)
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Certificate('usercertificate+', alwaysask=True, cli_name='certificate')
option: Str('version?')
@ -2965,7 +2971,7 @@ output: Output('result', type=[<type 'dict'>])
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: ListOfPrimaryKeys('value')
command: idoverrideuser_find/1
args: 2,16,4
args: 2,17,4
arg: Str('idviewcn', cli_name='idview')
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
@ -2977,6 +2983,7 @@ option: Str('homedirectory?', autofill=False, cli_name='homedir')
option: Str('ipaanchoruuid?', autofill=False, cli_name='anchor')
option: Str('ipaoriginaluid?', autofill=False)
option: Str('loginshell?', autofill=False, cli_name='shell')
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)
@ -2989,7 +2996,7 @@ output: ListOfEntries('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: Output('truncated', type=[<type 'bool'>])
command: idoverrideuser_mod/1
args: 2,19,3
args: 2,20,3
arg: Str('idviewcn', cli_name='idview')
arg: Str('ipaanchoruuid', cli_name='anchor')
option: Str('addattr*', cli_name='addattr')
@ -3003,6 +3010,7 @@ option: Str('homedirectory?', autofill=False, cli_name='homedir')
option: Str('ipaoriginaluid?', autofill=False)
option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey')
option: Str('loginshell?', autofill=False, cli_name='shell')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('rename?', cli_name='rename')
option: Flag('rights', autofill=True, default=False)
@ -3015,11 +3023,12 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: idoverrideuser_remove_cert/1
args: 2,5,3
args: 2,6,3
arg: Str('idviewcn', cli_name='idview')
arg: Str('ipaanchoruuid', cli_name='anchor')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('fallback_to_ldap?', autofill=True, default=False)
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Certificate('usercertificate+', alwaysask=True, cli_name='certificate')
option: Str('version?')
@ -3027,11 +3036,12 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: idoverrideuser_show/1
args: 2,5,3
args: 2,6,3
arg: Str('idviewcn', cli_name='idview')
arg: Str('ipaanchoruuid', cli_name='anchor')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Flag('fallback_to_ldap?', autofill=True, default=False)
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Flag('rights', autofill=True, default=False)
option: Str('version?')
@ -4147,12 +4157,13 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: role_add_member/1
args: 1,9,3
args: 1,10,3
arg: Str('cn', cli_name='name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('group*', alwaysask=True, cli_name='groups')
option: Str('host*', alwaysask=True, cli_name='hosts')
option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups')
option: Str('idoverrideuser*', alwaysask=True, cli_name='idoverrideusers')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('service*', alwaysask=True, cli_name='services')
@ -4213,12 +4224,13 @@ output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: role_remove_member/1
args: 1,9,3
args: 1,10,3
arg: Str('cn', cli_name='name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('group*', alwaysask=True, cli_name='groups')
option: Str('host*', alwaysask=True, cli_name='hosts')
option: Str('hostgroup*', alwaysask=True, cli_name='hostgroups')
option: Str('idoverrideuser*', alwaysask=True, cli_name='idoverrideusers')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('service*', alwaysask=True, cli_name='services')

View File

@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
define(IPA_API_VERSION_MINOR, 238)
# Last change: permission ipapermbindruletype=self
define(IPA_API_VERSION_MINOR, 239)
# Last change: allow ID overrides for users to be members of groups and roles
########################################################

View File

@ -120,6 +120,10 @@ global_output_params = (
Str('memberof_hbacrule?',
label='Member of HBAC rule',
),
Str('member_idoverrideuser?',
label=_('Member ID user overrides'),),
Str('memberindirect_idoverrideuser?',
label=_('Indirect Member ID user overrides'),),
Str('memberindirect_user?',
label=_('Indirect Member users'),
),

View File

@ -40,7 +40,7 @@ from .baseldap import (
LDAPRemoveMember,
LDAPQuery,
)
from .idviews import remove_ipaobject_overrides
from .idviews import remove_ipaobject_overrides, handle_idoverride_memberof
from . import baseldap
from ipalib import _, ngettext
from ipalib import errors
@ -204,10 +204,10 @@ class group(LDAPObject):
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
'member': ['user', 'group', 'service'],
'member': ['user', 'group', 'service', 'idoverrideuser'],
'membermanager': ['user', 'group'],
'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
'memberindirect': ['user', 'group', 'service'],
'memberindirect': ['user', 'group', 'service', 'idoverrideuser'],
'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule',
'sudorule'],
}
@ -593,6 +593,12 @@ class group_add_member(LDAPAddMember):
takes_options = (ipaexternalmember_param,)
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
assert isinstance(dn, DN)
handle_idoverride_memberof(self, ldap, dn, found, not_found,
*keys, **options)
return dn
def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
assert isinstance(dn, DN)
result = (completed, dn)

View File

@ -32,6 +32,8 @@ from .baseldap import (
LDAPRemoveReverseMember)
from ipalib import api, Str, _, ngettext
from ipalib import output
from ipapython.dn import DN
from .idviews import handle_idoverride_memberof
__doc__ = _("""
Roles
@ -86,7 +88,8 @@ class role(LDAPObject):
# 'memberindirect', 'memberofindirect',
attribute_members = {
'member': ['user', 'group', 'host', 'hostgroup', 'service'],
'member': ['user', 'group', 'host', 'hostgroup', 'service',
'idoverrideuser'],
'memberof': ['privilege'],
}
reverse_members = {
@ -198,6 +201,12 @@ class role_show(LDAPRetrieve):
class role_add_member(LDAPAddMember):
__doc__ = _('Add members to a role.')
def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
assert isinstance(dn, DN)
handle_idoverride_memberof(self, ldap, dn, found, not_found,
*keys, **options)
return dn
@register()