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