From dff4207a8998f96880cba209475efdf8b9a0926d Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 7 Aug 2017 15:24:36 -0400 Subject: [PATCH 1/2] Add feature flag to enable viewing builds and build logs for public repos --- config.py | 4 ++++ endpoints/api/build.py | 10 ++++++++-- static/directives/build-mini-status.html | 2 +- static/directives/repo-view/repo-panel-info.html | 2 +- static/js/directives/repo-view/repo-panel-info.js | 4 +++- static/js/directives/ui/build-mini-status.js | 2 +- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/config.py b/config.py index c4e06d14a..197a1f34e 100644 --- a/config.py +++ b/config.py @@ -269,6 +269,10 @@ class DefaultConfig(ImmutableConfig): # only private repositories can be returned. FEATURE_PUBLIC_CATALOG = False + # Feature Flag: If set to true, build logs may be read by those with read access to the repo, + # rather than only write access or admin access. + FEATURE_READER_BUILD_LOGS = False + # The namespace to use for library repositories. # Note: This must remain 'library' until Docker removes their hard-coded namespace for libraries. # See: https://github.com/docker/docker/blob/master/registry/session.go#L320 diff --git a/endpoints/api/build.py b/endpoints/api/build.py index 7a20c2872..5165761eb 100644 --- a/endpoints/api/build.py +++ b/endpoints/api/build.py @@ -8,6 +8,8 @@ import os from flask import request from urlparse import urlparse +import features + from app import userfiles as user_files, build_logs, log_archive, dockerfile_build_queue from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission, AdministerRepositoryPermission, AdministerOrganizationPermission, @@ -147,7 +149,7 @@ def build_status_view(build_obj): 'error': error, } - if can_write: + if can_write or features.READER_BUILD_LOGS: if build_obj.resource_key is not None: resp['archive_url'] = user_files.get_file_url(build_obj.resource_key, requires_cors=True) elif job_config.get('archive_url', None): @@ -424,11 +426,15 @@ def get_logs_or_log_url(build): @path_param('build_uuid', 'The UUID of the build') class RepositoryBuildLogs(RepositoryParamResource): """ Resource for loading repository build logs. """ - @require_repo_write + @require_repo_read @nickname('getRepoBuildLogs') @disallow_for_app_repositories def get(self, namespace, repository, build_uuid): """ Return the build logs for the build specified by the build uuid. """ + can_write = ModifyRepositoryPermission(namespace, repository).can() + if not features.READER_BUILD_LOGS and not can_write: + raise Unauthorized() + build = model.build.get_repository_build(build_uuid) if (not build or build.repository.name != repository or build.repository.namespace_user.username != namespace): diff --git a/static/directives/build-mini-status.html b/static/directives/build-mini-status.html index 97b14d5c5..d1dda84cc 100644 --- a/static/directives/build-mini-status.html +++ b/static/directives/build-mini-status.html @@ -1,7 +1,7 @@ + is-only-text="!canView">
diff --git a/static/directives/repo-view/repo-panel-info.html b/static/directives/repo-view/repo-panel-info.html index 5e2b47d51..390f80d3d 100644 --- a/static/directives/repo-view/repo-panel-info.html +++ b/static/directives/repo-view/repo-panel-info.html @@ -40,7 +40,7 @@
+ can-view="repository.can_write || Features.READER_BUILD_LOGS">
diff --git a/static/js/directives/repo-view/repo-panel-info.js b/static/js/directives/repo-view/repo-panel-info.js index 58cb1e038..2a86953e2 100644 --- a/static/js/directives/repo-view/repo-panel-info.js +++ b/static/js/directives/repo-view/repo-panel-info.js @@ -13,7 +13,9 @@ angular.module('quay').directive('repoPanelInfo', function () { 'builds': '=builds', 'isEnabled': '=isEnabled' }, - controller: function($scope, $element, ApiService, Config) { + controller: function($scope, $element, ApiService, Config, Features) { + $scope.Features = Features; + $scope.$watch('repository', function(repository) { if (!$scope.repository) { return; } diff --git a/static/js/directives/ui/build-mini-status.js b/static/js/directives/ui/build-mini-status.js index c48d7ea21..fcd201462 100644 --- a/static/js/directives/ui/build-mini-status.js +++ b/static/js/directives/ui/build-mini-status.js @@ -10,7 +10,7 @@ angular.module('quay').directive('buildMiniStatus', function () { restrict: 'C', scope: { 'build': '=build', - 'isAdmin': '=isAdmin' + 'canView': '=canView' }, controller: function($scope, $element, BuildService) { $scope.isBuilding = function(build) { From 650dbe5f5bc8e5db14c6cbb4624cf81d5e7579ed Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 7 Aug 2017 15:59:06 -0400 Subject: [PATCH 2/2] Add config to enable "public" namespaces These are namespaces that will be displayed in the repo list view, regardless of whether the user is a member. --- config.py | 6 ++++++ endpoints/api/user.py | 9 ++++++++- static/js/pages/repo-list.js | 8 +++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index 197a1f34e..005ea4880 100644 --- a/config.py +++ b/config.py @@ -273,6 +273,12 @@ class DefaultConfig(ImmutableConfig): # rather than only write access or admin access. FEATURE_READER_BUILD_LOGS = False + # If a namespace is defined in the public namespace list, then it will appear on *all* + # user's repository list pages, regardless of whether that user is a member of the namespace. + # Typically, this is used by an enterprise customer in configuring a set of "well-known" + # namespaces. + PUBLIC_NAMESPACES = [] + # The namespace to use for library repositories. # Note: This must remain 'library' until Docker removes their hard-coded namespace for libraries. # See: https://github.com/docker/docker/blob/master/registry/session.go#L320 diff --git a/endpoints/api/user.py b/endpoints/api/user.py index 14a180b50..e0446e122 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -79,6 +79,7 @@ def user_view(user, previous_username=None): 'name': o.username, 'avatar': avatar.get_data_for_org(o), 'can_create_repo': CreateRepositoryPermission(o.username).can(), + 'public': o.username in app.config.get('PUBLIC_NAMESPACES', []), } if user_admin: @@ -89,7 +90,13 @@ def user_view(user, previous_username=None): return org_response - organizations = model.organization.get_user_organizations(user.username) + # Retrieve the organizations for the user. + organizations = list(model.organization.get_user_organizations(user.username)) + + # Add any public namespaces. + public_namespaces = app.config.get('PUBLIC_NAMESPACES', []) + if public_namespaces: + organizations.extend([model.user.get_namespace_user(ns) for ns in public_namespaces]) def login_view(login): try: diff --git a/static/js/pages/repo-list.js b/static/js/pages/repo-list.js index 30e889bbb..f294c630e 100644 --- a/static/js/pages/repo-list.js +++ b/static/js/pages/repo-list.js @@ -11,7 +11,7 @@ }]); - function RepoListCtrl($scope, $sanitize, $q, Restangular, UserService, ApiService, Features) { + function RepoListCtrl($scope, $sanitize, $q, Restangular, UserService, ApiService, Features, Config) { $scope.namespace = null; $scope.page = 1; $scope.publicPageCount = null; @@ -35,7 +35,8 @@ user.organizations.map(function(org) { $scope.namespaces.push({ 'name': org.name, - 'avatar': org.avatar + 'avatar': org.avatar, + 'public': org.public }); }); @@ -99,7 +100,8 @@ var options = { 'namespace': namespace.name, 'last_modified': true, - 'popularity': true + 'popularity': true, + 'public': namespace.public }; namespace.repositories = ApiService.listReposAsResource().withOptions(options).get(function(resp) {