s4-python: Add functions to get linkid and systemflags of an attribute
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / samdb.py
blobba0dd99cb72b2c32da24089a7ada883957c1a71b
1 #!/usr/bin/env python
3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
5 # Copyright (C) Matthias Dieter Wallnoefer 2009
7 # Based on the original in EJS:
8 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 3 of the License, or
13 # (at your option) any later version.
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 """Convenience functions for using the SAM."""
26 import samba
27 import ldb
28 import time
29 import base64
30 from samba import dsdb
31 from samba.ndr import ndr_unpack, ndr_pack
32 from samba.dcerpc import drsblobs, misc
34 __docformat__ = "restructuredText"
37 class SamDB(samba.Ldb):
38 """The SAM database."""
40 hash_oid_name = {}
42 def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
43 credentials=None, flags=0, options=None, global_schema=True,
44 auto_connect=True, am_rodc=None):
45 self.lp = lp
46 if not auto_connect:
47 url = None
48 elif url is None and lp is not None:
49 url = lp.samdb_url()
51 super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
52 session_info=session_info, credentials=credentials, flags=flags,
53 options=options)
55 if global_schema:
56 dsdb._dsdb_set_global_schema(self)
58 if am_rodc is not None:
59 dsdb._dsdb_set_am_rodc(self, am_rodc)
61 def connect(self, url=None, flags=0, options=None):
62 if self.lp is not None:
63 url = self.lp.private_path(url)
65 super(SamDB, self).connect(url=url, flags=flags,
66 options=options)
68 def am_rodc(self):
69 return dsdb._am_rodc(self)
71 def domain_dn(self):
72 return str(self.get_default_basedn())
74 def enable_account(self, search_filter):
75 """Enables an account
77 :param search_filter: LDAP filter to find the user (eg
78 samccountname=name)
79 """
81 flags = samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_PASSWD_NOTREQD
82 self.toggle_userAccountFlags(search_filter, flags, on=False)
84 def toggle_userAccountFlags(self, search_filter, flags, on=True, strict=False):
85 """toggle_userAccountFlags
87 :param search_filter: LDAP filter to find the user (eg
88 samccountname=name)
89 :flags: samba.dsdb.UF_* flags
90 :on: on=True (default) => set, on=False => unset
91 :strict: strict=False (default) ignore if no action is needed
92 strict=True raises an Exception if...
93 """
94 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
95 expression=search_filter, attrs=["userAccountControl"])
96 if len(res) == 0:
97 raise Exception('Unable to find user "%s"' % search_filter)
98 assert(len(res) == 1)
99 account_dn = res[0].dn
101 old_uac = int(res[0]["userAccountControl"][0])
102 if on:
103 if strict and (old_uac & flags):
104 error = 'userAccountFlags[%d:0x%08X] already contain 0x%X' % (old_uac, old_uac, flags)
105 raise Exception(error)
107 new_uac = old_uac | flags
108 else:
109 if strict and not (old_uac & flags):
110 error = 'userAccountFlags[%d:0x%08X] not contain 0x%X' % (old_uac, old_uac, flags)
111 raise Exception(error)
113 new_uac = old_uac & ~flags
115 if old_uac == new_uac:
116 return
118 mod = """
119 dn: %s
120 changetype: modify
121 delete: userAccountControl
122 userAccountControl: %u
123 add: userAccountControl
124 userAccountControl: %u
125 """ % (account_dn, old_uac, new_uac)
126 self.modify_ldif(mod)
128 def force_password_change_at_next_login(self, search_filter):
129 """Forces a password change at next login
131 :param search_filter: LDAP filter to find the user (eg
132 samccountname=name)
134 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
135 expression=search_filter, attrs=[])
136 if len(res) == 0:
137 raise Exception('Unable to find user "%s"' % search_filter)
138 assert(len(res) == 1)
139 user_dn = res[0].dn
141 mod = """
142 dn: %s
143 changetype: modify
144 replace: pwdLastSet
145 pwdLastSet: 0
146 """ % (user_dn)
147 self.modify_ldif(mod)
149 def newgroup(self, groupname, groupou=None, grouptype=None,
150 description=None, mailaddress=None, notes=None, sd=None):
151 """Adds a new group with additional parameters
153 :param groupname: Name of the new group
154 :param grouptype: Type of the new group
155 :param description: Description of the new group
156 :param mailaddress: Email address of the new group
157 :param notes: Notes of the new group
158 :param sd: security descriptor of the object
161 group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn())
163 # The new user record. Note the reliance on the SAMLDB module which
164 # fills in the default informations
165 ldbmessage = {"dn": group_dn,
166 "sAMAccountName": groupname,
167 "objectClass": "group"}
169 if grouptype is not None:
170 ldbmessage["groupType"] = self.normalise_int32(grouptype)
172 if description is not None:
173 ldbmessage["description"] = description
175 if mailaddress is not None:
176 ldbmessage["mail"] = mailaddress
178 if notes is not None:
179 ldbmessage["info"] = notes
181 if sd is not None:
182 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
184 self.add(ldbmessage)
186 def deletegroup(self, groupname):
187 """Deletes a group
189 :param groupname: Name of the target group
192 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (groupname, "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
193 self.transaction_start()
194 try:
195 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
196 expression=groupfilter, attrs=[])
197 if len(targetgroup) == 0:
198 raise Exception('Unable to find group "%s"' % groupname)
199 assert(len(targetgroup) == 1)
200 self.delete(targetgroup[0].dn)
201 except Exception:
202 self.transaction_cancel()
203 raise
204 else:
205 self.transaction_commit()
207 def add_remove_group_members(self, groupname, listofmembers,
208 add_members_operation=True):
209 """Adds or removes group members
211 :param groupname: Name of the target group
212 :param listofmembers: Comma-separated list of group members
213 :param add_members_operation: Defines if its an add or remove
214 operation
217 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (groupname, "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
218 groupmembers = listofmembers.split(',')
220 self.transaction_start()
221 try:
222 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
223 expression=groupfilter, attrs=['member'])
224 if len(targetgroup) == 0:
225 raise Exception('Unable to find group "%s"' % groupname)
226 assert(len(targetgroup) == 1)
228 modified = False
230 addtargettogroup = """
231 dn: %s
232 changetype: modify
233 """ % (str(targetgroup[0].dn))
235 for member in groupmembers:
236 targetmember = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
237 expression="(|(sAMAccountName=%s)(CN=%s))" % (member, member), attrs=[])
239 if len(targetmember) != 1:
240 continue
242 if add_members_operation is True and (targetgroup[0].get('member') is None or str(targetmember[0].dn) not in targetgroup[0]['member']):
243 modified = True
244 addtargettogroup += """add: member
245 member: %s
246 """ % (str(targetmember[0].dn))
248 elif add_members_operation is False and (targetgroup[0].get('member') is not None and str(targetmember[0].dn) in targetgroup[0]['member']):
249 modified = True
250 addtargettogroup += """delete: member
251 member: %s
252 """ % (str(targetmember[0].dn))
254 if modified is True:
255 self.modify_ldif(addtargettogroup)
257 except Exception:
258 self.transaction_cancel()
259 raise
260 else:
261 self.transaction_commit()
263 def newuser(self, username, password,
264 force_password_change_at_next_login_req=False,
265 useusernameascn=False, userou=None, surname=None, givenname=None,
266 initials=None, profilepath=None, scriptpath=None, homedrive=None,
267 homedirectory=None, jobtitle=None, department=None, company=None,
268 description=None, mailaddress=None, internetaddress=None,
269 telephonenumber=None, physicaldeliveryoffice=None, sd=None,
270 setpassword=True):
271 """Adds a new user with additional parameters
273 :param username: Name of the new user
274 :param password: Password for the new user
275 :param force_password_change_at_next_login_req: Force password change
276 :param useusernameascn: Use username as cn rather that firstname +
277 initials + lastname
278 :param userou: Object container (without domainDN postfix) for new user
279 :param surname: Surname of the new user
280 :param givenname: First name of the new user
281 :param initials: Initials of the new user
282 :param profilepath: Profile path of the new user
283 :param scriptpath: Logon script path of the new user
284 :param homedrive: Home drive of the new user
285 :param homedirectory: Home directory of the new user
286 :param jobtitle: Job title of the new user
287 :param department: Department of the new user
288 :param company: Company of the new user
289 :param description: of the new user
290 :param mailaddress: Email address of the new user
291 :param internetaddress: Home page of the new user
292 :param telephonenumber: Phone number of the new user
293 :param physicaldeliveryoffice: Office location of the new user
294 :param sd: security descriptor of the object
295 :param setpassword: optionally disable password reset
298 displayname = ""
299 if givenname is not None:
300 displayname += givenname
302 if initials is not None:
303 displayname += ' %s.' % initials
305 if surname is not None:
306 displayname += ' %s' % surname
308 cn = username
309 if useusernameascn is None and displayname is not "":
310 cn = displayname
312 user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn())
314 dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "")
315 user_principal_name = "%s@%s" % (username, dnsdomain)
316 # The new user record. Note the reliance on the SAMLDB module which
317 # fills in the default informations
318 ldbmessage = {"dn": user_dn,
319 "sAMAccountName": username,
320 "userPrincipalName": user_principal_name,
321 "objectClass": "user"}
323 if surname is not None:
324 ldbmessage["sn"] = surname
326 if givenname is not None:
327 ldbmessage["givenName"] = givenname
329 if displayname is not "":
330 ldbmessage["displayName"] = displayname
331 ldbmessage["name"] = displayname
333 if initials is not None:
334 ldbmessage["initials"] = '%s.' % initials
336 if profilepath is not None:
337 ldbmessage["profilePath"] = profilepath
339 if scriptpath is not None:
340 ldbmessage["scriptPath"] = scriptpath
342 if homedrive is not None:
343 ldbmessage["homeDrive"] = homedrive
345 if homedirectory is not None:
346 ldbmessage["homeDirectory"] = homedirectory
348 if jobtitle is not None:
349 ldbmessage["title"] = jobtitle
351 if department is not None:
352 ldbmessage["department"] = department
354 if company is not None:
355 ldbmessage["company"] = company
357 if description is not None:
358 ldbmessage["description"] = description
360 if mailaddress is not None:
361 ldbmessage["mail"] = mailaddress
363 if internetaddress is not None:
364 ldbmessage["wWWHomePage"] = internetaddress
366 if telephonenumber is not None:
367 ldbmessage["telephoneNumber"] = telephonenumber
369 if physicaldeliveryoffice is not None:
370 ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice
372 if sd is not None:
373 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
375 self.transaction_start()
376 try:
377 self.add(ldbmessage)
379 # Sets the password for it
380 if setpassword:
381 self.setpassword("(samAccountName=%s)" % username, password,
382 force_password_change_at_next_login_req)
383 except Exception:
384 self.transaction_cancel()
385 raise
386 else:
387 self.transaction_commit()
389 def setpassword(self, search_filter, password,
390 force_change_at_next_login=False, username=None):
391 """Sets the password for a user
393 :param search_filter: LDAP filter to find the user (eg
394 samccountname=name)
395 :param password: Password for the user
396 :param force_change_at_next_login: Force password change
398 self.transaction_start()
399 try:
400 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
401 expression=search_filter, attrs=[])
402 if len(res) == 0:
403 raise Exception('Unable to find user "%s"' % (username or search_filter))
404 if len(res) > 1:
405 raise Exception('Matched %u multiple users with filter "%s"' % (len(res), search_filter))
406 user_dn = res[0].dn
407 setpw = """
408 dn: %s
409 changetype: modify
410 replace: unicodePwd
411 unicodePwd:: %s
412 """ % (user_dn, base64.b64encode(("\"" + password + "\"").encode('utf-16-le')))
414 self.modify_ldif(setpw)
416 if force_change_at_next_login:
417 self.force_password_change_at_next_login(
418 "(dn=" + str(user_dn) + ")")
420 # modify the userAccountControl to remove the disabled bit
421 self.enable_account(search_filter)
422 except Exception:
423 self.transaction_cancel()
424 raise
425 else:
426 self.transaction_commit()
428 def setexpiry(self, search_filter, expiry_seconds, no_expiry_req=False):
429 """Sets the account expiry for a user
431 :param search_filter: LDAP filter to find the user (eg
432 samaccountname=name)
433 :param expiry_seconds: expiry time from now in seconds
434 :param no_expiry_req: if set, then don't expire password
436 self.transaction_start()
437 try:
438 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
439 expression=search_filter,
440 attrs=["userAccountControl", "accountExpires"])
441 if len(res) == 0:
442 raise Exception('Unable to find user "%s"' % search_filter)
443 assert(len(res) == 1)
444 user_dn = res[0].dn
446 userAccountControl = int(res[0]["userAccountControl"][0])
447 accountExpires = int(res[0]["accountExpires"][0])
448 if no_expiry_req:
449 userAccountControl = userAccountControl | 0x10000
450 accountExpires = 0
451 else:
452 userAccountControl = userAccountControl & ~0x10000
453 accountExpires = samba.unix2nttime(expiry_seconds + int(time.time()))
455 setexp = """
456 dn: %s
457 changetype: modify
458 replace: userAccountControl
459 userAccountControl: %u
460 replace: accountExpires
461 accountExpires: %u
462 """ % (user_dn, userAccountControl, accountExpires)
464 self.modify_ldif(setexp)
465 except Exception:
466 self.transaction_cancel()
467 raise
468 else:
469 self.transaction_commit()
471 def set_domain_sid(self, sid):
472 """Change the domain SID used by this LDB.
474 :param sid: The new domain sid to use.
476 dsdb._samdb_set_domain_sid(self, sid)
478 def get_domain_sid(self):
479 """Read the domain SID used by this LDB. """
480 return dsdb._samdb_get_domain_sid(self)
482 domain_sid = property(get_domain_sid, set_domain_sid,
483 "SID for the domain")
485 def set_invocation_id(self, invocation_id):
486 """Set the invocation id for this SamDB handle.
488 :param invocation_id: GUID of the invocation id.
490 dsdb._dsdb_set_ntds_invocation_id(self, invocation_id)
492 def get_invocation_id(self):
493 """Get the invocation_id id"""
494 return dsdb._samdb_ntds_invocation_id(self)
496 invocation_id = property(get_invocation_id, set_invocation_id,
497 "Invocation ID GUID")
499 def get_oid_from_attid(self, attid):
500 return dsdb._dsdb_get_oid_from_attid(self, attid)
502 def get_attid_from_lDAPDisplayName(self, ldap_display_name,
503 is_schema_nc=False):
504 '''return the attribute ID for a LDAP attribute as an integer as found in DRSUAPI'''
505 return dsdb._dsdb_get_attid_from_lDAPDisplayName(self,
506 ldap_display_name, is_schema_nc)
508 def get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name):
509 '''return the syntax OID for a LDAP attribute as a string'''
510 return dsdb._dsdb_get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name)
512 def get_systemFlags_from_lDAPDisplayName(self, ldap_display_name):
513 '''return the systemFlags for a LDAP attribute as a integer'''
514 return dsdb._dsdb_get_systemFlags_from_lDAPDisplayName(self, ldap_display_name)
516 def get_linkId_from_lDAPDisplayName(self, ldap_display_name):
517 '''return the linkID for a LDAP attribute as a integer'''
518 return dsdb._dsdb_get_linkId_from_lDAPDisplayName(self, ldap_display_name)
520 def set_ntds_settings_dn(self, ntds_settings_dn):
521 """Set the NTDS Settings DN, as would be returned on the dsServiceName
522 rootDSE attribute.
524 This allows the DN to be set before the database fully exists
526 :param ntds_settings_dn: The new DN to use
528 dsdb._samdb_set_ntds_settings_dn(self, ntds_settings_dn)
530 def get_ntds_GUID(self):
531 """Get the NTDS objectGUID"""
532 return dsdb._samdb_ntds_objectGUID(self)
534 def server_site_name(self):
535 """Get the server site name"""
536 return dsdb._samdb_server_site_name(self)
538 def load_partition_usn(self, base_dn):
539 return dsdb._dsdb_load_partition_usn(self, base_dn)
541 def set_schema(self, schema):
542 self.set_schema_from_ldb(schema.ldb)
544 def set_schema_from_ldb(self, ldb_conn):
545 dsdb._dsdb_set_schema_from_ldb(self, ldb_conn)
547 def dsdb_DsReplicaAttribute(self, ldb, ldap_display_name, ldif_elements):
548 '''convert a list of attribute values to a DRSUAPI DsReplicaAttribute'''
549 return dsdb._dsdb_DsReplicaAttribute(ldb, ldap_display_name, ldif_elements)
551 def dsdb_normalise_attributes(self, ldb, ldap_display_name, ldif_elements):
552 '''normalise a list of attribute values'''
553 return dsdb._dsdb_normalise_attributes(ldb, ldap_display_name, ldif_elements)
555 def get_attribute_from_attid(self, attid):
556 """ Get from an attid the associated attribute
558 :param attid: The attribute id for searched attribute
559 :return: The name of the attribute associated with this id
561 if len(self.hash_oid_name.keys()) == 0:
562 self._populate_oid_attid()
563 if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)):
564 return self.hash_oid_name[self.get_oid_from_attid(attid)]
565 else:
566 return None
568 def _populate_oid_attid(self):
569 """Populate the hash hash_oid_name.
571 This hash contains the oid of the attribute as a key and
572 its display name as a value
574 self.hash_oid_name = {}
575 res = self.search(expression="objectClass=attributeSchema",
576 controls=["search_options:1:2"],
577 attrs=["attributeID",
578 "lDAPDisplayName"])
579 if len(res) > 0:
580 for e in res:
581 strDisplay = str(e.get("lDAPDisplayName"))
582 self.hash_oid_name[str(e.get("attributeID"))] = strDisplay
584 def get_attribute_replmetadata_version(self, dn, att):
585 """Get the version field trom the replPropertyMetaData for
586 the given field
588 :param dn: The on which we want to get the version
589 :param att: The name of the attribute
590 :return: The value of the version field in the replPropertyMetaData
591 for the given attribute. None if the attribute is not replicated
594 res = self.search(expression="dn=%s" % dn,
595 scope=ldb.SCOPE_SUBTREE,
596 controls=["search_options:1:2"],
597 attrs=["replPropertyMetaData"])
598 if len(res) == 0:
599 return None
601 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
602 str(res[0]["replPropertyMetaData"]))
603 ctr = repl.ctr
604 if len(self.hash_oid_name.keys()) == 0:
605 self._populate_oid_attid()
606 for o in ctr.array:
607 # Search for Description
608 att_oid = self.get_oid_from_attid(o.attid)
609 if self.hash_oid_name.has_key(att_oid) and\
610 att.lower() == self.hash_oid_name[att_oid].lower():
611 return o.version
612 return None
614 def set_attribute_replmetadata_version(self, dn, att, value,
615 addifnotexist=False):
616 res = self.search(expression="dn=%s" % dn,
617 scope=ldb.SCOPE_SUBTREE,
618 controls=["search_options:1:2"],
619 attrs=["replPropertyMetaData"])
620 if len(res) == 0:
621 return None
623 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
624 str(res[0]["replPropertyMetaData"]))
625 ctr = repl.ctr
626 now = samba.unix2nttime(int(time.time()))
627 found = False
628 if len(self.hash_oid_name.keys()) == 0:
629 self._populate_oid_attid()
630 for o in ctr.array:
631 # Search for Description
632 att_oid = self.get_oid_from_attid(o.attid)
633 if self.hash_oid_name.has_key(att_oid) and\
634 att.lower() == self.hash_oid_name[att_oid].lower():
635 found = True
636 seq = self.sequence_number(ldb.SEQ_NEXT)
637 o.version = value
638 o.originating_change_time = now
639 o.originating_invocation_id = misc.GUID(self.get_invocation_id())
640 o.originating_usn = seq
641 o.local_usn = seq
643 if not found and addifnotexist and len(ctr.array) >0:
644 o2 = drsblobs.replPropertyMetaData1()
645 o2.attid = 589914
646 att_oid = self.get_oid_from_attid(o2.attid)
647 seq = self.sequence_number(ldb.SEQ_NEXT)
648 o2.version = value
649 o2.originating_change_time = now
650 o2.originating_invocation_id = misc.GUID(self.get_invocation_id())
651 o2.originating_usn = seq
652 o2.local_usn = seq
653 found = True
654 tab = ctr.array
655 tab.append(o2)
656 ctr.count = ctr.count + 1
657 ctr.array = tab
659 if found :
660 replBlob = ndr_pack(repl)
661 msg = ldb.Message()
662 msg.dn = res[0].dn
663 msg["replPropertyMetaData"] = ldb.MessageElement(replBlob,
664 ldb.FLAG_MOD_REPLACE,
665 "replPropertyMetaData")
666 self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
668 def write_prefixes_from_schema(self):
669 dsdb._dsdb_write_prefixes_from_schema_to_ldb(self)
671 def get_partitions_dn(self):
672 return dsdb._dsdb_get_partitions_dn(self)
674 def set_minPwdAge(self, value):
675 m = ldb.Message()
676 m.dn = ldb.Dn(self, self.domain_dn())
677 m["minPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdAge")
678 self.modify(m)
680 def get_minPwdAge(self):
681 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdAge"])
682 if len(res) == 0:
683 return None
684 elif not "minPwdAge" in res[0]:
685 return None
686 else:
687 return res[0]["minPwdAge"][0]
689 def set_minPwdLength(self, value):
690 m = ldb.Message()
691 m.dn = ldb.Dn(self, self.domain_dn())
692 m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength")
693 self.modify(m)
695 def get_minPwdLength(self):
696 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"])
697 if len(res) == 0:
698 return None
699 elif not "minPwdLength" in res[0]:
700 return None
701 else:
702 return res[0]["minPwdLength"][0]
704 def set_pwdProperties(self, value):
705 m = ldb.Message()
706 m.dn = ldb.Dn(self, self.domain_dn())
707 m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties")
708 self.modify(m)
710 def get_pwdProperties(self):
711 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"])
712 if len(res) == 0:
713 return None
714 elif not "pwdProperties" in res[0]:
715 return None
716 else:
717 return res[0]["pwdProperties"][0]
719 def set_dsheuristics(self, dsheuristics):
720 m = ldb.Message()
721 m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s"
722 % self.get_config_basedn().get_linearized())
723 if dsheuristics is not None:
724 m["dSHeuristics"] = ldb.MessageElement(dsheuristics,
725 ldb.FLAG_MOD_REPLACE, "dSHeuristics")
726 else:
727 m["dSHeuristics"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
728 "dSHeuristics")
729 self.modify(m)
731 def get_dsheuristics(self):
732 res = self.search("CN=Directory Service,CN=Windows NT,CN=Services,%s"
733 % self.get_config_basedn().get_linearized(),
734 scope=ldb.SCOPE_BASE, attrs=["dSHeuristics"])
735 if len(res) == 0:
736 dsheuristics = None
737 elif "dSHeuristics" in res[0]:
738 dsheuristics = res[0]["dSHeuristics"][0]
739 else:
740 dsheuristics = None
742 return dsheuristics
744 def create_ou(self, ou_dn, description=None, name=None, sd=None):
745 """Creates an organizationalUnit object
746 :param ou_dn: dn of the new object
747 :param description: description attribute
748 :param name: name atttribute
749 :param sd: security descriptor of the object, can be
750 an SDDL string or security.descriptor type
752 m = {"dn": ou_dn,
753 "objectClass": "organizationalUnit"}
755 if description:
756 m["description"] = description
757 if name:
758 m["name"] = name
760 if sd:
761 m["nTSecurityDescriptor"] = ndr_pack(sd)
762 self.add(m)
764 def normalise_int32(self, ivalue):
765 '''normalise a ldap integer to signed 32 bit'''
766 if int(ivalue) & 0x80000000:
767 return str(int(ivalue) - 0x100000000)
768 return str(ivalue)