From 9cc0754b710500519c6f5fd41a0a0237a43e04b0 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Mon, 16 May 2011 17:39:23 -0400 Subject: Add option to limit the attributes allowed in an entry. Kerberos ticket policy can update policy in a user entry. This allowed set/addattr to be used to modify attributes outside of the ticket policy perview, also bypassing all validation/normalization. Likewise the ticket policy was updatable by the user plugin bypassing all validation. Add two new LDAPObject values to control this behavior: limit_object_classes: only attributes in these are allowed disallow_object_classes: attributes in these are disallowed By default both of these lists are empty so are skipped. ticket 744 --- ipalib/plugins/baseldap.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'ipalib/plugins/baseldap.py') diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 43533c8c..f07fb277 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -253,6 +253,8 @@ class LDAPObject(Object): # If an objectclass is possible but not default in an entry. Needed for # collecting attributes for ACI UI. possible_objectclasses = [] + limit_object_classes = [] # Only attributes in these are allowed + disallow_object_classes = [] # Disallow attributes in these search_attributes = [] search_attributes_config = None default_attributes = [] @@ -438,6 +440,36 @@ def _check_empty_attrs(params, entry_attrs): raise errors.RequirementError(name=a) +def _check_limit_object_class(attributes, attrs, allow_only): + """ + If the set of objectclasses is limited enforce that only those + are updated in entry_attrs (plus dn) + + allow_only tells us what mode to check in: + + If True then we enforce that the attributes must be in the list of + allowed. + + If False then those attributes are not allowed. + """ + if len(attributes[0]) == 0 and len(attributes[1]) == 0: + return + limitattrs = deepcopy(attrs) + # Go through the MUST first + for (oid, attr) in attributes[0].iteritems(): + if attr.names[0].lower() in limitattrs: + if not allow_only: + raise errors.ObjectclassViolation(info='attribute "%(attribute)s" not allowed' % dict(attribute=attr.names[0].lower())) + limitattrs.remove(attr.names[0].lower()) + # And now the MAY + for (oid, attr) in attributes[1].iteritems(): + if attr.names[0].lower() in limitattrs: + if not allow_only: + raise errors.ObjectclassViolation(info='attribute "%(attribute)s" not allowed' % dict(attribute=attr.names[0].lower())) + limitattrs.remove(attr.names[0].lower()) + if len(limitattrs) > 0 and allow_only: + raise errors.ObjectclassViolation(info='attribute "%(attribute)s" not allowed' % dict(attribute=limitattrs[0])) + class CallbackInterface(Method): """ Callback registration interface @@ -568,6 +600,8 @@ class LDAPCreate(CallbackInterface, crud.Create): ) _check_single_value_attrs(self.params, entry_attrs) + _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.limit_object_classes), entry_attrs.keys(), allow_only=True) + _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), entry_attrs.keys(), allow_only=False) try: ldap.add_entry(dn, entry_attrs, normalize=self.obj.normalize_dn) @@ -848,6 +882,8 @@ class LDAPUpdate(LDAPQuery, crud.Update): _check_single_value_attrs(self.params, entry_attrs) _check_empty_attrs(self.obj.params, entry_attrs) + _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.limit_object_classes), entry_attrs.keys(), allow_only=True) + _check_limit_object_class(self.api.Backend.ldap2.schema.attribute_types(self.obj.disallow_object_classes), entry_attrs.keys(), allow_only=False) rdnupdate = False try: -- cgit