diff --git a/auth/auth.py b/auth/auth.py index f2b512121..0cfcedfc4 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -14,7 +14,7 @@ from data.model import oauth from app import app from permissions import QuayDeferredPermissionUser from auth_context import (set_authenticated_user, set_validated_token, - set_authenticated_user_deferred) + set_authenticated_user_deferred, set_validated_oauth_token) from util.http import abort @@ -140,7 +140,9 @@ def process_oauth(f): scope_set = scopes.scopes_from_scope_string(validated.scope) logger.debug('Successfully validated oauth access token: %s with scope: %s', token, scope_set) + set_authenticated_user(validated.authorized_user) + set_validated_oauth_token(validated) new_identity = QuayDeferredPermissionUser(validated.authorized_user.username, 'username', scope_set) diff --git a/auth/auth_context.py b/auth/auth_context.py index 16d62f5d9..2aad14685 100644 --- a/auth/auth_context.py +++ b/auth/auth_context.py @@ -35,6 +35,15 @@ def set_authenticated_user_deferred(username_or_robotname): ctx.authenticated_username = username_or_robotname +def get_validated_oauth_token(): + return getattr(_request_ctx_stack.top, 'validated_oauth_token', None) + + +def set_validated_oauth_token(token): + ctx = _request_ctx_stack.top + ctx.validated_oauth_token = token + + def get_validated_token(): return getattr(_request_ctx_stack.top, 'validated_token', None) diff --git a/data/model/oauth.py b/data/model/oauth.py index 475893b4d..75596c535 100644 --- a/data/model/oauth.py +++ b/data/model/oauth.py @@ -176,3 +176,9 @@ def validate_access_token(access_token): return found except OAuthAccessToken.DoesNotExist: return None + +def get_application_for_client_id(client_id): + try: + return OAuthApplication.get(client_id=client_id) + except OAuthApplication.DoesNotExist: + return None diff --git a/endpoints/api/__init__.py b/endpoints/api/__init__.py index f248c8f8d..527c56518 100644 --- a/endpoints/api/__init__.py +++ b/endpoints/api/__init__.py @@ -16,7 +16,7 @@ from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission, AdministerRepositoryPermission) from auth import scopes -from auth.auth_context import get_authenticated_user +from auth.auth_context import get_authenticated_user, get_validated_oauth_token from auth.auth import process_oauth @@ -216,7 +216,16 @@ def request_error(exception=None, **kwargs): raise InvalidRequest(message, data) -def log_action(kind, user_or_orgname, metadata={}, repo=None): +def log_action(kind, user_or_orgname, metadata=None, repo=None): + if not metadata: + metadata = {} + + oauth_token = get_validated_oauth_token() + if oauth_token: + metadata['oauth_token_id'] = oauth_token.id + metadata['oauth_token_application_id'] = oauth_token.application.client_id + metadata['oauth_token_application'] = oauth_token.application.name + performer = get_authenticated_user() model.log_action(kind, user_or_orgname, performer=performer, ip=request.remote_addr, metadata=metadata, repository=repo) diff --git a/endpoints/api/organization.py b/endpoints/api/organization.py index f0683ab1f..5053368f0 100644 --- a/endpoints/api/organization.py +++ b/endpoints/api/organization.py @@ -252,4 +252,22 @@ class OrganizationMember(ApiResource): return {'member': member_dict} - raise Unauthorized() \ No newline at end of file + raise Unauthorized() + + +@resource('/v1/app/') +class ApplicationInformation(ApiResource): + """ Resource that returns public information about a registered application. """ + @nickname('getApplicationInformation') + def get(self, client_id): + """ Get information on the specified application. """ + application = model.oauth.get_application_for_client_id(client_id) + if not application: + raise NotFound() + + return { + 'name': application.name, + 'description': application.description, + 'uri': application.application_uri, + 'organization': org_view(application.organization, []) + } diff --git a/static/directives/application-reference.html b/static/directives/application-reference.html new file mode 100644 index 000000000..0f61c5dc2 --- /dev/null +++ b/static/directives/application-reference.html @@ -0,0 +1,4 @@ + + + {{ title }} + diff --git a/static/directives/logs-view.html b/static/directives/logs-view.html index d6405d342..2268d0dc3 100644 --- a/static/directives/logs-view.html +++ b/static/directives/logs-view.html @@ -42,7 +42,7 @@ Description Date/Time - User/Token + User/Token/App @@ -53,14 +53,24 @@ {{ log.datetime }} - + +
+ +
+
on behalf of
+
+ +
+
+ - + {{ log.metadata.token }} - + (anonymous) diff --git a/static/js/app.js b/static/js/app.js index 03ca9d383..861f627b7 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -921,6 +921,44 @@ quayApp.directive('entityReference', function () { }); +quayApp.directive('applicationReference', function () { + var directiveDefinitionObject = { + priority: 0, + templateUrl: '/static/directives/application-reference.html', + replace: false, + transclude: false, + restrict: 'C', + scope: { + 'title': '=title', + 'clientId': '=clientId' + }, + controller: function($scope, $element, ApiService, $modal) { + $scope.showAppDetails = function() { + var params = { + 'client_id': $scope.clientId + }; + + ApiService.getApplicationInformation(null, params).then(function(resp) { + // TODO: display the application information here. + }, function() { + bootbox.dialog({ + "message": 'The application could not be found; it might have been deleted.', + "title": "Cannot find application", + "buttons": { + "close": { + "label": "Close", + "className": "btn-primary" + } + } + }); + }); + }; + } + }; + return directiveDefinitionObject; +}); + + quayApp.directive('markdownView', function () { var directiveDefinitionObject = { priority: 0,