s4-samdb: seqence_number() operation must be in a transaction
[Samba/id10ts.git] / source4 / scripting / python / samba / samdb.py
blob0ad6857cac21ca39c5b7d9f8f71d248c8510b675
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 disable_account(self, search_filter):
84 """Disables an account
86 :param search_filter: LDAP filter to find the user (eg
87 samccountname=name)
88 """
90 flags = samba.dsdb.UF_ACCOUNTDISABLE
91 self.toggle_userAccountFlags(search_filter, flags, on=True)
93 def enable_account(self, search_filter):
94 """Enables an account
96 :param search_filter: LDAP filter to find the user (eg
97 samccountname=name)
98 """
100 flags = samba.dsdb.UF_ACCOUNTDISABLE | samba.dsdb.UF_PASSWD_NOTREQD
101 self.toggle_userAccountFlags(search_filter, flags, on=False)
103 def toggle_userAccountFlags(self, search_filter, flags, flags_str=None,
104 on=True, strict=False):
105 """toggle_userAccountFlags
107 :param search_filter: LDAP filter to find the user (eg
108 samccountname=name)
109 :flags: samba.dsdb.UF_* flags
110 :on: on=True (default) => set, on=False => unset
111 :strict: strict=False (default) ignore if no action is needed
112 strict=True raises an Exception if...
114 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
115 expression=search_filter, attrs=["userAccountControl"])
116 if len(res) == 0:
117 raise Exception("Unable to find account where '%s'" % search_filter)
118 assert(len(res) == 1)
119 account_dn = res[0].dn
121 old_uac = int(res[0]["userAccountControl"][0])
122 if on:
123 if strict and (old_uac & flags):
124 error = "Account flag(s) '%s' already set" % flags_str
125 raise Exception(error)
127 new_uac = old_uac | flags
128 else:
129 if strict and not (old_uac & flags):
130 error = "Account flag(s) '%s' already unset" % flags_str
131 raise Exception(error)
133 new_uac = old_uac & ~flags
135 if old_uac == new_uac:
136 return
138 mod = """
139 dn: %s
140 changetype: modify
141 delete: userAccountControl
142 userAccountControl: %u
143 add: userAccountControl
144 userAccountControl: %u
145 """ % (account_dn, old_uac, new_uac)
146 self.modify_ldif(mod)
148 def force_password_change_at_next_login(self, search_filter):
149 """Forces a password change at next login
151 :param search_filter: LDAP filter to find the user (eg
152 samccountname=name)
154 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
155 expression=search_filter, attrs=[])
156 if len(res) == 0:
157 raise Exception('Unable to find user "%s"' % search_filter)
158 assert(len(res) == 1)
159 user_dn = res[0].dn
161 mod = """
162 dn: %s
163 changetype: modify
164 replace: pwdLastSet
165 pwdLastSet: 0
166 """ % (user_dn)
167 self.modify_ldif(mod)
169 def newgroup(self, groupname, groupou=None, grouptype=None,
170 description=None, mailaddress=None, notes=None, sd=None):
171 """Adds a new group with additional parameters
173 :param groupname: Name of the new group
174 :param grouptype: Type of the new group
175 :param description: Description of the new group
176 :param mailaddress: Email address of the new group
177 :param notes: Notes of the new group
178 :param sd: security descriptor of the object
181 group_dn = "CN=%s,%s,%s" % (groupname, (groupou or "CN=Users"), self.domain_dn())
183 # The new user record. Note the reliance on the SAMLDB module which
184 # fills in the default informations
185 ldbmessage = {"dn": group_dn,
186 "sAMAccountName": groupname,
187 "objectClass": "group"}
189 if grouptype is not None:
190 ldbmessage["groupType"] = normalise_int32(grouptype)
192 if description is not None:
193 ldbmessage["description"] = description
195 if mailaddress is not None:
196 ldbmessage["mail"] = mailaddress
198 if notes is not None:
199 ldbmessage["info"] = notes
201 if sd is not None:
202 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
204 self.add(ldbmessage)
206 def deletegroup(self, groupname):
207 """Deletes a group
209 :param groupname: Name of the target group
212 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
213 self.transaction_start()
214 try:
215 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
216 expression=groupfilter, attrs=[])
217 if len(targetgroup) == 0:
218 raise Exception('Unable to find group "%s"' % groupname)
219 assert(len(targetgroup) == 1)
220 self.delete(targetgroup[0].dn)
221 except Exception:
222 self.transaction_cancel()
223 raise
224 else:
225 self.transaction_commit()
227 def add_remove_group_members(self, groupname, listofmembers,
228 add_members_operation=True):
229 """Adds or removes group members
231 :param groupname: Name of the target group
232 :param listofmembers: Comma-separated list of group members
233 :param add_members_operation: Defines if its an add or remove
234 operation
237 groupfilter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (
238 ldb.binary_encode(groupname), "CN=Group,CN=Schema,CN=Configuration", self.domain_dn())
239 groupmembers = listofmembers.split(',')
241 self.transaction_start()
242 try:
243 targetgroup = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
244 expression=groupfilter, attrs=['member'])
245 if len(targetgroup) == 0:
246 raise Exception('Unable to find group "%s"' % groupname)
247 assert(len(targetgroup) == 1)
249 modified = False
251 addtargettogroup = """
252 dn: %s
253 changetype: modify
254 """ % (str(targetgroup[0].dn))
256 for member in groupmembers:
257 targetmember = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
258 expression="(|(sAMAccountName=%s)(CN=%s))" % (
259 ldb.binary_encode(member), ldb.binary_encode(member)), attrs=[])
261 if len(targetmember) != 1:
262 continue
264 if add_members_operation is True and (targetgroup[0].get('member') is None or str(targetmember[0].dn) not in targetgroup[0]['member']):
265 modified = True
266 addtargettogroup += """add: member
267 member: %s
268 """ % (str(targetmember[0].dn))
270 elif add_members_operation is False and (targetgroup[0].get('member') is not None and str(targetmember[0].dn) in targetgroup[0]['member']):
271 modified = True
272 addtargettogroup += """delete: member
273 member: %s
274 """ % (str(targetmember[0].dn))
276 if modified is True:
277 self.modify_ldif(addtargettogroup)
279 except Exception:
280 self.transaction_cancel()
281 raise
282 else:
283 self.transaction_commit()
285 def newuser(self, username, password,
286 force_password_change_at_next_login_req=False,
287 useusernameascn=False, userou=None, surname=None, givenname=None,
288 initials=None, profilepath=None, scriptpath=None, homedrive=None,
289 homedirectory=None, jobtitle=None, department=None, company=None,
290 description=None, mailaddress=None, internetaddress=None,
291 telephonenumber=None, physicaldeliveryoffice=None, sd=None,
292 setpassword=True):
293 """Adds a new user with additional parameters
295 :param username: Name of the new user
296 :param password: Password for the new user
297 :param force_password_change_at_next_login_req: Force password change
298 :param useusernameascn: Use username as cn rather that firstname +
299 initials + lastname
300 :param userou: Object container (without domainDN postfix) for new user
301 :param surname: Surname of the new user
302 :param givenname: First name of the new user
303 :param initials: Initials of the new user
304 :param profilepath: Profile path of the new user
305 :param scriptpath: Logon script path of the new user
306 :param homedrive: Home drive of the new user
307 :param homedirectory: Home directory of the new user
308 :param jobtitle: Job title of the new user
309 :param department: Department of the new user
310 :param company: Company of the new user
311 :param description: of the new user
312 :param mailaddress: Email address of the new user
313 :param internetaddress: Home page of the new user
314 :param telephonenumber: Phone number of the new user
315 :param physicaldeliveryoffice: Office location of the new user
316 :param sd: security descriptor of the object
317 :param setpassword: optionally disable password reset
320 displayname = ""
321 if givenname is not None:
322 displayname += givenname
324 if initials is not None:
325 displayname += ' %s.' % initials
327 if surname is not None:
328 displayname += ' %s' % surname
330 cn = username
331 if useusernameascn is None and displayname is not "":
332 cn = displayname
334 user_dn = "CN=%s,%s,%s" % (cn, (userou or "CN=Users"), self.domain_dn())
336 dnsdomain = ldb.Dn(self, self.domain_dn()).canonical_str().replace("/", "")
337 user_principal_name = "%s@%s" % (username, dnsdomain)
338 # The new user record. Note the reliance on the SAMLDB module which
339 # fills in the default informations
340 ldbmessage = {"dn": user_dn,
341 "sAMAccountName": username,
342 "userPrincipalName": user_principal_name,
343 "objectClass": "user"}
345 if surname is not None:
346 ldbmessage["sn"] = surname
348 if givenname is not None:
349 ldbmessage["givenName"] = givenname
351 if displayname is not "":
352 ldbmessage["displayName"] = displayname
353 ldbmessage["name"] = displayname
355 if initials is not None:
356 ldbmessage["initials"] = '%s.' % initials
358 if profilepath is not None:
359 ldbmessage["profilePath"] = profilepath
361 if scriptpath is not None:
362 ldbmessage["scriptPath"] = scriptpath
364 if homedrive is not None:
365 ldbmessage["homeDrive"] = homedrive
367 if homedirectory is not None:
368 ldbmessage["homeDirectory"] = homedirectory
370 if jobtitle is not None:
371 ldbmessage["title"] = jobtitle
373 if department is not None:
374 ldbmessage["department"] = department
376 if company is not None:
377 ldbmessage["company"] = company
379 if description is not None:
380 ldbmessage["description"] = description
382 if mailaddress is not None:
383 ldbmessage["mail"] = mailaddress
385 if internetaddress is not None:
386 ldbmessage["wWWHomePage"] = internetaddress
388 if telephonenumber is not None:
389 ldbmessage["telephoneNumber"] = telephonenumber
391 if physicaldeliveryoffice is not None:
392 ldbmessage["physicalDeliveryOfficeName"] = physicaldeliveryoffice
394 if sd is not None:
395 ldbmessage["nTSecurityDescriptor"] = ndr_pack(sd)
397 self.transaction_start()
398 try:
399 self.add(ldbmessage)
401 # Sets the password for it
402 if setpassword:
403 self.setpassword("(samAccountName=%s)" % ldb.binary_encode(username), password,
404 force_password_change_at_next_login_req)
405 except Exception:
406 self.transaction_cancel()
407 raise
408 else:
409 self.transaction_commit()
412 def deleteuser(self, username):
413 """Deletes a user
415 :param username: Name of the target user
418 filter = "(&(sAMAccountName=%s)(objectCategory=%s,%s))" % (ldb.binary_encode(username), "CN=Person,CN=Schema,CN=Configuration", self.domain_dn())
419 self.transaction_start()
420 try:
421 target = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
422 expression=filter, attrs=[])
423 if len(target) == 0:
424 raise Exception('Unable to find user "%s"' % username)
425 assert(len(target) == 1)
426 self.delete(target[0].dn)
427 except Exception:
428 self.transaction_cancel()
429 raise
430 else:
431 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 "(dn=" + str(user_dn) + ")")
465 # modify the userAccountControl to remove the disabled bit
466 self.enable_account(search_filter)
467 except Exception:
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 Exception:
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):
611 self.set_schema_from_ldb(schema.ldb)
613 def set_schema_from_ldb(self, ldb_conn):
614 dsdb._dsdb_set_schema_from_ldb(self, ldb_conn)
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="dn=%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="dn=%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 set_minPwdAge(self, value):
744 m = ldb.Message()
745 m.dn = ldb.Dn(self, self.domain_dn())
746 m["minPwdAge"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdAge")
747 self.modify(m)
749 def get_minPwdAge(self):
750 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdAge"])
751 if len(res) == 0:
752 return None
753 elif not "minPwdAge" in res[0]:
754 return None
755 else:
756 return res[0]["minPwdAge"][0]
758 def set_minPwdLength(self, value):
759 m = ldb.Message()
760 m.dn = ldb.Dn(self, self.domain_dn())
761 m["minPwdLength"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "minPwdLength")
762 self.modify(m)
764 def get_minPwdLength(self):
765 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["minPwdLength"])
766 if len(res) == 0:
767 return None
768 elif not "minPwdLength" in res[0]:
769 return None
770 else:
771 return res[0]["minPwdLength"][0]
773 def set_pwdProperties(self, value):
774 m = ldb.Message()
775 m.dn = ldb.Dn(self, self.domain_dn())
776 m["pwdProperties"] = ldb.MessageElement(value, ldb.FLAG_MOD_REPLACE, "pwdProperties")
777 self.modify(m)
779 def get_pwdProperties(self):
780 res = self.search(self.domain_dn(), scope=ldb.SCOPE_BASE, attrs=["pwdProperties"])
781 if len(res) == 0:
782 return None
783 elif not "pwdProperties" in res[0]:
784 return None
785 else:
786 return res[0]["pwdProperties"][0]
788 def set_dsheuristics(self, dsheuristics):
789 m = ldb.Message()
790 m.dn = ldb.Dn(self, "CN=Directory Service,CN=Windows NT,CN=Services,%s"
791 % self.get_config_basedn().get_linearized())
792 if dsheuristics is not None:
793 m["dSHeuristics"] = ldb.MessageElement(dsheuristics,
794 ldb.FLAG_MOD_REPLACE, "dSHeuristics")
795 else:
796 m["dSHeuristics"] = ldb.MessageElement([], ldb.FLAG_MOD_DELETE,
797 "dSHeuristics")
798 self.modify(m)
800 def get_dsheuristics(self):
801 res = self.search("CN=Directory Service,CN=Windows NT,CN=Services,%s"
802 % self.get_config_basedn().get_linearized(),
803 scope=ldb.SCOPE_BASE, attrs=["dSHeuristics"])
804 if len(res) == 0:
805 dsheuristics = None
806 elif "dSHeuristics" in res[0]:
807 dsheuristics = res[0]["dSHeuristics"][0]
808 else:
809 dsheuristics = None
811 return dsheuristics
813 def create_ou(self, ou_dn, description=None, name=None, sd=None):
814 """Creates an organizationalUnit object
815 :param ou_dn: dn of the new object
816 :param description: description attribute
817 :param name: name atttribute
818 :param sd: security descriptor of the object, can be
819 an SDDL string or security.descriptor type
821 m = {"dn": ou_dn,
822 "objectClass": "organizationalUnit"}
824 if description:
825 m["description"] = description
826 if name:
827 m["name"] = name
829 if sd:
830 m["nTSecurityDescriptor"] = ndr_pack(sd)
831 self.add(m)
833 def sequence_number(self, seq_type):
834 """Returns the value of the sequence number according to the requested type
835 :param seq_type: type of sequence number
837 self.transaction_start()
838 try:
839 seq = super(SamDB, self).sequence_number(seq_type)
840 except Exception:
841 self.transaction_cancel()
842 raise
843 else:
844 self.transaction_commit()
845 return seq