This repository has been archived on 2020-03-24. You can view files and clone it, but cannot push or open issues or pull requests.
quay/endpoints/api/__init__.py

195 lines
5.4 KiB
Python
Raw Normal View History

import logging
import json
from flask import Blueprint, request
from flask.ext.restful import Resource, abort, Api, reqparse
from flask.ext.restful.utils.cors import crossdomain
from calendar import timegm
from email.utils import formatdate
2014-03-11 03:54:55 +00:00
from functools import partial, wraps
from jsonschema import validate, ValidationError
from data import model
from util.names import parse_namespace_repository
from auth.permissions import (ReadRepositoryPermission,
ModifyRepositoryPermission,
AdministerRepositoryPermission)
2014-03-12 16:37:06 +00:00
from auth import scopes
from auth.auth_context import get_authenticated_user
from auth.auth import process_oauth
2014-03-11 03:54:55 +00:00
logger = logging.getLogger(__name__)
api_bp = Blueprint('api', __name__)
2014-03-17 19:23:49 +00:00
api = Api()
api.init_app(api_bp)
api.decorators = [process_oauth,
crossdomain(origin='*', headers=['Authorization', 'Content-Type'])]
def resource(*urls, **kwargs):
def wrapper(api_resource):
api.add_resource(api_resource, *urls, **kwargs)
return api_resource
return wrapper
def truthy_bool(param):
return param not in {False, 'false', 'False', '0', 'FALSE', '', 'null'}
def format_date(date):
""" Output an RFC822 date format. """
return formatdate(timegm(date.utctimetuple()))
def add_method_metadata(name, value):
def modifier(func):
if '__api_metadata' not in dir(func):
2014-03-11 03:54:55 +00:00
func.__api_metadata = {}
func.__api_metadata[name] = value
return func
return modifier
def method_metadata(func, name):
if '__api_metadata' in dir(func):
2014-03-11 03:54:55 +00:00
return func.__api_metadata.get(name, None)
return None
nickname = partial(add_method_metadata, 'nickname')
related_user_resource = partial(add_method_metadata, 'related_user_resource')
2014-03-14 22:07:03 +00:00
internal_only = add_method_metadata('internal', True)
def query_param(name, help_str, type=reqparse.text_type, default=None,
choices=(), required=False):
def add_param(func):
if '__api_query_params' not in dir(func):
func.__api_query_params = []
func.__api_query_params.append({
'name': name,
'type': type,
'help': help_str,
'default': default,
'choices': choices,
'required': required,
})
return func
return add_param
def parse_args(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
if '__api_query_params' not in dir(func):
abort(400)
parser = reqparse.RequestParser()
for arg_spec in func.__api_query_params:
parser.add_argument(**arg_spec)
parsed_args = parser.parse_args()
return func(self, parsed_args, *args, **kwargs)
return wrapper
2014-03-11 03:54:55 +00:00
def parse_repository_name(func):
@wraps(func)
def wrapper(repository, *args, **kwargs):
(namespace, repository) = parse_namespace_repository(repository)
return func(namespace, repository, *args, **kwargs)
return wrapper
class ApiResource(Resource):
def options(self):
return None, 200
class RepositoryParamResource(ApiResource):
2014-03-11 03:54:55 +00:00
method_decorators = [parse_repository_name]
2014-03-12 16:37:06 +00:00
def require_repo_permission(permission_class, scope, allow_public=False):
2014-03-11 03:54:55 +00:00
def wrapper(func):
2014-03-12 16:37:06 +00:00
@add_method_metadata('oauth2_scope', scope)
2014-03-11 03:54:55 +00:00
@wraps(func)
def wrapped(self, namespace, repository, *args, **kwargs):
logger.debug('Checking permission %s for repo: %s/%s', permission_class, namespace,
repository)
2014-03-11 03:54:55 +00:00
permission = permission_class(namespace, repository)
if (permission.can() or
(allow_public and
model.repository_is_public(namespace, repository))):
return func(self, namespace, repository, *args, **kwargs)
abort(403)
return wrapped
return wrapper
2014-03-12 16:37:06 +00:00
require_repo_read = require_repo_permission(ReadRepositoryPermission, scopes.READ_REPO, True)
require_repo_write = require_repo_permission(ModifyRepositoryPermission, scopes.WRITE_REPO)
require_repo_admin = require_repo_permission(AdministerRepositoryPermission, scopes.ADMIN_REPO)
2014-03-11 03:54:55 +00:00
def require_scope(scope_object):
def wrapper(func):
@add_method_metadata('oauth2_scope', scope_object['scope'])
@wraps(func)
def wrapped(*args, **kwargs):
return func(*args, **kwargs)
return wrapped
return wrapper
2014-03-11 03:54:55 +00:00
def validate_json_request(schema_name):
def wrapper(func):
@add_method_metadata('request_schema', schema_name)
@wraps(func)
def wrapped(self, *args, **kwargs):
2014-03-11 03:54:55 +00:00
schema = self.schemas[schema_name]
try:
validate(request.get_json(), schema)
return func(self, *args, **kwargs)
2014-03-11 03:54:55 +00:00
except ValidationError as ex:
abort(400, message=ex.message)
return wrapped
return wrapper
def request_error(exception=None, **kwargs):
data = kwargs.copy()
if exception:
data['message'] = exception.message
return json.dumps(data), 400
2014-03-11 03:54:55 +00:00
def log_action(kind, user_or_orgname, metadata={}, repo=None):
performer = get_authenticated_user()
model.log_action(kind, user_or_orgname, performer=performer, ip=request.remote_addr,
metadata=metadata, repository=repo)
2014-03-11 03:54:55 +00:00
import endpoints.api.legacy
2014-03-14 19:35:20 +00:00
import endpoints.api.billing
import endpoints.api.build
2014-03-14 17:24:01 +00:00
import endpoints.api.discovery
import endpoints.api.image
2014-03-14 20:02:13 +00:00
import endpoints.api.logs
import endpoints.api.organization
2014-03-14 17:24:01 +00:00
import endpoints.api.permission
import endpoints.api.prototype
2014-03-14 17:24:01 +00:00
import endpoints.api.repository
import endpoints.api.repotoken
2014-03-14 20:02:13 +00:00
import endpoints.api.robot
2014-03-14 17:24:01 +00:00
import endpoints.api.search
import endpoints.api.tag
2014-03-14 18:20:51 +00:00
import endpoints.api.team
2014-03-14 17:24:01 +00:00
import endpoints.api.trigger
import endpoints.api.user
import endpoints.api.webhook