Merge branch 'security'
Conflicts: endpoints/api.py endpoints/web.py
This commit is contained in:
commit
845985c859
10 changed files with 202 additions and 153 deletions
|
@ -6,12 +6,12 @@ from app import app as application
|
||||||
logging.basicConfig(**application.config['LOGGING_CONFIG'])
|
logging.basicConfig(**application.config['LOGGING_CONFIG'])
|
||||||
|
|
||||||
|
|
||||||
import endpoints.index
|
from endpoints.api import api
|
||||||
import endpoints.api
|
from endpoints.index import index
|
||||||
import endpoints.web
|
from endpoints.web import web
|
||||||
import endpoints.tags
|
from endpoints.tags import tags
|
||||||
import endpoints.registry
|
from endpoints.registry import registry
|
||||||
import endpoints.webhooks
|
from endpoints.webhooks import webhooks
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -20,6 +20,13 @@ if application.config.get('INCLUDE_TEST_ENDPOINTS', False):
|
||||||
logger.debug('Loading test endpoints.')
|
logger.debug('Loading test endpoints.')
|
||||||
import endpoints.test
|
import endpoints.test
|
||||||
|
|
||||||
|
application.register_blueprint(web)
|
||||||
|
application.register_blueprint(index, url_prefix='/v1')
|
||||||
|
application.register_blueprint(tags, url_prefix='/v1')
|
||||||
|
application.register_blueprint(registry, url_prefix='/v1')
|
||||||
|
application.register_blueprint(api, url_prefix='/api')
|
||||||
|
application.register_blueprint(webhooks, url_prefix='/webhooks')
|
||||||
|
|
||||||
# Remove this for prod config
|
# Remove this for prod config
|
||||||
application.debug = True
|
application.debug = True
|
||||||
|
|
||||||
|
|
181
endpoints/api.py
181
endpoints/api.py
|
@ -4,7 +4,7 @@ import requests
|
||||||
import urlparse
|
import urlparse
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import request, make_response, jsonify, abort, url_for
|
from flask import request, make_response, jsonify, abort, url_for, Blueprint, session
|
||||||
from flask.ext.login import current_user, logout_user
|
from flask.ext.login import current_user, logout_user
|
||||||
from flask.ext.principal import identity_changed, AnonymousIdentity
|
from flask.ext.principal import identity_changed, AnonymousIdentity
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
@ -29,13 +29,25 @@ from endpoints.common import common_login
|
||||||
from util.cache import cache_control
|
from util.cache import cache_control
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
store = app.config['STORAGE']
|
store = app.config['STORAGE']
|
||||||
user_files = app.config['USERFILES']
|
user_files = app.config['USERFILES']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
route_data = None
|
route_data = None
|
||||||
|
|
||||||
|
api = Blueprint('api', __name__)
|
||||||
|
|
||||||
|
@api.before_request
|
||||||
|
def csrf_protect():
|
||||||
|
if request.method != "GET" and request.method != "HEAD":
|
||||||
|
token = session.get('_csrf_token', None)
|
||||||
|
found_token = request.values.get('_csrf_token', None)
|
||||||
|
|
||||||
|
# TODO: add if not token here, once we are sure all sessions have a token.
|
||||||
|
if token != found_token:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
def get_route_data():
|
def get_route_data():
|
||||||
global route_data
|
global route_data
|
||||||
if route_data:
|
if route_data:
|
||||||
|
@ -43,14 +55,14 @@ def get_route_data():
|
||||||
|
|
||||||
routes = []
|
routes = []
|
||||||
for rule in app.url_map.iter_rules():
|
for rule in app.url_map.iter_rules():
|
||||||
if rule.rule.startswith('/api/'):
|
if rule.endpoint.startswith('api.'):
|
||||||
endpoint_method = globals()[rule.endpoint]
|
endpoint_method = globals()[rule.endpoint[4:]] # Remove api.
|
||||||
is_internal = '__internal_call' in dir(endpoint_method)
|
is_internal = '__internal_call' in dir(endpoint_method)
|
||||||
is_org_api = '__user_call' in dir(endpoint_method)
|
is_org_api = '__user_call' in dir(endpoint_method)
|
||||||
methods = list(rule.methods.difference(['HEAD', 'OPTIONS']))
|
methods = list(rule.methods.difference(['HEAD', 'OPTIONS']))
|
||||||
|
|
||||||
route = {
|
route = {
|
||||||
'name': rule.endpoint,
|
'name': rule.endpoint[4:],
|
||||||
'methods': methods,
|
'methods': methods,
|
||||||
'path': rule.rule,
|
'path': rule.rule,
|
||||||
'parameters': list(rule.arguments)
|
'parameters': list(rule.arguments)
|
||||||
|
@ -111,28 +123,19 @@ def org_api_call(user_call_name):
|
||||||
|
|
||||||
return internal_decorator
|
return internal_decorator
|
||||||
|
|
||||||
@app.errorhandler(model.DataModelException)
|
|
||||||
def handle_dme(ex):
|
|
||||||
return make_response(ex.message, 400)
|
|
||||||
|
|
||||||
|
@api.route('/discovery')
|
||||||
@app.errorhandler(KeyError)
|
|
||||||
def handle_dme_key_error(ex):
|
|
||||||
return make_response(ex.message, 400)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/discovery')
|
|
||||||
def discovery():
|
def discovery():
|
||||||
return jsonify(get_route_data())
|
return jsonify(get_route_data())
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/')
|
@api.route('/')
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def welcome():
|
def welcome():
|
||||||
return make_response('welcome', 200)
|
return make_response('welcome', 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/plans/')
|
@api.route('/plans/')
|
||||||
def list_plans():
|
def list_plans():
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'plans': PLANS,
|
'plans': PLANS,
|
||||||
|
@ -175,7 +178,7 @@ def user_view(user):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/', methods=['GET'])
|
@api.route('/user/', methods=['GET'])
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def get_logged_in_user():
|
def get_logged_in_user():
|
||||||
if current_user.is_anonymous():
|
if current_user.is_anonymous():
|
||||||
|
@ -188,7 +191,7 @@ def get_logged_in_user():
|
||||||
return jsonify(user_view(user))
|
return jsonify(user_view(user))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/private', methods=['GET'])
|
@api.route('/user/private', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def get_user_private_count():
|
def get_user_private_count():
|
||||||
|
@ -209,7 +212,7 @@ def get_user_private_count():
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/convert', methods=['POST'])
|
@api.route('/user/convert', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def convert_user_to_organization():
|
def convert_user_to_organization():
|
||||||
|
@ -246,7 +249,7 @@ def convert_user_to_organization():
|
||||||
return conduct_signin(admin_username, admin_password)
|
return conduct_signin(admin_username, admin_password)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/', methods=['PUT'])
|
@api.route('/user/', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def change_user_details():
|
def change_user_details():
|
||||||
|
@ -288,7 +291,7 @@ def change_user_details():
|
||||||
return jsonify(user_view(user))
|
return jsonify(user_view(user))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/', methods=['POST'])
|
@api.route('/user/', methods=['POST'])
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def create_new_user():
|
def create_new_user():
|
||||||
user_data = request.get_json()
|
user_data = request.get_json()
|
||||||
|
@ -315,7 +318,7 @@ def create_new_user():
|
||||||
return error_resp
|
return error_resp
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/signin', methods=['POST'])
|
@api.route('/signin', methods=['POST'])
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def signin_user():
|
def signin_user():
|
||||||
signin_data = request.get_json()
|
signin_data = request.get_json()
|
||||||
|
@ -348,7 +351,7 @@ def conduct_signin(username_or_email, password):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/signout", methods=['POST'])
|
@api.route("/signout", methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def logout():
|
def logout():
|
||||||
|
@ -357,7 +360,7 @@ def logout():
|
||||||
return make_response('Success', 200)
|
return make_response('Success', 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/recovery", methods=['POST'])
|
@api.route("/recovery", methods=['POST'])
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def request_recovery_email():
|
def request_recovery_email():
|
||||||
email = request.get_json()['email']
|
email = request.get_json()['email']
|
||||||
|
@ -366,7 +369,7 @@ def request_recovery_email():
|
||||||
return make_response('Created', 201)
|
return make_response('Created', 201)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/users/<prefix>', methods=['GET'])
|
@api.route('/users/<prefix>', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_matching_users(prefix):
|
def get_matching_users(prefix):
|
||||||
users = model.get_matching_users(prefix)
|
users = model.get_matching_users(prefix)
|
||||||
|
@ -376,7 +379,7 @@ def get_matching_users(prefix):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/entities/<prefix>', methods=['GET'])
|
@api.route('/entities/<prefix>', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_matching_entities(prefix):
|
def get_matching_entities(prefix):
|
||||||
teams = []
|
teams = []
|
||||||
|
@ -442,7 +445,7 @@ def team_view(orgname, team):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/', methods=['POST'])
|
@api.route('/organization/', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def create_organization():
|
def create_organization():
|
||||||
|
@ -491,7 +494,7 @@ def org_view(o, teams):
|
||||||
return view
|
return view
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>', methods=['GET'])
|
@api.route('/organization/<orgname>', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_organization(orgname):
|
def get_organization(orgname):
|
||||||
permission = OrganizationMemberPermission(orgname)
|
permission = OrganizationMemberPermission(orgname)
|
||||||
|
@ -507,7 +510,7 @@ def get_organization(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>', methods=['PUT'])
|
@api.route('/organization/<orgname>', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('change_user_details')
|
@org_api_call('change_user_details')
|
||||||
def change_organization_details(orgname):
|
def change_organization_details(orgname):
|
||||||
|
@ -701,7 +704,7 @@ def update_organization_prototype_permission(orgname, prototypeid):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/members', methods=['GET'])
|
@api.route('/organization/<orgname>/members', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_organization_members(orgname):
|
def get_organization_members(orgname):
|
||||||
permission = AdministerOrganizationPermission(orgname)
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
@ -730,7 +733,7 @@ def get_organization_members(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/members/<membername>', methods=['GET'])
|
@api.route('/organization/<orgname>/members/<membername>', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_organization_member(orgname, membername):
|
def get_organization_member(orgname, membername):
|
||||||
permission = AdministerOrganizationPermission(orgname)
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
@ -759,7 +762,7 @@ def get_organization_member(orgname, membername):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/private', methods=['GET'])
|
@api.route('/organization/<orgname>/private', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def get_organization_private_allowed(orgname):
|
def get_organization_private_allowed(orgname):
|
||||||
|
@ -795,7 +798,7 @@ def member_view(member):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/team/<teamname>',
|
@api.route('/organization/<orgname>/team/<teamname>',
|
||||||
methods=['PUT', 'POST'])
|
methods=['PUT', 'POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def update_organization_team(orgname, teamname):
|
def update_organization_team(orgname, teamname):
|
||||||
|
@ -841,7 +844,7 @@ def update_organization_team(orgname, teamname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/team/<teamname>',
|
@api.route('/organization/<orgname>/team/<teamname>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def delete_organization_team(orgname, teamname):
|
def delete_organization_team(orgname, teamname):
|
||||||
|
@ -854,7 +857,7 @@ def delete_organization_team(orgname, teamname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/team/<teamname>/members',
|
@api.route('/organization/<orgname>/team/<teamname>/members',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_organization_team_members(orgname, teamname):
|
def get_organization_team_members(orgname, teamname):
|
||||||
|
@ -877,7 +880,7 @@ def get_organization_team_members(orgname, teamname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/team/<teamname>/members/<membername>',
|
@api.route('/organization/<orgname>/team/<teamname>/members/<membername>',
|
||||||
methods=['PUT', 'POST'])
|
methods=['PUT', 'POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def update_organization_team_member(orgname, teamname, membername):
|
def update_organization_team_member(orgname, teamname, membername):
|
||||||
|
@ -906,7 +909,7 @@ def update_organization_team_member(orgname, teamname, membername):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/team/<teamname>/members/<membername>',
|
@api.route('/organization/<orgname>/team/<teamname>/members/<membername>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def delete_organization_team_member(orgname, teamname, membername):
|
def delete_organization_team_member(orgname, teamname, membername):
|
||||||
|
@ -922,7 +925,7 @@ def delete_organization_team_member(orgname, teamname, membername):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository', methods=['POST'])
|
@api.route('/repository', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def create_repo():
|
def create_repo():
|
||||||
owner = current_user.db_user()
|
owner = current_user.db_user()
|
||||||
|
@ -956,7 +959,7 @@ def create_repo():
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/find/repository', methods=['GET'])
|
@api.route('/find/repository', methods=['GET'])
|
||||||
def find_repos():
|
def find_repos():
|
||||||
prefix = request.args.get('query', '')
|
prefix = request.args.get('query', '')
|
||||||
|
|
||||||
|
@ -979,7 +982,7 @@ def find_repos():
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/', methods=['GET'])
|
@api.route('/repository/', methods=['GET'])
|
||||||
def list_repos():
|
def list_repos():
|
||||||
def repo_view(repo_obj):
|
def repo_view(repo_obj):
|
||||||
return {
|
return {
|
||||||
|
@ -1039,7 +1042,7 @@ def list_repos():
|
||||||
return jsonify(response)
|
return jsonify(response)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>', methods=['PUT'])
|
@api.route('/repository/<path:repository>', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def update_repo(namespace, repository):
|
def update_repo(namespace, repository):
|
||||||
|
@ -1061,7 +1064,7 @@ def update_repo(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/changevisibility',
|
@api.route('/repository/<path:repository>/changevisibility',
|
||||||
methods=['POST'])
|
methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1082,7 +1085,7 @@ def change_repo_visibility(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>', methods=['DELETE'])
|
@api.route('/repository/<path:repository>', methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def delete_repository(namespace, repository):
|
def delete_repository(namespace, repository):
|
||||||
|
@ -1108,7 +1111,7 @@ def image_view(image):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>', methods=['GET'])
|
@api.route('/repository/<path:repository>', methods=['GET'])
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def get_repo(namespace, repository):
|
def get_repo(namespace, repository):
|
||||||
logger.debug('Get repo: %s/%s' % (namespace, repository))
|
logger.debug('Get repo: %s/%s' % (namespace, repository))
|
||||||
|
@ -1157,7 +1160,7 @@ def get_repo(namespace, repository):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/build/', methods=['GET'])
|
@api.route('/repository/<path:repository>/build/', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def get_repo_builds(namespace, repository):
|
def get_repo_builds(namespace, repository):
|
||||||
|
@ -1190,7 +1193,7 @@ def get_repo_builds(namespace, repository):
|
||||||
abort(403) # Permissions denied
|
abort(403) # Permissions denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/build/', methods=['POST'])
|
@api.route('/repository/<path:repository>/build/', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def request_repo_build(namespace, repository):
|
def request_repo_build(namespace, repository):
|
||||||
|
@ -1228,7 +1231,7 @@ def webhook_view(webhook):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/webhook/', methods=['POST'])
|
@api.route('/repository/<path:repository>/webhook/', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def create_webhook(namespace, repository):
|
def create_webhook(namespace, repository):
|
||||||
|
@ -1248,7 +1251,7 @@ def create_webhook(namespace, repository):
|
||||||
abort(403) # Permissions denied
|
abort(403) # Permissions denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/webhook/<public_id>',
|
@api.route('/repository/<path:repository>/webhook/<public_id>',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1261,7 +1264,7 @@ def get_webhook(namespace, repository, public_id):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/webhook/', methods=['GET'])
|
@api.route('/repository/<path:repository>/webhook/', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def list_webhooks(namespace, repository):
|
def list_webhooks(namespace, repository):
|
||||||
|
@ -1275,7 +1278,7 @@ def list_webhooks(namespace, repository):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/webhook/<public_id>',
|
@api.route('/repository/<path:repository>/webhook/<public_id>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1291,7 +1294,7 @@ def delete_webhook(namespace, repository, public_id):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/filedrop/', methods=['POST'])
|
@api.route('/filedrop/', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def get_filedrop_url():
|
def get_filedrop_url():
|
||||||
|
@ -1318,7 +1321,7 @@ def wrap_role_view_org(role_json, user, org_members):
|
||||||
return role_json
|
return role_json
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/image/', methods=['GET'])
|
@api.route('/repository/<path:repository>/image/', methods=['GET'])
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def list_repository_images(namespace, repository):
|
def list_repository_images(namespace, repository):
|
||||||
permission = ReadRepositoryPermission(namespace, repository)
|
permission = ReadRepositoryPermission(namespace, repository)
|
||||||
|
@ -1343,7 +1346,7 @@ def list_repository_images(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/image/<image_id>',
|
@api.route('/repository/<path:repository>/image/<image_id>',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def get_image(namespace, repository, image_id):
|
def get_image(namespace, repository, image_id):
|
||||||
|
@ -1357,7 +1360,7 @@ def get_image(namespace, repository, image_id):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/image/<image_id>/changes',
|
@api.route('/repository/<path:repository>/image/<image_id>/changes',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@cache_control(max_age=60*60) # Cache for one hour
|
@cache_control(max_age=60*60) # Cache for one hour
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1375,7 +1378,7 @@ def get_image_changes(namespace, repository, image_id):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tag/<tag>',
|
@api.route('/api/repository/<path:repository>/tag/<tag>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def delete_full_tag(namespace, repository, tag):
|
def delete_full_tag(namespace, repository, tag):
|
||||||
|
@ -1393,7 +1396,7 @@ def delete_full_tag(namespace, repository, tag):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tag/<tag>/images',
|
@api.route('/repository/<path:repository>/tag/<tag>/images',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def list_tag_images(namespace, repository, tag):
|
def list_tag_images(namespace, repository, tag):
|
||||||
|
@ -1417,7 +1420,7 @@ def list_tag_images(namespace, repository, tag):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/team/',
|
@api.route('/repository/<path:repository>/permissions/team/',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1434,7 +1437,7 @@ def list_repo_team_permissions(namespace, repository):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/user/',
|
@api.route('/repository/<path:repository>/permissions/user/',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1475,7 +1478,7 @@ def list_repo_user_permissions(namespace, repository):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
|
@api.route('/repository/<path:repository>/permissions/user/<username>',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1500,7 +1503,7 @@ def get_user_permissions(namespace, repository, username):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
|
@api.route('/repository/<path:repository>/permissions/team/<teamname>',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1515,7 +1518,7 @@ def get_team_permissions(namespace, repository, teamname):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
|
@api.route('/repository/<path:repository>/permissions/user/<username>',
|
||||||
methods=['PUT', 'POST'])
|
methods=['PUT', 'POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1558,7 +1561,7 @@ def change_user_permissions(namespace, repository, username):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
|
@api.route('/repository/<path:repository>/permissions/team/<teamname>',
|
||||||
methods=['PUT', 'POST'])
|
methods=['PUT', 'POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1586,7 +1589,7 @@ def change_team_permissions(namespace, repository, teamname):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
|
@api.route('/repository/<path:repository>/permissions/user/<username>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1611,7 +1614,7 @@ def delete_user_permissions(namespace, repository, username):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
|
@api.route('/repository/<path:repository>/permissions/team/<teamname>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1637,7 +1640,7 @@ def token_view(token_obj):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tokens/', methods=['GET'])
|
@api.route('/repository/<path:repository>/tokens/', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def list_repo_tokens(namespace, repository):
|
def list_repo_tokens(namespace, repository):
|
||||||
|
@ -1652,7 +1655,7 @@ def list_repo_tokens(namespace, repository):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tokens/<code>', methods=['GET'])
|
@api.route('/repository/<path:repository>/tokens/<code>', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def get_tokens(namespace, repository, code):
|
def get_tokens(namespace, repository, code):
|
||||||
|
@ -1664,7 +1667,7 @@ def get_tokens(namespace, repository, code):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tokens/', methods=['POST'])
|
@api.route('/repository/<path:repository>/tokens/', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def create_token(namespace, repository):
|
def create_token(namespace, repository):
|
||||||
|
@ -1686,7 +1689,7 @@ def create_token(namespace, repository):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tokens/<code>', methods=['PUT'])
|
@api.route('/repository/<path:repository>/tokens/<code>', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def change_token(namespace, repository, code):
|
def change_token(namespace, repository, code):
|
||||||
|
@ -1711,7 +1714,7 @@ def change_token(namespace, repository, code):
|
||||||
abort(403) # Permission denied
|
abort(403) # Permission denied
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/tokens/<code>',
|
@api.route('/repository/<path:repository>/tokens/<code>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -1739,7 +1742,7 @@ def subscription_view(stripe_subscription, used_repos):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/card', methods=['GET'])
|
@api.route('/user/card', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def get_user_card():
|
def get_user_card():
|
||||||
|
@ -1747,7 +1750,7 @@ def get_user_card():
|
||||||
return get_card(user)
|
return get_card(user)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/card', methods=['GET'])
|
@api.route('/organization/<orgname>/card', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
@org_api_call('get_user_card')
|
@org_api_call('get_user_card')
|
||||||
|
@ -1760,7 +1763,7 @@ def get_org_card(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/card', methods=['POST'])
|
@api.route('/user/card', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def set_user_card():
|
def set_user_card():
|
||||||
|
@ -1771,7 +1774,7 @@ def set_user_card():
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/card', methods=['POST'])
|
@api.route('/organization/<orgname>/card', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('set_user_card')
|
@org_api_call('set_user_card')
|
||||||
def set_org_card(orgname):
|
def set_org_card(orgname):
|
||||||
|
@ -1823,7 +1826,7 @@ def get_card(user):
|
||||||
|
|
||||||
return jsonify({'card': card_info})
|
return jsonify({'card': card_info})
|
||||||
|
|
||||||
@app.route('/api/user/plan', methods=['PUT'])
|
@api.route('/user/plan', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def update_user_subscription():
|
def update_user_subscription():
|
||||||
|
@ -1916,7 +1919,7 @@ def subscribe(user, plan, token, require_business_plan):
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/invoices', methods=['GET'])
|
@api.route('/user/invoices', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def list_user_invoices():
|
def list_user_invoices():
|
||||||
user = current_user.db_user()
|
user = current_user.db_user()
|
||||||
|
@ -1926,7 +1929,7 @@ def list_user_invoices():
|
||||||
return get_invoices(user.stripe_id)
|
return get_invoices(user.stripe_id)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/invoices', methods=['GET'])
|
@api.route('/organization/<orgname>/invoices', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('list_user_invoices')
|
@org_api_call('list_user_invoices')
|
||||||
def list_org_invoices(orgname):
|
def list_org_invoices(orgname):
|
||||||
|
@ -1963,7 +1966,7 @@ def get_invoices(customer_id):
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/plan', methods=['PUT'])
|
@api.route('/organization/<orgname>/plan', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
@org_api_call('update_user_subscription')
|
@org_api_call('update_user_subscription')
|
||||||
|
@ -1979,7 +1982,7 @@ def update_org_subscription(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/plan', methods=['GET'])
|
@api.route('/user/plan', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
def get_user_subscription():
|
def get_user_subscription():
|
||||||
|
@ -1998,7 +2001,7 @@ def get_user_subscription():
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/plan', methods=['GET'])
|
@api.route('/organization/<orgname>/plan', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@internal_api_call
|
@internal_api_call
|
||||||
@org_api_call('get_user_subscription')
|
@org_api_call('get_user_subscription')
|
||||||
|
@ -2028,7 +2031,7 @@ def robot_view(name, token):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/robots', methods=['GET'])
|
@api.route('/user/robots', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_user_robots():
|
def get_user_robots():
|
||||||
user = current_user.db_user()
|
user = current_user.db_user()
|
||||||
|
@ -2038,7 +2041,7 @@ def get_user_robots():
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/robots', methods=['GET'])
|
@api.route('/organization/<orgname>/robots', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('get_user_robots')
|
@org_api_call('get_user_robots')
|
||||||
def get_org_robots(orgname):
|
def get_org_robots(orgname):
|
||||||
|
@ -2052,7 +2055,7 @@ def get_org_robots(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/robots/<robot_shortname>', methods=['PUT'])
|
@api.route('/user/robots/<robot_shortname>', methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def create_user_robot(robot_shortname):
|
def create_user_robot(robot_shortname):
|
||||||
parent = current_user.db_user()
|
parent = current_user.db_user()
|
||||||
|
@ -2063,7 +2066,7 @@ def create_user_robot(robot_shortname):
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/robots/<robot_shortname>',
|
@api.route('/organization/<orgname>/robots/<robot_shortname>',
|
||||||
methods=['PUT'])
|
methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('create_user_robot')
|
@org_api_call('create_user_robot')
|
||||||
|
@ -2080,7 +2083,7 @@ def create_org_robot(orgname, robot_shortname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/robots/<robot_shortname>', methods=['DELETE'])
|
@api.route('/user/robots/<robot_shortname>', methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def delete_user_robot(robot_shortname):
|
def delete_user_robot(robot_shortname):
|
||||||
parent = current_user.db_user()
|
parent = current_user.db_user()
|
||||||
|
@ -2089,7 +2092,7 @@ def delete_user_robot(robot_shortname):
|
||||||
return make_response('No Content', 204)
|
return make_response('No Content', 204)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/robots/<robot_shortname>',
|
@api.route('/organization/<orgname>/robots/<robot_shortname>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('delete_user_robot')
|
@org_api_call('delete_user_robot')
|
||||||
|
@ -2122,7 +2125,7 @@ def log_view(log):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/repository/<path:repository>/logs', methods=['GET'])
|
@api.route('/repository/<path:repository>/logs', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def list_repo_logs(namespace, repository):
|
def list_repo_logs(namespace, repository):
|
||||||
|
@ -2139,7 +2142,7 @@ def list_repo_logs(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>/logs', methods=['GET'])
|
@api.route('/organization/<orgname>/logs', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
@org_api_call('list_user_logs')
|
@org_api_call('list_user_logs')
|
||||||
def list_org_logs(orgname):
|
def list_org_logs(orgname):
|
||||||
|
@ -2155,7 +2158,7 @@ def list_org_logs(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/api/user/logs', methods=['GET'])
|
@api.route('/user/logs', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def list_user_logs():
|
def list_user_logs():
|
||||||
performer_name = request.args.get('performer', None)
|
performer_name = request.args.get('performer', None)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from flask import request, abort, session
|
||||||
from flask.ext.login import login_user, UserMixin
|
from flask.ext.login import login_user, UserMixin
|
||||||
from flask.ext.principal import identity_changed
|
from flask.ext.principal import identity_changed
|
||||||
|
|
||||||
|
@ -46,3 +49,22 @@ def common_login(db_user):
|
||||||
else:
|
else:
|
||||||
logger.debug('User could not be logged in, inactive?.')
|
logger.debug('User could not be logged in, inactive?.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(model.DataModelException)
|
||||||
|
def handle_dme(ex):
|
||||||
|
return make_response(ex.message, 400)
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(KeyError)
|
||||||
|
def handle_dme_key_error(ex):
|
||||||
|
return make_response(ex.message, 400)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_csrf_token():
|
||||||
|
if '_csrf_token' not in session:
|
||||||
|
session['_csrf_token'] = base64.b64encode(os.urandom(48))
|
||||||
|
|
||||||
|
return session['_csrf_token']
|
||||||
|
|
||||||
|
app.jinja_env.globals['csrf_token'] = generate_csrf_token
|
||||||
|
|
|
@ -2,7 +2,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import urlparse
|
import urlparse
|
||||||
|
|
||||||
from flask import request, make_response, jsonify, abort, session
|
from flask import request, make_response, jsonify, abort, session, Blueprint
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
from data import model
|
from data import model
|
||||||
|
@ -18,6 +18,7 @@ from auth.permissions import (ModifyRepositoryPermission, UserPermission,
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
index = Blueprint('index', __name__)
|
||||||
|
|
||||||
def generate_headers(role='read'):
|
def generate_headers(role='read'):
|
||||||
def decorator_method(f):
|
def decorator_method(f):
|
||||||
|
@ -51,8 +52,8 @@ def generate_headers(role='read'):
|
||||||
return decorator_method
|
return decorator_method
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/users', methods=['POST'])
|
@index.route('/users', methods=['POST'])
|
||||||
@app.route('/v1/users/', methods=['POST'])
|
@index.route('/users/', methods=['POST'])
|
||||||
def create_user():
|
def create_user():
|
||||||
user_data = request.get_json()
|
user_data = request.get_json()
|
||||||
username = user_data['username']
|
username = user_data['username']
|
||||||
|
@ -87,8 +88,8 @@ def create_user():
|
||||||
return make_response('Created', 201)
|
return make_response('Created', 201)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/users', methods=['GET'])
|
@index.route('/users', methods=['GET'])
|
||||||
@app.route('/v1/users/', methods=['GET'])
|
@index.route('/users/', methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
def get_user():
|
def get_user():
|
||||||
if get_authenticated_user():
|
if get_authenticated_user():
|
||||||
|
@ -99,7 +100,7 @@ def get_user():
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/users/<username>/', methods=['PUT'])
|
@index.route('/users/<username>/', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
def update_user(username):
|
def update_user(username):
|
||||||
permission = UserPermission(username)
|
permission = UserPermission(username)
|
||||||
|
@ -124,7 +125,7 @@ def update_user(username):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>', methods=['PUT'])
|
@index.route('/repositories/<path:repository>', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(role='write')
|
@generate_headers(role='write')
|
||||||
|
@ -188,7 +189,7 @@ def create_repository(namespace, repository):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/images', methods=['PUT'])
|
@index.route('/repositories/<path:repository>/images', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(role='write')
|
@generate_headers(role='write')
|
||||||
|
@ -234,7 +235,7 @@ def update_images(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/images', methods=['GET'])
|
@index.route('/repositories/<path:repository>/images', methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(role='read')
|
@generate_headers(role='read')
|
||||||
|
@ -290,7 +291,7 @@ def get_repository_images(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/images', methods=['DELETE'])
|
@index.route('/repositories/<path:repository>/images', methods=['DELETE'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
@generate_headers(role='write')
|
@generate_headers(role='write')
|
||||||
|
@ -298,19 +299,19 @@ def delete_repository_images(namespace, repository):
|
||||||
return make_response('Not Implemented', 501)
|
return make_response('Not Implemented', 501)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/auth', methods=['PUT'])
|
@index.route('/repositories/<path:repository>/auth', methods=['PUT'])
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
def put_repository_auth(namespace, repository):
|
def put_repository_auth(namespace, repository):
|
||||||
return make_response('Not Implemented', 501)
|
return make_response('Not Implemented', 501)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/search', methods=['GET'])
|
@index.route('/search', methods=['GET'])
|
||||||
def get_search():
|
def get_search():
|
||||||
return make_response('Not Implemented', 501)
|
return make_response('Not Implemented', 501)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/_ping')
|
@index.route('/_ping')
|
||||||
@app.route('/v1/_ping')
|
@index.route('/_ping')
|
||||||
def ping():
|
def ping():
|
||||||
response = make_response('true', 200)
|
response = make_response('true', 200)
|
||||||
response.headers['X-Docker-Registry-Version'] = '0.6.0'
|
response.headers['X-Docker-Registry-Version'] = '0.6.0'
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import make_response, request, session, Response, abort, redirect
|
from flask import (make_response, request, session, Response, abort,
|
||||||
|
redirect, Blueprint)
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from time import time
|
from time import time
|
||||||
|
@ -15,6 +16,7 @@ from auth.permissions import (ReadRepositoryPermission,
|
||||||
ModifyRepositoryPermission)
|
ModifyRepositoryPermission)
|
||||||
from data import model
|
from data import model
|
||||||
|
|
||||||
|
registry = Blueprint('registry', __name__)
|
||||||
|
|
||||||
store = app.config['STORAGE']
|
store = app.config['STORAGE']
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -73,7 +75,7 @@ def set_cache_headers(f):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/images/<image_id>/layer', methods=['GET'])
|
@registry.route('/images/<image_id>/layer', methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@extract_namespace_repo_from_session
|
@extract_namespace_repo_from_session
|
||||||
@require_completion
|
@require_completion
|
||||||
|
@ -94,7 +96,7 @@ def get_image_layer(namespace, repository, image_id, headers):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/images/<image_id>/layer', methods=['PUT'])
|
@registry.route('/images/<image_id>/layer', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@extract_namespace_repo_from_session
|
@extract_namespace_repo_from_session
|
||||||
def put_image_layer(namespace, repository, image_id):
|
def put_image_layer(namespace, repository, image_id):
|
||||||
|
@ -165,7 +167,7 @@ def put_image_layer(namespace, repository, image_id):
|
||||||
return make_response('true', 200)
|
return make_response('true', 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/images/<image_id>/checksum', methods=['PUT'])
|
@registry.route('/images/<image_id>/checksum', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@extract_namespace_repo_from_session
|
@extract_namespace_repo_from_session
|
||||||
def put_image_checksum(namespace, repository, image_id):
|
def put_image_checksum(namespace, repository, image_id):
|
||||||
|
@ -208,7 +210,7 @@ def put_image_checksum(namespace, repository, image_id):
|
||||||
return make_response('true', 200)
|
return make_response('true', 200)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/images/<image_id>/json', methods=['GET'])
|
@registry.route('/images/<image_id>/json', methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@extract_namespace_repo_from_session
|
@extract_namespace_repo_from_session
|
||||||
@require_completion
|
@require_completion
|
||||||
|
@ -238,7 +240,7 @@ def get_image_json(namespace, repository, image_id, headers):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/images/<image_id>/ancestry', methods=['GET'])
|
@registry.route('/images/<image_id>/ancestry', methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@extract_namespace_repo_from_session
|
@extract_namespace_repo_from_session
|
||||||
@require_completion
|
@require_completion
|
||||||
|
@ -283,7 +285,7 @@ def store_checksum(namespace, repository, image_id, checksum):
|
||||||
store.put_content(checksum_path, checksum)
|
store.put_content(checksum_path, checksum)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/images/<image_id>/json', methods=['PUT'])
|
@registry.route('/images/<image_id>/json', methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@extract_namespace_repo_from_session
|
@extract_namespace_repo_from_session
|
||||||
def put_image_json(namespace, repository, image_id):
|
def put_image_json(namespace, repository, image_id):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from flask import abort, request, jsonify, make_response
|
from flask import abort, request, jsonify, make_response, Blueprint
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
from util.names import parse_repository_name
|
from util.names import parse_repository_name
|
||||||
|
@ -14,8 +14,10 @@ from data import model
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
tags = Blueprint('tags', __name__)
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/tags',
|
|
||||||
|
@tags.route('/repositories/<path:repository>/tags',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -30,7 +32,7 @@ def get_tags(namespace, repository):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/tags/<tag>',
|
@tags.route('/repositories/<path:repository>/tags/<tag>',
|
||||||
methods=['GET'])
|
methods=['GET'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -46,7 +48,7 @@ def get_tag(namespace, repository, tag):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/tags/<tag>',
|
@tags.route('/repositories/<path:repository>/tags/<tag>',
|
||||||
methods=['PUT'])
|
methods=['PUT'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -62,7 +64,7 @@ def put_tag(namespace, repository, tag):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/tags/<tag>',
|
@tags.route('/repositories/<path:repository>/tags/<tag>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
@ -77,7 +79,7 @@ def delete_tag(namespace, repository, tag):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1/repositories/<path:repository>/tags',
|
@tags.route('/repositories/<path:repository>/tags',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@process_auth
|
@process_auth
|
||||||
@parse_repository_name
|
@parse_repository_name
|
||||||
|
|
|
@ -3,7 +3,7 @@ import requests
|
||||||
import stripe
|
import stripe
|
||||||
|
|
||||||
from flask import (abort, redirect, request, url_for, render_template,
|
from flask import (abort, redirect, request, url_for, render_template,
|
||||||
make_response, Response)
|
make_response, Response, Blueprint)
|
||||||
from flask.ext.login import login_required, current_user
|
from flask.ext.login import login_required, current_user
|
||||||
from urlparse import urlparse
|
from urlparse import urlparse
|
||||||
|
|
||||||
|
@ -19,23 +19,28 @@ from endpoints.common import common_login
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
web = Blueprint('web', __name__)
|
||||||
|
|
||||||
|
|
||||||
def render_page_template(name, **kwargs):
|
def render_page_template(name, **kwargs):
|
||||||
return make_response(render_template(name, route_data=get_route_data(),
|
|
||||||
|
resp = make_response(render_template(name, route_data=get_route_data(),
|
||||||
**kwargs))
|
**kwargs))
|
||||||
|
resp.headers['X-FRAME-OPTIONS'] = 'DENY'
|
||||||
|
return resp
|
||||||
|
|
||||||
|
|
||||||
@app.route('/', methods=['GET'], defaults={'path': ''})
|
@web.route('/', methods=['GET'], defaults={'path': ''})
|
||||||
@app.route('/repository/<path:path>', methods=['GET'])
|
@web.route('/repository/<path:path>', methods=['GET'])
|
||||||
@app.route('/organization/<path:path>', methods=['GET'])
|
@web.route('/organization/<path:path>', methods=['GET'])
|
||||||
@no_cache
|
@no_cache
|
||||||
def index(path):
|
def index(path):
|
||||||
return render_page_template('index.html')
|
return render_page_template('index.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/snapshot', methods=['GET'])
|
@web.route('/snapshot', methods=['GET'])
|
||||||
@app.route('/snapshot/', methods=['GET'])
|
@web.route('/snapshot/', methods=['GET'])
|
||||||
@app.route('/snapshot/<path:path>', methods=['GET'])
|
@web.route('/snapshot/<path:path>', methods=['GET'])
|
||||||
def snapshot(path = ''):
|
def snapshot(path = ''):
|
||||||
parsed = urlparse(request.url)
|
parsed = urlparse(request.url)
|
||||||
final_url = '%s://%s/%s' % (parsed.scheme, 'localhost', path)
|
final_url = '%s://%s/%s' % (parsed.scheme, 'localhost', path)
|
||||||
|
@ -46,92 +51,93 @@ def snapshot(path = ''):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/plans/')
|
@web.route('/plans/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def plans():
|
def plans():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/guide/')
|
@web.route('/guide/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def guide():
|
def guide():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/organizations/')
|
@web.route('/organizations/')
|
||||||
@app.route('/organizations/new/')
|
@web.route('/organizations/new/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def organizations():
|
def organizations():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/user/')
|
@web.route('/user/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def user():
|
def user():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/signin/')
|
@web.route('/signin/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def signin():
|
def signin():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/contact/')
|
@web.route('/contact/')
|
||||||
|
@no_cache
|
||||||
def contact():
|
def contact():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/new/')
|
@web.route('/new/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def new():
|
def new():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/repository/')
|
@web.route('/repository/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def repository():
|
def repository():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/security/')
|
@web.route('/security/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def security():
|
def security():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/v1')
|
@web.route('/v1')
|
||||||
@app.route('/v1/')
|
@web.route('/v1/')
|
||||||
@no_cache
|
@no_cache
|
||||||
def v1():
|
def v1():
|
||||||
return index('')
|
return index('')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/status', methods=['GET'])
|
@web.route('/status', methods=['GET'])
|
||||||
@no_cache
|
@no_cache
|
||||||
def status():
|
def status():
|
||||||
return make_response('Healthy')
|
return make_response('Healthy')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/tos', methods=['GET'])
|
@web.route('/tos', methods=['GET'])
|
||||||
@no_cache
|
@no_cache
|
||||||
def tos():
|
def tos():
|
||||||
return render_page_template('tos.html')
|
return render_page_template('tos.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/disclaimer', methods=['GET'])
|
@web.route('/disclaimer', methods=['GET'])
|
||||||
@no_cache
|
@no_cache
|
||||||
def disclaimer():
|
def disclaimer():
|
||||||
return render_page_template('disclaimer.html')
|
return render_page_template('disclaimer.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/privacy', methods=['GET'])
|
@web.route('/privacy', methods=['GET'])
|
||||||
@no_cache
|
@no_cache
|
||||||
def privacy():
|
def privacy():
|
||||||
return render_page_template('privacy.html')
|
return render_page_template('privacy.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/receipt', methods=['GET'])
|
@web.route('/receipt', methods=['GET'])
|
||||||
def receipt():
|
def receipt():
|
||||||
if not current_user.is_authenticated():
|
if not current_user.is_authenticated():
|
||||||
abort(401)
|
abort(401)
|
||||||
|
@ -188,7 +194,7 @@ def get_github_user(token):
|
||||||
return get_user.json()
|
return get_user.json()
|
||||||
|
|
||||||
|
|
||||||
@app.route('/oauth2/github/callback', methods=['GET'])
|
@web.route('/oauth2/github/callback', methods=['GET'])
|
||||||
def github_oauth_callback():
|
def github_oauth_callback():
|
||||||
error = request.args.get('error', None)
|
error = request.args.get('error', None)
|
||||||
if error:
|
if error:
|
||||||
|
@ -241,7 +247,7 @@ def github_oauth_callback():
|
||||||
return render_page_template('githuberror.html')
|
return render_page_template('githuberror.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/oauth2/github/callback/attach', methods=['GET'])
|
@web.route('/oauth2/github/callback/attach', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def github_oauth_attach():
|
def github_oauth_attach():
|
||||||
token = exchange_github_code_for_token(request.args.get('code'))
|
token = exchange_github_code_for_token(request.args.get('code'))
|
||||||
|
@ -252,7 +258,7 @@ def github_oauth_attach():
|
||||||
return redirect(url_for('user'))
|
return redirect(url_for('user'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/confirm', methods=['GET'])
|
@web.route('/confirm', methods=['GET'])
|
||||||
def confirm_email():
|
def confirm_email():
|
||||||
code = request.values['code']
|
code = request.values['code']
|
||||||
user = None
|
user = None
|
||||||
|
@ -268,7 +274,7 @@ def confirm_email():
|
||||||
return redirect(url_for('user', tab='email') if new_email else url_for('index'))
|
return redirect(url_for('user', tab='email') if new_email else url_for('index'))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/recovery', methods=['GET'])
|
@web.route('/recovery', methods=['GET'])
|
||||||
def confirm_recovery():
|
def confirm_recovery():
|
||||||
code = request.values['code']
|
code = request.values['code']
|
||||||
user = model.validate_reset_code(code)
|
user = model.validate_reset_code(code)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import logging
|
import logging
|
||||||
import stripe
|
import stripe
|
||||||
|
|
||||||
from flask import request, make_response
|
from flask import request, make_response, Blueprint
|
||||||
|
|
||||||
from data import model
|
from data import model
|
||||||
from app import app
|
from app import app
|
||||||
|
@ -11,8 +11,9 @@ from util.email import send_invoice_email
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
webhooks = Blueprint('webhooks', __name__)
|
||||||
|
|
||||||
@app.route('/webhooks/stripe', methods=['POST'])
|
@webhooks.route('/stripe', methods=['POST'])
|
||||||
def stripe_webhook():
|
def stripe_webhook():
|
||||||
request_data = request.get_json()
|
request_data = request.get_json()
|
||||||
logger.debug('Stripe webhook call: %s' % request_data)
|
logger.debug('Stripe webhook call: %s' % request_data)
|
||||||
|
|
|
@ -2512,6 +2512,10 @@ quayApp.directive('ngBlur', function() {
|
||||||
|
|
||||||
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout',
|
quayApp.run(['$location', '$rootScope', 'Restangular', 'UserService', 'PlanService', '$http', '$timeout',
|
||||||
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout) {
|
function($location, $rootScope, Restangular, UserService, PlanService, $http, $timeout) {
|
||||||
|
|
||||||
|
// Handle session security.
|
||||||
|
Restangular.setDefaultRequestParams({'_csrf_token': window.__token || ''});
|
||||||
|
|
||||||
// Handle session expiration.
|
// Handle session expiration.
|
||||||
Restangular.setErrorInterceptor(function(response) {
|
Restangular.setErrorInterceptor(function(response) {
|
||||||
if (response.status == 401) {
|
if (response.status == 401) {
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.__endpoints = {{ route_data|safe }}.endpoints;
|
window.__endpoints = {{ route_data|safe }}.endpoints;
|
||||||
|
window.__token = '{{ csrf_token() }}';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script src="static/js/app.js"></script>
|
<script src="static/js/app.js"></script>
|
||||||
|
|
Reference in a new issue