summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipalib/plugins/range.py81
-rw-r--r--tests/test_xmlrpc/test_range_plugin.py151
2 files changed, 225 insertions, 7 deletions
diff --git a/ipalib/plugins/range.py b/ipalib/plugins/range.py
index 95b00b39b..cc0c12753 100644
--- a/ipalib/plugins/range.py
+++ b/ipalib/plugins/range.py
@@ -91,6 +91,58 @@ class range(LDAPObject):
if not options.get('all', False) or options.get('pkey_only', False):
entry_attrs.pop('objectclass', None)
+ def check_ids_in_modified_range(self, old_base, old_size, new_base, new_size):
+ if new_base is None and new_size is None:
+ # nothing to check
+ return
+ if new_base is None:
+ new_base = old_base
+ if new_size is None:
+ new_size = old_size
+ old_interval = (old_base, old_base + old_size - 1)
+ new_interval = (new_base, new_base + new_size - 1)
+ checked_intervals = []
+ low_diff = new_interval[0] - old_interval[0]
+ if low_diff > 0:
+ checked_intervals.append(
+ (old_interval[0], min(old_interval[1], new_interval[0] - 1)))
+ high_diff = old_interval[1] - new_interval[1]
+ if high_diff > 0:
+ checked_intervals.append(
+ (max(old_interval[0], new_interval[1] + 1), old_interval[1]))
+
+ if not checked_intervals:
+ # range is equal or covers the entire old range, nothing to check
+ return
+
+ ldap = self.backend
+ id_filter_base = ["(objectclass=posixAccount)",
+ "(objectclass=posixGroup)",
+ "(objectclass=ipaIDObject)"]
+ id_filter_ids = []
+
+ for id_low, id_high in checked_intervals:
+ id_filter_ids.append("(&(uidNumber>=%(low)d)(uidNumber<=%(high)d))"
+ % dict(low=id_low, high=id_high))
+ id_filter_ids.append("(&(gidNumber>=%(low)d)(gidNumber<=%(high)d))"
+ % dict(low=id_low, high=id_high))
+ id_filter = ldap.combine_filters(
+ [ldap.combine_filters(id_filter_base, "|"),
+ ldap.combine_filters(id_filter_ids, "|")],
+ "&")
+
+ try:
+ (objects, truncated) = ldap.find_entries(filter=id_filter,
+ attrs_list=['uid', 'cn'],
+ base_dn=DN(api.env.container_accounts, api.env.basedn))
+ except errors.NotFound:
+ # no objects in this range found, allow the command
+ pass
+ else:
+ raise errors.ValidationError(name="ipabaseid,ipaidrangesize",
+ error=_('range modification leaving objects with ID out '
+ 'of the defined range is not allowed'))
+
class range_add(LDAPCreate):
__doc__ = _('Add new ID range.')
@@ -121,6 +173,18 @@ class range_del(LDAPDelete):
msg_summary = _('Deleted ID range "%(value)s"')
+ def pre_callback(self, ldap, dn, *keys, **options):
+ try:
+ (old_dn, old_attrs) = ldap.get_entry(dn, ['ipabaseid', 'ipaidrangesize'])
+ except errors.NotFound:
+ self.obj.handle_not_found(*keys)
+
+ old_base_id = int(old_attrs.get('ipabaseid', [0])[0])
+ old_range_size = int(old_attrs.get('ipaidrangesize', [0])[0])
+ self.obj.check_ids_in_modified_range(
+ old_base_id, old_range_size, 0, 0)
+ return dn
+
class range_find(LDAPSearch):
__doc__ = _('Search for ranges.')
@@ -161,6 +225,23 @@ class range_mod(LDAPUpdate):
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
assert isinstance(dn, DN)
attrs_list.append('objectclass')
+
+ try:
+ (old_dn, old_attrs) = ldap.get_entry(dn, ['ipabaseid', 'ipaidrangesize'])
+ except errors.NotFound:
+ self.obj.handle_not_found(*keys)
+
+ old_base_id = int(old_attrs.get('ipabaseid', [0])[0])
+ old_range_size = int(old_attrs.get('ipaidrangesize', [0])[0])
+ new_base_id = entry_attrs.get('ipabaseid')
+ if new_base_id is not None:
+ new_base_id = int(new_base_id)
+ new_range_size = entry_attrs.get('ipaidrangesize')
+ if new_range_size is not None:
+ new_range_size = int(new_range_size)
+ self.obj.check_ids_in_modified_range(old_base_id, old_range_size,
+ new_base_id, new_range_size)
+
return dn
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
diff --git a/tests/test_xmlrpc/test_range_plugin.py b/tests/test_xmlrpc/test_range_plugin.py
index ea4bc2b2d..4b7aa0893 100644
--- a/tests/test_xmlrpc/test_range_plugin.py
+++ b/tests/test_xmlrpc/test_range_plugin.py
@@ -23,21 +23,31 @@ Test the `ipalib/plugins/range.py` module, and XML-RPC in general.
from ipalib import api, errors, _
from tests.util import assert_equal, Fuzzy
-from xmlrpc_test import Declarative
+from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid
+from tests.test_xmlrpc import objectclasses
from ipapython.dn import *
testrange1 = u't-range-1'
+testrange1_base_id = 900000
+testrange1_size = 99999
+
+user1=u'tuser1'
+user1_uid = 900000
+group1=u'group1'
+group1_gid = 900100
class test_range(Declarative):
cleanup_commands = [
('range_del', [testrange1], {}),
+ ('user_del', [user1], {}),
+ ('group_del', [group1], {}),
]
tests = [
dict(
desc='Create range %r' % (testrange1),
command=('range_add', [testrange1],
- dict(ipabaseid=900000, ipaidrangesize=99999,
+ dict(ipabaseid=testrange1_base_id, ipaidrangesize=testrange1_size,
ipabaserid=1000, ipasecondarybaserid=20000)),
expected=dict(
result=dict(
@@ -45,10 +55,10 @@ class test_range(Declarative):
api.env.basedn),
cn=[testrange1],
objectclass=[u'ipaIDrange', u'ipadomainidrange'],
- ipabaseid=[u'900000'],
+ ipabaseid=[unicode(testrange1_base_id)],
ipabaserid=[u'1000'],
ipasecondarybaserid=[u'20000'],
- ipaidrangesize=[u'99999'],
+ ipaidrangesize=[unicode(testrange1_size)],
iparangetype=[u'local domain range'],
),
value=testrange1,
@@ -64,10 +74,10 @@ class test_range(Declarative):
dn=DN(('cn',testrange1),('cn','ranges'),('cn','etc'),
api.env.basedn),
cn=[testrange1],
- ipabaseid=[u'900000'],
+ ipabaseid=[unicode(testrange1_base_id)],
ipabaserid=[u'1000'],
ipasecondarybaserid=[u'20000'],
- ipaidrangesize=[u'99999'],
+ ipaidrangesize=[unicode(testrange1_size)],
iparangetype=[u'local domain range'],
),
value=testrange1,
@@ -77,12 +87,97 @@ class test_range(Declarative):
dict(
+ desc='Create user %r in range %r' % (user1, testrange1),
+ command=(
+ 'user_add', [user1], dict(givenname=u'Test', sn=u'User1',
+ uidnumber=user1_uid)
+ ),
+ expected=dict(
+ value=user1,
+ summary=u'Added user "%s"' % user1,
+ result=dict(
+ gecos=[u'Test User1'],
+ givenname=[u'Test'],
+ homedirectory=[u'/home/tuser1'],
+ krbprincipalname=[u'tuser1@' + api.env.realm],
+ loginshell=[u'/bin/sh'],
+ objectclass=objectclasses.user,
+ sn=[u'User1'],
+ uid=[user1],
+ uidnumber=[unicode(user1_uid)],
+ gidnumber=[unicode(user1_uid)],
+ displayname=[u'Test User1'],
+ cn=[u'Test User1'],
+ initials=[u'TU'],
+ ipauniqueid=[fuzzy_uuid],
+ krbpwdpolicyreference=[DN(('cn','global_policy'),('cn',api.env.realm),
+ ('cn','kerberos'),api.env.basedn)],
+ mepmanagedentry=[DN(('cn',user1),('cn','groups'),('cn','accounts'),
+ api.env.basedn)],
+ memberof_group=[u'ipausers'],
+ has_keytab=False,
+ has_password=False,
+ dn=DN(('uid',user1),('cn','users'),('cn','accounts'), api.env.basedn)
+ ),
+ ),
+ ),
+
+
+ dict(
+ desc='Create group %r in range %r' % (group1, testrange1),
+ command=(
+ 'group_add', [group1], dict(description=u'Test desc 1',
+ gidnumber=group1_gid)
+ ),
+ expected=dict(
+ value=group1,
+ summary=u'Added group "%s"' % group1,
+ result=dict(
+ cn=[group1],
+ description=[u'Test desc 1'],
+ gidnumber=[unicode(group1_gid)],
+ objectclass=objectclasses.group + [u'posixgroup'],
+ ipauniqueid=[fuzzy_uuid],
+ dn=DN(('cn',group1),('cn','groups'),('cn','accounts'), api.env.basedn),
+ ),
+ ),
+ ),
+
+
+ dict(
+ desc='Try to modify range %r to get out bounds object #1' % (testrange1),
+ command=('range_mod', [testrange1], dict(ipabaseid=90001)),
+ expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
+ error=u'range modification leaving objects with ID out of the'
+ u' defined range is not allowed'),
+ ),
+
+
+ dict(
+ desc='Try to modify range %r to get out bounds object #2' % (testrange1),
+ command=('range_mod', [testrange1], dict(ipaidrangesize=100)),
+ expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
+ error=u'range modification leaving objects with ID out of the'
+ u' defined range is not allowed'),
+ ),
+
+
+ dict(
+ desc='Try to modify range %r to get out bounds object #3' % (testrange1),
+ command=('range_mod', [testrange1], dict(ipabaseid=100, ipaidrangesize=100)),
+ expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
+ error=u'range modification leaving objects with ID out of the'
+ u' defined range is not allowed'),
+ ),
+
+
+ dict(
desc='Modify range %r' % (testrange1),
command=('range_mod', [testrange1], dict(ipaidrangesize=90000)),
expected=dict(
result=dict(
cn=[testrange1],
- ipabaseid=[u'900000'],
+ ipabaseid=[unicode(testrange1_base_id)],
ipabaserid=[u'1000'],
ipasecondarybaserid=[u'20000'],
ipaidrangesize=[u'90000'],
@@ -93,4 +188,46 @@ class test_range(Declarative):
),
),
+
+ dict(
+ desc='Try to delete range %r with active IDs inside it' % testrange1,
+ command=('range_del', [testrange1], {}),
+ expected=errors.ValidationError(name='ipabaseid,ipaidrangesize',
+ error=u'range modification leaving objects with ID out of the'
+ u' defined range is not allowed'),
+ ),
+
+
+ dict(
+ desc='Delete user %r' % user1,
+ command=('user_del', [user1], {}),
+ expected=dict(
+ result=dict(failed=u''),
+ value=user1,
+ summary=u'Deleted user "%s"' % user1,
+ ),
+ ),
+
+
+ dict(
+ desc='Delete group %r' % group1,
+ command=('group_del', [group1], {}),
+ expected=dict(
+ result=dict(failed=u''),
+ value=group1,
+ summary=u'Deleted group "%s"' % group1,
+ ),
+ ),
+
+
+ dict(
+ desc='Delete range %r' % testrange1,
+ command=('range_del', [testrange1], {}),
+ expected=dict(
+ result=dict(failed=u''),
+ value=testrange1,
+ summary=u'Deleted ID range "%s"' % testrange1,
+ ),
+ ),
+
]