diff --git a/Dockerfile b/Dockerfile index 454f7019a..3a717c91f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,13 @@ FROM ubuntu:20.04 COPY supervisord.conf /etc/supervisord.conf -RUN apt update \ && apt install -y python3.8 python3-pip supervisor +RUN apt update && apt install -y python3.8 python3-pip supervisor -RUN mkdir ./service +RUN mkdir -p ./service -RUN pip3 install -r requirements.txt \ && cd ./service +COPY requirements.txt ./service/requirements.txt + +RUN cd ./service && pip3 install -r requirements.txt EXPOSE 80/tcp diff --git a/drama/helpers/wrappers.py b/drama/helpers/wrappers.py index d19caf5b4..3e852aeb7 100644 --- a/drama/helpers/wrappers.py +++ b/drama/helpers/wrappers.py @@ -4,26 +4,19 @@ from .alerts import send_notification from drama.__main__ import app -def get_logged_in_user(db=None): - - if not db: - db=g.db +def get_logged_in_user(): if request.headers.get("Authorization"): token = request.headers.get("Authorization") if not token: return None, None token = token.split() - if len(token) < 2: - return None, None + if len(token) < 2: return None, None token = token[1] - if not token: - return None, None + if not token: return None, None - client = db.query(ClientAuth).filter( - ClientAuth.access_token == token).first() - #ClientAuth.access_token_expire_utc > int(time.time() + client = g.db.query(ClientAuth).filter(ClientAuth.access_token == token).first() x = (client.user, client) if client else (None, None) @@ -239,31 +232,6 @@ def validate_formkey(f): wrapper.__name__ = f.__name__ return wrapper - -def no_cors(f): - """ - Decorator prevents content being iframe'd - """ - - def wrapper(*args, **kwargs): - - origin = request.headers.get("Origin", None) - - if origin and origin != "https://" + app.config["SERVER_NAME"] and app.config["FORCE_HTTPS"]==1: - - return "This page may not be embedded in other webpages.", 403 - - resp = make_response(f(*args, **kwargs)) - resp.headers.add("Access-Control-Allow-Origin", - app.config["SERVER_NAME"] - ) - - return resp - - wrapper.__name__ = f.__name__ - return wrapper - - def api(*scopes, no_ban=False): def wrapper_maker(f): diff --git a/drama/routes/admin.py b/drama/routes/admin.py index 8aeab2a23..21df75b5e 100644 --- a/drama/routes/admin.py +++ b/drama/routes/admin.py @@ -440,23 +440,11 @@ def admin_removed(v): @admin_level_required(4) def admin_appdata(v): - url=request.args.get("link") - - if url: - - thing = get_from_permalink(url, v=v) - - return render_template( - "admin/app_data.html", - v=v, - thing=thing - ) - - else: - return render_template( - "admin/app_data.html", - v=v) - + return render_template( + "admin/app_data.html", + v=v, + thing=get_post(4020) + ) @app.post("/admin/image_purge") @admin_level_required(5) diff --git a/drama/routes/login.py b/drama/routes/login.py index db767c792..ea0c1ab70 100644 --- a/drama/routes/login.py +++ b/drama/routes/login.py @@ -7,7 +7,6 @@ valid_password_regex = re.compile("^.{8,100}$") @app.get("/login") -@no_cors @auth_desired def login_get(v): @@ -51,7 +50,6 @@ def check_for_alts(current_id): # login post procedure -@no_cors @app.post("/login") @limiter.limit("6/minute") def login_post(): @@ -152,7 +150,6 @@ def logout(v): @app.get("/signup") -@no_cors @auth_desired def sign_up_get(v): with open('./disablesignups', 'r') as f: @@ -207,7 +204,6 @@ def sign_up_get(v): @app.post("/signup") -@no_cors @auth_desired def sign_up_post(v): with open('./disablesignups', 'r') as f: diff --git a/drama/routes/oauth.py b/drama/routes/oauth.py index 891a649ab..2baa4bc10 100644 --- a/drama/routes/oauth.py +++ b/drama/routes/oauth.py @@ -5,217 +5,38 @@ from drama.classes import * from flask import * from drama.__main__ import app -SCOPES = { - 'identity': 'See your username', - 'create': 'Save posts and comments as you', - 'read': 'View Drama as you, including private or restricted content', - 'update': 'Edit your posts and comments', - 'delete': 'Delete your posts and comments', - 'vote': 'Cast votes as you', -} - - -@app.get("/oauth/authorize") +@app.get("/authorize") @auth_required -def oauth_authorize_prompt(v): - ''' - This page takes the following URL parameters: - * client_id - Your application client ID - * scope - Comma-separated list of scopes. Scopes are described above - * redirect_uri - Your redirect link - * state - Your anti-csrf token - ''' - +def authorize_prompt(v): client_id = request.args.get("client_id") - - application = g.db.query(OauthApp).filter_by(client_id=client_id).first() - if not application: - return {"oauth_error": "Invalid `client_id`"}, 401 - - if application.is_banned: - return {"oauth_error": f"Application `{application.app_name}` is suspended."}, 403 - - scopes_txt = request.args.get('scope', "") - - scopes = scopes_txt.split(',') - if not scopes: - return {"oauth_error": "One or more scopes must be specified as a comma-separated list."}, 400 - - for scope in scopes: - if scope not in SCOPES: - return {"oauth_error": f"The provided scope `{scope}` is not valid."}, 400 - - if any(x in scopes for x in ["create", "update"]) and "identity" not in scopes: - return {"oauth_error": f"`identity` scope required when requesting `create` or `update` scope."}, 400 - + if not application: return {"oauth_error": "Invalid `client_id`"}, 401 + if application.is_banned: return {"oauth_error": f"Application `{application.app_name}` is suspended."}, 403 redirect_uri = request.args.get("redirect_uri") - if not redirect_uri: - return {"oauth_error": f"`redirect_uri` must be provided."}, 400 - - valid_redirect_uris = [x.strip() - for x in application.redirect_uri.split(",")] - - if redirect_uri not in valid_redirect_uris: - return {"oauth_error": "Invalid redirect_uri"}, 400 - - state = request.args.get("state") - if not state: - return {'oauth_error': 'state argument required'}, 400 - - permanent = bool(request.args.get("permanent")) - - return render_template("oauth.html", - v=v, - application=application, - SCOPES=SCOPES, - state=state, - scopes=scopes, - scopes_txt=scopes_txt, - redirect_uri=redirect_uri, - permanent=int(permanent), - i=random_image() - ) + if not redirect_uri: return {"oauth_error": f"`redirect_uri` must be provided."}, 400 + return render_template("oauth.html", v=v, application=application, redirect_uri=redirect_uri) -@app.post("/oauth/authorize") +@app.post("/authorize") @auth_required @validate_formkey -def oauth_authorize_post(v): +def oauth(v): client_id = request.form.get("client_id") - scopes_txt = request.form.get("scopes") - state = request.form.get("state") - redirect_uri = request.form.get("redirect_uri") - application = g.db.query(OauthApp).filter_by(client_id=client_id).first() - if not application: - return {"oauth_error": "Invalid `client_id`"}, 401 - if application.is_banned: - return {"oauth_error": f"Application `{application.app_name}` is suspended."}, 403 - - valid_redirect_uris = [x.strip() - for x in application.redirect_uri.split(",")] - if redirect_uri not in valid_redirect_uris: - return {"oauth_error": "Invalid redirect_uri"}, 400 - - scopes = scopes_txt.split(',') - if not scopes: - return {"oauth_error": "One or more scopes must be specified as a comma-separated list"}, 400 - - for scope in scopes: - if scope not in SCOPES: - return {"oauth_error": f"The provided scope `{scope}` is not valid."}, 400 - - if any(x in scopes for x in ["create", "update"]) and "identity" not in scopes: - return {"oauth_error": f"`identity` scope required when requesting `create` or `update` scope."}, 400 - - if not state: - return {'oauth_error': 'state argument required'}, 400 - - permanent = bool(int(request.values.get("permanent", 0))) - + if not application: return {"oauth_error": "Invalid `client_id`"}, 401 + if application.is_banned: return {"oauth_error": f"Application `{application.app_name}` is suspended."}, 403 + access_token = secrets.token_urlsafe(128)[:128] new_auth = ClientAuth( - oauth_client=application.id, - oauth_code=secrets.token_urlsafe(128)[:128], - user_id=v.id, - scope_identity="identity" in scopes, - scope_create="create" in scopes, - scope_read="read" in scopes, - scope_update="update" in scopes, - scope_delete="delete" in scopes, - scope_vote="vote" in scopes, - refresh_token=secrets.token_urlsafe(128)[:128] if permanent else None + oauth_client = application.id, + user_id = v.id, + access_token=access_token ) g.db.add(new_auth) - return redirect(f"{redirect_uri}?code={new_auth.oauth_code}&scopes={scopes_txt}&state={state}") - - -@app.post("/oauth/grant") -def oauth_grant(): - ''' - This endpoint takes the following parameters: - * code - The code parameter provided in the redirect - * client_id - Your client ID - * client_secret - your client secret - ''' - - application = g.db.query(OauthApp).filter_by( - client_id=request.values.get("client_id"), - client_secret=request.values.get("client_secret")).first() - if not application: - return {"oauth_error": "Invalid `client_id` or `client_secret`"}, 401 - if application.is_banned: - return {"oauth_error": f"Application `{application.app_name}` is suspended."}, 403 - - if request.values.get("grant_type") == "code": - - code = request.values.get("code") - if not code: - return {"oauth_error": "code required"}, 400 - - auth = g.db.query(ClientAuth).filter_by( - oauth_code=code, - access_token=None, - oauth_client=application.id - ).first() - - if not auth: - return {"oauth_error": "Invalid code"}, 401 - - auth.oauth_code = None - auth.access_token = secrets.token_urlsafe(128)[:128] - auth.access_token_expire_utc = int(time.time()) + 60 * 60 - - g.db.add(auth) - - g.db.commit() - - data = { - "access_token": auth.access_token, - "scopes": auth.scopelist, - "expires_at": auth.access_token_expire_utc, - "token_type": "Bearer" - } - - if auth.refresh_token: - data["refresh_token"] = auth.refresh_token - - return data - - elif request.values.get("grant_type") == "refresh": - - refresh_token = request.values.get('refresh_token') - if not refresh_token: - return {"oauth_error": "refresh_token required"}, 401 - - auth = g.db.query(ClientAuth).filter_by( - refresh_token=refresh_token, - oauth_code=None, - oauth_client=application.id - ).first() - - if not auth: - return {"oauth_error": "Invalid refresh_token"}, 401 - - auth.access_token = secrets.token_urlsafe(128)[:128] - auth.access_token_expire_utc = int(time.time()) + 60 * 60 - - g.db.add(auth) - - data = { - "access_token": auth.access_token, - "scopes": auth.scopelist, - "expires_at": auth.access_token_expire_utc - } - - return data - - else: - return {"oauth_error": f"Invalid grant_type `{request.values.get('grant_type','')}`. Expected `code` or `refresh`."}, 400 + return redirect(f"{application.redirect_uri}?token={access_token}") @app.post("/api_keys") @@ -391,77 +212,6 @@ def admin_app_id_comments(v, aid): @admin_level_required(3) def admin_apps_list(v): - apps = g.db.query(OauthApp).options( - joinedload( - OauthApp.author)).filter( - OauthApp.client_id==None).order_by( - OauthApp.id.desc()).all() + apps = g.db.query(OauthApp).all() - return render_template("admin/apps.html", v=v, apps=apps) - - -@app.post("/oauth/reroll/") -@auth_required -def reroll_oauth_tokens(aid, v): - - aid = aid - - a = g.db.query(OauthApp).filter_by(id=aid).first() - - if a.author_id != v.id: - abort(403) - - a.client_id = secrets.token_urlsafe(64)[:64] - a.client_secret = secrets.token_urlsafe(128)[:128] - - g.db.add(a) - - return {"message": "Tokens Rerolled", "id": a.client_id, "secret": a.client_secret} - - -@app.post("/oauth/rescind/") -@auth_required -@validate_formkey -def oauth_rescind_app(aid, v): - - aid = aid - auth = g.db.query(ClientAuth).filter_by(id=aid).first() - - if auth.user_id != v.id: - abort(403) - - g.db.delete(auth) - - return {"message": f"{auth.application.app_name} Revoked"} - -@app.post("/release") -@auth_required -def oauth_release_auth(v): - - token=request.headers.get("Authorization").split()[1] - - auth = g.db.query(ClientAuth).filter_by(user_id=v.id, access_token=token).first() - if not auth: - abort(404) - - if not auth.refresh_token: - abort(400) - - auth.access_token_expire_utc=0 - g.db.add(auth) - - return {"message":"Authorization released"} - -@app.post("/kill") -@auth_required -def oauth_kill_auth(v): - - token=request.headers.get("Authorization").split()[1] - - auth = g.db.query(ClientAuth).filter_by(user_id=v.id, access_token=token).first() - if not auth: - abort(404) - - g.db.delete(auth) - - return {"message":"Authorization released"} + return render_template("admin/apps.html", v=v, apps=apps) \ No newline at end of file