samba-tool: Fix typo in usage.
[Samba/gebeck_regimport.git] / source4 / scripting / python / samba / samdb.py
blobd83e0a6f7c8c5ebcd1869131b49b327026e9c613
1 # Unix SMB/CIFS implementation.
2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2010
3 # Copyright (C) Matthias Dieter Wallnoefer 2009
5 # Based on the original in EJS:
6 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
7 # Copyright (C) Giampaolo Lauria <lauria2@yahoo.com> 2011
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 3 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 """Convenience functions for using the SAM."""
25 import samba
26 import ldb
27 import time
28 import base64
29 import os
30 from samba import dsdb
31 from samba.ndr import ndr_unpack, ndr_pack
32 from samba.dcerpc import drsblobs, misc
33 from samba.common import normalise_int32
35 __docformat__ = "restructuredText"
38 class SamDB(samba.Ldb):
39 """The SAM database."""
41 hash_oid_name = {}
43 def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
44 credentials=None, flags=0, options=None, global_schema=True,
45 auto_connect=True, am_rodc=None):
46 self.lp = lp
47 if not auto_connect:
48 url = None
49 elif url is None and lp is not None:
50 url = lp.samdb_url()
52 self.url = url
54 super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
55 session_info=session_info, credentials=credentials, flags=flags,
56 options=options)
58 if global_schema:
59 dsdb._dsdb_set_global_schema(self)
61 if am_rodc is not None:
62 dsdb._dsdb_set_am_rodc(self, am_rodc)
64 def connect(self, url=None, flags=0, options=None):
65 '''connect to the database'''
66 if self.lp is not None and not os.path.exists(url):
67 url = self.lp.private_path(url)
68 self.url = url
70 super(SamDB, self).connect(url=url, flags=flags,
71 options=options)
73 def am_rodc(self):
74 '''return True if we are an RODC'''
75 return dsdb._am_rodc(self)
77 def am_pdc(self):
78 '''return True if we are an PDC emulator'''
79 return dsdb._am_pdc(self)
81 def domain_dn(self):
82 '''return the domain DN'''
83 return str(self.get_default_basedn())
85 def disable_account(self, search_filter):
86 """Disables an account
88 :param search_filter: LDAP filter to find the user (eg
89 samccountname=name)
90 """
92 flags = samba.dsdb.UF_ACCOUNTDISABLE
93 self.toggle_userAccountFlags(search_filter, flags, on=True)
95 def enable_account(self, search_filter):
96 """Enables an account
98 :param search_filter: LDAP filter to find the user (eg
99 samccountname=name)
102 flags = samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_PASSWD_NOTREQD
103 self.toggle_userAccountFlags(search_filter, flags, on=False)
105 def toggle_userAccountFlags(self, search_filter, flags, flags_str=None,
106 on=True, strict=False):
107 """Toggle_userAccountFlags
109 :param search_filter: LDAP filter to find the user (eg
110 samccountname=name)
111 :param flags: samba.dsdb.UF_* flags
112 :param on: on=True (default) => set, on=False => unset
113 :param strict: strict=False (default) ignore if no action is needed
114 strict=True raises an Exception if...
116 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
117 expression=search_filter, attrs=["userAccountControl"])
118 if len(res) == 0:
119 raise Exception("Unable to find account where '%s'" % search_filter)
120 assert(len(res) == 1)
121 account_dn = res[0].dn
123 old_uac = int(res[0]["userAccountControl"][0])
124 if on:
125 if strict and (old_uac & flags):
126 error = "Account flag(s) '%s' already set" % flags_str
127 raise Exception(error)
129 new_uac = old_uac | flags
130 else:
131 if strict and not (old_uac & flags):
132 error = "Account flag(s) '%s' already unset" % flags_str
133 raise Exception(error)
135 new_uac = old_uac & ~flags
137 if old_uac == new_uac:
138 return
140 mod = """
141 dn: %s
142 changetype: modify
143 delete: userAccountControl
144 userAccountControl: %u
145 add: userAccountControl
146 userAccountControl: %u
147 """ % (account_dn, old_uac, new_uac)
148 self.modify_ldif(mod)
150 def force_password_change_at_next_login(self, search_filter):
151 """Forces a password change at next login
153 :param search_filter: LDAP filter to find the user (eg
154 samccountname=name)
156 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
157 expression=search_filter, attrs=[])
158 if len(res) == 0:
159 raise Exception('Unable to find user "%s"' % search_filter)
160 assert(len(res) == 1)
161 user_dn = res[0].dn
163 mod = """
164 dn: %s
165 changetype: modify
166 replace: pwdLastSet
167 pwdLastSet: 0
168 """ % (user_dn)
169 self.modify_ldif(mod)
171 def newgroup(self, groupname, groupou=None, grouptype=None,
172 description=None, mailaddress=None, notes=None, sd=None):
173 """Adds a new group with additional parameters
175 :param groupname: Name of the new group
176 :param grouptype: Type of the new group
177 :param description: Description of the new group
178 :param mailaddress: Email address of the new group
179 :param notes: Notes of the new group
180 :param sd: security descriptor of the object
183 group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn())
185 # The new user record. Note the reliance on the SAMLDB module which
186 # fills in the default informations
187 ldbmessage = {"dn": group_dn,
188 "sAMAccountName": groupname,
189 "objectClass": "group"}
191 if grouptype is not None:
192 ldbmessage["groupType"] = normalise_int32(grouptype)
194 if description is not None:
195 ldbmessage["description"] = description
197 if mailaddress is not None:
198 ldbmessage["mail"] = mailaddress
200 if notes is not None:
201 ldbmessage["info"] = notes
203 if sd is not None:
204 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
206 self.add(ldbmessage)
208 def deletegroup(self, groupname):
209 """Deletes a group
211 :param groupname: Name of the target group
214 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
215 self.transaction_start()
216 try:
217 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
218 expression=groupfilter, attrs=[])
219 if len(targetgroup) == 0:
220 raise Exception('Unable to find group "%s"' % groupname)
221 assert(len(targetgroup) == 1)
222 self.delete(targetgroup[0].dn)
223 except:
224 self.transaction_cancel()
225 raise
226 else:
227 self.transaction_commit()
229 def add_remove_group_members(self, groupname, members,
230 add_members_operation=True):
231 """Adds or removes group members
233 :param groupname: Name of the target group
234 :param members: list of group members
235 :param add_members_operation: Defines if its an add or remove
236 operation
239 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (
240 ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
242 self.transaction_start()
243 try:
244 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
245 expression=groupfilter, attrs=['member'])
246 if len(targetgroup) == 0:
247 raise Exception('Unable to find group "%s"' % groupname)
248 assert(len(targetgroup) == 1)
250 modified = False
252 addtargettogroup = """
253 dn: %s
254 changetype: modify
255 """ % (str(targetgroup[0].dn))
257 for member in members:
258 targetmember = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
259 expression="(|(sAMAccountName=%s)(CN=%s))" % (
260 ldb.binary_encode(member), ldb.binary_encode(member)), attrs=[])
262 if len(targetmember) != 1:
263 continue
265 if add_members_operation is True and (targetgroup[0].get('member') is None or str(targetmember[0].dn) not in targetgroup[0]['member']):
266 modified = True
267 addtargettogroup += """add: member
268 member: %s
269 """ % (str(targetmember[0].dn))
271 elif add_members_operation is False and (targetgroup[0].get('member') is not None and str(targetmember[0].dn) in targetgroup[0]['member']):
272 modified = True
273 addtargettogroup += """delete: member
274 member: %s
275 """ % (str(targetmember[0].dn))
277 if modified is True:
278 self.modify_ldif(addtargettogroup)
280 except:
281 self.transaction_cancel()
282 raise
283 else:
284 self.transaction_commit()
286 def newuser(self, username, password,
287 force_password_change_at_next_login_req=False,
288 useusernameascn=False, userou=None, surname=None, givenname=None,
289 initials=None, profilepath=None, scriptpath=None, homedrive=None,
290 homedirectory=None, jobtitle=None, department=None, company=None,
291 description=None, mailaddress=None, internetaddress=None,
292 telephonenumber=None, physicaldeliveryoffice=None, sd=None,
293 setpassword=True):
294 """Adds a new user with additional parameters
296 :param username: Name of the new user
297 :param password: Password for the new user
298 :param force_password_change_at_next_login_req: Force password change
299 :param useusernameascn: Use username as cn rather that firstname +
300 initials + lastname
301 :param userou: Object container (without domainDN postfix) for new user
302 :param surname: Surname of the new user
303 :param givenname: First name of the new user
304 :param initials: Initials of the new user
305 :param profilepath: Profile path of the new user
306 :param scriptpath: Logon script path of the new user
307 :param homedrive: Home drive of the new user
308 :param homedirectory: Home directory of the new user
309 :param jobtitle: Job title of the new user
310 :param department: Department of the new user
311 :param company: Company of the new user
312 :param description: of the new user
313 :param mailaddress: Email address of the new user
314 :param internetaddress: Home page of the new user
315 :param telephonenumber: Phone number of the new user
316 :param physicaldeliveryoffice: Office location of the new user
317 :param sd: security descriptor of the object
318 :param setpassword: optionally disable password reset
321 displayname = ""
322 if givenname is not None:
323 displayname += givenname
325 if initials is not None:
326 displayname += ' %s.' % initials
328 if surname is not None:
329 displayname += ' %s' % surname
331 cn = username
332 if useusernameascn is None and displayname is not "":
333 cn = displayname
335 user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn())
337 dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "")
338 user_principal_name = "%s@%s" % (username, dnsdomain)
339 # The new user record. Note the reliance on the SAMLDB module which
340 # fills in the default informations
341 ldbmessage = {"dn": user_dn,
342 "sAMAccountName": username,
343 "userPrincipalName": user_principal_name,
344 "objectClass": "user"}
346 if surname is not None:
347 ldbmessage["sn"] = surname
349 if givenname is not None:
350 ldbmessage["givenName"] = givenname
352 if displayname is not "":
353 ldbmessage["displayName"] = displayname
354 ldbmessage["name"] = displayname
356 if initials is not None:
357 ldbmessage["initials"] = '%s.' % initials
359 if profilepath is not None:
360 ldbmessage["profilePath"] = profilepath
362 if scriptpath is not None:
363 ldbmessage["scriptPath"] = scriptpath
365 if homedrive is not None:
366 ldbmessage["homeDrive"] = homedrive
368 if homedirectory is not None:
369 ldbmessage["homeDirectory"] = homedirectory
371 if jobtitle is not None:
372 ldbmessage["title"] = jobtitle
374 if department is not None:
375 ldbmessage["department"] = department
377 if company is not None:
378 ldbmessage["company"] = company
380 if description is not None:
381 ldbmessage["description"] = description
383 if mailaddress is not None:
384 ldbmessage["mail"] = mailaddress
386 if internetaddress is not None:
387 ldbmessage["wWWHomePage"] = internetaddress
389 if telephonenumber is not None:
390 ldbmessage["telephoneNumber"] = telephonenumber
392 if physicaldeliveryoffice is not None:
393 ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice
395 if sd is not None:
396 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
398 self.transaction_start()
399 try:
400 self.add(ldbmessage)
402 # Sets the password for it
403 if setpassword:
404 self.setpassword("(samAccountName=%s)" % ldb.binary_encode(username), password,
405 force_password_change_at_next_login_req)
406 except:
407 self.transaction_cancel()
408 raise
409 else:
410 self.transaction_commit()
413 def deleteuser(self, username):
414 """Deletes a user
416 :param username: Name of the target user
419 filter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(username), "CN=Person,CN=Schema,CN=Configuration", self.domain_dn())
420 self.transaction_start()
421 try:
422 target = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
423 expression=filter, attrs=[])
424 if len(target) == 0:
425 raise Exception('Unable to find user "%s"' % username)
426 assert(len(target) == 1)
427 self.delete(target[0].dn)
428 except:
429 self.transaction_cancel()
430 raise
431 else:
432 self.transaction_commit()
434 def setpassword(self, search_filter, password,
435 force_change_at_next_login=False, username=None):
436 """Sets the password for a user
438 :param search_filter: LDAP filter to find the user (eg
439 samccountname=name)
440 :param password: Password for the user
441 :param force_change_at_next_login: Force password change
443 self.transaction_start()
444 try:
445 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
446 expression=search_filter, attrs=[])
447 if len(res) == 0:
448 raise Exception('Unable to find user "%s"' % (username or search_filter))
449 if len(res) > 1:
450 raise Exception('Matched %u multiple users with filter "%s"' % (len(res), search_filter))
451 user_dn = res[0].dn
452 setpw = """
453 dn: %s
454 changetype: modify
455 replace: unicodePwd
456 unicodePwd:: %s
457 """ % (user_dn, base64.b64encode(("\"" + password + "\"").encode('utf-16-le')))
459 self.modify_ldif(setpw)
461 if force_change_at_next_login:
462 self.force_password_change_at_next_login(
463 "(distinguishedName=" + str(user_dn) + ")")
465 # modify the userAccountControl to remove the disabled bit
466 self.enable_account(search_filter)
467 except:
468 self.transaction_cancel()
469 raise
470 else:
471 self.transaction_commit()
473 def setexpiry(self, search_filter, expiry_seconds, no_expiry_req=False):
474 """Sets the account expiry for a user
476 :param search_filter: LDAP filter to find the user (eg
477 samaccountname=name)
478 :param expiry_seconds: expiry time from now in seconds
479 :param no_expiry_req: if set, then don't expire password
481 self.transaction_start()
482 try:
483 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
484 expression=search_filter,
485 attrs=["userAccountControl", "accountExpires"])
486 if len(res) == 0:
487 raise Exception('Unable to find user "%s"' % search_filter)
488 assert(len(res) == 1)
489 user_dn = res[0].dn
491 userAccountControl = int(res[0]["userAccountControl"][0])
492 accountExpires = int(res[0]["accountExpires"][0])
493 if no_expiry_req:
494 userAccountControl = userAccountControl | 0x10000
495 accountExpires = 0
496 else:
497 userAccountControl = userAccountControl & ~0x10000
498 accountExpires = samba.unix2nttime(expiry_seconds + int(time.time()))
500 setexp = """
501 dn: %s
502 changetype: modify
503 replace: userAccountControl
504 userAccountControl: %u
505 replace: accountExpires
506 accountExpires: %u
507 """ % (user_dn, userAccountControl, accountExpires)
509 self.modify_ldif(setexp)
510 except:
511 self.transaction_cancel()
512 raise
513 else:
514 self.transaction_commit()
516 def set_domain_sid(self, sid):
517 """Change the domain SID used by this LDB.
519 :param sid: The new domain sid to use.
521 dsdb._samdb_set_domain_sid(self, sid)
523 def get_domain_sid(self):
524 """Read the domain SID used by this LDB. """
525 return dsdb._samdb_get_domain_sid(self)
527 domain_sid = property(get_domain_sid, set_domain_sid,
528 "SID for the domain")
530 def set_invocation_id(self, invocation_id):
531 """Set the invocation id for this SamDB handle.
533 :param invocation_id: GUID of the invocation id.
535 dsdb._dsdb_set_ntds_invocation_id(self, invocation_id)
537 def get_invocation_id(self):
538 """Get the invocation_id id"""
539 return dsdb._samdb_ntds_invocation_id(self)
541 invocation_id = property(get_invocation_id, set_invocation_id,
542 "Invocation ID GUID")
544 def get_oid_from_attid(self, attid):
545 return dsdb._dsdb_get_oid_from_attid(self, attid)
547 def get_attid_from_lDAPDisplayName(self, ldap_display_name,
548 is_schema_nc=False):
549 '''return the attribute ID for a LDAP attribute as an integer as found in DRSUAPI'''
550 return dsdb._dsdb_get_attid_from_lDAPDisplayName(self,
551 ldap_display_name, is_schema_nc)
553 def get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name):
554 '''return the syntax OID for a LDAP attribute as a string'''
555 return dsdb._dsdb_get_syntax_oid_from_lDAPDisplayName(self, ldap_display_name)
557 def get_systemFlags_from_lDAPDisplayName(self, ldap_display_name):
558 '''return the systemFlags for a LDAP attribute as a integer'''
559 return dsdb._dsdb_get_systemFlags_from_lDAPDisplayName(self, ldap_display_name)
561 def get_linkId_from_lDAPDisplayName(self, ldap_display_name):
562 '''return the linkID for a LDAP attribute as a integer'''
563 return dsdb._dsdb_get_linkId_from_lDAPDisplayName(self, ldap_display_name)
565 def get_lDAPDisplayName_by_attid(self, attid):
566 '''return the lDAPDisplayName from an integer DRS attribute ID'''
567 return dsdb._dsdb_get_lDAPDisplayName_by_attid(self, attid)
569 def get_backlink_from_lDAPDisplayName(self, ldap_display_name):
570 '''return the attribute name of the corresponding backlink from the name
571 of a forward link attribute. If there is no backlink return None'''
572 return dsdb._dsdb_get_backlink_from_lDAPDisplayName(self, ldap_display_name)
574 def set_ntds_settings_dn(self, ntds_settings_dn):
575 """Set the NTDS Settings DN, as would be returned on the dsServiceName
576 rootDSE attribute.
578 This allows the DN to be set before the database fully exists
580 :param ntds_settings_dn: The new DN to use
582 dsdb._samdb_set_ntds_settings_dn(self, ntds_settings_dn)
584 def get_ntds_GUID(self):
585 """Get the NTDS objectGUID"""
586 return dsdb._samdb_ntds_objectGUID(self)
588 def server_site_name(self):
589 """Get the server site name"""
590 return dsdb._samdb_server_site_name(self)
592 def host_dns_name(self):
593 """return the DNS name of this host"""
594 res = self.search(base='', scope=ldb.SCOPE_BASE, attrs=['dNSHostName'])
595 return res[0]['dNSHostName'][0]
597 def domain_dns_name(self):
598 """return the DNS name of the domain root"""
599 domain_dn = self.get_default_basedn()
600 return domain_dn.canonical_str().split('/')[0]
602 def forest_dns_name(self):
603 """return the DNS name of the forest root"""
604 forest_dn = self.get_root_basedn()
605 return forest_dn.canonical_str().split('/')[0]
607 def load_partition_usn(self, base_dn):
608 return dsdb._dsdb_load_partition_usn(self, base_dn)
610 def set_schema(self, schema, write_indices_and_attributes=True):
611 self.set_schema_from_ldb(schema.ldb, write_indices_and_attributes=write_indices_and_attributes)
613 def set_schema_from_ldb(self, ldb_conn, write_indices_and_attributes=True):
614 dsdb._dsdb_set_schema_from_ldb(self, ldb_conn, write_indices_and_attributes)
616 def dsdb_DsReplicaAttribute(self, ldb, ldap_display_name, ldif_elements):
617 '''convert a list of attribute values to a DRSUAPI DsReplicaAttribute'''
618 return dsdb._dsdb_DsReplicaAttribute(ldb, ldap_display_name, ldif_elements)
620 def dsdb_normalise_attributes(self, ldb, ldap_display_name, ldif_elements):
621 '''normalise a list of attribute values'''
622 return dsdb._dsdb_normalise_attributes(ldb, ldap_display_name, ldif_elements)
624 def get_attribute_from_attid(self, attid):
625 """ Get from an attid the associated attribute
627 :param attid: The attribute id for searched attribute
628 :return: The name of the attribute associated with this id
630 if len(self.hash_oid_name.keys()) == 0:
631 self._populate_oid_attid()
632 if self.hash_oid_name.has_key(self.get_oid_from_attid(attid)):
633 return self.hash_oid_name[self.get_oid_from_attid(attid)]
634 else:
635 return None
637 def _populate_oid_attid(self):
638 """Populate the hash hash_oid_name.
640 This hash contains the oid of the attribute as a key and
641 its display name as a value
643 self.hash_oid_name = {}
644 res = self.search(expression="objectClass=attributeSchema",
645 controls=["search_options:1:2"],
646 attrs=["attributeID",
647 "lDAPDisplayName"])
648 if len(res) > 0:
649 for e in res:
650 strDisplay = str(e.get("lDAPDisplayName"))
651 self.hash_oid_name[str(e.get("attributeID"))] = strDisplay
653 def get_attribute_replmetadata_version(self, dn, att):
654 """Get the version field trom the replPropertyMetaData for
655 the given field
657 :param dn: The on which we want to get the version
658 :param att: The name of the attribute
659 :return: The value of the version field in the replPropertyMetaData
660 for the given attribute. None if the attribute is not replicated
663 res = self.search(expression="distinguishedName=%s" % dn,
664 scope=ldb.SCOPE_SUBTREE,
665 controls=["search_options:1:2"],
666 attrs=["replPropertyMetaData"])
667 if len(res) == 0:
668 return None
670 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
671 str(res[0]["replPropertyMetaData"]))
672 ctr = repl.ctr
673 if len(self.hash_oid_name.keys()) == 0:
674 self._populate_oid_attid()
675 for o in ctr.array:
676 # Search for Description
677 att_oid = self.get_oid_from_attid(o.attid)
678 if self.hash_oid_name.has_key(att_oid) and\
679 att.lower() == self.hash_oid_name[att_oid].lower():
680 return o.version
681 return None
683 def set_attribute_replmetadata_version(self, dn, att, value,
684 addifnotexist=False):
685 res = self.search(expression="distinguishedName=%s" % dn,
686 scope=ldb.SCOPE_SUBTREE,
687 controls=["search_options:1:2"],
688 attrs=["replPropertyMetaData"])
689 if len(res) == 0:
690 return None
692 repl = ndr_unpack(drsblobs.replPropertyMetaDataBlob,
693 str(res[0]["replPropertyMetaData"]))
694 ctr = repl.ctr
695 now = samba.unix2nttime(int(time.time()))
696 found = False
697 if len(self.hash_oid_name.keys()) == 0:
698 self._populate_oid_attid()
699 for o in ctr.array:
700 # Search for Description
701 att_oid = self.get_oid_from_attid(o.attid)
702 if self.hash_oid_name.has_key(att_oid) and\
703 att.lower() == self.hash_oid_name[att_oid].lower():
704 found = True
705 seq = self.sequence_number(ldb.SEQ_NEXT)
706 o.version = value
707 o.originating_change_time = now
708 o.originating_invocation_id = misc.GUID(self.get_invocation_id())
709 o.originating_usn = seq
710 o.local_usn = seq
712 if not found and addifnotexist and len(ctr.array) >0:
713 o2 = drsblobs.replPropertyMetaData1()
714 o2.attid = 589914
715 att_oid = self.get_oid_from_attid(o2.attid)
716 seq = self.sequence_number(ldb.SEQ_NEXT)
717 o2.version = value
718 o2.originating_change_time = now
719 o2.originating_invocation_id = misc.GUID(self.get_invocation_id())
720 o2.originating_usn = seq
721 o2.local_usn = seq
722 found = True
723 tab = ctr.array
724 tab.append(o2)
725 ctr.count = ctr.count + 1
726 ctr.array = tab
728 if found :
729 replBlob = ndr_pack(repl)
730 msg = ldb.Message()
731 msg.dn = res[0].dn
732 msg["replPropertyMetaData"] = ldb.MessageElement(replBlob,
733 ldb.FLAG_MOD_REPLACE,
734 "replPropertyMetaData")
735 self.modify(msg, ["local_oid:1.3.6.1.4.1.7165.4.3.14:0"])
737 def write_prefixes_from_schema(self):
738 dsdb._dsdb_write_prefixes_from_schema_to_ldb(self)
740 def get_partitions_dn(self):
741 return dsdb._dsdb_get_partitions_dn(self)
743 def get_nc_root(self, dn):
744 return dsdb._dsdb_get_nc_root(self, dn)
746 def get_wellknown_dn(self, nc_root, wkguid):
747 return dsdb._dsdb_get_wellknown_dn(self, nc_root, wkguid)
749 def set_minPwdAge(self, value):
750 m = ldb.Message()
751 m.dn = ldb.Dn(self, self.domain_dn())
752 m["minPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdAge")
753 self.modify(m)
755 def get_minPwdAge(self):
756 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdAge"])
757 if len(res) == 0:
758 return None
759 elif not "minPwdAge" in res[0]:
760 return None
761 else:
762 return res[0]["minPwdAge"][0]
764 def set_minPwdLength(self, value):
765 m = ldb.Message()
766 m.dn = ldb.Dn(self, self.domain_dn())
767 m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength")
768 self.modify(m)
770 def get_minPwdLength(self):
771 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"])
772 if len(res) == 0:
773 return None
774 elif not "minPwdLength" in res[0]:
775 return None
776 else:
777 return res[0]["minPwdLength"][0]
779 def set_pwdProperties(self, value):
780 m = ldb.Message()
781 m.dn = ldb.Dn(self, self.domain_dn())
782 m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties")
783 self.modify(m)
785 def get_pwdProperties(self):
786 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"])
787 if len(res) == 0:
788 return None
789 elif not "pwdProperties" in res[0]:
790 return None
791 else:
792 return res[0]["pwdProperties"][0]
794 def set_dsheuristics(self, dsheuristics):
795 m = ldb.Message()
796 m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s"
797 % self.get_config_basedn().get_linearized())
798 if dsheuristics is not None:
799 m["dSHeuristics"] = ldb.MessageElement(dsheuristics,
800 ldb.FLAG_MOD_REPLACE, "dSHeuristics")
801 else:
802 m["dSHeuristics"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
803 "dSHeuristics")
804 self.modify(m)
806 def get_dsheuristics(self):
807 res = self.search("CN=Directory Service,CN=Windows NT,CN=Services,%s"
808 % self.get_config_basedn().get_linearized(),
809 scope=ldb.SCOPE_BASE, attrs=["dSHeuristics"])
810 if len(res) == 0:
811 dsheuristics = None
812 elif "dSHeuristics" in res[0]:
813 dsheuristics = res[0]["dSHeuristics"][0]
814 else:
815 dsheuristics = None
817 return dsheuristics
819 def create_ou(self, ou_dn, description=None, name=None, sd=None):
820 """Creates an organizationalUnit object
821 :param ou_dn: dn of the new object
822 :param description: description attribute
823 :param name: name atttribute
824 :param sd: security descriptor of the object, can be
825 an SDDL string or security.descriptor type
827 m = {"dn": ou_dn,
828 "objectClass": "organizationalUnit"}
830 if description:
831 m["description"] = description
832 if name:
833 m["name"] = name
835 if sd:
836 m["nTSecurityDescriptor"] = ndr_pack(sd)
837 self.add(m)
839 def sequence_number(self, seq_type):
840 """Returns the value of the sequence number according to the requested type
841 :param seq_type: type of sequence number
843 self.transaction_start()
844 try:
845 seq = super(SamDB, self).sequence_number(seq_type)
846 except:
847 self.transaction_cancel()
848 raise
849 else:
850 self.transaction_commit()
851 return seq
853 def get_dsServiceName(self):
854 '''get the NTDS DN from the rootDSE'''
855 res = self.search(base="", scope=ldb.SCOPE_BASE, attrs=["dsServiceName"])
856 return res[0]["dsServiceName"][0]
858 def get_serverName(self):
859 '''get the server DN from the rootDSE'''
860 res = self.search(base="", scope=ldb.SCOPE_BASE, attrs=["serverName"])
861 return res[0]["serverName"][0]