Feed error messages through a cors wrapper so that people on other domains can see what's happening.

This commit is contained in:
jakedt 2014-03-17 16:57:35 -04:00
parent 4673f40dd2
commit 3b3d71bfd7
18 changed files with 162 additions and 129 deletions

View file

@ -1,9 +1,10 @@
import logging import logging
import json import json
from flask import Blueprint, request from flask import Blueprint, request, make_response, jsonify
from flask.ext.restful import Resource, abort, Api, reqparse from flask.ext.restful import Resource, abort, Api, reqparse
from flask.ext.restful.utils.cors import crossdomain from flask.ext.restful.utils.cors import crossdomain
from werkzeug.exceptions import HTTPException
from calendar import timegm from calendar import timegm
from email.utils import formatdate from email.utils import formatdate
from functools import partial, wraps from functools import partial, wraps
@ -27,6 +28,57 @@ api.decorators = [process_oauth,
crossdomain(origin='*', headers=['Authorization', 'Content-Type'])] crossdomain(origin='*', headers=['Authorization', 'Content-Type'])]
class ApiException(Exception):
def __init__(self, error_type, status_code, error_description, payload=None):
Exception.__init__(self)
self.error_description = error_description
self.status_code = status_code
self.payload = payload
self.error_type = error_type
def to_dict(self):
rv = dict(self.payload or ())
if self.error_description is not None:
rv['error_description'] = self.error_description
rv['error_type'] = self.error_type
return rv
invalid_request = partial(ApiException, 'invalid_request', 400)
class InvalidRequest(ApiException):
def __init__(self, error_description, payload=None):
ApiException.__init__(self, 'invalid_request', 400, error_description, payload)
class InvalidToken(ApiException):
def __init__(self, error_description, payload=None):
ApiException.__init__(self, 'invalid_token', 401, error_description, payload)
class Unauthorized(ApiException):
def __init__(self, payload=None):
ApiException.__init__(self, 'insufficient_scope', 403, 'Unauthorized', payload)
class NotFound(ApiException):
def __init__(self, payload=None):
ApiException.__init__(self, None, 404, 'Not Found', payload)
@api_bp.app_errorhandler(ApiException)
@crossdomain(origin='*', headers=['Authorization', 'Content-Type'])
def handle_api_error(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
if error.error_type is not None:
response.headers['WWW-Authenticate'] = ('Bearer error="%s" error_description="%s"' %
(error.error_type, error.error_description))
return response
def resource(*urls, **kwargs): def resource(*urls, **kwargs):
def wrapper(api_resource): def wrapper(api_resource):
api.add_resource(api_resource, *urls, **kwargs) api.add_resource(api_resource, *urls, **kwargs)
@ -84,7 +136,7 @@ def parse_args(func):
@wraps(func) @wraps(func)
def wrapper(self, *args, **kwargs): def wrapper(self, *args, **kwargs):
if '__api_query_params' not in dir(func): if '__api_query_params' not in dir(func):
abort(400) abort(500)
parser = reqparse.RequestParser() parser = reqparse.RequestParser()
for arg_spec in func.__api_query_params: for arg_spec in func.__api_query_params:
@ -124,7 +176,7 @@ def require_repo_permission(permission_class, scope, allow_public=False):
(allow_public and (allow_public and
model.repository_is_public(namespace, repository))): model.repository_is_public(namespace, repository))):
return func(self, namespace, repository, *args, **kwargs) return func(self, namespace, repository, *args, **kwargs)
abort(403) raise Unauthorized()
return wrapped return wrapped
return wrapper return wrapper
@ -154,17 +206,14 @@ def validate_json_request(schema_name):
validate(request.get_json(), schema) validate(request.get_json(), schema)
return func(self, *args, **kwargs) return func(self, *args, **kwargs)
except ValidationError as ex: except ValidationError as ex:
abort(400, message=ex.message) InvalidRequest(ex.message)
return wrapped return wrapped
return wrapper return wrapper
def request_error(exception=None, **kwargs): def request_error(exception=None, **kwargs):
data = kwargs.copy() data = kwargs.copy()
if exception: raise InvalidRequest(exception.message, data)
data['message'] = exception.message
return json.dumps(data), 400
def log_action(kind, user_or_orgname, metadata={}, repo=None): def log_action(kind, user_or_orgname, metadata={}, repo=None):

View file

@ -1,11 +1,9 @@
import logging
import stripe import stripe
from flask import request from flask import request
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action, from endpoints.api import (resource, nickname, ApiResource, validate_json_request, log_action,
related_user_resource, internal_only) related_user_resource, internal_only, Unauthorized, NotFound)
from endpoints.api.subscribe import subscribe, subscription_view from endpoints.api.subscribe import subscribe, subscription_view
from auth.permissions import AdministerOrganizationPermission from auth.permissions import AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
@ -158,7 +156,7 @@ class OrganizationCard(ApiResource):
organization = model.get_organization(orgname) organization = model.get_organization(orgname)
return get_card(organization) return get_card(organization)
abort(403) raise Unauthorized()
@nickname('setOrgCard') @nickname('setOrgCard')
@validate_json_request('OrgCard') @validate_json_request('OrgCard')
@ -172,7 +170,7 @@ class OrganizationCard(ApiResource):
log_action('account_change_cc', orgname) log_action('account_change_cc', orgname)
return response return response
abort(403) raise Unauthorized()
@resource('/v1/user/plan') @resource('/v1/user/plan')
@ -266,7 +264,7 @@ class OrganizationPlan(ApiResource):
organization = model.get_organization(orgname) organization = model.get_organization(orgname)
return subscribe(organization, plan, token, True) # Business plan required return subscribe(organization, plan, token, True) # Business plan required
abort(403) raise Unauthorized()
@nickname('getOrgSubscription') @nickname('getOrgSubscription')
def get(self, orgname): def get(self, orgname):
@ -286,7 +284,7 @@ class OrganizationPlan(ApiResource):
'usedPrivateRepos': private_repos, 'usedPrivateRepos': private_repos,
} }
abort(403) raise Unauthorized()
@resource('/v1/user/invoices') @resource('/v1/user/invoices')
@ -297,7 +295,7 @@ class UserInvoiceList(ApiResource):
""" List the invoices for the current user. """ """ List the invoices for the current user. """
user = get_authenticated_user() user = get_authenticated_user()
if not user.stripe_id: if not user.stripe_id:
abort(404) raise NotFound()
return get_invoices(user.stripe_id) return get_invoices(user.stripe_id)
@ -313,8 +311,8 @@ class OrgnaizationInvoiceList(ApiResource):
if permission.can(): if permission.can():
organization = model.get_organization(orgname) organization = model.get_organization(orgname)
if not organization.stripe_id: if not organization.stripe_id:
abort(404) raise NotFound()
return get_invoices(organization.stripe_id) return get_invoices(organization.stripe_id)
abort(403) raise Unauthorized()

View file

@ -2,12 +2,11 @@ import logging
import json import json
from flask import request from flask import request
from flask.ext.restful import abort
from app import app from app import app
from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource, from endpoints.api import (RepositoryParamResource, parse_args, query_param, nickname, resource,
require_repo_read, require_repo_write, validate_json_request, require_repo_read, require_repo_write, validate_json_request,
ApiResource, internal_only, format_date, api) ApiResource, internal_only, format_date, api, Unauthorized, NotFound)
from endpoints.common import start_build from endpoints.common import start_build
from endpoints.trigger import BuildTrigger from endpoints.trigger import BuildTrigger
from data import model from data import model
@ -111,7 +110,7 @@ class RepositoryBuildList(RepositoryParamResource):
if associated_repository: if associated_repository:
if not ModifyRepositoryPermission(associated_repository.namespace, if not ModifyRepositoryPermission(associated_repository.namespace,
associated_repository.name): associated_repository.name):
abort(403) raise Unauthorized()
# Start the build. # Start the build.
repo = model.get_repository(namespace, repository) repo = model.get_repository(namespace, repository)
@ -137,7 +136,7 @@ class RepositoryBuildStatus(RepositoryParamResource):
""" Return the status for the builds specified by the build uuids. """ """ Return the status for the builds specified by the build uuids. """
build = model.get_repository_build(namespace, repository, build_uuid) build = model.get_repository_build(namespace, repository, build_uuid)
if not build: if not build:
abort(404) raise NotFound()
can_write = ModifyRepositoryPermission(namespace, repository).can() can_write = ModifyRepositoryPermission(namespace, repository).can()
return build_status_view(build, can_write) return build_status_view(build, can_write)

View file

@ -1,11 +1,10 @@
import json import json
from collections import defaultdict from collections import defaultdict
from flask.ext.restful import abort
from app import app from app import app
from endpoints.api import (resource, nickname, require_repo_read, RepositoryParamResource, from endpoints.api import (resource, nickname, require_repo_read, RepositoryParamResource,
format_date) format_date, NotFound)
from data import model from data import model
from util.cache import cache_control_flask_restful from util.cache import cache_control_flask_restful
@ -64,7 +63,7 @@ class RepositoryImage(RepositoryParamResource):
""" Get the information available for the specified image. """ """ Get the information available for the specified image. """
image = model.get_repo_image(namespace, repository, image_id) image = model.get_repo_image(namespace, repository, image_id)
if not image: if not image:
abort(404) raise NotFound()
return image_view(image) return image_view(image)
@ -81,7 +80,7 @@ class RepositoryImageChanges(RepositoryParamResource):
image = model.get_repo_image(namespace, repository, image_id) image = model.get_repo_image(namespace, repository, image_id)
if not image: if not image:
abort(404) raise NotFound()
uuid = image.storage and image.storage.uuid uuid = image.storage and image.storage.uuid
diffs_path = store.image_file_diffs_path(namespace, repository, image_id, uuid) diffs_path = store.image_file_diffs_path(namespace, repository, image_id, uuid)
@ -90,4 +89,4 @@ class RepositoryImageChanges(RepositoryParamResource):
response_json = json.loads(store.get_content(diffs_path)) response_json = json.loads(store.get_content(diffs_path))
return response_json return response_json
except IOError: except IOError:
abort(404) raise NotFound()

View file

@ -1,11 +1,10 @@
import json import json
from datetime import datetime, timedelta from datetime import datetime, timedelta
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args, from endpoints.api import (resource, nickname, ApiResource, query_param, parse_args,
RepositoryParamResource, require_repo_admin, related_user_resource, RepositoryParamResource, require_repo_admin, related_user_resource,
format_date) format_date, Unauthorized, NotFound)
from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission from auth.permissions import AdministerOrganizationPermission, AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from data import model from data import model
@ -75,7 +74,7 @@ class RepositoryLogs(RepositoryParamResource):
""" List the logs for the specified repository. """ """ List the logs for the specified repository. """
repo = model.get_repository(namespace, repository) repo = model.get_repository(namespace, repository)
if not repo: if not repo:
abort(404) raise NotFound()
start_time = args['starttime'] start_time = args['starttime']
end_time = args['endtime'] end_time = args['endtime']
@ -119,4 +118,4 @@ class OrgLogs(ApiResource):
return get_logs(orgname, start_time, end_time, performer_name=performer_name) return get_logs(orgname, start_time, end_time, performer_name=performer_name)
abort(403) raise Unauthorized()

View file

@ -2,10 +2,9 @@ import logging
import stripe import stripe
from flask import request from flask import request
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error, from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
related_user_resource, internal_only) related_user_resource, internal_only, Unauthorized, NotFound)
from endpoints.api.team import team_view from endpoints.api.team import team_view
from endpoints.api.user import User, PrivateRepositories from endpoints.api.user import User, PrivateRepositories
from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission, from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission,
@ -83,13 +82,13 @@ class OrganizationList(ApiResource):
if existing: if existing:
msg = 'A user or organization with this name already exists' msg = 'A user or organization with this name already exists'
return request_error(message=msg) raise request_error(message=msg)
try: try:
model.create_organization(org_data['name'], org_data['email'], get_authenticated_user()) model.create_organization(org_data['name'], org_data['email'], get_authenticated_user())
return 'Created', 201 return 'Created', 201
except model.DataModelException as ex: except model.DataModelException as ex:
return request_error(exception=ex) raise request_error(exception=ex)
@resource('/v1/organization/<orgname>') @resource('/v1/organization/<orgname>')
@ -121,12 +120,12 @@ class Organization(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
teams = model.get_teams_within_org(org) teams = model.get_teams_within_org(org)
return org_view(org, teams) return org_view(org, teams)
abort(403) raise Unauthorized()
@nickname('changeOrganizationDetails') @nickname('changeOrganizationDetails')
@validate_json_request('UpdateOrg') @validate_json_request('UpdateOrg')
@ -135,7 +134,7 @@ class Organization(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
org_data = request.get_json() org_data = request.get_json()
if 'invoice_email' in org_data: if 'invoice_email' in org_data:
@ -145,7 +144,7 @@ class Organization(ApiResource):
if 'email' in org_data and org_data['email'] != org.email: if 'email' in org_data and org_data['email'] != org.email:
new_email = org_data['email'] new_email = org_data['email']
if model.find_user_by_email(new_email): if model.find_user_by_email(new_email):
return request_error(message='E-mail address already used') raise request_error(message='E-mail address already used')
logger.debug('Changing email address for organization: %s', org.username) logger.debug('Changing email address for organization: %s', org.username)
model.update_email(org, new_email) model.update_email(org, new_email)
@ -185,7 +184,7 @@ class OrgPrivateRepositories(ApiResource):
return data return data
abort(403) raise Unauthorized()
@resource('/v1/organization/<orgname>/members') @resource('/v1/organization/<orgname>/members')
@ -199,7 +198,7 @@ class OrgnaizationMemberList(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
# Loop to create the members dictionary. Note that the members collection # Loop to create the members dictionary. Note that the members collection
# will return an entry for *every team* a member is on, so we will have # will return an entry for *every team* a member is on, so we will have
@ -217,7 +216,7 @@ class OrgnaizationMemberList(ApiResource):
return {'members': members_dict} return {'members': members_dict}
abort(403) raise Unauthorized()
@resource('/v1/organization/<orgname>/members/<membername>') @resource('/v1/organization/<orgname>/members/<membername>')
@ -231,7 +230,7 @@ class OrganizationMember(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
member_dict = None member_dict = None
member_teams = model.get_organization_members_with_teams(org, membername=membername) member_teams = model.get_organization_members_with_teams(org, membername=membername)
@ -245,8 +244,8 @@ class OrganizationMember(ApiResource):
member_dict['teams'].append(member.team.name) member_dict['teams'].append(member.team.name)
if not member_dict: if not member_dict:
abort(404) raise NotFound()
return {'member': member_dict} return {'member': member_dict}
abort(403) raise Unauthorized()

View file

@ -145,7 +145,7 @@ class RepositoryUserPermission(RepositoryParamResource):
# This repository is not part of an organization # This repository is not part of an organization
pass pass
except model.DataModelException as ex: except model.DataModelException as ex:
return request_error(exception=ex) raise request_error(exception=ex)
log_action('change_repo_permission', namespace, log_action('change_repo_permission', namespace,
{'username': username, 'repo': repository, {'username': username, 'repo': repository,
@ -161,7 +161,7 @@ class RepositoryUserPermission(RepositoryParamResource):
try: try:
model.delete_user_permission(username, namespace, repository) model.delete_user_permission(username, namespace, repository)
except model.DataModelException as ex: except model.DataModelException as ex:
return request_error(exception=ex) raise request_error(exception=ex)
log_action('delete_repo_permission', namespace, log_action('delete_repo_permission', namespace,
{'username': username, 'repo': repository}, {'username': username, 'repo': repository},

View file

@ -1,8 +1,7 @@
from flask import request from flask import request
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error, from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
log_action) log_action, Unauthorized, NotFound)
from auth.permissions import AdministerOrganizationPermission from auth.permissions import AdministerOrganizationPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from data import model from data import model
@ -123,13 +122,13 @@ class PermissionPrototypeList(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
permissions = model.get_prototype_permissions(org) permissions = model.get_prototype_permissions(org)
org_members = model.get_organization_member_set(orgname) org_members = model.get_organization_member_set(orgname)
return {'prototypes': [prototype_view(p, org_members) for p in permissions]} return {'prototypes': [prototype_view(p, org_members) for p in permissions]}
abort(403) raise Unauthorized()
@nickname('createOrganizationPrototypePermission') @nickname('createOrganizationPrototypePermission')
@validate_json_request('NewPrototype') @validate_json_request('NewPrototype')
@ -140,7 +139,7 @@ class PermissionPrototypeList(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
details = request.get_json() details = request.get_json()
activating_username = None activating_username = None
@ -162,10 +161,10 @@ class PermissionPrototypeList(ApiResource):
if delegate_teamname else None) if delegate_teamname else None)
if activating_username and not activating_user: if activating_username and not activating_user:
return request_error(message='Unknown activating user') raise request_error(message='Unknown activating user')
if not delegate_user and not delegate_team: if not delegate_user and not delegate_team:
return request_error(message='Missing delegate user or team') raise request_error(message='Missing delegate user or team')
role_name = details['role'] role_name = details['role']
@ -175,7 +174,7 @@ class PermissionPrototypeList(ApiResource):
org_members = model.get_organization_member_set(orgname) org_members = model.get_organization_member_set(orgname)
return prototype_view(prototype, org_members) return prototype_view(prototype, org_members)
abort(403) raise Unauthorized()
@resource('/v1/organization/<orgname>/prototypes/<prototypeid>') @resource('/v1/organization/<orgname>/prototypes/<prototypeid>')
@ -211,17 +210,17 @@ class PermissionPrototype(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
prototype = model.delete_prototype_permission(org, prototypeid) prototype = model.delete_prototype_permission(org, prototypeid)
if not prototype: if not prototype:
abort(404) raise NotFound()
log_prototype_action('delete_prototype_permission', orgname, prototype) log_prototype_action('delete_prototype_permission', orgname, prototype)
return 'Deleted', 204 return 'Deleted', 204
abort(403) raise Unauthorized()
@nickname('updateOrganizationPrototypePermission') @nickname('updateOrganizationPrototypePermission')
@validate_json_request('PrototypeUpdate') @validate_json_request('PrototypeUpdate')
@ -232,21 +231,21 @@ class PermissionPrototype(ApiResource):
try: try:
org = model.get_organization(orgname) org = model.get_organization(orgname)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
abort(404) raise NotFound()
existing = model.get_prototype_permission(org, prototypeid) existing = model.get_prototype_permission(org, prototypeid)
if not existing: if not existing:
abort(404) raise NotFound()
details = request.get_json() details = request.get_json()
role_name = details['role'] role_name = details['role']
prototype = model.update_prototype_permission(org, prototypeid, role_name) prototype = model.update_prototype_permission(org, prototypeid, role_name)
if not prototype: if not prototype:
abort(404) raise NotFound()
log_prototype_action('modify_prototype_permission', orgname, prototype, log_prototype_action('modify_prototype_permission', orgname, prototype,
original_role=existing.role.name) original_role=existing.role.name)
org_members = model.get_organization_member_set(orgname) org_members = model.get_organization_member_set(orgname)
return prototype_view(prototype, org_members) return prototype_view(prototype, org_members)
abort(403) raise Unauthorized()

View file

@ -2,13 +2,12 @@ import logging
import json import json
from flask import current_app, request from flask import current_app, request
from flask.ext.restful import reqparse, abort
from data import model from data import model
from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request, from endpoints.api import (truthy_bool, format_date, nickname, log_action, validate_json_request,
require_repo_read, require_repo_write, require_repo_admin, require_repo_read, require_repo_write, require_repo_admin,
RepositoryParamResource, resource, query_param, parse_args, ApiResource, RepositoryParamResource, resource, query_param, parse_args, ApiResource,
request_error, require_scope) request_error, require_scope, Unauthorized, NotFound)
from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission, from auth.permissions import (ReadRepositoryPermission, ModifyRepositoryPermission,
AdministerRepositoryPermission, CreateRepositoryPermission) AdministerRepositoryPermission, CreateRepositoryPermission)
from auth.auth import process_auth from auth.auth import process_auth
@ -73,7 +72,7 @@ class RepositoryList(ApiResource):
existing = model.get_repository(namespace_name, repository_name) existing = model.get_repository(namespace_name, repository_name)
if existing: if existing:
return request_error(message='Repository already exists') raise request_error(message='Repository already exists')
visibility = req['visibility'] visibility = req['visibility']
@ -89,7 +88,7 @@ class RepositoryList(ApiResource):
'name': repository_name 'name': repository_name
}, 201 }, 201
abort(403) raise Unauthorized()
@nickname('listRepos') @nickname('listRepos')
@parse_args @parse_args
@ -213,7 +212,7 @@ class Repository(RepositoryParamResource):
'status_token': repo.badge_token if not is_public else '' 'status_token': repo.badge_token if not is_public else ''
} }
abort(404) # Not found raise NotFound()
@require_repo_write @require_repo_write
@nickname('updateRepo') @nickname('updateRepo')
@ -232,7 +231,7 @@ class Repository(RepositoryParamResource):
return { return {
'success': True 'success': True
} }
abort(404) # Not found raise NotFound()
@require_repo_admin @require_repo_admin
@nickname('deleteRepository') @nickname('deleteRepository')

View file

@ -1,10 +1,9 @@
import logging import logging
from flask import request from flask import request
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource, from endpoints.api import (resource, nickname, require_repo_admin, RepositoryParamResource,
log_action, validate_json_request) log_action, validate_json_request, NotFound)
from data import model from data import model
@ -97,7 +96,7 @@ class RepositoryToken(RepositoryParamResource):
try: try:
perm = model.get_repo_delegate_token(namespace, repository, code) perm = model.get_repo_delegate_token(namespace, repository, code)
except model.InvalidTokenException: except model.InvalidTokenException:
abort(404) raise NotFound()
return token_view(perm) return token_view(perm)

View file

@ -1,6 +1,5 @@
from flask.ext.restful import abort from endpoints.api import (resource, nickname, ApiResource, log_action, related_user_resource,
Unauthorized)
from endpoints.api import resource, nickname, ApiResource, log_action, related_user_resource
from auth.permissions import AdministerOrganizationPermission, OrganizationMemberPermission from auth.permissions import AdministerOrganizationPermission, OrganizationMemberPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from data import model from data import model
@ -21,6 +20,9 @@ class UserRobotList(ApiResource):
def get(self): def get(self):
""" List the available robots for the user. """ """ List the available robots for the user. """
user = get_authenticated_user() user = get_authenticated_user()
if not user:
raise Unauthorized()
robots = model.list_entity_robots(user.username) robots = model.list_entity_robots(user.username)
return { return {
'robots': [robot_view(name, password) for name, password in robots] 'robots': [robot_view(name, password) for name, password in robots]
@ -34,6 +36,9 @@ class UserRobot(ApiResource):
def put(self, robot_shortname): def put(self, robot_shortname):
""" Create a new user robot with the specified name. """ """ Create a new user robot with the specified name. """
parent = get_authenticated_user() parent = get_authenticated_user()
if not parent:
raise Unauthorized()
robot, password = model.create_robot(robot_shortname, parent) robot, password = model.create_robot(robot_shortname, parent)
log_action('create_robot', parent.username, {'robot': robot_shortname}) log_action('create_robot', parent.username, {'robot': robot_shortname})
return robot_view(robot.username, password), 201 return robot_view(robot.username, password), 201
@ -42,6 +47,9 @@ class UserRobot(ApiResource):
def delete(self, robot_shortname): def delete(self, robot_shortname):
""" Delete an existing robot. """ """ Delete an existing robot. """
parent = get_authenticated_user() parent = get_authenticated_user()
if not parent:
raise Unauthorized()
model.delete_robot(format_robot_username(parent.username, robot_shortname)) model.delete_robot(format_robot_username(parent.username, robot_shortname))
log_action('delete_robot', parent.username, {'robot': robot_shortname}) log_action('delete_robot', parent.username, {'robot': robot_shortname})
return 'Deleted', 204 return 'Deleted', 204
@ -61,7 +69,7 @@ class OrgRobotList(ApiResource):
'robots': [robot_view(name, password) for name, password in robots] 'robots': [robot_view(name, password) for name, password in robots]
} }
abort(403) raise Unauthorized()
@resource('/v1/organization/<orgname>/robots/<robot_shortname>') @resource('/v1/organization/<orgname>/robots/<robot_shortname>')
@ -78,7 +86,7 @@ class OrgRobot(ApiResource):
log_action('create_robot', orgname, {'robot': robot_shortname}) log_action('create_robot', orgname, {'robot': robot_shortname})
return robot_view(robot.username, password), 201 return robot_view(robot.username, password), 201
abort(403) raise Unauthorized()
@nickname('deleteOrgRobot') @nickname('deleteOrgRobot')
def delete(self, orgname, robot_shortname): def delete(self, orgname, robot_shortname):
@ -89,4 +97,4 @@ class OrgRobot(ApiResource):
log_action('delete_robot', orgname, {'robot': robot_shortname}) log_action('delete_robot', orgname, {'robot': robot_shortname})
return 'Deleted', 204 return 'Deleted', 204
abort(403) raise Unauthorized()

View file

@ -1,10 +1,9 @@
import logging import logging
import stripe import stripe
from endpoints.api import request_error, log_action from endpoints.api import request_error, log_action, NotFound
from data import model from data import model
from data.plans import PLANS from data.plans import PLANS
from util.http import abort
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -31,13 +30,13 @@ def subscribe(user, plan, token, require_business_plan):
if not plan_found or plan_found['deprecated']: if not plan_found or plan_found['deprecated']:
logger.warning('Plan not found or deprecated: %s', plan) logger.warning('Plan not found or deprecated: %s', plan)
abort(404) raise NotFound()
if (require_business_plan and not plan_found['bus_features'] and not if (require_business_plan and not plan_found['bus_features'] and not
plan_found['price'] == 0): plan_found['price'] == 0):
logger.warning('Business attempting to subscribe to personal plan: %s', logger.warning('Business attempting to subscribe to personal plan: %s',
user.username) user.username)
return request_error(message='No matching plan found') raise request_error(message='No matching plan found')
private_repos = model.get_private_repo_count(user.username) private_repos = model.get_private_repo_count(user.username)

View file

@ -1,7 +1,5 @@
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, require_repo_read, require_repo_admin, from endpoints.api import (resource, nickname, require_repo_read, require_repo_admin,
RepositoryParamResource, log_action) RepositoryParamResource, log_action, NotFound)
from endpoints.api.image import image_view from endpoints.api.image import image_view
from data import model from data import model
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
@ -36,7 +34,7 @@ class RepositoryTagImages(RepositoryParamResource):
try: try:
tag_image = model.get_tag_image(namespace, repository, tag) tag_image = model.get_tag_image(namespace, repository, tag)
except model.DataModelException: except model.DataModelException:
abort(404) raise NotFound()
parent_images = model.get_parent_images(tag_image) parent_images = model.get_parent_images(tag_image)

View file

@ -1,8 +1,7 @@
from flask import request from flask import request
from flask.ext.restful import abort
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error, from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
log_action) log_action, Unauthorized, NotFound)
from auth.permissions import AdministerOrganizationPermission, ViewTeamPermission from auth.permissions import AdministerOrganizationPermission, ViewTeamPermission
from auth.auth_context import get_authenticated_user from auth.auth_context import get_authenticated_user
from data import model from data import model
@ -95,7 +94,7 @@ class OrganizationTeam(ApiResource):
return team_view(orgname, team), 200 return team_view(orgname, team), 200
abort(403) raise Unauthorized()
@nickname('deleteOrganizationTeam') @nickname('deleteOrganizationTeam')
def delete(self, orgname, teamname): def delete(self, orgname, teamname):
@ -106,7 +105,7 @@ class OrganizationTeam(ApiResource):
log_action('org_delete_team', orgname, {'team': teamname}) log_action('org_delete_team', orgname, {'team': teamname})
return 'Deleted', 204 return 'Deleted', 204
abort(403) raise Unauthorized()
@resource('/v1/organization/<orgname>/team/<teamname>/members') @resource('/v1/organization/<orgname>/team/<teamname>/members')
@ -123,7 +122,7 @@ class TeamMemberList(ApiResource):
try: try:
team = model.get_organization_team(orgname, teamname) team = model.get_organization_team(orgname, teamname)
except model.InvalidTeamException: except model.InvalidTeamException:
abort(404) raise NotFound()
members = model.get_organization_team_members(team.id) members = model.get_organization_team_members(team.id)
return { return {
@ -131,7 +130,7 @@ class TeamMemberList(ApiResource):
'can_edit': edit_permission.can() 'can_edit': edit_permission.can()
} }
abort(403) raise Unauthorized()
@resource('/v1/organization/<orgname>/team/<teamname>/members/<membername>') @resource('/v1/organization/<orgname>/team/<teamname>/members/<membername>')
@ -149,19 +148,19 @@ class TeamMember(ApiResource):
try: try:
team = model.get_organization_team(orgname, teamname) team = model.get_organization_team(orgname, teamname)
except model.InvalidTeamException: except model.InvalidTeamException:
abort(404) raise NotFound()
# Find the user. # Find the user.
user = model.get_user(membername) user = model.get_user(membername)
if not user: if not user:
return request_error(message='Unknown user') raise request_error(message='Unknown user')
# Add the user to the team. # Add the user to the team.
model.add_user_to_team(user, team) model.add_user_to_team(user, team)
log_action('org_add_team_member', orgname, {'member': membername, 'team': teamname}) log_action('org_add_team_member', orgname, {'member': membername, 'team': teamname})
return member_view(user) return member_view(user)
abort(403) raise Unauthorized()
@nickname('deleteOrganizationTeamMember') @nickname('deleteOrganizationTeamMember')
def delete(self, orgname, teamname, membername): def delete(self, orgname, teamname, membername):
@ -174,4 +173,4 @@ class TeamMember(ApiResource):
log_action('org_remove_team_member', orgname, {'member': membername, 'team': teamname}) log_action('org_remove_team_member', orgname, {'member': membername, 'team': teamname})
return 'Deleted', 204 return 'Deleted', 204
abort(403) raise Unauthorized()

View file

@ -2,14 +2,13 @@ import json
import logging import logging
from flask import request, url_for from flask import request, url_for
from flask.ext.restful import abort
from urllib import quote from urllib import quote
from urlparse import urlunparse from urlparse import urlunparse
from app import app from app import app
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin, from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
log_action, request_error, query_param, parse_args, log_action, request_error, query_param, parse_args,
validate_json_request, api) validate_json_request, api, Unauthorized, NotFound, InvalidRequest)
from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus from endpoints.api.build import build_status_view, trigger_view, RepositoryBuildStatus
from endpoints.common import start_build from endpoints.common import start_build
from endpoints.trigger import (BuildTrigger, TriggerDeactivationException, from endpoints.trigger import (BuildTrigger, TriggerDeactivationException,
@ -51,7 +50,7 @@ class BuildTrigger(RepositoryParamResource):
try: try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid) trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException: except model.InvalidBuildTriggerException:
abort(404) raise NotFound()
return trigger_view(trigger) return trigger_view(trigger)
@ -62,8 +61,7 @@ class BuildTrigger(RepositoryParamResource):
try: try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid) trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException: except model.InvalidBuildTriggerException:
abort(404) raise NotFound()
return
handler = BuildTrigger.get_trigger_for_service(trigger.service.name) handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
config_dict = json.loads(trigger.config) config_dict = json.loads(trigger.config)
@ -102,8 +100,7 @@ class BuildTriggerSubdirs(RepositoryParamResource):
try: try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid) trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException: except model.InvalidBuildTriggerException:
abort(404) raise NotFound()
return
handler = BuildTrigger.get_trigger_for_service(trigger.service.name) handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
user_permission = UserPermission(trigger.connected_user.username) user_permission = UserPermission(trigger.connected_user.username)
@ -122,7 +119,7 @@ class BuildTriggerSubdirs(RepositoryParamResource):
'message': exc.msg 'message': exc.msg
} }
else: else:
abort(403) raise Unauthorized()
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/activate') @resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/activate')
@ -145,12 +142,12 @@ class BuildTriggerActivate(RepositoryParamResource):
try: try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid) trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException: except model.InvalidBuildTriggerException:
abort(404) raise NotFound()
handler = BuildTrigger.get_trigger_for_service(trigger.service.name) handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
existing_config_dict = json.loads(trigger.config) existing_config_dict = json.loads(trigger.config)
if handler.is_active(existing_config_dict): if handler.is_active(existing_config_dict):
abort(400) raise InvalidRequest('Trigger config is not sufficient for activation.')
user_permission = UserPermission(trigger.connected_user.username) user_permission = UserPermission(trigger.connected_user.username)
if user_permission.can(): if user_permission.can():
@ -173,7 +170,7 @@ class BuildTriggerActivate(RepositoryParamResource):
trigger.auth_token, new_config_dict) trigger.auth_token, new_config_dict)
except TriggerActivationException as exc: except TriggerActivationException as exc:
token.delete_instance() token.delete_instance()
return request_error(message=exc.message) raise request_error(message=exc.message)
# Save the updated config. # Save the updated config.
trigger.config = json.dumps(final_config) trigger.config = json.dumps(final_config)
@ -189,7 +186,7 @@ class BuildTriggerActivate(RepositoryParamResource):
return trigger_view(trigger) return trigger_view(trigger)
else: else:
abort(403) raise Unauthorized()
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/start') @resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/start')
@ -203,12 +200,12 @@ class ActivateBuildTrigger(RepositoryParamResource):
try: try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid) trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException: except model.InvalidBuildTriggerException:
abort(404) raise NotFound()
handler = BuildTrigger.get_trigger_for_service(trigger.service.name) handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
existing_config_dict = json.loads(trigger.config) existing_config_dict = json.loads(trigger.config)
if not handler.is_active(existing_config_dict): if not handler.is_active(existing_config_dict):
abort(400) raise InvalidRequest('Trigger is not active.')
specs = handler.manual_start(trigger.auth_token, json.loads(trigger.config)) specs = handler.manual_start(trigger.auth_token, json.loads(trigger.config))
dockerfile_id, tags, name, subdir = specs dockerfile_id, tags, name, subdir = specs
@ -253,7 +250,7 @@ class BuildTriggerSources(RepositoryParamResource):
try: try:
trigger = model.get_build_trigger(namespace, repository, trigger_uuid) trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
except model.InvalidBuildTriggerException: except model.InvalidBuildTriggerException:
abort(404) raise NotFound()
user_permission = UserPermission(trigger.connected_user.username) user_permission = UserPermission(trigger.connected_user.username)
if user_permission.can(): if user_permission.can():
@ -263,4 +260,4 @@ class BuildTriggerSources(RepositoryParamResource):
'sources': trigger_handler.list_build_sources(trigger.auth_token) 'sources': trigger_handler.list_build_sources(trigger.auth_token)
} }
else: else:
abort(403) raise Unauthorized()

View file

@ -2,13 +2,12 @@ import logging
import stripe import stripe
from flask import request from flask import request
from flask.ext.restful import abort
from flask.ext.login import logout_user from flask.ext.login import logout_user
from flask.ext.principal import identity_changed, AnonymousIdentity from flask.ext.principal import identity_changed, AnonymousIdentity
from app import app from app import app
from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error, from endpoints.api import (ApiResource, nickname, resource, validate_json_request, request_error,
log_action, internal_only) log_action, internal_only, NotFound)
from endpoints.api.subscribe import subscribe from endpoints.api.subscribe import subscribe
from endpoints.common import common_login from endpoints.common import common_login
from data import model from data import model
@ -139,7 +138,7 @@ class User(ApiResource):
new_email = user_data['email'] new_email = user_data['email']
if model.find_user_by_email(new_email): if model.find_user_by_email(new_email):
# Email already used. # Email already used.
return request_error(message='E-mail address already used') raise request_error(message='E-mail address already used')
logger.debug('Sending email to change email address for user: %s', logger.debug('Sending email to change email address for user: %s',
user.username) user.username)
@ -147,7 +146,7 @@ class User(ApiResource):
send_change_email(user.username, user_data['email'], code.code) send_change_email(user.username, user_data['email'], code.code)
except model.InvalidPasswordException, ex: except model.InvalidPasswordException, ex:
return request_error(exception=ex) raise request_error(exception=ex)
return user_view(user) return user_view(user)
@ -160,7 +159,7 @@ class User(ApiResource):
existing_user = model.get_user(user_data['username']) existing_user = model.get_user(user_data['username'])
if existing_user: if existing_user:
return request_error(message='The username already exists') raise request_error(message='The username already exists')
try: try:
new_user = model.create_user(user_data['username'], user_data['password'], new_user = model.create_user(user_data['username'], user_data['password'],
@ -169,7 +168,7 @@ class User(ApiResource):
send_confirmation_email(new_user.username, new_user.email, code.code) send_confirmation_email(new_user.username, new_user.email, code.code)
return 'Created', 201 return 'Created', 201
except model.DataModelException as ex: except model.DataModelException as ex:
return request_error(exception=ex) raise request_error(exception=ex)
@resource('/v1/user/private') @resource('/v1/user/private')
@ -257,13 +256,13 @@ class ConvertToOrganization(ApiResource):
# Ensure that the new admin user is the not user being converted. # Ensure that the new admin user is the not user being converted.
admin_username = convert_data['adminUser'] admin_username = convert_data['adminUser']
if admin_username == user.username: if admin_username == user.username:
return request_error(reason='invaliduser', raise request_error(reason='invaliduser',
message='The admin user is not valid') message='The admin user is not valid')
# Ensure that the sign in credentials work. # Ensure that the sign in credentials work.
admin_password = convert_data['adminPassword'] admin_password = convert_data['adminPassword']
if not model.verify_user(admin_username, admin_password): if not model.verify_user(admin_username, admin_password):
return request_error(reason='invaliduser', raise request_error(reason='invaliduser',
message='The admin user credentials are not valid') message='The admin user credentials are not valid')
# Subscribe the organization to the new plan. # Subscribe the organization to the new plan.
@ -310,7 +309,7 @@ class Signin(ApiResource):
""" Sign in the user with the specified credentials. """ """ Sign in the user with the specified credentials. """
signin_data = request.get_json() signin_data = request.get_json()
if not signin_data: if not signin_data:
abort(404) raise NotFound()
username = signin_data['username'] username = signin_data['username']
password = signin_data['password'] password = signin_data['password']

View file

@ -1,10 +1,9 @@
import json import json
from flask import request from flask import request
from flask.ext.restful import abort
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin, from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
log_action, validate_json_request, api) log_action, validate_json_request, api, NotFound)
from data import model from data import model
@ -63,7 +62,7 @@ class Webhook(RepositoryParamResource):
try: try:
webhook = model.get_webhook(namespace, repository, public_id) webhook = model.get_webhook(namespace, repository, public_id)
except model.InvalidWebhookException: except model.InvalidWebhookException:
abort(404) raise NotFound()
return webhook_view(webhook) return webhook_view(webhook)

View file

@ -77,12 +77,6 @@ def handle_dme(ex):
return make_response(ex.message, 400) return make_response(ex.message, 400)
@app.errorhandler(KeyError)
def handle_dme_key_error(ex):
logger.exception(ex)
return make_response('Invalid key: %s' % ex.message, 400)
def generate_csrf_token(): def generate_csrf_token():
if '_csrf_token' not in session: if '_csrf_token' not in session:
session['_csrf_token'] = base64.b64encode(os.urandom(48)) session['_csrf_token'] = base64.b64encode(os.urandom(48))