summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins/idrange.py
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2012-08-23 14:17:34 +0200
committerMartin Kosek <mkosek@redhat.com>2012-09-07 16:50:35 +0200
commit377e1267b75e75fe0f5c7c557e1a9cce6b9fa160 (patch)
treeb70d6e88ae95d3de5408e6613e57f90c407e3005 /ipalib/plugins/idrange.py
parent1915c2d4dd949edfaefa915daf2250be6cfaeeb3 (diff)
downloadfreeipa-377e1267b75e75fe0f5c7c557e1a9cce6b9fa160.tar.gz
freeipa-377e1267b75e75fe0f5c7c557e1a9cce6b9fa160.tar.xz
freeipa-377e1267b75e75fe0f5c7c557e1a9cce6b9fa160.zip
Rename range CLI to idrange
Diffstat (limited to 'ipalib/plugins/idrange.py')
-rw-r--r--ipalib/plugins/idrange.py257
1 files changed, 257 insertions, 0 deletions
diff --git a/ipalib/plugins/idrange.py b/ipalib/plugins/idrange.py
new file mode 100644
index 000000000..23c8e0c34
--- /dev/null
+++ b/ipalib/plugins/idrange.py
@@ -0,0 +1,257 @@
+# Authors:
+# Sumit Bose <sbose@redhat.com>
+#
+# Copyright (C) 2012 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# 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 *
+from ipalib import api, Str, Password, DefaultFrom, _, ngettext, Object
+from ipalib.parameters import Enum
+from ipalib import Command
+from ipalib import errors
+from ipapython import ipautil
+from ipalib import util
+from ipapython.dn import DN
+
+
+__doc__ = _("""
+Manage ID ranges
+""")
+
+class idrange(LDAPObject):
+ """
+ Range object.
+ """
+
+ range_type = ('domain', 'ad', 'ipa')
+ container_dn = api.env.container_ranges
+ object_name = ('range')
+ object_name_plural = ('ranges')
+ object_class = ['ipaIDrange']
+ possible_objectclasses = ['ipadomainidrange', 'ipatrustedaddomainrange']
+ default_attributes = ['cn', 'ipabaseid', 'ipaidrangesize', 'ipabaserid',
+ 'ipasecondarybaserid', 'ipanttrusteddomainsid',
+ 'iparangetype']
+
+ label = _('ID Ranges')
+ label_singular = _('ID Range')
+
+ takes_params = (
+ Str('cn',
+ cli_name='name',
+ label=_('Range name'),
+ primary_key=True,
+ ),
+ Int('ipabaseid',
+ cli_name='base_id',
+ label=_("First Posix ID of the range"),
+ ),
+ Int('ipaidrangesize',
+ cli_name='range_size',
+ label=_("Number of IDs in the range"),
+ ),
+ Int('ipabaserid',
+ cli_name='rid_base',
+ label=_('First RID of the corresponding RID range'),
+ ),
+ Int('ipasecondarybaserid?',
+ cli_name='secondary_rid_base',
+ label=_('First RID of the secondary RID range'),
+ ),
+ Str('ipanttrusteddomainsid?',
+ cli_name='dom_sid',
+ label=_('Domain SID of the trusted domain'),
+ ),
+ Str('iparangetype?',
+ label=_('Range type'),
+ flags=['no_option'],
+ )
+ )
+
+ def handle_iparangetype(self, entry_attrs, options, keep_objectclass=False):
+ if not options.get('pkey_only', False):
+ if 'ipatrustedaddomainrange' in entry_attrs.get('objectclass', []):
+ entry_attrs['iparangetype'] = [unicode(_('Active Directory domain range'))]
+ else:
+ entry_attrs['iparangetype'] = [unicode(_(u'local domain range'))]
+ if not keep_objectclass:
+ 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 idrange_add(LDAPCreate):
+ __doc__ = _('Add new ID range.')
+
+ msg_summary = _('Added ID range "%(value)s"')
+
+ def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
+ if ('ipanttrusteddomainsid' not in options and
+ 'ipasecondarybaserid' not in options):
+ raise errors.ValidationError(name=_('Range setup'),
+ error=_('Ranges for local domain ' \
+ 'must have a secondary RID base'))
+
+ if 'ipanttrusteddomainsid' in options:
+ entry_attrs['objectclass'].append('ipatrustedaddomainrange')
+ else:
+ entry_attrs['objectclass'].append('ipadomainidrange')
+
+ return dn
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
+ self.obj.handle_iparangetype(entry_attrs, options, keep_objectclass=True)
+ return dn
+
+class idrange_del(LDAPDelete):
+ __doc__ = _('Delete an ID range.')
+
+ 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 idrange_find(LDAPSearch):
+ __doc__ = _('Search for ranges.')
+
+ msg_summary = ngettext(
+ '%(count)d range matched', '%(count)d ranges matched', 0
+ )
+
+ # Since all range types are stored within separate containers under
+ # 'cn=ranges,cn=etc' search can be done on a one-level scope
+ def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+ assert isinstance(base_dn, DN)
+ attrs_list.append('objectclass')
+ return (filters, base_dn, ldap.SCOPE_ONELEVEL)
+
+ def post_callback(self, ldap, entries, truncated, *args, **options):
+ for dn,entry in entries:
+ self.obj.handle_iparangetype(entry, options)
+ return truncated
+
+class idrange_show(LDAPRetrieve):
+ __doc__ = _('Display information about a range.')
+
+ def pre_callback(self, ldap, dn, attrs_list, *keys, **options):
+ assert isinstance(dn, DN)
+ attrs_list.append('objectclass')
+ return dn
+
+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ assert isinstance(dn, DN)
+ self.obj.handle_iparangetype(entry_attrs, options)
+ return dn
+
+class idrange_mod(LDAPUpdate):
+ __doc__ = _('Modify ID range.')
+
+ msg_summary = _('Modified ID range "%(value)s"')
+
+ 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):
+ assert isinstance(dn, DN)
+ self.obj.handle_iparangetype(entry_attrs, options)
+ return dn
+
+api.register(idrange)
+api.register(idrange_add)
+api.register(idrange_mod)
+api.register(idrange_del)
+api.register(idrange_find)
+api.register(idrange_show)