Wire up basic user login

This commit is contained in:
Tim Van Baak 2020-01-13 12:48:37 -08:00
parent 65049d8202
commit fcaa68850e
6 changed files with 92 additions and 17 deletions

View File

@ -179,7 +179,7 @@ def command_user_passwd(args):
if not args.username: if not args.username:
args.username = input("Username: ") args.username = input("Username: ")
uid = get_user_by_username(args.username) uid = uid_from_username(args.username)
if uid is None: if uid is None:
print("No user with username '{}'".format(args.username)) print("No user with username '{}'".format(args.username))
return -1 return -1

View File

@ -1,10 +1,12 @@
from flask import Flask, render_template from flask import Flask, render_template
from flask_login import LoginManager
import config import config
from server.auth import get_bp as get_auth_bp
app = Flask(__name__, template_folder="../templates") app = Flask(__name__, template_folder="../templates")
app.secret_key = bytes.fromhex(config.get('secret_key')) app.secret_key = bytes.fromhex(config.get('secret_key'))
login = LoginManager(app)
from server.auth import bp auth_bp = get_auth_bp(login)
app.register_blueprint(auth.bp) app.register_blueprint(auth_bp)

40
amanuensis/server/auth.py Normal file
View File

@ -0,0 +1,40 @@
import flask
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired
from flask_login import current_user, login_user
import config
import user
class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
#password = PasswordField('Password', validators=[DataRequired()])
#remember = BooleanField('Remember Me')
submit = SubmitField('Log in')
def get_bp(login_manager):
"""Create a blueprint for the auth functions"""
bp = flask.Blueprint('auth', __name__, url_prefix='/auth')
@login_manager.user_loader
def load_user(uid):
return user.user_from_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 None:
pass
u = user.user_from_uid(uid)
login_user(u)
config.logger.info("Logged in user '{}' ({})".format(u.get('username'), u.uid))
name = u.get('username')
else:
name = "guest"
return flask.render_template('auth/login.html', form=form, username=name)
return bp

View File

@ -1,6 +0,0 @@
<!doctype html>
<html>
<body>
<h1>Hello, {{username}}</h1>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!doctype html>
<html>
<body>
<h1>Hello, {{username}}</h1>
{% if current_user.is_authenticated %}{{ current_user.get('displayname') }}{% endif %}
<form action="" method="post" novalidate>
{{ form.hidden_tag() }}
<p>{{ form.username.label }}<br>{{ form.username(size=32) }}</p>
<p>{{ form.submit() }}</p>
</form>
</body>
</html>

View File

@ -3,24 +3,33 @@ import re
import time import time
import uuid import uuid
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
import config import config
class User(): class User(UserMixin):
def __init__(self, uid): def __init__(self, uid):
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 ValueError("No user with uid {}".format(uid))
self.uid = uid self.uid = str(uid)
self.config = os.path.join('user', uid, 'config.json') self.config_path = os.path.join('user', uid, 'config.json')
with config.json_ro(self.config_path) as j:
self.config = j
def get_id(self):
return self.uid
def get(self, key):
return self.config.get(key)
def set_password(self, pw): def set_password(self, pw):
h = generate_password_hash(pw) h = generate_password_hash(pw)
with config.json_rw(self.config) as j: with config.json_rw(self.config_path) as j:
j['password'] = h j['password'] = h
def check_password(self, pw): def check_password(self, pw):
with config.json_ro(self.config) as j: with config.json_ro(self.config_path) as j:
return check_password_hash(j['password'], pw) return check_password_hash(j['password'], pw)
def valid_username(username): def valid_username(username):
@ -34,6 +43,10 @@ def valid_email(email):
return re.match(addrspec, email) return re.match(addrspec, email)
def create_user(username, displayname, email): def create_user(username, displayname, email):
if not valid_username(username):
raise ValueError("Invalid username: '{}'".format(username))
if not valid_email(email):
raise ValueError("Invalid email: '{}'".format(email))
uid = uuid.uuid4().hex uid = uuid.uuid4().hex
now = int(time.time()) now = int(time.time())
temp_pw = os.urandom(32).hex() temp_pw = os.urandom(32).hex()
@ -51,6 +64,20 @@ def create_user(username, displayname, email):
u.set_password(temp_pw) u.set_password(temp_pw)
return u, temp_pw return u, temp_pw
def get_user_by_username(username): 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: with config.json_ro('user', 'index.json') as index:
return index.get(username) 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)