Merge branch 'security'

Conflicts:
	endpoints/api.py
	endpoints/web.py
This commit is contained in:
yackob03 2014-01-23 14:51:43 -05:00
commit 845985c859
10 changed files with 202 additions and 153 deletions

View file

@ -4,7 +4,7 @@ import requests
import urlparse
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.principal import identity_changed, AnonymousIdentity
from functools import wraps
@ -29,13 +29,25 @@ from endpoints.common import common_login
from util.cache import cache_control
from datetime import datetime, timedelta
store = app.config['STORAGE']
user_files = app.config['USERFILES']
logger = logging.getLogger(__name__)
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():
global route_data
if route_data:
@ -43,14 +55,14 @@ def get_route_data():
routes = []
for rule in app.url_map.iter_rules():
if rule.rule.startswith('/api/'):
endpoint_method = globals()[rule.endpoint]
if rule.endpoint.startswith('api.'):
endpoint_method = globals()[rule.endpoint[4:]] # Remove api.
is_internal = '__internal_call' in dir(endpoint_method)
is_org_api = '__user_call' in dir(endpoint_method)
methods = list(rule.methods.difference(['HEAD', 'OPTIONS']))
route = {
'name': rule.endpoint,
'name': rule.endpoint[4:],
'methods': methods,
'path': rule.rule,
'parameters': list(rule.arguments)
@ -111,28 +123,19 @@ def org_api_call(user_call_name):
return internal_decorator
@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)
@app.route('/api/discovery')
@api.route('/discovery')
def discovery():
return jsonify(get_route_data())
@app.route('/api/')
@api.route('/')
@internal_api_call
def welcome():
return make_response('welcome', 200)
@app.route('/api/plans/')
@api.route('/plans/')
def list_plans():
return jsonify({
'plans': PLANS,
@ -175,7 +178,7 @@ def user_view(user):
}
@app.route('/api/user/', methods=['GET'])
@api.route('/user/', methods=['GET'])
@internal_api_call
def get_logged_in_user():
if current_user.is_anonymous():
@ -188,7 +191,7 @@ def get_logged_in_user():
return jsonify(user_view(user))
@app.route('/api/user/private', methods=['GET'])
@api.route('/user/private', methods=['GET'])
@api_login_required
@internal_api_call
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
@internal_api_call
def convert_user_to_organization():
@ -246,7 +249,7 @@ def convert_user_to_organization():
return conduct_signin(admin_username, admin_password)
@app.route('/api/user/', methods=['PUT'])
@api.route('/user/', methods=['PUT'])
@api_login_required
@internal_api_call
def change_user_details():
@ -288,7 +291,7 @@ def change_user_details():
return jsonify(user_view(user))
@app.route('/api/user/', methods=['POST'])
@api.route('/user/', methods=['POST'])
@internal_api_call
def create_new_user():
user_data = request.get_json()
@ -315,7 +318,7 @@ def create_new_user():
return error_resp
@app.route('/api/signin', methods=['POST'])
@api.route('/signin', methods=['POST'])
@internal_api_call
def signin_user():
signin_data = request.get_json()
@ -348,7 +351,7 @@ def conduct_signin(username_or_email, password):
return response
@app.route("/api/signout", methods=['POST'])
@api.route("/signout", methods=['POST'])
@api_login_required
@internal_api_call
def logout():
@ -357,7 +360,7 @@ def logout():
return make_response('Success', 200)
@app.route("/api/recovery", methods=['POST'])
@api.route("/recovery", methods=['POST'])
@internal_api_call
def request_recovery_email():
email = request.get_json()['email']
@ -366,7 +369,7 @@ def request_recovery_email():
return make_response('Created', 201)
@app.route('/api/users/<prefix>', methods=['GET'])
@api.route('/users/<prefix>', methods=['GET'])
@api_login_required
def 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
def get_matching_entities(prefix):
teams = []
@ -442,7 +445,7 @@ def team_view(orgname, team):
}
@app.route('/api/organization/', methods=['POST'])
@api.route('/organization/', methods=['POST'])
@api_login_required
@internal_api_call
def create_organization():
@ -491,7 +494,7 @@ def org_view(o, teams):
return view
@app.route('/api/organization/<orgname>', methods=['GET'])
@api.route('/organization/<orgname>', methods=['GET'])
@api_login_required
def get_organization(orgname):
permission = OrganizationMemberPermission(orgname)
@ -507,7 +510,7 @@ def get_organization(orgname):
abort(403)
@app.route('/api/organization/<orgname>', methods=['PUT'])
@api.route('/organization/<orgname>', methods=['PUT'])
@api_login_required
@org_api_call('change_user_details')
def change_organization_details(orgname):
@ -701,7 +704,7 @@ def update_organization_prototype_permission(orgname, prototypeid):
abort(403)
@app.route('/api/organization/<orgname>/members', methods=['GET'])
@api.route('/organization/<orgname>/members', methods=['GET'])
@api_login_required
def get_organization_members(orgname):
permission = AdministerOrganizationPermission(orgname)
@ -730,7 +733,7 @@ def get_organization_members(orgname):
abort(403)
@app.route('/api/organization/<orgname>/members/<membername>', methods=['GET'])
@api.route('/organization/<orgname>/members/<membername>', methods=['GET'])
@api_login_required
def get_organization_member(orgname, membername):
permission = AdministerOrganizationPermission(orgname)
@ -759,7 +762,7 @@ def get_organization_member(orgname, membername):
abort(403)
@app.route('/api/organization/<orgname>/private', methods=['GET'])
@api.route('/organization/<orgname>/private', methods=['GET'])
@api_login_required
@internal_api_call
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'])
@api_login_required
def update_organization_team(orgname, teamname):
@ -841,7 +844,7 @@ def update_organization_team(orgname, teamname):
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>',
@api.route('/organization/<orgname>/team/<teamname>',
methods=['DELETE'])
@api_login_required
def delete_organization_team(orgname, teamname):
@ -854,7 +857,7 @@ def delete_organization_team(orgname, teamname):
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>/members',
@api.route('/organization/<orgname>/team/<teamname>/members',
methods=['GET'])
@api_login_required
def get_organization_team_members(orgname, teamname):
@ -877,7 +880,7 @@ def get_organization_team_members(orgname, teamname):
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>/members/<membername>',
@api.route('/organization/<orgname>/team/<teamname>/members/<membername>',
methods=['PUT', 'POST'])
@api_login_required
def update_organization_team_member(orgname, teamname, membername):
@ -906,7 +909,7 @@ def update_organization_team_member(orgname, teamname, membername):
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>/members/<membername>',
@api.route('/organization/<orgname>/team/<teamname>/members/<membername>',
methods=['DELETE'])
@api_login_required
def delete_organization_team_member(orgname, teamname, membername):
@ -922,7 +925,7 @@ def delete_organization_team_member(orgname, teamname, membername):
abort(403)
@app.route('/api/repository', methods=['POST'])
@api.route('/repository', methods=['POST'])
@api_login_required
def create_repo():
owner = current_user.db_user()
@ -956,7 +959,7 @@ def create_repo():
abort(403)
@app.route('/api/find/repository', methods=['GET'])
@api.route('/find/repository', methods=['GET'])
def find_repos():
prefix = request.args.get('query', '')
@ -979,7 +982,7 @@ def find_repos():
return jsonify(response)
@app.route('/api/repository/', methods=['GET'])
@api.route('/repository/', methods=['GET'])
def list_repos():
def repo_view(repo_obj):
return {
@ -1039,7 +1042,7 @@ def list_repos():
return jsonify(response)
@app.route('/api/repository/<path:repository>', methods=['PUT'])
@api.route('/repository/<path:repository>', methods=['PUT'])
@api_login_required
@parse_repository_name
def update_repo(namespace, repository):
@ -1061,7 +1064,7 @@ def update_repo(namespace, repository):
abort(403)
@app.route('/api/repository/<path:repository>/changevisibility',
@api.route('/repository/<path:repository>/changevisibility',
methods=['POST'])
@api_login_required
@parse_repository_name
@ -1082,7 +1085,7 @@ def change_repo_visibility(namespace, repository):
abort(403)
@app.route('/api/repository/<path:repository>', methods=['DELETE'])
@api.route('/repository/<path:repository>', methods=['DELETE'])
@api_login_required
@parse_repository_name
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
def get_repo(namespace, repository):
logger.debug('Get repo: %s/%s' % (namespace, repository))
@ -1157,7 +1160,7 @@ def get_repo(namespace, repository):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/build/', methods=['GET'])
@api.route('/repository/<path:repository>/build/', methods=['GET'])
@api_login_required
@parse_repository_name
def get_repo_builds(namespace, repository):
@ -1190,7 +1193,7 @@ def get_repo_builds(namespace, repository):
abort(403) # Permissions denied
@app.route('/api/repository/<path:repository>/build/', methods=['POST'])
@api.route('/repository/<path:repository>/build/', methods=['POST'])
@api_login_required
@parse_repository_name
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
@parse_repository_name
def create_webhook(namespace, repository):
@ -1248,7 +1251,7 @@ def create_webhook(namespace, repository):
abort(403) # Permissions denied
@app.route('/api/repository/<path:repository>/webhook/<public_id>',
@api.route('/repository/<path:repository>/webhook/<public_id>',
methods=['GET'])
@api_login_required
@parse_repository_name
@ -1261,7 +1264,7 @@ def get_webhook(namespace, repository, public_id):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/webhook/', methods=['GET'])
@api.route('/repository/<path:repository>/webhook/', methods=['GET'])
@api_login_required
@parse_repository_name
def list_webhooks(namespace, repository):
@ -1275,7 +1278,7 @@ def list_webhooks(namespace, repository):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/webhook/<public_id>',
@api.route('/repository/<path:repository>/webhook/<public_id>',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@ -1291,7 +1294,7 @@ def delete_webhook(namespace, repository, public_id):
abort(403) # Permission denied
@app.route('/api/filedrop/', methods=['POST'])
@api.route('/filedrop/', methods=['POST'])
@api_login_required
@internal_api_call
def get_filedrop_url():
@ -1318,7 +1321,7 @@ def wrap_role_view_org(role_json, user, org_members):
return role_json
@app.route('/api/repository/<path:repository>/image/', methods=['GET'])
@api.route('/repository/<path:repository>/image/', methods=['GET'])
@parse_repository_name
def list_repository_images(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
@ -1343,7 +1346,7 @@ def list_repository_images(namespace, repository):
abort(403)
@app.route('/api/repository/<path:repository>/image/<image_id>',
@api.route('/repository/<path:repository>/image/<image_id>',
methods=['GET'])
@parse_repository_name
def get_image(namespace, repository, image_id):
@ -1357,7 +1360,7 @@ def get_image(namespace, repository, image_id):
abort(403)
@app.route('/api/repository/<path:repository>/image/<image_id>/changes',
@api.route('/repository/<path:repository>/image/<image_id>/changes',
methods=['GET'])
@cache_control(max_age=60*60) # Cache for one hour
@parse_repository_name
@ -1375,7 +1378,7 @@ def get_image_changes(namespace, repository, image_id):
abort(403)
@app.route('/api/repository/<path:repository>/tag/<tag>',
@api.route('/api/repository/<path:repository>/tag/<tag>',
methods=['DELETE'])
@parse_repository_name
def delete_full_tag(namespace, repository, tag):
@ -1393,7 +1396,7 @@ def delete_full_tag(namespace, repository, tag):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tag/<tag>/images',
@api.route('/repository/<path:repository>/tag/<tag>/images',
methods=['GET'])
@parse_repository_name
def list_tag_images(namespace, repository, tag):
@ -1417,7 +1420,7 @@ def list_tag_images(namespace, repository, tag):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/team/',
@api.route('/repository/<path:repository>/permissions/team/',
methods=['GET'])
@api_login_required
@parse_repository_name
@ -1434,7 +1437,7 @@ def list_repo_team_permissions(namespace, repository):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/',
@api.route('/repository/<path:repository>/permissions/user/',
methods=['GET'])
@api_login_required
@parse_repository_name
@ -1475,7 +1478,7 @@ def list_repo_user_permissions(namespace, repository):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
@api.route('/repository/<path:repository>/permissions/user/<username>',
methods=['GET'])
@api_login_required
@parse_repository_name
@ -1500,7 +1503,7 @@ def get_user_permissions(namespace, repository, username):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
@api.route('/repository/<path:repository>/permissions/team/<teamname>',
methods=['GET'])
@api_login_required
@parse_repository_name
@ -1515,7 +1518,7 @@ def get_team_permissions(namespace, repository, teamname):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
@api.route('/repository/<path:repository>/permissions/user/<username>',
methods=['PUT', 'POST'])
@api_login_required
@parse_repository_name
@ -1558,7 +1561,7 @@ def change_user_permissions(namespace, repository, username):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
@api.route('/repository/<path:repository>/permissions/team/<teamname>',
methods=['PUT', 'POST'])
@api_login_required
@parse_repository_name
@ -1586,7 +1589,7 @@ def change_team_permissions(namespace, repository, teamname):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
@api.route('/repository/<path:repository>/permissions/user/<username>',
methods=['DELETE'])
@api_login_required
@parse_repository_name
@ -1611,7 +1614,7 @@ def delete_user_permissions(namespace, repository, username):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
@api.route('/repository/<path:repository>/permissions/team/<teamname>',
methods=['DELETE'])
@api_login_required
@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
@parse_repository_name
def list_repo_tokens(namespace, repository):
@ -1652,7 +1655,7 @@ def list_repo_tokens(namespace, repository):
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
@parse_repository_name
def get_tokens(namespace, repository, code):
@ -1664,7 +1667,7 @@ def get_tokens(namespace, repository, code):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tokens/', methods=['POST'])
@api.route('/repository/<path:repository>/tokens/', methods=['POST'])
@api_login_required
@parse_repository_name
def create_token(namespace, repository):
@ -1686,7 +1689,7 @@ def create_token(namespace, repository):
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
@parse_repository_name
def change_token(namespace, repository, code):
@ -1711,7 +1714,7 @@ def change_token(namespace, repository, code):
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tokens/<code>',
@api.route('/repository/<path:repository>/tokens/<code>',
methods=['DELETE'])
@api_login_required
@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
@internal_api_call
def get_user_card():
@ -1747,7 +1750,7 @@ def get_user_card():
return get_card(user)
@app.route('/api/organization/<orgname>/card', methods=['GET'])
@api.route('/organization/<orgname>/card', methods=['GET'])
@api_login_required
@internal_api_call
@org_api_call('get_user_card')
@ -1760,7 +1763,7 @@ def get_org_card(orgname):
abort(403)
@app.route('/api/user/card', methods=['POST'])
@api.route('/user/card', methods=['POST'])
@api_login_required
@internal_api_call
def set_user_card():
@ -1771,7 +1774,7 @@ def set_user_card():
return response
@app.route('/api/organization/<orgname>/card', methods=['POST'])
@api.route('/organization/<orgname>/card', methods=['POST'])
@api_login_required
@org_api_call('set_user_card')
def set_org_card(orgname):
@ -1823,7 +1826,7 @@ def get_card(user):
return jsonify({'card': card_info})
@app.route('/api/user/plan', methods=['PUT'])
@api.route('/user/plan', methods=['PUT'])
@api_login_required
@internal_api_call
def update_user_subscription():
@ -1916,7 +1919,7 @@ def subscribe(user, plan, token, require_business_plan):
return resp
@app.route('/api/user/invoices', methods=['GET'])
@api.route('/user/invoices', methods=['GET'])
@api_login_required
def list_user_invoices():
user = current_user.db_user()
@ -1926,7 +1929,7 @@ def list_user_invoices():
return get_invoices(user.stripe_id)
@app.route('/api/organization/<orgname>/invoices', methods=['GET'])
@api.route('/organization/<orgname>/invoices', methods=['GET'])
@api_login_required
@org_api_call('list_user_invoices')
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
@internal_api_call
@org_api_call('update_user_subscription')
@ -1979,7 +1982,7 @@ def update_org_subscription(orgname):
abort(403)
@app.route('/api/user/plan', methods=['GET'])
@api.route('/user/plan', methods=['GET'])
@api_login_required
@internal_api_call
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
@internal_api_call
@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
def get_user_robots():
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
@org_api_call('get_user_robots')
def get_org_robots(orgname):
@ -2052,7 +2055,7 @@ def get_org_robots(orgname):
abort(403)
@app.route('/api/user/robots/<robot_shortname>', methods=['PUT'])
@api.route('/user/robots/<robot_shortname>', methods=['PUT'])
@api_login_required
def create_user_robot(robot_shortname):
parent = current_user.db_user()
@ -2063,7 +2066,7 @@ def create_user_robot(robot_shortname):
return resp
@app.route('/api/organization/<orgname>/robots/<robot_shortname>',
@api.route('/organization/<orgname>/robots/<robot_shortname>',
methods=['PUT'])
@api_login_required
@org_api_call('create_user_robot')
@ -2080,7 +2083,7 @@ def create_org_robot(orgname, robot_shortname):
abort(403)
@app.route('/api/user/robots/<robot_shortname>', methods=['DELETE'])
@api.route('/user/robots/<robot_shortname>', methods=['DELETE'])
@api_login_required
def delete_user_robot(robot_shortname):
parent = current_user.db_user()
@ -2089,7 +2092,7 @@ def delete_user_robot(robot_shortname):
return make_response('No Content', 204)
@app.route('/api/organization/<orgname>/robots/<robot_shortname>',
@api.route('/organization/<orgname>/robots/<robot_shortname>',
methods=['DELETE'])
@api_login_required
@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
@parse_repository_name
def list_repo_logs(namespace, repository):
@ -2139,7 +2142,7 @@ def list_repo_logs(namespace, repository):
abort(403)
@app.route('/api/organization/<orgname>/logs', methods=['GET'])
@api.route('/organization/<orgname>/logs', methods=['GET'])
@api_login_required
@org_api_call('list_user_logs')
def list_org_logs(orgname):
@ -2155,7 +2158,7 @@ def list_org_logs(orgname):
abort(403)
@app.route('/api/user/logs', methods=['GET'])
@api.route('/user/logs', methods=['GET'])
@api_login_required
def list_user_logs():
performer_name = request.args.get('performer', None)