Merge pull request #2512 from ecordell/tufmetadata

Add tufmetadata endpoint
This commit is contained in:
josephschorr 2017-04-07 17:16:11 -04:00 committed by GitHub
commit 2bc619137a
12 changed files with 292 additions and 1 deletions

30
endpoints/api/signing.py Normal file
View file

@ -0,0 +1,30 @@
""" List and manage repository signing information """
import logging
import features
from app import tuf_metadata_api
from endpoints.api import (require_repo_read, path_param,
RepositoryParamResource, resource, nickname, show_if,
disallow_for_app_repositories)
logger = logging.getLogger(__name__)
@show_if(features.SIGNING)
@resource('/v1/repository/<apirepopath:repository>/signatures')
@path_param('repository', 'The full path of the repository. e.g. namespace/name')
class RepositorySignatures(RepositoryParamResource):
""" Operations for managing the signatures in a repository image. """
@require_repo_read
@nickname('getRepoSignatures')
@disallow_for_app_repositories
def get(self, namespace, repository):
""" Fetches the list of signed tags for the repository"""
tag_data, expiration = tuf_metadata_api.get_default_tags_with_expiration(namespace, repository)
return {
'tags': tag_data,
'expiration': expiration
}

View file

@ -10,6 +10,7 @@ from endpoints.api.repositorynotification import (RepositoryNotification,
RepositoryNotificationList,
TestRepositoryNotification)
from endpoints.api.secscan import RepositoryImageSecurity, RepositoryManifestSecurity
from endpoints.api.signing import RepositorySignatures
from endpoints.api.tag import ListRepositoryTags, RepositoryTag, RepositoryTagImages, RestoreTag
from endpoints.api.trigger import (BuildTriggerList, BuildTrigger, BuildTriggerSubdirs,
BuildTriggerActivate, BuildTriggerAnalyze, ActivateBuildTrigger,
@ -47,6 +48,7 @@ FIELD_ARGS = {'trigger_uuid': '1234', 'field_name': 'foobar'}
(TestRepositoryNotification, 'post', NOTIFICATION_ARGS),
(RepositoryImageSecurity, 'get', IMAGE_ARGS),
(RepositoryManifestSecurity, 'get', MANIFEST_ARGS),
(RepositorySignatures, 'get', None),
(ListRepositoryTags, 'get', None),
(RepositoryTag, 'put', TAG_ARGS),
(RepositoryTag, 'delete', TAG_ARGS),

View file

@ -1,14 +1,17 @@
import pytest
from flask_principal import AnonymousIdentity
from endpoints.api import api
from endpoints.api.team import OrganizationTeamSyncing
from endpoints.api.test.shared import client_with_identity, conduct_api_call
from endpoints.api.superuser import SuperUserRepositoryBuildLogs, SuperUserRepositoryBuildResource
from endpoints.api.superuser import SuperUserRepositoryBuildStatus
from endpoints.api.signing import RepositorySignatures
from test.fixtures import app, appconfig, database_uri, init_db_path, sqlitedb_file
TEAM_PARAMS = {'orgname': 'buynlarge', 'teamname': 'owners'}
BUILD_PARAMS = {'build_uuid': 'test-1234'}
REPO_PARAMS = {'repository': 'devtable/someapp'}
@pytest.mark.parametrize('resource,method,params,body,identity,expected', [
(OrganizationTeamSyncing, 'POST', TEAM_PARAMS, {}, None, 403),
@ -35,6 +38,10 @@ BUILD_PARAMS = {'build_uuid': 'test-1234'}
(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', 200),
])
def test_api_security(resource, method, params, body, identity, expected, client):
with client_with_identity(identity, client) as cl:

View file

@ -0,0 +1,43 @@
from collections import Counter
import pytest
from endpoints.api.test.shared import client_with_identity, conduct_api_call
from endpoints.api.signing import RepositorySignatures
from test.fixtures import app, appconfig, database_uri, init_db_path, sqlitedb_file
from mock import patch
VALID_TARGETS = {
'latest': {
'hashes': {
'sha256': 'mLmxwTyUrqIRDaz8uaBapfrp3GPERfsDg2kiMujlteo='
},
'length': 1500
},
'test_tag': {
'hashes': {
'sha256': '1234123'
},
'length': 50
}
}
def tags_equal(expected, actual):
expected_tags = expected.get('tags')
actual_tags = actual.get('tags')
if expected_tags and actual_tags:
return Counter(expected_tags) == Counter(actual_tags)
return expected == actual
@pytest.mark.parametrize('targets,expected', [
(VALID_TARGETS, {'tags': VALID_TARGETS, 'expiration': 'expires'}),
({'bad': 'tags'}, {'tags': {'bad': 'tags'}, 'expiration': 'expires'}),
({}, {'tags': {}, 'expiration': 'expires'}),
(None, {'tags': None, 'expiration': 'expires'}), # API returns None on exceptions
])
def test_get_signatures(targets, expected, client):
with patch('endpoints.api.signing.tuf_metadata_api') as mock_tuf:
mock_tuf.get_default_tags_with_expiration.return_value = (targets, 'expires')
with client_with_identity('devtable', client) as cl:
params = {'repository': 'devtable/repo'}
assert tags_equal(expected, conduct_api_call(cl, RepositorySignatures, 'GET', params, None, 200).json)

View file

@ -64,7 +64,7 @@ def generate_registry_jwt(auth_result):
user_event_data = {
'action': 'login',
}
tuf_root = 'quay'
tuf_root = QUAY_TUF_ROOT
if len(scope_param) > 0:
match = get_scope_regex().match(scope_param)