зеркало из https://github.com/mozilla/mozillians.git
1484 строки
51 KiB
Python
1484 строки
51 KiB
Python
"""Tests for access control and other rules in LDAP
|
|
|
|
Most of these focus on ACLs, but some rules are implemented in other ways.
|
|
All get tested here as they all contribute to the overall security policy.
|
|
|
|
The actual tests all have names like test_T1234_mozillian_do_something
|
|
where the T1234 bit is a cross-reference to the LDAP design document.
|
|
There may be several tests with the same cross-ref.
|
|
|
|
Andrew Findlay
|
|
21 June 2011
|
|
"""
|
|
|
|
import sys
|
|
import unittest
|
|
import re
|
|
import ldap
|
|
import ldap.modlist
|
|
from ldif import LDIFParser
|
|
|
|
########################################################################
|
|
# Configuration
|
|
########################################################################
|
|
|
|
ldap_url = 'ldap://localhost:1389/'
|
|
ldap_suffix = 'dc=mozillians,dc=org'
|
|
|
|
people_node = 'ou=people,dc=mozillians,dc=org'
|
|
|
|
# Credentials for the all-powerful user
|
|
# (Don't put the password for your production server here!)
|
|
ldap_rootDN = 'cn=root,dc=mozillians,dc=org'
|
|
ldap_rootPW = 'secret'
|
|
|
|
# Credentials for an existing non-vouched Applicant
|
|
ldap_applicant001DN = 'uniqueIdentifier=test001,ou=people,dc=mozillians,dc=org'
|
|
ldap_applicant001PW = 'secret'
|
|
|
|
# Credentials for an existing vouched Mozillian
|
|
ldap_mozillian011DN = 'uniqueIdentifier=test011,ou=people,dc=mozillians,dc=org'
|
|
ldap_mozillian011PW = 'secret'
|
|
|
|
# Credentials for a system account
|
|
ldap_sys999DN = 'uid=test999,ou=accounts,ou=system,dc=mozillians,dc=org'
|
|
ldap_sys999PW = 'secret'
|
|
|
|
# DNs of some victims
|
|
ldap_applicant002DN = 'uniqueIdentifier=test002,ou=people,dc=mozillians,dc=org'
|
|
ldap_mozillian012DN = 'uniqueIdentifier=test012,ou=people,dc=mozillians,dc=org'
|
|
ldap_newuserDN = 'uniqueIdentifier=testnew,ou=people,dc=mozillians,dc=org'
|
|
ldap_sys900DN = 'uid=test900,ou=accounts,ou=system,dc=mozillians,dc=org'
|
|
|
|
# The root of the system part of the DIT
|
|
system_suffix = 'ou=system,dc=mozillians,dc=org'
|
|
|
|
# Where the LDAP server keeps its monitoring stats
|
|
monitor_suffix = 'cn=Monitor'
|
|
# ...and the group you have to be in if you want to see that data
|
|
monitor_group = 'cn=monitors,ou=groups,ou=system,dc=mozillians,dc=org'
|
|
|
|
# Group of accounts that may replicate the entire DIT
|
|
replicator_group = 'cn=replicators,ou=groups,ou=system,dc=mozillians,dc=org'
|
|
|
|
# Group of accounts that can manage user and group entries
|
|
admin_group = 'cn=LDAPAdmins,ou=groups,ou=system,dc=mozillians,dc=org'
|
|
|
|
# Group of accounts that can register new users
|
|
regAgent_group = 'cn=registrationAgents,ou=groups,ou=system,dc=mozillians,dc=org'
|
|
|
|
# The name of the setup file
|
|
setup_ldif = 'setup.ldif'
|
|
|
|
########################################################################
|
|
# Globals
|
|
########################################################################
|
|
|
|
# Keep a list of entries that need deleting in tearDown
|
|
global entry_list
|
|
entry_list = []
|
|
|
|
########################################################################
|
|
# Utility functions
|
|
########################################################################
|
|
|
|
# Get list of attributes in result
|
|
#
|
|
def getAttrNames ( ldap_result ):
|
|
# ldap_result has the form:
|
|
# (dn, dict)
|
|
# where dict has a list of values for each named attribute
|
|
|
|
return ldap_result[1].keys()
|
|
|
|
# Get list of values for attribute
|
|
# If there is no such attribute then we return an empty list
|
|
#
|
|
def getAttrValueList( ldap_result, attrname ):
|
|
# ldap_result has the form:
|
|
# (dn, dict)
|
|
# where dict has a list of values for each named attribute
|
|
|
|
# First problem: dictionary keys are case-sensitive.
|
|
# Attribute names are not.
|
|
myattr = None
|
|
for k in ldap_result[1].keys():
|
|
if k.lower() == attrname.lower():
|
|
myattr = k
|
|
return ldap_result[1].get(myattr,[])
|
|
|
|
# Get the first value of an attribute
|
|
# If there is no such attribute then we return None
|
|
#
|
|
def getAttrValue( ldap_result, attrname ):
|
|
# ldap_result has the form:
|
|
# (dn, dict)
|
|
# where dict has a list of values for each named attribute
|
|
attr_list = getAttrValueList( ldap_result, attrname )
|
|
if not attr_list:
|
|
return None
|
|
else:
|
|
return attr_list[0]
|
|
|
|
# Check for a value of an attribute that matches a given pattern
|
|
#
|
|
def attrValueMatch( ldap_result, attrname, pattern ):
|
|
attr_list = getAttrValueList( ldap_result, attrname )
|
|
if not attr_list:
|
|
return None
|
|
else:
|
|
for val in attr_list:
|
|
# print "TESTING:", val
|
|
if re.search(pattern,val):
|
|
return True
|
|
return False
|
|
|
|
# Load an LDIF file
|
|
# Used for setting up test cases
|
|
#
|
|
class LdifLoader(LDIFParser):
|
|
def handle(self,dn,entry):
|
|
# print dn
|
|
# Try to delete the entry before adding it, just in case...
|
|
try:
|
|
self.ldap_handle.delete_s(dn)
|
|
except ldap.NO_SUCH_OBJECT:
|
|
pass
|
|
# We need to delete this later
|
|
entry_list.append(dn)
|
|
# Add the entry
|
|
self.ldap_handle.add_s(dn,ldap.modlist.addModlist(entry))
|
|
|
|
|
|
########################################################################
|
|
# Common test-fixture code
|
|
########################################################################
|
|
|
|
def setUpCommon(self):
|
|
# Set up the connections, and by doing so implement test_T0005_anon_bind
|
|
try:
|
|
self.ldap_anon = ldap.initialize(ldap_url)
|
|
|
|
self.ldap_rootDN = ldap.initialize(ldap_url)
|
|
self.ldap_rootDN.simple_bind_s(ldap_rootDN,ldap_rootPW)
|
|
|
|
ldifparser = LdifLoader(open(setup_ldif, 'r'), None)
|
|
ldifparser.ldap_handle = self.ldap_rootDN
|
|
ldifparser.parse()
|
|
|
|
self.ldap_applicant001 = ldap.initialize(ldap_url)
|
|
self.ldap_applicant001.simple_bind_s(ldap_applicant001DN,ldap_applicant001PW)
|
|
|
|
self.ldap_mozillian011 = ldap.initialize(ldap_url)
|
|
self.ldap_mozillian011.simple_bind_s(ldap_mozillian011DN,ldap_mozillian011PW)
|
|
|
|
self.ldap_sys999 = ldap.initialize(ldap_url)
|
|
self.ldap_sys999.simple_bind_s(ldap_sys999DN,ldap_sys999PW)
|
|
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP connection setup error " + str(sys.exc_info()[0]) )
|
|
|
|
|
|
def tearDownCommon(self):
|
|
global entry_list
|
|
|
|
# We delete the longest DNs first to avoid trying to delete
|
|
# non-leaf objects
|
|
for dn in sorted(entry_list, key=len, reverse=True):
|
|
# print dn
|
|
# It is OK to ignore an error if we are deleting something that
|
|
# has gone already
|
|
try:
|
|
self.ldap_rootDN.delete_s(dn)
|
|
except ldap.NO_SUCH_OBJECT:
|
|
pass
|
|
|
|
# We have deleted everything that was on the list
|
|
entry_list = []
|
|
|
|
self.ldap_anon.unbind()
|
|
self.ldap_rootDN.unbind()
|
|
self.ldap_applicant001.unbind()
|
|
self.ldap_mozillian011.unbind()
|
|
self.ldap_sys999.unbind()
|
|
|
|
|
|
# Changing password
|
|
def change_and_check_password(self, user, userDN, ldap_conn, old_pw, new_pw):
|
|
try:
|
|
ldap_conn.passwd_s(userDN, old_pw, new_pw)
|
|
except:
|
|
self.fail( user + " cannot change password: " + str(sys.exc_info()[0]) )
|
|
|
|
try:
|
|
ldap_check = ldap.initialize(ldap_url)
|
|
ldap_check.simple_bind_s(userDN, new_pw)
|
|
except:
|
|
self.fail( user + " cannot bind with new password: " + str(sys.exc_info()[0]) )
|
|
|
|
ldap_check.unbind()
|
|
|
|
########################################################################
|
|
# Tests
|
|
########################################################################
|
|
|
|
class LdapUserTests(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
setUpCommon(self)
|
|
|
|
def tearDown(self):
|
|
tearDownCommon(self)
|
|
|
|
# All users including Anon should be able to read the schema
|
|
def read_root_DSE_and_schema(self, user, ldap_conn):
|
|
# Read subschemaSubentry from the null DN
|
|
try:
|
|
res = ldap_conn.search_s(
|
|
'',
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['subschemaSubentry'] )
|
|
|
|
# We only expect one value here
|
|
schema_ptr = getAttrValue(res[0],'subschemaSubentry')
|
|
if schema_ptr:
|
|
self.assertRegexpMatches(
|
|
schema_ptr, '^[a-zA-Z]+=',
|
|
'root DSE should contain a valid schema pointer')
|
|
else:
|
|
self.fail( user + " did not get a subschemaSubentry attribute from the root DSE" )
|
|
|
|
except ldap.LDAPError:
|
|
self.fail( user + " cannot read root DSE " + str(sys.exc_info()[0]) )
|
|
|
|
# Now check that we can read the schema
|
|
try:
|
|
res = ldap_conn.search_s(
|
|
schema_ptr,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['attributetypes','objectclasses'])
|
|
except ldap.LDAPError:
|
|
self.fail( user + " cannot read schema subentry " + schema_ptr + " " + str(sys.exc_info()[0]) )
|
|
|
|
# Did we get any Mozillians attributes?
|
|
self.assertTrue( attrValueMatch( res[0], 'attributetypes', 'mozillians' ),
|
|
"Mozillians attribute types must be listed in schema" )
|
|
|
|
|
|
# Changing own user attributes
|
|
def change_user_attributes(self, user, userDN, ldap_conn):
|
|
try:
|
|
ldap_conn.modify_s(
|
|
userDN,
|
|
[
|
|
(ldap.MOD_REPLACE,'cn','modified CN'),
|
|
(ldap.MOD_REPLACE,'sn','modified SN'),
|
|
(ldap.MOD_REPLACE,'displayName','modified displayName'),
|
|
(ldap.MOD_REPLACE,'mail',['new@mail.one','new@mail.two']),
|
|
(ldap.MOD_REPLACE,'uid','modified UID'),
|
|
(ldap.MOD_REPLACE,'telephoneNumber',['+1 234','+5-678-90']),
|
|
(ldap.MOD_REPLACE,'description','modified description'),
|
|
(ldap.MOD_REPLACE,'jpegPhoto','modified jpegPhoto'),
|
|
]
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( user + " cannot modify their own user atttributes " + str(sys.exc_info()[0]) )
|
|
|
|
|
|
|
|
#######################################################################################
|
|
# Actual tests start here
|
|
#######################################################################################
|
|
|
|
def test_T0015_anon_read_suffix(self):
|
|
res = self.ldap_anon.search_s(ldap_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
self.assertTrue( attrValueMatch( res[0], 'dc', 'mozillians' ),
|
|
'suffix entry should have an attribute dc=mozillians')
|
|
|
|
def test_T0016_anon_read_people_container(self):
|
|
res = self.ldap_anon.search_s(people_node,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
self.assertTrue( attrValueMatch( res[0], 'ou', 'people' ),
|
|
'ou=people entry should have an attribute ou=people')
|
|
|
|
def test_T0010_anon_read_root_DSE_and_schema(self):
|
|
self.read_root_DSE_and_schema("Anon", self.ldap_anon)
|
|
|
|
def test_T0010_applicant_read_root_DSE_and_schema(self):
|
|
self.read_root_DSE_and_schema("Applicant 001", self.ldap_applicant001)
|
|
|
|
def test_T0010_mozillian_read_root_DSE_and_schema(self):
|
|
self.read_root_DSE_and_schema("Mozillian 011", self.ldap_mozillian011)
|
|
|
|
def test_T0020_anon_search_person(self):
|
|
# Anon trying to find a person entry
|
|
# This should work, but not expose any data apart from the DN
|
|
try:
|
|
res = self.ldap_anon.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test002)' )
|
|
|
|
self.assertEqual( len(res), 1,
|
|
"Anon search for (uid=test002) should return exactly one entry. We got "+str(len(res)) )
|
|
# print res[0]
|
|
except ldap.LDAPError:
|
|
self.fail( "Anon cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
# Now test to see if we got any attributes that we should not see
|
|
for attr in getAttrNames(res[0]):
|
|
if attr.lower() != 'uniqueIdentifier'.lower():
|
|
self.fail( "Anon should not be able to read attributes from user entries. Got: " +
|
|
str(getAttrNames(res[0])) )
|
|
|
|
def test_T0030_anon_search_multi(self):
|
|
# Anon trying to find multiple entries
|
|
# This should limit at 2 entries returned but the search filter matches 3
|
|
# so we expect to get an exception.
|
|
# LDAP actually supplies the entries, but Python LDAP does not deliver them to us.
|
|
try:
|
|
with self.assertRaises(ldap.SIZELIMIT_EXCEEDED):
|
|
res = self.ldap_anon.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test00*)' )
|
|
|
|
except ldap.LDAPError:
|
|
self.fail( "Anon cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
def test_T0040_anon_fake_vouch_for_applicant(self):
|
|
# Anon should not be able to put a DN into
|
|
# an applicant's mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_anon.modify_s(
|
|
ldap_applicant001DN,
|
|
[ (ldap.MOD_ADD,'mozilliansVouchedBy',ldap_applicant001DN) ]
|
|
)
|
|
|
|
def test_T0040_anon_fake_cn(self):
|
|
# Anon should not be able to put a value into
|
|
# an applicant's cn attribute
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_anon.modify_s(
|
|
ldap_applicant001DN,
|
|
[ (ldap.MOD_REPLACE,'cn','modified CN') ]
|
|
)
|
|
|
|
def test_T6040_applicant_search_person(self):
|
|
# Applicant trying to find a person entry that is not their own
|
|
# This should work, but not expose any data apart from the DN
|
|
try:
|
|
res = self.ldap_applicant001.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test002)' )
|
|
|
|
self.assertEqual( len(res), 1,
|
|
"Applicant search for (uid=test002) should return exactly one entry. We got "+str(len(res)) )
|
|
except ldap.LDAPError:
|
|
self.fail( "Applicant cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
# Now test to see if we got any attributes that we should not see
|
|
for attr in getAttrNames(res[0]):
|
|
if attr.lower() != 'uniqueIdentifier'.lower():
|
|
self.fail( "Applicant should not be able to read attributes from user entries. Got: " +
|
|
str(getAttrNames(res[0])) )
|
|
|
|
# It is not practical to enforce different limits on Applicants and Mozillians
|
|
# with the current implementation because the limits statement in OpenLDAP does not accept
|
|
# set specifications
|
|
#
|
|
# def test_T6050_applicant_search_multi(self):
|
|
# # Applicant trying to find multiple entries
|
|
# # The filter matches 3 in this case
|
|
# # This should limit at 2 entries returned
|
|
# try:
|
|
# with self.assertRaises(ldap.SIZELIMIT_EXCEEDED):
|
|
# res = self.ldap_applicant001.search_s(
|
|
# people_node,
|
|
# ldap.SCOPE_SUBTREE,
|
|
# filterstr='(uid=test00*)' )
|
|
#
|
|
# except ldap.LDAPError:
|
|
# self.fail( "Applicant cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
def test_T6030_mozillian_search_mozillian(self):
|
|
# Mozillian searching for a person entry (another Mozillian in this case)
|
|
try:
|
|
res = self.ldap_mozillian011.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test012)' )
|
|
|
|
self.assertEqual( len(res), 1,
|
|
"Mozillian search for (uid=test012) should return exactly one entry. We got "+str(len(res)) )
|
|
# print res[0]
|
|
except ldap.LDAPError:
|
|
self.fail( "Mozillian cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
# Check that we got values for the basic attributes
|
|
if not getAttrValue(res[0],'objectClass'):
|
|
self.fail( "Mozillian should see the objectClass value" )
|
|
if not getAttrValue(res[0],'cn'):
|
|
self.fail( "Mozillian should see the cn value" )
|
|
if not getAttrValue(res[0],'sn'):
|
|
self.fail( "Mozillian should see the sn value" )
|
|
if not getAttrValue(res[0],'uid'):
|
|
self.fail( "Mozillian should see the uid value" )
|
|
if not getAttrValue(res[0],'uniqueIdentifier'):
|
|
self.fail( "Mozillian should see the uniqueIdentifier value" )
|
|
if not getAttrValue(res[0],'mozilliansVouchedBy'):
|
|
self.fail( "Mozillian should see the mozilliansVouchedBy value" )
|
|
|
|
# Now test to see if we got any attributes that we should not see (T1050)
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "Mozillian should not be able to read passwords" )
|
|
|
|
|
|
def test_T6030_mozillian_search_applicant(self):
|
|
# Mozillian searching for a person entry (an applicant in this case)
|
|
try:
|
|
res = self.ldap_mozillian011.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test002)' )
|
|
|
|
self.assertEqual( len(res), 1,
|
|
"Mozillian search for (uid=test002) should return exactly one entry. We got "+str(len(res)) )
|
|
# print res[0]
|
|
except ldap.LDAPError:
|
|
self.fail( "Mozillian cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
# Check that we got values for the basic attributes
|
|
if not getAttrValue(res[0],'objectClass'):
|
|
self.fail( "Mozillian should see the objectClass value" )
|
|
if not getAttrValue(res[0],'cn'):
|
|
self.fail( "Mozillian should see the cn value" )
|
|
if not getAttrValue(res[0],'sn'):
|
|
self.fail( "Mozillian should see the sn value" )
|
|
if not getAttrValue(res[0],'uid'):
|
|
self.fail( "Mozillian should see the uid value" )
|
|
if not getAttrValue(res[0],'uniqueIdentifier'):
|
|
self.fail( "Mozillian should see the uniqueIdentifier value" )
|
|
|
|
# Now test to see if we got any attributes that we should not see (T1050)
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "Mozillian should not be able to read passwords" )
|
|
|
|
# There should not be a vouched entry in an applicant
|
|
if getAttrValue(res[0],'mozilliansVouchedBy'):
|
|
self.fail( "Mozillian should see the mozilliansVouchedBy value" )
|
|
|
|
def test_T6060_mozillian_delete_applicant(self):
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_mozillian011.delete_s(ldap_applicant002DN)
|
|
|
|
def test_T6060_mozillian_delete_self(self):
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_mozillian011.delete_s(ldap_mozillian011DN)
|
|
|
|
|
|
def test_T0030_mozillian_search_multi(self):
|
|
# Mozillian trying to find multiple entries
|
|
# This should limit at rather more than 2 entries returned
|
|
try:
|
|
res = self.ldap_mozillian011.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test*)' )
|
|
|
|
self.assertGreater( len(res), 2,
|
|
"Mozillian search for (uid=test*) should return more than 2 entries. We got "+str(len(res)) )
|
|
# print res[0]
|
|
except ldap.LDAPError:
|
|
self.fail( "Mozillian cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
|
|
def test_T1010_applicant_change_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'Applicant',
|
|
ldap_applicant001DN,
|
|
self.ldap_applicant001,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T1010_mozillian_change_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'Mozillian',
|
|
ldap_mozillian011DN,
|
|
self.ldap_mozillian011,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T1020_change_others_password(self):
|
|
# Try to change other people's passwords
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.passwd_s(ldap_applicant002DN, None, 'owned!')
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.passwd_s(ldap_mozillian012DN, None, 'owned!')
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_applicant001.passwd_s(ldap_applicant002DN, None, 'owned!')
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_applicant001.passwd_s(ldap_mozillian012DN, None, 'owned!')
|
|
|
|
def test_T6010_applicant_change_user_attributes(self):
|
|
self.change_user_attributes(
|
|
'Applicant',
|
|
ldap_applicant001DN,
|
|
self.ldap_applicant001 )
|
|
|
|
def test_T6010_mozillian_change_user_attributes(self):
|
|
self.change_user_attributes(
|
|
'Mozillian',
|
|
ldap_mozillian011DN,
|
|
self.ldap_mozillian011 )
|
|
|
|
def test_T6010_mozillian_delete_uid(self):
|
|
# Users should not be able to delete uid as then it will be
|
|
# impossible for them to log in again
|
|
# The error here is OBJECT_CLASS_VIOLATION because this is enforced
|
|
# by a DIT content rule rather than an ACL
|
|
with self.assertRaises(ldap.OBJECT_CLASS_VIOLATION):
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_mozillian011DN,
|
|
[
|
|
(ldap.MOD_DELETE,'uid',None),
|
|
]
|
|
)
|
|
|
|
|
|
def test_T6020_mozillian_read_obscure_attrs(self):
|
|
# Mozillian reading more obscure attributes in their own entry
|
|
try:
|
|
res = self.ldap_mozillian011.search_s(
|
|
ldap_mozillian011DN,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['mozilliansVouchedBy','modifiersName','modifyTimestamp','userPassword'] )
|
|
except ldap.LDAPError:
|
|
self.fail( "Mozillian cannot search own entry " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'modifiersName'):
|
|
self.fail( "Mozillian should see their own modifiersName value" )
|
|
if not getAttrValue(res[0],'modifyTimestamp'):
|
|
self.fail( "Mozillian should see their own modifyTimestamp value" )
|
|
if not getAttrValue(res[0],'mozilliansVouchedBy'):
|
|
self.fail( "Mozillian should see their own mozilliansVouchedBy value" )
|
|
# Should NOT see own password (T1050)
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "Mozillian should not see their own userPassword value" )
|
|
|
|
def test_T6020_applicant_read_own_attrs(self):
|
|
# Applicant reading common attributes in their own entry
|
|
try:
|
|
res = self.ldap_applicant001.search_s(
|
|
ldap_applicant001DN,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)' )
|
|
except ldap.LDAPError:
|
|
self.fail( "Applicant cannot search own entry " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'objectClass'):
|
|
self.fail( "Applicant should see their own objectClass value" )
|
|
if not getAttrValue(res[0],'cn'):
|
|
self.fail( "Applicant should see their own cn value" )
|
|
if not getAttrValue(res[0],'sn'):
|
|
self.fail( "Applicant should see their own sn value" )
|
|
if not getAttrValue(res[0],'description'):
|
|
self.fail( "Applicant should see their own description value" )
|
|
if not getAttrValue(res[0],'mail'):
|
|
self.fail( "Applicant should see their own mail value" )
|
|
|
|
def test_T6020_applicant_read_obscure_attrs(self):
|
|
# Applicant reading more obscure attributes in their own entry
|
|
try:
|
|
res = self.ldap_applicant001.search_s(
|
|
ldap_applicant001DN,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['mozilliansVouchedBy','modifiersName','modifyTimestamp','userPassword'] )
|
|
except ldap.LDAPError:
|
|
self.fail( "Applicant cannot search own entry " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'modifiersName'):
|
|
self.fail( "Applicant should see their own modifiersName value" )
|
|
if not getAttrValue(res[0],'modifyTimestamp'):
|
|
self.fail( "Applicant should see their own modifyTimestamp value" )
|
|
# There should not be a mozilliansVouchedBy value in an applicant entry!
|
|
if getAttrValue(res[0],'mozilliansVouchedBy'):
|
|
self.fail( "Applicant should not have a mozilliansVouchedBy value" )
|
|
# Should NOT see own password (T1050)
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "Applicant should not see their own userPassword value" )
|
|
|
|
|
|
def test_T5010_mozillian_vouch_for_applicant(self):
|
|
try:
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_applicant001DN,
|
|
[ (ldap.MOD_ADD,'mozilliansVouchedBy',ldap_mozillian011DN) ]
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Mozillian cannot vouch for applicant " + str(sys.exc_info()[0]) )
|
|
|
|
def test_T5010_mozillian_fake_vouch_for_applicant(self):
|
|
# Mozillian should not be able to put someone else's DN into
|
|
# an applicant's mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_applicant001DN,
|
|
[ (ldap.MOD_ADD,'mozilliansVouchedBy',ldap_applicant001DN) ]
|
|
)
|
|
|
|
def test_T5010_mozillian_unvouch_applicant(self):
|
|
# Mozillian should not be able to remove any value from
|
|
# an applicant's mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_applicant001DN,
|
|
[ (ldap.MOD_DELETE,'mozilliansVouchedBy',None) ]
|
|
)
|
|
|
|
def test_T5020_mozillian_fake_vouch_for_self(self):
|
|
# Mozillian should not be able to modify
|
|
# their own mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_mozillian011DN,
|
|
[ (ldap.MOD_ADD,'mozilliansVouchedBy',ldap_applicant001DN) ]
|
|
)
|
|
|
|
def test_T5020_applicant_fake_vouch_for_self(self):
|
|
# Applicant should not be able to modify
|
|
# their own mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_applicant001.modify_s(
|
|
ldap_applicant001DN,
|
|
[ (ldap.MOD_ADD,'mozilliansVouchedBy',ldap_applicant001DN) ]
|
|
)
|
|
|
|
def test_T5020_mozillian_fake_unvouch_self(self):
|
|
# Mozillian should not be able to modify
|
|
# their own mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_mozillian011DN,
|
|
[ (ldap.MOD_DELETE,'mozilliansVouchedBy',None) ]
|
|
)
|
|
|
|
def test_T5030_applicant_fake_vouch_for_another(self):
|
|
# Applicant should not be able to modify
|
|
# another applicant's mozilliansVouchedBy attribute
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_applicant001.modify_s(
|
|
ldap_applicant002DN,
|
|
[ (ldap.MOD_ADD,'mozilliansVouchedBy',ldap_applicant001DN) ]
|
|
)
|
|
|
|
|
|
def test_T7030_mozillian_add_entry(self):
|
|
# Should not be able to add entries
|
|
global entry_list
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
|
|
def test_T7030_applicant_add_entry(self):
|
|
# Should not be able to add entries
|
|
global entry_list
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_applicant001.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
|
|
def test_T8010_mozillian_hack_people_node(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_mozillian011.modify_s(
|
|
people_node,
|
|
[
|
|
(ldap.MOD_REPLACE,'description','Bad, very bad...'),
|
|
]
|
|
)
|
|
|
|
def test_T8010_applicant_hack_people_node(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_applicant001.modify_s(
|
|
people_node,
|
|
[
|
|
(ldap.MOD_REPLACE,'description','Bad, very bad...'),
|
|
]
|
|
)
|
|
|
|
def test_T8030_applicant_snooping_on_system_tree(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_applicant001.search_s(system_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_applicant001.search_s(system_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_applicant001.search_s(admin_group,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
|
|
def test_T8030_mozillian_snooping_on_system_tree(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_mozillian011.search_s(system_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_mozillian011.search_s(system_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_mozillian011.search_s(admin_group,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
|
|
|
|
def test_T9010_uid_must_be_unique(self):
|
|
# There is already an entry with uid=test002 in the test data
|
|
# so we should not be able to change our own UID to clash
|
|
with self.assertRaises(ldap.CONSTRAINT_VIOLATION):
|
|
self.ldap_mozillian011.modify_s(
|
|
ldap_mozillian011DN,
|
|
[
|
|
(ldap.MOD_REPLACE,'uid','test002'),
|
|
]
|
|
)
|
|
|
|
def test_T9010_uid_must_be_unique_even_when_forced(self):
|
|
# There is already an entry with uid=test002 in the test data
|
|
# so even rootDN should not be able to change a UID to clash
|
|
with self.assertRaises(ldap.CONSTRAINT_VIOLATION):
|
|
self.ldap_rootDN.modify_s(
|
|
ldap_mozillian011DN,
|
|
[
|
|
(ldap.MOD_REPLACE,'uid','test002'),
|
|
]
|
|
)
|
|
|
|
|
|
class LdapMonitorUserTests(unittest.TestCase):
|
|
|
|
# These tests require a system user in the Monitor group
|
|
#
|
|
# Note also that most of the attributes we want are 'operational'
|
|
# so we must request them by name
|
|
# (or use the '*' '+' convention: OpenLDAP supports that, but some others don't)
|
|
|
|
def setUp(self):
|
|
setUpCommon(self)
|
|
# Add test user to group
|
|
self.ldap_rootDN.modify_s(
|
|
monitor_group,
|
|
[ (ldap.MOD_ADD,'member',ldap_sys999DN) ]
|
|
)
|
|
|
|
def tearDown(self):
|
|
# Remove test user from group
|
|
self.ldap_rootDN.modify_s(
|
|
monitor_group,
|
|
[ (ldap.MOD_DELETE,'member',ldap_sys999DN) ]
|
|
)
|
|
# Now clear out test entries and close connections
|
|
tearDownCommon(self)
|
|
|
|
def test_T7050_mozillian_snooping_on_stats(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_mozillian011.search_s(monitor_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_mozillian011.search_s(monitor_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
|
|
def test_T7050_anon_snooping_on_stats(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_anon.search_s(monitor_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_anon.search_s(monitor_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
|
|
def test_T7050_monitor_read_suffix(self):
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
monitor_suffix,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['cn','monitoredInfo']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Monitor user cannot read "+monitor_suffix+" " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'cn'):
|
|
self.fail( "Monitor account should be able to see cn in cn=monitor" )
|
|
if not getAttrValue(res[0],'monitoredInfo'):
|
|
self.fail( "Monitor account should be able to see monitoredInfo in cn=monitor" )
|
|
|
|
def test_T7050_monitor_read_stats(self):
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
'cn=Total,cn=Connections,'+monitor_suffix,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['monitorCounter','modifyTimestamp']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Monitor user cannot read cn=Total,cn=Connections,"+monitor_suffix+" " +
|
|
str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'monitorCounter'):
|
|
self.fail( "Monitor account should be able to see monitorCounter" )
|
|
if not getAttrValue(res[0],'modifyTimestamp'):
|
|
self.fail( "Monitor account should be able to see modifyTimestamp" )
|
|
|
|
def test_T8040_monitor_change_own_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'LDAPAdmin',
|
|
ldap_sys999DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T8030_monitor_snooping_on_system_tree(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(system_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(system_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(admin_group,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
|
|
def test_T6060_monitor_delete_applicant(self):
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_sys999.delete_s(ldap_applicant002DN)
|
|
|
|
def test_T7030_monitor_add_entry(self):
|
|
# Should not be able to add entries
|
|
global entry_list
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
|
|
class LdapReplicatorsUserTests(unittest.TestCase):
|
|
|
|
# These tests require a system user in the Replicators group
|
|
|
|
def setUp(self):
|
|
setUpCommon(self)
|
|
# Add test user to group
|
|
self.ldap_rootDN.modify_s(
|
|
replicator_group,
|
|
[ (ldap.MOD_ADD,'member',ldap_sys999DN) ]
|
|
)
|
|
|
|
def tearDown(self):
|
|
# Remove test user from group
|
|
self.ldap_rootDN.modify_s(
|
|
replicator_group,
|
|
[ (ldap.MOD_DELETE,'member',ldap_sys999DN) ]
|
|
)
|
|
# Now clear out test entries and close connections
|
|
tearDownCommon(self)
|
|
|
|
def test_T7010_replicator_read_suffix(self):
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_suffix,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['*','+']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Replicator user cannot read "+ldap_suffix+" " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'objectclass'):
|
|
self.fail( "Replicator account should be able to see objectclass in suffix entry" )
|
|
if not getAttrValue(res[0],'entryCSN'):
|
|
self.fail( "Replicator account should be able to see entryCSN in suffix entry" )
|
|
|
|
def test_T7010_replicator_read_password(self):
|
|
# This is the only account that should be able to read passwords
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_suffix,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test001)',
|
|
attrlist=['*','+']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Replicator user cannot find uid=test001" + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'objectclass'):
|
|
self.fail( "Replicator account should be able to see objectclass in uid=test001 entry" )
|
|
if not getAttrValue(res[0],'userPassword'):
|
|
self.fail( "Replicator account should be able to see userPassword in uid=test001 entry" )
|
|
|
|
def test_T7020_replicator_read_dit(self):
|
|
|
|
# WARNING: this will take a long time if run on a server with a lot of entries
|
|
# It may also cause memory exhaustion.
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_suffix,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['*','+']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Replicator user cannot enumerate the entire DIT " +
|
|
str(sys.exc_info()[0]) )
|
|
|
|
# print "Got", len(res), "entries"
|
|
|
|
|
|
def test_T7020_replicator_read_system_tree(self):
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
system_suffix,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)'
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Replicator user cannot read the system data at "+system_suffix+" " +
|
|
str(sys.exc_info()[0]) )
|
|
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
monitor_group,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)'
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Replicator user cannot read the system data at "+monitor_group+" " +
|
|
str(sys.exc_info()[0]) )
|
|
if not getAttrValue(res[0],'member'):
|
|
self.fail( "Replicator account should be able to see members in system groups" )
|
|
|
|
def test_T6060_replicator_delete_applicant(self):
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_sys999.delete_s(ldap_applicant002DN)
|
|
|
|
def test_T7030_replicator_add_entry(self):
|
|
# Should not be able to add entries
|
|
global entry_list
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
|
|
def test_T8010_replicator_hack_people_node(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.modify_s(
|
|
people_node,
|
|
[
|
|
(ldap.MOD_REPLACE,'description','Bad, very bad...'),
|
|
]
|
|
)
|
|
|
|
class LdapAdminsUserTests(unittest.TestCase):
|
|
|
|
# These tests require a system user in the Admins group
|
|
|
|
def setUp(self):
|
|
setUpCommon(self)
|
|
# Add test user to group
|
|
self.ldap_rootDN.modify_s(
|
|
admin_group,
|
|
[ (ldap.MOD_ADD,'member',ldap_sys999DN) ]
|
|
)
|
|
|
|
def tearDown(self):
|
|
# Remove test user from group
|
|
self.ldap_rootDN.modify_s(
|
|
admin_group,
|
|
[ (ldap.MOD_DELETE,'member',ldap_sys999DN) ]
|
|
)
|
|
# Now clear out test entries and close connections
|
|
tearDownCommon(self)
|
|
|
|
def test_T7010_admin_read_suffix(self):
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_suffix,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['*','+']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Admin user cannot read "+ldap_suffix+" " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'objectclass'):
|
|
self.fail( "Admin account should be able to see objectclass in suffix entry" )
|
|
|
|
def test_T1050_admin_read_password(self):
|
|
# Although this account can change passwords it should not be able to read them
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_applicant002DN,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['uid','userPassword']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "Admin user cannot read "+ldap_applicant002DN+" " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'uid'):
|
|
self.fail( "Admin account should be able to see uid in user entry" )
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "Admin account should not be able to see userPassword in user entry" )
|
|
|
|
def test_T1030_admin_change_mozillian_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'LDAPAdmin',
|
|
ldap_mozillian011DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T1030_admin_change_applicant_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'LDAPAdmin',
|
|
ldap_applicant002DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T8040_admin_change_own_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'LDAPAdmin',
|
|
ldap_sys999DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
|
|
def test_T2010_admin_search_mozillian(self):
|
|
# LDAP Admin searching for a person entry
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test012)' )
|
|
|
|
self.assertEqual( len(res), 1,
|
|
"LDAP Admin search for (uid=test012) should return exactly one entry. We got "+str(len(res)) )
|
|
# print res[0]
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP Admin cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
# Check that we got values for the basic attributes
|
|
if not getAttrValue(res[0],'objectClass'):
|
|
self.fail( "LDAP Admin should see the objectClass value" )
|
|
if not getAttrValue(res[0],'cn'):
|
|
self.fail( "LDAP Admin should see the cn value" )
|
|
if not getAttrValue(res[0],'sn'):
|
|
self.fail( "LDAP Admin should see the sn value" )
|
|
if not getAttrValue(res[0],'uid'):
|
|
self.fail( "LDAP Admin should see the uid value" )
|
|
if not getAttrValue(res[0],'uniqueIdentifier'):
|
|
self.fail( "LDAP Admin should see the uniqueIdentifier value" )
|
|
if not getAttrValue(res[0],'mozilliansVouchedBy'):
|
|
self.fail( "LDAP Admin should see the mozilliansVouchedBy value" )
|
|
|
|
# Now test to see if we got any attributes that we should not see (T1050)
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "LDAP Admin should not be able to read passwords" )
|
|
|
|
|
|
def test_T2020_admin_change_user_attributes(self):
|
|
try:
|
|
self.ldap_sys999.modify_s(
|
|
ldap_mozillian012DN,
|
|
[
|
|
(ldap.MOD_REPLACE,'cn','modified CN'),
|
|
(ldap.MOD_REPLACE,'sn','modified SN'),
|
|
(ldap.MOD_REPLACE,'displayName','modified displayName'),
|
|
(ldap.MOD_REPLACE,'mail',['new@mail.one','new@mail.two']),
|
|
(ldap.MOD_REPLACE,'uid','modified UID'),
|
|
(ldap.MOD_REPLACE,'telephoneNumber',['+1 234','+5-678-90']),
|
|
(ldap.MOD_REPLACE,'description','modified description'),
|
|
(ldap.MOD_REPLACE,'jpegPhoto','modified jpegPhoto'),
|
|
]
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP Admin cannot modify Mozillian's user atttributes " + str(sys.exc_info()[0]) )
|
|
|
|
|
|
def test_T2030_admin_unvouch_user(self):
|
|
try:
|
|
self.ldap_sys999.modify_s(
|
|
ldap_mozillian012DN,
|
|
[
|
|
(ldap.MOD_DELETE,'mozilliansVouchedBy',None),
|
|
]
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP Admin cannot un-vouch a user " + str(sys.exc_info()[0]) )
|
|
|
|
# LDAP Admin is allowed to put any value into the voucher attribute
|
|
def test_T2035_admin_revouch_user(self):
|
|
try:
|
|
self.ldap_sys999.modify_s(
|
|
ldap_mozillian012DN,
|
|
[
|
|
(ldap.MOD_REPLACE,'mozilliansVouchedBy',ldap_mozillian012DN),
|
|
]
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP Admin cannot un-vouch a user " + str(sys.exc_info()[0]) )
|
|
|
|
# LDAP Admin is allowed to delete user entries completely
|
|
def test_T2040_admin_delete_user(self):
|
|
try:
|
|
self.ldap_sys999.delete_s(ldap_mozillian012DN)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP Admin cannot delete a user " + str(sys.exc_info()[0]) )
|
|
|
|
# LDAP Admin is allowed to add new user entries
|
|
def test_T2060_admin_add_user(self):
|
|
global entry_list
|
|
|
|
try:
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP Admin cannot add a user " + str(sys.exc_info()[0]) )
|
|
|
|
def test_T2060_admin_add_bad_user_entry(self):
|
|
# Should not be able to add entries with the wrong objectclass
|
|
# (mozilliansObject in place of mozilliansPerson in this case)
|
|
global entry_list
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansObject']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
|
|
|
|
def test_T2050_admin_hack_sys_password(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.passwd_s(ldap_sys900DN, None, 'owned!')
|
|
|
|
|
|
def test_T8010_admin_hack_people_node(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.modify_s(
|
|
people_node,
|
|
[
|
|
(ldap.MOD_REPLACE,'description','Bad, very bad...'),
|
|
]
|
|
)
|
|
|
|
def test_T8030_ldapadmin_snooping_on_system_tree(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(system_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(system_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(admin_group,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
# Should not even see its own entry
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(ldap_sys999DN,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
|
|
|
|
class RegistrationAgentTests(unittest.TestCase):
|
|
|
|
# These tests require a system user in the RegistrationAgent group
|
|
|
|
def setUp(self):
|
|
setUpCommon(self)
|
|
# Add test user to group
|
|
self.ldap_rootDN.modify_s(
|
|
regAgent_group,
|
|
[ (ldap.MOD_ADD,'member',ldap_sys999DN) ]
|
|
)
|
|
|
|
def tearDown(self):
|
|
# Remove test user from group
|
|
self.ldap_rootDN.modify_s(
|
|
regAgent_group,
|
|
[ (ldap.MOD_DELETE,'member',ldap_sys999DN) ]
|
|
)
|
|
# Now clear out test entries and close connections
|
|
tearDownCommon(self)
|
|
|
|
def test_T7010_regAgent_read_suffix(self):
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_suffix,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['*','+']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "regAgent user cannot read "+ldap_suffix+" " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'objectclass'):
|
|
self.fail( "regAgent account should be able to see objectclass in suffix entry" )
|
|
|
|
def test_T1050_regAgent_read_password(self):
|
|
# Although this account can change passwords it should not be able to read them
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
ldap_applicant002DN,
|
|
ldap.SCOPE_BASE,
|
|
filterstr='(objectclass=*)',
|
|
attrlist=['uid','userPassword']
|
|
)
|
|
except ldap.LDAPError:
|
|
self.fail( "regAgent user cannot read "+ldap_applicant002DN+" " + str(sys.exc_info()[0]) )
|
|
|
|
if not getAttrValue(res[0],'uid'):
|
|
self.fail( "regAgent account should be able to see uid in user entry" )
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "regAgent account should not be able to see userPassword in user entry" )
|
|
|
|
def test_T1030_regAgent_change_mozillian_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'regAgent',
|
|
ldap_mozillian011DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T1030_regAgent_change_applicant_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'regAgent',
|
|
ldap_applicant002DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
def test_T8040_regAgent_change_own_password(self):
|
|
change_and_check_password(
|
|
self,
|
|
'LDAPregAgent',
|
|
ldap_sys999DN,
|
|
self.ldap_sys999,
|
|
None,
|
|
'evenmoresecret' )
|
|
|
|
|
|
def test_T3060_regAgent_search_mozillian(self):
|
|
# regAgent searching for a person entry
|
|
try:
|
|
res = self.ldap_sys999.search_s(
|
|
people_node,
|
|
ldap.SCOPE_SUBTREE,
|
|
filterstr='(uid=test012)' )
|
|
|
|
self.assertEqual( len(res), 1,
|
|
"regAgent search for (uid=test012) should return exactly one entry. We got "+str(len(res)) )
|
|
# print res[0]
|
|
except ldap.LDAPError:
|
|
self.fail( "regAgent cannot search under "+people_node+" " + str(sys.exc_info()[0]) )
|
|
|
|
# Check that we got values for the basic attributes
|
|
if not getAttrValue(res[0],'objectClass'):
|
|
self.fail( "regAgent should see the objectClass value" )
|
|
if not getAttrValue(res[0],'cn'):
|
|
self.fail( "regAgent should see the cn value" )
|
|
if not getAttrValue(res[0],'sn'):
|
|
self.fail( "regAgent should see the sn value" )
|
|
if not getAttrValue(res[0],'uid'):
|
|
self.fail( "regAgent should see the uid value" )
|
|
if not getAttrValue(res[0],'uniqueIdentifier'):
|
|
self.fail( "regAgent should see the uniqueIdentifier value" )
|
|
if not getAttrValue(res[0],'mozilliansVouchedBy'):
|
|
self.fail( "regAgent should see the mozilliansVouchedBy value" )
|
|
|
|
# Now test to see if we got any attributes that we should not see (T1050)
|
|
if getAttrValue(res[0],'userPassword'):
|
|
self.fail( "regAgent should not be able to read passwords" )
|
|
|
|
|
|
def test_T3040_regAgent_change_user_attributes(self):
|
|
# This should fail because it involves deleting a value as well as adding a new one
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.modify_s(
|
|
ldap_mozillian012DN,
|
|
[
|
|
(ldap.MOD_REPLACE,'cn','modified CN'),
|
|
]
|
|
)
|
|
|
|
|
|
def test_T2030_regAgent_unvouch_user(self):
|
|
# We may permit the regAgent to vouch for users
|
|
# but we do not allow it to un-vouch
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.modify_s(
|
|
ldap_mozillian012DN,
|
|
[
|
|
(ldap.MOD_DELETE,'mozilliansVouchedBy',None),
|
|
]
|
|
)
|
|
|
|
def test_T6060_replicator_delete_applicant(self):
|
|
# LDAP regAgent is not allowed to delete user entries
|
|
with self.assertRaises(ldap.LDAPError):
|
|
self.ldap_sys999.delete_s(ldap_applicant002DN)
|
|
|
|
# regAgent is allowed to add new user entries
|
|
def test_T3010_regAgent_add_user(self):
|
|
global entry_list
|
|
|
|
try:
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP regAgent cannot add a user " + str(sys.exc_info()[0]) )
|
|
|
|
# regAgent is allowed to add new user entries that are pre-vouched
|
|
# It may set the password as well, but we would not normally do that by
|
|
# writing direct to the userPassword attribute like this test does
|
|
def test_T3020_regAgent_add_prevouched_user(self):
|
|
global entry_list
|
|
|
|
try:
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansPerson']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test'),
|
|
('mozilliansVouchedBy', ldap_mozillian012DN),
|
|
('userPassword','notverysecret')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
except ldap.LDAPError:
|
|
self.fail( "LDAP regAgent cannot add a user " + str(sys.exc_info()[0]) )
|
|
|
|
def test_T3010_regAgent_add_bad_user_entry(self):
|
|
# Should not be able to add entries with the wrong objectclass
|
|
# (mozilliansObject in place of mozilliansPerson in this case)
|
|
global entry_list
|
|
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.add_s(
|
|
ldap_newuserDN,
|
|
[
|
|
('objectClass', ['inetOrgPerson','mozilliansObject']),
|
|
('uniqueIdentifier', 'testnew'),
|
|
('uid', 'testnew'),
|
|
('cn', 'Test new user'),
|
|
('sn', 'Test')
|
|
]
|
|
)
|
|
# Make sure that we clear this entry up afterwards
|
|
entry_list.append(ldap_newuserDN)
|
|
|
|
|
|
def test_T3050_regAgent_delete_applicant(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.delete_s(ldap_applicant002DN)
|
|
|
|
def test_T2050_regAgent_hack_sys_password(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.passwd_s(ldap_sys900DN, None, 'owned!')
|
|
|
|
|
|
def test_T8010_regAgent_hack_people_node(self):
|
|
with self.assertRaises(ldap.INSUFFICIENT_ACCESS):
|
|
self.ldap_sys999.modify_s(
|
|
people_node,
|
|
[
|
|
(ldap.MOD_REPLACE,'description','Bad, very bad...'),
|
|
]
|
|
)
|
|
|
|
def test_T8030_regAgent_snooping_on_system_tree(self):
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(system_suffix,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(system_suffix,ldap.SCOPE_SUBTREE,'(objectclass=*)')
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(admin_group,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
# Should not even see its own entry
|
|
with self.assertRaises(ldap.NO_SUCH_OBJECT):
|
|
self.ldap_sys999.search_s(ldap_sys999DN,ldap.SCOPE_BASE,'(objectclass=*)')
|
|
|
|
|
|
|
|
########################################################################
|
|
# Main program
|
|
########################################################################
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|
|
|