diff --git a/endpoints/web.py b/endpoints/web.py index 15f7261a3..5fdbe6ee5 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -17,7 +17,7 @@ from auth import scopes from auth.auth import require_session_login, process_oauth, has_basic_auth, process_auth_or_cookie from auth.permissions import (AdministerOrganizationPermission, ReadRepositoryPermission, SuperUserPermission, AdministerRepositoryPermission, - ModifyRepositoryPermission) + ModifyRepositoryPermission, OrganizationMemberPermission) from auth.auth_context import get_authenticated_user from buildtrigger.basehandler import BuildTriggerHandler from buildtrigger.bitbuckethandler import BitbucketBuildTrigger @@ -69,7 +69,7 @@ def internal_error_display(): @web.errorhandler(404) @web.route('/404', methods=['GET']) def not_found_error_display(e = None): - resp = render_page_template_with_routedata('404.html') + resp = index('', error_code=404) resp.status_code = 404 return resp @@ -670,17 +670,46 @@ def attach_custom_build_trigger(namespace_name, repo_name): @parse_repository_name(include_tag=True) @anon_protect def redirect_to_repository(namespace_name, repo_name, tag_name): - permission = ReadRepositoryPermission(namespace_name, repo_name) - is_public = model.repository.repository_is_public(namespace_name, repo_name) - + # Always return 200 for ac-discovery, to ensure that rkt and other ACI-compliant clients can + # find the metadata they need. Permissions will be checked in the registry API. if request.args.get('ac-discovery', 0) == 1: return index('') - if permission.can() or is_public: + # Redirect to the repository page if the user can see the repository. + is_public = model.repository.repository_is_public(namespace_name, repo_name) + permission = ReadRepositoryPermission(namespace_name, repo_name) + repo_exists = bool(model.repository.get_repository(namespace_name, repo_name)) + + if repo_exists and (permission.can() or is_public): repo_path = '/'.join([namespace_name, repo_name]) return redirect(url_for('web.repository', path=repo_path, tab="tags", tag=tag_name)) - abort(404) + namespace_exists = bool(model.user.get_user_or_org(namespace_name)) + namespace_permission = OrganizationMemberPermission(namespace_name).can() + if get_authenticated_user() and get_authenticated_user().username == namespace_name: + namespace_permission = True + + # Otherwise, we display an error for the user. Which error we display depends on permissions: + # > If the namespace doesn't exist, 404. + # > If the user is a member of the namespace: + # - If the repository doesn't exist, 404 + # - If the repository does exist (no access), 403 + # > If the user is not a member of the namespace: 403 + error_info = { + 'for_repo': True, + 'namespace_exists': namespace_exists, + 'namespace': namespace_name, + 'repo_name': repo_name, + } + + if not namespace_exists or (namespace_permission and not repo_exists): + resp = index('', error_code=404, error_info=json.dumps(error_info)) + resp.status_code = 404 + return resp + else: + resp = index('', error_code=403, error_info=json.dumps(error_info)) + resp.status_code = 403 + return resp @web.route('/') diff --git a/static/css/pages/error-view.css b/static/css/pages/error-view.css new file mode 100644 index 000000000..b3984a95e --- /dev/null +++ b/static/css/pages/error-view.css @@ -0,0 +1,25 @@ +.error-view-element { + text-align: center; +} + +.error-view-element h2 { + font-size: 42px; + margin-bottom: 10px; +} + +.error-view-element h3 { + font-size: 24px; +} + +.error-view-element img { + margin-top: 20px; + width: 255px; + height: 235px; + margin-bottom: 40px; +} + +.error-view-element .err403 img { + margin-top: 30px; + width: 225px; + height: 205px; +} diff --git a/static/img/40x/QE-logomark.svg b/static/img/40x/QE-logomark.svg new file mode 100644 index 000000000..26d528f2c --- /dev/null +++ b/static/img/40x/QE-logomark.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/static/img/40x/Quay-logomark.svg b/static/img/40x/Quay-logomark.svg new file mode 100644 index 000000000..fa6e7171c --- /dev/null +++ b/static/img/40x/Quay-logomark.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/static/img/40x/quay-logo-404.svg b/static/img/40x/quay-logo-404.svg new file mode 100644 index 000000000..6898c448b --- /dev/null +++ b/static/img/40x/quay-logo-404.svg @@ -0,0 +1 @@ +Artboard 1 \ No newline at end of file diff --git a/static/js/app.js b/static/js/app.js index 32626a6fd..b87f5fc72 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -207,8 +207,9 @@ quayApp.config(['$routeProvider', '$locationProvider', 'pages', function($routeP // Public Repo Experiments .route('/__exp/publicRepo', 'public-repo-exp') - // Default: Redirect to the landing page - .otherwise({redirectTo: '/'}); + // 404/403 + .route('/:catchall', 'error-view') + .route('/:catch/:all', 'error-view'); }]); // Configure compile provider to add additional URL prefixes to the sanitization list. We use diff --git a/static/js/pages/error-view.js b/static/js/pages/error-view.js new file mode 100644 index 000000000..9f13cb717 --- /dev/null +++ b/static/js/pages/error-view.js @@ -0,0 +1,17 @@ +(function() { + /** + * Error view page. + */ + angular.module('quayPages').config(['pages', function(pages) { + pages.create('error-view', 'error-view.html', ErrorViewCtrl, { + 'title': '{{code}}', + 'description': 'Error', + 'newLayout': false + }); + }]); + + function ErrorViewCtrl($scope, ApiService, $routeParams, UserService) { + $scope.info = window.__error_info; + $scope.code = window.__error_code; + } +}()); \ No newline at end of file diff --git a/static/js/pages/new-organization.js b/static/js/pages/new-organization.js index f63de46c4..ffd84f3c5 100644 --- a/static/js/pages/new-organization.js +++ b/static/js/pages/new-organization.js @@ -13,6 +13,9 @@ function NewOrgCtrl($scope, $routeParams, $timeout, $location, UserService, PlanService, ApiService, CookieService, Features) { $scope.Features = Features; $scope.holder = {}; + $scope.org = { + 'name': $routeParams['namespace'] || '' + }; UserService.updateUserIn($scope); diff --git a/static/js/pages/new-repo.js b/static/js/pages/new-repo.js index e4843afa7..ffd9806fe 100644 --- a/static/js/pages/new-repo.js +++ b/static/js/pages/new-repo.js @@ -10,7 +10,7 @@ }) }]); - function NewRepoCtrl($scope, $location, $http, $timeout, UserService, ApiService, PlanService, TriggerService, Features) { + function NewRepoCtrl($scope, $location, $http, $timeout, $routeParams, UserService, ApiService, PlanService, TriggerService, Features) { UserService.updateUserIn($scope); $scope.Features = Features; @@ -19,7 +19,8 @@ $scope.repo = { 'is_public': 0, 'description': '', - 'initialize': '' + 'initialize': '', + 'name': $routeParams['name'] }; $scope.changeNamespace = function(namespace) { diff --git a/static/partials/error-view.html b/static/partials/error-view.html new file mode 100644 index 000000000..df17e4bc3 --- /dev/null +++ b/static/partials/error-view.html @@ -0,0 +1,33 @@ +
+ +
+

404: Not Found

+

The resource you're looking for doesn't exists

+

Namespace {{ info.namespace }} doesn't exists

+

The repository you're looking for doesn't exists

+ + + +

+ Return to the main page +

+

+ Create this namespace or return to the main page +

+

+ Create this repository or return to the main page +

+
+ + +
+

403: Unauthorized

+

You are not authorized to view this resource

+

You are not authorized to view this repository

+ + + +

Contact the admin of the {{ info.namespace }} namespace for access to the repository or you can return to the main page

+

Return to the main page

+
+
\ No newline at end of file diff --git a/templates/404.html b/templates/404.html deleted file mode 100644 index b7f396a3f..000000000 --- a/templates/404.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "error.html" %} - -{% block title %} - Page Not Found · Quay -{% endblock %} - -{% block content %} -

The page you're looking for doesn't exist!

-

-

This is somewhat embarrassing, isn’t it? It looks like there's nothing here.

-

You probably want to return to the main page.

-

-{% endblock %} diff --git a/templates/base.html b/templates/base.html index 8ea902d6f..c67835a93 100644 --- a/templates/base.html +++ b/templates/base.html @@ -40,6 +40,14 @@ window.__auth_scopes = {{ scope_set|safe }}; window.__vuln_priority = {{ vuln_priority_set|safe }} window.__token = '{{ csrf_token() }}'; + + {% if error_code %} + window.__error_code = {{ error_code }}; + {% endif %} + + {% if error_info %} + window.__error_info = {{ error_info|safe }}; + {% endif %} {% for script_url in external_scripts %}