s3-lsa: Fix static list of luids in our privileges implementation.
[Samba/ekacnet.git] / source4 / scripting / python / samba / samdb.py
blob4af330b906b6c8d5d9c79275b541c37cdf87265f
1 #!/usr/bin/python
3 # Unix SMB/CIFS implementation.
4 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2007-2008
5 # Copyright (C) Matthias Dieter Wallnoefer 2009
7 # Based on the original in EJS:
8 # Copyright (C) Andrew Tridgell <tridge@samba.org> 2005
9 #
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 dsdb
27 import samba
28 import ldb
29 from samba.idmap import IDmapDB
30 import pwd
31 import time
32 import base64
34 __docformat__ = "restructuredText"
36 class SamDB(samba.Ldb):
37 """The SAM database."""
39 def __init__(self, url=None, lp=None, modules_dir=None, session_info=None,
40 credentials=None, flags=0, options=None, global_schema=True, auto_connect=True,
41 am_rodc=False):
42 self.lp = lp
43 if not auto_connect:
44 url = None
45 elif url is None and lp is not None:
46 url = lp.get("sam database")
48 super(SamDB, self).__init__(url=url, lp=lp, modules_dir=modules_dir,
49 session_info=session_info, credentials=credentials, flags=flags,
50 options=options)
52 if global_schema:
53 dsdb.dsdb_set_global_schema(self)
55 dsdb.dsdb_set_am_rodc(self, am_rodc)
57 def connect(self, url=None, flags=0, options=None):
58 if self.lp is not None:
59 url = self.lp.private_path(url)
61 super(SamDB, self).connect(url=url, flags=flags,
62 options=options)
64 def domain_dn(self):
65 # find the DNs for the domain
66 res = self.search(base="",
67 scope=ldb.SCOPE_BASE,
68 expression="(defaultNamingContext=*)",
69 attrs=["defaultNamingContext"])
70 assert(len(res) == 1 and res[0]["defaultNamingContext"] is not None)
71 return res[0]["defaultNamingContext"][0]
73 def enable_account(self, filter):
74 """Enables an account
76 :param filter: LDAP filter to find the user (eg samccountname=name)
77 """
78 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
79 expression=filter, attrs=["userAccountControl"])
80 assert(len(res) == 1)
81 user_dn = res[0].dn
83 userAccountControl = int(res[0]["userAccountControl"][0])
84 if (userAccountControl & 0x2):
85 userAccountControl = userAccountControl & ~0x2 # remove disabled bit
86 if (userAccountControl & 0x20):
87 userAccountControl = userAccountControl & ~0x20 # remove 'no password required' bit
89 mod = """
90 dn: %s
91 changetype: modify
92 replace: userAccountControl
93 userAccountControl: %u
94 """ % (user_dn, userAccountControl)
95 self.modify_ldif(mod)
97 def force_password_change_at_next_login(self, filter):
98 """Forces a password change at next login
100 :param filter: LDAP filter to find the user (eg samccountname=name)
102 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
103 expression=filter, attrs=[])
104 assert(len(res) == 1)
105 user_dn = res[0].dn
107 mod = """
108 dn: %s
109 changetype: modify
110 replace: pwdLastSet
111 pwdLastSet: 0
112 """ % (user_dn)
113 self.modify_ldif(mod)
115 def newuser(self, username, password,
116 force_password_change_at_next_login_req=False):
117 """Adds a new user
119 :param username: Name of the new user
120 :param password: Password for the new user
121 :param force_password_change_at_next_login_req: Force password change
123 self.transaction_start()
124 try:
125 user_dn = "CN=%s,CN=Users,%s" % (username, self.domain_dn())
127 # The new user record. Note the reliance on the SAMLDB module which
128 # fills in the default informations
129 self.add({"dn": user_dn,
130 "sAMAccountName": username,
131 "objectClass": "user"})
133 # Sets the password for it
134 self.setpassword("(dn=" + user_dn + ")", password,
135 force_password_change_at_next_login_req)
137 except:
138 self.transaction_cancel()
139 raise
140 else:
141 self.transaction_commit()
143 def setpassword(self, filter, password,
144 force_change_at_next_login=False,
145 username=None):
146 """Sets the password for a user
148 Note: This call uses the "userPassword" attribute to set the password.
149 This works correctly on SAMBA 4 and on Windows DCs with
150 "2003 Native" or higer domain function level.
152 :param filter: LDAP filter to find the user (eg samccountname=name)
153 :param password: Password for the user
154 :param force_change_at_next_login: Force password change
156 self.transaction_start()
157 try:
158 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
159 expression=filter, attrs=[])
160 if len(res) == 0:
161 print('Unable to find user "%s"' % (username or filter))
162 raise
163 assert(len(res) == 1)
164 user_dn = res[0].dn
166 setpw = """
167 dn: %s
168 changetype: modify
169 replace: userPassword
170 userPassword:: %s
171 """ % (user_dn, base64.b64encode(password))
173 self.modify_ldif(setpw)
175 if force_change_at_next_login:
176 self.force_password_change_at_next_login(
177 "(dn=" + str(user_dn) + ")")
179 # modify the userAccountControl to remove the disabled bit
180 self.enable_account(filter)
181 except:
182 self.transaction_cancel()
183 raise
184 else:
185 self.transaction_commit()
187 def setexpiry(self, filter, expiry_seconds, no_expiry_req=False):
188 """Sets the account expiry for a user
190 :param filter: LDAP filter to find the user (eg samccountname=name)
191 :param expiry_seconds: expiry time from now in seconds
192 :param no_expiry_req: if set, then don't expire password
194 self.transaction_start()
195 try:
196 res = self.search(base=self.domain_dn(), scope=ldb.SCOPE_SUBTREE,
197 expression=filter,
198 attrs=["userAccountControl", "accountExpires"])
199 assert(len(res) == 1)
200 user_dn = res[0].dn
202 userAccountControl = int(res[0]["userAccountControl"][0])
203 accountExpires = int(res[0]["accountExpires"][0])
204 if no_expiry_req:
205 userAccountControl = userAccountControl | 0x10000
206 accountExpires = 0
207 else:
208 userAccountControl = userAccountControl & ~0x10000
209 accountExpires = samba.unix2nttime(expiry_seconds + int(time.time()))
211 setexp = """
212 dn: %s
213 changetype: modify
214 replace: userAccountControl
215 userAccountControl: %u
216 replace: accountExpires
217 accountExpires: %u
218 """ % (user_dn, userAccountControl, accountExpires)
220 self.modify_ldif(setexp)
221 except:
222 self.transaction_cancel()
223 raise
224 else:
225 self.transaction_commit()
227 def set_domain_sid(self, sid):
228 """Change the domain SID used by this LDB.
230 :param sid: The new domain sid to use.
232 dsdb.samdb_set_domain_sid(self, sid)
234 def get_domain_sid(self):
235 """Read the domain SID used by this LDB.
238 dsdb.samdb_get_domain_sid(self)
240 def set_invocation_id(self, invocation_id):
241 """Set the invocation id for this SamDB handle.
243 :param invocation_id: GUID of the invocation id.
245 dsdb.dsdb_set_ntds_invocation_id(self, invocation_id)
247 def get_invocation_id(self):
248 "Get the invocation_id id"
249 return dsdb.samdb_ntds_invocation_id(self)
251 def set_ntds_settings_dn(self, ntds_settings_dn):
252 """Set the NTDS Settings DN, as would be returned on the dsServiceName rootDSE attribute
254 This allows the DN to be set before the database fully exists
256 :param ntds_settings_dn: The new DN to use
258 dsdb.samdb_set_ntds_settings_dn(self, ntds_settings_dn)
260 invocation_id = property(get_invocation_id, set_invocation_id)
262 domain_sid = property(get_domain_sid, set_domain_sid)
264 def get_ntds_GUID(self):
265 "Get the NTDS objectGUID"
266 return dsdb.samdb_ntds_objectGUID(self)
268 def server_site_name(self):
269 "Get the server site name"
270 return dsdb.samdb_server_site_name(self)
272 def load_partition_usn(self, base_dn):
273 return dsdb.dsdb_load_partition_usn(self, base_dn)