Compare commits
No commits in common. "73671bd8f13b0c40dd0accd6ba27683b3bde442a" and "cdca5646efb44bf895b02370a538cba79eabe2df" have entirely different histories.
73671bd8f1
...
cdca5646ef
20 changed files with 52 additions and 288 deletions
|
@ -1,10 +0,0 @@
|
||||||
# Ignore everything
|
|
||||||
*
|
|
||||||
|
|
||||||
# Include source, config and scripts
|
|
||||||
!app
|
|
||||||
!etc
|
|
||||||
!*.md
|
|
||||||
!*.sh
|
|
||||||
!*.txt
|
|
||||||
!LICENSE
|
|
26
Dockerfile
26
Dockerfile
|
@ -1,26 +0,0 @@
|
||||||
# syntax=docker/dockerfile:1
|
|
||||||
FROM python:3.9.2-slim AS development
|
|
||||||
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
RUN pip install pymysql
|
|
||||||
|
|
||||||
ADD https://git.zeus.gent/haldis/menus/-/archive/master/menus-master.tar /tmp
|
|
||||||
RUN mkdir menus && \
|
|
||||||
tar --directory=menus --extract --strip-components=1 --file=/tmp/menus-master.tar
|
|
||||||
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN pip install -r requirements.txt
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
WORKDIR /src/app
|
|
||||||
CMD python app.py db upgrade && \
|
|
||||||
python app.py runserver -h 0.0.0.0 -p 8000
|
|
||||||
|
|
||||||
FROM development AS production
|
|
||||||
|
|
||||||
RUN pip install waitress
|
|
||||||
|
|
||||||
CMD python app.py db upgrade && \
|
|
||||||
python waitress_wsgi.py
|
|
22
app/app.py
22
app/app.py
|
@ -15,17 +15,15 @@ from flask_bootstrap import Bootstrap, StaticCDN
|
||||||
from flask_debugtoolbar import DebugToolbarExtension
|
from flask_debugtoolbar import DebugToolbarExtension
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
from flask_migrate import Migrate, MigrateCommand
|
from flask_migrate import Migrate, MigrateCommand
|
||||||
|
from flask_oauthlib.client import OAuth, OAuthException
|
||||||
from flask_script import Manager, Server
|
from flask_script import Manager, Server
|
||||||
|
from login import init_login
|
||||||
from markupsafe import Markup
|
from markupsafe import Markup
|
||||||
|
|
||||||
from admin import init_admin
|
|
||||||
from auth.login import init_login
|
|
||||||
from auth.zeus import init_oauth
|
|
||||||
from config import Configuration
|
|
||||||
from models import db
|
from models import db
|
||||||
from models.anonymous_user import AnonymouseUser
|
from models.anonymous_user import AnonymouseUser
|
||||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
from sentry_sdk.integrations.flask import FlaskIntegration
|
||||||
from utils import euro_string, price_range_string, ignore_none
|
from utils import euro_string, price_range_string, ignore_none
|
||||||
|
from zeus import init_oauth
|
||||||
|
|
||||||
|
|
||||||
def register_plugins(app: Flask) -> Manager:
|
def register_plugins(app: Flask) -> Manager:
|
||||||
|
@ -102,22 +100,18 @@ def add_routes(application: Flask) -> None:
|
||||||
# import views # TODO convert to blueprint
|
# import views # TODO convert to blueprint
|
||||||
# import views.stats # TODO convert to blueprint
|
# import views.stats # TODO convert to blueprint
|
||||||
|
|
||||||
from auth.login import auth_bp
|
from login import auth_bp
|
||||||
from auth.microsoft import auth_microsoft_bp
|
|
||||||
from auth.zeus import auth_zeus_bp
|
|
||||||
from views.debug import debug_bp
|
from views.debug import debug_bp
|
||||||
from views.general import general_bp
|
from views.general import general_bp
|
||||||
from views.order import order_bp
|
from views.order import order_bp
|
||||||
from views.stats import stats_blueprint
|
from views.stats import stats_blueprint
|
||||||
|
from zeus import oauth_bp
|
||||||
|
|
||||||
application.register_blueprint(general_bp, url_prefix="/")
|
application.register_blueprint(general_bp, url_prefix="/")
|
||||||
application.register_blueprint(order_bp, url_prefix="/order")
|
application.register_blueprint(order_bp, url_prefix="/order")
|
||||||
application.register_blueprint(stats_blueprint, url_prefix="/stats")
|
application.register_blueprint(stats_blueprint, url_prefix="/stats")
|
||||||
application.register_blueprint(auth_bp, url_prefix="/")
|
application.register_blueprint(auth_bp, url_prefix="/")
|
||||||
if Configuration.ENABLE_MICROSOFT_AUTH:
|
application.register_blueprint(oauth_bp, url_prefix="/")
|
||||||
application.register_blueprint(auth_microsoft_bp,
|
|
||||||
url_prefix="/users/auth/microsoft_graph_auth") # "/auth/microsoft")
|
|
||||||
application.register_blueprint(auth_zeus_bp, url_prefix="/auth/zeus")
|
|
||||||
|
|
||||||
if application.debug:
|
if application.debug:
|
||||||
application.register_blueprint(debug_bp, url_prefix="/debug")
|
application.register_blueprint(debug_bp, url_prefix="/debug")
|
||||||
|
@ -181,10 +175,6 @@ def create_app():
|
||||||
add_routes(app)
|
add_routes(app)
|
||||||
add_template_filters(app)
|
add_template_filters(app)
|
||||||
|
|
||||||
@app.context_processor
|
|
||||||
def inject_config():
|
|
||||||
return dict(configuration=Configuration)
|
|
||||||
|
|
||||||
return app, app_manager
|
return app, app_manager
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
import typing
|
|
||||||
|
|
||||||
from flask import Blueprint, url_for, request, redirect, flash, Response
|
|
||||||
from flask_login import login_user
|
|
||||||
from microsoftgraph.client import Client
|
|
||||||
|
|
||||||
from config import Configuration
|
|
||||||
from models import User, db
|
|
||||||
|
|
||||||
auth_microsoft_bp = Blueprint("auth_microsoft_bp", __name__)
|
|
||||||
|
|
||||||
client = Client(Configuration.MICROSOFT_AUTH_ID,
|
|
||||||
Configuration.MICROSOFT_AUTH_SECRET,
|
|
||||||
account_type="ugentbe.onmicrosoft.com")
|
|
||||||
|
|
||||||
|
|
||||||
def microsoft_login():
|
|
||||||
"""Log in using Microsoft"""
|
|
||||||
scope = ["openid", "profile", "User.Read", "User.Read.All"]
|
|
||||||
url = client.authorization_url(url_for("auth_microsoft_bp.authorized", _external=True), scope, state=None)
|
|
||||||
return redirect(url)
|
|
||||||
|
|
||||||
|
|
||||||
@auth_microsoft_bp.route("/login")
|
|
||||||
def login():
|
|
||||||
"""Function to handle a user trying to log in"""
|
|
||||||
return microsoft_login()
|
|
||||||
|
|
||||||
|
|
||||||
@auth_microsoft_bp.route("callback") # "/authorized")
|
|
||||||
def authorized() -> typing.Any:
|
|
||||||
# type is 'typing.Union[str, Response]', but this errors due to
|
|
||||||
# https://github.com/python/mypy/issues/7187
|
|
||||||
"""Check authorized status"""
|
|
||||||
|
|
||||||
oauth_code = request.args['code']
|
|
||||||
|
|
||||||
resp = client.exchange_code(url_for("auth_microsoft_bp.authorized", _external=True), oauth_code)
|
|
||||||
client.set_token(resp.data)
|
|
||||||
|
|
||||||
resp = client.users.get_me()
|
|
||||||
microsoft_uuid = resp.data['id']
|
|
||||||
username = resp.data['userPrincipalName']
|
|
||||||
|
|
||||||
# Fail if fields are not populated
|
|
||||||
if not microsoft_uuid or not username:
|
|
||||||
flash("You're not allowed to enter, please contact a system administrator")
|
|
||||||
return redirect(url_for("general_bp.home"))
|
|
||||||
|
|
||||||
# Find existing user by Microsoft UUID (userPrincipalName can change)
|
|
||||||
user = User.query.filter_by(microsoft_uuid=microsoft_uuid).first()
|
|
||||||
if user:
|
|
||||||
return login_and_redirect_user(user)
|
|
||||||
|
|
||||||
# Find existing user by username (pre-existing account)
|
|
||||||
user = User.query.filter_by(username=username).first()
|
|
||||||
if user:
|
|
||||||
return login_and_redirect_user(user)
|
|
||||||
|
|
||||||
# No user found, create a new one
|
|
||||||
user = create_user(username, microsoft_uuid=microsoft_uuid)
|
|
||||||
return login_and_redirect_user(user)
|
|
||||||
|
|
||||||
|
|
||||||
def login_and_redirect_user(user) -> Response:
|
|
||||||
"""Log in the user and then redirect them"""
|
|
||||||
login_user(user)
|
|
||||||
return redirect(url_for("general_bp.home"))
|
|
||||||
|
|
||||||
|
|
||||||
def create_user(username, *, microsoft_uuid) -> User:
|
|
||||||
"""Create a temporary user if it is needed"""
|
|
||||||
user = User()
|
|
||||||
user.configure(username, False, 1, microsoft_uuid=microsoft_uuid)
|
|
||||||
db.session.add(user)
|
|
||||||
db.session.commit()
|
|
||||||
return user
|
|
|
@ -1,16 +1,11 @@
|
||||||
"""An example for a Haldis config"""
|
"An example for a Haldis config"
|
||||||
# import os
|
# config
|
||||||
|
|
||||||
|
|
||||||
class Configuration:
|
class Configuration:
|
||||||
"Haldis configuration object"
|
"Haldis configuration object"
|
||||||
# pylint: disable=too-few-public-methods
|
# pylint: disable=too-few-public-methods
|
||||||
SQLALCHEMY_DATABASE_URI = "sqlite:///haldis.db"
|
SQLALCHEMY_DATABASE_URI = "sqlite:///haldis.db"
|
||||||
# MARIADB_HOST = os.environ.get("MARIADB_HOST")
|
|
||||||
# MARIADB_DB = os.environ.get("MARIADB_DATABASE")
|
|
||||||
# MARIADB_USER = os.environ.get("MARIADB_USER")
|
|
||||||
# MARIADB_PASS = os.environ.get("MARIADB_PASSWORD")
|
|
||||||
# SQLALCHEMY_DATABASE_URI = f"mysql+pymysql://{MARIADB_USER}:{MARIADB_PASS}@{MARIADB_HOST}/{MARIADB_DB}"
|
|
||||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
HALDIS_ADMINS = []
|
HALDIS_ADMINS = []
|
||||||
|
@ -20,7 +15,3 @@ class Configuration:
|
||||||
SENTRY_DSN = None
|
SENTRY_DSN = None
|
||||||
ZEUS_KEY = "tomtest"
|
ZEUS_KEY = "tomtest"
|
||||||
ZEUS_SECRET = "blargh"
|
ZEUS_SECRET = "blargh"
|
||||||
|
|
||||||
ENABLE_MICROSOFT_AUTH = False
|
|
||||||
MICROSOFT_AUTH_ID = ""
|
|
||||||
MICROSOFT_AUTH_SECRET = ""
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Import this class to load the standard HLDS definitions
|
# Import this class to load the standard HLDS definitions
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from os import path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from .models import Location
|
from .models import Location
|
||||||
|
@ -11,14 +12,10 @@ __all__ = ["location_definitions", "location_definition_version"]
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
|
|
||||||
# TODO Use proper way to get resources, see https://stackoverflow.com/a/10935674
|
# TODO Use proper way to get resources, see https://stackoverflow.com/a/10935674
|
||||||
ROOT_DIR = Path(__file__).parent.parent.parent
|
DATA_DIR = path.join(path.dirname(__file__), "..", "..", "menus")
|
||||||
DATA_DIR = ROOT_DIR / "menus"
|
|
||||||
|
|
||||||
location_definitions: List[Location] = parse_all_directory(str(DATA_DIR))
|
location_definitions: List[Location] = parse_all_directory(DATA_DIR)
|
||||||
location_definitions.sort(key=lambda l: l.name)
|
location_definitions.sort(key=lambda l: l.name)
|
||||||
|
|
||||||
try:
|
proc = subprocess.run(["git", "rev-parse", "HEAD"], stdout=subprocess.PIPE, check=True)
|
||||||
proc = subprocess.run(["git", "rev-parse", "HEAD"], stdout=subprocess.PIPE, cwd=str(ROOT_DIR), check=True)
|
location_definition_version = proc.stdout.decode().strip()
|
||||||
location_definition_version = proc.stdout.decode().strip()
|
|
||||||
except FileNotFoundError:
|
|
||||||
location_definition_version = ""
|
|
||||||
|
|
|
@ -29,9 +29,9 @@ location = >location_header items:{ block } ;
|
||||||
|
|
||||||
|
|
||||||
attributes =
|
attributes =
|
||||||
name:/[^\n#]*?(?= +-- | | €| *\n| *#)/
|
name:/[^\n#]*?(?= +-- | | *\n| *#)/
|
||||||
[ s '--' ~ s description:/[^\n#]*?(?= | *\n| *#)/ ]
|
[ s '--' ~ s description:/[^\n#]*?(?= | *\n| *#)/ ]
|
||||||
[ / +/ ~
|
[ / {2,}/ ~
|
||||||
[ {[ s ] ('{' tags+:identifier '}')} / +|$/ ]
|
[ {[ s ] ('{' tags+:identifier '}')} / +|$/ ]
|
||||||
[ price:price ]
|
[ price:price ]
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,25 +1,31 @@
|
||||||
"""Script for everything related to logging in and out"""
|
"Script for everything related to logging in and out"
|
||||||
from flask import Blueprint, abort, redirect, session, url_for
|
from flask import Blueprint, abort, redirect, session, url_for
|
||||||
from flask_login import current_user, logout_user
|
from flask_login import current_user, logout_user
|
||||||
from models import User
|
from models import User
|
||||||
from werkzeug.wrappers import Response
|
from werkzeug.wrappers import Response
|
||||||
|
from zeus import zeus_login
|
||||||
|
|
||||||
auth_bp = Blueprint("auth_bp", __name__)
|
auth_bp = Blueprint("auth_bp", __name__)
|
||||||
|
|
||||||
|
|
||||||
def init_login(app) -> None:
|
def init_login(app) -> None:
|
||||||
"""Initialize the login"""
|
"Initialize the login"
|
||||||
|
|
||||||
# pylint: disable=W0612
|
# pylint: disable=W0612
|
||||||
@app.login_manager.user_loader
|
@app.login_manager.user_loader
|
||||||
def load_user(userid) -> User:
|
def load_user(userid) -> User:
|
||||||
"""Load the user"""
|
"Load the user"
|
||||||
return User.query.filter_by(id=userid).first()
|
return User.query.filter_by(id=userid).first()
|
||||||
|
|
||||||
|
|
||||||
|
@auth_bp.route("/login")
|
||||||
|
def login():
|
||||||
|
"Function to handle a user trying to log in"
|
||||||
|
return zeus_login()
|
||||||
|
|
||||||
|
|
||||||
@auth_bp.route("/logout")
|
@auth_bp.route("/logout")
|
||||||
def logout() -> Response:
|
def logout() -> Response:
|
||||||
"""Function to handle a user trying to log out"""
|
"Function to handle a user trying to log out"
|
||||||
if "zeus_token" in session:
|
if "zeus_token" in session:
|
||||||
session.pop("zeus_token", None)
|
session.pop("zeus_token", None)
|
||||||
logout_user()
|
logout_user()
|
||||||
|
@ -27,6 +33,6 @@ def logout() -> Response:
|
||||||
|
|
||||||
|
|
||||||
def before_request() -> None:
|
def before_request() -> None:
|
||||||
"""Function for what has to be done before a request"""
|
"Function for what has to be done before a request"
|
||||||
if current_user.is_anonymous() or not current_user.is_allowed():
|
if current_user.is_anonymous() or not current_user.is_allowed():
|
||||||
abort(401)
|
abort(401)
|
|
@ -43,7 +43,7 @@ def upgrade():
|
||||||
sa.Column("starttime", sa.DateTime(), nullable=True),
|
sa.Column("starttime", sa.DateTime(), nullable=True),
|
||||||
sa.Column("stoptime", sa.DateTime(), nullable=True),
|
sa.Column("stoptime", sa.DateTime(), nullable=True),
|
||||||
sa.Column("public", sa.Boolean(), nullable=True),
|
sa.Column("public", sa.Boolean(), nullable=True),
|
||||||
sa.ForeignKeyConstraint(["location_id"], ["location.id"], name="order_ibfk_1"),
|
sa.ForeignKeyConstraint(["location_id"], ["location.id"]),
|
||||||
sa.PrimaryKeyConstraint("id"),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
op.create_table(
|
op.create_table(
|
||||||
|
@ -65,7 +65,7 @@ def upgrade():
|
||||||
sa.Column("extra", sa.String(length=254), nullable=True),
|
sa.Column("extra", sa.String(length=254), nullable=True),
|
||||||
sa.Column("name", sa.String(length=120), nullable=True),
|
sa.Column("name", sa.String(length=120), nullable=True),
|
||||||
sa.ForeignKeyConstraint(["order_id"], ["order.id"]),
|
sa.ForeignKeyConstraint(["order_id"], ["order.id"]),
|
||||||
sa.ForeignKeyConstraint(["product_id"], ["product.id"], name="order_item_ibfk_3"),
|
sa.ForeignKeyConstraint(["product_id"], ["product.id"]),
|
||||||
sa.ForeignKeyConstraint(["user_id"], ["user.id"]),
|
sa.ForeignKeyConstraint(["user_id"], ["user.id"]),
|
||||||
sa.PrimaryKeyConstraint("id"),
|
sa.PrimaryKeyConstraint("id"),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
"""empty message
|
|
||||||
|
|
||||||
Revision ID: 89b2c980b663
|
|
||||||
Revises: 9eac0f3d7b1e
|
|
||||||
Create Date: 2023-04-20 02:01:54.558602
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision = '89b2c980b663'
|
|
||||||
down_revision = '9eac0f3d7b1e'
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.add_column('user', sa.Column('microsoft_uuid', sa.VARCHAR(length=120), nullable=True))
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade():
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_column('user', 'microsoft_uuid')
|
|
||||||
# ### end Alembic commands ###
|
|
|
@ -112,12 +112,14 @@ def upgrade():
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# Historical product data migrated, drop obsolete column and table
|
# Historical product data migrated, drop obsolete column and table
|
||||||
op.drop_constraint("order_item_ibfk_3", "order_item", type_="foreignkey")
|
op.execute(text("ALTER TABLE order_item DROP FOREIGN KEY order_item_ibfk_3"))
|
||||||
op.drop_column("order_item", "product_id")
|
op.drop_column("order_item", "product_id")
|
||||||
op.drop_table("product")
|
op.drop_table("product")
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------------------
|
||||||
# Migrate historical location data to orders
|
# Migrate historical location data to orders
|
||||||
|
|
||||||
|
op.execute(text("ALTER TABLE `order` DROP FOREIGN KEY order_ibfk_2"))
|
||||||
op.alter_column(
|
op.alter_column(
|
||||||
"order",
|
"order",
|
||||||
"location_id",
|
"location_id",
|
||||||
|
@ -155,7 +157,6 @@ def upgrade():
|
||||||
for query in chain(new_location_id, [location_name_from_location]):
|
for query in chain(new_location_id, [location_name_from_location]):
|
||||||
op.execute(query)
|
op.execute(query)
|
||||||
# Historical location data migrated, drop obsolete column and table
|
# Historical location data migrated, drop obsolete column and table
|
||||||
op.drop_constraint("order_ibfk_1", "order", type_="foreignkey")
|
|
||||||
op.drop_column("order", "legacy_location_id")
|
op.drop_column("order", "legacy_location_id")
|
||||||
op.drop_table("location")
|
op.drop_table("location")
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,12 @@ from models import db
|
||||||
|
|
||||||
|
|
||||||
class User(db.Model):
|
class User(db.Model):
|
||||||
"""Class used for configuring the User model in the database"""
|
"Class used for configuring the User model in the database"
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||||
admin = db.Column(db.Boolean)
|
admin = db.Column(db.Boolean)
|
||||||
bias = db.Column(db.Integer)
|
bias = db.Column(db.Integer)
|
||||||
# Microsoft OAUTH info
|
# Assocation logic
|
||||||
microsoft_uuid = db.Column(db.String(120), unique=True)
|
|
||||||
# Association logic
|
|
||||||
associations = db.Column(db.String(255), nullable=False, server_default="")
|
associations = db.Column(db.String(255), nullable=False, server_default="")
|
||||||
|
|
||||||
# Relations
|
# Relations
|
||||||
|
@ -27,14 +25,13 @@ class User(db.Model):
|
||||||
def association_list(self) -> List[str]:
|
def association_list(self) -> List[str]:
|
||||||
return self.associations.split(",")
|
return self.associations.split(",")
|
||||||
|
|
||||||
def configure(self, username: str, admin: bool, bias: int, *, microsoft_uuid: str = None, associations: Optional[List[str]] = None) -> None:
|
def configure(self, username: str, admin: bool, bias: int, associations: Optional[List[str]] = None) -> None:
|
||||||
"""Configure the User"""
|
"""Configure the User"""
|
||||||
if associations is None:
|
if associations is None:
|
||||||
associations = []
|
associations = []
|
||||||
self.username = username
|
self.username = username
|
||||||
self.admin = admin
|
self.admin = admin
|
||||||
self.bias = bias
|
self.bias = bias
|
||||||
self.microsoft_uuid = microsoft_uuid
|
|
||||||
self.associations = ",".join(associations)
|
self.associations = ",".join(associations)
|
||||||
|
|
||||||
# pylint: disable=C0111, R0201
|
# pylint: disable=C0111, R0201
|
||||||
|
|
|
@ -63,8 +63,7 @@
|
||||||
<nav class="navbar navbar-default navbar-fixed-top">
|
<nav class="navbar navbar-default navbar-fixed-top">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar"
|
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
|
||||||
aria-expanded="false" aria-controls="navbar">
|
|
||||||
<span class="sr-only">Toggle navigation</span>
|
<span class="sr-only">Toggle navigation</span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
|
@ -82,10 +81,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
{% if current_user.is_anonymous() %}
|
{% if current_user.is_anonymous() %}
|
||||||
{% if configuration.ENABLE_MICROSOFT_AUTH %}
|
<li><a href="{{ url_for('auth_bp.login') }}">Login</a></li>
|
||||||
<li><a href="{{ url_for('auth_microsoft_bp.login') }}">Login with Microsoft</a></li>
|
|
||||||
{% endif %}
|
|
||||||
<li><a href="{{ url_for('auth_zeus_bp.login') }}">Login with Zeus</a></li>
|
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="{{ url_for('general_bp.profile') }}">{{ current_user.username }}</a></li>
|
<li><a href="{{ url_for('general_bp.profile') }}">{{ current_user.username }}</a></li>
|
||||||
<li><a href="{{ url_for('auth_bp.logout') }}">Logout</a></li>
|
<li><a href="{{ url_for('auth_bp.logout') }}">Logout</a></li>
|
||||||
|
@ -100,8 +96,8 @@
|
||||||
{{ utils.flashed_messages(container=True) }}
|
{{ utils.flashed_messages(container=True) }}
|
||||||
|
|
||||||
<div class="container main">
|
<div class="container main">
|
||||||
{% block container -%}
|
{% block container -%}
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
import sentry_sdk
|
|
||||||
from sentry_sdk.integrations.flask import FlaskIntegration
|
|
||||||
from waitress import serve
|
|
||||||
|
|
||||||
from app import create_app
|
|
||||||
from config import Configuration
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
if Configuration.SENTRY_DSN:
|
|
||||||
sentry_sdk.init(
|
|
||||||
dsn=Configuration.SENTRY_DSN,
|
|
||||||
integrations=[FlaskIntegration()]
|
|
||||||
)
|
|
||||||
|
|
||||||
app, app_mgr = create_app()
|
|
||||||
serve(app, host="0.0.0.0", port=8000)
|
|
|
@ -4,30 +4,24 @@ import typing
|
||||||
from flask import (Blueprint, current_app, flash, redirect, request, session,
|
from flask import (Blueprint, current_app, flash, redirect, request, session,
|
||||||
url_for)
|
url_for)
|
||||||
from flask_login import login_user
|
from flask_login import login_user
|
||||||
from flask_oauthlib.client import OAuth, OAuthException, OAuthRemoteApp
|
from flask_oauthlib.client import OAuth, OAuthException
|
||||||
from models import User, db
|
from models import User, db
|
||||||
from werkzeug.wrappers import Response
|
from werkzeug.wrappers import Response
|
||||||
|
|
||||||
auth_zeus_bp = Blueprint("auth_zeus_bp", __name__)
|
oauth_bp = Blueprint("oauth_bp", __name__)
|
||||||
|
|
||||||
|
|
||||||
def zeus_login():
|
def zeus_login():
|
||||||
"""Log in using ZeusWPI"""
|
"Log in using ZeusWPI"
|
||||||
return current_app.zeus.authorize(
|
return current_app.zeus.authorize(
|
||||||
callback=url_for("auth_zeus_bp.authorized", _external=True))
|
callback=url_for("oauth_bp.authorized", _external=True))
|
||||||
|
|
||||||
|
|
||||||
@auth_zeus_bp.route("/login")
|
@oauth_bp.route("/login/zeus/authorized")
|
||||||
def login():
|
|
||||||
"""Function to handle a user trying to log in"""
|
|
||||||
return zeus_login()
|
|
||||||
|
|
||||||
|
|
||||||
@auth_zeus_bp.route("/authorized")
|
|
||||||
def authorized() -> typing.Any:
|
def authorized() -> typing.Any:
|
||||||
# type is 'typing.Union[str, Response]', but this errors due to
|
# type is 'typing.Union[str, Response]', but this errors due to
|
||||||
# https://github.com/python/mypy/issues/7187
|
# https://github.com/python/mypy/issues/7187
|
||||||
"""Check authorized status"""
|
"Check authorized status"
|
||||||
resp = current_app.zeus.authorized_response()
|
resp = current_app.zeus.authorized_response()
|
||||||
if resp is None:
|
if resp is None:
|
||||||
# pylint: disable=C0301
|
# pylint: disable=C0301
|
||||||
|
@ -51,8 +45,8 @@ def authorized() -> typing.Any:
|
||||||
return redirect(url_for("general_bp.home"))
|
return redirect(url_for("general_bp.home"))
|
||||||
|
|
||||||
|
|
||||||
def init_oauth(app) -> OAuthRemoteApp:
|
def init_oauth(app):
|
||||||
"""Initialize the OAuth for ZeusWPI"""
|
"Initialize the OAuth for ZeusWPI"
|
||||||
oauth = OAuth(app)
|
oauth = OAuth(app)
|
||||||
|
|
||||||
zeus = oauth.remote_app(
|
zeus = oauth.remote_app(
|
||||||
|
@ -75,13 +69,13 @@ def init_oauth(app) -> OAuthRemoteApp:
|
||||||
|
|
||||||
|
|
||||||
def login_and_redirect_user(user) -> Response:
|
def login_and_redirect_user(user) -> Response:
|
||||||
"""Log in the user and then redirect them"""
|
"Log in the user and then redirect them"
|
||||||
login_user(user)
|
login_user(user)
|
||||||
return redirect(url_for("general_bp.home"))
|
return redirect(url_for("general_bp.home"))
|
||||||
|
|
||||||
|
|
||||||
def create_user(username) -> User:
|
def create_user(username) -> User:
|
||||||
"""Create a temporary user if it is needed"""
|
"Create a temporary user if it is needed"
|
||||||
user = User()
|
user = User()
|
||||||
user.configure(username, False, 1, associations=["zeus"])
|
user.configure(username, False, 1, associations=["zeus"])
|
||||||
db.session.add(user)
|
db.session.add(user)
|
|
@ -1,17 +0,0 @@
|
||||||
version: "3.4"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
target: "development"
|
|
||||||
environment:
|
|
||||||
- MARIADB_DATABASE=haldis
|
|
||||||
- MARIADB_USER=haldis
|
|
||||||
- MARIADB_PASSWORD=haldis
|
|
||||||
volumes: ["$PWD:/src"]
|
|
||||||
database:
|
|
||||||
environment:
|
|
||||||
- MARIADB_DATABASE=haldis
|
|
||||||
- MARIADB_ROOT_PASSWORD=mariadb
|
|
||||||
- MARIADB_USER=haldis
|
|
||||||
- MARIADB_PASSWORD=haldis
|
|
|
@ -1,31 +0,0 @@
|
||||||
version: "3.4"
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
target: production
|
|
||||||
restart: on-failure
|
|
||||||
depends_on: [database]
|
|
||||||
ports: ["8000:8000"]
|
|
||||||
environment:
|
|
||||||
- MARIADB_HOST=database
|
|
||||||
- MARIADB_DATABASE
|
|
||||||
- MARIADB_USER
|
|
||||||
- MARIADB_PASSWORD
|
|
||||||
networks: [haldis]
|
|
||||||
database:
|
|
||||||
image: mariadb:10.8
|
|
||||||
hostname: database
|
|
||||||
restart: on-failure
|
|
||||||
environment:
|
|
||||||
- MARIADB_DATABASE
|
|
||||||
- MARIADB_ROOT_PASSWORD
|
|
||||||
- MARIADB_USER
|
|
||||||
- MARIADB_PASSWORD
|
|
||||||
networks: [haldis]
|
|
||||||
volumes: [haldis_data:/var/lib/mysql]
|
|
||||||
networks:
|
|
||||||
haldis:
|
|
||||||
volumes:
|
|
||||||
haldis_data:
|
|
|
@ -12,7 +12,7 @@ E="${normal}"
|
||||||
if [ ! -d "venv" ]; then
|
if [ ! -d "venv" ]; then
|
||||||
PYTHON_VERSION=$(cat .python-version)
|
PYTHON_VERSION=$(cat .python-version)
|
||||||
echo -e "${B} No venv found, creating a new one with version ${PYTHON_VERSION} ${E}"
|
echo -e "${B} No venv found, creating a new one with version ${PYTHON_VERSION} ${E}"
|
||||||
python3 -m virtualenv -p "$PYTHON_VERSION" venv
|
python3 -m virtualenv -p $PYTHON_VERSION venv
|
||||||
fi
|
fi
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
|
|
||||||
|
|
|
@ -12,5 +12,4 @@ black
|
||||||
pymysql
|
pymysql
|
||||||
pyyaml
|
pyyaml
|
||||||
tatsu<5.6 # >=5.6 needs Python >=3.8
|
tatsu<5.6 # >=5.6 needs Python >=3.8
|
||||||
microsoftgraph-python
|
|
||||||
sentry-sdk[flask]
|
sentry-sdk[flask]
|
||||||
|
|
|
@ -79,8 +79,6 @@ markupsafe==2.0.1
|
||||||
# jinja2
|
# jinja2
|
||||||
# mako
|
# mako
|
||||||
# wtforms
|
# wtforms
|
||||||
microsoftgraph-python==1.1.3
|
|
||||||
# via -r requirements.in
|
|
||||||
mypy-extensions==0.4.3
|
mypy-extensions==0.4.3
|
||||||
# via black
|
# via black
|
||||||
oauthlib==2.1.0
|
oauthlib==2.1.0
|
||||||
|
@ -100,9 +98,7 @@ pyyaml==5.4.1
|
||||||
regex==2021.4.4
|
regex==2021.4.4
|
||||||
# via black
|
# via black
|
||||||
requests==2.25.1
|
requests==2.25.1
|
||||||
# via
|
# via requests-oauthlib
|
||||||
# microsoftgraph-python
|
|
||||||
# requests-oauthlib
|
|
||||||
requests-oauthlib==1.1.0
|
requests-oauthlib==1.1.0
|
||||||
# via flask-oauthlib
|
# via flask-oauthlib
|
||||||
sentry-sdk[flask]==1.10.1
|
sentry-sdk[flask]==1.10.1
|
||||||
|
|
Loading…
Reference in a new issue