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.
"""
from lexicon.manage import create_lexicon
import user
from user import UserModel
# TODO verify args
uid = user.uid_from_username(args.editor)
u = user.user_from_uid(uid)
create_lexicon(args.name, u)
editor = UserModel.by(name=args.editor)
create_lexicon(args.name, editor)
@requires_lexicon
def command_delete(args):

View File

@ -2,9 +2,9 @@ from cli.helpers import (
add_argument, no_argument, requires_username,
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("--email", help="User's email")
def command_create(args):
"""
Create a user
@ -15,18 +15,14 @@ def command_create(args):
import config
# Verify or query parameters
if not args.username:
args.username = input("username: ").strip()
if not user.valid_username(args.username):
config.logger.error("Invalid username: usernames may only contain alphanumeric characters, dashes, and underscores")
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")
return -1
if not args.displayname:
args.displayname = args.username
if not args.email:
args.email = input("email: ").strip()
if not user.valid_email(args.email):
config.logger.error("Invalid email")
return -1
@ -37,7 +33,7 @@ def command_create(args):
print(json.dumps(js, indent=2))
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):
"""
Delete a user
@ -84,23 +80,23 @@ def command_config(args):
"""
import json
import config
import user
from user import UserModel
if args.get and args.set:
config.logger.error("Specify one of --get and --set")
return -1
uid = user.uid_from_username(args.username)
if not uid:
u = UserModel.by(name=args.username)
if not u:
config.logger.error("User not found")
return -1
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)
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)
@add_argument("--username", help="The user to change password for")
@ -112,14 +108,13 @@ def command_passwd(args):
import os
import config
import user
from user import UserModel
if not args.username:
args.username = input("Username: ")
uid = user.uid_from_username(args.username)
if uid is None:
u = UserModel.by(name=args.username)
if u is None:
config.logger.error("No user with username '{}'".format(args.username))
return -1
u = user.user_from_uid(uid)
pw = getpass.getpass("Password: ")
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
import config
import user
from user import UserModel
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
@ -19,25 +19,21 @@ def get_bp(login_manager):
@login_manager.user_loader
def load_user(uid):
return user.user_from_uid(str(uid))
return UserModel.by(uid=str(uid))
@bp.route('/login/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if form.validate_on_submit():
username = form.username.data
uid = user.uid_from_username(username)
if uid is not None:
u = user.user_from_uid(uid)
if u.check_password(form.password.data):
remember_me = form.remember.data
login_user(u, remember=remember_me)
config.logger.info("Logged in user '{}' ({})".format(
u.username, u.uid))
return redirect(url_for('home.home'))
u = UserModel.by(name=username)
if u is not None and u.check_password(form.password.data):
remember_me = form.remember.data
login_user(u, remember=remember_me)
config.logger.info("Logged in user '{}' ({})".format(
u.username, u.uid))
return redirect(url_for('home.home'))
flash("Login not recognized")
else:
pass
return render_template('auth/login.html', form=form)
@bp.route("/logout/", methods=['GET'])

View File

@ -6,15 +6,37 @@ import uuid
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
from errors import InternalMisuseError, MissingConfigError, IndexMismatchError
import config
import resources
class User(UserMixin):
def __init__(self, uid):
class UserModel(UserMixin):
@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)):
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')):
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.config_path = config.prepend('user', uid, 'config.json')
with config.json_ro(self.config_path) as j:
@ -76,25 +98,7 @@ def create_user(username, displayname, email):
# Set a temporary password
temp_pw = os.urandom(32).hex()
u = User(uid)
u = UserModel.by(uid=uid)
u.set_password(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)