This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository lib389.
commit 121ebdbeeef3656116eca9cfa8b9319d7463f5a9
Author: Ilias Stamatis <stamatis.iliass(a)gmail.com>
Date: Tue Jun 27 03:10:22 2017 +0300
Issue31 - Add memberOf fix-up task
Description:
- Implement a new-style Task class based on DSLdapObject.
- Create the memberof fix-up task class.
- Add dsconf support for calling the task.
- Write a functional test for testing the fix-up task.
- Small bug fixes on the test utils for plugin tests.
- Replace some "from foo import *" imports with explicit imports.
https://pagure.io/lib389/issue/31
Author: Ilias95
Review by: wibrown (Thank you, great work!)
---
lib389/cli_conf/plugins/memberof.py | 17 +++++++---
lib389/plugins.py | 21 ++++++++----
lib389/tasks.py | 62 +++++++++++++++++++++++++++++++++--
lib389/tests/plugins/memberof_test.py | 49 +++++++++++++++++++++++++--
lib389/tests/plugins/utils.py | 11 ++++---
5 files changed, 139 insertions(+), 21 deletions(-)
diff --git a/lib389/cli_conf/plugins/memberof.py b/lib389/cli_conf/plugins/memberof.py
index 943a20d..525bc34 100644
--- a/lib389/cli_conf/plugins/memberof.py
+++ b/lib389/cli_conf/plugins/memberof.py
@@ -53,7 +53,7 @@ def remove_groupattr(inst, basedn, log, args):
plugin.remove_groupattr(args.value)
except ldap.UNWILLING_TO_PERFORM:
log.error("Error: Failed to delete. memberOfGroupAttr is required.")
- except ldap.NO_SUCH_ATTRIBUTE as ex:
+ except ldap.NO_SUCH_ATTRIBUTE:
log.error('Error: Failed to delete. No value "{0}"
found.'.format(args.value))
else:
log.info('successfully removed memberOfGroupAttr value
"{}"'.format(args.value))
@@ -150,7 +150,7 @@ def remove_scope(inst, basedn, log, args):
plugin = MemberOfPlugin(inst)
try:
plugin.remove_entryscope(args.value)
- except ldap.NO_SUCH_ATTRIBUTE as ex:
+ except ldap.NO_SUCH_ATTRIBUTE:
log.error('Error: Failed to delete. No value "{0}"
found.'.format(args.value))
else:
log.info('successfully removed memberOfEntryScope value
"{}"'.format(args.value))
@@ -181,14 +181,16 @@ def remove_excludescope(inst, basedn, log, args):
plugin = MemberOfPlugin(inst)
try:
plugin.remove_excludescope(args.value)
- except ldap.NO_SUCH_ATTRIBUTE as ex:
+ except ldap.NO_SUCH_ATTRIBUTE:
log.error('Error: Failed to delete. No value "{0}"
found.'.format(args.value))
else:
log.info('successfully removed memberOfEntryScopeExcludeSubtree value
"{}"'.format(args.value))
def fixup(inst, basedn, log, args):
- """Run the fix-up task for memberof plugin."""
- pass
+ plugin = MemberOfPlugin(inst)
+ log.info('Attempting to add task entry... This will fail if MemberOf plug-in is
not enabled.')
+ fixup_task = plugin.fixup(args.basedn, args.filter)
+ log.info('Successfully added task entry ' + fixup_task.dn)
def create_parser(subparsers):
memberof_parser = subparsers.add_parser('memberof', help='Manage and
configure MemberOf plugin')
@@ -269,3 +271,8 @@ def create_parser(subparsers):
fixup_parser = subcommands.add_parser('fixup', help='run the fix-up task
for memberof plugin')
fixup_parser.set_defaults(func=fixup)
+ fixup_parser.add_argument('-b', '--basedn', required=True,
help="base DN that contains entries to fix up")
+ fixup_parser.add_argument('-f', '--filter', help="Filter for
entries to fix up.\n"
+ "If omitted, all entries with objectclass inetuser/inetadmin/nsmemberof
under the\n"
+ "specified base will have their memberOf attribute regenerated."
+ )
diff --git a/lib389/plugins.py b/lib389/plugins.py
index 57e983e..4a9cc43 100644
--- a/lib389/plugins.py
+++ b/lib389/plugins.py
@@ -9,10 +9,13 @@
import ldap
import copy
-from lib389.properties import *
-from lib389._constants import *
+from lib389 import tasks
from lib389._mapped_object import DSLdapObjects, DSLdapObject
-from lib389.utils import ensure_str
+from lib389._constants import DN_PLUGIN
+from lib389.properties import (
+ PLUGINS_OBJECTCLASS_VALUE, PLUGIN_PROPNAME_TO_ATTRNAME,
+ PLUGINS_ENABLE_ON_VALUE, PLUGINS_ENABLE_OFF_VALUE, PLUGIN_ENABLE
+ )
class Plugin(DSLdapObject):
_plugin_properties = {
@@ -43,7 +46,7 @@ class Plugin(DSLdapObject):
self.set('nsslapd-pluginEnabled', 'off')
def status(self):
- return ensure_str(self.get_attr_val('nsslapd-pluginEnabled')) ==
'on'
+ return self.get_attr_val_utf8('nsslapd-pluginEnabled') == 'on'
def create(self, rdn=None, properties=None, basedn=None):
# When we create plugins, we don't want people to have to consider all
@@ -256,8 +259,14 @@ class MemberOfPlugin(Plugin):
# def remove_all_excludescope(self):
# self.remove_all('memberofentryscopeexcludesubtree')
- def fixup(self):
- pass
+ def fixup(self, basedn, _filter=None):
+ task = tasks.MemberOfFixupTask(self._instance)
+ task_properties = {'basedn': basedn}
+ if _filter is not None:
+ task_properties['filter'] = _filter
+ task.create(properties=task_properties)
+
+ return task
class RetroChangelogPlugin(Plugin):
def __init__(self, instance, dn="cn=Retro Changelog
Plugin,cn=plugins,cn=config", batch=False):
diff --git a/lib389/tasks.py b/lib389/tasks.py
index bb23d74..19b1f0b 100644
--- a/lib389/tasks.py
+++ b/lib389/tasks.py
@@ -9,10 +9,65 @@
import time
import os.path
import ldap
+from datetime import datetime
+
+
+from lib389 import Entry
+from lib389._mapped_object import DSLdapObject
+from lib389._constants import (
+ DEFAULT_SUFFIX, DEFAULT_BENAME, DN_EXPORT_TASK, DN_BACKUP_TASK,
+ DN_IMPORT_TASK, DN_RESTORE_TASK, DN_INDEX_TASK, DN_MBO_TASK,
+ DN_TOMB_FIXUP_TASK
+ )
+from lib389.properties import (
+ TASK_WAIT, EXPORT_REPL_INFO, MT_PROPNAME_TO_ATTRNAME, MT_SUFFIX,
+ TASK_TOMB_STRIP
+ )
+
+
+class Task(DSLdapObject):
+ def __init__(self, instance, dn=None, batch=False):
+ super(Task, self).__init__(instance, dn, batch)
+ self._rdn_attribute = 'cn'
+ self._must_attributes = ['cn']
+ self._create_objectclasses = ['top', 'extensibleObject']
+ self._protected = False
+
+ def is_complete(self):
+ """Return True if task is complete, else False."""
+ self._exit_code = self.get_attr_val("nsTaskExitCode")
+ return self._exit_code is not None
+
+ def get_exit_code(self):
+ """Return task's exit code if task is complete, else
None."""
+ if self.is_complete():
+ return int(self._exit_code)
+ return None
+
+ def wait(self):
+ """Wait until task is complete."""
+ while True:
+ if self.is_complete():
+ break
+ time.sleep(1)
+
+ def create(self, rdn=None, properties=None, basedn=None):
+ properties['cn'] = self.cn
+ return super(Task, self).create(rdn, properties, basedn)
+
+ @staticmethod
+ def _get_task_date():
+ """Return a timestamp to use in naming new task
entries."""
+ return datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
+
+
+class MemberOfFixupTask(Task):
+ def __init__(self, instance, dn=None, batch=False):
+ self.cn = 'memberOf_fixup_' + Task._get_task_date()
+ dn = "cn=" + self.cn + "," + DN_MBO_TASK
-from lib389._constants import *
-from lib389.properties import *
-from lib389 import DirSrv, Entry
+ super(MemberOfFixupTask, self).__init__(instance, dn, batch)
+ self._must_attributes.extend(['basedn'])
class Tasks(object):
@@ -26,6 +81,7 @@ class Tasks(object):
self.entry = None
def __getattr__(self, name):
+ from lib389 import DirSrv
if name in Tasks.proxied_methods:
return DirSrv.__getattr__(self.conn, name)
diff --git a/lib389/tests/plugins/memberof_test.py
b/lib389/tests/plugins/memberof_test.py
index 5079bd1..f569b93 100644
--- a/lib389/tests/plugins/memberof_test.py
+++ b/lib389/tests/plugins/memberof_test.py
@@ -8,6 +8,7 @@
#
import pytest
+from time import sleep
from lib389.topologies import topology_st
from lib389.plugins import MemberOfPlugin
@@ -128,14 +129,14 @@ def test_memberofallbackends(plugin):
who are in the same database as the group. Test that when memberOfAllBackends
is enabled, memberOf will search across all databases instead.
"""
- ou_value = "ou=People2"
+ ou_value = "People2"
ou_suffix = DEFAULT_SUFFIX
# create a new backend
plugin._instance.backends.create(
None, properties={
BACKEND_NAME: "People2Data",
- BACKEND_SUFFIX: ou_value + "," + ou_suffix,
+ BACKEND_SUFFIX: "ou=" + ou_value + "," + ou_suffix,
})
# create a new sub-suffix stored in the new backend
@@ -308,3 +309,47 @@ def test_scoping(plugin):
# This does not work at the moment, because there is a bug in DS (#49284).
# plugin.remove_all_excludescope()
# plugin.remove_all_entryscope()
+
+def test_fixup_task(plugin):
+ """
+ Test that after creating the fix-up task entry, initial memberOf attributes
+ are created on the member's user entries in the directory automatically.
+ Also, test that the filter provided to the task is correctly applied.
+ """
+ # disable the plugin and restart the server for the action to take effect
+ plugin.disable()
+ plugin._instance.restart()
+
+ user1 = create_test_user(plugin._instance)
+ user2 = create_test_user(plugin._instance, cn="testuser2")
+ group = create_test_group(plugin._instance)
+
+ group.add_member(user1.dn)
+ group.add_member(user2.dn)
+
+ # enable the plugin and restart the server for the action to take effect
+ plugin.enable()
+ plugin._instance.restart()
+
+ memberofattr = plugin.get_attr()
+ # memberof attribute should not appear on user entries
+ assert not memberofattr in user1.get_all_attrs()
+ assert not memberofattr in user2.get_all_attrs()
+
+ # run the fix-up task and provide a filter for the entry
+ task = plugin.fixup(basedn=DEFAULT_SUFFIX, _filter="(cn=testuser2)")
+ # wait for the task to complete
+ task.wait()
+
+ assert task.is_complete()
+ assert task.get_exit_code() == 0
+
+ # memberof attribute should now appear on the user entry matching the filter
+ assert memberofattr in user2.get_all_attrs()
+ assert group.dn in user2.get_attr_vals(memberofattr)
+
+ # but should not appear on user entry that doesn't match the filter
+ assert not memberofattr in user1.get_all_attrs()
+
+ # clean up for subsequent test cases
+ delete_objects([user1, user2, group])
diff --git a/lib389/tests/plugins/utils.py b/lib389/tests/plugins/utils.py
index 4a7e248..f5dfff4 100644
--- a/lib389/tests/plugins/utils.py
+++ b/lib389/tests/plugins/utils.py
@@ -34,12 +34,12 @@ def create_test_user(instance, cn=None, suffix=None):
global test_user_id
if cn is None:
- cn = "cn=testuser_" + str(test_user_id)
+ cn = "testuser_" + str(test_user_id)
test_user_id += 1
if suffix is None:
suffix = "ou=People," + DEFAULT_SUFFIX
- dn = cn + "," + suffix
+ dn = "cn=" + cn + "," + suffix
properties = {
'uid': cn,
@@ -68,12 +68,12 @@ def create_test_group(instance, cn=None, suffix=None,
unique_group=False):
global test_group_id
if cn is None:
- cn = "cn=testgroup_" + str(test_group_id)
+ cn = "testgroup_" + str(test_group_id)
test_group_id += 1
if suffix is None:
suffix = "ou=Groups," + DEFAULT_SUFFIX
- dn = cn + "," + suffix
+ dn = "cn=" + cn + "," + suffix
properties = {
'cn': cn,
@@ -102,12 +102,13 @@ def create_test_ou(instance, ou=None, suffix=None):
global test_ou_id
if ou is None:
- ou = "ou=TestOU_" + str(test_ou_id)
+ ou = "TestOU_" + str(test_ou_id)
test_ou_id += 1
if suffix is None:
suffix = DEFAULT_SUFFIX
dn = ou + "," + suffix
+ dn = "ou=" + ou + "," + suffix
properties = {
'ou': ou,
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.