Add group membership management

A group membership manager is a user or a group that can add members to
a group or remove members from a group or host group.

Fixes: https://pagure.io/freeipa/issue/8114
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
Christian Heimes 2019-11-06 06:58:42 +01:00
parent 87c24ebd34
commit f0a1f084b6
17 changed files with 421 additions and 20 deletions

View File

@ -99,7 +99,7 @@ aci: (targetattr = "ipaexternalmember")(targetfilter = "(objectclass=ipaexternal
dn: cn=groups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "member")(targetfilter = "(&(!(cn=admins))(objectclass=ipausergroup))")(version 3.0;acl "permission:System: Modify Group Membership";allow (write) groupdn = "ldap:///cn=System: Modify Group Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=groups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "cn || description || gidnumber || ipauniqueid || mepmanagedby || objectclass")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Modify Groups";allow (write) groupdn = "ldap:///cn=System: Modify Groups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
aci: (targetattr = "cn || description || gidnumber || ipauniqueid || membermanager || mepmanagedby || objectclass")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Modify Groups";allow (write) groupdn = "ldap:///cn=System: Modify Groups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=groups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "ipaexternalmember")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Read External Group Membership";allow (compare,read,search) userdn = "ldap:///all";)
dn: dc=ipa,dc=example
@ -109,7 +109,7 @@ aci: (targetattr = "member || memberhost || memberof || memberuid || memberuser"
dn: dc=ipa,dc=example
aci: (targetattr = "cn || createtimestamp || entryusn || gidnumber || memberuid || modifytimestamp || objectclass")(target = "ldap:///cn=groups,cn=*,cn=views,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Group Views Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";)
dn: cn=groups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "businesscategory || cn || createtimestamp || description || entryusn || gidnumber || ipaexternalmember || ipantsecurityidentifier || ipauniqueid || mepmanagedby || modifytimestamp || o || objectclass || ou || owner || seealso")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Read Groups";allow (compare,read,search) userdn = "ldap:///anyone";)
aci: (targetattr = "businesscategory || cn || createtimestamp || description || entryusn || gidnumber || ipaexternalmember || ipantsecurityidentifier || ipauniqueid || membermanager || mepmanagedby || modifytimestamp || o || objectclass || ou || owner || seealso")(targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Read Groups";allow (compare,read,search) userdn = "ldap:///anyone";)
dn: cn=groups,cn=accounts,dc=ipa,dc=example
aci: (targetfilter = "(|(objectclass=ipausergroup)(objectclass=posixgroup))")(version 3.0;acl "permission:System: Remove Groups";allow (delete) groupdn = "ldap:///cn=System: Remove Groups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=hbac,dc=ipa,dc=example
@ -169,11 +169,11 @@ aci: (targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:S
dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "member")(targetfilter = "(&(!(cn=ipaservers))(objectclass=ipahostgroup))")(version 3.0;acl "permission:System: Modify Hostgroup Membership";allow (write) groupdn = "ldap:///cn=System: Modify Hostgroup Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "cn || description")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Modify Hostgroups";allow (write) groupdn = "ldap:///cn=System: Modify Hostgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
aci: (targetattr = "cn || description || membermanager")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Modify Hostgroups";allow (write) groupdn = "ldap:///cn=System: Modify Hostgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "member || memberhost || memberof || memberuser")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Read Hostgroup Membership";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
aci: (targetattr = "businesscategory || cn || createtimestamp || description || entryusn || ipauniqueid || modifytimestamp || o || objectclass || ou || owner || seealso")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Read Hostgroups";allow (compare,read,search) userdn = "ldap:///all";)
aci: (targetattr = "businesscategory || cn || createtimestamp || description || entryusn || ipauniqueid || membermanager || modifytimestamp || o || objectclass || ou || owner || seealso")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Read Hostgroups";allow (compare,read,search) userdn = "ldap:///all";)
dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
aci: (targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Remove Hostgroups";allow (delete) groupdn = "ldap:///cn=System: Remove Hostgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
dn: cn=views,cn=accounts,dc=ipa,dc=example

64
API.txt
View File

@ -1972,6 +1972,18 @@ option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: group_add_member_manager/1
args: 1,6,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: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('user*', alwaysask=True, cli_name='users')
option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: group_del/1
args: 1,2,3
arg: Str('cn+', cli_name='group_name')
@ -1988,7 +2000,7 @@ output: Output('result', type=[<type 'bool'>])
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
command: group_find/1
args: 1,30,4
args: 1,34,4
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('cn?', autofill=False, cli_name='group_name')
@ -2001,6 +2013,8 @@ option: Str('in_hbacrule*', cli_name='in_hbacrules')
option: Str('in_netgroup*', cli_name='in_netgroups')
option: Str('in_role*', cli_name='in_roles')
option: Str('in_sudorule*', cli_name='in_sudorules')
option: Str('membermanager_group*', cli_name='membermanager_groups')
option: Str('membermanager_user*', cli_name='membermanager_users')
option: Str('no_group*', cli_name='no_groups')
option: Flag('no_members', autofill=True, default=True)
option: Principal('no_service*', cli_name='no_services')
@ -2011,6 +2025,8 @@ option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules')
option: Str('not_in_netgroup*', cli_name='not_in_netgroups')
option: Str('not_in_role*', cli_name='not_in_roles')
option: Str('not_in_sudorule*', cli_name='not_in_sudorules')
option: Str('not_membermanager_group*', cli_name='not_membermanager_groups')
option: Str('not_membermanager_user*', cli_name='not_membermanager_users')
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('posix', autofill=True, cli_name='posix', default=False)
option: Flag('private', autofill=True, cli_name='private', default=False)
@ -2057,6 +2073,18 @@ option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: group_remove_member_manager/1
args: 1,6,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: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('user*', alwaysask=True, cli_name='users')
option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: group_show/1
args: 1,5,3
arg: Str('cn', cli_name='group_name')
@ -2708,6 +2736,18 @@ option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: hostgroup_add_member_manager/1
args: 1,6,3
arg: Str('cn', cli_name='hostgroup_name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('group*', alwaysask=True, cli_name='groups')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('user*', alwaysask=True, cli_name='users')
option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: hostgroup_del/1
args: 1,2,3
arg: Str('cn+', cli_name='hostgroup_name')
@ -2717,7 +2757,7 @@ output: Output('result', type=[<type 'dict'>])
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: ListOfPrimaryKeys('value')
command: hostgroup_find/1
args: 1,21,4
args: 1,25,4
arg: Str('criteria?')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('cn?', autofill=False, cli_name='hostgroup_name')
@ -2728,6 +2768,8 @@ option: Str('in_hbacrule*', cli_name='in_hbacrules')
option: Str('in_hostgroup*', cli_name='in_hostgroups')
option: Str('in_netgroup*', cli_name='in_netgroups')
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_host*', cli_name='no_hosts')
option: Str('no_hostgroup*', cli_name='no_hostgroups')
option: Flag('no_members', autofill=True, default=True)
@ -2735,6 +2777,8 @@ option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules')
option: Str('not_in_hostgroup*', cli_name='not_in_hostgroups')
option: Str('not_in_netgroup*', cli_name='not_in_netgroups')
option: Str('not_in_sudorule*', cli_name='not_in_sudorules')
option: Str('not_membermanager_group*', cli_name='not_membermanager_groups')
option: Str('not_membermanager_user*', cli_name='not_membermanager_users')
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Int('sizelimit?', autofill=False)
@ -2771,6 +2815,18 @@ option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: hostgroup_remove_member_manager/1
args: 1,6,3
arg: Str('cn', cli_name='hostgroup_name')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('group*', alwaysask=True, cli_name='groups')
option: Flag('no_members', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Str('user*', alwaysask=True, cli_name='users')
option: Str('version?')
output: Output('completed', type=[<type 'int'>])
output: Output('failed', type=[<type 'dict'>])
output: Entry('result')
command: hostgroup_show/1
args: 1,5,3
arg: Str('cn', cli_name='hostgroup_name')
@ -6739,11 +6795,13 @@ default: env/1
default: group/1
default: group_add/1
default: group_add_member/1
default: group_add_member_manager/1
default: group_del/1
default: group_detach/1
default: group_find/1
default: group_mod/1
default: group_remove_member/1
default: group_remove_member_manager/1
default: group_show/1
default: hbacrule/1
default: hbacrule_add/1
@ -6796,10 +6854,12 @@ default: host_show/1
default: hostgroup/1
default: hostgroup_add/1
default: hostgroup_add_member/1
default: hostgroup_add_member_manager/1
default: hostgroup_del/1
default: hostgroup_find/1
default: hostgroup_mod/1
default: hostgroup_remove_member/1
default: hostgroup_remove_member_manager/1
default: hostgroup_show/1
default: i18n_messages/1
default: idoverridegroup/1

View File

@ -86,9 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
define(IPA_API_VERSION_MINOR, 234)
# Last change: Added new auth indicators to ipauserauthtype and krbprincipalauthind.
# Converted krbprincipalauthind from Str() to StrEnum()
define(IPA_API_VERSION_MINOR, 235)
# Last change: Add memberManager to groups.
########################################################
# Following values are auto-generated from values above

View File

@ -0,0 +1,130 @@
# Member Manager for group membership
## Overview
A member manager is a principal that is able to manage members of a
group. Member managers are able to add new members to a group or remove
existing members from a group. They cannot modify additional attributes
of a group as a part of the member manager role.
Member management is implemented for *user groups* and *host groups*.
Membership can be managed by users or user groups. Member managers are
independent from members. A principal can be a member manager of a
group without being a member of a group.
## Use Cases
An administrator can use member management feature to delegate some
control over user groups and host groups to users. For example a
project manager is now able to add new team members to a project group.
A NFS admin with member management capability for a host group is able
to indirectly influence an HBAC rules and control which hosts can
connect to an NFS file share.
## Implementation
The user group commands and host group commands are extended to handle
member managers. The plugin classes grow two additional sub commands,
one for adding and one for removing member managers. The show command
prints member manager users and member manager groups. The find command
can search by member manager.
Member managers are stored in a new LDAP attribute ``memberManager``
with OID 2.16.840.1.113730.3.8.23.1. It is multi-valued and contains
DNs of users and groups which can manage members of the group. The
attribute can be added to entries with object class ``ipaUserGroup``
or ``ipaHostGroup``. The attribute is indexed and its membership
controlled by referential integrity postoperation plugin.
New userattr ACIs grant principals with user DN or group DN in
``memberManager`` write permission to the ``member`` attribute of the
group.
The ``memberManager`` attribute is protected by the generic read and
modify permissions for each type of group. It is readable by everybody
with ``System: Read Groups`` / ``System: Read Hostgroups`` permission
and writable by everybody with ``System: Modify Groups`` /
``System: Modify Hostgroups`` permission.
## Examples
Add example user and groups:
```
$ kinit admin
$ ipa user-add john --first John --last Doe --random
$ ipa user-add tom --first Tom --last Doe --random
$ ipa group-add project
$ ipa group-add project_admins
```
Make user and group member managers:
```
$ ipa group-add-member-manager project --users=john
$ ipa group-add-member-manager project --groups=project_admins
```
Show group:
```
$ ipa group-show project
Group name: project
GID: 787600003
Membership managed by groups: project_admins
Membership managed by users: john
```
Find groups by member managers:
```
$ ipa group-find --membermanager-users=john
---------------
1 group matched
---------------
Group name: project
GID: 787600003
----------------------------
Number of entries returned 1
----------------------------
$ ipa group-find --membermanager-groups=project_admins
---------------
1 group matched
---------------
Group name: project
GID: 787600003
----------------------------
Number of entries returned 1
----------------------------
```
Use member management capability:
```
$ kinit john
$ ipa group-add-member project --users=tom
Group name: project
GID: 787600003
Member users: tom
Membership managed by groups: project_admins
Membership managed by users: john
-------------------------
Number of members added 1
-------------------------
```
Remove member management capability:
```
$ kinit admin
$ ipa group-remove-member-manager project --groups=project_admins
Group name: project
GID: 787600003
Member users: tom
Membership managed by users: john
---------------------------
Number of members removed 1
---------------------------
```

View File

@ -2,6 +2,7 @@
##
## Attributes: 2.16.840.1.113730.3.8.3 - V2 base attributres
## ObjectClasses: 2.16.840.1.113730.3.8.4 - V2 base objectclasses
## Attributes: 2.16.840.1.113730.3.8.23 - V4 base attributes
##
dn: cn=schema
attributeTypes: (2.16.840.1.113730.3.8.3.1 NAME 'ipaUniqueID' DESC 'Unique identifier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
@ -21,8 +22,10 @@ objectClasses: (2.16.840.1.113730.3.8.4.14 NAME 'ipaEntitlement' DESC 'IPA Entit
objectClasses: (2.16.840.1.113730.3.8.4.15 NAME 'ipaPermission' DESC 'IPA Permission objectclass' AUXILIARY MAY ( ipaPermissionType ) X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.2 NAME 'ipaService' DESC 'IPA service objectclass' AUXILIARY MAY ( memberOf $ managedBy $ ipaKrbAuthzData) X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.3 NAME 'nestedGroup' DESC 'Group that supports nesting' SUP groupOfNames STRUCTURAL MAY memberOf X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.4 NAME 'ipaUserGroup' DESC 'IPA user group object class' SUP nestedGroup STRUCTURAL X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.5 NAME 'ipaHostGroup' DESC 'IPA host group object class' SUP nestedGroup STRUCTURAL X-ORIGIN 'IPA v2' )
# Same for memberManager except it's actually v4 attribute
attributeTypes: (2.16.840.1.113730.3.8.23.1 NAME 'memberManager' DESC 'DNs of entries allowed to manage group membership' SUP distinguishedName EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'IPA v4')
objectClasses: (2.16.840.1.113730.3.8.4.4 NAME 'ipaUserGroup' DESC 'IPA user group object class' SUP nestedGroup STRUCTURAL MAY memberManager X-ORIGIN 'IPA v2' )
objectClasses: (2.16.840.1.113730.3.8.4.5 NAME 'ipaHostGroup' DESC 'IPA host group object class' SUP nestedGroup STRUCTURAL MAY memberManager X-ORIGIN 'IPA v2' )
attributeTypes: (2.16.840.1.113730.3.8.3.5 NAME 'memberUser' DESC 'Reference to a principal that performs an action (usually user).' SUP distinguishedName EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'IPA v2' )
attributeTypes: (2.16.840.1.113730.3.8.3.6 NAME 'userCategory' DESC 'Additional classification for users' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' )
attributeTypes: (2.16.840.1.113730.3.8.3.7 NAME 'memberHost' DESC 'Reference to a device where the operation takes place (usually host).' SUP distinguishedName EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'IPA v2' )

View File

@ -70,6 +70,19 @@ changetype: modify
add: aci
aci: (targetattr = "krbPrincipalKey || krbLastPwdChange")(target = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX")(version 3.0;acl "Admins can manage host keytab";allow (write) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
# Allow member managers to modify members of user groups
dn: cn=groups,cn=accounts,$SUFFIX
changetype: modify
add: aci
aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN" or userattr = "memberManager#GROUPDN";)
# Allow member managers to modify members of a host group
dn: cn=hostgroups,cn=accounts,$SUFFIX
changetype: modify
add: aci
aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN" or userattr = "memberManager#GROUPDN";)
# This is used for the host/service one-time passwordn and keytab indirectors.
# We can do a query on a DN to see if an attribute exists.
dn: cn=accounts,$SUFFIX

View File

@ -417,3 +417,12 @@ objectClass: top
objectClass: nsIndex
nsSystemIndex: false
nsIndexType: eq
dn: cn=memberManager,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
changetype: add
cn: memberManager
objectClass: top
objectClass: nsIndex
nsSystemIndex: false
nsIndexType: eq
nsIndexType: pres

View File

@ -442,7 +442,8 @@ exp.entity_builder = IPA.entity_builder = function(entity) {
'member',
'settings',
'memberof',
'managedby'
'managedby',
'membermanager'
];
/**

View File

@ -151,6 +151,20 @@ return {
}
]
},
{
$type: 'association',
name: 'membermanager_group',
associator: IPA.serial_associator,
add_title: '@i18n:objects.group.add_membermanager_group',
remove_title: '@i18n:objects.group.remove_membermanager_group'
},
{
$type: 'association',
name: 'membermanager_user',
associator: IPA.serial_associator,
add_title: '@i18n:objects.group.add_membermanager_user',
remove_title: '@i18n:objects.group.remove_membermanager_user'
},
{
$type: 'association',
name: 'memberof_group',

View File

@ -63,15 +63,31 @@ return {
}
]
},
{
$type: 'association',
name: 'membermanager_group',
associator: IPA.serial_associator,
add_title: '@i18n:objects.hostgroup.add_membermanager_group',
remove_title: '@i18n:objects.hostgroup.remove_membermanager_group'
},
{
$type: 'association',
name: 'membermanager_user',
associator: IPA.serial_associator,
add_title: '@i18n:objects.hostgroup.add_membermanager_user',
remove_title: '@i18n:objects.hostgroup.remove_membermanager_user'
},
{
$type: 'association',
name: 'member_host',
associator: IPA.serial_associator,
add_title: '@i18n:objects.hostgroup.add_hosts',
remove_title: '@i18n:objects.hostgroup.remove_hosts'
},
{
$type: 'association',
name: 'member_hostgroup',
associator: IPA.serial_associator,
add_title: '@i18n:objects.hostgroup.add_hostgroups',
remove_title: '@i18n:objects.hostgroup.remove_hostgroups'
},

View File

@ -132,6 +132,14 @@ add:aci: (targetfilter="(|(objectclass=ipaHost)(objectclass=ipaService))")(targe
dn: $SUFFIX
add:aci:(targetattr = "usercertificate")(version 3.0;acl "selfservice:Users can manage their own X.509 certificates";allow (write) userdn = "ldap:///self";)
# Allow member managers to modify members of user groups
dn: cn=groups,cn=accounts,$SUFFIX
add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN";)
# Allow member managers to modify members of host groups
dn: cn=hostgroups,cn=accounts,$SUFFIX
add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN";)
# Hosts can add and delete their own services
dn: cn=services,cn=accounts,$SUFFIX
remove:aci: (target = "ldap:///krbprincipalname=*/($$dn)@$REALM,cn=services,cn=accounts,$SUFFIX")(targetfilter = "(objectClass=ipaKrbPrincipal)")(version 3.0;acl "Hosts can add own services"; allow(add) userdn="ldap:///fqdn=($$dn),cn=computers,cn=accounts,$SUFFIX";)

View File

@ -380,3 +380,11 @@ default: objectClass: top
default: objectClass: nsIndex
default: nsSystemIndex: false
default: nsIndexType: eq
dn: cn=memberManager,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config
default: cn: memberManager
default: objectClass: top
default: objectClass: nsIndex
default: nsSystemIndex: false
default: nsIndexType: eq
default: nsIndexType: pres

View File

@ -20,3 +20,4 @@ add: referint-membership-attr: ipaallowedtarget
add: referint-membership-attr: ipamemberca
add: referint-membership-attr: ipamembercertprofile
add: referint-membership-attr: ipalocation
add: referint-membership-attr: membermanager

View File

@ -564,6 +564,11 @@ class LDAPObject(Object):
'memberofindirect': (
'Indirect Member Of', None, 'not_in_indirect_'
),
'membermanager': (
'Group membership managed by',
'membermanager_',
'not_membermanager_'
),
}
label = _('Entry')
label_singular = _('Entry')

View File

@ -114,6 +114,15 @@ EXAMPLES:
Display information about a named group.
ipa group-show localadmins
Group membership managers are users or groups that can add members to a
group or remove members from a group.
Allow user "test2" to add or remove members from group "localadmins":
ipa group-add-member-manager --users=test2 localadmins
Revoke membership management rights for user "test2" from "localadmins":
ipa group-remove-member-manager --users=test2 localadmins
External group membership is designed to allow users from trusted domains
to be mapped to local POSIX groups in order to actually use IPA resources.
External members should be added to groups that specifically created as
@ -159,6 +168,22 @@ ipaexternalmember_param = Str('ipaexternalmember*',
)
group_output_params = (
Str(
'membermanager_group',
label='Membership managed by groups',
),
Str(
'membermanager_user',
label='Membership managed by users',
),
Str(
'membermanager',
label=_('Failed membermanager'),
),
)
@register()
class group(LDAPObject):
"""
@ -175,10 +200,12 @@ class group(LDAPObject):
default_attributes = [
'cn', 'description', 'gidnumber', 'member', 'memberof',
'memberindirect', 'memberofindirect', 'ipaexternalmember',
'membermanager',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
'member': ['user', 'group', 'service'],
'membermanager': ['user', 'group'],
'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'],
'memberindirect': ['user', 'group', 'service'],
'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule',
@ -194,7 +221,7 @@ class group(LDAPObject):
'businesscategory', 'cn', 'description', 'gidnumber',
'ipaexternalmember', 'ipauniqueid', 'mepmanagedby', 'o',
'objectclass', 'ou', 'owner', 'seealso',
'ipantsecurityidentifier'
'ipantsecurityidentifier', 'membermanager',
},
},
'System: Read Group Membership': {
@ -248,7 +275,7 @@ class group(LDAPObject):
'ipapermright': {'write'},
'ipapermdefaultattr': {
'cn', 'description', 'gidnumber', 'ipauniqueid',
'mepmanagedby', 'objectclass'
'mepmanagedby', 'objectclass', 'membermanager',
},
'replaces': [
'(targetattr = "cn || description || gidnumber || objectclass || mepmanagedby || ipauniqueid")(target = "ldap:///cn=*,cn=groups,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Modify Groups";allow (write) groupdn = "ldap:///cn=Modify Groups,cn=permissions,cn=pbac,$SUFFIX";)',
@ -316,6 +343,7 @@ class group(LDAPObject):
class group_add(LDAPCreate):
__doc__ = _('Create a new group.')
has_output_params = LDAPCreate.has_output_params + group_output_params
msg_summary = _('Added group "%(value)s"')
takes_options = LDAPCreate.takes_options + (
@ -397,6 +425,7 @@ class group_del(LDAPDelete):
class group_mod(LDAPUpdate):
__doc__ = _('Modify a group.')
has_output_params = LDAPUpdate.has_output_params + group_output_params
msg_summary = _('Modified group "%(value)s"')
takes_options = LDAPUpdate.takes_options + (
@ -468,8 +497,9 @@ class group_mod(LDAPUpdate):
class group_find(LDAPSearch):
__doc__ = _('Search for groups.')
member_attributes = ['member', 'memberof']
member_attributes = ['member', 'memberof', 'membermanager']
has_output_params = LDAPSearch.has_output_params + group_output_params
msg_summary = ngettext(
'%(count)d group matched', '%(count)d groups matched', 0
)
@ -538,6 +568,8 @@ class group_find(LDAPSearch):
class group_show(LDAPRetrieve):
__doc__ = _('Display information about a named group.')
has_output_params = LDAPRetrieve.has_output_params + group_output_params
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
assert isinstance(dn, DN)
if ('ipaexternalmember' in entry_attrs and
@ -736,3 +768,21 @@ class group_detach(LDAPQuery):
result=True,
value=pkey_to_value(keys[0], options),
)
@register()
class group_add_member_manager(LDAPAddMember):
__doc__ = _('Add users that can manage members of this group.')
has_output_params = LDAPAddMember.has_output_params + group_output_params
member_attributes = ['membermanager']
@register()
class group_remove_member_manager(LDAPRemoveMember):
__doc__ = _('Remove users that can manage members of this group.')
has_output_params = (
LDAPRemoveMember.has_output_params + group_output_params
)
member_attributes = ['membermanager']

View File

@ -58,6 +58,12 @@ EXAMPLES:
Display a host group:
ipa hostgroup-show baltimore
Add a member manager:
ipa hostgroup-add-member-manager --users=user1 baltimore
Remove a member manager
ipa hostgroup-remove-member-manager --users=user1 baltimore
Delete a hostgroup:
ipa hostgroup-del baltimore
""")
@ -75,6 +81,22 @@ register = Registry()
PROTECTED_HOSTGROUPS = (u'ipaservers',)
hostgroup_output_params = (
Str(
'membermanager_group',
label='Membership managed by groups',
),
Str(
'membermanager_user',
label='Membership managed by users',
),
Str(
'membermanager',
label=_('Failed membermanager'),
),
)
@register()
class hostgroup(LDAPObject):
"""
@ -86,12 +108,14 @@ class hostgroup(LDAPObject):
object_class = ['ipaobject', 'ipahostgroup']
permission_filter_objectclasses = ['ipahostgroup']
search_attributes = ['cn', 'description', 'member', 'memberof']
default_attributes = ['cn', 'description', 'member', 'memberof',
'memberindirect', 'memberofindirect',
default_attributes = [
'cn', 'description', 'member', 'memberof', 'memberindirect',
'memberofindirect', 'membermanager',
]
uuid_attribute = 'ipauniqueid'
attribute_members = {
'member': ['host', 'hostgroup'],
'membermanager': ['user', 'group'],
'memberof': ['hostgroup', 'netgroup', 'hbacrule', 'sudorule'],
'memberindirect': ['host', 'hostgroup'],
'memberofindirect': ['hostgroup', 'hbacrule', 'sudorule'],
@ -103,7 +127,7 @@ class hostgroup(LDAPObject):
'ipapermright': {'read', 'search', 'compare'},
'ipapermdefaultattr': {
'businesscategory', 'cn', 'description', 'ipauniqueid', 'o',
'objectclass', 'ou', 'owner', 'seealso',
'objectclass', 'ou', 'owner', 'seealso', 'membermanager',
},
},
'System: Read Hostgroup Membership': {
@ -135,7 +159,7 @@ class hostgroup(LDAPObject):
},
'System: Modify Hostgroups': {
'ipapermright': {'write'},
'ipapermdefaultattr': {'cn', 'description'},
'ipapermdefaultattr': {'cn', 'description', 'membermanager'},
'replaces': [
'(targetattr = "cn || description")(target = "ldap:///cn=*,cn=hostgroups,cn=accounts,$SUFFIX")(version 3.0; acl "permission:Modify Hostgroups";allow (write) groupdn = "ldap:///cn=Modify Hostgroups,cn=permissions,cn=pbac,$SUFFIX";)',
],
@ -194,6 +218,7 @@ class hostgroup(LDAPObject):
class hostgroup_add(LDAPCreate):
__doc__ = _('Add a new hostgroup.')
has_output_params = LDAPCreate.has_output_params + hostgroup_output_params
msg_summary = _('Added hostgroup "%(value)s"')
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
@ -248,6 +273,7 @@ class hostgroup_del(LDAPDelete):
class hostgroup_mod(LDAPUpdate):
__doc__ = _('Modify a hostgroup.')
has_output_params = LDAPUpdate.has_output_params + hostgroup_output_params
msg_summary = _('Modified hostgroup "%(value)s"')
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
@ -260,7 +286,8 @@ class hostgroup_mod(LDAPUpdate):
class hostgroup_find(LDAPSearch):
__doc__ = _('Search for hostgroups.')
member_attributes = ['member', 'memberof']
member_attributes = ['member', 'memberof', 'membermanager']
has_output_params = LDAPSearch.has_output_params + hostgroup_output_params
msg_summary = ngettext(
'%(count)d hostgroup matched', '%(count)d hostgroups matched', 0
)
@ -277,6 +304,10 @@ class hostgroup_find(LDAPSearch):
class hostgroup_show(LDAPRetrieve):
__doc__ = _('Display information about a hostgroup.')
has_output_params = (
LDAPRetrieve.has_output_params + hostgroup_output_params
)
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(ldap, dn, entry_attrs)
@ -313,3 +344,23 @@ class hostgroup_remove_member(LDAPRemoveMember):
assert isinstance(dn, DN)
self.obj.suppress_netgroup_memberof(ldap, dn, entry_attrs)
return (completed, dn)
@register()
class hostgroup_add_member_manager(LDAPAddMember):
__doc__ = _('Add users that can manage members of this hostgroup.')
has_output_params = (
LDAPAddMember.has_output_params + hostgroup_output_params
)
member_attributes = ['membermanager']
@register()
class hostgroup_remove_member_manager(LDAPRemoveMember):
__doc__ = _('Remove users that can manage members of this hostgroup.')
has_output_params = (
LDAPRemoveMember.has_output_params + hostgroup_output_params
)
member_attributes = ['membermanager']

View File

@ -301,6 +301,7 @@ class i18n_messages(Command):
"managedby": _("${primary_key} is managed by:"),
"member": _("${primary_key} members:"),
"memberof": _("${primary_key} is a member of:"),
"membermanager": _("${primary_key} member managers:"),
},
"facets": {
"details": _("Settings"),
@ -814,6 +815,22 @@ class i18n_messages(Command):
"add_users": _(
"Add users into user group '${primary_key}'"
),
"add_membermanager_group": _(
"Add groups as member managers for user group "
"'${primary_key}'"
),
"remove_membermanager_group": _(
"Remove groups from member managers for user group "
"'${primary_key}'"
),
"add_membermanager_user": _(
"Add users as member managers for user group "
"'${primary_key}'"
),
"remove_membermanager_user": _(
"Remove users from member managers for user group "
"'${primary_key}'"
),
"details": _("Group Settings"),
"external": _("External"),
"groups": _("Groups"),
@ -1026,6 +1043,22 @@ class i18n_messages(Command):
"add_into_sudo": _(
"Add host group '${primary_key}' into sudo rules"
),
"add_membermanager_group": _(
"Add groups as member managers for host group "
"'${primary_key}'"
),
"remove_membermanager_group": _(
"Remove groups from member managers for host group "
"'${primary_key}'"
),
"add_membermanager_user": _(
"Add users as member managers for host group "
"'${primary_key}'"
),
"remove_membermanager_user": _(
"Remove users from member managers for host group "
"'${primary_key}'"
),
"host_group": _("Host Groups"),
"identity": _("Host Group Settings"),
"remove": _("Remove host groups"),