summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2013-11-13 16:31:58 +0100
committerMartin Kosek <mkosek@redhat.com>2013-12-13 15:08:52 +0100
commitd7ee87cfa1e288fe18dc2dbeb2d691753048f4db (patch)
tree10f41a5a3a82011428f170fe725bafdce77845d7
parent445634d6ac39669cc007871861e19e15ae22c12d (diff)
downloadfreeipa-d7ee87cfa1e288fe18dc2dbeb2d691753048f4db.tar.gz
freeipa-d7ee87cfa1e288fe18dc2dbeb2d691753048f4db.tar.xz
freeipa-d7ee87cfa1e288fe18dc2dbeb2d691753048f4db.zip
Rewrite the Permission plugin
Ticket: https://fedorahosted.org/freeipa/ticket/3566 Design: http://www.freeipa.org/page/V3/Permissions_V2
-rw-r--r--API.txt95
-rw-r--r--VERSION2
-rw-r--r--ipalib/capabilities.py6
-rw-r--r--ipalib/plugins/dns.py2
-rw-r--r--ipalib/plugins/permission.py1038
-rw-r--r--ipatests/test_xmlrpc/objectclasses.py6
-rw-r--r--ipatests/test_xmlrpc/test_dns_plugin.py1
-rw-r--r--ipatests/test_xmlrpc/test_permission_plugin.py1020
-rw-r--r--ipatests/test_xmlrpc/test_privilege_plugin.py26
9 files changed, 1678 insertions, 518 deletions
diff --git a/API.txt b/API.txt
index f1103e0f..842a06bf 100644
--- a/API.txt
+++ b/API.txt
@@ -2228,27 +2228,33 @@ output: Output('result', <type 'bool'>, None)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
command: permission_add
-args: 1,13,3
-arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, required=True)
+args: 1,19,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, required=True)
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Str('attrs', alwaysask=True, attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, query=False, required=False)
-option: Str('filter', alwaysask=True, attribute=True, autofill=False, cli_name='filter', multivalue=False, query=False, required=False)
-option: Str('memberof', alwaysask=True, attribute=True, autofill=False, cli_name='memberof', multivalue=False, query=False, required=False)
+option: Str('attrs', attribute=False, cli_name='attrs', multivalue=True, required=False)
+option: Str('filter', attribute=False, cli_name='filter', multivalue=True, required=False)
+option: Str('ipapermallowedattr', attribute=True, cli_name='attrs', multivalue=True, required=False)
+option: StrEnum('ipapermbindruletype', attribute=True, autofill=True, cli_name='bindtype', default=u'permission', multivalue=False, required=True, values=(u'permission',))
+option: DNOrURL('ipapermlocation', alwaysask=True, attribute=True, autofill=False, cli_name='subtree', default=ipapython.dn.DN('dc=idm,dc=lab,dc=eng,dc=brq,dc=redhat,dc=com'), multivalue=False, query=False, required=False)
+option: StrEnum('ipapermright', attribute=True, cli_name='permissions', multivalue=True, required=False, values=(u'read', u'search', u'compare', u'write', u'add', u'delete', u'all'))
+option: DNParam('ipapermtarget', attribute=True, cli_name='target', multivalue=False, required=False)
+option: Str('ipapermtargetfilter', attribute=True, cli_name='filter', multivalue=False, required=False)
+option: Str('memberof', alwaysask=True, attribute=False, autofill=False, cli_name='memberof', multivalue=False, query=False, required=False)
option: Flag('no_members', autofill=True, default=False, exclude='webui')
-option: Str('permissions', attribute=True, cli_name='permissions', csv=True, multivalue=True, required=True)
+option: Str('permissions', attribute=False, cli_name='permissions', multivalue=True, required=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Str('setattr*', cli_name='setattr', exclude='webui')
-option: Str('subtree', alwaysask=True, attribute=True, autofill=False, cli_name='subtree', multivalue=False, query=False, required=False)
-option: Str('targetgroup', alwaysask=True, attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, query=False, required=False)
-option: StrEnum('type', alwaysask=True, attribute=True, autofill=False, cli_name='type', multivalue=False, query=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: Str('subtree', attribute=False, cli_name='subtree', multivalue=True, required=False)
+option: Str('targetgroup', alwaysask=True, attribute=False, autofill=False, cli_name='targetgroup', multivalue=False, query=False, required=False)
+option: StrEnum('type', alwaysask=True, attribute=False, autofill=False, cli_name='type', multivalue=False, query=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
command: permission_add_member
args: 1,5,3
-arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Str('privilege*', alwaysask=True, cli_name='privileges', csv=True)
@@ -2259,18 +2265,18 @@ output: Output('failed', <type 'dict'>, None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
command: permission_add_noaci
args: 1,5,3
-arg: Str('cn', cli_name='name', multivalue=False, pattern=None, primary_key=True, required=True)
-option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Flag('no_members', autofill=True, default=False, exclude='webui')
-option: StrEnum('permissiontype?', values=(u'SYSTEM',))
-option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
-option: Str('version?', exclude='webui')
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', multivalue=False, required=True)
+option: Str('ipapermissiontype', cli_name='ipapermissiontype', multivalue=True, required=True)
+option: Flag('no_members', autofill=True, cli_name='no_members', default=False, exclude='webui', multivalue=False, required=True)
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', multivalue=False, required=True)
+option: Str('version', cli_name='version', exclude='webui', multivalue=False, required=False)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
command: permission_del
args: 1,3,3
-arg: Str('cn', attribute=True, cli_name='name', multivalue=True, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', multivalue=True, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, query=True, required=True)
option: Flag('continue', autofill=True, cli_name='continue', default=False)
option: Flag('force', autofill=True, default=False)
option: Str('version?', exclude='webui')
@@ -2278,52 +2284,64 @@ output: Output('result', <type 'dict'>, None)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
command: permission_find
-args: 1,15,4
+args: 1,21,4
arg: Str('criteria?', noextrawhitespace=False)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Str('attrs', attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, query=True, required=False)
-option: Str('cn', attribute=True, autofill=False, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=False)
-option: Str('filter', attribute=True, autofill=False, cli_name='filter', multivalue=False, query=True, required=False)
-option: Str('memberof', attribute=True, autofill=False, cli_name='memberof', multivalue=False, query=True, required=False)
+option: Str('attrs', attribute=False, autofill=False, cli_name='attrs', multivalue=True, query=True, required=False)
+option: Str('cn', attribute=True, autofill=False, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, query=True, required=False)
+option: Str('filter', attribute=False, autofill=False, cli_name='filter', multivalue=True, query=True, required=False)
+option: Str('ipapermallowedattr', attribute=True, autofill=False, cli_name='attrs', multivalue=True, query=True, required=False)
+option: StrEnum('ipapermbindruletype', attribute=True, autofill=False, cli_name='bindtype', default=u'permission', multivalue=False, query=True, required=False, values=(u'permission',))
+option: DNOrURL('ipapermlocation', attribute=True, autofill=False, cli_name='subtree', default=ipapython.dn.DN('dc=idm,dc=lab,dc=eng,dc=brq,dc=redhat,dc=com'), multivalue=False, query=True, required=False)
+option: StrEnum('ipapermright', attribute=True, autofill=False, cli_name='permissions', multivalue=True, query=True, required=False, values=(u'read', u'search', u'compare', u'write', u'add', u'delete', u'all'))
+option: DNParam('ipapermtarget', attribute=True, autofill=False, cli_name='target', multivalue=False, query=True, required=False)
+option: Str('ipapermtargetfilter', attribute=True, autofill=False, cli_name='filter', multivalue=False, query=True, required=False)
+option: Str('memberof', attribute=False, autofill=False, cli_name='memberof', multivalue=False, query=True, required=False)
option: Flag('no_members', autofill=True, default=False, exclude='webui')
-option: Str('permissions', attribute=True, autofill=False, cli_name='permissions', csv=True, multivalue=True, query=True, required=False)
+option: Str('permissions', attribute=False, autofill=False, cli_name='permissions', multivalue=True, query=True, required=False)
option: Flag('pkey_only?', autofill=True, default=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
option: Int('sizelimit?', autofill=False, minvalue=0)
-option: Str('subtree', attribute=True, autofill=False, cli_name='subtree', multivalue=False, query=True, required=False)
-option: Str('targetgroup', attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, query=True, required=False)
+option: Str('subtree', attribute=False, autofill=False, cli_name='subtree', multivalue=True, query=True, required=False)
+option: Str('targetgroup', attribute=False, autofill=False, cli_name='targetgroup', multivalue=False, query=True, required=False)
option: Int('timelimit?', autofill=False, minvalue=0)
-option: StrEnum('type', attribute=True, autofill=False, cli_name='type', multivalue=False, query=True, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: StrEnum('type', attribute=False, autofill=False, cli_name='type', multivalue=False, query=True, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
option: Str('version?', exclude='webui')
output: Output('count', <type 'int'>, None)
output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('truncated', <type 'bool'>, None)
command: permission_mod
-args: 1,16,3
-arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=True)
+args: 1,22,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, query=True, required=True)
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
-option: Str('attrs', attribute=True, autofill=False, cli_name='attrs', csv=True, multivalue=True, required=False)
+option: Str('attrs', attribute=False, autofill=False, cli_name='attrs', multivalue=True, required=False)
option: Str('delattr*', cli_name='delattr', exclude='webui')
-option: Str('filter', attribute=True, autofill=False, cli_name='filter', multivalue=False, required=False)
-option: Str('memberof', attribute=True, autofill=False, cli_name='memberof', multivalue=False, required=False)
+option: Str('filter', attribute=False, autofill=False, cli_name='filter', multivalue=True, required=False)
+option: Str('ipapermallowedattr', attribute=True, autofill=False, cli_name='attrs', multivalue=True, required=False)
+option: StrEnum('ipapermbindruletype', attribute=True, autofill=False, cli_name='bindtype', default=u'permission', multivalue=False, required=False, values=(u'permission',))
+option: DNOrURL('ipapermlocation', attribute=True, autofill=False, cli_name='subtree', default=ipapython.dn.DN('dc=idm,dc=lab,dc=eng,dc=brq,dc=redhat,dc=com'), multivalue=False, required=False)
+option: StrEnum('ipapermright', attribute=True, autofill=False, cli_name='permissions', multivalue=True, required=False, values=(u'read', u'search', u'compare', u'write', u'add', u'delete', u'all'))
+option: DNParam('ipapermtarget', attribute=True, autofill=False, cli_name='target', multivalue=False, required=False)
+option: Str('ipapermtargetfilter', attribute=True, autofill=False, cli_name='filter', multivalue=False, required=False)
+option: Str('memberof', attribute=False, autofill=False, cli_name='memberof', multivalue=False, required=False)
option: Flag('no_members', autofill=True, default=False, exclude='webui')
-option: Str('permissions', attribute=True, autofill=False, cli_name='permissions', csv=True, multivalue=True, required=False)
+option: Str('permissions', attribute=False, autofill=False, cli_name='permissions', multivalue=True, required=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
-option: Str('rename', cli_name='rename', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, required=False)
+option: Str('rename', cli_name='rename', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, required=False)
option: Flag('rights', autofill=True, default=False)
option: Str('setattr*', cli_name='setattr', exclude='webui')
-option: Str('subtree', attribute=True, autofill=False, cli_name='subtree', multivalue=False, required=False)
-option: Str('targetgroup', attribute=True, autofill=False, cli_name='targetgroup', multivalue=False, required=False)
-option: StrEnum('type', attribute=True, autofill=False, cli_name='type', multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
+option: Str('subtree', attribute=False, autofill=False, cli_name='subtree', multivalue=True, required=False)
+option: Str('targetgroup', attribute=False, autofill=False, cli_name='targetgroup', multivalue=False, required=False)
+option: StrEnum('type', attribute=False, autofill=False, cli_name='type', multivalue=False, required=False, values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord'))
option: Str('version?', exclude='webui')
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
command: permission_remove_member
args: 1,5,3
-arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Str('privilege*', alwaysask=True, cli_name='privileges', csv=True)
@@ -2334,7 +2352,7 @@ output: Output('failed', <type 'dict'>, None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
command: permission_show
args: 1,5,3
-arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9]+$', primary_key=True, query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, pattern='^[-_ a-zA-Z0-9.]+$', primary_key=True, query=True, required=True)
option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
option: Flag('no_members', autofill=True, default=False, exclude='webui')
option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
@@ -3871,3 +3889,4 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('value', <type 'unicode'>, None)
capability: messages 2.52
capability: optional_uid_params 2.54
+capability: permissions2 2.69
diff --git a/VERSION b/VERSION
index e7d7bc3e..6ead76c6 100644
--- a/VERSION
+++ b/VERSION
@@ -89,4 +89,4 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=70
+IPA_API_VERSION_MINOR=71
diff --git a/ipalib/capabilities.py b/ipalib/capabilities.py
index 6fcc436d..b60a570c 100644
--- a/ipalib/capabilities.py
+++ b/ipalib/capabilities.py
@@ -40,7 +40,11 @@ capabilities = dict(
# a user with UID=999. With the capability, these parameters are optional
# and 999 really means 999.
# https://fedorahosted.org/freeipa/ticket/2886
- optional_uid_params=u'2.54'
+ optional_uid_params=u'2.54',
+
+ # permissions2: Reworked permission system
+ # http://www.freeipa.org/page/V3/Permissions_V2
+ permissions2=u'2.69',
)
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 07523dc7..19811d7f 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -2030,7 +2030,7 @@ class dnszone_add_permission(LDAPQuery):
permission_name = self.obj.permission_name(keys[-1])
permission = api.Command['permission_add_noaci'](permission_name,
- permissiontype=u'SYSTEM'
+ ipapermissiontype=u'SYSTEM'
)['result']
update = {}
diff --git a/ipalib/plugins/permission.py b/ipalib/plugins/permission.py
index 0f1fe667..ff5335d1 100644
--- a/ipalib/plugins/permission.py
+++ b/ipalib/plugins/permission.py
@@ -1,7 +1,7 @@
# Authors:
-# Rob Crittenden <rcritten@redhat.com>
+# Petr Viktorin <pviktori@redhat.com>
#
-# Copyright (C) 2010 Red Hat
+# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
@@ -17,106 +17,130 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from ipalib.plugins.baseldap import *
+import re
+
+from ipalib.plugins import baseldap
+from ipalib import errors
+from ipalib.parameters import Str, StrEnum, DNParam, Flag
from ipalib import api, _, ngettext
-from ipalib import Flag, Str, StrEnum
from ipalib.plugable import Registry
+from ipalib.capabilities import client_has_capability
+from ipalib.aci import ACI
+from ipapython.dn import DN
from ipalib.request import context
-from ipalib import errors
-from ipapython.dn import DN, EditableDN
__doc__ = _("""
Permissions
-
+""" + """
A permission enables fine-grained delegation of rights. A permission is
-a human-readable form of a 389-ds Access Control Rule, or instruction (ACI).
+a human-readable wrapper around a 389-ds Access Control Rule,
+or instruction (ACI).
A permission grants the right to perform a specific task such as adding a
user, modifying a group, etc.
-
+""" + """
A permission may not contain other permissions.
-
-* A permission grants access to read, write, add or delete.
+""" + """
+* A permission grants access to read, write, add, delete, read, search,
+ or compare.
* A privilege combines similar permissions (for example all the permissions
needed to add a user).
* A role grants a set of privileges to users, groups, hosts or hostgroups.
-
+""" + """
A permission is made up of a number of different parts:
1. The name of the permission.
2. The target of the permission.
3. The rights granted by the permission.
-
+""" + """
Rights define what operations are allowed, and may be one or more
of the following:
1. write - write one or more attributes
2. read - read one or more attributes
-3. add - add a new entry to the tree
-4. delete - delete an existing entry
-5. all - all permissions are granted
-
-Read permission is granted for most attributes by default so the read
-permission is not expected to be used very often.
-
+3. search - search on one or more attributes
+4. compare - compare one or more attributes
+5. add - add a new entry to the tree
+6. delete - delete an existing entry
+7. all - all permissions are granted
+""" + """
Note the distinction between attributes and entries. The permissions are
independent, so being able to add a user does not mean that the user will
be editable.
-
+""" + """
There are a number of allowed targets:
-1. type: a type of object (user, group, etc).
-2. memberof: a member of a group or hostgroup
-3. filter: an LDAP filter
-4. subtree: an LDAP filter specifying part of the LDAP DIT. This is a
- super-set of the "type" target.
-5. targetgroup: grant access to modify a specific group (such as granting
- the rights to manage group membership)
-
+1. subtree: a DN; the permission applies to the subtree under this DN
+2. target filter: an LDAP filter
+3. target: DN with possible wildcards, specifies entries permission applies to
+""" + """
+Additionally, there are the following convenience options.
+Setting one of these options will set the corresponding attribute(s).
+1. type: a type of object (user, group, etc); sets subtree and target filter.
+2. memberof: apply to members of a group; sets target filter
+3. targetgroup: grant access to modify a specific group (such as granting
+ the rights to manage group membership); sets target.
+""" + """
EXAMPLES:
-
+""" + """
Add a permission that grants the creation of users:
ipa permission-add --type=user --permissions=add "Add Users"
-
+""" + """
Add a permission that grants the ability to manage group membership:
ipa permission-add --attrs=member --permissions=write --type=group "Manage Group Members"
""")
-ACI_PREFIX=u"permission"
-
register = Registry()
+VALID_OBJECT_TYPES = (u'user', u'group', u'host', u'service', u'hostgroup',
+ u'netgroup', u'dnsrecord',)
+
+_DEPRECATED_OPTION_ALIASES = {
+ 'permissions': 'ipapermright',
+ 'attrs': 'ipapermallowedattr',
+ 'filter': 'ipapermtargetfilter',
+ 'subtree': 'ipapermlocation',
+}
+
+KNOWN_FLAGS = {'SYSTEM', 'V2'}
+
output_params = (
- Str('ipapermissiontype',
- label=_('Permission Type'),
- ),
Str('aci',
label=_('ACI'),
),
)
-def filter_options(options, keys):
- """Return a dict that includes entries from `options` that are in `keys`
- example:
- >>> filtered = filter_options({'a': 1, 'b': 2, 'c': 3}, ['a', 'c'])
- >>> filtered == {'a': 1, 'c': 3}
- True
+def strip_ldap_prefix(uri):
+ prefix = 'ldap:///'
+ if not uri.startswith(prefix):
+ raise ValueError('%r does not start with %r' % (uri, prefix))
+ return uri[len(prefix):]
+
+
+class DNOrURL(DNParam):
+ """DN parameter that allows, and strips, a "ldap:///" prefix on input
+
+ Used for ``subtree`` to maintain backward compatibility.
"""
- return dict((k, options[k]) for k in keys if k in options)
+
+ def _convert_scalar(self, value, index=None):
+ if isinstance(value, basestring) and value.startswith('ldap:///'):
+ value = strip_ldap_prefix(value)
+ return super(DNOrURL, self)._convert_scalar(value, index=index)
@register()
-class permission(LDAPObject):
+class permission(baseldap.LDAPObject):
"""
Permission object.
"""
container_dn = api.env.container_permission
object_name = _('permission')
object_name_plural = _('permissions')
- object_class = ['groupofnames', 'ipapermission']
+ object_class = ['groupofnames', 'ipapermission', 'ipapermissionv2']
default_attributes = ['cn', 'member', 'memberof',
- 'memberindirect', 'ipapermissiontype',
- ]
- aci_attributes = ['aci', 'group', 'permissions', 'attrs', 'type',
- 'filter', 'subtree', 'targetgroup', 'memberof',
+ 'memberindirect', 'ipapermissiontype', 'objectclass',
+ 'ipapermdefaultattr', 'ipapermallowedattr', 'ipapermexcludedattr',
+ 'ipapermbindruletype', 'ipapermlocation', 'ipapermright',
+ 'ipapermtargetfilter', 'ipapermtarget'
]
attribute_members = {
'member': ['privilege'],
@@ -132,396 +156,740 @@ class permission(LDAPObject):
cli_name='name',
label=_('Permission name'),
primary_key=True,
- pattern='^[-_ a-zA-Z0-9]+$',
- pattern_errmsg="May only contain letters, numbers, -, _, and space",
+ pattern='^[-_ a-zA-Z0-9.]+$',
+ pattern_errmsg="May only contain letters, numbers, -, _, ., and space",
),
- Str('permissions+',
+ StrEnum(
+ 'ipapermright*',
cli_name='permissions',
label=_('Permissions'),
- doc=_('Permissions to grant ' \
- '(read, write, add, delete, all)'),
- csv=True,
+ doc=_('Rights to grant '
+ '(read, search, compare, write, add, delete, all)'),
+ values=(u'read', u'search', u'compare',
+ u'write', u'add', u'delete', u'all'),
),
- Str('attrs*',
+ Str('ipapermallowedattr*',
cli_name='attrs',
label=_('Attributes'),
doc=_('Attributes to which the permission applies'),
- csv=True,
- normalizer=lambda value: value.lower(),
- flags=('ask_create'),
),
- StrEnum('type?',
- cli_name='type',
- label=_('Type'),
- doc=_('Type of IPA object (user, group, host, hostgroup, service, netgroup, dns)'),
- values=(u'user', u'group', u'host', u'service', u'hostgroup', u'netgroup', u'dnsrecord',),
- flags=('ask_create'),
- ),
- Str('memberof?',
- cli_name='memberof',
- label=_('Member of group'), # FIXME: Does this label make sense?
- doc=_('Target members of a group'),
- flags=('ask_create'),
+ StrEnum(
+ 'ipapermbindruletype',
+ cli_name='bindtype',
+ label=_('Bind rule type'),
+ doc=_('Bind rule type'),
+ autofill=True,
+ values=(u'permission',),
+ default=u'permission',
),
- Str('filter?',
- cli_name='filter',
- label=_('Filter'),
- doc=_('Legal LDAP filter (e.g. ou=Engineering)'),
- flags=('ask_create'),
- ),
- Str('subtree?',
+ DNOrURL(
+ 'ipapermlocation?',
cli_name='subtree',
label=_('Subtree'),
doc=_('Subtree to apply permissions to'),
- flags=('ask_create'),
+ default=api.env.basedn,
+ flags={'ask_create'},
+ ),
+ Str(
+ 'ipapermtargetfilter?',
+ cli_name='filter',
+ label=_('ACI target filter'),
+ doc=_('ACI target filter'),
+ ),
+
+ DNParam(
+ 'ipapermtarget?',
+ cli_name='target',
+ label=_('ACI target DN'),
+ flags={'no_option'}
+ ),
+
+ Str('memberof?',
+ label=_('Member of group'), # FIXME: Does this label make sense?
+ doc=_('Target members of a group (sets targetfilter)'),
+ flags={'ask_create', 'virtual_attribute'},
),
Str('targetgroup?',
- cli_name='targetgroup',
label=_('Target group'),
- doc=_('User group to apply permissions to'),
- flags=('ask_create'),
+ doc=_('User group to apply permissions to (sets target)'),
+ flags={'ask_create', 'virtual_attribute'},
+ ),
+ StrEnum(
+ 'type?',
+ label=_('Type'),
+ doc=_('Type of IPA object (sets subtree and filter)'),
+ values=VALID_OBJECT_TYPES,
+ flags={'ask_create', 'virtual_attribute'},
),
+ ) + tuple(
+ Str(old_name + '*',
+ doc=_('Deprecated; use %s' % new_name),
+ flags={'no_option', 'virtual_attribute'})
+ for old_name, new_name in _DEPRECATED_OPTION_ALIASES.items()
)
- # Don't allow SYSTEM permissions to be modified or removed
- def check_system(self, ldap, dn, *keys):
+ def reject_system(self, entry):
+ """Raise if permission entry has unknown flags, or is a SYSTEM perm"""
+ flags = entry.get('ipapermissiontype', [])
+ for flag in flags:
+ if flag not in KNOWN_FLAGS:
+ raise errors.ACIError(
+ info=_('Permission with unknown flag %s may not be '
+ 'modified or removed') % flag)
+ if list(flags) == [u'SYSTEM']:
+ raise errors.ACIError(
+ info=_('A SYSTEM permission may not be modified or removed'))
+
+ def postprocess_result(self, entry, options):
+ """Update a permission entry for output (in place)
+
+ :param entry: The entry to update
+ :param options:
+ Command options. Contains keys such as ``raw``, ``all``,
+ ``pkey_only``, ``version``.
+ """
+ if not options.get('raw') and not options.get('pkey_only'):
+ ipapermtargetfilter = entry.single_value.get('ipapermtargetfilter',
+ '')
+ ipapermtarget = entry.single_value.get('ipapermtarget')
+ ipapermlocation = entry.single_value.get('ipapermlocation')
+
+ # memberof
+ match = re.match('^\(memberof=(.*)\)$', ipapermtargetfilter, re.I)
+ if match:
+ dn = DN(match.group(1))
+ if dn[1:] == DN(self.api.Object.group.container_dn,
+ self.api.env.basedn)[:] and dn[0].attr == 'cn':
+ entry.single_value['memberof'] = dn[0].value
+
+ # targetgroup
+ if ipapermtarget:
+ dn = DN(ipapermtarget)
+ if (dn[1:] == DN(self.api.Object.group.container_dn,
+ self.api.env.basedn)[:] and
+ dn[0].attr == 'cn' and dn[0].value != '*'):
+ entry.single_value['targetgroup'] = dn[0].value
+
+ # type
+ if ipapermtarget and ipapermlocation:
+ for objname in VALID_OBJECT_TYPES:
+ obj = self.api.Object[objname]
+ wantdn = DN(obj.container_dn, self.api.env.basedn)
+ if DN(ipapermlocation) == wantdn:
+ targetdn = DN(
+ (obj.rdn_attribute or obj.primary_key.name, '*'),
+ obj.container_dn,
+ self.api.env.basedn)
+ if ipapermtarget == targetdn:
+ entry.single_value['type'] = objname
+ break
+
+ # old output names
+ if not client_has_capability(options['version'], 'permissions2'):
+ for old_name, new_name in _DEPRECATED_OPTION_ALIASES.items():
+ if new_name in entry:
+ entry[old_name] = entry[new_name]
+ del entry[new_name]
+
+ rights = entry.get('attributelevelrights')
+ if rights:
+ rights['memberof'] = rights['ipapermtargetfilter']
+ rights['targetgroup'] = rights['ipapermtarget']
+
+ type_rights = set(rights['ipapermtarget'])
+ type_rights.intersection_update(rights['ipapermlocation'])
+ rights['type'] = ''.join(sorted(type_rights,
+ key=rights['ipapermtarget'].index))
+
+ if not client_has_capability(options['version'], 'permissions2'):
+ for old_name, new_name in _DEPRECATED_OPTION_ALIASES.items():
+ if new_name in entry:
+ rights[old_name] = rights[new_name]
+ del rights[new_name]
+
+ if options.get('raw'):
+ # Retreive the ACI from LDAP to ensure we get the real thing
+ acientry, acistring = self._get_aci_entry_and_string(entry)
+ entry.single_value['aci'] = acistring
+
+ if not client_has_capability(options['version'], 'permissions2'):
+ # Legacy clients expect some attributes as a single value
+ for attr in 'type', 'targetgroup', 'memberof', 'aci':
+ if attr in entry:
+ entry[attr] = entry.single_value[attr]
+ if 'subtree' in entry:
+ # Legacy clients expect subtree as a URL
+ dn = entry.single_value['subtree']
+ entry['subtree'] = u'ldap:///%s' % dn
+ if 'filter' in entry:
+ # Legacy clients expect filter without parentheses
+ new_filter = []
+ for flt in entry['filter']:
+ assert flt[0] == '(' and flt[-1] == ')'
+ new_filter.append(flt[1:-1])
+ entry['filter'] = new_filter
+
+ def make_aci(self, entry):
+ """Make an ACI string from the given permission entry"""
+
+ aci = ACI()
+ name = entry.single_value['cn']
+ aci.name = 'permission:%s' % name
+ ipapermtarget = entry.single_value.get('ipapermtarget')
+ if ipapermtarget:
+ aci.set_target('ldap:///%s' % ipapermtarget)
+ ipapermtargetfilter = entry.single_value.get('ipapermtargetfilter')
+ if ipapermtargetfilter:
+ aci.set_target_filter(ipapermtargetfilter)
+
+ ipapermbindruletype = entry.single_value.get('ipapermbindruletype',
+ 'permission')
+ if ipapermbindruletype == 'permission':
+ dn = DN(('cn', name), self.container_dn, self.api.env.basedn)
+ aci.set_bindrule('groupdn = "ldap:///%s"' % dn)
+ elif ipapermbindruletype == 'all':
+ aci.set_bindrule('userdn = "ldap:///all"')
+ elif ipapermbindruletype == 'anonymous':
+ aci.set_bindrule('userdn = "ldap:///anyone"')
+ else:
+ raise ValueError(ipapermbindruletype)
+ aci.permissions = entry['ipapermright']
+ aci.set_target_attr(entry.get('ipapermallowedattr', []))
+
+ return aci.export_to_string()
+
+ def add_aci(self, permission_entry):
+ """Add the ACI coresponding to the given permission entry"""
+ ldap = self.api.Backend.ldap2
+ acistring = self.make_aci(permission_entry)
+ location = permission_entry.single_value.get('ipapermlocation',
+ self.api.env.basedn)
+
+ self.log.debug('Adding ACI %r to %s' % (acistring, location))
+ entry = ldap.get_entry(location, ['aci'])
+ entry.setdefault('aci', []).append(acistring)
+ ldap.update_entry(entry)
+
+ def remove_aci(self, permission_entry):
+ """Remove the ACI corresponding to the given permission entry"""
+ self._replace_aci(permission_entry)
+
+ def update_aci(self, permission_entry, old_name=None):
+ """Update the ACI corresponding to the given permission entry"""
+ new_acistring = self.make_aci(permission_entry)
+ self._replace_aci(permission_entry, old_name, new_acistring)
+
+ def _replace_aci(self, permission_entry, old_name=None, new_acistring=None):
+ """Replace ACI corresponding to permission_entry
+
+ :param old_name: the old name of the permission, if different from new
+ :param new_acistring: new ACI string; if None the ACI is just deleted
+ """
+ ldap = self.api.Backend.ldap2
+ acientry, acistring = self._get_aci_entry_and_string(
+ permission_entry, old_name, notfound_ok=True)
+
+ # (pylint thinks `acientry` is just a dict, but it's an LDAPEntry)
+ acidn = acientry.dn # pylint: disable=E1103
+
+ if acistring is not None:
+ self.log.debug('Removing ACI %r from %s' % (acistring, acidn))
+ acientry['aci'].remove(acistring)
+ if new_acistring:
+ self.log.debug('Adding ACI %r to %s' % (new_acistring, acidn))
+ acientry['aci'].append(new_acistring)
try:
- (dn, entry_attrs) = ldap.get_entry(dn, ['ipapermissiontype'])
+ ldap.update_entry(acientry)
+ except errors.EmptyModlist:
+ self.log.info('No changes to ACI')
+
+ def _get_aci_entry_and_string(self, permission_entry, name=None,
+ notfound_ok=False):
+ """Get the entry and ACI corresponding to the permission entry
+
+ :param name: The name of the permission, or None for the cn
+ :param notfound_ok:
+ If true, (acientry, None) will be returned on missing ACI, rather
+ than raising exception
+ """
+ ldap = self.api.Backend.ldap2
+ if name is None:
+ name = permission_entry.single_value['cn']
+ location = permission_entry.single_value.get('ipapermlocation',
+ self.api.env.basedn)
+ wanted_aciname = 'permission:%s' % name
+
+ try:
+ acientry = ldap.get_entry(location, ['aci'])
except errors.NotFound:
- self.handle_not_found(*keys)
- if 'ipapermissiontype' in entry_attrs:
- if 'SYSTEM' in entry_attrs['ipapermissiontype']:
- return False
- return True
+ acientry = {}
+ acis = acientry.get('aci', ())
+ for acistring in acis:
+ aci = ACI(acistring)
+ if aci.name == wanted_aciname:
+ return acientry, acistring
+ else:
+ if notfound_ok:
+ return acientry, None
+ raise errors.NotFound(
+ reason=_('The ACI for permission %(name)s was not found '
+ 'in %(dn)s ') % {'name': name, 'dn': location})
+
+ def upgrade_permission(self, entry, target_entry=None,
+ output_only=False):
+ """Upgrade the given permission entry to V2, in-place
+
+ The entry is only upgraded if it is a plain old-style permission,
+ that is, it has no flags set.
+
+ :param target_entry:
+ If given, ``target_entry`` is filled from information taken
+ from the ACI corresponding to ``entry``.
+ If None, ``entry`` itself is filled
+ :param output_only:
+ If true, the flags are not updated to V2.
+ Used for the -find and -show commands.
+ """
+ if entry.get('ipapermissiontype'):
+ # Only convert old-style, non-SYSTEM permissions -- i.e. no flags
+ return
+ base, acistring = self._get_aci_entry_and_string(entry)
+
+ if not target_entry:
+ target_entry = entry
+
+ # The DN of old permissions is always basedn
+ # (pylint thinks `base` is just a dict, but it's an LDAPEntry)
+ assert base.dn == self.api.env.basedn, base # pylint: disable=E1103
+
+ aci = ACI(acistring)
+
+ if 'targetattr' in aci.target:
+ target_entry['ipapermallowedattr'] = (
+ aci.target['targetattr']['expression'])
+ if 'target' in aci.target:
+ target_entry.single_value['ipapermtarget'] = DN(strip_ldap_prefix(
+ aci.target['target']['expression']))
+ if 'targetfilter' in aci.target:
+ target_entry.single_value['ipapermtargetfilter'] = unicode(
+ aci.target['targetfilter']['expression'])
+ if aci.bindrule['expression'] == 'ldap:///all':
+ target_entry.single_value['ipapermbindruletype'] = u'all'
+ elif aci.bindrule['expression'] == 'ldap:///anyone':
+ target_entry.single_value['ipapermbindruletype'] = u'anonymous'
+ else:
+ target_entry.single_value['ipapermbindruletype'] = u'permission'
+ target_entry['ipapermright'] = aci.permissions
+ if 'targetattr' in aci.target:
+ target_entry['ipapermallowedattr'] = [
+ unicode(a) for a in aci.target['targetattr']['expression']]
- def filter_aci_attributes(self, options):
- """Return option dictionary that only includes ACI attributes"""
- return dict((k, v) for k, v in options.items() if
- k in self.aci_attributes)
+ if not output_only:
+ target_entry['ipapermissiontype'] = ['SYSTEM', 'V2']
+ if 'ipapermissionv2' not in entry['objectclass']:
+ target_entry['objectclass'] = list(entry['objectclass']) + [
+ u'ipapermissionv2']
-@register()
-class permission_add(LDAPCreate):
- __doc__ = _('Add a new permission.')
+ target_entry['ipapermlocation'] = [self.api.env.basedn]
- msg_summary = _('Added permission "%(value)s"')
- has_output_params = LDAPCreate.has_output_params + output_params
-
- def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
- assert isinstance(dn, DN)
- # Test the ACI before going any further
- opts = self.obj.filter_aci_attributes(options)
- opts['test'] = True
- opts['permission'] = keys[-1]
- opts['aciprefix'] = ACI_PREFIX
- self.api.Command.aci_add(keys[-1], **opts)
-
- # Clear the aci attributes out of the permission entry
- for o in options:
- try:
- if o not in ['objectclass']:
- del entry_attrs[o]
- except:
- pass
- return dn
+ # Make sure we're not losing *any info* by the upgrade
+ new_acistring = self.make_aci(target_entry)
+ if not ACI(new_acistring).isequal(aci):
+ raise ValueError('Cannot convert ACI, %r != %r' % (new_acistring,
+ acistring))
- def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
- assert isinstance(dn, DN)
- # Now actually add the aci.
- opts = self.obj.filter_aci_attributes(options)
- opts['test'] = False
- opts['permission'] = keys[-1]
- opts['aciprefix'] = ACI_PREFIX
- try:
- result = self.api.Command.aci_add(keys[-1], **opts)['result']
- for attr in self.obj.aci_attributes:
- if attr in result:
- entry_attrs[attr] = result[attr]
- except errors.InvalidSyntax, e:
- # A syntax error slipped past our attempt at validation, clean up
- self.api.Command.permission_del(keys[-1])
- raise e
- except Exception, e:
- # Something bad happened, clean up as much as we can and return
- # that error
+ def preprocess_options(self, options):
+ """Preprocess options (in-place)"""
+
+ if options.get('subtree'):
+ if isinstance(options['subtree'], (list, tuple)):
+ [options['subtree']] = options['subtree']
try:
- self.api.Command.permission_del(keys[-1])
- except Exception, ignore:
- pass
+ options['subtree'] = strip_ldap_prefix(options['subtree'])
+ except ValueError:
+ raise errors.ValidationError(
+ name='subtree',
+ error='does not start with "ldap:///"')
+
+ # Handle old options
+ for old_name, new_name in _DEPRECATED_OPTION_ALIASES.items():
+ if old_name in options:
+ if client_has_capability(options['version'], 'permissions2'):
+ raise errors.ValidationError(
+ name=old_name,
+ error=_('option was renamed; use %s') % new_name)
+ if new_name in options:
+ raise errors.ValidationError(
+ name=old_name,
+ error=(_('Cannot use %(old_name)s with %(new_name)s') %
+ {'old_name': old_name, 'new_name': new_name}))
+ options[new_name] = options[old_name]
+ del options[old_name]
+
+ # memberof
+ if 'memberof' in options:
+ memberof = options.pop('memberof')
+ if memberof:
+ if 'ipapermtargetfilter' in options:
+ raise errors.ValidationError(
+ name='ipapermtargetfilter',
+ error=_('filter and memberof are mutually exclusive'))
+ try:
+ groupdn = self.api.Object.group.get_dn_if_exists(memberof)
+ except errors.NotFound:
+ raise errors.NotFound(
+ reason=_('%s: group not found') % memberof)
+ options['ipapermtargetfilter'] = u'(memberOf=%s)' % groupdn
+ else:
+ if 'ipapermtargetfilter' not in options:
+ options['ipapermtargetfilter'] = None
+
+ # targetgroup
+ if 'targetgroup' in options:
+ targetgroup = options.pop('targetgroup')
+ if targetgroup:
+ if 'ipapermtarget' in options:
+ raise errors.ValidationError(
+ name='ipapermtarget',
+ error=_('target and targetgroup are mutually exclusive'))
+ try:
+ groupdn = self.api.Object.group.get_dn_if_exists(targetgroup)
+ except errors.NotFound:
+ raise errors.NotFound(
+ reason=_('%s: group not found') % targetgroup)
+ options['ipapermtarget'] = groupdn
+ else:
+ if 'ipapermtarget' not in options:
+ options['ipapermtarget'] = None
+
+ # type
+ if 'type' in options:
+ objtype = options.pop('type')
+ if objtype:
+ if 'ipapermlocation' in options:
+ raise errors.ValidationError(
+ name='ipapermlocation',
+ error=_('subtree and type are mutually exclusive'))
+ if 'ipapermtarget' in options:
+ raise errors.ValidationError(
+ name='ipapermtarget',
+ error=_('target and type are mutually exclusive'))
+ obj = self.api.Object[objtype.lower()]
+ container_dn = DN(obj.container_dn, self.api.env.basedn)
+ options['ipapermtarget'] = DN(
+ (obj.rdn_attribute or obj.primary_key.name, '*'),
+ container_dn)
+ options['ipapermlocation'] = container_dn
+ else:
+ if 'ipapermtarget' not in options:
+ options['ipapermtarget'] = None
+ if 'ipapermlocation' not in options:
+ options['ipapermlocation'] = None
+
+ def validate_permission(self, entry):
+ ldap = self.Backend.ldap2
+
+ # Rough filter validation by a search
+ if 'ipapermtargetfilter' in entry:
try:
- self.api.Command.aci_del(keys[-1], aciprefix=ACI_PREFIX)
- except Exception, ignore:
+ ldap.find_entries(
+ filter=entry.single_value['ipapermtargetfilter'],
+ base_dn=self.env.basedn,
+ scope=ldap.SCOPE_BASE,
+ size_limit=1)
+ except errors.NotFound:
pass
- raise e
- return dn
+ except errors.BadSearchFilter:
+ raise errors.ValidationError(
+ name='ipapermtargetfilter',
+ error=_('Bad search filter'))
+
+ # Ensure there's something in the ACI's filter
+ needed_attrs = (
+ 'ipapermtarget', 'ipapermtargetfilter', 'ipapermallowedattr')
+ if not any(entry.single_value.get(a) for a in needed_attrs):
+ raise errors.ValidationError(
+ name='target',
+ error=_('there must be at least one target entry specifier '
+ '(e.g. target, targetfilter, attrs)'))
+
+ # Ensure there's a right
+ if not entry.get('ipapermright'):
+ raise errors.RequirementError(name='ipapermright')
@register()
-class permission_add_noaci(LDAPCreate):
- __doc__ = _('Add a system permission without an ACI')
+class permission_add_noaci(baseldap.LDAPCreate):
+ __doc__ = _('Add a system permission without an ACI (internal command)')
msg_summary = _('Added permission "%(value)s"')
- has_output_params = LDAPCreate.has_output_params + output_params
NO_CLI = True
+ has_output_params = baseldap.LDAPCreate.has_output_params + output_params
takes_options = (
- StrEnum('permissiontype?',
- label=_('Permission type'),
- values=(u'SYSTEM',),
+ Str('ipapermissiontype+',
+ label=_('Permission flags'),
),
)
- def get_args(self):
- # do not validate system permission names
- yield self.obj.primary_key.clone(pattern=None, pattern_errmsg=None)
-
def get_options(self):
+ perm_options = set(o.name for o in self.obj.takes_params)
for option in super(permission_add_noaci, self).get_options():
- # filter out ACI options
- if option.name in self.obj.aci_attributes:
- continue
- yield option
-
- def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
- assert isinstance(dn, DN)
- permission_type = options.get('permissiontype')
- if permission_type:
- entry_attrs['ipapermissiontype'] = [ permission_type ]
+ # From new options, only cn & ipapermissiontype are supported
+ if option.name in ['ipapermissiontype']:
+ yield option.clone()
+ # Other options such as raw, version are supported
+ elif option.name not in perm_options:
+ yield option.clone()
+
+ def pre_callback(self, ldap, dn, entry, attrs_list, *keys, **options):
+ entry['ipapermissiontype'] = list(options['ipapermissiontype'])
+ entry['objectclass'] = [oc for oc in entry['objectclass']
+ if oc.lower() != 'ipapermissionv2']
return dn
@register()
-class permission_del(LDAPDelete):
+class permission_add(baseldap.LDAPCreate):
+ __doc__ = _('Add a new permission.')
+
+ msg_summary = _('Added permission "%(value)s"')
+ has_output_params = baseldap.LDAPCreate.has_output_params + output_params
+
+ # Need to override args_options_2_params so that processed options apply to
+ # the whole command, not just the callbacks
+ def args_options_2_params(self, *args, **options):
+ if self.env.in_server:
+ self.obj.preprocess_options(options)
+
+ return super(permission_add, self).args_options_2_params(
+ *args, **options)
+
+ def pre_callback(self, ldap, dn, entry, attrs_list, *keys, **options):
+ entry['ipapermissiontype'] = ['SYSTEM', 'V2']
+ entry['cn'] = list(keys)
+ if not entry.get('ipapermlocation'):
+ entry.setdefault('ipapermlocation', [api.env.basedn])
+
+ self.obj.validate_permission(entry)
+ return dn
+
+ def post_callback(self, ldap, dn, entry, *keys, **options):
+ self.obj.add_aci(entry)
+ self.obj.postprocess_result(entry, options)
+ return dn
+
+
+@register()
+class permission_del(baseldap.LDAPDelete):
__doc__ = _('Delete a permission.')
msg_summary = _('Deleted permission "%(value)s"')
- takes_options = LDAPDelete.takes_options + (
+ takes_options = baseldap.LDAPDelete.takes_options + (
Flag('force',
label=_('Force'),
- flags=['no_option', 'no_output'],
+ flags={'no_option', 'no_output'},
doc=_('force delete of SYSTEM permissions'),
),
)
def pre_callback(self, ldap, dn, *keys, **options):
- assert isinstance(dn, DN)
- if not options.get('force') and not self.obj.check_system(ldap, dn, *keys):
- raise errors.ACIError(
- info=_('A SYSTEM permission may not be removed'))
- # remove permission even when the underlying ACI is missing
try:
- self.api.Command.aci_del(keys[-1], aciprefix=ACI_PREFIX)
+ entry = ldap.get_entry(dn, attrs_list=self.obj.default_attributes)
except errors.NotFound:
- pass
+ self.obj.handle_not_found(*keys)
+
+ if not options.get('force'):
+ self.obj.reject_system(entry)
+
+ try:
+ self.obj.remove_aci(entry)
+ except errors.NotFound:
+ errors.NotFound('ACI of permission %s was not found' % keys[0])
+
return dn
@register()
-class permission_mod(LDAPUpdate):
+class permission_mod(baseldap.LDAPUpdate):
__doc__ = _('Modify a permission.')
msg_summary = _('Modified permission "%(value)s"')
- has_output_params = LDAPUpdate.has_output_params + output_params
+ has_output_params = baseldap.LDAPUpdate.has_output_params + output_params
- def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
- assert isinstance(dn, DN)
- if not self.obj.check_system(ldap, dn, *keys):
- raise errors.ACIError(
- info=_('A SYSTEM permission may not be modified'))
+ def args_options_2_params(self, *args, **options):
+ if self.env.in_server:
+ self.obj.preprocess_options(options)
+
+ return super(permission_mod, self).args_options_2_params(
+ *args, **options)
+
+ def pre_callback(self, ldap, dn, entry, attrs_list, *keys, **options):
+ if 'rename' in options and not options['rename']:
+ raise errors.ValidationError(name='rename',
+ error='New name can not be empty')
- # check if permission is in LDAP
try:
- (dn, attrs) = ldap.get_entry(dn, attrs_list)
+ attrs_list = self.obj.default_attributes
+ old_entry = ldap.get_entry(dn, attrs_list=attrs_list)
except errors.NotFound:
self.obj.handle_not_found(*keys)
- # when renaming permission, check if the target permission does not
- # exists already. Then, make changes to underlying ACI
- if 'rename' in options:
- if options['rename']:
- try:
- try:
- new_dn = EditableDN(dn)
- new_dn[0]['cn'] # assure the first RDN has cn as it's type
- except (IndexError, KeyError), e:
- raise ValueError("expected dn starting with 'cn=' but got '%s'" % dn)
- new_dn[0].value = options['rename']
- entry = ldap.get_entry(new_dn, attrs_list)
- raise errors.DuplicateEntry()
- except errors.NotFound:
- pass # permission may be renamed, continue
- else:
- raise errors.ValidationError(
- name='rename', error=_('New name can not be empty'))
-
- opts = self.obj.filter_aci_attributes(options)
- setattr(context, 'aciupdate', False)
- # If there are no options left we don't need to do anything to the
- # underlying ACI.
- if len(opts) > 0:
- opts['permission'] = keys[-1]
- opts['aciprefix'] = ACI_PREFIX
- self.api.Command.aci_mod(keys[-1], **opts)
- setattr(context, 'aciupdate', True)
-
- # Clear the aci attributes out of the permission entry
- for o in self.obj.aci_attributes:
- try:
- del entry_attrs[o]
- except:
- pass
+ self.obj.reject_system(old_entry)
+ self.obj.upgrade_permission(old_entry)
- return dn
+ # Since `entry` only contains the attributes we are currently changing,
+ # it cannot be used directly to generate an ACI.
+ # First we need to copy the original data into it.
+ for key, value in old_entry.iteritems():
+ if key not in options and key != 'cn':
+ entry.setdefault(key, value)
- def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
- if call_func.func_name == 'update_entry':
- if isinstance(exc, errors.EmptyModlist):
- aciupdate = getattr(context, 'aciupdate')
- if aciupdate:
- return
- raise exc
+ if not entry.get('ipapermlocation'):
+ entry['ipapermlocation'] = [self.api.env.basedn]
- def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
- assert isinstance(dn, DN)
- # rename the underlying ACI after the change to permission
- cn = keys[-1]
+ self.obj.validate_permission(entry)
- if 'rename' in options:
- self.api.Command.aci_mod(cn,aciprefix=ACI_PREFIX,
- permission=options['rename'])
+ old_location = old_entry.single_value.get('ipapermlocation',
+ self.api.env.basedn)
+ if old_location == options.get('ipapermlocation', old_location):
+ context.permision_moving_aci = False
+ else:
+ context.permision_moving_aci = True
+ try:
+ self.obj.remove_aci(old_entry)
+ except errors.NotFound, e:
+ self.log.error('permission ACI not found: %s' % e)
- self.api.Command.aci_rename(cn, aciprefix=ACI_PREFIX,
- newname=options['rename'])
+ # To pass data to postcallback, we currently need to use the context
+ context.old_entry = old_entry
- cn = options['rename'] # rename finished
+ return dn
- # all common options to permission-mod and show need to be listed here
- common_options = filter_options(options, ['all', 'raw', 'rights'])
- result = self.api.Command.permission_show(cn, **common_options)['result']
+ def post_callback(self, ldap, dn, entry, *keys, **options):
+ old_entry = context.old_entry
- for r in result:
- if not r.startswith('member_'):
- entry_attrs[r] = result[r]
+ if context.permision_moving_aci:
+ self.obj.add_aci(entry)
+ else:
+ self.obj.update_aci(entry, old_entry.single_value['cn'])
+ self.obj.postprocess_result(entry, options)
+ entry['dn'] = entry.dn
return dn
@register()
-class permission_find(LDAPSearch):
+class permission_find(baseldap.LDAPSearch):
__doc__ = _('Search for permissions.')
msg_summary = ngettext(
- '%(count)d permission matched', '%(count)d permissions matched', 0
- )
- has_output_params = LDAPSearch.has_output_params + output_params
+ '%(count)d permission matched', '%(count)d permissions matched', 0)
+ has_output_params = baseldap.LDAPSearch.has_output_params + output_params
- def post_callback(self, ldap, entries, truncated, *args, **options):
+ def args_options_2_params(self, *args, **options):
+ if self.env.in_server:
+ self.obj.preprocess_options(options)
- # There is an option/param overlap: "cn" must be passed as "aciname"
- # to aci-find. Besides that we don't need cn anymore so pop it
- aciname = options.pop('cn', None)
+ return super(permission_find, self).args_options_2_params(
+ *args, **options)
+
+ def post_callback(self, ldap, entries, truncated, *args, **options):
+ attribute_options = [o for o in options
+ if (o in self.options and
+ self.options[o].attribute)]
- pkey_only = options.pop('pkey_only', False)
- if not pkey_only:
+ if not options.get('pkey_only'):
for entry in entries:
- (dn, attrs) = entry
- try:
- common_options = filter_options(options, ['all', 'raw'])
- aci = self.api.Command.aci_show(attrs['cn'][0],
- aciprefix=ACI_PREFIX, **common_options)['result']
-
- # copy information from respective ACI to permission entry
- for attr in self.obj.aci_attributes:
- if attr in aci:
- attrs[attr] = aci[attr]
- except errors.NotFound:
- self.debug('ACI not found for %s' % attrs['cn'][0])
- if truncated:
- # size/time limit met, no need to search acis
- return truncated
+ # Old-style permissions might have matched (e.g. by name)
+ self.obj.upgrade_permission(entry, output_only=True)
- if 'sizelimit' in options:
- max_entries = options['sizelimit']
- else:
- config = ldap.get_ipa_config()[1]
- max_entries = config['ipasearchrecordslimit']
-
- # Now find all the ACIs that match. Once we find them, add any that
- # aren't already in the list along with their permission info.
-
- opts = self.obj.filter_aci_attributes(options)
- if aciname:
- opts['aciname'] = aciname
- opts['aciprefix'] = ACI_PREFIX
- # permission ACI attribute is needed
- aciresults = self.api.Command.aci_find(*args, **opts)
- truncated = truncated or aciresults['truncated']
- results = aciresults['result']
-
- for aci in results:
- found = False
- if 'permission' in aci:
- for entry in entries:
- (dn, attrs) = entry
- if aci['permission'] == attrs['cn'][0]:
- found = True
- break
- if not found:
- common_options = filter_options(options, ['all', 'raw'])
- permission = self.api.Command.permission_show(
- aci['permission'], **common_options)['result']
- dn = permission['dn']
- del permission['dn']
- if pkey_only:
- pk = self.obj.primary_key.name
- new_entry = ldap.make_entry(dn, {pk: permission[pk]})
+ if not truncated:
+ if 'sizelimit' in options:
+ max_entries = options['sizelimit']
+ else:
+ config = ldap.get_ipa_config()[1]
+ max_entries = int(config.single_value['ipasearchrecordslimit'])
+
+ filters = ['(objectclass=ipaPermission)',
+ '(!(ipaPermissionType=V2))']
+ if args:
+ filters.append(ldap.make_filter_from_attr('cn', args[0],
+ exact=False))
+ attrs_list = list(self.obj.default_attributes)
+ attrs_list += list(self.obj.attribute_members)
+ if options.get('all'):
+ attrs_list.append('*')
+ try:
+ legacy_entries = ldap.get_entries(
+ base_dn=DN(self.obj.container_dn, self.api.env.basedn),
+ filter=ldap.combine_filters(filters, rules=ldap.MATCH_ALL),
+ attrs_list=attrs_list)
+ except errors.NotFound:
+ legacy_entries = ()
+ self.log.debug('potential legacy entries: %s', len(legacy_entries))
+ nonlegacy_names = {e.single_value['cn'] for e in entries}
+ for entry in legacy_entries:
+ if entry.single_value['cn'] in nonlegacy_names:
+ continue
+ if len(entries) > max_entries:
+ # We've over the limit, pop the last entry and set
+ # truncated flag
+ # (this is easier to do than checking before adding
+ # the entry to results)
+ entries.pop()
+ truncated = True
+ break
+ self.obj.upgrade_permission(entry, output_only=True)
+ cn = entry.single_value['cn']
+ if any(a.lower() in cn.lower() for a in args if a):
+ entries.append(entry)
+ else:
+ # If all given options match, include the entry
+ # Do a case-insensitive match, on any value if multi-valued
+ for opt in attribute_options:
+ optval = options[opt]
+ if not isinstance(optval, (tuple, list)):
+ optval = [optval]
+ value = entry.get(opt)
+ if not value:
+ break
+ if not all(any(str(ov).lower() in str(v).lower()
+ for v in value) for ov in optval):
+ break
else:
- new_entry = ldap.make_entry(dn, permission)
-
- if (dn, permission) not in entries:
- if len(entries) < max_entries:
- entries.append(new_entry)
- else:
- truncated = True
- break
+ entries.append(entry)
+
+ for entry in entries:
+ if options.get('pkey_only'):
+ for opt_name in entry.keys():
+ if opt_name != self.obj.primary_key.name:
+ del entry[opt_name]
+ else:
+ self.obj.postprocess_result(entry, options)
+
return truncated
@register()
-class permission_show(LDAPRetrieve):
+class permission_show(baseldap.LDAPRetrieve):
__doc__ = _('Display information about a permission.')
+ has_output_params = baseldap.LDAPRetrieve.has_output_params + output_params
- has_output_params = LDAPRetrieve.has_output_params + output_params
- def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
- assert isinstance(dn, DN)
- try:
- common_options = filter_options(options, ['all', 'raw'])
- aci = self.api.Command.aci_show(keys[-1], aciprefix=ACI_PREFIX,
- **common_options)['result']
- for attr in self.obj.aci_attributes:
- if attr in aci:
- entry_attrs[attr] = aci[attr]
- except errors.NotFound:
- self.debug('ACI not found for %s' % entry_attrs['cn'][0])
- if options.get('rights', False) and options.get('all', False):
- # The ACI attributes are just broken-out components of aci so
- # the rights should all match it.
- for attr in self.obj.aci_attributes:
- entry_attrs['attributelevelrights'][attr] = entry_attrs['attributelevelrights']['aci']
+ def post_callback(self, ldap, dn, entry, *keys, **options):
+ self.obj.upgrade_permission(entry, output_only=True)
+ self.obj.postprocess_result(entry, options)
return dn
@register()
-class permission_add_member(LDAPAddMember):
- """
- Add members to a permission.
- """
+class permission_add_member(baseldap.LDAPAddMember):
+ """Add members to a permission."""
NO_CLI = True
@register()
-class permission_remove_member(LDAPRemoveMember):
- """
- Remove members from a permission.
- """
+class permission_remove_member(baseldap.LDAPRemoveMember):
+ """Remove members from a permission."""
NO_CLI = True
diff --git a/ipatests/test_xmlrpc/objectclasses.py b/ipatests/test_xmlrpc/objectclasses.py
index 089ee69a..363e1c7c 100644
--- a/ipatests/test_xmlrpc/objectclasses.py
+++ b/ipatests/test_xmlrpc/objectclasses.py
@@ -77,12 +77,16 @@ role = [
u'top',
]
-permission = [
+system_permission = [
u'groupofnames',
u'ipapermission',
u'top'
]
+permission = system_permission + [
+ u'ipapermissionv2',
+]
+
privilege = [
u'nestedgroup',
u'groupofnames',
diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
index 81e8e4ed..8dbdec6b 100644
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
@@ -1361,6 +1361,7 @@ class test_dns(Declarative):
result={
'dn': dnszone1_permission_dn,
'cn': [dnszone1_permission],
+ 'objectclass': objectclasses.system_permission,
'ipapermissiontype': [u'SYSTEM'],
},
),
diff --git a/ipatests/test_xmlrpc/test_permission_plugin.py b/ipatests/test_xmlrpc/test_permission_plugin.py
index a3913a85..3931c0a8 100644
--- a/ipatests/test_xmlrpc/test_permission_plugin.py
+++ b/ipatests/test_xmlrpc/test_permission_plugin.py
@@ -24,7 +24,7 @@ Test the `ipalib/plugins/permission.py` module.
from ipalib import api, errors
from ipatests.test_xmlrpc import objectclasses
-from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid
+from xmlrpc_test import Declarative
from ipapython.dn import DN
permission1 = u'testperm'
@@ -57,18 +57,22 @@ permission3_attributelevelrights = {
'objectclass': u'rscwo',
'memberof': u'rscwo',
'aci': u'rscwo',
- 'subtree': u'rscwo',
+ 'ipapermlocation': u'rscwo',
'o': u'rscwo',
- 'filter': u'rscwo',
- 'attrs': u'rscwo',
+ 'ipapermallowedattr': u'rscwo',
+ 'ipapermdefaultattr': u'rscwo',
+ 'ipapermexcludedattr': u'rscwo',
'owner': u'rscwo',
- 'group': u'rscwo',
'ou': u'rscwo',
- 'targetgroup': u'rscwo',
- 'type': u'rscwo',
- 'permissions': u'rscwo',
+ 'ipapermright': u'rscwo',
'nsaccountlock': u'rscwo',
'description': u'rscwo',
+ 'ipapermtargetfilter': u'rscwo',
+ 'ipapermbindruletype': u'rscwo',
+ 'ipapermlocation': u'rscwo',
+ 'ipapermtarget': u'rscwo',
+ 'type': u'rscwo',
+ 'targetgroup': u'rscwo',
}
privilege1 = u'testpriv1'
@@ -78,19 +82,18 @@ privilege1_dn = DN(('cn',privilege1),
invalid_permission1 = u'bad;perm'
-class test_permission(Declarative):
+users_dn = DN(api.env.container_user, api.env.basedn)
+groups_dn = DN(api.env.container_group, api.env.basedn)
+
+
+class test_permission_negative(Declarative):
+ """Make sure invalid operations fail"""
cleanup_commands = [
('permission_del', [permission1], {'force': True}),
- ('permission_del', [permission2], {'force': True}),
- ('permission_del', [permission3], {'force': True}),
- ('permission_del', [permission1_renamed], {'force': True}),
- ('permission_del', [permission1_renamed_ucase], {'force': True}),
- ('privilege_del', [privilege1], {}),
]
tests = [
-
dict(
desc='Try to retrieve non-existent %r' % permission1,
command=('permission_show', [permission1], {}),
@@ -101,7 +104,7 @@ class test_permission(Declarative):
dict(
desc='Try to update non-existent %r' % permission1,
- command=('permission_mod', [permission1], dict(permissions=u'all')),
+ command=('permission_mod', [permission1], dict(ipapermright=u'all')),
expected=errors.NotFound(
reason=u'%s: permission not found' % permission1),
),
@@ -114,7 +117,6 @@ class test_permission(Declarative):
reason=u'%s: permission not found' % permission1),
),
-
dict(
desc='Search for non-existent %r' % permission1,
command=('permission_find', [permission1], {}),
@@ -126,13 +128,47 @@ class test_permission(Declarative):
),
),
+ dict(
+ desc='Try creating %r with no ipapermright' % permission1,
+ command=(
+ 'permission_add', [permission1], dict(
+ type=u'user',
+ ipapermallowedattr=[u'sn'],
+ )
+ ),
+ expected=errors.RequirementError(name='ipapermright'),
+ ),
dict(
- desc='Create %r' % permission1,
+ desc='Try creating %r with no target option' % permission1,
command=(
'permission_add', [permission1], dict(
+ ipapermright=u'write',
+ )
+ ),
+ expected=errors.ValidationError(
+ name='target',
+ error='there must be at least one target entry specifier '
+ '(e.g. target, targetfilter, attrs)'),
+ ),
+
+ dict(
+ desc='Try to create invalid %r' % invalid_permission1,
+ command=('permission_add', [invalid_permission1], dict(
type=u'user',
- permissions=u'write',
+ ipapermright=u'write',
+ )),
+ expected=errors.ValidationError(name='name',
+ error='May only contain letters, numbers, -, _, ., and space'),
+ ),
+
+ dict(
+ desc='Create %r so we can try breaking it' % permission1,
+ command=(
+ 'permission_add', [permission1], dict(
+ type=u'user',
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
)
),
expected=dict(
@@ -142,8 +178,104 @@ class test_permission(Declarative):
dn=permission1_dn,
cn=[permission1],
objectclass=objectclasses.permission,
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Try remove ipapermright from %r' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermright=None,
+ )
+ ),
+ expected=errors.RequirementError(name='ipapermright'),
+ ),
+
+ dict(
+ desc='Try to remove type from %r' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermallowedattr=None,
+ type=None,
+ )
+ ),
+ expected=errors.ValidationError(
+ name='target',
+ error='there must be at least one target entry specifier '
+ '(e.g. target, targetfilter, attrs)'),
+ ),
+
+ dict(
+ desc='Try to remove target and memberof from %r' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermallowedattr=None,
+ ipapermtarget=None,
+ )
+ ),
+ expected=errors.ValidationError(
+ name='target',
+ error='there must be at least one target entry specifier '
+ '(e.g. target, targetfilter, attrs)'),
+ ),
+
+ dict(
+ desc='Try to rename %r to invalid invalid %r' % (
+ permission1, invalid_permission1),
+ command=('permission_mod', [permission1], dict(
+ rename=invalid_permission1,
+ )),
+ expected=errors.ValidationError(name='rename',
+ error='May only contain letters, numbers, -, _, ., and space'),
+ ),
+
+ ]
+
+
+class test_permission(Declarative):
+ """Misc. tests for the permission plugin"""
+ cleanup_commands = [
+ ('permission_del', [permission1], {'force': True}),
+ ('permission_del', [permission2], {'force': True}),
+ ('permission_del', [permission3], {'force': True}),
+ ('permission_del', [permission1_renamed], {'force': True}),
+ ('permission_del', [permission1_renamed_ucase], {'force': True}),
+ ('privilege_del', [privilege1], {}),
+ ]
+
+ tests = [
+
+ dict(
+ desc='Create %r' % permission1,
+ command=(
+ 'permission_add', [permission1], dict(
type=u'user',
- permissions=[u'write'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Added permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
),
),
),
@@ -154,10 +286,12 @@ class test_permission(Declarative):
command=(
'permission_add', [permission1], dict(
type=u'user',
- permissions=u'write',
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
),
),
- expected=errors.DuplicateEntry(),
+ expected=errors.DuplicateEntry(
+ message='permission with name "%s" already exists' % permission1),
),
@@ -211,9 +345,15 @@ class test_permission(Declarative):
result={
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
),
),
@@ -221,17 +361,28 @@ class test_permission(Declarative):
dict(
desc='Retrieve %r with --raw' % permission1,
- command=('permission_show', [permission1], {'raw' : True}),
+ command=('permission_show', [permission1], {'raw': True}),
expected=dict(
value=permission1,
summary=None,
result={
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member': [privilege1_dn],
- 'aci': u'(target = "ldap:///%s")(version 3.0;acl "permission:testperm";allow (write) groupdn = "ldap:///%s";)' % \
- (DN(('uid', '*'), ('cn', 'users'), ('cn', 'accounts'), api.env.basedn),
- DN(('cn', 'testperm'), ('cn', 'permissions'), ('cn', 'pbac'), api.env.basedn))
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermright': [u'write'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
+ 'aci': ['(targetattr = "sn")'
+ '(target = "ldap:///%(tdn)s")'
+ '(version 3.0;acl "permission:%(name)s";'
+ 'allow (write) groupdn = "ldap:///%(pdn)s";)' %
+ {'tdn': DN(('uid', '*'), users_dn),
+ 'name': permission1,
+ 'pdn': permission1_dn}],
},
),
),
@@ -248,9 +399,15 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
],
),
@@ -268,9 +425,15 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
],
),
@@ -278,7 +441,7 @@ class test_permission(Declarative):
dict(
- desc='Search for non-existence permission using --name',
+ desc='Search for non-existent permission using --name',
command=('permission_find', [], {'cn': u'notfound'}),
expected=dict(
count=0,
@@ -300,9 +463,15 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
],
),
@@ -311,7 +480,7 @@ class test_permission(Declarative):
dict(
desc='Search for %r with --raw' % permission1,
- command=('permission_find', [permission1], {'raw' : True}),
+ command=('permission_find', [permission1], {'raw': True}),
expected=dict(
count=1,
truncated=False,
@@ -320,10 +489,21 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member': [privilege1_dn],
- 'aci': u'(target = "ldap:///%s")(version 3.0;acl "permission:testperm";allow (write) groupdn = "ldap:///%s";)' % \
- (DN(('uid', '*'), ('cn', 'users'), ('cn', 'accounts'), api.env.basedn),
- DN(('cn', 'testperm'), ('cn', 'permissions'), ('cn', 'pbac'), api.env.basedn)),
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermright': [u'write'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
+ 'aci': ['(targetattr = "sn")'
+ '(target = "ldap:///%(tdn)s")'
+ '(version 3.0;acl "permission:%(name)s";'
+ 'allow (write) groupdn = "ldap:///%(pdn)s";)' %
+ {'tdn': DN(('uid', '*'), users_dn),
+ 'name': permission1,
+ 'pdn': permission1_dn}],
},
],
),
@@ -335,9 +515,10 @@ class test_permission(Declarative):
command=(
'permission_add', [permission2], dict(
type=u'user',
- permissions=u'write',
+ ipapermright=u'write',
setattr=u'owner=cn=test',
addattr=u'owner=cn=test2',
+ ipapermallowedattr=[u'cn'],
)
),
expected=dict(
@@ -347,9 +528,14 @@ class test_permission(Declarative):
dn=permission2_dn,
cn=[permission2],
objectclass=objectclasses.permission,
- type=u'user',
- permissions=[u'write'],
+ type=[u'user'],
+ ipapermright=[u'write'],
owner=[u'cn=test', u'cn=test2'],
+ ipapermallowedattr=[u'cn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
),
),
),
@@ -366,15 +552,27 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
{
'dn': permission2_dn,
'cn': [permission2],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'objectclass': objectclasses.permission,
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'cn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
],
),
@@ -405,7 +603,7 @@ class test_permission(Declarative):
dict(
desc='Search by ACI attribute with --pkey-only',
command=('permission_find', [], {'pkey_only': True,
- 'attrs': [u'krbminpwdlife']}),
+ 'ipapermallowedattr': [u'krbminpwdlife']}),
expected=dict(
count=1,
truncated=False,
@@ -451,9 +649,15 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
],
),
@@ -471,15 +675,27 @@ class test_permission(Declarative):
{
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
},
{
'dn': permission2_dn,
'cn': [permission2],
- 'type': u'user',
- 'permissions': [u'write'],
+ 'objectclass': objectclasses.permission,
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'cn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
],
),
@@ -492,7 +708,7 @@ class test_permission(Declarative):
# to change.
dict(
desc='Search for permissions by attr with a limit of 1 (truncated)',
- command=('permission_find', [], dict(attrs=u'ipaenabledflag',
+ command=('permission_find', [], dict(ipapermallowedattr=u'ipaenabledflag',
sizelimit=1)),
expected=dict(
count=1,
@@ -503,11 +719,14 @@ class test_permission(Declarative):
'dn': DN(('cn', 'Modify HBAC rule'),
api.env.container_permission, api.env.basedn),
'cn': [u'Modify HBAC rule'],
+ 'objectclass': objectclasses.permission,
'member_privilege': [u'HBAC Administrator'],
'memberindirect_role': [u'IT Security Specialist'],
- 'permissions' : [u'write'],
- 'attrs': [u'servicecategory', u'sourcehostcategory', u'cn', u'description', u'ipaenabledflag', u'accesstime', u'usercategory', u'hostcategory', u'accessruletype', u'sourcehost'],
- 'subtree' : u'ldap:///%s' % DN(('ipauniqueid', '*'), ('cn', 'hbac'), api.env.basedn),
+ 'ipapermright' : [u'write'],
+ 'ipapermallowedattr': [u'servicecategory', u'sourcehostcategory', u'cn', u'description', u'ipaenabledflag', u'accesstime', u'usercategory', u'hostcategory', u'accessruletype', u'sourcehost'],
+ 'ipapermtarget': [DN(('ipauniqueid', '*'), ('cn', 'hbac'), api.env.basedn)],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermlocation': [api.env.basedn],
},
],
),
@@ -518,7 +737,7 @@ class test_permission(Declarative):
desc='Update %r' % permission1,
command=(
'permission_mod', [permission1], dict(
- permissions=u'read',
+ ipapermright=u'read',
memberof=u'ipausers',
setattr=u'owner=cn=other-test',
addattr=u'owner=cn=other-test2',
@@ -530,11 +749,19 @@ class test_permission(Declarative):
result=dict(
dn=permission1_dn,
cn=[permission1],
+ objectclass=objectclasses.permission,
member_privilege=[privilege1],
- type=u'user',
- permissions=[u'read'],
- memberof=u'ipausers',
+ type=[u'user'],
+ ipapermright=[u'read'],
+ memberof=[u'ipausers'],
owner=[u'cn=other-test', u'cn=other-test2'],
+ ipapermallowedattr=[u'sn'],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN('cn=ipausers',
+ groups_dn)],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
),
),
),
@@ -549,10 +776,18 @@ class test_permission(Declarative):
result={
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'read'],
- 'memberof': u'ipausers',
+ 'type': [u'user'],
+ 'ipapermright': [u'read'],
+ 'memberof': [u'ipausers'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermtargetfilter': [u'(memberOf=%s)' % DN('cn=ipausers',
+ groups_dn)],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
),
),
@@ -564,7 +799,7 @@ class test_permission(Declarative):
permission2),
command=(
'permission_mod', [permission1], dict(rename=permission2,
- permissions=u'all',)
+ ipapermright=u'all',)
),
expected=errors.DuplicateEntry(),
),
@@ -574,7 +809,7 @@ class test_permission(Declarative):
desc='Try to rename %r to empty name' % (permission1),
command=(
'permission_mod', [permission1], dict(rename=u'',
- permissions=u'all',)
+ ipapermright=u'all',)
),
expected=errors.ValidationError(name='rename',
error=u'New name can not be empty'),
@@ -590,10 +825,18 @@ class test_permission(Declarative):
result={
'dn': permission1_dn,
'cn': [permission1],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'read'],
- 'memberof': u'ipausers',
+ 'type': [u'user'],
+ 'ipapermright': [u'read'],
+ 'memberof': [u'ipausers'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermtargetfilter': [u'(memberOf=%s)' % DN('cn=ipausers',
+ groups_dn)],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
),
),
@@ -604,7 +847,7 @@ class test_permission(Declarative):
permission1_renamed),
command=(
'permission_mod', [permission1], dict(rename=permission1_renamed,
- permissions= u'all',)
+ ipapermright= u'all',)
),
expected=dict(
value=permission1,
@@ -612,10 +855,18 @@ class test_permission(Declarative):
result={
'dn': permission1_renamed_dn,
'cn': [permission1_renamed],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'all'],
- 'memberof': u'ipausers',
+ 'type': [u'user'],
+ 'ipapermright': [u'all'],
+ 'memberof': [u'ipausers'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermtargetfilter': [u'(memberOf=%s)' % DN('cn=ipausers',
+ groups_dn)],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
),
),
@@ -626,7 +877,7 @@ class test_permission(Declarative):
permission1_renamed_ucase),
command=(
'permission_mod', [permission1_renamed], dict(rename=permission1_renamed_ucase,
- permissions= u'write',)
+ ipapermright= u'write',)
),
expected=dict(
value=permission1_renamed,
@@ -634,10 +885,18 @@ class test_permission(Declarative):
result={
'dn': permission1_renamed_ucase_dn,
'cn': [permission1_renamed_ucase],
+ 'objectclass': objectclasses.permission,
'member_privilege': [privilege1],
- 'type': u'user',
- 'permissions': [u'write'],
- 'memberof': u'ipausers',
+ 'type': [u'user'],
+ 'ipapermright': [u'write'],
+ 'memberof': [u'ipausers'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermtargetfilter': [u'(memberOf=%s)' % DN('cn=ipausers',
+ groups_dn)],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
},
),
),
@@ -647,8 +906,7 @@ class test_permission(Declarative):
desc='Change %r to a subtree type' % permission1_renamed_ucase,
command=(
'permission_mod', [permission1_renamed_ucase],
- dict(subtree=u'ldap:///%s' % DN(('cn', '*'), ('cn', 'test'), ('cn', 'accounts'), api.env.basedn),
- type=None)
+ dict(ipapermlocation=users_dn, type=None)
),
expected=dict(
value=permission1_renamed_ucase,
@@ -656,19 +914,47 @@ class test_permission(Declarative):
result=dict(
dn=permission1_renamed_ucase_dn,
cn=[permission1_renamed_ucase],
+ objectclass=objectclasses.permission,
member_privilege=[privilege1],
- subtree=u'ldap:///%s' % DN(('cn', '*'), ('cn', 'test'), ('cn', 'accounts'), api.env.basedn),
- permissions=[u'write'],
- memberof=u'ipausers',
+ ipapermlocation=[users_dn],
+ ipapermright=[u'write'],
+ memberof=[u'ipausers'],
+ ipapermallowedattr=[u'sn'],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN('cn=ipausers',
+ groups_dn)],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
),
),
),
+ dict(
+ desc='Reset --subtree of %r' % permission2,
+ command=(
+ 'permission_mod', [permission2],
+ dict(ipapermlocation=api.env.basedn)
+ ),
+ expected=dict(
+ value=permission2,
+ summary=u'Modified permission "%s"' % permission2,
+ result={
+ 'dn': permission2_dn,
+ 'cn': [permission2],
+ 'objectclass': objectclasses.permission,
+ 'ipapermright': [u'write'],
+ 'ipapermallowedattr': [u'cn'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermtarget': [DN(('uid', '*'), users_dn)],
+ 'ipapermlocation': [api.env.basedn],
+ },
+ ),
+ ),
dict(
desc='Search for %r using --subtree' % permission1,
command=('permission_find', [],
- {'subtree': u'ldap:///%s' % DN(('cn', '*'), ('cn', 'test'), ('cn', 'accounts'), api.env.basedn)}),
+ {'ipapermlocation': u'ldap:///%s' % users_dn}),
expected=dict(
count=1,
truncated=False,
@@ -677,10 +963,17 @@ class test_permission(Declarative):
{
'dn':permission1_renamed_ucase_dn,
'cn':[permission1_renamed_ucase],
+ 'objectclass': objectclasses.permission,
'member_privilege':[privilege1],
- 'subtree':u'ldap:///%s' % DN(('cn', '*'), ('cn', 'test'), ('cn', 'accounts'), api.env.basedn),
- 'permissions':[u'write'],
- 'memberof':u'ipausers',
+ 'ipapermlocation': [users_dn],
+ 'ipapermright':[u'write'],
+ 'memberof':[u'ipausers'],
+ 'ipapermallowedattr': [u'sn'],
+ 'ipapermtargetfilter': [u'(memberOf=%s)' % DN(
+ 'cn=ipausers', groups_dn)],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermissiontype': [u'SYSTEM', u'V2'],
+ 'ipapermlocation': [users_dn],
},
],
),
@@ -689,13 +982,9 @@ class test_permission(Declarative):
dict(
desc='Search using nonexistent --subtree',
- command=('permission_find', [], {'subtree': u'foo'}),
- expected=dict(
- count=0,
- truncated=False,
- summary=u'0 permissions matched',
- result=[],
- ),
+ command=('permission_find', [], {'ipapermlocation': u'foo'}),
+ expected=errors.ConversionError(
+ name='subtree', error='malformed RDN string = "foo"'),
),
@@ -711,11 +1000,17 @@ class test_permission(Declarative):
'dn': DN(('cn','Add user to default group'),
api.env.container_permission, api.env.basedn),
'cn': [u'Add user to default group'],
+ 'objectclass': objectclasses.permission,
'member_privilege': [u'User Administrators'],
- 'attrs': [u'member'],
- 'targetgroup': u'ipausers',
+ 'ipapermallowedattr': [u'member'],
+ 'targetgroup': [u'ipausers'],
'memberindirect_role': [u'User Administrator'],
- 'permissions': [u'write']
+ 'ipapermright': [u'write'],
+ 'ipapermbindruletype': [u'permission'],
+ 'ipapermtarget': [DN(
+ 'cn=ipausers', api.env.container_group,
+ api.env.basedn)],
+ 'ipapermlocation': [api.env.basedn],
}
],
),
@@ -795,7 +1090,8 @@ class test_permission(Declarative):
command=(
'permission_add', [permission1], dict(
memberof=u'nonexisting',
- permissions=u'write',
+ ipapermright=u'write',
+ ipapermallowedattr=[u'cn'],
)
),
expected=errors.NotFound(reason=u'nonexisting: group not found'),
@@ -806,8 +1102,9 @@ class test_permission(Declarative):
command=(
'permission_add', [permission1], dict(
memberof=u'editors',
- permissions=u'write',
+ ipapermright=u'write',
type=u'user',
+ ipapermallowedattr=[u'sn'],
)
),
expected=dict(
@@ -817,9 +1114,16 @@ class test_permission(Declarative):
dn=permission1_dn,
cn=[permission1],
objectclass=objectclasses.permission,
- memberof=u'editors',
- permissions=[u'write'],
- type=u'user',
+ memberof=[u'editors'],
+ ipapermright=[u'write'],
+ type=[u'user'],
+ ipapermallowedattr=[u'sn'],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'editors'),
+ groups_dn)],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
),
),
),
@@ -844,9 +1148,17 @@ class test_permission(Declarative):
result=dict(
dn=permission1_dn,
cn=[permission1],
- memberof=u'admins',
- permissions=[u'write'],
- type=u'user',
+ objectclass=objectclasses.permission,
+ memberof=[u'admins'],
+ ipapermright=[u'write'],
+ type=[u'user'],
+ ipapermallowedattr=[u'sn'],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
),
),
),
@@ -864,8 +1176,14 @@ class test_permission(Declarative):
result=dict(
dn=permission1_dn,
cn=[permission1],
- permissions=[u'write'],
- type=u'user',
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ type=[u'user'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
),
),
),
@@ -887,7 +1205,8 @@ class test_permission(Declarative):
command=(
'permission_add', [permission1], dict(
targetgroup=u'editors',
- permissions=u'write',
+ ipapermright=u'write',
+ ipapermallowedattr=[u'sn'],
)
),
expected=dict(
@@ -897,29 +1216,24 @@ class test_permission(Declarative):
dn=permission1_dn,
cn=[permission1],
objectclass=objectclasses.permission,
- targetgroup=u'editors',
- permissions=[u'write'],
+ targetgroup=[u'editors'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermtarget=[DN(('cn', 'editors'), groups_dn)],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[api.env.basedn],
),
),
),
dict(
- desc='Try to create invalid %r' % invalid_permission1,
- command=('permission_add', [invalid_permission1], dict(
- type=u'user',
- permissions=u'write',
- )),
- expected=errors.ValidationError(name='name',
- error='May only contain letters, numbers, -, _, and space'),
- ),
-
- dict(
desc='Create %r' % permission3,
command=(
'permission_add', [permission3], dict(
type=u'user',
- permissions=u'write',
- attrs=[u'cn']
+ ipapermright=u'write',
+ ipapermallowedattr=[u'cn']
)
),
expected=dict(
@@ -929,9 +1243,13 @@ class test_permission(Declarative):
dn=permission3_dn,
cn=[permission3],
objectclass=objectclasses.permission,
- type=u'user',
- permissions=[u'write'],
- attrs=(u'cn',),
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=(u'cn',),
+ ipapermbindruletype=[u'permission'],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
),
),
),
@@ -946,17 +1264,23 @@ class test_permission(Declarative):
dn=permission3_dn,
cn=[permission3],
objectclass=objectclasses.permission,
- type=u'user',
- attrs=(u'cn',),
- permissions=[u'write'],
- attributelevelrights=permission3_attributelevelrights
+ type=[u'user'],
+ ipapermallowedattr=(u'cn',),
+ ipapermright=[u'write'],
+ attributelevelrights=permission3_attributelevelrights,
+ ipapermbindruletype=[u'permission'],
+ ipapermtarget=[DN(('uid', '*'),users_dn)],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
),
),
),
dict(
- desc='Modify %r with --all -rights' % permission3,
- command=('permission_mod', [permission3], {'all' : True, 'rights': True, 'attrs':[u'cn',u'uid']}),
+ desc='Modify %r with --all --rights' % permission3,
+ command=('permission_mod', [permission3], {
+ 'all': True, 'rights': True,
+ 'ipapermallowedattr': [u'cn', u'uid']}),
expected=dict(
value=permission3,
summary=u'Modified permission "%s"' % permission3,
@@ -964,11 +1288,441 @@ class test_permission(Declarative):
dn=permission3_dn,
cn=[permission3],
objectclass=objectclasses.permission,
- type=u'user',
- attrs=(u'cn',u'uid'),
- permissions=[u'write'],
+ type=[u'user'],
+ ipapermallowedattr=(u'cn',u'uid'),
+ ipapermright=[u'write'],
attributelevelrights=permission3_attributelevelrights,
+ ipapermbindruletype=[u'permission'],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Try to modify %r with invalid targetfilter' % permission1,
+ command=('permission_mod', [permission1],
+ {'ipapermtargetfilter': u"ceci n'est pas un filtre"}),
+ expected=errors.ValidationError(
+ name='ipapermtargetfilter',
+ error='Bad search filter'),
+ ),
+ ]
+
+
+class test_permission_sync_attributes(Declarative):
+ """Test the effects of setting permission attributes"""
+ cleanup_commands = [
+ ('permission_del', [permission1], {'force': True}),
+ ]
+
+ tests = [
+ dict(
+ desc='Create %r' % permission1,
+ command=(
+ 'permission_add', [permission1], dict(
+ ipapermlocation=users_dn,
+ ipapermright=u'write',
+ ipapermallowedattr=u'sn',
+ ipapermtargetfilter=u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn),
+ ipapermtarget=DN(('uid', '*'), users_dn),
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Added permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ memberof=[u'admins'],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Unset location on %r, verify type is gone' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermlocation=None,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ memberof=[u'admins'],
+ ipapermlocation=[api.env.basedn],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Reset location on %r' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermlocation=users_dn,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ memberof=[u'admins'],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Unset target on %r, verify type is gone' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermtarget=None,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ memberof=[u'admins'],
),
),
),
+
+ dict(
+ desc='Unset targetfilter on %r, verify memberof is gone' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermtargetfilter=None,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Set type of %r to group' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ type=u'group',
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ type=[u'group'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[groups_dn],
+ ipapermtarget=[DN(('cn', '*'), groups_dn)],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Set target on %r, verify targetgroup is set' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ ipapermtarget=DN('cn=editors', groups_dn),
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermtarget=[DN('cn=editors', groups_dn)],
+ ipapermlocation=[groups_dn],
+ targetgroup=[u'editors'],
+ ),
+ ),
+ ),
+ ]
+
+
+class test_permission_sync_nice(Declarative):
+ """Test the effects of setting convenience options on permissions"""
+ cleanup_commands = [
+ ('permission_del', [permission1], {'force': True}),
+ ]
+
+ tests = [
+ dict(
+ desc='Create %r' % permission1,
+ command=(
+ 'permission_add', [permission1], dict(
+ type=u'user',
+ ipapermright=u'write',
+ ipapermallowedattr=u'sn',
+ memberof=u'admins',
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Added permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN(('uid', '*'), users_dn)],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ memberof=[u'admins'],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Unset type on %r, verify target & location are gone' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ type=None,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermtargetfilter=[u'(memberOf=%s)' % DN(('cn', 'admins'),
+ groups_dn)],
+ memberof=[u'admins'],
+ ipapermlocation=[api.env.basedn],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Unset memberof on %r, verify targetfilter is gone' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ memberof=None,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[api.env.basedn],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Set type of %r to group' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ type=u'group',
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ type=[u'group'],
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[groups_dn],
+ ipapermtarget=[DN(('cn', '*'), groups_dn)],
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Set targetgroup on %r, verify target is set' % permission1,
+ command=(
+ 'permission_mod', [permission1], dict(
+ targetgroup=u'editors',
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Modified permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.permission,
+ ipapermright=[u'write'],
+ ipapermallowedattr=[u'sn'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermtarget=[DN('cn=editors', groups_dn)],
+ ipapermlocation=[groups_dn],
+ targetgroup=[u'editors'],
+ ),
+ ),
+ ),
+ ]
+
+
+def _make_permission_flag_tests(flags, expected_message):
+ return [
+
+ dict(
+ desc='Create %r with flags %s' % (permission1, flags),
+ command=(
+ 'permission_add_noaci', [permission1], dict(
+ ipapermissiontype=flags,
+ )
+ ),
+ expected=dict(
+ value=permission1,
+ summary=u'Added permission "%s"' % permission1,
+ result=dict(
+ dn=permission1_dn,
+ cn=[permission1],
+ objectclass=objectclasses.system_permission,
+ ipapermissiontype=flags,
+ ),
+ ),
+ ),
+
+ dict(
+ desc='Try to modify %r' % permission1,
+ command=('permission_mod', [permission1], {'type': u'user'}),
+ expected=errors.ACIError(info=expected_message),
+ ),
+
+ dict(
+ desc='Try to delete %r' % permission1,
+ command=('permission_del', [permission1], {}),
+ expected=errors.ACIError(info=expected_message),
+ ),
+
+ dict(
+ desc='Delete %r with --force' % permission1,
+ command=('permission_del', [permission1], {'force': True}),
+ expected=dict(
+ result=dict(failed=u''),
+ value=permission1,
+ summary=u'Deleted permission "%s"' % permission1,
+ ),
+ ),
+ ]
+
+
+class test_permission_flags(Declarative):
+ """Test that permission flags are handled correctly"""
+ cleanup_commands = [
+ ('permission_del', [permission1], {'force': True}),
+ ]
+
+ tests = (
+ _make_permission_flag_tests(
+ [u'SYSTEM'],
+ 'A SYSTEM permission may not be modified or removed') +
+ _make_permission_flag_tests(
+ [u'??'],
+ 'Permission with unknown flag ?? may not be modified or removed') +
+ _make_permission_flag_tests(
+ [u'SYSTEM', u'??'],
+ 'Permission with unknown flag ?? may not be modified or removed'))
+
+
+class test_permission_legacy(Declarative):
+ """Tests for non-upgraded permissions"""
+
+ tests = [
+ dict(
+ desc='Search for all permissions in $SUFFIX',
+ command=('permission_find', [],
+ {'ipapermlocation': api.env.basedn}),
+ expected=dict(
+ count=lambda n: n > 50,
+ truncated=False,
+ summary=lambda s: True,
+ result=lambda s: True,
+ ),
+ ),
]
diff --git a/ipatests/test_xmlrpc/test_privilege_plugin.py b/ipatests/test_xmlrpc/test_privilege_plugin.py
index 741590dd..b76c87c7 100644
--- a/ipatests/test_xmlrpc/test_privilege_plugin.py
+++ b/ipatests/test_xmlrpc/test_privilege_plugin.py
@@ -38,6 +38,8 @@ privilege1 = u'testpriv1'
privilege1_dn = DN(('cn',privilege1),
api.env.container_privilege,api.env.basedn)
+users_dn = DN(api.env.container_user, api.env.basedn)
+
class test_privilege(Declarative):
@@ -89,8 +91,8 @@ class test_privilege(Declarative):
desc='Create %r' % permission1,
command=(
'permission_add', [permission1], dict(
- type=u'user',
- permissions=[u'add', u'delete'],
+ type=u'user',
+ ipapermright=[u'add', u'delete'],
)
),
expected=dict(
@@ -100,8 +102,12 @@ class test_privilege(Declarative):
dn=permission1_dn,
cn=[permission1],
objectclass=objectclasses.permission,
- type=u'user',
- permissions=[u'add', u'delete'],
+ type=[u'user'],
+ ipapermright=[u'add', u'delete'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN('uid=*', users_dn)],
),
),
),
@@ -206,8 +212,8 @@ class test_privilege(Declarative):
desc='Create %r' % permission2,
command=(
'permission_add', [permission2], dict(
- type=u'user',
- permissions=u'write',
+ type=u'user',
+ ipapermright=u'write',
)
),
expected=dict(
@@ -217,8 +223,12 @@ class test_privilege(Declarative):
dn=permission2_dn,
cn=[permission2],
objectclass=objectclasses.permission,
- type=u'user',
- permissions=[u'write'],
+ type=[u'user'],
+ ipapermright=[u'write'],
+ ipapermbindruletype=[u'permission'],
+ ipapermissiontype=[u'SYSTEM', u'V2'],
+ ipapermlocation=[users_dn],
+ ipapermtarget=[DN('uid=*', users_dn)],
),
),
),