@@ -33,6 +33,32 @@ def log_user_action(user, event_class, detail):
33
33
except Exception as e :
34
34
print (e )
35
35
36
+ def password_is_strong (password ):
37
+ """ Check plain-text password against strength rules."""
38
+
39
+ def has_digit (test_string ):
40
+ """Test if any character is a digit."""
41
+ for c in test_string :
42
+ if c .isdigit ():
43
+ return True
44
+ return False
45
+
46
+ def has_alpha (test_string ):
47
+ """Test if any character is alphabetic."""
48
+ for c in test_string :
49
+ if c .isalpha ():
50
+ return True
51
+ return False
52
+
53
+ if (len (password ) > 11
54
+ # and has_alpha(password)
55
+ # and has_digit(password)
56
+ ):
57
+ return True
58
+
59
+ else :
60
+ return False
61
+
36
62
37
63
def hash_password (password ):
38
64
""" Generate salt+hash for storing in db"""
@@ -191,6 +217,7 @@ def user_refresh():
191
217
192
218
if is_active [0 ].lower () == 'y' : # In the user DB and still Active?
193
219
token = jwt_ops .create_token (user_name ,old_jwt ['role' ])
220
+ log_user_action (user_name , "Success" , "Refreshed token" )
194
221
return token
195
222
196
223
else :
@@ -208,7 +235,7 @@ def user_create():
208
235
209
236
Requires admin role
210
237
211
- Form POST Parameters
238
+ Form POST JSON Parameters
212
239
----------
213
240
username : str
214
241
full_name : str
@@ -222,12 +249,18 @@ def user_create():
222
249
Duplicate user: 409 + DB error
223
250
224
251
"""
225
- new_user = request .form ["username" ]
226
- fullname = request .form ["full_name" ]
227
- userpw = request .form ["password" ]
228
- user_role = request .form ["role" ]
229
252
230
- requesting_user = jwt_ops .get_jwt_user ()
253
+ try :
254
+ post_dict = json .loads (request .data )
255
+ new_user = post_dict ["username" ]
256
+ fullname = post_dict ["full_name" ]
257
+ userpw = post_dict ["password" ]
258
+ user_role = post_dict ["role" ]
259
+ except :
260
+ return jsonify ("Missing one or more parameters" ), 400
261
+
262
+
263
+ requesting_user = jwt_ops .validate_decode_jwt ()['sub' ]
231
264
232
265
pw_hash = hash_password (userpw )
233
266
@@ -287,31 +320,100 @@ def get_user_count():
287
320
return jsonify (user_count [0 ])
288
321
289
322
290
- # TODO: A single do-all update_user()
291
- @user_api .route ("/api/admin/user/deactivate" , methods = ["POST" ])
323
+ @user_api .route ("/api/admin/user/check_name" , methods = ["POST" ])
292
324
@jwt_ops .admin_required
293
- def user_deactivate ():
294
- """Mark user as inactive in DB"""
295
- # TODO
296
- return "" , 200
325
+ def check_username ():
326
+ """Return 1 if username exists already, else 0."""
297
327
328
+ try :
329
+ post_dict = json .loads (request .data )
330
+ test_username = post_dict ["username" ]
331
+ except :
332
+ return jsonify ("Missing username" ), 400
298
333
299
- @user_api .route ("/api/admin/user/activate" , methods = ["POST" ])
300
- @jwt_ops .admin_required
301
- def user_activate ():
302
- """Mark user as active in DB"""
303
- # TODO
304
- return "" , 200
334
+ with engine .connect () as connection :
335
+
336
+ s = text ( """select count(username) from pdp_users where username=:u """ )
337
+ s = s .bindparams (u = test_username )
338
+ result = connection .execute (s )
339
+
340
+ if result .rowcount : # As we're doing a count() we *should* get a result
341
+ user_exists = result .fetchone ()[0 ]
342
+ else :
343
+ log_user_action (test_username , "Failure" , "Error when checking username" )
344
+ return jsonify ("Error checking username" ), 500
345
+
346
+ return jsonify (user_exists )
347
+
348
+ @user_api .route ("/api/admin/user/update" , methods = ["POST" ])
349
+ @jwt_ops .admin_required
350
+ def user_update ():
351
+ """Update existing user record
352
+ """
353
+
354
+ post_dict = json .loads (request .data )
355
+
356
+ try :
357
+ username = post_dict ["username" ]
358
+ except :
359
+ return jsonify ("Must specify username" ), 400
360
+
361
+ update_dict = {}
362
+
363
+ # Need to be a bit defensive here & select what we want instead of taking what we're given
364
+ for key in ["full_name" , "active" , "role" , "password" ]:
365
+ try :
366
+ val = post_dict [key ]
367
+ update_dict [key ] = val
368
+ except :
369
+ pass
370
+
371
+
372
+ if not update_dict :
373
+ return jsonify ("No changed items specified" ) # If nothing to do, declare victory
374
+
375
+ if "password" in update_dict .keys ():
376
+
377
+ if password_is_strong (update_dict ['password' ]):
378
+ update_dict ['password' ] = hash_password (update_dict ['password' ])
379
+ else :
380
+ return jsonify ("Password too weak" )
381
+
382
+
383
+
384
+ # We have a variable number of columns to update.
385
+ # We could generate a text query on the fly, but this seems the perfect place to use the ORM
386
+ # and let it handle the update for us.
387
+
388
+ from sqlalchemy import update
389
+ from sqlalchemy .orm import Session , sessionmaker
390
+
391
+ Session = sessionmaker (engine )
392
+
393
+ session = Session ()
394
+ # #TODO: Figure out why context manager doesn't work or do try/finally
395
+
396
+ PU = Table ("pdp_users" , metadata , autoload = True , autoload_with = engine )
397
+ # pr = Table("pdp_user_roles", metadata, autoload=True, autoload_with=engine)
398
+
399
+ #TODO: Check tendered role or join roles table for update
400
+
401
+ stmt = update (PU ).where (PU .columns .username == username ).values (update_dict ).\
402
+ execution_options (synchronize_session = "fetch" )
403
+
404
+ result = session .execute (stmt )
405
+
406
+ session .commit ()
407
+ session .close ()
408
+
409
+ return jsonify ("Updated" )
305
410
306
411
307
412
@user_api .route ("/api/admin/user/get_users" , methods = ["GET" ])
308
413
@jwt_ops .admin_required
309
414
def user_get_list ():
310
415
"""Return list of users"""
311
416
312
- # pu = Table("pdp_users", metadata, autoload=True, autoload_with=engine)
313
- # pr = Table("pdp_user_roles", metadata, autoload=True, autoload_with=engine)
314
-
315
417
with engine .connect () as connection :
316
418
317
419
s = text (
@@ -331,3 +433,27 @@ def user_get_list():
331
433
332
434
return jsonify (ul ), 200
333
435
436
+ @user_api .route ("/api/admin/user/get_info/<string:username>" , methods = ["GET" ])
437
+ @jwt_ops .admin_required
438
+ def user_get_info (username ):
439
+ """Return info on a specified user"""
440
+
441
+ with engine .connect () as connection :
442
+
443
+ s = text (
444
+ """ select username, full_name, active, pr.role
445
+ from pdp_users as pu
446
+ left join pdp_user_roles as pr on pu.role = pr._id
447
+ where username=:u
448
+ """
449
+ )
450
+ s = s .bindparams (u = username )
451
+ result = connection .execute (s )
452
+
453
+ if result .rowcount :
454
+ user_row = result .fetchone ()
455
+ else :
456
+ log_user_action (username , "Failure" , "Error when getting user info" )
457
+ return jsonify ("Username not found" ), 400
458
+
459
+ return jsonify ( dict (zip (result .keys (), user_row )) ), 200
0 commit comments