s4:ldap.py - split it up and move SAM related stuff to sam.py
[mat/samba.git] / source4 / dsdb / tests / python / sam.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # This is a port of the original in testprogs/ejs/ldap.js
4
5 import optparse
6 import sys
7 import time
8 import base64
9 import os
10
11 sys.path.append("bin/python")
12 import samba
13 samba.ensure_external_module("subunit", "subunit/python")
14 samba.ensure_external_module("testtools", "testtools")
15
16 import samba.getopt as options
17
18 from samba.auth import system_session
19 from ldb import SCOPE_SUBTREE, SCOPE_ONELEVEL, SCOPE_BASE, LdbError
20 from ldb import ERR_NO_SUCH_OBJECT, ERR_ATTRIBUTE_OR_VALUE_EXISTS
21 from ldb import ERR_ENTRY_ALREADY_EXISTS, ERR_UNWILLING_TO_PERFORM
22 from ldb import ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_OTHER, ERR_INVALID_DN_SYNTAX
23 from ldb import ERR_NO_SUCH_ATTRIBUTE
24 from ldb import ERR_OBJECT_CLASS_VIOLATION, ERR_NOT_ALLOWED_ON_RDN
25 from ldb import ERR_NAMING_VIOLATION, ERR_CONSTRAINT_VIOLATION
26 from ldb import ERR_UNDEFINED_ATTRIBUTE_TYPE
27 from ldb import Message, MessageElement, Dn
28 from ldb import FLAG_MOD_ADD, FLAG_MOD_REPLACE, FLAG_MOD_DELETE
29 from samba import Ldb
30 from samba.dsdb import (UF_NORMAL_ACCOUNT, UF_INTERDOMAIN_TRUST_ACCOUNT,
31     UF_WORKSTATION_TRUST_ACCOUNT, UF_SERVER_TRUST_ACCOUNT,
32     UF_PARTIAL_SECRETS_ACCOUNT,
33     UF_PASSWD_NOTREQD, UF_ACCOUNTDISABLE, ATYPE_NORMAL_ACCOUNT,
34     ATYPE_WORKSTATION_TRUST, SYSTEM_FLAG_DOMAIN_DISALLOW_MOVE,
35     SYSTEM_FLAG_CONFIG_ALLOW_RENAME, SYSTEM_FLAG_CONFIG_ALLOW_MOVE,
36     SYSTEM_FLAG_CONFIG_ALLOW_LIMITED_MOVE)
37 from samba.dcerpc.security import (DOMAIN_RID_USERS, DOMAIN_RID_DOMAIN_MEMBERS,
38     DOMAIN_RID_DCS, DOMAIN_RID_READONLY_DCS)
39
40 from subunit.run import SubunitTestRunner
41 import unittest
42
43 from samba.ndr import ndr_pack, ndr_unpack
44 from samba.dcerpc import security
45
46 parser = optparse.OptionParser("sam.py [options] <host>")
47 sambaopts = options.SambaOptions(parser)
48 parser.add_option_group(sambaopts)
49 parser.add_option_group(options.VersionOptions(parser))
50 # use command line creds if available
51 credopts = options.CredentialsOptions(parser)
52 parser.add_option_group(credopts)
53 opts, args = parser.parse_args()
54
55 if len(args) < 1:
56     parser.print_usage()
57     sys.exit(1)
58
59 host = args[0]
60
61 lp = sambaopts.get_loadparm()
62 creds = credopts.get_credentials(lp)
63
64 class SamTests(unittest.TestCase):
65
66     def delete_force(self, ldb, dn):
67         try:
68             ldb.delete(dn)
69         except LdbError, (num, _):
70             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
71
72     def find_basedn(self, ldb):
73         res = ldb.search(base="", expression="", scope=SCOPE_BASE,
74                          attrs=["defaultNamingContext"])
75         self.assertEquals(len(res), 1)
76         return res[0]["defaultNamingContext"][0]
77
78     def find_domain_sid(self):
79         res = self.ldb.search(base=self.base_dn, expression="(objectClass=*)", scope=SCOPE_BASE)
80         return ndr_unpack( security.dom_sid,res[0]["objectSid"][0])
81
82     def setUp(self):
83         super(SamTests, self).setUp()
84         self.ldb = ldb
85         self.gc_ldb = gc_ldb
86         self.base_dn = self.find_basedn(ldb)
87         self.domain_sid = self.find_domain_sid()
88
89         print "baseDN: %s\n" % self.base_dn
90
91         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
92         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
93         self.delete_force(self.ldb, "cn=ldaptestuser2,cn=users," + self.base_dn)
94
95     def test_users_groups(self):
96         """This tests the SAM users and groups behaviour"""
97         print "Testing users and groups behaviour\n"
98
99         ldb.add({
100             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
101             "objectclass": "group"})
102
103         ldb.add({
104             "dn": "cn=ldaptestgroup2,cn=users," + self.base_dn,
105             "objectclass": "group"})
106
107         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
108                           scope=SCOPE_BASE, attrs=["objectSID"])
109         self.assertTrue(len(res1) == 1)
110         group_rid_1 = security.dom_sid(ldb.schema_format_value("objectSID",
111           res1[0]["objectSID"][0])).split()[1]
112
113         res1 = ldb.search("cn=ldaptestgroup2,cn=users," + self.base_dn,
114                           scope=SCOPE_BASE, attrs=["objectSID"])
115         self.assertTrue(len(res1) == 1)
116         group_rid_2 = security.dom_sid(ldb.schema_format_value("objectSID",
117           res1[0]["objectSID"][0])).split()[1]
118
119         # Try to create a user with an invalid primary group
120         try:
121             ldb.add({
122                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
123                 "objectclass": ["user", "person"],
124                 "primaryGroupID": "0"})
125             self.fail()
126         except LdbError, (num, _):
127             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
128         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
129
130         # Try to Create a user with a valid primary group
131         try:
132             ldb.add({
133                 "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
134                 "objectclass": ["user", "person"],
135                 "primaryGroupID": str(group_rid_1)})
136             self.fail()
137         except LdbError, (num, _):
138             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
139         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
140
141         # Test to see how we should behave when the user account doesn't
142         # exist
143         m = Message()
144         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
145         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
146           "primaryGroupID")
147         try:
148             ldb.modify(m)
149             self.fail()
150         except LdbError, (num, _):
151             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
152
153         # Test to see how we should behave when the account isn't a user
154         m = Message()
155         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
156         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
157           "primaryGroupID")
158         try:
159             ldb.modify(m)
160             self.fail()
161         except LdbError, (num, _):
162             self.assertEquals(num, ERR_OBJECT_CLASS_VIOLATION)
163
164         # Test default primary groups on add operations
165
166         ldb.add({
167             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
168             "objectclass": ["user", "person"]})
169
170         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
171                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
172         self.assertTrue(len(res1) == 1)
173         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
174
175         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
176
177         ldb.add({
178             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
179             "objectclass": ["user", "person"],
180             "userAccountControl": str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD) })
181
182         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
183                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
184         self.assertTrue(len(res1) == 1)
185         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
186
187         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
188
189         # unfortunately the INTERDOMAIN_TRUST_ACCOUNT case cannot be tested
190         # since such accounts aren't directly creatable (ACCESS_DENIED)
191
192         ldb.add({
193             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
194             "objectclass": ["computer"],
195             "userAccountControl": str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD) })
196
197         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
198                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
199         self.assertTrue(len(res1) == 1)
200         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DOMAIN_MEMBERS))
201
202         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
203
204         ldb.add({
205             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
206             "objectclass": ["computer"],
207             "userAccountControl": str(UF_SERVER_TRUST_ACCOUNT | UF_PASSWD_NOTREQD) })
208
209         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
210                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
211         self.assertTrue(len(res1) == 1)
212         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DCS))
213
214         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
215
216         # Read-only DC accounts are only creatable by
217         # UF_WORKSTATION_TRUST_ACCOUNT and work only on DCs >= 2008 (therefore
218         # we have a fallback in the assertion)
219         ldb.add({
220             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
221             "objectclass": ["computer"],
222             "userAccountControl": str(UF_PARTIAL_SECRETS_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD) })
223
224         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
225                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
226         self.assertTrue(len(res1) == 1)
227         self.assertTrue(res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_READONLY_DCS) or
228                         res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_DOMAIN_MEMBERS))
229
230         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
231
232         # Test default primary groups on modify operations
233
234         ldb.add({
235             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
236             "objectclass": ["user", "person"]})
237
238         m = Message()
239         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
240         m["userAccountControl"] = MessageElement(str(UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
241           "userAccountControl")
242         ldb.modify(m)
243
244         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
245                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
246         self.assertTrue(len(res1) == 1)
247         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
248
249         # unfortunately the INTERDOMAIN_TRUST_ACCOUNT case cannot be tested
250         # since such accounts aren't directly creatable (ACCESS_DENIED)
251
252         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
253
254         ldb.add({
255             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
256             "objectclass": ["computer"]})
257
258         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
259                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
260         self.assertTrue(len(res1) == 1)
261         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_USERS))
262
263         m = Message()
264         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
265         m["userAccountControl"] = MessageElement(str(UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
266           "userAccountControl")
267         ldb.modify(m)
268
269         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
270                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
271         self.assertTrue(len(res1) == 1)
272         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DOMAIN_MEMBERS))
273
274         m = Message()
275         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
276         m["userAccountControl"] = MessageElement(str(UF_SERVER_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
277           "userAccountControl")
278         ldb.modify(m)
279
280         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
281                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
282         self.assertTrue(len(res1) == 1)
283         self.assertEquals(res1[0]["primaryGroupID"][0], str(DOMAIN_RID_DCS))
284
285         # Read-only DC accounts are only creatable by
286         # UF_WORKSTATION_TRUST_ACCOUNT and work only on DCs >= 2008 (therefore
287         # we have a fallback in the assertion)
288         m = Message()
289         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
290         m["userAccountControl"] = MessageElement(str(UF_PARTIAL_SECRETS_ACCOUNT | UF_WORKSTATION_TRUST_ACCOUNT | UF_PASSWD_NOTREQD), FLAG_MOD_REPLACE,
291           "userAccountControl")
292         ldb.modify(m)
293
294         res1 = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
295                           scope=SCOPE_BASE, attrs=["primaryGroupID"])
296         self.assertTrue(len(res1) == 1)
297         self.assertTrue(res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_READONLY_DCS) or
298                         res1[0]["primaryGroupID"][0] == str(DOMAIN_RID_DOMAIN_MEMBERS))
299
300         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
301
302         # Recreate account for further tests
303
304         ldb.add({
305             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
306             "objectclass": ["user", "person"]})
307
308         # We should be able to reset our actual primary group
309         m = Message()
310         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
311         m["primaryGroupID"] = MessageElement(str(DOMAIN_RID_USERS), FLAG_MOD_REPLACE,
312           "primaryGroupID")
313         ldb.modify(m)
314
315         # Try to add invalid primary group
316         m = Message()
317         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
318         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_REPLACE,
319           "primaryGroupID")
320         try:
321             ldb.modify(m)
322             self.fail()
323         except LdbError, (num, _):
324             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
325
326         # Try to make group 1 primary - should be denied since it is not yet
327         # secondary
328         m = Message()
329         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
330         m["primaryGroupID"] = MessageElement(str(group_rid_1),
331           FLAG_MOD_REPLACE, "primaryGroupID")
332         try:
333             ldb.modify(m)
334             self.fail()
335         except LdbError, (num, _):
336             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
337
338         # Make group 1 secondary
339         m = Message()
340         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
341         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
342                                      FLAG_MOD_REPLACE, "member")
343         ldb.modify(m)
344
345         # Make group 1 primary
346         m = Message()
347         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
348         m["primaryGroupID"] = MessageElement(str(group_rid_1),
349           FLAG_MOD_REPLACE, "primaryGroupID")
350         ldb.modify(m)
351
352         # Try to delete group 1 - should be denied
353         try:
354             ldb.delete("cn=ldaptestgroup,cn=users," + self.base_dn)
355             self.fail()
356         except LdbError, (num, _):
357             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
358
359         # Try to add group 1 also as secondary - should be denied
360         m = Message()
361         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
362         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
363                                      FLAG_MOD_ADD, "member")
364         try:
365             ldb.modify(m)
366             self.fail()
367         except LdbError, (num, _):
368             self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS)
369
370         # Try to add invalid member to group 1 - should be denied
371         m = Message()
372         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
373         m["member"] = MessageElement(
374           "cn=ldaptestuser3,cn=users," + self.base_dn,
375           FLAG_MOD_ADD, "member")
376         try:
377             ldb.modify(m)
378             self.fail()
379         except LdbError, (num, _):
380             self.assertEquals(num, ERR_NO_SUCH_OBJECT)
381
382         # Make group 2 secondary
383         m = Message()
384         m.dn = Dn(ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
385         m["member"] = MessageElement("cn=ldaptestuser,cn=users," + self.base_dn,
386                                      FLAG_MOD_ADD, "member")
387         ldb.modify(m)
388
389         # Swap the groups
390         m = Message()
391         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
392         m["primaryGroupID"] = MessageElement(str(group_rid_2),
393           FLAG_MOD_REPLACE, "primaryGroupID")
394         ldb.modify(m)
395
396         # Old primary group should contain a "member" attribute for the user,
397         # the new shouldn't contain anymore one
398         res1 = ldb.search("cn=ldaptestgroup, cn=users," + self.base_dn,
399                           scope=SCOPE_BASE, attrs=["member"])
400         self.assertTrue(len(res1) == 1)
401         self.assertTrue(len(res1[0]["member"]) == 1)
402         self.assertEquals(res1[0]["member"][0].lower(),
403           ("cn=ldaptestuser,cn=users," + self.base_dn).lower())
404
405         res1 = ldb.search("cn=ldaptestgroup2, cn=users," + self.base_dn,
406                           scope=SCOPE_BASE, attrs=["member"])
407         self.assertTrue(len(res1) == 1)
408         self.assertFalse("member" in res1[0])
409
410         # Also this should be denied
411         try:
412             ldb.add({
413               "dn": "cn=ldaptestuser1,cn=users," + self.base_dn,
414               "objectclass": ["user", "person"],
415               "primaryGroupID": "0"})
416             self.fail()
417         except LdbError, (num, _):
418             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
419
420         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
421         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
422         self.delete_force(self.ldb, "cn=ldaptestgroup2,cn=users," + self.base_dn)
423
424     def test_sam_attributes(self):
425         """Test the behaviour of special attributes of SAM objects"""
426         print "Testing the behaviour of special attributes of SAM objects\n"""
427
428         ldb.add({
429             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
430             "objectclass": ["user", "person"]})
431         ldb.add({
432             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
433             "objectclass": "group"})
434
435         m = Message()
436         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
437         m["groupType"] = MessageElement("0", FLAG_MOD_ADD,
438           "groupType")
439         try:
440             ldb.modify(m)
441             self.fail()
442         except LdbError, (num, _):
443             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
444
445         m = Message()
446         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
447         m["groupType"] = MessageElement([], FLAG_MOD_DELETE,
448           "groupType")
449         try:
450             ldb.modify(m)
451             self.fail()
452         except LdbError, (num, _):
453             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
454
455         m = Message()
456         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
457         m["primaryGroupID"] = MessageElement("0", FLAG_MOD_ADD,
458           "primaryGroupID")
459         try:
460             ldb.modify(m)
461             self.fail()
462         except LdbError, (num, _):
463             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
464
465         m = Message()
466         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
467         m["primaryGroupID"] = MessageElement([], FLAG_MOD_DELETE,
468           "primaryGroupID")
469         try:
470             ldb.modify(m)
471             self.fail()
472         except LdbError, (num, _):
473             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
474
475         m = Message()
476         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
477         m["userAccountControl"] = MessageElement("0", FLAG_MOD_ADD,
478           "userAccountControl")
479         try:
480             ldb.modify(m)
481             self.fail()
482         except LdbError, (num, _):
483             self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS)
484
485         m = Message()
486         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
487         m["userAccountControl"] = MessageElement([], FLAG_MOD_DELETE,
488           "userAccountControl")
489         try:
490             ldb.modify(m)
491             self.fail()
492         except LdbError, (num, _):
493             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
494
495         m = Message()
496         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
497         m["sAMAccountType"] = MessageElement("0", FLAG_MOD_ADD,
498           "sAMAccountType")
499         try:
500             ldb.modify(m)
501             self.fail()
502         except LdbError, (num, _):
503             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
504
505         m = Message()
506         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
507         m["sAMAccountType"] = MessageElement([], FLAG_MOD_REPLACE,
508           "sAMAccountType")
509         try:
510             ldb.modify(m)
511             self.fail()
512         except LdbError, (num, _):
513             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
514
515         m = Message()
516         m.dn = Dn(ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
517         m["sAMAccountType"] = MessageElement([], FLAG_MOD_DELETE,
518           "sAMAccountType")
519         try:
520             ldb.modify(m)
521             self.fail()
522         except LdbError, (num, _):
523             self.assertEquals(num, ERR_UNWILLING_TO_PERFORM)
524
525         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
526         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
527
528     def test_primary_group_token_constructed(self):
529         """Test the primary group token behaviour (hidden-generated-readonly attribute on groups) and some other constructed attributes"""
530         print "Testing primary group token behaviour and other constructed attributes\n"
531
532         try:
533             ldb.add({
534                 "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
535                 "objectclass": "group",
536                 "primaryGroupToken": "100"})
537             self.fail()
538         except LdbError, (num, _):
539             self.assertEquals(num, ERR_UNDEFINED_ATTRIBUTE_TYPE)
540         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
541
542         ldb.add({
543             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
544             "objectclass": ["user", "person"]})
545
546         ldb.add({
547             "dn": "cn=ldaptestgroup,cn=users," + self.base_dn,
548             "objectclass": "group"})
549
550         # Testing for one invalid, and one valid operational attribute, but also the things they are built from
551         res1 = ldb.search(self.base_dn,
552                           scope=SCOPE_BASE, attrs=["primaryGroupToken", "canonicalName", "objectClass", "objectSid"])
553         self.assertTrue(len(res1) == 1)
554         self.assertFalse("primaryGroupToken" in res1[0])
555         self.assertTrue("canonicalName" in res1[0])
556         self.assertTrue("objectClass" in res1[0])
557         self.assertTrue("objectSid" in res1[0])
558
559         res1 = ldb.search(self.base_dn,
560                           scope=SCOPE_BASE, attrs=["primaryGroupToken", "canonicalName"])
561         self.assertTrue(len(res1) == 1)
562         self.assertFalse("primaryGroupToken" in res1[0])
563         self.assertFalse("objectSid" in res1[0])
564         self.assertFalse("objectClass" in res1[0])
565         self.assertTrue("canonicalName" in res1[0])
566
567         res1 = ldb.search("cn=users,"+self.base_dn,
568                           scope=SCOPE_BASE, attrs=["primaryGroupToken"])
569         self.assertTrue(len(res1) == 1)
570         self.assertFalse("primaryGroupToken" in res1[0])
571
572         res1 = ldb.search("cn=ldaptestuser, cn=users," + self.base_dn,
573                           scope=SCOPE_BASE, attrs=["primaryGroupToken"])
574         self.assertTrue(len(res1) == 1)
575         self.assertFalse("primaryGroupToken" in res1[0])
576
577         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
578                           scope=SCOPE_BASE)
579         self.assertTrue(len(res1) == 1)
580         self.assertFalse("primaryGroupToken" in res1[0])
581
582         res1 = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn,
583                           scope=SCOPE_BASE, attrs=["primaryGroupToken", "objectSID"])
584         self.assertTrue(len(res1) == 1)
585         primary_group_token = int(res1[0]["primaryGroupToken"][0])
586
587         rid = security.dom_sid(ldb.schema_format_value("objectSID", res1[0]["objectSID"][0])).split()[1]
588         self.assertEquals(primary_group_token, rid)
589
590         m = Message()
591         m.dn = Dn(ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
592         m["primaryGroupToken"] = "100"
593         try:
594             ldb.modify(m)
595             self.fail()
596         except LdbError, (num, _):
597             self.assertEquals(num, ERR_CONSTRAINT_VIOLATION)
598
599         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
600         self.delete_force(self.ldb, "cn=ldaptestgroup,cn=users," + self.base_dn)
601
602     def test_tokenGroups(self):
603         """Test the tokenGroups behaviour (hidden-generated-readonly attribute on SAM objects)"""
604         print "Testing tokenGroups behaviour\n"
605
606         # The domain object shouldn't contain any "tokenGroups" entry
607         res = ldb.search(self.base_dn, scope=SCOPE_BASE, attrs=["tokenGroups"])
608         self.assertTrue(len(res) == 1)
609         self.assertFalse("tokenGroups" in res[0])
610
611         # The domain administrator should contain "tokenGroups" entries
612         # (the exact number depends on the domain/forest function level and the
613         # DC software versions)
614         res = ldb.search("cn=Administrator,cn=Users," + self.base_dn,
615                          scope=SCOPE_BASE, attrs=["tokenGroups"])
616         self.assertTrue(len(res) == 1)
617         self.assertTrue("tokenGroups" in res[0])
618
619         ldb.add({
620             "dn": "cn=ldaptestuser,cn=users," + self.base_dn,
621             "objectclass": ["user", "person"]})
622
623         # This testuser should contain at least two "tokenGroups" entries
624         # (exactly two on an unmodified "Domain Users" and "Users" group)
625         res = ldb.search("cn=ldaptestuser,cn=users," + self.base_dn,
626                          scope=SCOPE_BASE, attrs=["tokenGroups"])
627         self.assertTrue(len(res) == 1)
628         self.assertTrue(len(res[0]["tokenGroups"]) >= 2)
629
630         # one entry which we need to find should point to domains "Domain Users"
631         # group and another entry should point to the builtin "Users"group
632         domain_users_group_found = False
633         users_group_found = False
634         for sid in res[0]["tokenGroups"]:
635             rid = security.dom_sid(ldb.schema_format_value("objectSID", sid)).split()[1]
636             if rid == 513:
637                 domain_users_group_found = True
638             if rid == 545:
639                 users_group_found = True
640
641         self.assertTrue(domain_users_group_found)
642         self.assertTrue(users_group_found)
643
644         self.delete_force(self.ldb, "cn=ldaptestuser,cn=users," + self.base_dn)
645
646 if not "://" in host:
647     if os.path.isfile(host):
648         host = "tdb://%s" % host
649     else:
650         host = "ldap://%s" % host
651
652 ldb = Ldb(host, credentials=creds, session_info=system_session(), lp=lp)
653 if not "tdb://" in host:
654     gc_ldb = Ldb("%s:3268" % host, credentials=creds,
655                  session_info=system_session(), lp=lp)
656 else:
657     gc_ldb = None
658
659 runner = SubunitTestRunner()
660 rc = 0
661 if not runner.run(unittest.makeSuite(SamTests)).wasSuccessful():
662     rc = 1
663 sys.exit(rc)