addns: Remove unused empty header file
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / samdb.py
blob5cceb062eaf074692c3a2d83e5c3143b7dd7db57
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 import os
31 from samba import dsdb
32 from samba.ndr import ndr_unpack, ndr_pack
33 from samba.dcerpc import drsblobs, misc
34 from samba.common import normalise_int32
36 __docformat__ = "restructuredText"
39 class SamDB(samba.Ldb):
40 """The SAM database."""
42 hash_oid_name = {}
44 def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
45 credentials=None, flags=0, options=None, global_schema=True,
46 auto_connect=True, am_rodc=None):
47 self.lp = lp
48 if not auto_connect:
49 url = None
50 elif url is None and lp is not None:
51 url = lp.samdb_url()
53 self.url = url
55 super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
56 session_info=session_info, credentials=credentials, flags=flags,
57 options=options)
59 if global_schema:
60 dsdb._dsdb_set_global_schema(self)
62 if am_rodc is not None:
63 dsdb._dsdb_set_am_rodc(self, am_rodc)
65 def connect(self, url=None, flags=0, options=None):
66 '''connect to the database'''
67 if self.lp is not None and not os.path.exists(url):
68 url = self.lp.private_path(url)
69 self.url = url
71 super(SamDB, self).connect(url=url, flags=flags,
72 options=options)
74 def am_rodc(self):
75 '''return True if we are an RODC'''
76 return dsdb._am_rodc(self)
78 def domain_dn(self):
79 '''return the domain DN'''
80 return str(self.get_default_basedn())
82 def enable_account(self, search_filter):
83 """Enables an account
85 :param search_filter: LDAP filter to find the user (eg
86 samccountname=name)
87 """
89 flags = samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_PASSWD_NOTREQD
90 self.toggle_userAccountFlags(search_filter, flags, on=False)
92 def toggle_userAccountFlags(self, search_filter, flags, on=True, strict=False):
93 """toggle_userAccountFlags
95 :param search_filter: LDAP filter to find the user (eg
96 samccountname=name)
97 :flags: samba.dsdb.UF_* flags
98 :on: on=True (default) => set, on=False => unset
99 :strict: strict=False (default) ignore if no action is needed
100 strict=True raises an Exception if...
102 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
103 expression=search_filter, attrs=["userAccountControl"])
104 if len(res) == 0:
105 raise Exception('Unable to find user "%s"' % search_filter)
106 assert(len(res) == 1)
107 account_dn = res[0].dn
109 old_uac = int(res[0]["userAccountControl"][0])
110 if on:
111 if strict and (old_uac & flags):
112 error = 'userAccountFlags[%d:0x%08X] already contain 0x%X' % (old_uac, old_uac, flags)
113 raise Exception(error)
115 new_uac = old_uac | flags
116 else:
117 if strict and not (old_uac & flags):
118 error = 'userAccountFlags[%d:0x%08X] not contain 0x%X' % (old_uac, old_uac, flags)
119 raise Exception(error)
121 new_uac = old_uac & ~flags
123 if old_uac == new_uac:
124 return
126 mod = """
127 dn: %s
128 changetype: modify
129 delete: userAccountControl
130 userAccountControl: %u
131 add: userAccountControl
132 userAccountControl: %u
133 """ % (account_dn, old_uac, new_uac)
134 self.modify_ldif(mod)
136 def force_password_change_at_next_login(self, search_filter):
137 """Forces a password change at next login
139 :param search_filter: LDAP filter to find the user (eg
140 samccountname=name)
142 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
143 expression=search_filter, attrs=[])
144 if len(res) == 0:
145 raise Exception('Unable to find user "%s"' % search_filter)
146 assert(len(res) == 1)
147 user_dn = res[0].dn
149 mod = """
150 dn: %s
151 changetype: modify
152 replace: pwdLastSet
153 pwdLastSet: 0
154 """ % (user_dn)
155 self.modify_ldif(mod)
157 def newgroup(self, groupname, groupou=None, grouptype=None,
158 description=None, mailaddress=None, notes=None, sd=None):
159 """Adds a new group with additional parameters
161 :param groupname: Name of the new group
162 :param grouptype: Type of the new group
163 :param description: Description of the new group
164 :param mailaddress: Email address of the new group
165 :param notes: Notes of the new group
166 :param sd: security descriptor of the object
169 group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn())
171 # The new user record. Note the reliance on the SAMLDB module which
172 # fills in the default informations
173 ldbmessage = {"dn": group_dn,
174 "sAMAccountName": groupname,
175 "objectClass": "group"}
177 if grouptype is not None:
178 ldbmessage["groupType"] = normalise_int32(grouptype)
180 if description is not None:
181 ldbmessage["description"] = description
183 if mailaddress is not None:
184 ldbmessage["mail"] = mailaddress
186 if notes is not None:
187 ldbmessage["info"] = notes
189 if sd is not None:
190 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
192 self.add(ldbmessage)
194 def deletegroup(self, groupname):
195 """Deletes a group
197 :param groupname: Name of the target group
200 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
201 self.transaction_start()
202 try:
203 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
204 expression=groupfilter, attrs=[])
205 if len(targetgroup) == 0:
206 raise Exception('Unable to find group "%s"' % groupname)
207 assert(len(targetgroup) == 1)
208 self.delete(targetgroup[0].dn)
209 except Exception:
210 self.transaction_cancel()
211 raise
212 else:
213 self.transaction_commit()
215 def add_remove_group_members(self, groupname, listofmembers,
216 add_members_operation=True):
217 """Adds or removes group members
219 :param groupname: Name of the target group
220 :param listofmembers: Comma-separated list of group members
221 :param add_members_operation: Defines if its an add or remove
222 operation
225 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (
226 ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
227 groupmembers = listofmembers.split(',')
229 self.transaction_start()
230 try:
231 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
232 expression=groupfilter, attrs=['member'])
233 if len(targetgroup) == 0:
234 raise Exception('Unable to find group "%s"' % groupname)
235 assert(len(targetgroup) == 1)
237 modified = False
239 addtargettogroup = """
240 dn: %s
241 changetype: modify
242 """ % (str(targetgroup[0].dn))
244 for member in groupmembers:
245 targetmember = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
246 expression="(|(sAMAccountName=%s)(CN=%s))" % (
247 ldb.binary_encode(member), ldb.binary_encode(member)), attrs=[])
249 if len(targetmember) != 1:
250 continue
252 if add_members_operation is True and (targetgroup[0].get('member') is None or str(targetmember[0].dn) not in targetgroup[0]['member']):
253 modified = True
254 addtargettogroup += """add: member
255 member: %s
256 """ % (str(targetmember[0].dn))
258 elif add_members_operation is False and (targetgroup[0].get('member') is not None and str(targetmember[0].dn) in targetgroup[0]['member']):
259 modified = True
260 addtargettogroup += """delete: member
261 member: %s
262 """ % (str(targetmember[0].dn))
264 if modified is True:
265 self.modify_ldif(addtargettogroup)
267 except Exception:
268 self.transaction_cancel()
269 raise
270 else:
271 self.transaction_commit()
273 def newuser(self, username, password,
274 force_password_change_at_next_login_req=False,
275 useusernameascn=False, userou=None, surname=None, givenname=None,
276 initials=None, profilepath=None, scriptpath=None, homedrive=None,
277 homedirectory=None, jobtitle=None, department=None, company=None,
278 description=None, mailaddress=None, internetaddress=None,
279 telephonenumber=None, physicaldeliveryoffice=None, sd=None,
280 setpassword=True):
281 """Adds a new user with additional parameters
283 :param username: Name of the new user
284 :param password: Password for the new user
285 :param force_password_change_at_next_login_req: Force password change
286 :param useusernameascn: Use username as cn rather that firstname +
287 initials + lastname
288 :param userou: Object container (without domainDN postfix) for new user
289 :param surname: Surname of the new user
290 :param givenname: First name of the new user
291 :param initials: Initials of the new user
292 :param profilepath: Profile path of the new user
293 :param scriptpath: Logon script path of the new user
294 :param homedrive: Home drive of the new user
295 :param homedirectory: Home directory of the new user
296 :param jobtitle: Job title of the new user
297 :param department: Department of the new user
298 :param company: Company of the new user
299 :param description: of the new user
300 :param mailaddress: Email address of the new user
301 :param internetaddress: Home page of the new user
302 :param telephonenumber: Phone number of the new user
303 :param physicaldeliveryoffice: Office location of the new user
304 :param sd: security descriptor of the object
305 :param setpassword: optionally disable password reset
308 displayname = ""
309 if givenname is not None:
310 displayname += givenname
312 if initials is not None:
313 displayname += ' %s.' % initials
315 if surname is not None:
316 displayname += ' %s' % surname
318 cn = username
319 if useusernameascn is None and displayname is not "":
320 cn = displayname
322 user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn())
324 dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "")
325 user_principal_name = "%s@%s" % (username, dnsdomain)
326 # The new user record. Note the reliance on the SAMLDB module which
327 # fills in the default informations
328 ldbmessage = {"dn": user_dn,
329 "sAMAccountName": username,
330 "userPrincipalName": user_principal_name,
331 "objectClass": "user"}
333 if surname is not None:
334 ldbmessage["sn"] = surname
336 if givenname is not None:
337 ldbmessage["givenName"] = givenname
339 if displayname is not "":
340 ldbmessage["displayName"] = displayname
341 ldbmessage["name"] = displayname
343 if initials is not None:
344 ldbmessage["initials"] = '%s.' % initials
346 if profilepath is not None:
347 ldbmessage["profilePath"] = profilepath
349 if scriptpath is not None:
350 ldbmessage["scriptPath"] = scriptpath
352 if homedrive is not None:
353 ldbmessage["homeDrive"] = homedrive
355 if homedirectory is not None:
356 ldbmessage["homeDirectory"] = homedirectory
358 if jobtitle is not None:
359 ldbmessage["title"] = jobtitle
361 if department is not None:
362 ldbmessage["department"] = department
364 if company is not None:
365 ldbmessage["company"] = company
367 if description is not None:
368 ldbmessage["description"] = description
370 if mailaddress is not None:
371 ldbmessage["mail"] = mailaddress
373 if internetaddress is not None:
374 ldbmessage["wWWHomePage"] = internetaddress
376 if telephonenumber is not None:
377 ldbmessage["telephoneNumber"] = telephonenumber
379 if physicaldeliveryoffice is not None:
380 ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice
382 if sd is not None:
383 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
385 self.transaction_start()
386 try:
387 self.add(ldbmessage)
389 # Sets the password for it
390 if setpassword:
391 self.setpassword("(samAccountName=%s)" % ldb.binary_encode(username), password,
392 force_password_change_at_next_login_req)
393 except Exception:
394 self.transaction_cancel()
395 raise
396 else:
397 self.transaction_commit()
400 def deleteuser(self, username):
401 """Deletes a user
403 :param username: Name of the target user
406 filter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(username), "CN=Person,CN=Schema,CN=Configuration", self.domain_dn())
407 self.transaction_start()
408 try:
409 target = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
410 expression=filter, attrs=[])
411 if len(target) == 0:
412 raise Exception('Unable to find user "%s"' % username)
413 assert(len(target) == 1)
414 self.delete(target[0].dn)
415 except Exception:
416 self.transaction_cancel()
417 raise
418 else:
419 self.transaction_commit()
422 def setpassword(self, search_filter, password,
423 force_change_at_next_login=False, username=None):
424 """Sets the password for a user
426 :param search_filter: LDAP filter to find the user (eg
427 samccountname=name)
428 :param password: Password for the user
429 :param force_change_at_next_login: Force password change
431 self.transaction_start()
432 try:
433 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
434 expression=search_filter, attrs=[])
435 if len(res) == 0:
436 raise Exception('Unable to find user "%s"' % (username or search_filter))
437 if len(res) > 1:
438 raise Exception('Matched %u multiple users with filter "%s"' % (len(res), search_filter))
439 user_dn = res[0].dn
440 setpw = """
441 dn: %s
442 changetype: modify
443 replace: unicodePwd
444 unicodePwd:: %s
445 """ % (user_dn, base64.b64encode(("\"" + password + "\"").encode('utf-16-le')))
447 self.modify_ldif(setpw)
449 if force_change_at_next_login:
450 self.force_password_change_at_next_login(
451 "(dn=" + str(user_dn) + ")")
453 # modify the userAccountControl to remove the disabled bit
454 self.enable_account(search_filter)
455 except Exception:
456 self.transaction_cancel()
457 raise
458 else:
459 self.transaction_commit()
461 def setexpiry(self, search_filter, expiry_seconds, no_expiry_req=False):
462 """Sets the account expiry for a user
464 :param search_filter: LDAP filter to find the user (eg
465 samaccountname=name)
466 :param expiry_seconds: expiry time from now in seconds
467 :param no_expiry_req: if set, then don't expire password
469 self.transaction_start()
470 try:
471 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
472 expression=search_filter,
473 attrs=["userAccountControl", "accountExpires"])
474 if len(res) == 0:
475 raise Exception('Unable to find user "%s"' % search_filter)
476 assert(len(res) == 1)
477 user_dn = res[0].dn
479 userAccountControl = int(res[0]["userAccountControl"][0])
480 accountExpires = int(res[0]["accountExpires"][0])
481 if no_expiry_req:
482 userAccountControl = userAccountControl | 0x10000
483 accountExpires = 0
484 else:
485 userAccountControl = userAccountControl & ~0x10000
486 accountExpires = samba.unix2nttime(expiry_seconds + int(time.time()))
488 setexp = """
489 dn: %s
490 changetype: modify
491 replace: userAccountControl
492 userAccountControl: %u
493 replace: accountExpires
494 accountExpires: %u
495 """ % (user_dn, userAccountControl, accountExpires)
497 self.modify_ldif(setexp)
498 except Exception:
499 self.transaction_cancel()
500 raise
501 else:
502 self.transaction_commit()
504 def set_domain_sid(self, sid):
505 """Change the domain SID used by this LDB.
507 :param sid: The new domain sid to use.
509 dsdb._samdb_set_domain_sid(self, sid)
511 def get_domain_sid(self):
512 """Read the domain SID used by this LDB. """
513 return dsdb._samdb_get_domain_sid(self)
515 domain_sid = property(get_domain_sid, set_domain_sid,
516 "SID for the domain")
518 def set_invocation_id(self, invocation_id):
519 """Set the invocation id for this SamDB handle.
521 :param invocation_id: GUID of the invocation id.
523 dsdb._dsdb_set_ntds_invocation_id(self, invocation_id)
525 def get_invocation_id(self):
526 """Get the invocation_id id"""
527 return dsdb._samdb_ntds_invocation_id(self)
529 invocation_id = property(get_invocation_id, set_invocation_id,
530 "Invocation ID GUID")
532 def get_oid_from_attid(self, attid):
533 return dsdb._dsdb_get_oid_from_attid(self, attid)
535 def get_attid_from_lDAPDisplayName(self, ldap_display_name,
536 is_schema_nc=False):
537 '''return the attribute ID for a LDAP attribute as an integer as found in DRSUAPI'''
538 return dsdb._dsdb_get_attid_from_lDAPDisplayName(self,
539 ldap_display_name, is_schema_nc)
541 def get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name):
542 '''return the syntax OID for a LDAP attribute as a string'''
543 return dsdb._dsdb_get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name)
545 def get_systemFlags_from_lDAPDisplayName(self, ldap_display_name):
546 '''return the systemFlags for a LDAP attribute as a integer'''
547 return dsdb._dsdb_get_systemFlags_from_lDAPDisplayName(self, ldap_display_name)
549 def get_linkId_from_lDAPDisplayName(self, ldap_display_name):
550 '''return the linkID for a LDAP attribute as a integer'''
551 return dsdb._dsdb_get_linkId_from_lDAPDisplayName(self, ldap_display_name)
553 def get_lDAPDisplayName_by_attid(self, attid):
554 '''return the lDAPDisplayName from an integer DRS attribute ID'''
555 return dsdb._dsdb_get_lDAPDisplayName_by_attid(self, attid)
557 def get_backlink_from_lDAPDisplayName(self, ldap_display_name):
558 '''return the attribute name of the corresponding backlink from the name
559 of a forward link attribute. If there is no backlink return None'''
560 return dsdb._dsdb_get_backlink_from_lDAPDisplayName(self, ldap_display_name)
562 def set_ntds_settings_dn(self, ntds_settings_dn):
563 """Set the NTDS Settings DN, as would be returned on the dsServiceName
564 rootDSE attribute.
566 This allows the DN to be set before the database fully exists
568 :param ntds_settings_dn: The new DN to use
570 dsdb._samdb_set_ntds_settings_dn(self, ntds_settings_dn)
572 def get_ntds_GUID(self):
573 """Get the NTDS objectGUID"""
574 return dsdb._samdb_ntds_objectGUID(self)
576 def server_site_name(self):
577 """Get the server site name"""
578 return dsdb._samdb_server_site_name(self)
580 def host_dns_name(self):
581 """return the DNS name of this host"""
582 res = self.search(base='', scope=ldb.SCOPE_BASE, attrs=['dNSHostName'])
583 return res[0]['dNSHostName'][0]
585 def domain_dns_name(self):
586 """return the DNS name of the domain root"""
587 domain_dn = self.get_default_basedn()
588 return domain_dn.canonical_str().split('/')[0]
590 def forest_dns_name(self):
591 """return the DNS name of the forest root"""
592 forest_dn = self.get_root_basedn()
593 return forest_dn.canonical_str().split('/')[0]
595 def load_partition_usn(self, base_dn):
596 return dsdb._dsdb_load_partition_usn(self, base_dn)
598 def set_schema(self, schema):
599 self.set_schema_from_ldb(schema.ldb)
601 def set_schema_from_ldb(self, ldb_conn):
602 dsdb._dsdb_set_schema_from_ldb(self, ldb_conn)
604 def dsdb_DsReplicaAttribute(self, ldb, ldap_display_name, ldif_elements):
605 '''convert a list of attribute values to a DRSUAPI DsReplicaAttribute'''
606 return dsdb._dsdb_DsReplicaAttribute(ldb, ldap_display_name, ldif_elements)
608 def dsdb_normalise_attributes(self, ldb, ldap_display_name, ldif_elements):
609 '''normalise a list of attribute values'''
610 return dsdb._dsdb_normalise_attributes(ldb, ldap_display_name, ldif_elements)
612 def get_attribute_from_attid(self, attid):
613 """ Get from an attid the associated attribute
615 :param attid: The attribute id for searched attribute
616 :return: The name of the attribute associated with this id
618 if len(self.hash_oid_name.keys()) == 0:
619 self._populate_oid_attid()
620 if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)):
621 return self.hash_oid_name[self.get_oid_from_attid(attid)]
622 else:
623 return None
625 def _populate_oid_attid(self):
626 """Populate the hash hash_oid_name.
628 This hash contains the oid of the attribute as a key and
629 its display name as a value
631 self.hash_oid_name = {}
632 res = self.search(expression="objectClass=attributeSchema",
633 controls=["search_options:1:2"],
634 attrs=["attributeID",
635 "lDAPDisplayName"])
636 if len(res) > 0:
637 for e in res:
638 strDisplay = str(e.get("lDAPDisplayName"))
639 self.hash_oid_name[str(e.get("attributeID"))] = strDisplay
641 def get_attribute_replmetadata_version(self, dn, att):
642 """Get the version field trom the replPropertyMetaData for
643 the given field
645 :param dn: The on which we want to get the version
646 :param att: The name of the attribute
647 :return: The value of the version field in the replPropertyMetaData
648 for the given attribute. None if the attribute is not replicated
651 res = self.search(expression="dn=%s" % dn,
652 scope=ldb.SCOPE_SUBTREE,
653 controls=["search_options:1:2"],
654 attrs=["replPropertyMetaData"])
655 if len(res) == 0:
656 return None
658 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
659 str(res[0]["replPropertyMetaData"]))
660 ctr = repl.ctr
661 if len(self.hash_oid_name.keys()) == 0:
662 self._populate_oid_attid()
663 for o in ctr.array:
664 # Search for Description
665 att_oid = self.get_oid_from_attid(o.attid)
666 if self.hash_oid_name.has_key(att_oid) and\
667 att.lower() == self.hash_oid_name[att_oid].lower():
668 return o.version
669 return None
671 def set_attribute_replmetadata_version(self, dn, att, value,
672 addifnotexist=False):
673 res = self.search(expression="dn=%s" % dn,
674 scope=ldb.SCOPE_SUBTREE,
675 controls=["search_options:1:2"],
676 attrs=["replPropertyMetaData"])
677 if len(res) == 0:
678 return None
680 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
681 str(res[0]["replPropertyMetaData"]))
682 ctr = repl.ctr
683 now = samba.unix2nttime(int(time.time()))
684 found = False
685 if len(self.hash_oid_name.keys()) == 0:
686 self._populate_oid_attid()
687 for o in ctr.array:
688 # Search for Description
689 att_oid = self.get_oid_from_attid(o.attid)
690 if self.hash_oid_name.has_key(att_oid) and\
691 att.lower() == self.hash_oid_name[att_oid].lower():
692 found = True
693 seq = self.sequence_number(ldb.SEQ_NEXT)
694 o.version = value
695 o.originating_change_time = now
696 o.originating_invocation_id = misc.GUID(self.get_invocation_id())
697 o.originating_usn = seq
698 o.local_usn = seq
700 if not found and addifnotexist and len(ctr.array) >0:
701 o2 = drsblobs.replPropertyMetaData1()
702 o2.attid = 589914
703 att_oid = self.get_oid_from_attid(o2.attid)
704 seq = self.sequence_number(ldb.SEQ_NEXT)
705 o2.version = value
706 o2.originating_change_time = now
707 o2.originating_invocation_id = misc.GUID(self.get_invocation_id())
708 o2.originating_usn = seq
709 o2.local_usn = seq
710 found = True
711 tab = ctr.array
712 tab.append(o2)
713 ctr.count = ctr.count + 1
714 ctr.array = tab
716 if found :
717 replBlob = ndr_pack(repl)
718 msg = ldb.Message()
719 msg.dn = res[0].dn
720 msg["replPropertyMetaData"] = ldb.MessageElement(replBlob,
721 ldb.FLAG_MOD_REPLACE,
722 "replPropertyMetaData")
723 self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
725 def write_prefixes_from_schema(self):
726 dsdb._dsdb_write_prefixes_from_schema_to_ldb(self)
728 def get_partitions_dn(self):
729 return dsdb._dsdb_get_partitions_dn(self)
731 def set_minPwdAge(self, value):
732 m = ldb.Message()
733 m.dn = ldb.Dn(self, self.domain_dn())
734 m["minPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdAge")
735 self.modify(m)
737 def get_minPwdAge(self):
738 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdAge"])
739 if len(res) == 0:
740 return None
741 elif not "minPwdAge" in res[0]:
742 return None
743 else:
744 return res[0]["minPwdAge"][0]
746 def set_minPwdLength(self, value):
747 m = ldb.Message()
748 m.dn = ldb.Dn(self, self.domain_dn())
749 m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength")
750 self.modify(m)
752 def get_minPwdLength(self):
753 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"])
754 if len(res) == 0:
755 return None
756 elif not "minPwdLength" in res[0]:
757 return None
758 else:
759 return res[0]["minPwdLength"][0]
761 def set_pwdProperties(self, value):
762 m = ldb.Message()
763 m.dn = ldb.Dn(self, self.domain_dn())
764 m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties")
765 self.modify(m)
767 def get_pwdProperties(self):
768 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"])
769 if len(res) == 0:
770 return None
771 elif not "pwdProperties" in res[0]:
772 return None
773 else:
774 return res[0]["pwdProperties"][0]
776 def set_dsheuristics(self, dsheuristics):
777 m = ldb.Message()
778 m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s"
779 % self.get_config_basedn().get_linearized())
780 if dsheuristics is not None:
781 m["dSHeuristics"] = ldb.MessageElement(dsheuristics,
782 ldb.FLAG_MOD_REPLACE, "dSHeuristics")
783 else:
784 m["dSHeuristics"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
785 "dSHeuristics")
786 self.modify(m)
788 def get_dsheuristics(self):
789 res = self.search("CN=Directory Service,CN=Windows NT,CN=Services,%s"
790 % self.get_config_basedn().get_linearized(),
791 scope=ldb.SCOPE_BASE, attrs=["dSHeuristics"])
792 if len(res) == 0:
793 dsheuristics = None
794 elif "dSHeuristics" in res[0]:
795 dsheuristics = res[0]["dSHeuristics"][0]
796 else:
797 dsheuristics = None
799 return dsheuristics
801 def create_ou(self, ou_dn, description=None, name=None, sd=None):
802 """Creates an organizationalUnit object
803 :param ou_dn: dn of the new object
804 :param description: description attribute
805 :param name: name atttribute
806 :param sd: security descriptor of the object, can be
807 an SDDL string or security.descriptor type
809 m = {"dn": ou_dn,
810 "objectClass": "organizationalUnit"}
812 if description:
813 m["description"] = description
814 if name:
815 m["name"] = name
817 if sd:
818 m["nTSecurityDescriptor"] = ndr_pack(sd)
819 self.add(m)