s4 provision/dns: Move secretsdb_setup_dns to the AD DNS specific setup
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / samdb.py
blobdf05a5208b96da309bfb6e7fc1d2404d446f6e42
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
9 # Copyright (C) Giampaolo Lauria <lauria2@yahoo.com> 2011
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 """Convenience functions for using the SAM."""
27 import samba
28 import ldb
29 import time
30 import base64
31 import os
32 from samba import dsdb
33 from samba.ndr import ndr_unpack, ndr_pack
34 from samba.dcerpc import drsblobs, misc
35 from samba.common import normalise_int32
37 __docformat__ = "restructuredText"
40 class SamDB(samba.Ldb):
41 """The SAM database."""
43 hash_oid_name = {}
45 def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
46 credentials=None, flags=0, options=None, global_schema=True,
47 auto_connect=True, am_rodc=None):
48 self.lp = lp
49 if not auto_connect:
50 url = None
51 elif url is None and lp is not None:
52 url = lp.samdb_url()
54 self.url = url
56 super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
57 session_info=session_info, credentials=credentials, flags=flags,
58 options=options)
60 if global_schema:
61 dsdb._dsdb_set_global_schema(self)
63 if am_rodc is not None:
64 dsdb._dsdb_set_am_rodc(self, am_rodc)
66 def connect(self, url=None, flags=0, options=None):
67 '''connect to the database'''
68 if self.lp is not None and not os.path.exists(url):
69 url = self.lp.private_path(url)
70 self.url = url
72 super(SamDB, self).connect(url=url, flags=flags,
73 options=options)
75 def am_rodc(self):
76 '''return True if we are an RODC'''
77 return dsdb._am_rodc(self)
79 def domain_dn(self):
80 '''return the domain DN'''
81 return str(self.get_default_basedn())
83 def enable_account(self, search_filter):
84 """Enables an account
86 :param search_filter: LDAP filter to find the user (eg
87 samccountname=name)
88 """
90 flags = samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_PASSWD_NOTREQD
91 self.toggle_userAccountFlags(search_filter, flags, on=False)
93 def toggle_userAccountFlags(self, search_filter, flags, flags_str=None,
94 on=True, strict=False):
95 """toggle_userAccountFlags
97 :param search_filter: LDAP filter to find the user (eg
98 samccountname=name)
99 :flags: samba.dsdb.UF_* flags
100 :on: on=True (default) => set, on=False => unset
101 :strict: strict=False (default) ignore if no action is needed
102 strict=True raises an Exception if...
104 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
105 expression=search_filter, attrs=["userAccountControl"])
106 if len(res) == 0:
107 raise Exception("Unable to find account where '%s'" % search_filter)
108 assert(len(res) == 1)
109 account_dn = res[0].dn
111 old_uac = int(res[0]["userAccountControl"][0])
112 if on:
113 if strict and (old_uac & flags):
114 error = "Account flag(s) '%s' already set" % flags_str
115 raise Exception(error)
117 new_uac = old_uac | flags
118 else:
119 if strict and not (old_uac & flags):
120 error = "Account flag(s) '%s' already unset" % flags_str
121 raise Exception(error)
123 new_uac = old_uac & ~flags
125 if old_uac == new_uac:
126 return
128 mod = """
129 dn: %s
130 changetype: modify
131 delete: userAccountControl
132 userAccountControl: %u
133 add: userAccountControl
134 userAccountControl: %u
135 """ % (account_dn, old_uac, new_uac)
136 self.modify_ldif(mod)
138 def force_password_change_at_next_login(self, search_filter):
139 """Forces a password change at next login
141 :param search_filter: LDAP filter to find the user (eg
142 samccountname=name)
144 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
145 expression=search_filter, attrs=[])
146 if len(res) == 0:
147 raise Exception('Unable to find user "%s"' % search_filter)
148 assert(len(res) == 1)
149 user_dn = res[0].dn
151 mod = """
152 dn: %s
153 changetype: modify
154 replace: pwdLastSet
155 pwdLastSet: 0
156 """ % (user_dn)
157 self.modify_ldif(mod)
159 def newgroup(self, groupname, groupou=None, grouptype=None,
160 description=None, mailaddress=None, notes=None, sd=None):
161 """Adds a new group with additional parameters
163 :param groupname: Name of the new group
164 :param grouptype: Type of the new group
165 :param description: Description of the new group
166 :param mailaddress: Email address of the new group
167 :param notes: Notes of the new group
168 :param sd: security descriptor of the object
171 group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn())
173 # The new user record. Note the reliance on the SAMLDB module which
174 # fills in the default informations
175 ldbmessage = {"dn": group_dn,
176 "sAMAccountName": groupname,
177 "objectClass": "group"}
179 if grouptype is not None:
180 ldbmessage["groupType"] = normalise_int32(grouptype)
182 if description is not None:
183 ldbmessage["description"] = description
185 if mailaddress is not None:
186 ldbmessage["mail"] = mailaddress
188 if notes is not None:
189 ldbmessage["info"] = notes
191 if sd is not None:
192 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
194 self.add(ldbmessage)
196 def deletegroup(self, groupname):
197 """Deletes a group
199 :param groupname: Name of the target group
202 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
203 self.transaction_start()
204 try:
205 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
206 expression=groupfilter, attrs=[])
207 if len(targetgroup) == 0:
208 raise Exception('Unable to find group "%s"' % groupname)
209 assert(len(targetgroup) == 1)
210 self.delete(targetgroup[0].dn)
211 except Exception:
212 self.transaction_cancel()
213 raise
214 else:
215 self.transaction_commit()
217 def add_remove_group_members(self, groupname, listofmembers,
218 add_members_operation=True):
219 """Adds or removes group members
221 :param groupname: Name of the target group
222 :param listofmembers: Comma-separated list of group members
223 :param add_members_operation: Defines if its an add or remove
224 operation
227 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (
228 ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
229 groupmembers = listofmembers.split(',')
231 self.transaction_start()
232 try:
233 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
234 expression=groupfilter, attrs=['member'])
235 if len(targetgroup) == 0:
236 raise Exception('Unable to find group "%s"' % groupname)
237 assert(len(targetgroup) == 1)
239 modified = False
241 addtargettogroup = """
242 dn: %s
243 changetype: modify
244 """ % (str(targetgroup[0].dn))
246 for member in groupmembers:
247 targetmember = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
248 expression="(|(sAMAccountName=%s)(CN=%s))" % (
249 ldb.binary_encode(member), ldb.binary_encode(member)), attrs=[])
251 if len(targetmember) != 1:
252 continue
254 if add_members_operation is True and (targetgroup[0].get('member') is None or str(targetmember[0].dn) not in targetgroup[0]['member']):
255 modified = True
256 addtargettogroup += """add: member
257 member: %s
258 """ % (str(targetmember[0].dn))
260 elif add_members_operation is False and (targetgroup[0].get('member') is not None and str(targetmember[0].dn) in targetgroup[0]['member']):
261 modified = True
262 addtargettogroup += """delete: member
263 member: %s
264 """ % (str(targetmember[0].dn))
266 if modified is True:
267 self.modify_ldif(addtargettogroup)
269 except Exception:
270 self.transaction_cancel()
271 raise
272 else:
273 self.transaction_commit()
275 def newuser(self, username, password,
276 force_password_change_at_next_login_req=False,
277 useusernameascn=False, userou=None, surname=None, givenname=None,
278 initials=None, profilepath=None, scriptpath=None, homedrive=None,
279 homedirectory=None, jobtitle=None, department=None, company=None,
280 description=None, mailaddress=None, internetaddress=None,
281 telephonenumber=None, physicaldeliveryoffice=None, sd=None,
282 setpassword=True):
283 """Adds a new user with additional parameters
285 :param username: Name of the new user
286 :param password: Password for the new user
287 :param force_password_change_at_next_login_req: Force password change
288 :param useusernameascn: Use username as cn rather that firstname +
289 initials + lastname
290 :param userou: Object container (without domainDN postfix) for new user
291 :param surname: Surname of the new user
292 :param givenname: First name of the new user
293 :param initials: Initials of the new user
294 :param profilepath: Profile path of the new user
295 :param scriptpath: Logon script path of the new user
296 :param homedrive: Home drive of the new user
297 :param homedirectory: Home directory of the new user
298 :param jobtitle: Job title of the new user
299 :param department: Department of the new user
300 :param company: Company of the new user
301 :param description: of the new user
302 :param mailaddress: Email address of the new user
303 :param internetaddress: Home page of the new user
304 :param telephonenumber: Phone number of the new user
305 :param physicaldeliveryoffice: Office location of the new user
306 :param sd: security descriptor of the object
307 :param setpassword: optionally disable password reset
310 displayname = ""
311 if givenname is not None:
312 displayname += givenname
314 if initials is not None:
315 displayname += ' %s.' % initials
317 if surname is not None:
318 displayname += ' %s' % surname
320 cn = username
321 if useusernameascn is None and displayname is not "":
322 cn = displayname
324 user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn())
326 dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "")
327 user_principal_name = "%s@%s" % (username, dnsdomain)
328 # The new user record. Note the reliance on the SAMLDB module which
329 # fills in the default informations
330 ldbmessage = {"dn": user_dn,
331 "sAMAccountName": username,
332 "userPrincipalName": user_principal_name,
333 "objectClass": "user"}
335 if surname is not None:
336 ldbmessage["sn"] = surname
338 if givenname is not None:
339 ldbmessage["givenName"] = givenname
341 if displayname is not "":
342 ldbmessage["displayName"] = displayname
343 ldbmessage["name"] = displayname
345 if initials is not None:
346 ldbmessage["initials"] = '%s.' % initials
348 if profilepath is not None:
349 ldbmessage["profilePath"] = profilepath
351 if scriptpath is not None:
352 ldbmessage["scriptPath"] = scriptpath
354 if homedrive is not None:
355 ldbmessage["homeDrive"] = homedrive
357 if homedirectory is not None:
358 ldbmessage["homeDirectory"] = homedirectory
360 if jobtitle is not None:
361 ldbmessage["title"] = jobtitle
363 if department is not None:
364 ldbmessage["department"] = department
366 if company is not None:
367 ldbmessage["company"] = company
369 if description is not None:
370 ldbmessage["description"] = description
372 if mailaddress is not None:
373 ldbmessage["mail"] = mailaddress
375 if internetaddress is not None:
376 ldbmessage["wWWHomePage"] = internetaddress
378 if telephonenumber is not None:
379 ldbmessage["telephoneNumber"] = telephonenumber
381 if physicaldeliveryoffice is not None:
382 ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice
384 if sd is not None:
385 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
387 self.transaction_start()
388 try:
389 self.add(ldbmessage)
391 # Sets the password for it
392 if setpassword:
393 self.setpassword("(samAccountName=%s)" % ldb.binary_encode(username), password,
394 force_password_change_at_next_login_req)
395 except Exception:
396 self.transaction_cancel()
397 raise
398 else:
399 self.transaction_commit()
402 def deleteuser(self, username):
403 """Deletes a user
405 :param username: Name of the target user
408 filter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(username), "CN=Person,CN=Schema,CN=Configuration", self.domain_dn())
409 self.transaction_start()
410 try:
411 target = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
412 expression=filter, attrs=[])
413 if len(target) == 0:
414 raise Exception('Unable to find user "%s"' % username)
415 assert(len(target) == 1)
416 self.delete(target[0].dn)
417 except Exception:
418 self.transaction_cancel()
419 raise
420 else:
421 self.transaction_commit()
424 def setpassword(self, search_filter, password,
425 force_change_at_next_login=False, username=None):
426 """Sets the password for a user
428 :param search_filter: LDAP filter to find the user (eg
429 samccountname=name)
430 :param password: Password for the user
431 :param force_change_at_next_login: Force password change
433 self.transaction_start()
434 try:
435 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
436 expression=search_filter, attrs=[])
437 if len(res) == 0:
438 raise Exception('Unable to find user "%s"' % (username or search_filter))
439 if len(res) > 1:
440 raise Exception('Matched %u multiple users with filter "%s"' % (len(res), search_filter))
441 user_dn = res[0].dn
442 setpw = """
443 dn: %s
444 changetype: modify
445 replace: unicodePwd
446 unicodePwd:: %s
447 """ % (user_dn, base64.b64encode(("\"" + password + "\"").encode('utf-16-le')))
449 self.modify_ldif(setpw)
451 if force_change_at_next_login:
452 self.force_password_change_at_next_login(
453 "(dn=" + str(user_dn) + ")")
455 # modify the userAccountControl to remove the disabled bit
456 self.enable_account(search_filter)
457 except Exception:
458 self.transaction_cancel()
459 raise
460 else:
461 self.transaction_commit()
463 def setexpiry(self, search_filter, expiry_seconds, no_expiry_req=False):
464 """Sets the account expiry for a user
466 :param search_filter: LDAP filter to find the user (eg
467 samaccountname=name)
468 :param expiry_seconds: expiry time from now in seconds
469 :param no_expiry_req: if set, then don't expire password
471 self.transaction_start()
472 try:
473 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
474 expression=search_filter,
475 attrs=["userAccountControl", "accountExpires"])
476 if len(res) == 0:
477 raise Exception('Unable to find user "%s"' % search_filter)
478 assert(len(res) == 1)
479 user_dn = res[0].dn
481 userAccountControl = int(res[0]["userAccountControl"][0])
482 accountExpires = int(res[0]["accountExpires"][0])
483 if no_expiry_req:
484 userAccountControl = userAccountControl | 0x10000
485 accountExpires = 0
486 else:
487 userAccountControl = userAccountControl & ~0x10000
488 accountExpires = samba.unix2nttime(expiry_seconds + int(time.time()))
490 setexp = """
491 dn: %s
492 changetype: modify
493 replace: userAccountControl
494 userAccountControl: %u
495 replace: accountExpires
496 accountExpires: %u
497 """ % (user_dn, userAccountControl, accountExpires)
499 self.modify_ldif(setexp)
500 except Exception:
501 self.transaction_cancel()
502 raise
503 else:
504 self.transaction_commit()
506 def set_domain_sid(self, sid):
507 """Change the domain SID used by this LDB.
509 :param sid: The new domain sid to use.
511 dsdb._samdb_set_domain_sid(self, sid)
513 def get_domain_sid(self):
514 """Read the domain SID used by this LDB. """
515 return dsdb._samdb_get_domain_sid(self)
517 domain_sid = property(get_domain_sid, set_domain_sid,
518 "SID for the domain")
520 def set_invocation_id(self, invocation_id):
521 """Set the invocation id for this SamDB handle.
523 :param invocation_id: GUID of the invocation id.
525 dsdb._dsdb_set_ntds_invocation_id(self, invocation_id)
527 def get_invocation_id(self):
528 """Get the invocation_id id"""
529 return dsdb._samdb_ntds_invocation_id(self)
531 invocation_id = property(get_invocation_id, set_invocation_id,
532 "Invocation ID GUID")
534 def get_oid_from_attid(self, attid):
535 return dsdb._dsdb_get_oid_from_attid(self, attid)
537 def get_attid_from_lDAPDisplayName(self, ldap_display_name,
538 is_schema_nc=False):
539 '''return the attribute ID for a LDAP attribute as an integer as found in DRSUAPI'''
540 return dsdb._dsdb_get_attid_from_lDAPDisplayName(self,
541 ldap_display_name, is_schema_nc)
543 def get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name):
544 '''return the syntax OID for a LDAP attribute as a string'''
545 return dsdb._dsdb_get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name)
547 def get_systemFlags_from_lDAPDisplayName(self, ldap_display_name):
548 '''return the systemFlags for a LDAP attribute as a integer'''
549 return dsdb._dsdb_get_systemFlags_from_lDAPDisplayName(self, ldap_display_name)
551 def get_linkId_from_lDAPDisplayName(self, ldap_display_name):
552 '''return the linkID for a LDAP attribute as a integer'''
553 return dsdb._dsdb_get_linkId_from_lDAPDisplayName(self, ldap_display_name)
555 def get_lDAPDisplayName_by_attid(self, attid):
556 '''return the lDAPDisplayName from an integer DRS attribute ID'''
557 return dsdb._dsdb_get_lDAPDisplayName_by_attid(self, attid)
559 def get_backlink_from_lDAPDisplayName(self, ldap_display_name):
560 '''return the attribute name of the corresponding backlink from the name
561 of a forward link attribute. If there is no backlink return None'''
562 return dsdb._dsdb_get_backlink_from_lDAPDisplayName(self, ldap_display_name)
564 def set_ntds_settings_dn(self, ntds_settings_dn):
565 """Set the NTDS Settings DN, as would be returned on the dsServiceName
566 rootDSE attribute.
568 This allows the DN to be set before the database fully exists
570 :param ntds_settings_dn: The new DN to use
572 dsdb._samdb_set_ntds_settings_dn(self, ntds_settings_dn)
574 def get_ntds_GUID(self):
575 """Get the NTDS objectGUID"""
576 return dsdb._samdb_ntds_objectGUID(self)
578 def server_site_name(self):
579 """Get the server site name"""
580 return dsdb._samdb_server_site_name(self)
582 def host_dns_name(self):
583 """return the DNS name of this host"""
584 res = self.search(base='', scope=ldb.SCOPE_BASE, attrs=['dNSHostName'])
585 return res[0]['dNSHostName'][0]
587 def domain_dns_name(self):
588 """return the DNS name of the domain root"""
589 domain_dn = self.get_default_basedn()
590 return domain_dn.canonical_str().split('/')[0]
592 def forest_dns_name(self):
593 """return the DNS name of the forest root"""
594 forest_dn = self.get_root_basedn()
595 return forest_dn.canonical_str().split('/')[0]
597 def load_partition_usn(self, base_dn):
598 return dsdb._dsdb_load_partition_usn(self, base_dn)
600 def set_schema(self, schema):
601 self.set_schema_from_ldb(schema.ldb)
603 def set_schema_from_ldb(self, ldb_conn):
604 dsdb._dsdb_set_schema_from_ldb(self, ldb_conn)
606 def dsdb_DsReplicaAttribute(self, ldb, ldap_display_name, ldif_elements):
607 '''convert a list of attribute values to a DRSUAPI DsReplicaAttribute'''
608 return dsdb._dsdb_DsReplicaAttribute(ldb, ldap_display_name, ldif_elements)
610 def dsdb_normalise_attributes(self, ldb, ldap_display_name, ldif_elements):
611 '''normalise a list of attribute values'''
612 return dsdb._dsdb_normalise_attributes(ldb, ldap_display_name, ldif_elements)
614 def get_attribute_from_attid(self, attid):
615 """ Get from an attid the associated attribute
617 :param attid: The attribute id for searched attribute
618 :return: The name of the attribute associated with this id
620 if len(self.hash_oid_name.keys()) == 0:
621 self._populate_oid_attid()
622 if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)):
623 return self.hash_oid_name[self.get_oid_from_attid(attid)]
624 else:
625 return None
627 def _populate_oid_attid(self):
628 """Populate the hash hash_oid_name.
630 This hash contains the oid of the attribute as a key and
631 its display name as a value
633 self.hash_oid_name = {}
634 res = self.search(expression="objectClass=attributeSchema",
635 controls=["search_options:1:2"],
636 attrs=["attributeID",
637 "lDAPDisplayName"])
638 if len(res) > 0:
639 for e in res:
640 strDisplay = str(e.get("lDAPDisplayName"))
641 self.hash_oid_name[str(e.get("attributeID"))] = strDisplay
643 def get_attribute_replmetadata_version(self, dn, att):
644 """Get the version field trom the replPropertyMetaData for
645 the given field
647 :param dn: The on which we want to get the version
648 :param att: The name of the attribute
649 :return: The value of the version field in the replPropertyMetaData
650 for the given attribute. None if the attribute is not replicated
653 res = self.search(expression="dn=%s" % dn,
654 scope=ldb.SCOPE_SUBTREE,
655 controls=["search_options:1:2"],
656 attrs=["replPropertyMetaData"])
657 if len(res) == 0:
658 return None
660 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
661 str(res[0]["replPropertyMetaData"]))
662 ctr = repl.ctr
663 if len(self.hash_oid_name.keys()) == 0:
664 self._populate_oid_attid()
665 for o in ctr.array:
666 # Search for Description
667 att_oid = self.get_oid_from_attid(o.attid)
668 if self.hash_oid_name.has_key(att_oid) and\
669 att.lower() == self.hash_oid_name[att_oid].lower():
670 return o.version
671 return None
673 def set_attribute_replmetadata_version(self, dn, att, value,
674 addifnotexist=False):
675 res = self.search(expression="dn=%s" % dn,
676 scope=ldb.SCOPE_SUBTREE,
677 controls=["search_options:1:2"],
678 attrs=["replPropertyMetaData"])
679 if len(res) == 0:
680 return None
682 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
683 str(res[0]["replPropertyMetaData"]))
684 ctr = repl.ctr
685 now = samba.unix2nttime(int(time.time()))
686 found = False
687 if len(self.hash_oid_name.keys()) == 0:
688 self._populate_oid_attid()
689 for o in ctr.array:
690 # Search for Description
691 att_oid = self.get_oid_from_attid(o.attid)
692 if self.hash_oid_name.has_key(att_oid) and\
693 att.lower() == self.hash_oid_name[att_oid].lower():
694 found = True
695 seq = self.sequence_number(ldb.SEQ_NEXT)
696 o.version = value
697 o.originating_change_time = now
698 o.originating_invocation_id = misc.GUID(self.get_invocation_id())
699 o.originating_usn = seq
700 o.local_usn = seq
702 if not found and addifnotexist and len(ctr.array) >0:
703 o2 = drsblobs.replPropertyMetaData1()
704 o2.attid = 589914
705 att_oid = self.get_oid_from_attid(o2.attid)
706 seq = self.sequence_number(ldb.SEQ_NEXT)
707 o2.version = value
708 o2.originating_change_time = now
709 o2.originating_invocation_id = misc.GUID(self.get_invocation_id())
710 o2.originating_usn = seq
711 o2.local_usn = seq
712 found = True
713 tab = ctr.array
714 tab.append(o2)
715 ctr.count = ctr.count + 1
716 ctr.array = tab
718 if found :
719 replBlob = ndr_pack(repl)
720 msg = ldb.Message()
721 msg.dn = res[0].dn
722 msg["replPropertyMetaData"] = ldb.MessageElement(replBlob,
723 ldb.FLAG_MOD_REPLACE,
724 "replPropertyMetaData")
725 self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
727 def write_prefixes_from_schema(self):
728 dsdb._dsdb_write_prefixes_from_schema_to_ldb(self)
730 def get_partitions_dn(self):
731 return dsdb._dsdb_get_partitions_dn(self)
733 def set_minPwdAge(self, value):
734 m = ldb.Message()
735 m.dn = ldb.Dn(self, self.domain_dn())
736 m["minPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdAge")
737 self.modify(m)
739 def get_minPwdAge(self):
740 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdAge"])
741 if len(res) == 0:
742 return None
743 elif not "minPwdAge" in res[0]:
744 return None
745 else:
746 return res[0]["minPwdAge"][0]
748 def set_minPwdLength(self, value):
749 m = ldb.Message()
750 m.dn = ldb.Dn(self, self.domain_dn())
751 m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength")
752 self.modify(m)
754 def get_minPwdLength(self):
755 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"])
756 if len(res) == 0:
757 return None
758 elif not "minPwdLength" in res[0]:
759 return None
760 else:
761 return res[0]["minPwdLength"][0]
763 def set_pwdProperties(self, value):
764 m = ldb.Message()
765 m.dn = ldb.Dn(self, self.domain_dn())
766 m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties")
767 self.modify(m)
769 def get_pwdProperties(self):
770 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"])
771 if len(res) == 0:
772 return None
773 elif not "pwdProperties" in res[0]:
774 return None
775 else:
776 return res[0]["pwdProperties"][0]
778 def set_dsheuristics(self, dsheuristics):
779 m = ldb.Message()
780 m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s"
781 % self.get_config_basedn().get_linearized())
782 if dsheuristics is not None:
783 m["dSHeuristics"] = ldb.MessageElement(dsheuristics,
784 ldb.FLAG_MOD_REPLACE, "dSHeuristics")
785 else:
786 m["dSHeuristics"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
787 "dSHeuristics")
788 self.modify(m)
790 def get_dsheuristics(self):
791 res = self.search("CN=Directory Service,CN=Windows NT,CN=Services,%s"
792 % self.get_config_basedn().get_linearized(),
793 scope=ldb.SCOPE_BASE, attrs=["dSHeuristics"])
794 if len(res) == 0:
795 dsheuristics = None
796 elif "dSHeuristics" in res[0]:
797 dsheuristics = res[0]["dSHeuristics"][0]
798 else:
799 dsheuristics = None
801 return dsheuristics
803 def create_ou(self, ou_dn, description=None, name=None, sd=None):
804 """Creates an organizationalUnit object
805 :param ou_dn: dn of the new object
806 :param description: description attribute
807 :param name: name atttribute
808 :param sd: security descriptor of the object, can be
809 an SDDL string or security.descriptor type
811 m = {"dn": ou_dn,
812 "objectClass": "organizationalUnit"}
814 if description:
815 m["description"] = description
816 if name:
817 m["name"] = name
819 if sd:
820 m["nTSecurityDescriptor"] = ndr_pack(sd)
821 self.add(m)