diff --git a/amanuensis/cli.py b/amanuensis/cli.py
index 362acc3..4d436df 100644
--- a/amanuensis/cli.py
+++ b/amanuensis/cli.py
@@ -179,7 +179,7 @@ def command_user_passwd(args):
if not args.username:
args.username = input("Username: ")
- uid = get_user_by_username(args.username)
+ uid = uid_from_username(args.username)
if uid is None:
print("No user with username '{}'".format(args.username))
return -1
diff --git a/amanuensis/server/__init__.py b/amanuensis/server/__init__.py
index 093024c..54f8ee2 100644
--- a/amanuensis/server/__init__.py
+++ b/amanuensis/server/__init__.py
@@ -1,10 +1,12 @@
from flask import Flask, render_template
+from flask_login import LoginManager
import config
+from server.auth import get_bp as get_auth_bp
app = Flask(__name__, template_folder="../templates")
app.secret_key = bytes.fromhex(config.get('secret_key'))
-
-from server.auth import bp
-app.register_blueprint(auth.bp)
+login = LoginManager(app)
+auth_bp = get_auth_bp(login)
+app.register_blueprint(auth_bp)
diff --git a/amanuensis/server/auth.py b/amanuensis/server/auth.py
new file mode 100644
index 0000000..da3e414
--- /dev/null
+++ b/amanuensis/server/auth.py
@@ -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
diff --git a/amanuensis/templates/admin.html b/amanuensis/templates/admin.html
deleted file mode 100644
index 1db858a..0000000
--- a/amanuensis/templates/admin.html
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-Hello, {{username}}
-
-
diff --git a/amanuensis/templates/auth/login.html b/amanuensis/templates/auth/login.html
new file mode 100644
index 0000000..30bc909
--- /dev/null
+++ b/amanuensis/templates/auth/login.html
@@ -0,0 +1,12 @@
+
+
+
+Hello, {{username}}
+{% if current_user.is_authenticated %}{{ current_user.get('displayname') }}{% endif %}
+
+
+
diff --git a/amanuensis/user.py b/amanuensis/user.py
index f1935e5..e04f3ba 100644
--- a/amanuensis/user.py
+++ b/amanuensis/user.py
@@ -3,24 +3,33 @@ import re
import time
import uuid
+from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
import config
-class User():
+class User(UserMixin):
def __init__(self, uid):
if not os.path.isdir(config.prepend('user', uid)):
raise ValueError("No user with uid {}".format(uid))
- self.uid = uid
- self.config = os.path.join('user', uid, 'config.json')
+ self.uid = str(uid)
+ 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):
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
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)
def valid_username(username):
@@ -34,6 +43,10 @@ def valid_email(email):
return re.match(addrspec, 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
now = int(time.time())
temp_pw = os.urandom(32).hex()
@@ -51,6 +64,20 @@ def create_user(username, displayname, email):
u.set_password(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:
- 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)