Bug 41207 - objectClass is not removed from object when extended attribute wants it
objectClass is not removed from object when extended attribute wants it
Status: CLOSED FIXED
Product: UCS
Classification: Unclassified
Component: UDM - Extended Attributes
UCS 4.1
Other Linux
: P5 normal (vote)
: UCS 4.1-2-errata
Assigned To: Florian Best
Philipp Hahn
:
: 15841 41530 (view as bug list)
Depends on:
Blocks: 41580
  Show dependency treegraph
 
Reported: 2016-05-04 17:57 CEST by Nico Stöckigt
Modified: 2016-09-29 20:48 CEST (History)
3 users (show)

See Also:
What kind of report is it?: Bug Report
What type of bug is this?: 5: Major Usability: Impairs usability in key scenarios
Who will be affected by this bug?: 2: Will only affect a few installed domains
How will those affected feel about the bug?: 2: A Pain – users won’t like this once they notice it
User Pain: 0.114
Enterprise Customer affected?: Yes
School Customer affected?:
ISV affected?:
Waiting Support:
Flags outvoted (downgraded) after PO Review:
Ticket number: 2016050321000407
Bug group (optional): External feedback
Max CVSS v3 score:


Attachments
test_object_classes.py (4.77 KB, text/x-python)
2016-05-27 08:59 CEST, Florian Best
Details
patch (3.21 KB, patch)
2016-05-27 10:20 CEST, Florian Best
Details | Diff
Fix multiple-alias-names-per-attribute (4.95 KB, patch)
2016-06-15 13:57 CEST, Philipp Hahn
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Nico Stöckigt univentionstaff 2016-05-04 17:57:07 CEST
On the Tab 'LDAP mapping' within LDAP/Extended Attributes, there is a Checkbox labeled 'Remove object class if the attribute is removed'. When it's checked and the Attribute is cleared/removed, the ObjectClass remains at the Object itself.

Happened here Ticket #2016050321000407
Comment 1 Philipp Hahn univentionstaff 2016-05-06 09:53:53 CEST
This is Bug #15841 again, which was tagged WONTFIX.
FYI: Removing the OC only works for boolean EAs.
Comment 2 Nico Stöckigt univentionstaff 2016-05-06 21:29:49 CEST
(In reply to Philipp Hahn from comment #1)
> This is Bug #15841 again, which was tagged WONTFIX.
> FYI: Removing the OC only works for boolean EAs.

Bug #15841 was filed against UCS 2.4 and had been discarded due to vast changes in newer Versions. However this issue is still present in UCS 4.1 so it had been cloned to fit the adopted conditions.
Comment 3 Stefan Gohmann univentionstaff 2016-05-10 06:21:45 CEST
(In reply to Philipp Hahn from comment #1)
> FYI: Removing the OC only works for boolean EAs.

The expected result would be that it also works for other extended attributes, wouldn't it?
Comment 4 Florian Best univentionstaff 2016-05-10 11:54:26 CEST
Assuming we have the following schema:

objectClass foo MAY (foo1, foo2, foo3, foo4)
objectClass bar MAY (bar1, bar2, bar3, bar4)

And the following UDM module/handler:
property_descriptions = [
   foo1,
   foo2,
   bar1,
   bar3,
]
extended_attributes = [{
   objectClass = bar,
   attribute = bar2,
   removeObjectClassWhenRemovingAttribute = True
}]

And the LDAP might contain an object like:
DN: cn=test
objectClass: foo
objectClass: bar
foo1: 'hi'
foo2: 'blub'
foo3: 'blah'
bar1: 'test'
bar2: 'ext.attr'
bar3: 'asdf'
bar4: 'fouh'

When I now want to --remove=bar2 the code cannot remove the object class 'bar' because the attribute bar1 is still set (which is defined in UDM) and the attribute bar4 is also set (which exists at the LDAP object but is not even mapped in the UDM handler).

To fix this bug I need to parse the Schema (by reading and parsing cn=Subschema). Then I need to get the final modlist (ml) and merge it with all currently set values (self.oldattr). Then I can check if any attribute which is used by that object class is still used. If not, the object class can be removed. Otherwise it must be kept to prevent e.g. "LDAP Error: No such attribute: modify/delete: oxDisplayName: no such attribute".
Comment 5 Stefan Gohmann univentionstaff 2016-05-10 12:15:54 CEST
Let's start with a simple solution without parsing the LDAP schema. If one sets removeObjectClassWhenRemovingAttributs flag and the objectClass is still used, an error message would be OK.
Comment 6 Florian Best univentionstaff 2016-05-10 12:22:44 CEST
(In reply to Stefan Gohmann from comment #5)
> Let's start with a simple solution without parsing the LDAP schema. If one
> sets removeObjectClassWhenRemovingAttributs flag and the objectClass is
> still used, an error message would be OK.
This will break OX as all extended attributes from OX define univentionUDMPropertyDeleteObjectClass=1 which will remove the object class if any of those values is empty (has no default value or OX was installed after the user was created).

It would be this patch:
diff --git a/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py b/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py
index 9a2ed9e..e71b505 100644
--- a/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py
+++ b/management/univention-directory-manager-modules/modules/univention/admin/handlers/__init__.py
@@ -951 +951 @@ def _modify(self, modify_childs=1, ignore_license=0):
-                                               if prop.deleteObjClass == '1' and prop.name not in self.info:
+                                               if prop.deleteObjClass and prop.name not in self.info:
Comment 7 Stefan Gohmann univentionstaff 2016-05-10 14:24:48 CEST
(In reply to Florian Best from comment #6)
> (In reply to Stefan Gohmann from comment #5)
> > Let's start with a simple solution without parsing the LDAP schema. If one
> > sets removeObjectClassWhenRemovingAttributs flag and the objectClass is
> > still used, an error message would be OK.
> This will break OX as all extended attributes from OX define
> univentionUDMPropertyDeleteObjectClass=1 which will remove the object class
> if any of those values is empty (has no default value or OX was installed
> after the user was created).

That's a valid and important point.
Comment 8 Florian Best univentionstaff 2016-05-25 12:38:22 CEST
*** Bug 15841 has been marked as a duplicate of this bug. ***
Comment 9 Florian Best univentionstaff 2016-05-27 08:59:22 CEST
Created attachment 7693 [details]
test_object_classes.py

Added a test case which checks various valid and invalid combinations.
@Philipp, etc.:
Do you come up with more combinations?
Comment 10 Florian Best univentionstaff 2016-05-27 10:20:59 CEST
Created attachment 7694 [details]
patch

First patch. I hope I can further cleanup the patch, the code looks a little bit complex. The udm-users tests pass.
Comment 11 Florian Best univentionstaff 2016-05-27 10:24:27 CEST
In the logfiles I can see that the patch successfully prevents to delete some invalid combinations:
The attribute 'surname' is required in the current object classes.
The attribute 'commonName' is required in the current object classes.
The attribute 'userid' is required in the current object classes.
Comment 12 Florian Best univentionstaff 2016-05-31 13:08:44 CEST
univention-directory-manager-modules (11.0.3-1):
r69589 | Bug #41207: reenable object class removal for extended attributes

univention-directory-manager-modules.yaml:
r69590 | YAML Bug #41207
Comment 13 Philipp Hahn univentionstaff 2016-06-09 15:13:22 CEST
RFC: handlers/__init__.py:806
> schema = ldap.schema.SubSchema(self.lo.lo.lo.read_subschemasubentry_s(self.lo.lo.lo.search_subschemasubentry_s()), 0)

every _modify() will now require two additional *synchronous* requests to the LDAP server to find and fetch the LDAP schema.

$ time ./create-32k-users-in-groups -n 1000
$ time (base=$(ucr get ldap/base);now=$(date +%s);for ((i=0;i<1000;i+=1)); do udm users/user modify --dn "$(printf 'uid=nscd%04x,cn=users,%s' $i "$base")" --set description="$now";done)

# original unpatched version
 real    4m25.033s
 user    2m51.572s
 sys     0m5.172s

 real    1m44.533s
 user    0m30.616s
 sys     0m6.440s


# your patched version
 real    4m20.282s
 user    2m52.492s
 sys     0m5.096s

 real    5m50.531s
 user    0m31.692s
 sys     0m6.780s

Either cache it or limit it to the case when objectClass is modified (you already check that in line 801)
> if set(self.oldattr.get('objectClass', [])) != ocs:
Comment 14 Florian Best univentionstaff 2016-06-09 19:01:37 CEST
Valid point! The schema is now cached in univention.uldap.access() and dropped if the connection re-establishes (as the schema might be updated then).

univention-python (9.0.1-3):
r70051 | Bug #41207: autopep8
r70050 | Bug #41207: code cleanup
r70049 | Bug #41207: add access.get_schema()

univention-python.yaml:
r70052 | YAML Bug #41207

univention-directory-manager-modules (11.0.3-5):
r70048 | Bug #41207: cache schema
Comment 15 Philipp Hahn univentionstaff 2016-06-10 17:15:43 CEST
OK: Performance is okay again
real    4m18.561s
user    2m50.440s
sys     0m4.972s

real    1m45.837s
user    0m30.928s
sys     0m6.400s
Comment 16 Florian Best univentionstaff 2016-06-13 07:24:03 CEST
*** Bug 41530 has been marked as a duplicate of this bug. ***
Comment 17 Philipp Hahn univentionstaff 2016-06-15 13:55:44 CEST
FORK: Bug #21608 2): currently the objectClass associated with an Extended Option can't be removed, because UDM has no logic to parse the "objectClass"es on load and to enable the associated options. That only works if at least one "attribute" of that objectClass is loaded. Thus neither UDM-UMC nor udm-cli show the option as being enabled after loading. Thus the option can't be deselected, thus the objectClass is not removed.

FAIL: LDAP-Schema-handling is incomplete due to multiple-alias-names-per-attribute:
 # ldapsearch -LLLo ldif-wrap=no -x -b cn=Subschema -s base attributeTypes objectClasses | less
 # univention-ldapsearch -LLLb cn=bqwds365ti,cn=groups,dc=phahn,dc=qa objectClass structuralObjectClass
dn: cn=bqwds365ti,cn=groups,dc=phahn,dc=qa
objectClass: posixGroup
objectClass: sambaGroupMapping
objectClass: top
objectClass: univentionGroup
objectClass: univentionFreeAttributes
objectClass: univentionObject
structuralObjectClass: posixGroup

 # ldapsearch -LLLo ldif-wrap=no -x -b cn=Subschema -s base objectClasses | grep posixGroup
objectClasses: ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPassword $ memberUid $ description ) )

 # ldapsearch -LLLo ldif-wrap=no -x -b cn=Subschema -s base attributeTypes | grep --word cn
dn: cn=Subschema
attributeTypes: ( 2.5.4.3 NAME ( 'cn' 'commonName' ) DESC 'RFC4519: common name(s) for which the entity is known by' SUP name )

self.oldattr() only contains 'cn', but not 'commonName'; or vis versa. Patch follows.


FYI: There's a 2nd issue when two EAs are defined using the same OC, but only one using an option. In that case removing the dependent EA also removed the non-dependent EA. This is not nice, but IMHO preferable to only one EA being removed, while the 2nd EA survives and the EO is shown again next time, as one of the EAs still exists.


OK: r70051 r70050 r70049 r70048

OK: dpkg-query -W python-univention-directory-manager # 11.0.3-10.1402.201606141456
OK: dpkg-query -W python-univention # 9.0.1-3.161.201606091857

OK: errata-announce -V --only univention-directory-manager-modules.yaml
OK: univention-directory-manager-modules.yaml

OK: errata-announce -V --only univention-python.yaml
FIXED: univention-python.yaml
 r70191 | Bug #41207 QA: Fix YAML
Comment 18 Philipp Hahn univentionstaff 2016-06-15 13:57:06 CEST
Created attachment 7746 [details]
Fix multiple-alias-names-per-attribute

First check newattr for allowed attributes.
Then check MUST attributes.

Return early from function to simplify nested logic.
Comment 19 Philipp Hahn univentionstaff 2016-06-15 14:08:36 CEST
ucs-test:
 r70218 | Bug #41207 test: Add tests for EA/EO removal
 r70217 | Bug #25240, et al: test: Fix UDM-CLI building
 r70216 | Bug #25240, et al: test Print UDM-CLI command
 r70215 | Bug #25240, et al: ucs-test autopep8
Comment 20 Florian Best univentionstaff 2016-06-15 15:30:13 CEST
Let's release the changes as is - it doesn't break anything comparing to before. The continued fix will be done at Bug #41580.