134
140
Raises a DBException if the dictionary contains invalid fields.
136
142
if not DB.check_dict(dict, tablefields, disallowed):
137
raise DBException("Supplied dictionary contains invalid fields.")
143
extras = set(dict.keys()) - tablefields
144
raise DBException("Supplied dictionary contains invalid fields. (%s)" % (repr(extras)))
138
145
# Build two lists concurrently: field names and values, as SQL strings
167
174
if (not (DB.check_dict(primarydict, primary_keys, must=True)
168
175
and DB.check_dict(updatedict, tablefields, disallowed_update))):
169
raise DBException("Supplied dictionary contains invalid or "
176
raise DBException("Supplied dictionary contains invalid or missing fields (1).")
171
177
# Make a list of SQL fragments of the form "field = 'new value'"
172
178
# These fragments are ALREADY-ESCAPED
193
199
primarydict, tablename, primary_keys: See update.
195
201
if not DB.check_dict(primarydict, primary_keys, must=True):
196
raise DBException("Supplied dictionary contains invalid or "
202
raise DBException("Supplied dictionary contains invalid or missing fields (2).")
199
204
for k,v in primarydict.items():
200
205
wherelist.append("%s = %s" % (k, _escape(v)))
219
224
primary_keys is indeed the primary key).
221
226
if not DB.check_dict(primarydict, primary_keys, must=True):
222
raise DBException("Supplied dictionary contains invalid or "
227
raise DBException("Supplied dictionary contains invalid or missing fields (3).")
225
229
for k,v in primarydict.items():
226
230
wherelist.append("%s = %s" % (k, _escape(v)))
254
258
if dry: return query
255
259
return self.db.query(query).dictresult()
261
def start_transaction(self, dry=False):
262
"""Starts a DB transaction.
263
Will not commit any changes until self.commit() is called.
265
query = "START TRANSACTION;"
269
def commit(self, dry=False):
270
"""Commits (ends) a DB transaction.
271
Commits all changes since the call to start_transaction.
277
def rollback(self, dry=False):
278
"""Rolls back (ends) a DB transaction, undoing all changes since the
279
call to start_transaction.
257
285
# USER MANAGEMENT FUNCTIONS #
259
287
login_primary = frozenset(["login"])
260
288
login_fields_list = [
261
289
"login", "passhash", "state", "unixid", "email", "nick", "fullname",
262
"rolenm", "studentid", "acct_exp", "pass_exp", "last_login"
290
"rolenm", "studentid", "acct_exp", "pass_exp", "last_login", "svn_pass"
264
292
login_fields = frozenset(login_fields_list)
265
# Do not return passhash when reading from the DB
266
login_getfields = login_fields - frozenset(["passhash"])
268
def create_user(self, dry=False, **kwargs):
294
def create_user(self, user_obj=None, dry=False, **kwargs):
269
295
"""Creates a user login entry in the database.
296
Two ways to call this - passing a user object, or passing
297
all fields as separate arguments.
299
Either pass a "user_obj" as the first argument (in which case other
300
fields will be ignored), or pass all fields as arguments.
270
302
All user fields are to be passed as args. The argument names
271
303
are the field names of the "login" table of the DB schema.
272
304
However, instead of supplying a "passhash", you must supply a
277
309
invalid keys or is missing required keys.
279
311
if 'passhash' in kwargs:
280
raise DBException("Supplied arguments include passhash (invalid).")
312
raise DBException("Supplied arguments include passhash (invalid) (1).")
281
313
# Make a copy of the dict. Change password to passhash (hashing it),
282
314
# and set 'state' to "no_agreement".
283
kwargs = copy.copy(kwargs)
284
if 'password' in kwargs:
285
kwargs['passhash'] = _passhash(kwargs['password'])
286
del kwargs['password']
287
kwargs['state'] = "no_agreement"
317
fields = copy.copy(kwargs)
319
# Use the user object
320
fields = dict(user_obj)
321
if 'password' in fields:
322
fields['passhash'] = _passhash(fields['password'])
323
del fields['password']
325
# Convert role to rolenm
326
fields['rolenm'] = str(user_obj.role)
329
fields['state'] = "no_agreement"
330
# else, we'll trust the user, but it SHOULD be "no_agreement"
331
# (We can't change it because then the user object would not
333
if 'local_password' in fields:
334
del fields['local_password']
288
335
# Execute the query.
289
return self.insert(kwargs, "login", self.login_fields, dry=dry)
336
return self.insert(fields, "login", self.login_fields, dry=dry)
291
338
def update_user(self, login, dry=False, **kwargs):
292
339
"""Updates fields of a particular user. login is the name of the user
315
362
def get_user(self, login, dry=False):
316
"""Given a login, returns a dictionary of the user's DB fields,
317
excluding the passhash field.
363
"""Given a login, returns a User object containing details looked up
319
366
Raises a DBException if the login is not found in the DB.
321
return self.get_single({"login": login}, "login",
322
self.login_getfields, self.login_primary,
368
userdict = self.get_single({"login": login}, "login",
369
self.login_fields, self.login_primary,
323
370
error_notfound="get_user: No user with that login name", dry=dry)
372
return userdict # Query string
373
# Package into a User object
374
return user.User(**userdict)
325
376
def get_users(self, dry=False):
326
"""Returns a list of all users. The list elements are a dictionary of
327
the user's DB fields, excluding the passhash field.
377
"""Returns a list of all users in the DB, as User objects.
329
return self.get_all("login", self.login_getfields, dry=dry)
379
userdicts = self.get_all("login", self.login_fields, dry=dry)
381
return userdicts # Query string
382
# Package into User objects
383
return [user.User(**userdict) for userdict in userdicts]
331
385
def user_authenticate(self, login, password, dry=False):
332
386
"""Performs a password authentication on a user. Returns True if
333
387
"passhash" is the correct passhash for the given login, False
388
if the passhash does not match the password in the DB,
389
and None if the passhash in the DB is NULL.
335
390
Also returns False if the login does not exist (so if you want to
336
391
differentiate these cases, use get_user and catch an exception).
338
query = ("SELECT login FROM login "
339
"WHERE login = '%s' AND passhash = %s;"
340
% (login, _escape(_passhash(password))))
393
query = "SELECT passhash FROM login WHERE login = '%s';" % login
341
394
if dry: return query
342
395
result = self.db.query(query)
343
# If one row was returned, succeed.
344
# Otherwise, fail to authenticate.
345
return result.ntuples() == 1
396
if result.ntuples() == 1:
397
# Valid username. Check password.
398
passhash = result.getresult()[0][0]
401
return _passhash(password) == passhash
348
406
"""Close the DB connection. Do not call any other functions after