Rname User to UserModel, revamp factory function

This commit is contained in:
Tim Van Baak 2020-01-18 12:39:54 -08:00
parent 5972a309f1
commit 8e99788fcd
4 changed files with 51 additions and 57 deletions

View File

@ -17,11 +17,10 @@ def command_create(args):
settings are as desired before opening the lexicon for player joins. settings are as desired before opening the lexicon for player joins.
""" """
from lexicon.manage import create_lexicon from lexicon.manage import create_lexicon
import user from user import UserModel
# TODO verify args # TODO verify args
uid = user.uid_from_username(args.editor) editor = UserModel.by(name=args.editor)
u = user.user_from_uid(uid) create_lexicon(args.name, editor)
create_lexicon(args.name, u)
@requires_lexicon @requires_lexicon
def command_delete(args): def command_delete(args):

View File

@ -2,9 +2,9 @@ from cli.helpers import (
add_argument, no_argument, requires_username, add_argument, no_argument, requires_username,
config_get, config_set, CONFIG_GET_ROOT_VALUE) config_get, config_set, CONFIG_GET_ROOT_VALUE)
@add_argument("--username", help="User's login handle") @requires_username
@add_argument("--email", required=True, help="User's email")
@add_argument("--displayname", help="User's publicly displayed name") @add_argument("--displayname", help="User's publicly displayed name")
@add_argument("--email", help="User's email")
def command_create(args): def command_create(args):
""" """
Create a user Create a user
@ -15,18 +15,14 @@ def command_create(args):
import config import config
# Verify or query parameters # Verify or query parameters
if not args.username:
args.username = input("username: ").strip()
if not user.valid_username(args.username): if not user.valid_username(args.username):
config.logger.error("Invalid username: usernames may only contain alphanumeric characters, dashes, and underscores") config.logger.error("Invalid username: usernames may only contain alphanumeric characters, dashes, and underscores")
return -1 return -1
if user.uid_from_username(args.username) is not None: if user.UserModel.by(name=args.username) is not None:
config.logger.error("Invalid username: username is already taken") config.logger.error("Invalid username: username is already taken")
return -1 return -1
if not args.displayname: if not args.displayname:
args.displayname = args.username args.displayname = args.username
if not args.email:
args.email = input("email: ").strip()
if not user.valid_email(args.email): if not user.valid_email(args.email):
config.logger.error("Invalid email") config.logger.error("Invalid email")
return -1 return -1
@ -37,7 +33,7 @@ def command_create(args):
print(json.dumps(js, indent=2)) print(json.dumps(js, indent=2))
print("Username: {}\nUser ID: {}\nPassword: {}".format(args.username, new_user.uid, tmp_pw)) print("Username: {}\nUser ID: {}\nPassword: {}".format(args.username, new_user.uid, tmp_pw))
@add_argument("--id", help="id of user to delete") @add_argument("--id", required=True, help="id of user to delete")
def command_delete(args): def command_delete(args):
""" """
Delete a user Delete a user
@ -84,23 +80,23 @@ def command_config(args):
""" """
import json import json
import config import config
import user from user import UserModel
if args.get and args.set: if args.get and args.set:
config.logger.error("Specify one of --get and --set") config.logger.error("Specify one of --get and --set")
return -1 return -1
uid = user.uid_from_username(args.username) u = UserModel.by(name=args.username)
if not uid: if not u:
config.logger.error("User not found") config.logger.error("User not found")
return -1 return -1
if args.get: if args.get:
with config.json_ro('user', uid, 'config.json') as cfg: with config.json_ro('user', u.id, 'config.json') as cfg:
config_get(cfg, args.get) config_get(cfg, args.get)
if args.set: if args.set:
with config.json_rw('user', uid, 'config.json') as cfg: with config.json_rw('user', u.id, 'config.json') as cfg:
config_set(cfg, args.set) config_set(cfg, args.set)
@add_argument("--username", help="The user to change password for") @add_argument("--username", help="The user to change password for")
@ -112,14 +108,13 @@ def command_passwd(args):
import os import os
import config import config
import user from user import UserModel
if not args.username: if not args.username:
args.username = input("Username: ") args.username = input("Username: ")
uid = user.uid_from_username(args.username) u = UserModel.by(name=args.username)
if uid is None: if u is None:
config.logger.error("No user with username '{}'".format(args.username)) config.logger.error("No user with username '{}'".format(args.username))
return -1 return -1
u = user.user_from_uid(uid)
pw = getpass.getpass("Password: ") pw = getpass.getpass("Password: ")
u.set_password(pw) u.set_password(pw)

View File

@ -5,7 +5,7 @@ from wtforms.validators import DataRequired
from flask_login import current_user, login_user, logout_user, login_required from flask_login import current_user, login_user, logout_user, login_required
import config import config
import user from user import UserModel
class LoginForm(FlaskForm): class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()]) username = StringField('Username', validators=[DataRequired()])
@ -19,25 +19,21 @@ def get_bp(login_manager):
@login_manager.user_loader @login_manager.user_loader
def load_user(uid): def load_user(uid):
return user.user_from_uid(str(uid)) return UserModel.by(uid=str(uid))
@bp.route('/login/', methods=['GET', 'POST']) @bp.route('/login/', methods=['GET', 'POST'])
def login(): def login():
form = LoginForm() form = LoginForm()
if form.validate_on_submit(): if form.validate_on_submit():
username = form.username.data username = form.username.data
uid = user.uid_from_username(username) u = UserModel.by(name=username)
if uid is not None: if u is not None and u.check_password(form.password.data):
u = user.user_from_uid(uid) remember_me = form.remember.data
if u.check_password(form.password.data): login_user(u, remember=remember_me)
remember_me = form.remember.data config.logger.info("Logged in user '{}' ({})".format(
login_user(u, remember=remember_me) u.username, u.uid))
config.logger.info("Logged in user '{}' ({})".format( return redirect(url_for('home.home'))
u.username, u.uid))
return redirect(url_for('home.home'))
flash("Login not recognized") flash("Login not recognized")
else:
pass
return render_template('auth/login.html', form=form) return render_template('auth/login.html', form=form)
@bp.route("/logout/", methods=['GET']) @bp.route("/logout/", methods=['GET'])

View File

@ -6,15 +6,37 @@ import uuid
from flask_login import UserMixin from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash from werkzeug.security import generate_password_hash, check_password_hash
from errors import InternalMisuseError, MissingConfigError, IndexMismatchError
import config import config
import resources import resources
class User(UserMixin): class UserModel(UserMixin):
def __init__(self, uid): @staticmethod
def by(uid=None, name=None):
"""
Gets the UserModel with the given uid or username
If the uid or name simply does not match an existing user, returns
None. If the uid matches the index but there is something wrong with
the user's config, raises an error.
"""
if uid and name:
raise InternalMisuseError("uid and name both specified to UserModel.by()")
if not uid and not name:
raise ValueError("One of uid or name must be not None")
if not uid:
with config.json_ro('user', 'index.json') as index:
uid = index.get(name)
if not uid:
return None
if not os.path.isdir(config.prepend('user', uid)): if not os.path.isdir(config.prepend('user', uid)):
raise ValueError("No user with uid {}".format(uid)) raise IndexMismatchError("username={} uid={}".format(name, uid))
if not os.path.isfile(config.prepend('user', uid, 'config.json')): if not os.path.isfile(config.prepend('user', uid, 'config.json')):
raise FileNotFoundError("User {} missing config.json".format(uid)) raise MissingConfigError("uid={}".format(uid))
return UserModel(uid)
def __init__(self, uid):
"""User model initializer, assume all checks were done by by()"""
self.id = str(uid) # Flask-Login checks for this self.id = str(uid) # Flask-Login checks for this
self.config_path = config.prepend('user', uid, 'config.json') self.config_path = config.prepend('user', uid, 'config.json')
with config.json_ro(self.config_path) as j: with config.json_ro(self.config_path) as j:
@ -76,25 +98,7 @@ def create_user(username, displayname, email):
# Set a temporary password # Set a temporary password
temp_pw = os.urandom(32).hex() temp_pw = os.urandom(32).hex()
u = User(uid) u = UserModel.by(uid=uid)
u.set_password(temp_pw) u.set_password(temp_pw)
return u, temp_pw return u, temp_pw
def uid_from_username(username):
"""Gets the internal uid of a user given a username"""
if username is None:
raise ValueError("username must not be None")
if not username:
raise ValueError("username must not be empty")
with config.json_ro('user', 'index.json') as index:
uid = index.get(username)
if uid is None:
config.logger.debug("uid_from_username('{}') returned None".format(username))
return uid
def user_from_uid(uid):
if not os.path.isdir(config.prepend('user', uid)):
config.logger.debug("No user with uid '{}'".format(uid))
return None
return User(uid)