From 88af29a5bb971e7c1fd3f3bc71d9024453ab7240 Mon Sep 17 00:00:00 2001 From: Mark <mark@openappstack.net> Date: Mon, 29 Jun 2020 17:27:38 +0200 Subject: [PATCH] Change from flask-oauthlib to Flask-dance --- test/integration_tests/app.py | 95 ++++++++++--------- test/integration_tests/requirements.txt | 4 +- .../test/behave/features/login.feature | 2 +- .../reject_unauthorized_logins.feature | 4 +- .../test/behave/features/remember_me.feature | 4 +- 5 files changed, 56 insertions(+), 53 deletions(-) diff --git a/test/integration_tests/app.py b/test/integration_tests/app.py index e58a728..71019c0 100644 --- a/test/integration_tests/app.py +++ b/test/integration_tests/app.py @@ -1,9 +1,7 @@ -from flask import Flask, abort, url_for, redirect, request, session, jsonify -from werkzeug import security -from json import dumps +from flask import Flask, url_for, redirect, jsonify +import json from os import environ -from flask_oauthlib.client import OAuth -import uuid +from flask_dance.consumer import OAuth2ConsumerBlueprint, oauth_authorized, oauth_error BASE_URL=environ["BASE_URL"] @@ -17,54 +15,59 @@ SECRET=environ["SECRET"] app = Flask(__name__) app.secret_key = 'development' -oauth = OAuth(app) - -sso = oauth.remote_app( - "sso", +sso = OAuth2ConsumerBlueprint( + "sso", __name__, + client_id=KEY, + client_secret=SECRET, base_url=BASE_URL, - request_token_url=None, - access_token_url=ACCESS_TOKEN_URL, - authorize_url=AUTHORIZE_URL, - consumer_key=KEY, - request_token_params={'state': lambda: security.gen_salt(10), "scope": "openid profile email openappstack_roles"}, - consumer_secret=SECRET) + token_url=ACCESS_TOKEN_URL, + authorization_url=AUTHORIZE_URL, + ) + +sso.error = {} -@sso.tokengetter -def get_sso_token(): - return session.get('sso_token') +app.register_blueprint(sso, url_prefix="/login") -@app.route('/') -def login(): - return sso.authorize(url_for('callback', _external=True)) +@app.route("/") +def index(): + if sso.error: + descr = sso.error["error_description"] + error = sso.error["error"] + sso.error = {} + return "error: {} - {}".format(error, descr) + elif not sso.token: + return redirect(url_for("sso.login")) + else: + return jsonify(sso.token) -@app.route('/userinfo') -def get_userinfo(): - if "id_token" in session: - # Use the id_token to retrieve user information from the sso server - resp = sso.request(USERINFO_URL) - return jsonify(resp.data) - abort(403) +@app.route("/userinfo") +def info(): + if sso.authorized: + resp = sso.session.get("/userinfo") + assert resp.ok + return jsonify(json.loads(resp.content.decode())) + else: + return redirect("/") -@app.route('/logout') +@app.route("/logout") def logout(): - del session['sso_token'] - return redirect(url_for('login')) + if sso.authorized: + del sso.token + return redirect("/") + else: + return redirect("/") + +@oauth_authorized.connect +def redirect_to_next_url(blueprint, token): + # set OAuth token in the token storage backend + blueprint.token = token -@app.route('/callback') -def callback(): - # retrieve all sso related values from the response - resp = sso.authorized_response() - if resp is None: - # Response was not authorized. It doesn't contain any authorized values - if "error" in request.args: - return jsonify(request.args) - abort(403) - # The authorized response contains tokens that we can use to authenticate - # In order for the agent to do that we save the tokens in the user session - session['sso_token'] = (resp['access_token'],None) - if "id_token" in resp: - session['id_token'] = resp['id_token'] - return jsonify(resp) +@oauth_error.connect +def asdasd(blueprint, **error): + # set OAuth token in the token storage backend + blueprint.error = error if __name__ == "__main__": app.run() + + diff --git a/test/integration_tests/requirements.txt b/test/integration_tests/requirements.txt index 065d7e3..40df930 100644 --- a/test/integration_tests/requirements.txt +++ b/test/integration_tests/requirements.txt @@ -1,3 +1,3 @@ Flask -Flask-OAuthlib -Werkzeug==0.16.1 # Due to bug: cannot find url_quote https://stackoverflow.com/questions/60192172/importerror-cannot-import-name-filestorage-from-werkzeug. Can be fixed by migrating from flask-oauthlib to oauthlib. +Flask-Dance +blinker diff --git a/test/integration_tests/test/behave/features/login.feature b/test/integration_tests/test/behave/features/login.feature index 93da304..86a132a 100644 --- a/test/integration_tests/test/behave/features/login.feature +++ b/test/integration_tests/test/behave/features/login.feature @@ -11,7 +11,7 @@ Scenario: Open the oAuth application and Login witha valid user And I enter the "password" in the inputfield "input#password" And I click on the button "input#submit" Then I wait on element "input#password" for 1000ms to not exist - And I expect that the path is "/callback" + And I expect that the path is "/" And I expect that element "body" contains the text "access_token" Scenario: Get OpenID Connect userdata for testuser diff --git a/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature b/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature index c95b995..e4b6150 100644 --- a/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature +++ b/test/integration_tests/test/behave/features/reject_unauthorized_logins.feature @@ -12,7 +12,7 @@ Scenario: Login with a valid user without access to an application And I click on the button "input#submit" Then I wait on element "input#password" for 1000ms to not exist And I expect that element "input#username" does not exist - And I expect that the path is "/callback" + And I expect that the path is "/" And I expect that element "body" contains the text "error" And I expect that element "body" contains the text "Permission denied" And I expect that element "body" contains the text "missing application permission" @@ -25,7 +25,7 @@ Scenario: Login with an invalid user And I click on the button "input#submit" Then I wait on element "input#password" for 1000ms to not exist And I expect that element "input#username" does not exist - And I expect that the path is "/callback" + And I expect that the path is "/" And I expect that element "body" contains the text "error" And I expect that element "body" contains the text "Login denied" And I expect that element "body" contains the text "Invalid username or password" diff --git a/test/integration_tests/test/behave/features/remember_me.feature b/test/integration_tests/test/behave/features/remember_me.feature index 81ad49e..d5b3e53 100644 --- a/test/integration_tests/test/behave/features/remember_me.feature +++ b/test/integration_tests/test/behave/features/remember_me.feature @@ -14,7 +14,7 @@ Scenario: Login with a valid user and remember session And I click on the button "input#submit" Then I wait on element "input#password" for 1000ms to not exist And I expect that element "input#username" does not exist - And I expect that the path is "/callback" + And I expect that the path is "/" And I expect that element "body" contains the text "access_token" Scenario: Login without providing credentials @@ -25,7 +25,7 @@ Scenario: Login without providing credentials And the element "button#continue" is visible When I click on the element "button#continue" Then I wait on element "button#continue" for 1000ms to not exist - And I expect that the path is "/callback" + And I expect that the path is "/" And I expect that element "body" contains the text "access_token" Scenario: Terminate single sign-on session -- GitLab