From 5516911de96d2fb4a76acd8ef119bb848f500d92 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 1 Jun 2015 13:43:38 -0400 Subject: [PATCH] Fix OAuth redirect for denial action when generating for internal tokens --- config.py | 5 ++++- data/model/oauth.py | 24 +++++++++++------------ endpoints/web.py | 15 +++++++++++++- static/partials/manage-application.html | 2 +- templates/generatedtoken.html | 26 +++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 templates/generatedtoken.html diff --git a/config.py b/config.py index 6bb7d0126..f4174de0f 100644 --- a/config.py +++ b/config.py @@ -19,7 +19,7 @@ def build_requests_session(): CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY', 'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN', 'AUTHENTICATION_TYPE', 'REGISTRY_TITLE', 'REGISTRY_TITLE_SHORT', - 'CONTACT_INFO', 'AVATAR_KIND'] + 'CONTACT_INFO', 'AVATAR_KIND', 'LOCAL_OAUTH_HANDLER'] def getFrontendVisibleConfig(config_dict): @@ -213,6 +213,9 @@ class DefaultConfig(object): # Signed registry grant token expiration in seconds SIGNED_GRANT_EXPIRATION_SEC = 60 * 60 * 24 # One day to complete a push/pull + # The URL endpoint to which we redirect OAuth when generating a token locally. + LOCAL_OAUTH_HANDLER = '/oauth/localapp' + # The various avatar background colors. AVATAR_KIND = 'local' AVATAR_COLORS = ['#969696', '#aec7e8', '#ff7f0e', '#ffbb78', '#2ca02c', '#98df8a', '#d62728', diff --git a/data/model/oauth.py b/data/model/oauth.py index 1be84114a..3e7cf23c2 100644 --- a/data/model/oauth.py +++ b/data/model/oauth.py @@ -1,6 +1,7 @@ import logging import json +from flask import url_for from datetime import datetime, timedelta from oauth2lib.provider import AuthorizationProvider from oauth2lib import utils @@ -9,12 +10,10 @@ from data.database import (OAuthApplication, OAuthAuthorizationCode, OAuthAccess random_string_generator) from data.model.legacy import get_user from auth import scopes -from flask import render_template logger = logging.getLogger(__name__) - class DatabaseAuthorizationProvider(AuthorizationProvider): def get_authorized_user(self): raise NotImplementedError('Subclasses must fill in the ability to get the authorized_user.') @@ -45,9 +44,12 @@ class DatabaseAuthorizationProvider(AuthorizationProvider): return False def validate_redirect_uri(self, client_id, redirect_uri): + if redirect_uri == url_for('web.oauth_local_handler', _external=True): + return True + try: - app = OAuthApplication.get(client_id=client_id) - if app.redirect_uri and redirect_uri and redirect_uri.startswith(app.redirect_uri): + oauth_app = OAuthApplication.get(client_id=client_id) + if oauth_app.redirect_uri and redirect_uri and redirect_uri.startswith(oauth_app.redirect_uri): return True return False except OAuthApplication.DoesNotExist: @@ -106,9 +108,9 @@ class DatabaseAuthorizationProvider(AuthorizationProvider): return None def persist_authorization_code(self, client_id, code, scope): - app = OAuthApplication.get(client_id=client_id) + oauth_app = OAuthApplication.get(client_id=client_id) data = self._generate_data_string() - OAuthAuthorizationCode.create(application=app, code=code, scope=scope, data=data) + OAuthAuthorizationCode.create(application=oauth_app, code=code, scope=scope, data=data) def persist_token_information(self, client_id, scope, access_token, token_type, expires_in, refresh_token, data): @@ -116,9 +118,9 @@ class DatabaseAuthorizationProvider(AuthorizationProvider): if not user: raise RuntimeError('Username must be in the data field') - app = OAuthApplication.get(client_id=client_id) + oauth_app = OAuthApplication.get(client_id=client_id) expires_at = datetime.utcnow() + timedelta(seconds=expires_in) - OAuthAccessToken.create(application=app, authorized_user=user, scope=scope, + OAuthAccessToken.create(application=oauth_app, authorized_user=user, scope=scope, access_token=access_token, token_type=token_type, expires_at=expires_at, refresh_token=refresh_token, data=data) @@ -163,7 +165,7 @@ class DatabaseAuthorizationProvider(AuthorizationProvider): # Check redirect URI is_valid_redirect_uri = self.validate_redirect_uri(client_id, redirect_uri) - if redirect_uri != 'display' and not is_valid_redirect_uri: + if not is_valid_redirect_uri: return self._invalid_redirect_uri_response() # Check conditions @@ -198,10 +200,6 @@ class DatabaseAuthorizationProvider(AuthorizationProvider): url = utils.build_url(redirect_uri, params) url += '#access_token=%s&token_type=%s&expires_in=%s' % (access_token, token_type, expires_in) - if redirect_uri == 'display': - return self._make_response( - render_template("message.html", message="Access Token: " + access_token)) - return self._make_response(headers={'Location': url}, status_code=302) diff --git a/endpoints/web.py b/endpoints/web.py index 7e61589af..3016721be 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -414,6 +414,19 @@ def authorize_application(): return provider.get_token_response('token', client_id, redirect_uri, scope=scope) + +@web.route(app.config['LOCAL_OAUTH_HANDLER'], methods=['GET']) +def oauth_local_handler(): + if not current_user.is_authenticated(): + abort(401) + return + + if not request.args.get('scope'): + return render_page_template("message.html", message="Authorization canceled") + else: + return render_page_template("generatedtoken.html") + + @web.route('/oauth/denyapp', methods=['POST']) @csrf_protect def deny_application(): @@ -444,7 +457,7 @@ def request_authorization_code(): if (not current_user.is_authenticated() or not provider.validate_has_scopes(client_id, current_user.db_user().username, scope)): - if redirect_uri != 'display' and not provider.validate_redirect_uri(client_id, redirect_uri): + if not provider.validate_redirect_uri(client_id, redirect_uri): current_app = provider.get_application_for_client_id(client_id) if not current_app: abort(404) diff --git a/static/partials/manage-application.html b/static/partials/manage-application.html index 45fb03ad5..2a2b7f939 100644 --- a/static/partials/manage-application.html +++ b/static/partials/manage-application.html @@ -114,7 +114,7 @@ Generate Access Token diff --git a/templates/generatedtoken.html b/templates/generatedtoken.html new file mode 100644 index 000000000..a7e493216 --- /dev/null +++ b/templates/generatedtoken.html @@ -0,0 +1,26 @@ + + Quay.io + + + + + + +
+ +
Access Token: + +
+
+ +