This is an automated email from the git hooks/post-receive script.
firstyear pushed a commit to branch master
in repository 389-ds-base.
commit b086d41607628635e242a8a80631dfe259fcc75f
Author: William Brown <firstyear(a)redhat.com>
Date: Wed Jan 10 10:04:39 2018 +1000
Ticket 49425 - improve demo objects for install
Bug Description: Improve demo objects for install
Fix Description: Change the tree a tiny bit - add hidden
389 container, add ous, use better aci examples. This also
adds a set of tests to assert the default aci's work as
advertised.
https://pagure.io/389-ds-base/issue/49425
Author: wibrown
Review by: mreynolds, spichugi, tbordaz, lkrispen (Thank you all!)
---
ldap/schema/30ns-common.ldif | 11 +-
src/lib389/lib389/cli_idm/user.py | 14 +-
src/lib389/lib389/configurations/__init__.py | 18 +-
.../lib389/configurations/config_001004000.py | 151 ++++++++++++++
src/lib389/lib389/idm/account.py | 21 +-
src/lib389/lib389/idm/user.py | 104 ++++++++--
src/lib389/lib389/tests/cli/idm_user_test.py | 5 +-
.../tests/configurations/config_001004000_test.py | 231 +++++++++++++++++++++
8 files changed, 518 insertions(+), 37 deletions(-)
diff --git a/ldap/schema/30ns-common.ldif b/ldap/schema/30ns-common.ldif
index 2b6edc1..80b8cf6 100644
--- a/ldap/schema/30ns-common.ldif
+++ b/ldap/schema/30ns-common.ldif
@@ -54,6 +54,8 @@ attributeTypes: ( nsLogSuppress-oid NAME 'nsLogSuppress' DESC
'Netscape defined
attributeTypes: ( nsJarfilename-oid NAME 'nsJarfilename' DESC 'Netscape
defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN
'Netscape' )
attributeTypes: ( nsClassname-oid NAME 'nsClassname' DESC 'Netscape defined
attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' )
attributeTypes: ( 2.16.840.1.113730.3.1.2337 NAME 'nsCertSubjectDN' DESC 'An
x509 DN from a certificate used to map during a TLS bind process' SYNTAX
1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN '389 Directory Server Project' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2342 NAME 'nsSshPublicKey' DESC 'An
nsSshPublicKey record' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN '389
Directory Server Project' )
+attributeTypes: ( 2.16.840.1.113730.3.1.2343 NAME 'legalName' DESC 'An
individuals legalName' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN '389 Directory Server
Project' )
objectClasses: ( nsAdminDomain-oid NAME 'nsAdminDomain' DESC 'Netscape
defined objectclass' SUP organizationalUnit MAY ( nsAdminDomainName ) X-ORIGIN
'Netscape' )
objectClasses: ( nsHost-oid NAME 'nsHost' DESC 'Netscape defined
objectclass' SUP top MUST ( cn ) MAY ( serverHostName $ description $ l $
nsHostLocation $ nsHardwarePlatform $ nsOsVersion ) X-ORIGIN 'Netscape' )
objectClasses: ( nsAdminGroup-oid NAME 'nsAdminGroup' DESC 'Netscape defined
objectclass' SUP top MUST ( cn ) MAY ( nsAdminGroupName $ description $ nsConfigRoot $
nsAdminSIEDN ) X-ORIGIN 'Netscape' )
@@ -65,4 +67,11 @@ objectClasses: ( nsAdminObject-oid NAME 'nsAdminObject' DESC
'Netscape defined o
objectClasses: ( nsConfig-oid NAME 'nsConfig' DESC 'Netscape defined
objectclass' SUP top MUST ( cn ) MAY ( description $ nsServerPort $ nsServerAddress $
nsSuiteSpotUser $ nsErrorLog $ nsPidLog $ nsAccessLog $ nsDefaultAcceptLanguage $
nsServerSecurity ) X-ORIGIN 'Netscape' )
objectClasses: ( nsDirectoryInfo-oid NAME 'nsDirectoryInfo' DESC 'Netscape
defined objectclass' SUP top MUST ( cn ) MAY ( nsBindDN $ nsBindPassword $
nsDirectoryURL $ nsDirectoryFailoverList $ nsDirectoryInfoRef ) X-ORIGIN
'Netscape' )
objectClasses: ( 2.16.840.1.113730.3.2.329 NAME 'nsMemberOf' DESC 'Allow
memberOf assignment on groups for nesting and users' SUP top AUXILIARY MAY ( memberOf
) X-ORIGIN '389 Directory Server Project' )
-objectClasses: ( 2.16.840.1.113730.3.2.331 NAME 'nsAccount' DESC 'A
representation of a user in a directory server' SUP top AUXILIARY MAY (
userCertificate $ nsCertSubjectDN ) X-ORIGIN '389 Directory Server Project' )
+objectClasses: ( 2.16.840.1.113730.3.2.331 NAME 'nsAccount' DESC 'A
representation of a binding user in a directory server' SUP top AUXILIARY MAY (
userCertificate $ nsCertSubjectDN $ nsSshPublicKey $ userPassword ) X-ORIGIN '389
Directory Server Project' )
+objectClasses: ( 2.16.840.1.113730.3.2.333 NAME 'nsPerson' DESC 'A
representation of a person in a directory server' SUP top STRUCTURAL MUST (
displayName $ cn ) MAY ( userPassword $ seeAlso $ description $ legalName $ mail $
preferredLanguage ) X-ORIGIN '389 Directory Server Project' )
+objectClasses: ( 2.16.840.1.113730.3.2.334 NAME 'nsOrgPerson' DESC 'A
representation of an org person in directory server. See also inetOrgPerson.' SUP top
AUXILIARY MAY (
+ businessCategory $ carLicense $ departmentNumber $ employeeNumber $ employeeType $
+ homePhone $ homePostalAddress $ initials $ jpegPhoto $ labeledURI $ manager $
+ mobile $ o $ pager $ photo $ roomNumber $ uid $ userCertificate $ telephoneNumber $
+ x500uniqueIdentifier $ userSMIMECertificate $ userPKCS12 )
+ X-ORIGIN '389 Directory Server Project' )
diff --git a/src/lib389/lib389/cli_idm/user.py b/src/lib389/lib389/cli_idm/user.py
index 70e7148..b2f71d9 100644
--- a/src/lib389/lib389/cli_idm/user.py
+++ b/src/lib389/lib389/cli_idm/user.py
@@ -7,7 +7,7 @@
# --- END COPYRIGHT BLOCK ---
import argparse
-from lib389.idm.user import UserAccount, UserAccounts, MUST_ATTRIBUTES
+from lib389.idm.user import nsUserAccount, nsUserAccounts
from lib389.cli_base import (
populate_attr_arguments,
@@ -22,8 +22,8 @@ from lib389.cli_base import (
_warn,
)
-SINGULAR = UserAccount
-MANY = UserAccounts
+SINGULAR = nsUserAccount
+MANY = nsUserAccounts
RDN = 'uid'
# These are a generic specification, try not to tamper with them
@@ -40,7 +40,7 @@ def get_dn(inst, basedn, log, args):
_generic_get_dn(inst, basedn, log.getChild('_generic_get_dn'), MANY, dn)
def create(inst, basedn, log, args):
- kwargs = _get_attributes(args, MUST_ATTRIBUTES)
+ kwargs = _get_attributes(args, SINGULAR._must_attributes)
_generic_create(inst, basedn, log.getChild('_generic_create'), MANY, kwargs)
def delete(inst, basedn, log, args, warn=True):
@@ -51,7 +51,7 @@ def delete(inst, basedn, log, args, warn=True):
def status(inst, basedn, log, args):
uid = _get_arg( args.uid, msg="Enter %s to check" % RDN)
- uas = UserAccounts(inst, basedn)
+ uas = MANY(inst, basedn)
acct = uas.get(uid)
acct_str = "locked: %s" % acct.is_locked()
log.info('uid: %s' % uid)
@@ -59,14 +59,14 @@ def status(inst, basedn, log, args):
def lock(inst, basedn, log, args):
uid = _get_arg( args.uid, msg="Enter %s to check" % RDN)
- accounts = UserAccounts(inst, basedn)
+ accounts = MANY(inst, basedn)
acct = accounts.get(uid)
acct.lock()
log.info('locked %s' % uid)
def unlock(inst, basedn, log, args):
uid = _get_arg( args.uid, msg="Enter %s to check" % RDN)
- accounts = UserAccounts(inst, basedn)
+ accounts = MANY(inst, basedn)
acct = accounts.get(uid)
acct.unlock()
log.info('unlocked %s' % uid)
diff --git a/src/lib389/lib389/configurations/__init__.py
b/src/lib389/lib389/configurations/__init__.py
index c7b9067..434147a 100644
--- a/src/lib389/lib389/configurations/__init__.py
+++ b/src/lib389/lib389/configurations/__init__.py
@@ -6,20 +6,30 @@
# See LICENSE for details.
# --- END COPYRIGHT BLOCK ---
+from lib389.utils import ds_is_newer
from lib389._constants import INSTALL_LATEST_CONFIG
from .config_001003006 import c001003006, c001003006_sample_entries
+from .config_001004000 import c001004000, c001004000_sample_entries
def get_config(version):
- if (version == INSTALL_LATEST_CONFIG):
+ # We do this to avoid test breaking on older version that may
+ # not expect the new default layout.
+ if (version == INSTALL_LATEST_CONFIG and ds_is_newer('1.4.0')):
+ return c001004000
+ elif (version == INSTALL_LATEST_CONFIG):
return c001003006
- if (version == '001003006'):
+ elif (version == '001004000' and ds_is_newer('1.4.0')):
+ return c001004000
+ elif (version == '001003006'):
return c001003006
raise Exception('version %s no match' % version)
def get_sample_entries(version):
if (version == INSTALL_LATEST_CONFIG):
- return c001003006_sample_entries
- if (version == '001003006'):
+ return c001004000_sample_entries
+ elif (version == '001004000'):
+ return c001004000_sample_entries
+ elif (version == '001003006'):
return c001003006_sample_entries
raise Exception('version %s no match' % version)
diff --git a/src/lib389/lib389/configurations/config_001004000.py
b/src/lib389/lib389/configurations/config_001004000.py
new file mode 100644
index 0000000..36b89e9
--- /dev/null
+++ b/src/lib389/lib389/configurations/config_001004000.py
@@ -0,0 +1,151 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+
+from ldap import dn
+
+from .config import baseconfig, configoperation
+from .sample import sampleentries
+
+from lib389.idm.domain import Domain
+from lib389.idm.organisationalunit import OrganisationalUnits
+from lib389.idm.group import Groups
+from lib389.idm.posixgroup import PosixGroups
+from lib389.idm.user import nsUserAccounts
+from lib389.idm.services import ServiceAccounts
+
+from lib389.idm.nscontainer import nsHiddenContainers
+
+class c001004000_sample_entries(sampleentries):
+ def __init__(self, instance, basedn):
+ super(c001004000_sample_entries, self).__init__(instance, basedn)
+ self.description = """Apply sample entries matching the 1.4.0
sample data and access controls"""
+
+ # All checks done, apply!
+ def _apply(self):
+ # Create the base domain object
+ domain = Domain(self._instance, dn=self._basedn)
+ # Explode the dn to get the first bit.
+ avas = dn.str2dn(self._basedn)
+ dc_ava = avas[0][0][1]
+
+ domain.create(properties={
+ # I think in python 2 this forces unicode return ...
+ 'dc': dc_ava,
+ 'description': self._basedn,
+ 'aci': [
+ # Allow reading the base domain object
+ '(targetattr="dc || description ||
objectClass")(targetfilter="(objectClass=domain)")(version 3.0; acl
"Enable anyone domain read"; allow (read, search,
compare)(userdn="ldap:///anyone");)',
+ # Allow reading the ou
+ '(targetattr="ou ||
objectClass")(targetfilter="(objectClass=organizationalUnit)")(version 3.0;
acl "Enable anyone ou read"; allow (read, search,
compare)(userdn="ldap:///anyone");)'
+ ]
+ })
+
+ # Create the 389 service container
+ # This could also move to be part of core later ....
+ hidden_containers = nsHiddenContainers(self._instance, self._basedn)
+ ns389container = hidden_containers.create(properties={
+ 'cn': '389_ds_system'
+ })
+
+ # Create our ous.
+ ous = OrganisationalUnits(self._instance, self._basedn)
+ ous.create(properties = {
+ 'ou': 'groups',
+ 'aci': [
+ # Allow anon partial read
+ '(targetattr="cn || member || gidNumber || nsUniqueId ||
description ||
objectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl
"Enable anyone group read"; allow (read, search,
compare)(userdn="ldap:///anyone");)',
+ # Allow group_modify to modify but not create groups
+
'(targetattr="member")(targetfilter="(objectClass=groupOfNames)")(version
3.0; acl "Enable group_modify to alter members"; allow
(write)(groupdn="ldap:///cn=group_modify,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn),
+ # Allow group_admin to fully manage groups (posix or not).
+ '(targetattr="cn || member || gidNumber || description ||
objectClass")(targetfilter="(objectClass=groupOfNames)")(version 3.0; acl
"Enable group_admin to manage groups"; allow (write, add,
delete)(groupdn="ldap:///cn=group_admin,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn),
+ ]
+ })
+
+ ous.create(properties = {
+ 'ou': 'people',
+ 'aci': [
+ # allow anon partial read.
+ '(targetattr="objectClass || description || nsUniqueId || uid ||
displayName || loginShell || uidNumber || gidNumber || gecos || homeDirectory || cn ||
memberOf || mail || nsSshPublicKey || nsAccountLock ||
userCertificate")(targetfilter="(objectClass=posixaccount)")(version 3.0;
acl "Enable anyone user read"; allow (read, search,
compare)(userdn="ldap:///anyone");)',
+ # allow self partial mod
+ '(targetattr="displayName || nsSshPublicKey")(version 3.0;
acl "Enable self partial modify"; allow
(write)(userdn="ldap:///self");)',
+ # Allow self full read
+ '(targetattr="legalName || telephoneNumber ||
mobile")(targetfilter="(objectClass=nsPerson)")(version 3.0; acl
"Enable self legalname read"; allow (read, search,
compare)(userdn="ldap:///self");)',
+ # Allow reading legal name
+ '(targetattr="legalName ||
telephoneNumber")(targetfilter="(objectClass=nsPerson)")(version 3.0; acl
"Enable user legalname read"; allow (read, search,
compare)(groupdn="ldap:///cn=user_private_read,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn),
+ # These below need READ so they can read userPassword and legalName
+ # Allow user admin create mod
+ '(targetattr="uid || description || displayName || loginShell ||
uidNumber || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalName ||
telephoneNumber ||
mobile")(targetfilter="(&(objectClass=nsPerson)(objectClass=nsAccount))")(version
3.0; acl "Enable user admin create"; allow (write, add, delete,
read)(groupdn="ldap:///cn=user_admin,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn),
+ # Allow user mod mod only
+ '(targetattr="uid || description || displayName || loginShell ||
uidNumber || gidNumber || gecos || homeDirectory || cn || memberOf || mail || legalName ||
telephoneNumber ||
mobile")(targetfilter="(&(objectClass=nsPerson)(objectClass=nsAccount))")(version
3.0; acl "Enable user modify to change users"; allow (write,
read)(groupdn="ldap:///cn=user_modify,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn),
+ # Allow user_pw_admin to nsaccountlock and password
+ '(targetattr="userPassword || nsAccountLock || userCertificate
|| nsSshPublicKey")(targetfilter="(objectClass=nsAccount)")(version 3.0;
acl "Enable user password reset"; allow (write,
read)(groupdn="ldap:///cn=user_passwd_reset,ou=permissions,{BASEDN}");)'.format(BASEDN=self._basedn),
+ ]
+ })
+
+ ous.create(properties = {
+ 'ou': 'permissions',
+ })
+
+ ous.create(properties = {
+ 'ou': 'services',
+ 'aci': [
+ # Minimal service read
+ '(targetattr="objectClass || description || nsUniqueId || cn ||
memberOf || nsAccountLock
")(targetfilter="(objectClass=netscapeServer)")(version 3.0; acl
"Enable anyone service account read"; allow (read, search,
compare)(userdn="ldap:///anyone");)',
+ ]
+ })
+
+ # Create the demo user
+ users = nsUserAccounts(self._instance, self._basedn)
+ users.create(properties={
+ 'uid': 'demo_user',
+ 'cn': 'Demo User',
+ 'displayName': 'Demo User',
+ 'legalName': 'Demo User Name',
+ 'uidNumber': '99998',
+ 'gidNumber': '99998',
+ 'homeDirectory': '/var/empty',
+ 'loginShell': '/bin/false',
+ 'nsAccountlock': 'true'
+ })
+
+ # Create the demo group
+ groups = PosixGroups(self._instance, self._basedn)
+ groups.create(properties={
+ 'cn' : 'demo_group',
+ 'gidNumber': '99999'
+ })
+
+ # Create the permission groups required for the acis
+ permissions = Groups(self._instance, self._basedn, rdn='ou=permissions')
+ permissions.create(properties={
+ 'cn': 'group_admin',
+ })
+ permissions.create(properties={
+ 'cn': 'group_modify',
+ })
+ permissions.create(properties={
+ 'cn': 'user_admin',
+ })
+ permissions.create(properties={
+ 'cn': 'user_modify',
+ })
+ permissions.create(properties={
+ 'cn': 'user_passwd_reset',
+ })
+ permissions.create(properties={
+ 'cn': 'user_private_read',
+ })
+
+
+class c001004000(baseconfig):
+ def __init__(self, instance):
+ super(c001004000, self).__init__(instance)
+ self._operations = [
+ # For now this is an empty place holder - likely this
+ # will become part of core server.
+ ]
diff --git a/src/lib389/lib389/idm/account.py b/src/lib389/lib389/idm/account.py
index 92038bf..100bfec 100644
--- a/src/lib389/lib389/idm/account.py
+++ b/src/lib389/lib389/idm/account.py
@@ -93,6 +93,22 @@ class Account(DSLdapObject):
inst_clone.open(saslmethod='gssapi')
return inst_clone
+ def enroll_certificate(self, der_path):
+ """Enroll a certificate for certmap verification. Because of the
userCertificate
+ attribute, we have to do this on userAccount which has support for it.
+
+ :param der_path: the certificate file in DER format to include.
+ :type der_path: str
+ """
+ if ds_is_older('1.4.0'):
+ raise Exception("This version of DS does not support nsAccount")
+ # Given a cert path, add this to the object as a userCertificate
+ crt = None
+ with open(der_path, 'rb') as f:
+ crt = f.read()
+ self.add('usercertificate;binary', crt)
+
+
class Accounts(DSLdapObjects):
"""DSLdapObjects that represents Account entry
@@ -106,9 +122,12 @@ class Accounts(DSLdapObjects):
super(Accounts, self).__init__(instance)
# These are all the objects capable of holding a password.
self._objectclasses = [
+ 'nsAccount',
+ 'nsPerson',
'simpleSecurityObject',
'organization',
- 'personperson',
+ 'person',
+ 'account',
'organizationalUnit',
'netscapeServer',
'domain',
diff --git a/src/lib389/lib389/idm/user.py b/src/lib389/lib389/idm/user.py
index c4d7928..38977a5 100644
--- a/src/lib389/lib389/idm/user.py
+++ b/src/lib389/lib389/idm/user.py
@@ -30,10 +30,89 @@ TEST_USER_PROPERTIES = {
'homeDirectory' : '/home/testuser'
}
+#### Modern userAccounts
+
+class nsUserAccount(Account):
+ _must_attributes = [
+ 'uid',
+ 'cn',
+ 'displayName',
+ 'uidNumber',
+ 'gidNumber',
+ 'homeDirectory',
+ ]
+
+ """A single instance of an nsPerson, capable of posix login,
certificate
+ authentication, sshkey distribution, and more.
+
+ This is the modern and correct userAccount type to choose for DS 1.4.0 and above.
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param dn: Entry DN
+ :type dn: str
+ """
+ def __init__(self, instance, dn=None):
+ if ds_is_older('1.4.0'):
+ raise Exception("Not supported")
+ super(nsUserAccount, self).__init__(instance, dn)
+ self._rdn_attribute = RDN
+ self._must_attributes = nsUserAccount._must_attributes
+ # Can I generate these from schema?
+ self._create_objectclasses = [
+ 'top',
+ 'nsPerson',
+ 'nsAccount',
+ 'nsOrgPerson',
+ 'posixAccount',
+ ]
+ user_compare_exclude = [
+ 'nsUniqueId',
+ 'modifyTimestamp',
+ 'createTimestamp',
+ 'entrydn'
+ ]
+ self._compare_exclude = self._compare_exclude + user_compare_exclude
+ self._protected = False
+
+class nsUserAccounts(DSLdapObjects):
+ """DSLdapObjects that represents all nsUserAccount entries in suffix.
+ By default it uses 'ou=People' as rdn.
+
+ This is the modern and correct userAccount type to choose for DS 1.4.0 and above.
+
+ :param instance: An instance
+ :type instance: lib389.DirSrv
+ :param basedn: Suffix DN
+ :type basedn: str
+ :param rdn: The DN that will be combined wit basedn
+ :type rdn: str
+ """
+
+ def __init__(self, instance, basedn, rdn='ou=people'):
+ super(nsUserAccounts, self).__init__(instance)
+ self._objectclasses = [
+ 'nsPerson',
+ 'nsAccount',
+ 'nsOrgPerson',
+ 'posixAccount',
+ ]
+ self._filterattrs = [RDN, 'displayName', 'cn']
+ self._childobject = nsUserAccount
+ if rdn is None:
+ self._basedn = basedn
+ else:
+ self._basedn = '{},{}'.format(rdn, basedn)
+
+
+#### Traditional style userAccounts.
class UserAccount(Account):
"""A single instance of User Account entry
+ This is the classic "user account" style of cn + sn. You should consider
+ nsUserAccount instead.
+
:param instance: An instance
:type instance: lib389.DirSrv
:param dn: Entry DN
@@ -49,12 +128,8 @@ class UserAccount(Account):
'top',
'account',
'posixaccount',
- # inetOrgPerson allows userCertificate
'inetOrgPerson',
'organizationalPerson',
- # This may not always work at sites?
- # Can we get this into core?
- # 'ldapPublicKey',
]
if ds_is_older('1.3.7'):
self._create_objectclasses.append('inetUser')
@@ -77,29 +152,14 @@ class UserAccount(Account):
return super(UserAccount, self)._validate(rdn, properties, basedn)
- def enroll_certificate(self, der_path):
- """Enroll a certificate for certmap verification. Because of the
userCertificate
- attribute, we have to do this on userAccount which has support for it.
-
- :param der_path: the certificate file in DER format to include.
- :type der_path: str
- """
- if ds_is_older('1.4.0'):
- raise Exception("This version of DS does not support nsAccount")
- # Given a cert path, add this to the object as a userCertificate
- crt = None
- with open(der_path, 'rb') as f:
- crt = f.read()
- self.add('usercertificate;binary', crt)
-
- # Add a set password function....
- # Can't I actually just set, and it will hash?
-
class UserAccounts(DSLdapObjects):
"""DSLdapObjects that represents all User Account entries in suffix.
By default it uses 'ou=People' as rdn.
+ This is the classic "user account" style of cn + sn. You should consider
+ nsUserAccounts instead.
+
:param instance: An instance
:type instance: lib389.DirSrv
:param basedn: Suffix DN
diff --git a/src/lib389/lib389/tests/cli/idm_user_test.py
b/src/lib389/lib389/tests/cli/idm_user_test.py
index da579b4..537e12d 100644
--- a/src/lib389/lib389/tests/cli/idm_user_test.py
+++ b/src/lib389/lib389/tests/cli/idm_user_test.py
@@ -44,9 +44,10 @@ def test_user_tasks(topology):
# Create the user
topology.logcap.flush()
- u_args.cn = 'testuser'
u_args.uid = 'testuser'
- u_args.sn = 'testuser'
+ # u_args.sn = 'testuser'
+ u_args.cn = 'Test User'
+ u_args.displayName = 'Test User'
u_args.homeDirectory = '/home/testuser'
u_args.uidNumber = '5000'
u_args.gidNumber = '5000'
diff --git a/src/lib389/lib389/tests/configurations/config_001004000_test.py
b/src/lib389/lib389/tests/configurations/config_001004000_test.py
new file mode 100644
index 0000000..0cb31ab
--- /dev/null
+++ b/src/lib389/lib389/tests/configurations/config_001004000_test.py
@@ -0,0 +1,231 @@
+# --- BEGIN COPYRIGHT BLOCK ---
+# Copyright (C) 2017 Red Hat, Inc.
+# All rights reserved.
+#
+# License: GPL (version 3 or any later version).
+# See LICENSE for details.
+# --- END COPYRIGHT BLOCK ---
+#
+import ldap
+
+import logging
+import sys
+import time
+
+import pytest
+
+from lib389 import DirSrv
+from lib389._constants import *
+from lib389.properties import *
+
+from lib389.topologies import topology_st
+
+from lib389.idm.user import nsUserAccounts
+from lib389.idm.posixgroup import PosixGroups
+from lib389.idm.group import Groups
+
+from lib389.utils import ds_is_older
+pytestmark = pytest.mark.skipif(ds_is_older('1.4.0'), reason="Not
implemented")
+
+REQUIRED_DNS = [
+ 'dc=example,dc=com',
+ 'ou=groups,dc=example,dc=com',
+ 'ou=people,dc=example,dc=com',
+ 'ou=services,dc=example,dc=com',
+ 'ou=permissions,dc=example,dc=com',
+ 'uid=demo_user,ou=people,dc=example,dc=com',
+ 'cn=demo_group,ou=groups,dc=example,dc=com',
+ 'cn=group_admin,ou=permissions,dc=example,dc=com',
+ 'cn=group_modify,ou=permissions,dc=example,dc=com',
+ 'cn=user_admin,ou=permissions,dc=example,dc=com',
+ 'cn=user_modify,ou=permissions,dc=example,dc=com',
+ 'cn=user_passwd_reset,ou=permissions,dc=example,dc=com',
+ 'cn=user_private_read,ou=permissions,dc=example,dc=com',
+]
+
+def test_install_sample_entries(topology_st):
+ """Assert that our entries match."""
+
+ entries = topology_st.standalone.search_s(DEFAULT_SUFFIX, ldap.SCOPE_SUBTREE)
+
+ for entry in entries:
+ assert(entry.dn in REQUIRED_DNS)
+ # We can make this assert the full object content, plugins and more later.
+
+def test_install_aci(topology_st):
+ """Assert our default aci's work as expected."""
+
+ # Create some users and groups.
+ users = nsUserAccounts(topology_st.standalone, DEFAULT_SUFFIX)
+ groups = PosixGroups(topology_st.standalone, DEFAULT_SUFFIX)
+
+ user_basic = users.create(properties={
+ 'uid': 'basic',
+ 'cn': 'Basic',
+ 'displayName': 'Basic',
+ 'uidNumber': '100000',
+ 'gidNumber': '100000',
+ 'homeDirectory': '/home/basic',
+ 'userPassword': 'password',
+ 'legalName': 'Super Secret PII',
+ })
+
+ user_modify = users.create(properties={
+ 'uid': 'modify',
+ 'cn': 'Modify',
+ 'displayName': 'Modify',
+ 'uidNumber': '100001',
+ 'gidNumber': '100001',
+ 'homeDirectory': '/home/modify',
+ 'userPassword': 'password',
+ 'legalName': 'Super Secret PII',
+ })
+
+ user_admin = users.create(properties={
+ 'uid': 'admin',
+ 'cn': 'Admin',
+ 'displayName': 'Admin',
+ 'uidNumber': '100002',
+ 'gidNumber': '100002',
+ 'homeDirectory': '/home/admin',
+ 'userPassword': 'password',
+ 'legalName': 'Super Secret PII',
+ })
+
+ user_pw_reset = users.create(properties={
+ 'uid': 'pw_reset',
+ 'cn': 'Password Reset',
+ 'displayName': 'Password Reset',
+ 'uidNumber': '100003',
+ 'gidNumber': '100003',
+ 'homeDirectory': '/home/pw_reset',
+ 'userPassword': 'password',
+ 'legalName': 'Super Secret PII',
+ })
+
+ # Add users to various permissions.
+
+ permissions = Groups(topology_st.standalone, DEFAULT_SUFFIX,
rdn='ou=permissions')
+
+ g_group_admin = permissions.get('group_admin')
+ g_group_modify = permissions.get('group_modify')
+ g_user_admin = permissions.get('user_admin')
+ g_user_modify = permissions.get('user_modify')
+ g_user_pw_reset = permissions.get('user_passwd_reset')
+
+ g_group_admin.add_member(user_admin.dn)
+ g_user_admin.add_member(user_admin.dn)
+
+ g_group_modify.add_member(user_modify.dn)
+ g_user_modify.add_member(user_modify.dn)
+
+ g_user_pw_reset.add_member(user_pw_reset.dn)
+
+ # Bind as a user and assert what we can and can not see
+ c_user_basic = user_basic.bind(password='password')
+ c_user_modify = user_modify.bind(password='password')
+ c_user_admin = user_admin.bind(password='password')
+ c_user_pw_reset = user_pw_reset.bind(password='password')
+
+ c_user_basic_users = nsUserAccounts(c_user_basic, DEFAULT_SUFFIX)
+ c_user_pw_reset_users = nsUserAccounts(c_user_pw_reset, DEFAULT_SUFFIX)
+ c_user_modify_users = nsUserAccounts(c_user_modify, DEFAULT_SUFFIX)
+ c_user_admin_users = nsUserAccounts(c_user_admin, DEFAULT_SUFFIX)
+
+ # Should be able to see users, but not their legalNames
+ user_basic_view_demo_user = c_user_basic_users.get('demo_user')
+ assert user_basic_view_demo_user.get_attr_val_utf8('legalName') is None
+ assert user_basic_view_demo_user.get_attr_val_utf8('uid') ==
'demo_user'
+
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_basic_view_demo_user.replace('description', 'change value')
+
+ user_pw_reset_view_demo_user = c_user_pw_reset_users.get('demo_user')
+ assert user_pw_reset_view_demo_user.get_attr_val_utf8('legalName') is None
+ assert user_pw_reset_view_demo_user.get_attr_val_utf8('uid') ==
'demo_user'
+
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_pw_reset_view_demo_user.replace('description', 'change
value')
+
+ # The user admin and modify should be able to read it.
+ user_modify_view_demo_user = c_user_modify_users.get('demo_user')
+ assert user_modify_view_demo_user.get_attr_val_utf8('legalName') == 'Demo
User Name'
+ assert user_modify_view_demo_user.get_attr_val_utf8('uid') ==
'demo_user'
+ user_modify_view_demo_user.replace('description', 'change value')
+
+ user_admin_view_demo_user = c_user_admin_users.get('demo_user')
+ assert user_admin_view_demo_user.get_attr_val_utf8('legalName') == 'Demo
User Name'
+ assert user_admin_view_demo_user.get_attr_val_utf8('uid') ==
'demo_user'
+ user_admin_view_demo_user.replace('description', 'change value')
+
+ # Assert only admin can create:
+
+ test_user_properties = {
+ 'uid': 'test_user',
+ 'cn': 'Test User',
+ 'displayName': 'Test User',
+ 'uidNumber': '100005',
+ 'gidNumber': '100005',
+ 'homeDirectory': '/home/test_user',
+ 'legalName': 'Super Secret PII',
+ }
+
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ c_user_basic_users.create(properties=test_user_properties)
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ c_user_pw_reset_users.create(properties=test_user_properties)
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ c_user_modify_users.create(properties=test_user_properties)
+
+ test_user = c_user_admin_users.create(properties=test_user_properties)
+ test_user.delete()
+
+ # Assert on pw_reset can unlock/pw
+
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_basic_view_demo_user.lock()
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_modify_view_demo_user.lock()
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_admin_view_demo_user.lock()
+ user_pw_reset_view_demo_user.lock()
+
+ # Group test
+ c_user_basic_groups = PosixGroups(c_user_basic, DEFAULT_SUFFIX)
+ c_user_pw_reset_groups = PosixGroups(c_user_pw_reset, DEFAULT_SUFFIX)
+ c_user_modify_groups = PosixGroups(c_user_modify, DEFAULT_SUFFIX)
+ c_user_admin_groups = PosixGroups(c_user_admin, DEFAULT_SUFFIX)
+
+ # Assert that members can be read, but only modify/admin can edit.
+ user_basic_view_demo_group = c_user_basic_groups.get('demo_group')
+ assert user_basic_view_demo_group.get_attr_val_utf8('cn') ==
'demo_group'
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_basic_view_demo_group.add_member(user_basic.dn)
+
+ user_pw_reset_view_demo_group = c_user_pw_reset_groups.get('demo_group')
+ assert user_pw_reset_view_demo_group.get_attr_val_utf8('cn') ==
'demo_group'
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ user_pw_reset_view_demo_group.add_member(user_pw_reset.dn)
+
+ user_modify_view_demo_group = c_user_modify_groups.get('demo_group')
+ assert user_modify_view_demo_group.get_attr_val_utf8('cn') ==
'demo_group'
+ user_modify_view_demo_group.add_member(user_modify.dn)
+
+ user_admin_view_demo_group = c_user_admin_groups.get('demo_group')
+ assert user_admin_view_demo_group.get_attr_val_utf8('cn') ==
'demo_group'
+ user_admin_view_demo_group.add_member(user_admin.dn)
+
+ # Assert that only admin can add new group.
+ group_properties = {
+ 'cn': 'test_group',
+ 'gidNumber': '100009'
+ }
+
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ c_user_basic_groups.create(properties=group_properties)
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ c_user_pw_reset_groups.create(properties=group_properties)
+ with pytest.raises(ldap.INSUFFICIENT_ACCESS):
+ c_user_modify_groups.create(properties=group_properties)
+ c_user_admin_groups.create(properties=group_properties)
+
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.