|
|
b161c9 |
From b19dc5b453751ca63545e39537bfb87707cf867e Mon Sep 17 00:00:00 2001
|
|
|
b161c9 |
From: "Thierry bordaz (tbordaz)" <tbordaz@redhat.com>
|
|
|
b161c9 |
Date: Thu, 16 Oct 2014 17:24:15 +0200
|
|
|
b161c9 |
Subject: [PATCH 77/84] Ticket 47553: Enhance ACIs to have more control over
|
|
|
b161c9 |
MODRDN operations
|
|
|
b161c9 |
|
|
|
b161c9 |
Bug Description:
|
|
|
b161c9 |
The ticket 47553 introduces a new aci right: moddn.
|
|
|
b161c9 |
This rights allows/deny to do a MODDN from a part of the DIT to an other.
|
|
|
b161c9 |
Before the right allow/deny to do a MODDN was granted if the subject had 'write' access to the rdn attribute.
|
|
|
b161c9 |
To switch from the previous mode to the new one, there is a toggle 'nsslapd-moddn-aci'.
|
|
|
b161c9 |
The getEffectiveRight control, should report the MODDN right ('n') according to
|
|
|
b161c9 |
the acis and the value of this toggle
|
|
|
b161c9 |
|
|
|
b161c9 |
Fix Description:
|
|
|
b161c9 |
test 'nsslapd-moddn-aci' in the geteffectiveright code
|
|
|
b161c9 |
|
|
|
b161c9 |
https://fedorahosted.org/389/ticket/47553
|
|
|
b161c9 |
|
|
|
b161c9 |
Reviewed by: Noriko (Thanks !)
|
|
|
b161c9 |
|
|
|
b161c9 |
Platforms tested: F17
|
|
|
b161c9 |
|
|
|
b161c9 |
Flag Day: no
|
|
|
b161c9 |
|
|
|
b161c9 |
Doc impact: no
|
|
|
b161c9 |
|
|
|
b161c9 |
(cherry picked from commit be67f8128d4a19edec95a6b1b022fd9262710e74)
|
|
|
b161c9 |
---
|
|
|
b161c9 |
dirsrvtests/tickets/ticket47553_ger.py | 553 ++++++++++++++++++++++++++
|
|
|
b161c9 |
ldap/servers/plugins/acl/acleffectiverights.c | 67 ++--
|
|
|
b161c9 |
2 files changed, 590 insertions(+), 30 deletions(-)
|
|
|
b161c9 |
create mode 100644 dirsrvtests/tickets/ticket47553_ger.py
|
|
|
b161c9 |
|
|
|
b161c9 |
diff --git a/dirsrvtests/tickets/ticket47553_ger.py b/dirsrvtests/tickets/ticket47553_ger.py
|
|
|
b161c9 |
new file mode 100644
|
|
|
b161c9 |
index 0000000..d688c70
|
|
|
b161c9 |
--- /dev/null
|
|
|
b161c9 |
+++ b/dirsrvtests/tickets/ticket47553_ger.py
|
|
|
b161c9 |
@@ -0,0 +1,553 @@
|
|
|
b161c9 |
+'''
|
|
|
b161c9 |
+Created on Nov 7, 2013
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+@author: tbordaz
|
|
|
b161c9 |
+'''
|
|
|
b161c9 |
+import os
|
|
|
b161c9 |
+import sys
|
|
|
b161c9 |
+import time
|
|
|
b161c9 |
+import ldap
|
|
|
b161c9 |
+import logging
|
|
|
b161c9 |
+import socket
|
|
|
b161c9 |
+import time
|
|
|
b161c9 |
+import logging
|
|
|
b161c9 |
+import pytest
|
|
|
b161c9 |
+import re
|
|
|
b161c9 |
+from lib389 import DirSrv, Entry, tools
|
|
|
b161c9 |
+from lib389.tools import DirSrvTools
|
|
|
b161c9 |
+from lib389._constants import *
|
|
|
b161c9 |
+from lib389.properties import *
|
|
|
b161c9 |
+from constants import *
|
|
|
b161c9 |
+from lib389._constants import REPLICAROLE_MASTER
|
|
|
b161c9 |
+from ldap.controls.simple import GetEffectiveRightsControl
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+logging.getLogger(__name__).setLevel(logging.DEBUG)
|
|
|
b161c9 |
+log = logging.getLogger(__name__)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+#
|
|
|
b161c9 |
+# important part. We can deploy Master1 and Master2 on different versions
|
|
|
b161c9 |
+#
|
|
|
b161c9 |
+installation1_prefix = None
|
|
|
b161c9 |
+installation2_prefix = None
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+TEST_REPL_DN = "cn=test_repl, %s" % SUFFIX
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+STAGING_CN = "staged user"
|
|
|
b161c9 |
+PRODUCTION_CN = "accounts"
|
|
|
b161c9 |
+EXCEPT_CN = "excepts"
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+STAGING_DN = "cn=%s,%s" % (STAGING_CN, SUFFIX)
|
|
|
b161c9 |
+PRODUCTION_DN = "cn=%s,%s" % (PRODUCTION_CN, SUFFIX)
|
|
|
b161c9 |
+PROD_EXCEPT_DN = "cn=%s,%s" % (EXCEPT_CN, PRODUCTION_DN)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+STAGING_PATTERN = "cn=%s*,%s" % (STAGING_CN[:2], SUFFIX)
|
|
|
b161c9 |
+PRODUCTION_PATTERN = "cn=%s*,%s" % (PRODUCTION_CN[:2], SUFFIX)
|
|
|
b161c9 |
+BAD_STAGING_PATTERN = "cn=bad*,%s" % (SUFFIX)
|
|
|
b161c9 |
+BAD_PRODUCTION_PATTERN = "cn=bad*,%s" % (SUFFIX)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+BIND_CN = "bind_entry"
|
|
|
b161c9 |
+BIND_DN = "cn=%s,%s" % (BIND_CN, SUFFIX)
|
|
|
b161c9 |
+BIND_PW = "password"
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+NEW_ACCOUNT = "new_account"
|
|
|
b161c9 |
+MAX_ACCOUNTS = 20
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+CONFIG_MODDN_ACI_ATTR = "nsslapd-moddn-aci"
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+class TopologyMaster1Master2(object):
|
|
|
b161c9 |
+ def __init__(self, master1, master2):
|
|
|
b161c9 |
+ master1.open()
|
|
|
b161c9 |
+ self.master1 = master1
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ master2.open()
|
|
|
b161c9 |
+ self.master2 = master2
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+@pytest.fixture(scope="module")
|
|
|
b161c9 |
+def topology(request):
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ This fixture is used to create a replicated topology for the 'module'.
|
|
|
b161c9 |
+ The replicated topology is MASTER1 <-> Master2.
|
|
|
b161c9 |
+ At the beginning, It may exists a master2 instance and/or a master2 instance.
|
|
|
b161c9 |
+ It may also exists a backup for the master1 and/or the master2.
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ Principle:
|
|
|
b161c9 |
+ If master1 instance exists:
|
|
|
b161c9 |
+ restart it
|
|
|
b161c9 |
+ If master2 instance exists:
|
|
|
b161c9 |
+ restart it
|
|
|
b161c9 |
+ If backup of master1 AND backup of master2 exists:
|
|
|
b161c9 |
+ create or rebind to master1
|
|
|
b161c9 |
+ create or rebind to master2
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ restore master1 from backup
|
|
|
b161c9 |
+ restore master2 from backup
|
|
|
b161c9 |
+ else:
|
|
|
b161c9 |
+ Cleanup everything
|
|
|
b161c9 |
+ remove instances
|
|
|
b161c9 |
+ remove backups
|
|
|
b161c9 |
+ Create instances
|
|
|
b161c9 |
+ Initialize replication
|
|
|
b161c9 |
+ Create backups
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ global installation1_prefix
|
|
|
b161c9 |
+ global installation2_prefix
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # allocate master1 on a given deployement
|
|
|
b161c9 |
+ master1 = DirSrv(verbose=False)
|
|
|
b161c9 |
+ if installation1_prefix:
|
|
|
b161c9 |
+ args_instance[SER_DEPLOYED_DIR] = installation1_prefix
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Args for the master1 instance
|
|
|
b161c9 |
+ args_instance[SER_HOST] = HOST_MASTER_1
|
|
|
b161c9 |
+ args_instance[SER_PORT] = PORT_MASTER_1
|
|
|
b161c9 |
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_1
|
|
|
b161c9 |
+ args_master = args_instance.copy()
|
|
|
b161c9 |
+ master1.allocate(args_master)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # allocate master1 on a given deployement
|
|
|
b161c9 |
+ master2 = DirSrv(verbose=False)
|
|
|
b161c9 |
+ if installation2_prefix:
|
|
|
b161c9 |
+ args_instance[SER_DEPLOYED_DIR] = installation2_prefix
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Args for the consumer instance
|
|
|
b161c9 |
+ args_instance[SER_HOST] = HOST_MASTER_2
|
|
|
b161c9 |
+ args_instance[SER_PORT] = PORT_MASTER_2
|
|
|
b161c9 |
+ args_instance[SER_SERVERID_PROP] = SERVERID_MASTER_2
|
|
|
b161c9 |
+ args_master = args_instance.copy()
|
|
|
b161c9 |
+ master2.allocate(args_master)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Get the status of the backups
|
|
|
b161c9 |
+ backup_master1 = master1.checkBackupFS()
|
|
|
b161c9 |
+ backup_master2 = master2.checkBackupFS()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Get the status of the instance and restart it if it exists
|
|
|
b161c9 |
+ instance_master1 = master1.exists()
|
|
|
b161c9 |
+ if instance_master1:
|
|
|
b161c9 |
+ master1.stop(timeout=10)
|
|
|
b161c9 |
+ master1.start(timeout=10)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ instance_master2 = master2.exists()
|
|
|
b161c9 |
+ if instance_master2:
|
|
|
b161c9 |
+ master2.stop(timeout=10)
|
|
|
b161c9 |
+ master2.start(timeout=10)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ if backup_master1 and backup_master2:
|
|
|
b161c9 |
+ # The backups exist, assuming they are correct
|
|
|
b161c9 |
+ # we just re-init the instances with them
|
|
|
b161c9 |
+ if not instance_master1:
|
|
|
b161c9 |
+ master1.create()
|
|
|
b161c9 |
+ # Used to retrieve configuration information (dbdir, confdir...)
|
|
|
b161c9 |
+ master1.open()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ if not instance_master2:
|
|
|
b161c9 |
+ master2.create()
|
|
|
b161c9 |
+ # Used to retrieve configuration information (dbdir, confdir...)
|
|
|
b161c9 |
+ master2.open()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # restore master1 from backup
|
|
|
b161c9 |
+ master1.stop(timeout=10)
|
|
|
b161c9 |
+ master1.restoreFS(backup_master1)
|
|
|
b161c9 |
+ master1.start(timeout=10)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # restore master2 from backup
|
|
|
b161c9 |
+ master2.stop(timeout=10)
|
|
|
b161c9 |
+ master2.restoreFS(backup_master2)
|
|
|
b161c9 |
+ master2.start(timeout=10)
|
|
|
b161c9 |
+ else:
|
|
|
b161c9 |
+ # We should be here only in two conditions
|
|
|
b161c9 |
+ # - This is the first time a test involve master-consumer
|
|
|
b161c9 |
+ # so we need to create everything
|
|
|
b161c9 |
+ # - Something weird happened (instance/backup destroyed)
|
|
|
b161c9 |
+ # so we discard everything and recreate all
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Remove all the backups. So even if we have a specific backup file
|
|
|
b161c9 |
+ # (e.g backup_master) we clear all backups that an instance my have created
|
|
|
b161c9 |
+ if backup_master1:
|
|
|
b161c9 |
+ master1.clearBackupFS()
|
|
|
b161c9 |
+ if backup_master2:
|
|
|
b161c9 |
+ master2.clearBackupFS()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Remove all the instances
|
|
|
b161c9 |
+ if instance_master1:
|
|
|
b161c9 |
+ master1.delete()
|
|
|
b161c9 |
+ if instance_master2:
|
|
|
b161c9 |
+ master2.delete()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Create the instances
|
|
|
b161c9 |
+ master1.create()
|
|
|
b161c9 |
+ master1.open()
|
|
|
b161c9 |
+ master2.create()
|
|
|
b161c9 |
+ master2.open()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ #
|
|
|
b161c9 |
+ # Now prepare the Master-Consumer topology
|
|
|
b161c9 |
+ #
|
|
|
b161c9 |
+ # First Enable replication
|
|
|
b161c9 |
+ master1.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_1)
|
|
|
b161c9 |
+ master2.replica.enableReplication(suffix=SUFFIX, role=REPLICAROLE_MASTER, replicaId=REPLICAID_MASTER_2)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Initialize the supplier->consumer
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ properties = {RA_NAME: r'meTo_$host:$port',
|
|
|
b161c9 |
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
|
|
|
b161c9 |
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
|
|
|
b161c9 |
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
|
|
|
b161c9 |
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
|
|
b161c9 |
+ repl_agreement = master1.agreement.create(suffix=SUFFIX, host=master2.host, port=master2.port, properties=properties)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ if not repl_agreement:
|
|
|
b161c9 |
+ log.fatal("Fail to create a replica agreement")
|
|
|
b161c9 |
+ sys.exit(1)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ log.debug("%s created" % repl_agreement)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ properties = {RA_NAME: r'meTo_$host:$port',
|
|
|
b161c9 |
+ RA_BINDDN: defaultProperties[REPLICATION_BIND_DN],
|
|
|
b161c9 |
+ RA_BINDPW: defaultProperties[REPLICATION_BIND_PW],
|
|
|
b161c9 |
+ RA_METHOD: defaultProperties[REPLICATION_BIND_METHOD],
|
|
|
b161c9 |
+ RA_TRANSPORT_PROT: defaultProperties[REPLICATION_TRANSPORT]}
|
|
|
b161c9 |
+ master2.agreement.create(suffix=SUFFIX, host=master1.host, port=master1.port, properties=properties)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ master1.agreement.init(SUFFIX, HOST_MASTER_2, PORT_MASTER_2)
|
|
|
b161c9 |
+ master1.waitForReplInit(repl_agreement)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Check replication is working fine
|
|
|
b161c9 |
+ master1.add_s(Entry((TEST_REPL_DN, {
|
|
|
b161c9 |
+ 'objectclass': "top person".split(),
|
|
|
b161c9 |
+ 'sn': 'test_repl',
|
|
|
b161c9 |
+ 'cn': 'test_repl'})))
|
|
|
b161c9 |
+ loop = 0
|
|
|
b161c9 |
+ while loop <= 10:
|
|
|
b161c9 |
+ try:
|
|
|
b161c9 |
+ ent = master2.getEntry(TEST_REPL_DN, ldap.SCOPE_BASE, "(objectclass=*)")
|
|
|
b161c9 |
+ break
|
|
|
b161c9 |
+ except ldap.NO_SUCH_OBJECT:
|
|
|
b161c9 |
+ time.sleep(1)
|
|
|
b161c9 |
+ loop += 1
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Time to create the backups
|
|
|
b161c9 |
+ master1.stop(timeout=10)
|
|
|
b161c9 |
+ master1.backupfile = master1.backupFS()
|
|
|
b161c9 |
+ master1.start(timeout=10)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ master2.stop(timeout=10)
|
|
|
b161c9 |
+ master2.backupfile = master2.backupFS()
|
|
|
b161c9 |
+ master2.start(timeout=10)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # clear the tmp directory
|
|
|
b161c9 |
+ master1.clearTmpDir(__file__)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ #
|
|
|
b161c9 |
+ # Here we have two instances master and consumer
|
|
|
b161c9 |
+ # with replication working. Either coming from a backup recovery
|
|
|
b161c9 |
+ # or from a fresh (re)init
|
|
|
b161c9 |
+ # Time to return the topology
|
|
|
b161c9 |
+ return TopologyMaster1Master2(master1, master2)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def _bind_manager(topology):
|
|
|
b161c9 |
+ topology.master1.log.info("Bind as %s " % DN_DM)
|
|
|
b161c9 |
+ topology.master1.simple_bind_s(DN_DM, PASSWORD)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def _bind_normal(topology):
|
|
|
b161c9 |
+ # bind as bind_entry
|
|
|
b161c9 |
+ topology.master1.log.info("Bind as %s" % BIND_DN)
|
|
|
b161c9 |
+ topology.master1.simple_bind_s(BIND_DN, BIND_PW)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def _moddn_aci_deny_tree(topology, mod_type=None, target_from=STAGING_DN, target_to=PROD_EXCEPT_DN):
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ It denies the access moddn_to in cn=except,cn=accounts,SUFFIX
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ assert mod_type != None
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ ACI_TARGET_FROM = ""
|
|
|
b161c9 |
+ ACI_TARGET_TO = ""
|
|
|
b161c9 |
+ if target_from:
|
|
|
b161c9 |
+ ACI_TARGET_FROM = "(target_from = \"ldap:///%s\")" % (target_from)
|
|
|
b161c9 |
+ if target_to:
|
|
|
b161c9 |
+ ACI_TARGET_TO = "(target_to = \"ldap:///%s\")" % (target_to)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ ACI_ALLOW = "(version 3.0; acl \"Deny MODDN to prod_except\"; deny (moddn)"
|
|
|
b161c9 |
+ ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
|
|
|
b161c9 |
+ ACI_BODY = ACI_TARGET_TO + ACI_TARGET_FROM + ACI_ALLOW + ACI_SUBJECT
|
|
|
b161c9 |
+ mod = [(mod_type, 'aci', ACI_BODY)]
|
|
|
b161c9 |
+ #topology.master1.modify_s(SUFFIX, mod)
|
|
|
b161c9 |
+ topology.master1.log.info("Add a DENY aci under %s " % PROD_EXCEPT_DN)
|
|
|
b161c9 |
+ topology.master1.modify_s(PROD_EXCEPT_DN, mod)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def _moddn_aci_staging_to_production(topology, mod_type=None, target_from=STAGING_DN, target_to=PRODUCTION_DN):
|
|
|
b161c9 |
+ assert mod_type != None
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ ACI_TARGET_FROM = ""
|
|
|
b161c9 |
+ ACI_TARGET_TO = ""
|
|
|
b161c9 |
+ if target_from:
|
|
|
b161c9 |
+ ACI_TARGET_FROM = "(target_from = \"ldap:///%s\")" % (target_from)
|
|
|
b161c9 |
+ if target_to:
|
|
|
b161c9 |
+ ACI_TARGET_TO = "(target_to = \"ldap:///%s\")" % (target_to)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ ACI_ALLOW = "(version 3.0; acl \"MODDN from staging to production\"; allow (moddn)"
|
|
|
b161c9 |
+ ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
|
|
|
b161c9 |
+ ACI_BODY = ACI_TARGET_FROM + ACI_TARGET_TO + ACI_ALLOW + ACI_SUBJECT
|
|
|
b161c9 |
+ mod = [(mod_type, 'aci', ACI_BODY)]
|
|
|
b161c9 |
+ topology.master1.modify_s(SUFFIX, mod)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def _moddn_aci_from_production_to_staging(topology, mod_type=None):
|
|
|
b161c9 |
+ assert mod_type != None
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ ACI_TARGET = "(target_from = \"ldap:///%s\") (target_to = \"ldap:///%s\")" % (PRODUCTION_DN, STAGING_DN)
|
|
|
b161c9 |
+ ACI_ALLOW = "(version 3.0; acl \"MODDN from production to staging\"; allow (moddn)"
|
|
|
b161c9 |
+ ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
|
|
|
b161c9 |
+ ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
|
|
|
b161c9 |
+ mod = [(mod_type, 'aci', ACI_BODY)]
|
|
|
b161c9 |
+ topology.master1.modify_s(SUFFIX, mod)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_init(topology):
|
|
|
b161c9 |
+ """
|
|
|
b161c9 |
+ Creates
|
|
|
b161c9 |
+ - a staging DIT
|
|
|
b161c9 |
+ - a production DIT
|
|
|
b161c9 |
+ - add accounts in staging DIT
|
|
|
b161c9 |
+ - enable ACL logging (commented for performance reason)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ """
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### INITIALIZATION ######################\n")
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # entry used to bind with
|
|
|
b161c9 |
+ topology.master1.log.info("Add %s" % BIND_DN)
|
|
|
b161c9 |
+ topology.master1.add_s(Entry((BIND_DN, {
|
|
|
b161c9 |
+ 'objectclass': "top person".split(),
|
|
|
b161c9 |
+ 'sn': BIND_CN,
|
|
|
b161c9 |
+ 'cn': BIND_CN,
|
|
|
b161c9 |
+ 'userpassword': BIND_PW})))
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # DIT for staging
|
|
|
b161c9 |
+ topology.master1.log.info("Add %s" % STAGING_DN)
|
|
|
b161c9 |
+ topology.master1.add_s(Entry((STAGING_DN, {
|
|
|
b161c9 |
+ 'objectclass': "top organizationalRole".split(),
|
|
|
b161c9 |
+ 'cn': STAGING_CN,
|
|
|
b161c9 |
+ 'description': "staging DIT"})))
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # DIT for production
|
|
|
b161c9 |
+ topology.master1.log.info("Add %s" % PRODUCTION_DN)
|
|
|
b161c9 |
+ topology.master1.add_s(Entry((PRODUCTION_DN, {
|
|
|
b161c9 |
+ 'objectclass': "top organizationalRole".split(),
|
|
|
b161c9 |
+ 'cn': PRODUCTION_CN,
|
|
|
b161c9 |
+ 'description': "production DIT"})))
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # DIT for production/except
|
|
|
b161c9 |
+ topology.master1.log.info("Add %s" % PROD_EXCEPT_DN)
|
|
|
b161c9 |
+ topology.master1.add_s(Entry((PROD_EXCEPT_DN, {
|
|
|
b161c9 |
+ 'objectclass': "top organizationalRole".split(),
|
|
|
b161c9 |
+ 'cn': EXCEPT_CN,
|
|
|
b161c9 |
+ 'description': "production except DIT"})))
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # enable acl error logging
|
|
|
b161c9 |
+ #mod = [(ldap.MOD_REPLACE, 'nsslapd-errorlog-level', '128')]
|
|
|
b161c9 |
+ #topology.master1.modify_s(DN_CONFIG, mod)
|
|
|
b161c9 |
+ #topology.master2.modify_s(DN_CONFIG, mod)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # add dummy entries in the staging DIT
|
|
|
b161c9 |
+ for cpt in range(MAX_ACCOUNTS):
|
|
|
b161c9 |
+ name = "%s%d" % (NEW_ACCOUNT, cpt)
|
|
|
b161c9 |
+ topology.master1.add_s(Entry(("cn=%s,%s" % (name, STAGING_DN), {
|
|
|
b161c9 |
+ 'objectclass': "top person".split(),
|
|
|
b161c9 |
+ 'sn': name,
|
|
|
b161c9 |
+ 'cn': name})))
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_default_add_deny(topology):
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ This test case checks that the ADD operation fails (no ADD aci on production)
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### mode moddn_aci : ADD (should fail) ######################\n")
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ #
|
|
|
b161c9 |
+ # First try to add an entry in production => INSUFFICIENT_ACCESS
|
|
|
b161c9 |
+ #
|
|
|
b161c9 |
+ try:
|
|
|
b161c9 |
+ topology.master1.log.info("Try to add %s" % PRODUCTION_DN)
|
|
|
b161c9 |
+ name = "%s%d" % (NEW_ACCOUNT, 0)
|
|
|
b161c9 |
+ topology.master1.add_s(Entry(("cn=%s,%s" % (name, PRODUCTION_DN), {
|
|
|
b161c9 |
+ 'objectclass': "top person".split(),
|
|
|
b161c9 |
+ 'sn': name,
|
|
|
b161c9 |
+ 'cn': name})))
|
|
|
b161c9 |
+ assert 0 # this is an error, we should not be allowed to add an entry in production
|
|
|
b161c9 |
+ except Exception as e:
|
|
|
b161c9 |
+ topology.master1.log.info("Exception (expected): %s" % type(e).__name__)
|
|
|
b161c9 |
+ assert isinstance(e, ldap.INSUFFICIENT_ACCESS)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_default_ger_no_moddn(topology):
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### mode moddn_aci : GER no moddn ######################\n")
|
|
|
b161c9 |
+ request_ctrl = GetEffectiveRightsControl(criticality=True,authzId="dn: " + BIND_DN)
|
|
|
b161c9 |
+ msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
|
|
|
b161c9 |
+ rtype,rdata,rmsgid,response_ctrl = topology.master1.result3(msg_id)
|
|
|
b161c9 |
+ ger={}
|
|
|
b161c9 |
+ value=''
|
|
|
b161c9 |
+ for dn, attrs in rdata:
|
|
|
b161c9 |
+ topology.master1.log.info ("dn: %s" % dn)
|
|
|
b161c9 |
+ value = attrs['entryLevelRights'][0]
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info ("############### entryLevelRights: %r" % value)
|
|
|
b161c9 |
+ assert 'n' not in value
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_default_ger_with_moddn(topology):
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ This test case adds the moddn aci and check ger contains 'n'
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### mode moddn_aci: GER with moddn ######################\n")
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # successfull MOD with the ACI
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN)
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ request_ctrl = GetEffectiveRightsControl(criticality=True,authzId="dn: " + BIND_DN)
|
|
|
b161c9 |
+ msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
|
|
|
b161c9 |
+ rtype,rdata,rmsgid,response_ctrl = topology.master1.result3(msg_id)
|
|
|
b161c9 |
+ ger={}
|
|
|
b161c9 |
+ value = ''
|
|
|
b161c9 |
+ for dn, attrs in rdata:
|
|
|
b161c9 |
+ topology.master1.log.info ("dn: %s" % dn)
|
|
|
b161c9 |
+ value = attrs['entryLevelRights'][0]
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info ("############### entryLevelRights: %r" % value)
|
|
|
b161c9 |
+ assert 'n' in value
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # successfull MOD with the both ACI
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN)
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_switch_default_to_legacy(topology):
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ This test switch the server from default mode to legacy
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### Disable the moddn aci mod ######################\n" )
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ mod = [(ldap.MOD_REPLACE, CONFIG_MODDN_ACI_ATTR, 'off')]
|
|
|
b161c9 |
+ topology.master1.modify_s(DN_CONFIG, mod)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_legacy_ger_no_moddn1(topology):
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### mode legacy 1: GER no moddn ######################\n")
|
|
|
b161c9 |
+ request_ctrl = GetEffectiveRightsControl(criticality=True,authzId="dn: " + BIND_DN)
|
|
|
b161c9 |
+ msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
|
|
|
b161c9 |
+ rtype,rdata,rmsgid,response_ctrl = topology.master1.result3(msg_id)
|
|
|
b161c9 |
+ ger={}
|
|
|
b161c9 |
+ value=''
|
|
|
b161c9 |
+ for dn, attrs in rdata:
|
|
|
b161c9 |
+ topology.master1.log.info ("dn: %s" % dn)
|
|
|
b161c9 |
+ value = attrs['entryLevelRights'][0]
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info ("############### entryLevelRights: %r" % value)
|
|
|
b161c9 |
+ assert 'n' not in value
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_legacy_ger_no_moddn2(topology):
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### mode legacy 2: GER no moddn ######################\n")
|
|
|
b161c9 |
+ # successfull MOD with the ACI
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_ADD, target_from=STAGING_DN, target_to=PRODUCTION_DN)
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ request_ctrl = GetEffectiveRightsControl(criticality=True,authzId="dn: " + BIND_DN)
|
|
|
b161c9 |
+ msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
|
|
|
b161c9 |
+ rtype,rdata,rmsgid,response_ctrl = topology.master1.result3(msg_id)
|
|
|
b161c9 |
+ ger={}
|
|
|
b161c9 |
+ value=''
|
|
|
b161c9 |
+ for dn, attrs in rdata:
|
|
|
b161c9 |
+ topology.master1.log.info ("dn: %s" % dn)
|
|
|
b161c9 |
+ value = attrs['entryLevelRights'][0]
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info ("############### entryLevelRights: %r" % value)
|
|
|
b161c9 |
+ assert 'n' not in value
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # successfull MOD with the both ACI
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ _moddn_aci_staging_to_production(topology, mod_type=ldap.MOD_DELETE, target_from=STAGING_DN, target_to=PRODUCTION_DN)
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_mode_legacy_ger_with_moddn(topology):
|
|
|
b161c9 |
+ topology.master1.log.info("\n\n######################### mode legacy : GER with moddn ######################\n")
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # being allowed to read/write the RDN attribute use to allow the RDN
|
|
|
b161c9 |
+ ACI_TARGET = "(target = \"ldap:///%s\")(targetattr=\"cn\")" % (PRODUCTION_DN)
|
|
|
b161c9 |
+ ACI_ALLOW = "(version 3.0; acl \"MODDN production changing the RDN attribute\"; allow (read,search,write)"
|
|
|
b161c9 |
+ ACI_SUBJECT = " userdn = \"ldap:///%s\";)" % BIND_DN
|
|
|
b161c9 |
+ ACI_BODY = ACI_TARGET + ACI_ALLOW + ACI_SUBJECT
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # successfull MOD with the ACI
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ mod = [(ldap.MOD_ADD, 'aci', ACI_BODY)]
|
|
|
b161c9 |
+ topology.master1.modify_s(SUFFIX, mod)
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ request_ctrl = GetEffectiveRightsControl(criticality=True,authzId="dn: " + BIND_DN)
|
|
|
b161c9 |
+ msg_id = topology.master1.search_ext(PRODUCTION_DN, ldap.SCOPE_SUBTREE, "objectclass=*", serverctrls=[request_ctrl])
|
|
|
b161c9 |
+ rtype,rdata,rmsgid,response_ctrl = topology.master1.result3(msg_id)
|
|
|
b161c9 |
+ ger={}
|
|
|
b161c9 |
+ value=''
|
|
|
b161c9 |
+ for dn, attrs in rdata:
|
|
|
b161c9 |
+ topology.master1.log.info ("dn: %s" % dn)
|
|
|
b161c9 |
+ value = attrs['entryLevelRights'][0]
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topology.master1.log.info ("############### entryLevelRights: %r" % value)
|
|
|
b161c9 |
+ assert 'n' in value
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # successfull MOD with the both ACI
|
|
|
b161c9 |
+ _bind_manager(topology)
|
|
|
b161c9 |
+ mod = [(ldap.MOD_DELETE, 'aci', ACI_BODY)]
|
|
|
b161c9 |
+ topology.master1.modify_s(SUFFIX, mod)
|
|
|
b161c9 |
+ _bind_normal(topology)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def test_ticket47553_final(topology):
|
|
|
b161c9 |
+ topology.master1.stop(timeout=10)
|
|
|
b161c9 |
+ topology.master2.stop(timeout=10)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+def run_isolated():
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ run_isolated is used to run these test cases independently of a test scheduler (xunit, py.test..)
|
|
|
b161c9 |
+ To run isolated without py.test, you need to
|
|
|
b161c9 |
+ - edit this file and comment '@pytest.fixture' line before 'topology' function.
|
|
|
b161c9 |
+ - set the installation prefix
|
|
|
b161c9 |
+ - run this program
|
|
|
b161c9 |
+ '''
|
|
|
b161c9 |
+ global installation1_prefix
|
|
|
b161c9 |
+ global installation2_prefix
|
|
|
b161c9 |
+ installation1_prefix = None
|
|
|
b161c9 |
+ installation2_prefix = None
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ topo = topology(True)
|
|
|
b161c9 |
+ topo.master1.log.info("\n\n######################### Ticket 47553 ######################\n")
|
|
|
b161c9 |
+ test_ticket47553_init(topo)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ # Check that without appropriate aci we are not allowed to add/delete
|
|
|
b161c9 |
+ test_ticket47553_mode_default_add_deny(topo)
|
|
|
b161c9 |
+ test_ticket47553_mode_default_ger_no_moddn(topo)
|
|
|
b161c9 |
+ test_ticket47553_mode_default_ger_with_moddn(topo)
|
|
|
b161c9 |
+ test_ticket47553_mode_switch_default_to_legacy(topo)
|
|
|
b161c9 |
+ test_ticket47553_mode_legacy_ger_no_moddn1(topo)
|
|
|
b161c9 |
+ test_ticket47553_mode_legacy_ger_no_moddn2(topo)
|
|
|
b161c9 |
+ test_ticket47553_mode_legacy_ger_with_moddn(topo)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ test_ticket47553_final(topo)
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+if __name__ == '__main__':
|
|
|
b161c9 |
+ run_isolated()
|
|
|
b161c9 |
+
|
|
|
b161c9 |
diff --git a/ldap/servers/plugins/acl/acleffectiverights.c b/ldap/servers/plugins/acl/acleffectiverights.c
|
|
|
b161c9 |
index b9e2055..3c7d635 100644
|
|
|
b161c9 |
--- a/ldap/servers/plugins/acl/acleffectiverights.c
|
|
|
b161c9 |
+++ b/ldap/servers/plugins/acl/acleffectiverights.c
|
|
|
b161c9 |
@@ -456,38 +456,45 @@ _ger_get_entry_rights (
|
|
|
b161c9 |
entryrights |= SLAPI_ACL_DELETE;
|
|
|
b161c9 |
_append_gerstr(gerstr, gerstrsize, gerstrcap, "d", NULL);
|
|
|
b161c9 |
}
|
|
|
b161c9 |
- /*
|
|
|
b161c9 |
- * Some limitation/simplification applied here:
|
|
|
b161c9 |
- * - The modrdn right requires the rights to delete the old rdn and
|
|
|
b161c9 |
- * the new one. However we have no knowledge of what the new rdn
|
|
|
b161c9 |
- * is going to be.
|
|
|
b161c9 |
- * - In multi-valued RDN case, we check the right on
|
|
|
b161c9 |
- * the first rdn type only for now.
|
|
|
b161c9 |
- */
|
|
|
b161c9 |
- rdn = slapi_rdn_new_dn ( slapi_entry_get_ndn (e) );
|
|
|
b161c9 |
- slapi_rdn_get_first(rdn, &rdntype, &rdnvalue);
|
|
|
b161c9 |
- if ( NULL != rdntype ) {
|
|
|
b161c9 |
- slapi_log_error (SLAPI_LOG_ACL, plugin_name,
|
|
|
b161c9 |
- "_ger_get_entry_rights: SLAPI_ACL_WRITE_DEL & _ADD %s\n", rdntype );
|
|
|
b161c9 |
- if (acl_access_allowed(gerpb, e, rdntype, NULL,
|
|
|
b161c9 |
- ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS &&
|
|
|
b161c9 |
- acl_access_allowed(gerpb, e, rdntype, NULL,
|
|
|
b161c9 |
- ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
|
|
|
b161c9 |
- {
|
|
|
b161c9 |
- /* n - rename e */
|
|
|
b161c9 |
- entryrights |= SLAPI_ACL_WRITE;
|
|
|
b161c9 |
- _append_gerstr(gerstr, gerstrsize, gerstrcap, "n", NULL);
|
|
|
b161c9 |
- }
|
|
|
b161c9 |
- }
|
|
|
b161c9 |
- slapi_rdn_free ( &rdn );
|
|
|
b161c9 |
-
|
|
|
b161c9 |
- if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_MODDN) == LDAP_SUCCESS) {
|
|
|
b161c9 |
- slapi_log_error (SLAPI_LOG_ACL, plugin_name,
|
|
|
b161c9 |
- "_ger_get_entry_rights: SLAPI_ACL_MODDN %s\n", slapi_entry_get_ndn (e) );
|
|
|
b161c9 |
+
|
|
|
b161c9 |
+ if (config_get_moddn_aci()) {
|
|
|
b161c9 |
+ /* The server enforces the new MODDN aci right.
|
|
|
b161c9 |
+ * So the status 'n' is set if this right is granted.
|
|
|
b161c9 |
+ * Opposed to the legacy mode where this flag is set if
|
|
|
b161c9 |
+ * WRITE was granted on rdn attrbibute
|
|
|
b161c9 |
+ */
|
|
|
b161c9 |
+ if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_MODDN) == LDAP_SUCCESS) {
|
|
|
b161c9 |
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
|
|
|
b161c9 |
+ "_ger_get_entry_rights: SLAPI_ACL_MODDN %s\n", slapi_entry_get_ndn(e));
|
|
|
b161c9 |
+ /* n - rename e */
|
|
|
b161c9 |
+ entryrights |= SLAPI_ACL_MODDN;
|
|
|
b161c9 |
+ _append_gerstr(gerstr, gerstrsize, gerstrcap, "n", NULL);
|
|
|
b161c9 |
+ }
|
|
|
b161c9 |
+ } else {
|
|
|
b161c9 |
+ /*
|
|
|
b161c9 |
+ * Some limitation/simplification applied here:
|
|
|
b161c9 |
+ * - The modrdn right requires the rights to delete the old rdn and
|
|
|
b161c9 |
+ * the new one. However we have no knowledge of what the new rdn
|
|
|
b161c9 |
+ * is going to be.
|
|
|
b161c9 |
+ * - In multi-valued RDN case, we check the right on
|
|
|
b161c9 |
+ * the first rdn type only for now.
|
|
|
b161c9 |
+ */
|
|
|
b161c9 |
+ rdn = slapi_rdn_new_dn(slapi_entry_get_ndn(e));
|
|
|
b161c9 |
+ slapi_rdn_get_first(rdn, &rdntype, &rdnvalue);
|
|
|
b161c9 |
+ if (NULL != rdntype) {
|
|
|
b161c9 |
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
|
|
|
b161c9 |
+ "_ger_get_entry_rights: SLAPI_ACL_WRITE_DEL & _ADD %s\n", rdntype);
|
|
|
b161c9 |
+ if (acl_access_allowed(gerpb, e, rdntype, NULL,
|
|
|
b161c9 |
+ ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS &&
|
|
|
b161c9 |
+ acl_access_allowed(gerpb, e, rdntype, NULL,
|
|
|
b161c9 |
+ ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS) {
|
|
|
b161c9 |
/* n - rename e */
|
|
|
b161c9 |
- entryrights |= SLAPI_ACL_MODDN;
|
|
|
b161c9 |
- _append_gerstr(gerstr, gerstrsize, gerstrcap, "n", NULL);
|
|
|
b161c9 |
+ entryrights |= SLAPI_ACL_WRITE;
|
|
|
b161c9 |
+ _append_gerstr(gerstr, gerstrsize, gerstrcap, "n", NULL);
|
|
|
b161c9 |
+ }
|
|
|
b161c9 |
}
|
|
|
b161c9 |
+ slapi_rdn_free(&rdn;;
|
|
|
b161c9 |
+ }
|
|
|
b161c9 |
if ( entryrights == 0 )
|
|
|
b161c9 |
{
|
|
|
b161c9 |
_append_gerstr(gerstr, gerstrsize, gerstrcap, "none", NULL);
|
|
|
b161c9 |
--
|
|
|
b161c9 |
1.9.3
|
|
|
b161c9 |
|