Get home page and login working #14
|
@ -2,6 +2,7 @@
|
|||
User query interface
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import re
|
||||
from typing import Optional, Sequence
|
||||
|
||||
|
@ -75,6 +76,15 @@ def get_all_users(db: DbContext) -> Sequence[User]:
|
|||
return db(select(User)).scalars()
|
||||
|
||||
|
||||
def get_user_by_id(db: DbContext, user_id: int) -> Optional[User]:
|
||||
"""
|
||||
Get a user by the user's id.
|
||||
Returns None if no user was found.
|
||||
"""
|
||||
user: User = db(select(User).where(User.id == user_id)).scalar_one_or_none()
|
||||
return user
|
||||
|
||||
|
||||
def get_user_by_username(db: DbContext, username: str) -> Optional[User]:
|
||||
"""
|
||||
Get a user by the user's username.
|
||||
|
@ -98,3 +108,12 @@ def password_check(db: DbContext, username: str, password: str) -> bool:
|
|||
).scalar_one()
|
||||
return check_password_hash(user_password_hash, password)
|
||||
|
||||
|
||||
def update_logged_in(db: DbContext, username: str) -> None:
|
||||
"""Bump the value of the last_login column for a user."""
|
||||
db(
|
||||
update(User)
|
||||
.where(User.username == username)
|
||||
.values(last_login=datetime.datetime.utcnow())
|
||||
)
|
||||
db.session.commit()
|
||||
|
|
|
@ -100,6 +100,25 @@ class User(ModelBase):
|
|||
articles = relationship("Article", back_populates="user")
|
||||
posts = relationship("Post", back_populates="user")
|
||||
|
||||
#########################
|
||||
# Flask-Login interface #
|
||||
#########################
|
||||
|
||||
@property
|
||||
def is_authenticated(self: "User") -> bool:
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_active(self: "User") -> bool:
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_anonymous(self: "User") -> bool:
|
||||
return False
|
||||
|
||||
def get_id(self: "User") -> str:
|
||||
return str(self.id)
|
||||
|
||||
|
||||
class Lexicon(ModelBase):
|
||||
"""
|
||||
|
|
|
@ -5,7 +5,8 @@ from flask import Flask, g
|
|||
|
||||
from amanuensis.config import AmanuensisConfig, CommandLineConfig
|
||||
from amanuensis.db import DbContext
|
||||
import amanuensis.server.home
|
||||
import amanuensis.server.auth as auth
|
||||
import amanuensis.server.home as home
|
||||
|
||||
|
||||
def get_app(
|
||||
|
@ -48,10 +49,11 @@ def get_app(
|
|||
app.jinja_options.update(trim_blocks=True, lstrip_blocks=True)
|
||||
|
||||
# Set up Flask-Login
|
||||
# TODO
|
||||
auth.get_login_manager().init_app(app)
|
||||
|
||||
# Register blueprints
|
||||
app.register_blueprint(amanuensis.server.home.bp)
|
||||
app.register_blueprint(auth.bp)
|
||||
app.register_blueprint(home.bp)
|
||||
|
||||
def test():
|
||||
return "Hello, world!"
|
||||
|
|
|
@ -1,76 +1,79 @@
|
|||
import logging
|
||||
import time
|
||||
from typing import Optional
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
flash,
|
||||
current_app)
|
||||
g,
|
||||
redirect,
|
||||
render_template,
|
||||
url_for,
|
||||
)
|
||||
from flask_login import (
|
||||
AnonymousUserMixin,
|
||||
login_user,
|
||||
logout_user,
|
||||
login_required,
|
||||
LoginManager)
|
||||
LoginManager,
|
||||
)
|
||||
|
||||
from amanuensis.config import RootConfigDirectoryContext
|
||||
from amanuensis.models import ModelFactory, AnonymousUserModel
|
||||
import amanuensis.backend.user as userq
|
||||
from amanuensis.db import User
|
||||
|
||||
from .forms import LoginForm
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
bp = Blueprint("auth", __name__, url_prefix="/auth", template_folder=".")
|
||||
|
||||
|
||||
def get_login_manager(root: RootConfigDirectoryContext) -> LoginManager:
|
||||
"""
|
||||
Creates a login manager
|
||||
"""
|
||||
def get_login_manager() -> LoginManager:
|
||||
"""Login manager factory"""
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'auth.login'
|
||||
login_manager.anonymous_user = AnonymousUserModel
|
||||
login_manager.login_view = "auth.login"
|
||||
login_manager.anonymous_user = AnonymousUserMixin
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(uid):
|
||||
return current_app.config['model_factory'].user(str(uid))
|
||||
def load_user(user_id_str: str) -> Optional[User]:
|
||||
try:
|
||||
user_id = int(user_id_str)
|
||||
except:
|
||||
return None
|
||||
return userq.get_user_by_id(g.db, user_id)
|
||||
|
||||
login_manager.user_loader(load_user)
|
||||
|
||||
return login_manager
|
||||
|
||||
|
||||
bp_auth = Blueprint('auth', __name__,
|
||||
url_prefix='/auth',
|
||||
template_folder='.')
|
||||
|
||||
|
||||
@bp_auth.route('/login/', methods=['GET', 'POST'])
|
||||
@bp.route("/login/", methods=["GET", "POST"])
|
||||
def login():
|
||||
model_factory: ModelFactory = current_app.config['model_factory']
|
||||
form = LoginForm()
|
||||
|
||||
if not form.validate_on_submit():
|
||||
# Either the request was GET and we should render the form,
|
||||
# or the request was POST and validation failed.
|
||||
return render_template('auth.login.jinja', form=form)
|
||||
return render_template("auth.login.jinja", form=form)
|
||||
|
||||
# POST with valid data
|
||||
username = form.username.data
|
||||
user = model_factory.try_user(username)
|
||||
if not user or not user.check_password(form.password.data):
|
||||
username: str = form.username.data
|
||||
password: str = form.password.data
|
||||
user: User = userq.get_user_by_username(g.db, username)
|
||||
if not user or not userq.password_check(g.db, username, password):
|
||||
# Bad creds
|
||||
flash("Login not recognized")
|
||||
return redirect(url_for('auth.login'))
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
# Login credentials were correct
|
||||
remember_me = form.remember.data
|
||||
remember_me: bool = form.remember.data
|
||||
login_user(user, remember=remember_me)
|
||||
with user.ctx.edit_config() as cfg:
|
||||
cfg.last_login = int(time.time())
|
||||
logger.info('Logged in user "{0.username}" ({0.uid})'.format(user.cfg))
|
||||
return redirect(url_for('home.home'))
|
||||
userq.update_logged_in(g.db, username)
|
||||
LOG.info("Logged in user {0.username} ({0.id})".format(user))
|
||||
return redirect(url_for("home.admin"))
|
||||
|
||||
|
||||
@bp_auth.route("/logout/", methods=['GET'])
|
||||
@bp.get("/logout/")
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for('home.home'))
|
||||
return redirect(url_for("home.admin"))
|
||||
|
|
|
@ -5,11 +5,8 @@ from wtforms.validators import DataRequired
|
|||
|
||||
class LoginForm(FlaskForm):
|
||||
"""/auth/login/"""
|
||||
username = StringField(
|
||||
'Username',
|
||||
validators=[DataRequired()])
|
||||
password = PasswordField(
|
||||
'Password',
|
||||
validators=[DataRequired()])
|
||||
remember = BooleanField('Stay logged in')
|
||||
submit = SubmitField('Log in')
|
||||
|
||||
username = StringField("Username", validators=[DataRequired()])
|
||||
password = PasswordField("Password", validators=[DataRequired()])
|
||||
remember = BooleanField("Stay logged in")
|
||||
submit = SubmitField("Log in")
|
||||
|
|
|
@ -9,12 +9,12 @@
|
|||
[{{ lexicon.status.capitalize() }}]
|
||||
</p>
|
||||
<p><i>{{ lexicon.prompt }}</i></p>
|
||||
{# {% if current_user.is_authenticated %} #}
|
||||
{% if current_user.is_authenticated %}
|
||||
<p>
|
||||
{# TODO #}
|
||||
{# {%
|
||||
if current_user.uid in lexicon.cfg.join.joined
|
||||
or current_user.cfg.is_admin
|
||||
or current_user.is_site_admin
|
||||
%} #}
|
||||
Editor: {#{ lexicon.cfg.editor|user_attr('username') }#} /
|
||||
Players:
|
||||
|
@ -31,7 +31,7 @@
|
|||
{# {% endif %} #}
|
||||
{# {% endif %} #}
|
||||
</p>
|
||||
{# {% endif %} #}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
@ -11,13 +11,12 @@
|
|||
<div id="wrapper">
|
||||
<div id="header">
|
||||
<div id="login-status" {% block login_status_attr %}{% endblock %}>
|
||||
{# TODO #}
|
||||
{# {% if current_user.is_authenticated %}
|
||||
<b>{{ current_user.cfg.username -}}</b>
|
||||
{% if current_user.is_authenticated %}
|
||||
<b>{{ current_user.username -}}</b>
|
||||
(<a href="{{ url_for('auth.logout') }}">Logout</a>)
|
||||
{% else %} #}
|
||||
<a href="#{#{ url_for('auth.login') }#}">Login</a>
|
||||
{# {% endif %} #}
|
||||
{% else %}
|
||||
<a href="{{ url_for('auth.login') }}">Login</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% block header %}{% endblock %}
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue