This is an automated email from the git hooks/post-receive script.
firstyear pushed a change to branch master in repository 389-ds-base.
from 7b03c31 Ticket 49537 - allow asan to build with stable rustc new b086d41 Ticket 49425 - improve demo objects for install
The 1 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference.
Summary of changes: 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(-) create mode 100644 src/lib389/lib389/configurations/config_001004000.py create mode 100644 src/lib389/lib389/tests/configurations/config_001004000_test.py
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@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) +
389-commits@lists.fedoraproject.org