From 4d9b37545d9d62e06cd08cde7280a126bf6bc4ca Mon Sep 17 00:00:00 2001 From: lza_menace Date: Thu, 30 Jul 2020 01:36:31 -0700 Subject: [PATCH] further refactoring, moving out auth into blueprints --- suchwow/app.py | 90 ++------------------------------ suchwow/config.example.py | 2 +- suchwow/routes/auth.py | 69 ++++++++++++++++++++++++ suchwow/routes/post.py | 10 +++- suchwow/templates/base.html | 4 +- suchwow/templates/post/read.html | 2 +- suchwow/utils/decorators.py | 8 +-- suchwow/utils/helpers.py | 12 +---- 8 files changed, 88 insertions(+), 109 deletions(-) diff --git a/suchwow/app.py b/suchwow/app.py index 8c4b7c8..9a48605 100644 --- a/suchwow/app.py +++ b/suchwow/app.py @@ -1,9 +1,7 @@ -import uuid import json -import requests -from flask import Flask, request, redirect, url_for -from flask import render_template, flash, session -from flask import send_from_directory, make_response +from flask import Flask, request, session +from flask import render_template, flash +from flask import make_response from flask import jsonify from flask_session import Session from suchwow.models import Post, Profile, Comment, Notification, db @@ -16,13 +14,8 @@ app.config.from_envvar("FLASK_SECRETS") app.secret_key = app.config["SECRET_KEY"] Session(app) -OPENID_URL = app.config["OIDC_URL"] -OPENID_CLIENT_ID = app.config["OIDC_CLIENT_ID"] -OPENID_CLIENT_SECRET = app.config["OIDC_CLIENT_SECRET"] -OPENID_REDIRECT_URI = "http://localhost:5000/auth" - app.register_blueprint(post.bp) - +app.register_blueprint(auth.bp) @app.route("/") def index(): @@ -34,76 +27,6 @@ def index(): posts = Post.select().order_by(Post.timestamp).paginate(int(page), 10) return render_template("index.html", posts=posts, page=page) -@app.route("/uploads/") -def uploaded_file(filename): - return send_from_directory(app.config["UPLOAD_FOLDER"], filename) - -@app.route("/login") -def login(): - state = uuid.uuid4().hex - session["auth_state"] = state - url = f"{OPENID_URL}/auth?" \ - f"client_id={OPENID_CLIENT_ID}&" \ - f"redirect_uri={OPENID_REDIRECT_URI}&" \ - f"response_type=code&" \ - f"state={state}" - - if app.debug: - debug_login() - return redirect(url_for("index")) - else: - return redirect(url) - - -@app.route("/auth/") -def auth(): - # todo - assert "state" in request.args - assert "session_state" in request.args - assert "code" in request.args - - # verify state - if not session.get("auth_state"): - return "session error", 500 - if request.args["state"] != session["auth_state"]: - return "attack detected :)", 500 - - # with this authorization code we can fetch an access token - url = f"{OPENID_URL}/token" - data = { - "grant_type": "authorization_code", - "code": request.args["code"], - "redirect_uri": OPENID_REDIRECT_URI, - "client_id": OPENID_CLIENT_ID, - "client_secret": OPENID_CLIENT_SECRET, - "state": request.args["state"] - } - try: - resp = requests.post(url, data=data) - resp.raise_for_status() - except: - return resp.content, 500 - - data = resp.json() - assert "access_token" in data - assert data.get("token_type") == "bearer" - access_token = data["access_token"] - - # fetch user information with the access token - url = f"{OPENID_URL}/userinfo" - - try: - resp = requests.post(url, headers={"Authorization": f"Bearer {access_token}"}) - resp.raise_for_status() - user_profile = resp.json() - except: - return resp.content, 500 - - # user can now visit /secret - session["auth"] = user_profile - return redirect(url_for("index")) - - @app.route("/debug") @login_required def debug(): @@ -113,11 +36,6 @@ def debug(): Logout """ -@app.route("/logout") -def logout(): - session["auth"] = None - return redirect(url_for("index")) - @app.errorhandler(404) def not_found(error): return make_response(jsonify({"error": "Page not found"}), 404) diff --git a/suchwow/config.example.py b/suchwow/config.example.py index fda9a78..7829d6f 100644 --- a/suchwow/config.example.py +++ b/suchwow/config.example.py @@ -2,7 +2,7 @@ OIDC_URL = 'https://login.wownero.com/auth/realms/master/protocol/openid-connect OIDC_CLIENT_ID = 'suchwowxxx', OIDC_CLIENT_SECRET = 'xxxxxxxxxx', OIDC_REDIRECT_URL = 'http://localhost:5000/auth' -SECRET = 'yyyyyyyyyyyyy', +SECRET_KEY = 'yyyyyyyyyyyyy', SESSION_TYPE = 'filesystem' UPLOAD_FOLDER = '/path/to/the/uploads' ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'} diff --git a/suchwow/routes/auth.py b/suchwow/routes/auth.py index e69de29..adaed4f 100644 --- a/suchwow/routes/auth.py +++ b/suchwow/routes/auth.py @@ -0,0 +1,69 @@ +import requests +from uuid import uuid4 +from flask import session, redirect, url_for, request, Blueprint +from suchwow import config + + +bp = Blueprint("auth", "auth") + +@bp.route("/login") +def login(): + state = uuid4().hex + session["auth_state"] = state + url = f"{config.OIDC_URL}/auth?" \ + f"client_id={config.OIDC_CLIENT_ID}&" \ + f"redirect_uri={config.OIDC_REDIRECT_URL}&" \ + f"response_type=code&" \ + f"state={state}" + + return redirect(url) + +@bp.route("/logout") +def logout(): + session["auth"] = None + return redirect(url_for("index")) + +@bp.route("/auth/") +def auth(): + # todo - clean up assertions + assert "state" in request.args + assert "session_state" in request.args + assert "code" in request.args + + # verify state + if not session.get("auth_state"): + return "session error", 500 + if request.args["state"] != session["auth_state"]: + return "attack detected :)", 500 + + # with this authorization code we can fetch an access token + url = f"{config.OIDC_URL}/token" + data = { + "grant_type": "authorization_code", + "code": request.args["code"], + "redirect_uri": config.OIDC_REDIRECT_URL, + "client_id": config.OIDC_CLIENT_ID, + "client_secret": config.OIDC_CLIENT_SECRET, + "state": request.args["state"] + } + resp = requests.post(url, data=data) + resp.raise_for_status() + + data = resp.json() + assert "access_token" in data + assert data.get("token_type") == "bearer" + access_token = data["access_token"] + + # fetch user information with the access token + url = f"{config.OIDC_URL}/userinfo" + + try: + resp = requests.post(url, headers={"Authorization": f"Bearer {access_token}"}) + resp.raise_for_status() + user_profile = resp.json() + except: + return resp.content, 500 + + # user can now visit /secret + session["auth"] = user_profile + return redirect(url_for("index")) \ No newline at end of file diff --git a/suchwow/routes/post.py b/suchwow/routes/post.py index 88b77a3..598ab31 100644 --- a/suchwow/routes/post.py +++ b/suchwow/routes/post.py @@ -1,8 +1,10 @@ from os import path from flask import render_template, Blueprint, request, session +from flask import send_from_directory, redirect, url_for, current_app from werkzeug.utils import secure_filename from suchwow.models import Post from suchwow.utils.decorators import login_required +from suchwow.utils.helpers import allowed_file bp = Blueprint("post", "post") @@ -35,7 +37,7 @@ def create(): return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) - save_path = path.join(app.config["UPLOAD_FOLDER"], filename) + save_path = path.join(current_app.config["UPLOAD_FOLDER"], filename) file.save(save_path) # gen wallet post = Post( @@ -48,4 +50,8 @@ def create(): ) post.save() return redirect(url_for("post.read", id=post.id)) - return render_template("post/create.html") \ No newline at end of file + return render_template("post/create.html") + +@bp.route("/uploads/") +def uploaded_file(filename): + return send_from_directory(current_app.config["UPLOAD_FOLDER"], filename) \ No newline at end of file diff --git a/suchwow/templates/base.html b/suchwow/templates/base.html index d9dadf4..2e42901 100644 --- a/suchwow/templates/base.html +++ b/suchwow/templates/base.html @@ -11,9 +11,9 @@ Home
{% if session.auth == None %} - Login
+ Login
{% else %} - Logout
+ Logout
{% endif %}
{% with messages = get_flashed_messages() %} diff --git a/suchwow/templates/post/read.html b/suchwow/templates/post/read.html index 4f35c47..28ee434 100644 --- a/suchwow/templates/post/read.html +++ b/suchwow/templates/post/read.html @@ -14,6 +14,6 @@ You cannot see this post

Subtitle: {{ post.subtitle }}

Submitted: {{ post.timestamp }}

Image Name: {{ post.image_name }}

- + {% endif %} {% endblock %} diff --git a/suchwow/utils/decorators.py b/suchwow/utils/decorators.py index 615ce8d..390f52e 100644 --- a/suchwow/utils/decorators.py +++ b/suchwow/utils/decorators.py @@ -1,15 +1,11 @@ -from flask import current_app, session, redirect, url_for +from flask import session, redirect, url_for from functools import wraps -from suchwow.utils.helpers import debug_login def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): - if current_app.debug: - debug_login() - return f(*args, **kwargs) if "auth" not in session or not session["auth"]: - return redirect(url_for("login")) + return redirect(url_for("auth.login")) return f(*args, **kwargs) return decorated_function \ No newline at end of file diff --git a/suchwow/utils/helpers.py b/suchwow/utils/helpers.py index f8f6c7b..74a4aea 100644 --- a/suchwow/utils/helpers.py +++ b/suchwow/utils/helpers.py @@ -1,16 +1,6 @@ -from flask import current_app, session +from flask import current_app def allowed_file(filename): return "." in filename and \ filename.rsplit(".", 1)[1].lower() in current_app.config["ALLOWED_EXTENSIONS"] - -def debug_login(): - session["auth"] = { - "state": "active", - "session_state": "debug", - "code": "abcdefg", - "preferred_username": "debuguser", - "debug": True - } - return \ No newline at end of file