163 lines
5.1 KiB
Python
163 lines
5.1 KiB
Python
|
import os
|
||
|
import secrets
|
||
|
from datetime import timedelta
|
||
|
|
||
|
from flask import Flask, abort
|
||
|
from flask_login import LoginManager
|
||
|
from flask_sqlalchemy import SQLAlchemy
|
||
|
from flask_sslify import SSLify
|
||
|
from flask_talisman import Talisman
|
||
|
from flask_talisman.talisman import ONE_YEAR_IN_SECS
|
||
|
from flask_wtf.csrf import CSRFProtect
|
||
|
|
||
|
domain = None # "127.0.0.1:8000"
|
||
|
# domain = f"dungeonsandexploits.team{team_number}.m0lecon.fans"
|
||
|
|
||
|
app = Flask(__name__)
|
||
|
|
||
|
# init sslify
|
||
|
sslify = SSLify(app)
|
||
|
|
||
|
# init talisman
|
||
|
talisman = Talisman(
|
||
|
app,
|
||
|
content_security_policy_nonce_in=["script-src", "style-src"],
|
||
|
# content_security_policy_nonce_in=["script-src", "style-src"],
|
||
|
content_security_policy_report_only=False,
|
||
|
content_security_policy_report_uri=None,
|
||
|
content_security_policy={ # do not add stuff here, put tags in html template tags: nonce="{{ csp_nonce() }}"
|
||
|
"default-src": "'none'",
|
||
|
# "script-src": "'self'",
|
||
|
"script-src": "'self' 'wasm-unsafe-eval'", # MAYBE THIS SHOULD NOT BE LIKE THIS BUT UNITY NEEDS IT
|
||
|
"style-src": "'self'",
|
||
|
"img-src": "'self'",
|
||
|
"connect-src": "'self'",
|
||
|
"base-uri": "'none'",
|
||
|
"form-action": "'none'",
|
||
|
"frame-ancestors": "'none'",
|
||
|
"strict-dynamic": "",
|
||
|
},
|
||
|
feature_policy={},
|
||
|
force_file_save=True,
|
||
|
force_https_permanent=True,
|
||
|
force_https=True,
|
||
|
frame_options_allow_from=None,
|
||
|
frame_options="DENY",
|
||
|
referrer_policy="strict-origin-when-cross-origin",
|
||
|
session_cookie_http_only=True,
|
||
|
session_cookie_secure=True,
|
||
|
strict_transport_security_include_subdomains=True,
|
||
|
strict_transport_security_max_age=ONE_YEAR_IN_SECS,
|
||
|
strict_transport_security_preload=False, # SOULD BE TRUE, enables HSTS preloading if you register your application with Google's HSTS preload list, Firefox and Chrome will never load your site over a non-secure connection.
|
||
|
strict_transport_security=True,
|
||
|
)
|
||
|
|
||
|
# init session
|
||
|
app.config.update(
|
||
|
APPLICATION_ROOT="/",
|
||
|
JSON_AS_ASCII=True,
|
||
|
JSON_SORT_KEYS=False,
|
||
|
JSONIFY_MIMETYPE="application/json",
|
||
|
JSONIFY_PRETTYPRINT_REGULAR=False,
|
||
|
MAX_CONTENT_LENGTH=16 * 1000 * 1000,
|
||
|
MAX_COOKIE_SIZE=4093,
|
||
|
PERMANENT_SESSION_LIFETIME=timedelta(minutes=30),
|
||
|
PREFERRED_URL_SCHEME="https",
|
||
|
SECRET_KEY=secrets.token_hex(256),
|
||
|
SEND_FILE_MAX_AGE_DEFAULT=timedelta(hours=12),
|
||
|
SERVER_NAME=domain,
|
||
|
SESSION_COOKIE_DOMAIN=domain,
|
||
|
SESSION_COOKIE_HTTPONLY=True,
|
||
|
SESSION_COOKIE_NAME="session",
|
||
|
SESSION_COOKIE_PATH="/",
|
||
|
SESSION_COOKIE_SAMESITE="Strict",
|
||
|
SESSION_COOKIE_SECURE=True,
|
||
|
SESSION_REFRESH_EACH_REQUEST=True,
|
||
|
USE_X_SENDFILE=False, # disabled: does not work with gunicorn
|
||
|
)
|
||
|
|
||
|
# init flask-wtf
|
||
|
app.config.update(
|
||
|
# RECAPTCHA_API_SERVER=None,
|
||
|
# RECAPTCHA_DATA_ATTRS=None,
|
||
|
# RECAPTCHA_DIV_CLASS="g-recaptcha",
|
||
|
# RECAPTCHA_HTML=None,
|
||
|
# RECAPTCHA_PARAMETERS=None,
|
||
|
# RECAPTCHA_PRIVATE_KEY=None,
|
||
|
# RECAPTCHA_PUBLIC_KEY=None,
|
||
|
# RECAPTCHA_SCRIPT="https://www.google.com/recaptcha/api.js",
|
||
|
# RECAPTCHA_VERIFY_SERVER="https://www.google.com/recaptcha/api/siteverify",
|
||
|
WTF_CSRF_CHECK_DEFAULT=True,
|
||
|
WTF_CSRF_ENABLED=True,
|
||
|
WTF_CSRF_FIELD_NAME="csrf_token",
|
||
|
WTF_CSRF_HEADERS=["X-CSRFToken", "X-CSRF-Token"],
|
||
|
WTF_CSRF_METHODS={"POST", "PUT", "PATCH", "DELETE"},
|
||
|
WTF_CSRF_SECRET_KEY=secrets.token_hex(256),
|
||
|
WTF_CSRF_SSL_STRICT=True,
|
||
|
WTF_CSRF_TIME_LIMIT=1200,
|
||
|
WTF_I18N_ENABLED=True,
|
||
|
)
|
||
|
csrfprotect = CSRFProtect(app)
|
||
|
|
||
|
# init flask-login
|
||
|
app.config.update(
|
||
|
AUTH_HEADER_NAME="Authorization",
|
||
|
COOKIE_DURATION=timedelta(minutes=30),
|
||
|
COOKIE_HTTPONLY=True,
|
||
|
COOKIE_NAME="remember_token",
|
||
|
COOKIE_SAMESITE=True,
|
||
|
COOKIE_SECURE=True,
|
||
|
EXEMPT_METHODS=set(),
|
||
|
LOGIN_MESSAGE_CATEGORY="message",
|
||
|
LOGIN_MESSAGE="Please log in to access this page.",
|
||
|
REFRESH_MESSAGE_CATEGORY="message",
|
||
|
REFRESH_MESSAGE="Please reauthenticate to access this page.",
|
||
|
REMEMBER_COOKIE_DOMAIN=domain,
|
||
|
REMEMBER_COOKIE_DURATION=timedelta(minutes=30),
|
||
|
REMEMBER_COOKIE_HTTPONLY=True,
|
||
|
REMEMBER_COOKIE_NAME="remember_token",
|
||
|
REMEMBER_COOKIE_PATH="/",
|
||
|
REMEMBER_COOKIE_REFRESH_EACH_REQUEST=True,
|
||
|
REMEMBER_COOKIE_SECURE=True,
|
||
|
# SESSION_KEYS=set(["_user_id", "_remember", "_remember_seconds", "_id", "_fresh", "next", ]),
|
||
|
SESSION_PROTECTION="strong",
|
||
|
USE_SESSION_FOR_NEXT=True,
|
||
|
)
|
||
|
login_manager = LoginManager(app)
|
||
|
|
||
|
# init flask-sqlalchemy
|
||
|
app.config.update(
|
||
|
# SQLALCHEMY_DATABASE_URI="sqlite:///:memory:?cache=shared",
|
||
|
SQLALCHEMY_DATABASE_URI="sqlite:////tmp/db.db",
|
||
|
# SQLALCHEMY_BINDS={},
|
||
|
# SQLALCHEMY_ECHO=True,
|
||
|
# SQLALCHEMY_RECORD_QUERIES=True,
|
||
|
SQLALCHEMY_TRACK_MODIFICATIONS=False,
|
||
|
# SQLALCHEMY_ENGINE_OPTIONS={},
|
||
|
)
|
||
|
db = SQLAlchemy(app)
|
||
|
|
||
|
|
||
|
def register_blueprints(app):
|
||
|
from auth import auth
|
||
|
from game import game
|
||
|
from website import website
|
||
|
app.register_blueprint(auth)
|
||
|
app.register_blueprint(website)
|
||
|
app.register_blueprint(game)
|
||
|
|
||
|
|
||
|
register_blueprints(app)
|
||
|
|
||
|
with app.app_context():
|
||
|
# create all missing db tables
|
||
|
db.create_all()
|
||
|
|
||
|
|
||
|
@app.route("/teapot")
|
||
|
async def teapot():
|
||
|
abort(418)
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
app.run()
|