@@ -33,6 +33,32 @@ def log_user_action(user, event_class, detail):
3333 except Exception as e :
3434 print (e )
3535
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+
3662
3763def hash_password (password ):
3864 """ Generate salt+hash for storing in db"""
@@ -191,6 +217,7 @@ def user_refresh():
191217
192218 if is_active [0 ].lower () == 'y' : # In the user DB and still Active?
193219 token = jwt_ops .create_token (user_name ,old_jwt ['role' ])
220+ log_user_action (user_name , "Success" , "Refreshed token" )
194221 return token
195222
196223 else :
@@ -208,7 +235,7 @@ def user_create():
208235
209236 Requires admin role
210237
211- Form POST Parameters
238+ Form POST JSON Parameters
212239 ----------
213240 username : str
214241 full_name : str
@@ -222,12 +249,18 @@ def user_create():
222249 Duplicate user: 409 + DB error
223250
224251 """
225- new_user = request .form ["username" ]
226- fullname = request .form ["full_name" ]
227- userpw = request .form ["password" ]
228- user_role = request .form ["role" ]
229252
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' ]
231264
232265 pw_hash = hash_password (userpw )
233266
@@ -287,31 +320,100 @@ def get_user_count():
287320 return jsonify (user_count [0 ])
288321
289322
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" ])
292324@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."""
297327
328+ try :
329+ post_dict = json .loads (request .data )
330+ test_username = post_dict ["username" ]
331+ except :
332+ return jsonify ("Missing username" ), 400
298333
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" )
305410
306411
307412@user_api .route ("/api/admin/user/get_users" , methods = ["GET" ])
308413@jwt_ops .admin_required
309414def user_get_list ():
310415 """Return list of users"""
311416
312- # pu = Table("pdp_users", metadata, autoload=True, autoload_with=engine)
313- # pr = Table("pdp_user_roles", metadata, autoload=True, autoload_with=engine)
314-
315417 with engine .connect () as connection :
316418
317419 s = text (
@@ -331,3 +433,27 @@ def user_get_list():
331433
332434 return jsonify (ul ), 200
333435
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