fc6eb71ab1
We no longer allow viewing individual images, but instead only manifests. This will help with the transition to Clair V3 (which is manifest based) and, eventually, the the new data model (which will also be manifest based)
165 lines
8.9 KiB
Python
165 lines
8.9 KiB
Python
from mock import patch
|
|
|
|
import pytest
|
|
from flask_principal import AnonymousIdentity
|
|
|
|
from endpoints.api import api
|
|
from endpoints.api.organization import OrganizationCollaboratorList
|
|
from endpoints.api.repositorynotification import RepositoryNotification
|
|
from endpoints.api.permission import RepositoryUserTransitivePermission
|
|
from endpoints.api.team import OrganizationTeamSyncing
|
|
from endpoints.api.test.shared import conduct_api_call
|
|
from endpoints.api.repository import RepositoryTrust
|
|
from endpoints.api.signing import RepositorySignatures
|
|
from endpoints.api.search import ConductRepositorySearch
|
|
from endpoints.api.superuser import SuperUserRepositoryBuildLogs, SuperUserRepositoryBuildResource
|
|
from endpoints.api.superuser import SuperUserRepositoryBuildStatus
|
|
from endpoints.api.appspecifictokens import AppTokens, AppToken
|
|
from endpoints.api.manifest import RepositoryManifest
|
|
from endpoints.api.trigger import BuildTrigger
|
|
from endpoints.test.shared import client_with_identity, toggle_feature
|
|
|
|
from test.fixtures import *
|
|
|
|
ORG_PARAMS = {'orgname': 'buynlarge'}
|
|
TEAM_PARAMS = {'orgname': 'buynlarge', 'teamname': 'owners'}
|
|
BUILD_PARAMS = {'build_uuid': 'test-1234'}
|
|
REPO_PARAMS = {'repository': 'devtable/someapp'}
|
|
SEARCH_PARAMS = {'query': ''}
|
|
NOTIFICATION_PARAMS = {'namespace': 'devtable', 'repository': 'devtable/simple', 'uuid': 'some uuid'}
|
|
TOKEN_PARAMS = {'token_uuid': 'someuuid'}
|
|
TRIGGER_PARAMS = {'repository': 'devtable/simple', 'trigger_uuid': 'someuuid'}
|
|
MANIFEST_PARAMS = {'repository': 'devtable/simple', 'manifestref': 'sha256:deadbeef'}
|
|
|
|
@pytest.mark.parametrize('resource,method,params,body,identity,expected', [
|
|
(AppTokens, 'GET', {}, {}, None, 401),
|
|
(AppTokens, 'GET', {}, {}, 'freshuser', 200),
|
|
(AppTokens, 'GET', {}, {}, 'reader', 200),
|
|
(AppTokens, 'GET', {}, {}, 'devtable', 200),
|
|
|
|
(AppTokens, 'POST', {}, {}, None, 403),
|
|
(AppTokens, 'POST', {}, {}, 'freshuser', 400),
|
|
(AppTokens, 'POST', {}, {}, 'reader', 400),
|
|
(AppTokens, 'POST', {}, {}, 'devtable', 400),
|
|
|
|
(AppToken, 'GET', TOKEN_PARAMS, {}, None, 401),
|
|
(AppToken, 'GET', TOKEN_PARAMS, {}, 'freshuser', 404),
|
|
(AppToken, 'GET', TOKEN_PARAMS, {}, 'reader', 404),
|
|
(AppToken, 'GET', TOKEN_PARAMS, {}, 'devtable', 404),
|
|
|
|
(AppToken, 'DELETE', TOKEN_PARAMS, {}, None, 403),
|
|
(AppToken, 'DELETE', TOKEN_PARAMS, {}, 'freshuser', 404),
|
|
(AppToken, 'DELETE', TOKEN_PARAMS, {}, 'reader', 404),
|
|
(AppToken, 'DELETE', TOKEN_PARAMS, {}, 'devtable', 404),
|
|
|
|
(RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, None, 401),
|
|
(RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, 'freshuser', 403),
|
|
(RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, 'reader', 403),
|
|
(RepositoryManifest, 'GET', MANIFEST_PARAMS, {}, 'devtable', 404),
|
|
|
|
(OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, None, 401),
|
|
(OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, 'freshuser', 403),
|
|
(OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, 'reader', 403),
|
|
(OrganizationCollaboratorList, 'GET', ORG_PARAMS, None, 'devtable', 200),
|
|
|
|
(OrganizationTeamSyncing, 'POST', TEAM_PARAMS, {}, None, 403),
|
|
(OrganizationTeamSyncing, 'POST', TEAM_PARAMS, {}, 'freshuser', 403),
|
|
(OrganizationTeamSyncing, 'POST', TEAM_PARAMS, {}, 'reader', 403),
|
|
(OrganizationTeamSyncing, 'POST', TEAM_PARAMS, {}, 'devtable', 400),
|
|
|
|
(OrganizationTeamSyncing, 'DELETE', TEAM_PARAMS, {}, None, 403),
|
|
(OrganizationTeamSyncing, 'DELETE', TEAM_PARAMS, {}, 'freshuser', 403),
|
|
(OrganizationTeamSyncing, 'DELETE', TEAM_PARAMS, {}, 'reader', 403),
|
|
(OrganizationTeamSyncing, 'DELETE', TEAM_PARAMS, {}, 'devtable', 200),
|
|
|
|
(ConductRepositorySearch, 'GET', SEARCH_PARAMS, None, None, 200),
|
|
(ConductRepositorySearch, 'GET', SEARCH_PARAMS, None, 'freshuser', 200),
|
|
(ConductRepositorySearch, 'GET', SEARCH_PARAMS, None, 'reader', 200),
|
|
(ConductRepositorySearch, 'GET', SEARCH_PARAMS, None, 'devtable', 200),
|
|
|
|
(SuperUserRepositoryBuildLogs, 'GET', BUILD_PARAMS, None, None, 401),
|
|
(SuperUserRepositoryBuildLogs, 'GET', BUILD_PARAMS, None, 'freshuser', 403),
|
|
(SuperUserRepositoryBuildLogs, 'GET', BUILD_PARAMS, None, 'reader', 403),
|
|
(SuperUserRepositoryBuildLogs, 'GET', BUILD_PARAMS, None, 'devtable', 400),
|
|
|
|
(SuperUserRepositoryBuildStatus, 'GET', BUILD_PARAMS, None, None, 401),
|
|
(SuperUserRepositoryBuildStatus, 'GET', BUILD_PARAMS, None, 'freshuser', 403),
|
|
(SuperUserRepositoryBuildStatus, 'GET', BUILD_PARAMS, None, 'reader', 403),
|
|
(SuperUserRepositoryBuildStatus, 'GET', BUILD_PARAMS, None, 'devtable', 400),
|
|
|
|
(SuperUserRepositoryBuildResource, 'GET', BUILD_PARAMS, None, None, 401),
|
|
(SuperUserRepositoryBuildResource, 'GET', BUILD_PARAMS, None, 'freshuser', 403),
|
|
(SuperUserRepositoryBuildResource, 'GET', BUILD_PARAMS, None, 'reader', 403),
|
|
(SuperUserRepositoryBuildResource, 'GET', BUILD_PARAMS, None, 'devtable', 404),
|
|
|
|
(RepositorySignatures, 'GET', REPO_PARAMS, {}, 'freshuser', 403),
|
|
(RepositorySignatures, 'GET', REPO_PARAMS, {}, 'reader', 403),
|
|
(RepositorySignatures, 'GET', REPO_PARAMS, {}, 'devtable', 404),
|
|
|
|
(RepositoryNotification, 'POST', NOTIFICATION_PARAMS, {}, None, 403),
|
|
(RepositoryNotification, 'POST', NOTIFICATION_PARAMS, {}, 'freshuser', 403),
|
|
(RepositoryNotification, 'POST', NOTIFICATION_PARAMS, {}, 'reader', 403),
|
|
(RepositoryNotification, 'POST', NOTIFICATION_PARAMS, {}, 'devtable', 400),
|
|
|
|
(RepositoryTrust, 'POST', REPO_PARAMS, {'trust_enabled': True}, None, 403),
|
|
(RepositoryTrust, 'POST', REPO_PARAMS, {'trust_enabled': True}, 'freshuser', 403),
|
|
(RepositoryTrust, 'POST', REPO_PARAMS, {'trust_enabled': True}, 'reader', 403),
|
|
(RepositoryTrust, 'POST', REPO_PARAMS, {'trust_enabled': True}, 'devtable', 404),
|
|
|
|
(BuildTrigger, 'GET', TRIGGER_PARAMS, {}, None, 401),
|
|
(BuildTrigger, 'GET', TRIGGER_PARAMS, {}, 'freshuser', 403),
|
|
(BuildTrigger, 'GET', TRIGGER_PARAMS, {}, 'reader', 403),
|
|
(BuildTrigger, 'GET', TRIGGER_PARAMS, {}, 'devtable', 404),
|
|
|
|
(BuildTrigger, 'DELETE', TRIGGER_PARAMS, {}, None, 403),
|
|
(BuildTrigger, 'DELETE', TRIGGER_PARAMS, {}, 'freshuser', 403),
|
|
(BuildTrigger, 'DELETE', TRIGGER_PARAMS, {}, 'reader', 403),
|
|
(BuildTrigger, 'DELETE', TRIGGER_PARAMS, {}, 'devtable', 404),
|
|
|
|
(BuildTrigger, 'PUT', TRIGGER_PARAMS, {}, None, 403),
|
|
(BuildTrigger, 'PUT', TRIGGER_PARAMS, {}, 'freshuser', 403),
|
|
(BuildTrigger, 'PUT', TRIGGER_PARAMS, {}, 'reader', 403),
|
|
(BuildTrigger, 'PUT', TRIGGER_PARAMS, {}, 'devtable', 400),
|
|
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'public/publicrepo'}, None, None, 401),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'public/publicrepo'}, None, 'freshuser', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'public/publicrepo'}, None, 'reader', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'public/publicrepo'}, None, 'devtable', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'devtable/shared'}, None, None, 401),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'devtable/shared'}, None, 'freshuser', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'devtable/shared'}, None, 'reader', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'devtable/shared'}, None, 'devtable', 404),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'buynlarge/orgrepo'}, None, None, 401),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'buynlarge/orgrepo'}, None, 'freshuser', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'buynlarge/orgrepo'}, None, 'reader', 403),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'A2O9','repository': 'buynlarge/orgrepo'}, None, 'devtable', 404),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'devtable','repository': 'devtable/shared'}, None, 'devtable', 200),
|
|
(RepositoryUserTransitivePermission, 'GET', {'username': 'devtable','repository': 'devtable/nope'}, None, 'devtable', 404),
|
|
])
|
|
def test_api_security(resource, method, params, body, identity, expected, client):
|
|
with client_with_identity(identity, client) as cl:
|
|
conduct_api_call(cl, resource, method, params, body, expected)
|
|
|
|
|
|
@pytest.mark.parametrize('is_superuser', [
|
|
(True),
|
|
(False),
|
|
])
|
|
@pytest.mark.parametrize('allow_nonsuperuser', [
|
|
(True),
|
|
(False),
|
|
])
|
|
@pytest.mark.parametrize('method, expected', [
|
|
('POST', 400),
|
|
('DELETE', 200),
|
|
])
|
|
def test_team_sync_security(is_superuser, allow_nonsuperuser, method, expected, client):
|
|
def is_superuser_method(_):
|
|
return is_superuser
|
|
|
|
with patch('auth.permissions.superusers.is_superuser', is_superuser_method):
|
|
with toggle_feature('NONSUPERUSER_TEAM_SYNCING_SETUP', allow_nonsuperuser):
|
|
with client_with_identity('devtable', client) as cl:
|
|
expect_success = is_superuser or allow_nonsuperuser
|
|
expected_status = expected if expect_success else 403
|
|
conduct_api_call(cl, OrganizationTeamSyncing, method, TEAM_PARAMS, {}, expected_status)
|