Reduce controller code file size; add logging
This commit is contained in:
parent
501d878443
commit
29543026ed
|
|
@ -58,14 +58,3 @@ class Database(MongoClient):
|
||||||
if user := self.db.users.find_one({'_id': id}):
|
if user := self.db.users.find_one({'_id': id}):
|
||||||
return User(**user)
|
return User(**user)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_user_from_token(self, token: str) -> Optional[User]:
|
|
||||||
"""
|
|
||||||
Verify a user and retreive a their full profile from the database.
|
|
||||||
|
|
||||||
This is like User.verify_jwt except it also fetches fields from the
|
|
||||||
database which are not present in the client-visible token.
|
|
||||||
"""
|
|
||||||
if jwt_user := User.verify_jwt(token):
|
|
||||||
return self.get_user_by_id(jwt_user._id)
|
|
||||||
return None
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
from functools import wraps
|
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
from random import randbytes
|
|
||||||
from sys import stderr
|
|
||||||
|
|
||||||
from flask import (Flask, redirect, url_for, request, send_file, make_response,
|
|
||||||
abort, render_template, session, g)
|
|
||||||
|
|
||||||
from roc_fnb.util.env_file import env_file
|
|
||||||
from roc_fnb.website.database import Database
|
|
||||||
from roc_fnb.website.models.user import JwtUser
|
|
||||||
|
|
||||||
db = Database.from_env()
|
|
||||||
|
|
||||||
app = Flask(
|
|
||||||
import_name=__name__.split('.')[0],
|
|
||||||
static_url_path='/',
|
|
||||||
template_folder=Path(__file__).absolute().parent / 'templates',
|
|
||||||
static_folder=Path(__file__).absolute().parent / 'static',
|
|
||||||
)
|
|
||||||
|
|
||||||
app.secret_key = env_file('FLASK_SECRET', default_file='./flask.secret', default_fn=lambda: randbytes(12))
|
|
||||||
|
|
||||||
@app.before_request
|
|
||||||
def decode_user():
|
|
||||||
if user := session.get('user'):
|
|
||||||
g.user = JwtUser.from_json(data=json.loads(user))
|
|
||||||
|
|
||||||
|
|
||||||
def require_user(admin = False, moderator = False):
|
|
||||||
"""
|
|
||||||
A decorator for any routes which require authentication.
|
|
||||||
|
|
||||||
https://stackoverflow.com/a/51820573
|
|
||||||
"""
|
|
||||||
def _require_user(handler):
|
|
||||||
@wraps(handler)
|
|
||||||
def __require_user():
|
|
||||||
if getattr(g, 'user', None) is None \
|
|
||||||
or (admin and not user.admin) \
|
|
||||||
or (moderator and not user.moderator):
|
|
||||||
abort(401)
|
|
||||||
return handler()
|
|
||||||
return __require_user
|
|
||||||
return _require_user
|
|
||||||
|
|
||||||
@app.route('/ig')
|
|
||||||
def ig_redir():
|
|
||||||
return redirect('https://instagram.com/RocFNB')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/donate')
|
|
||||||
def donate_redir():
|
|
||||||
return redirect('https://venmo.com/RocFoodNotBombs')
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
|
||||||
def index():
|
|
||||||
return redirect('/index.html')
|
|
||||||
|
|
||||||
|
|
||||||
@app.post('/login')
|
|
||||||
def submit_login():
|
|
||||||
form = request.json
|
|
||||||
user = db.get_user_by_name(form['name'])
|
|
||||||
if not user.check_password(form['password']):
|
|
||||||
abort(401) # unauthorized
|
|
||||||
session['user'] = json.dumps(user.public_fields)
|
|
||||||
return redirect('/me')
|
|
||||||
|
|
||||||
@app.get('/login')
|
|
||||||
def render_login_page():
|
|
||||||
if getattr(g, 'user', None):
|
|
||||||
return redirect('/me')
|
|
||||||
return render_template('login.html')
|
|
||||||
|
|
||||||
@app.get('/me')
|
|
||||||
@require_user()
|
|
||||||
def get_profile():
|
|
||||||
return render_template('profile.html', user=g.user)
|
|
||||||
1
roc_fnb/website/server/__init__.py
Normal file
1
roc_fnb/website/server/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from roc_fnb.website.server.server import app
|
||||||
38
roc_fnb/website/server/decorators.py
Normal file
38
roc_fnb/website/server/decorators.py
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
"""
|
||||||
|
Various decorators which may be applied to specific routes
|
||||||
|
|
||||||
|
See https://stackoverflow.com/a/51820573 for reference.
|
||||||
|
"""
|
||||||
|
from flask import g, abort, request
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
|
||||||
|
def require_user(admin = False, moderator = False):
|
||||||
|
"""A decorator for any routes which require authentication."""
|
||||||
|
def _require_user(handler):
|
||||||
|
@wraps(handler)
|
||||||
|
def __require_user():
|
||||||
|
if getattr(g, 'user', None) is None \
|
||||||
|
or (admin and not g.user.admin) \
|
||||||
|
or (moderator and not g.user.moderator):
|
||||||
|
abort(401)
|
||||||
|
return handler()
|
||||||
|
return __require_user
|
||||||
|
return _require_user
|
||||||
|
|
||||||
|
|
||||||
|
def logger_request_bindings(log):
|
||||||
|
"""Applied to a route which logs something to have request data bound to the logger."""
|
||||||
|
def _lrb(handler):
|
||||||
|
@wraps(handler)
|
||||||
|
def __lrb():
|
||||||
|
log.bind(
|
||||||
|
path=request.path,
|
||||||
|
method=request.method,
|
||||||
|
user_agent=request.user_agent,
|
||||||
|
remote_ip=request.remote_addr,
|
||||||
|
user=getattr(g, 'user', '(anonymous)'),
|
||||||
|
)
|
||||||
|
return handler(log)
|
||||||
|
return __lrb
|
||||||
|
return _lrb
|
||||||
46
roc_fnb/website/server/server.py
Normal file
46
roc_fnb/website/server/server.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
from functools import wraps
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from random import randbytes
|
||||||
|
from sys import stderr
|
||||||
|
|
||||||
|
from flask import Flask, redirect
|
||||||
|
|
||||||
|
from roc_fnb.util.env_file import env_file
|
||||||
|
from roc_fnb.util import log
|
||||||
|
from roc_fnb.website.database import Database
|
||||||
|
from roc_fnb.website.server.user import setup_user_routes
|
||||||
|
|
||||||
|
|
||||||
|
db = Database.from_env()
|
||||||
|
|
||||||
|
app = Flask(
|
||||||
|
import_name=__name__.split('.')[0],
|
||||||
|
static_url_path='/',
|
||||||
|
template_folder=Path(__file__).absolute().parent / 'templates',
|
||||||
|
static_folder=Path(__file__).absolute().parent / 'static',
|
||||||
|
)
|
||||||
|
|
||||||
|
app.secret_key = env_file('FLASK_SECRET', default_file='./flask.secret', default_fn=lambda: randbytes(12))
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def decode_user():
|
||||||
|
if user := session.get('user'):
|
||||||
|
g.user = JwtUser.from_json(data=json.loads(user))
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/ig')
|
||||||
|
def ig_redir():
|
||||||
|
return redirect('https://instagram.com/RocFNB')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/donate')
|
||||||
|
def donate_redir():
|
||||||
|
return redirect('https://venmo.com/RocFoodNotBombs')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return redirect('/index.html')
|
||||||
|
|
||||||
|
setup_user_routes(app, db)
|
||||||
31
roc_fnb/website/server/user.py
Normal file
31
roc_fnb/website/server/user.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from flask import request, redirect, render_template, g, abort
|
||||||
|
|
||||||
|
from roc_fnb.util import log
|
||||||
|
from roc_fnb.website.server.decorators import require_user, logger_request_bindings
|
||||||
|
|
||||||
|
def setup_user_routes(app, db):
|
||||||
|
@app.post('/login')
|
||||||
|
@logger_request_bindings(log)
|
||||||
|
def submit_login(log):
|
||||||
|
form = request.json
|
||||||
|
log.info('user attempting login', name=form.get('name'))
|
||||||
|
user = db.get_user_by_name(form['name'])
|
||||||
|
if not user.check_password(form['password']):
|
||||||
|
log.warn('incorrect password submitted', name=form['name'])
|
||||||
|
abort(401) # unauthorized
|
||||||
|
session['user'] = json.dumps(user.public_fields)
|
||||||
|
return redirect('/me')
|
||||||
|
|
||||||
|
@app.get('/login')
|
||||||
|
def render_login_page():
|
||||||
|
if getattr(g, 'user', None):
|
||||||
|
log.debug('user is already logged in', user=g.user)
|
||||||
|
return redirect('/me')
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
@app.get('/me')
|
||||||
|
@require_user()
|
||||||
|
def get_profile():
|
||||||
|
return render_template('profile.html', user=g.user)
|
||||||
Loading…
Reference in a new issue