On Tue, 21 Oct 2014, Alexander Bokovoy wrote:
On Tue, 21 Oct 2014, Martin Kosek wrote:
On 10/21/2014 08:41 AM, Alexander Bokovoy wrote:
On Tue, 21 Oct 2014, Martin Kosek wrote:
On 10/20/2014 08:25 PM, Alexander Bokovoy wrote:

This patch is for ipa-4-1 branch to enable uniqueness plugin for uid
attribute for entries with objectclass posixAccount.

We don't have uid uniqueness enforced in FreeIPA < 4.1 yet but for
posixAccounts it worked due to our design of a flat tree: as uid attribute is
part of the DN, renaming user entries
enforces uniqueness as MODRDN will fail if entry with the same uid
already exists.

However, it is not enough for ID views -- we should be able to allow
ID view overrides for the same uid across multiple views and we should
be able to protect uid uniqueness more generally too.

Implementation is done via update plugin that checks for existing uid
uniqueness plugin and if it is missing, it will be added. If plugin
exists, its configuration will be updated.

I haven't added update specific to git master where staging subtree is
added but I'll do that after FreeIPA 4.1 release as in 4.1 we don't yet
have the staging subtree. Currently master has broken setup for uid
uniqueness plugin that doesn't actually work anyway so it will be easier
to add upgrade over properly configured entry.


Hi Alexander,

Thanks for the patch! However, I am personally not very confident with merging
it right before 4.1 release, I thought it will be a simple update definition
while this is a complex upgrade script which needs to be properly tested.

I would rather wait for 4.1.x, especially given it does not block any 4.1 major
feature in any way.
I disagree on it for multiple reasons and one of them is that 'a simple
update definition' is not right here. Attribute uniqueness plugin
supports three different types of setting its own arguments. These types
aren't mixable, you have to do switch from one to another. That's why
update plugin is the correct approach here.

The update plugin I've wrote is very simple by itself.

Ok, reviewing the patch now. I found 2 issues:

1) It is missing in plugins' Makefile.am so it won't be built properly

2) Instead of
+        if len(entries) == 0:
+        if entries:
You mean 'if not entries:'? Will fix.

3) I think we should force "uid uniqueness" plugin creation in all cases so
that we can expect it is there in future and for example do simple updates for 
It does enforce it creating already. The only case that is not handled
right now is the case of already existing multiple uid uniqueness
plugin instances which I explicitly want to handle in git master and
then merge back to 4.1 as a backport -- we only have any chance to
encounter this case with current git master.

So if existing active UID uniqueness is there, should we just remove uid from
it's list of watched attributes, give warning and add our own controlled plugin?
No. There could be multiple uid uniqueness plugin instances, looking for
different subtrees, potentially with different top level object classes
and different object class filtering.

I am just trying to get rid of clashes with custom user configuration.
I understand that, I'll handle it in git master version. Right now we
didn't set up any of uid uniqueness and chances touching any custom
configuration like that are low, looking into my experience with
existing deployments.

4) Because of 3), when I enabled "attribute uniqueness" plugin before upgrade,
it did a strange franken-update and now I have an update plugin with DN
"cn=attribute uniqueness,cn=plugins,cn=config" but "cn: uid uniqueness: inside.
Good catch. I'll fix this part.

(BTW these concerns is where my simplicity expectations came from ;-)
Note that now you realize the whole deal is not that simple as you
thought. ;)

New patch attached.
Missed Makefile.am part.

/ Alexander Bokovoy
From 7951e57ca52d38497b0a87610685f173ebb558a3 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
Date: Mon, 20 Oct 2014 21:01:33 +0300
Subject: [PATCH] updater: enable uid uniqueness plugin for posixAccounts

 ipaserver/install/plugins/Makefile.am          |   1 +
 ipaserver/install/plugins/update_uniqueness.py | 115 +++++++++++++++++++++++++
 2 files changed, 116 insertions(+)
 create mode 100644 ipaserver/install/plugins/update_uniqueness.py

diff --git a/ipaserver/install/plugins/Makefile.am 
index 7cf0495..635877d 100644
--- a/ipaserver/install/plugins/Makefile.am
+++ b/ipaserver/install/plugins/Makefile.am
@@ -12,6 +12,7 @@ app_PYTHON =                  \
        update_anonymous_aci.py \
        update_pacs.py          \
        ca_renewal_master.py    \
+       update_uniqueness.py    \
 EXTRA_DIST =                   \
diff --git a/ipaserver/install/plugins/update_uniqueness.py 
new file mode 100644
index 0000000..a1b4638
--- /dev/null
+++ b/ipaserver/install/plugins/update_uniqueness.py
@@ -0,0 +1,115 @@
+# Authors:
+#   Alexander Bokovoy <aboko...@redhat.com>
+# Copyright (C) 2014  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
+# 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 ipaserver.install.plugins import MIDDLE
+from ipaserver.install.plugins.baseupdate import PostUpdate
+from ipalib import api, errors
+from ipapython.dn import DN
+from ipapython.ipa_log_manager import *
+class update_uid_uniqueness(PostUpdate):
+    """
+    Create plugin configuration to ensure uid uniqueness
+    """
+    order = MIDDLE
+    uid_uniqueness_dn = DN(('cn', 'uid uniqueness'), ('cn', 'plugins'), ('cn', 
+    uid_uniqueness_template = {
+     'objectClass'                   : ["top", "nsSlapdPlugin", 
+     'cn'                            : 'uid uniqueness',
+     'nsslapd-pluginPath'            : 'libattr-unique-plugin',
+     'nsslapd-pluginInitfunc'        : 'NSUniqueAttr_Init',
+     'nsslapd-pluginType'            : 'betxnpreoperation',
+     'nsslapd-pluginEnabled'         : 'on',
+     'uniqueness-attribute-name'     : 'uid',
+     'uniqueness-subtrees'           : 'dc=example,dc=com',
+     'uniqueness-across-all-subtrees': 'off',
+     'uniqueness-subtree-entries-oc' : 'posixAccount',
+     'nsslapd-plugin-depends-on-type': 'database',
+     'nsslapd-pluginId'              : 'none',
+     'nsslapd-pluginVersion'         : 'none',
+     'nsslapd-pluginVendor'          : 'none',
+     'nsslapd-pluginDescription'     : 'none',
+    }
+    def execute(self, **options):
+        ldap = self.obj.backend
+        config_dn = DN(('cn','config'))
+        search_filter = ("(&(objectclass=nsslapdplugin)"
+                           "(nsslapd-pluginpath=libattr-unique-plugin)"
+                           "(nsslapd-pluginInitfunc=NSUniqueAttr_Init)"
+                           "(!(nsslapd-pluginenabled=off))"
+        root_logger.debug("update_uid_uniqueness: search for existing uid 
uniqueness "
+                          "configuration")
+        try:
+            (entries, truncated) = ldap.find_entries(search_filter, ['*'], 
+                                                     time_limit=0, 
+        except errors.NotFound:
+            # add entry
+            entries = []
+        except errors.ExecutionError, e:
+            root_logger.error("update_uid_uniqueness: cannot retrieve "
+                              "list of uniqueness plugin instances: %s", e)
+            return (False, False, [])
+        if len(entries) > 1:
+            root_logger.error("update_uid_uniqueness: found more than one uid "
+                              "uniqueness plugin definition: %s", [str(x.dn) 
for x in entries])
+            return (False, False, [])
+        error = False
+        if not entries:
+            root_logger.debug("update_uid_uniqueness: adding new uid 
uniqueness "
+                              "plugin definition")
+            uid_uniqueness_plugin_attrs = dict(self.uid_uniqueness_template)
+            uid_uniqueness_plugin_attrs['uniqueness-subtrees'] = api.env.basedn
+            uid_uniqueness_plugin = ldap.make_entry(self.uid_uniqueness_dn, 
+            try:
+                ldap.add_entry(uid_uniqueness_plugin)
+            except errors.ExecutionError, e:
+                root_logger.debug("update_uid_uniqueness: cannot "
+                                  "create uid uniqueness plugin entry: %s", e)
+                error = True
+        else:
+            root_logger.debug("update_uid_uniqueness: updating existing uid 
uniqueness "
+                              "plugin definition")
+            uid_uniqueness_plugin_attrs = dict(self.uid_uniqueness_template)
+            uid_uniqueness_plugin_attrs['uniqueness-subtrees'] = api.env.basedn
+            uid_uniqueness_plugin_attrs['cn'] = entries[0]['cn']
+            uid_uniqueness_plugin = ldap.make_entry(entries[0].dn, 
+            try:
+                ldap.update_entry(uid_uniqueness_plugin)
+            except errors.ExecutionError, e:
+                root_logger.debug("update_uid_uniqueness: cannot "
+                                  "update uid uniqueness plugin entry: %s", e)
+                error = True
+        if error:
+            root_logger.error("update_uid_uniqueness: error(s)"
+                              "detected during plugin update")
+        return (True, False, [])

Freeipa-devel mailing list

Reply via email to