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

1118 lines
31 KiB
Python
Raw Normal View History

2013-09-23 16:37:40 +00:00
import logging
2013-10-02 04:48:03 +00:00
import stripe
import re
import requests
import urlparse
import json
2013-09-23 16:37:40 +00:00
from flask import request, make_response, jsonify, abort, url_for
from flask.ext.login import login_required, current_user, logout_user
from flask.ext.principal import identity_changed, AnonymousIdentity
2013-09-23 16:37:40 +00:00
from functools import wraps
from collections import defaultdict
2013-09-23 16:37:40 +00:00
import storage
from data import model
from data.userfiles import UserRequestFiles
from data.queue import dockerfile_build_queue
2013-11-05 19:40:45 +00:00
from data.plans import USER_PLANS, BUSINESS_PLANS, get_plan
2013-09-23 16:37:40 +00:00
from app import app
from util.email import send_confirmation_email, send_recovery_email
from util.names import parse_repository_name
2013-09-27 22:15:31 +00:00
from util.gravatar import compute_hash
2013-09-26 21:59:20 +00:00
from auth.permissions import (ReadRepositoryPermission,
2013-09-27 17:24:07 +00:00
ModifyRepositoryPermission,
AdministerRepositoryPermission,
CreateRepositoryPermission,
AdministerOrganizationPermission,
OrganizationMemberPermission,
ViewTeamPermission)
2013-10-01 18:46:44 +00:00
from endpoints import registry
from endpoints.web import common_login
from util.cache import cache_control
2013-09-23 16:37:40 +00:00
store = storage.load()
2013-09-23 16:37:40 +00:00
logger = logging.getLogger(__name__)
def api_login_required(f):
@wraps(f)
def decorated_view(*args, **kwargs):
if not current_user.is_authenticated():
abort(401)
return f(*args, **kwargs)
return decorated_view
@app.errorhandler(model.DataModelException)
def handle_dme(ex):
return make_response(ex.message, 400)
2013-11-05 19:40:45 +00:00
2013-09-23 16:37:40 +00:00
@app.route('/api/')
def welcome():
return make_response('welcome', 200)
2013-11-05 19:40:45 +00:00
@app.route('/api/plans/')
def plans_list():
2013-11-05 19:40:45 +00:00
return jsonify({
'user': USER_PLANS,
'business': BUSINESS_PLANS,
})
2013-09-23 16:37:40 +00:00
@app.route('/api/user/', methods=['GET'])
def get_logged_in_user():
def org_view(o):
admin_org = AdministerOrganizationPermission(o.username)
return {
'name': o.username,
'gravatar': compute_hash(o.email),
'is_org_admin': admin_org.can()
}
2013-09-30 21:32:03 +00:00
if current_user.is_anonymous():
return jsonify({'anonymous': True})
user = current_user.db_user()
organizations = model.get_user_organizations(user.username)
return jsonify({
'verified': user.verified,
'anonymous': False,
'username': user.username,
'email': user.email,
2013-09-27 22:15:31 +00:00
'gravatar': compute_hash(user.email),
'askForPassword': user.password_hash is None,
'organizations': [org_view(o) for o in organizations]
})
@app.route('/api/user/', methods=['PUT'])
@api_login_required
def change_user_details():
user = current_user.db_user()
user_data = request.get_json();
try:
if user_data['password']:
logger.debug('Changing password for user: %s', user.username)
model.change_password(user, user_data['password'])
except model.InvalidPasswordException, ex:
error_resp = jsonify({
'message': ex.message,
})
error_resp.status_code = 400
return error_resp
return jsonify({
'verified': user.verified,
'anonymous': False,
'username': user.username,
'email': user.email,
'gravatar': compute_hash(user.email),
'askForPassword': user.password_hash is None,
})
2013-11-05 19:40:45 +00:00
@app.route('/api/user/', methods=['POST'])
def create_user_api():
user_data = request.get_json()
existing_user = model.get_user(user_data['username'])
if existing_user:
error_resp = jsonify({
'message': 'The username already exists'
})
error_resp.status_code = 400
return error_resp
try:
new_user = model.create_user(user_data['username'], user_data['password'],
user_data['email'])
code = model.create_confirm_email_code(new_user)
send_confirmation_email(new_user.username, new_user.email, code.code)
return make_response('Created', 201)
except model.DataModelException as ex:
error_resp = jsonify({
'message': ex.message,
})
error_resp.status_code = 400
return error_resp
@app.route('/api/signin', methods=['POST'])
def signin_api():
signin_data = request.get_json()
username = signin_data['username']
password = signin_data['password']
#TODO Allow email login
needs_email_verification = False
invalid_credentials = False
verified = model.verify_user(username, password)
if verified:
if common_login(verified):
return make_response('Success', 200)
else:
needs_email_verification = True
else:
invalid_credentials = True
response = jsonify({
'needsEmailVerification': needs_email_verification,
'invalidCredentials': invalid_credentials,
})
response.status_code = 403
return response
@app.route("/api/signout", methods=['POST'])
@api_login_required
def logout():
logout_user()
identity_changed.send(app, identity=AnonymousIdentity())
return make_response('Success', 200)
@app.route("/api/recovery", methods=['POST'])
def send_recovery():
email = request.get_json()['email']
code = model.create_reset_password_email_code(email)
send_recovery_email(email, code.code)
return make_response('Created', 201)
2013-09-27 23:21:54 +00:00
@app.route('/api/users/<prefix>', methods=['GET'])
@api_login_required
2013-09-27 23:21:54 +00:00
def get_matching_users(prefix):
users = model.get_matching_users(prefix)
return jsonify({
'users': [user.username for user in users]
})
@app.route('/api/entities/<prefix>', methods=['GET'])
@api_login_required
def get_matching_entities(prefix):
teams = []
organization_name = request.args.get('organization', None)
organization = None
if organization_name:
permission = OrganizationMemberPermission(organization_name)
if permission.can():
try:
organization = model.get_organization(organization_name)
except:
pass
if organization:
teams = model.get_matching_teams(prefix, organization)
users = model.get_matching_users(prefix, organization)
def team_view(team):
2013-11-05 03:58:21 +00:00
result = {
'name': team.name,
2013-11-05 00:06:56 +00:00
'kind': 'team',
2013-11-05 03:58:21 +00:00
'is_org_member': True
}
def user_view(user):
return {
'name': user.username,
'kind': 'user',
'is_org_member': user.is_org_member,
}
team_data = [team_view(team) for team in teams]
user_data = [user_view(user) for user in users]
return jsonify({
'results': team_data + user_data
})
user_files = UserRequestFiles(app.config['AWS_ACCESS_KEY'],
app.config['AWS_SECRET_KEY'],
app.config['REGISTRY_S3_BUCKET'])
def team_view(orgname, t):
view_permission = ViewTeamPermission(orgname, t.name)
2013-11-05 03:58:21 +00:00
role = model.get_team_org_role(t).name
return {
'id': t.id,
'name': t.name,
'description': t.description,
2013-11-05 03:58:21 +00:00
'can_view': view_permission.can(),
'role': role
}
2013-11-05 19:40:45 +00:00
@app.route('/api/organization/<orgname>', methods=['GET'])
def get_organization(orgname):
2013-11-04 21:39:29 +00:00
user = current_user.db_user()
def org_view(o, teams):
2013-11-04 21:51:25 +00:00
admin_org = AdministerOrganizationPermission(orgname)
2013-11-05 03:58:21 +00:00
is_admin = admin_org.can()
return {
'name': o.username,
'gravatar': compute_hash(o.email),
'teams': {t.name : team_view(orgname, t) for t in teams},
2013-11-05 03:58:21 +00:00
'is_admin': is_admin
}
if current_user.is_anonymous():
abort(404)
org = model.get_organization(orgname)
if not org:
abort(404)
2013-11-04 21:39:29 +00:00
teams = model.get_teams_within_org(org)
return jsonify(org_view(org, teams))
@app.route('/api/organization/<orgname>/private', methods=['GET'])
def get_organization_private_allowed(orgname):
permission = CreateRepositoryPermission(orgname)
if permission.can():
2013-11-05 19:53:48 +00:00
organization = model.get_organization(orgname)
private_repos = model.get_private_repo_count(organization.username)
if organization.stripe_id:
cus = stripe.Customer.retrieve(organization.stripe_id)
if cus.subscription:
repos_allowed = get_plan(cus.subscription.plan.id)
return jsonify({
'privateAllowed': (private_repos < repos_allowed)
})
return jsonify({
'privateAllowed': False
})
abort(403)
def member_view(m):
return {
'username': m.username
}
@app.route('/api/organization/<orgname>/team/<teamname>',
methods=['PUT', 'POST'])
def update_organization_team(orgname, teamname):
edit_permission = AdministerOrganizationPermission(orgname)
if edit_permission.can():
team = None
2013-11-05 03:58:21 +00:00
json = request.get_json()
is_existing = False
try:
team = model.get_organization_team(orgname, teamname)
2013-11-05 03:58:21 +00:00
is_existing = True
except:
2013-11-05 03:58:21 +00:00
# Create the new team.
description = json['description'] if 'description' in json else ''
role = json['role'] if 'role' in json else 'member'
team = model.create_team(teamname, orgname, role, description)
if is_existing:
if 'description' in json:
team.description = json['description']
team.save()
if 'role' in json:
team = model.set_team_org_permission(team, json['role'])
return jsonify(team_view(orgname, team))
abort(403)
2013-11-05 19:53:48 +00:00
@app.route('/api/organization/<orgname>/team/<teamname>',
methods=['DELETE'])
def delete_organization_team(orgname, teamname):
edit_permission = AdministerOrganizationPermission(orgname)
if edit_permission.can():
model.remove_team(teamname, orgname)
return make_response('Deleted', 204)
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>/members',
methods=['GET'])
def get_organization_team_members(orgname, teamname):
view_permission = ViewTeamPermission(orgname, teamname)
edit_permission = AdministerOrganizationPermission(orgname)
if view_permission.can():
user = current_user.db_user()
team = None
try:
team = model.get_organization_team(orgname, teamname)
except:
abort(404)
members = model.get_organization_team_members(team.id)
return jsonify({
'members': { m.username : member_view(m) for m in members },
'can_edit': edit_permission.can()
})
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>/members/<membername>',
methods=['PUT', 'POST'])
def update_organization_team_member(orgname, teamname, membername):
permission = AdministerOrganizationPermission(orgname)
if permission.can():
team = None
user = None
# Find the team.
try:
team = model.get_organization_team(orgname, teamname)
except:
abort(404)
# Find the user.
user = model.get_user(membername)
if not user:
abort(400)
# Add the user to the team.
model.add_user_to_team(user, team)
return jsonify(member_view(user))
abort(403)
@app.route('/api/organization/<orgname>/team/<teamname>/members/<membername>',
methods=['DELETE'])
def delete_organization_team_member(orgname, teamname, membername):
permission = AdministerOrganizationPermission(orgname)
if permission.can():
team = None
user = None
# Find the team.
try:
team = model.get_organization_team(orgname, teamname)
except:
abort(404)
# Find the user.
user = model.get_user(membername)
if not user:
abort(400)
# Remote the user from the team.
model.remove_user_from_team(user, team)
2013-11-05 19:53:48 +00:00
return make_response('Deleted', 204)
abort(403)
@app.route('/api/repository', methods=['POST'])
@api_login_required
2013-09-23 16:37:40 +00:00
def create_repo_api():
owner = current_user.db_user()
json = request.get_json()
namespace_name = json['namespace'] if 'namespace' in json else owner.username
permission = CreateRepositoryPermission(namespace_name)
if permission.can():
repository_name = json['repository']
visibility = json['visibility']
existing = model.get_repository(namespace_name, repository_name)
if existing:
return make_response('Repository already exists', 400)
visibility = request.get_json()['visibility']
repo = model.create_repository(namespace_name, repository_name, owner,
visibility)
repo.description = json['description']
repo.save()
return jsonify({
'namespace': namespace_name,
'name': repository_name
})
abort(403)
2013-09-23 16:37:40 +00:00
@app.route('/api/find/repository', methods=['GET'])
def match_repos_api():
prefix = request.args.get('query', '')
2013-10-08 15:29:42 +00:00
2013-09-27 23:21:54 +00:00
def repo_view(repo):
return {
'namespace': repo.namespace,
'name': repo.name,
'description': repo.description
2013-09-27 23:21:54 +00:00
}
2013-10-08 15:29:42 +00:00
username = None
if current_user.is_authenticated():
username = current_user.db_user().username
2013-10-08 15:29:42 +00:00
2013-09-28 03:25:57 +00:00
matching = model.get_matching_repositories(prefix, username)
2013-09-27 23:21:54 +00:00
response = {
2013-09-28 03:25:57 +00:00
'repositories': [repo_view(repo) for repo in matching]
2013-09-27 23:21:54 +00:00
}
return jsonify(response)
2013-09-26 21:59:20 +00:00
2013-09-23 16:37:40 +00:00
@app.route('/api/repository/', methods=['GET'])
def list_repos_api():
2013-09-28 04:05:32 +00:00
def repo_view(repo_obj):
is_public = model.repository_is_public(repo_obj.namespace, repo_obj.name)
2013-09-23 16:37:40 +00:00
return {
2013-09-28 04:05:32 +00:00
'namespace': repo_obj.namespace,
'name': repo_obj.name,
'description': repo_obj.description,
'is_public': is_public
2013-09-23 16:37:40 +00:00
}
2013-10-08 15:29:42 +00:00
limit = request.args.get('limit', None)
namespace_filter = request.args.get('namespace', None)
include_public = request.args.get('public', 'true')
include_private = request.args.get('private', 'true')
sort = request.args.get('sort', 'false')
2013-09-23 16:37:40 +00:00
try:
limit = int(limit) if limit else None
except:
limit = None
include_public = include_public == 'true'
include_private = include_private == 'true'
sort = sort == 'true'
2013-10-08 15:29:42 +00:00
username = None
if current_user.is_authenticated() and include_private:
username = current_user.db_user().username
2013-10-08 15:29:42 +00:00
repo_query = model.get_visible_repositories(username, limit=limit,
include_public=include_public,
sort=sort, namespace=namespace_filter)
2013-10-08 15:29:42 +00:00
repos = [repo_view(repo) for repo in repo_query]
2013-09-23 16:37:40 +00:00
response = {
'repositories': repos
}
return jsonify(response)
@app.route('/api/repository/<path:repository>', methods=['PUT'])
@api_login_required
2013-09-23 16:37:40 +00:00
@parse_repository_name
def update_repo_api(namespace, repository):
2013-09-26 21:59:20 +00:00
permission = ModifyRepositoryPermission(namespace, repository)
2013-09-28 00:03:07 +00:00
if permission.can():
2013-09-26 21:59:20 +00:00
repo = model.get_repository(namespace, repository)
if repo:
values = request.get_json()
repo.description = values['description']
repo.save()
return jsonify({
'success': True
})
2013-09-28 00:03:07 +00:00
2013-09-26 21:59:20 +00:00
abort(404)
2013-09-23 16:37:40 +00:00
2013-10-08 15:29:42 +00:00
@app.route('/api/repository/<path:repository>/changevisibility',
methods=['POST'])
@api_login_required
@parse_repository_name
def change_repo_visibility_api(namespace, repository):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
repo = model.get_repository(namespace, repository)
if repo:
values = request.get_json()
model.set_repository_visibility(repo, values['visibility'])
return jsonify({
'success': True
})
abort(404)
@app.route('/api/repository/<path:repository>', methods=['DELETE'])
@api_login_required
@parse_repository_name
def delete_repository(namespace, repository):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
model.purge_repository(namespace, repository)
2013-10-01 18:46:44 +00:00
registry.delete_repository_storage(namespace, repository)
return make_response('Deleted', 204)
abort(404)
def image_view(image):
return {
'id': image.docker_image_id,
'created': image.created,
'comment': image.comment,
'ancestors': image.ancestors,
'dbid': image.id,
}
2013-09-23 16:37:40 +00:00
@app.route('/api/repository/<path:repository>', methods=['GET'])
@parse_repository_name
def get_repo_api(namespace, repository):
logger.debug('Get repo: %s/%s' % (namespace, repository))
2013-09-28 00:03:07 +00:00
2013-09-26 21:59:20 +00:00
def tag_view(tag):
image = model.get_tag_image(namespace, repository, tag.name)
if not image:
return {}
return {
'name': tag.name,
2013-09-27 17:24:07 +00:00
'image': image_view(image),
2013-09-26 21:59:20 +00:00
}
organization = None
try:
organization = model.get_organization(namespace)
except:
pass
2013-09-26 21:59:20 +00:00
permission = ReadRepositoryPermission(namespace, repository)
is_public = model.repository_is_public(namespace, repository)
if permission.can() or is_public:
2013-09-26 21:59:20 +00:00
repo = model.get_repository(namespace, repository)
if repo:
tags = model.list_repository_tags(namespace, repository)
2013-09-27 17:24:07 +00:00
tag_dict = {tag.name: tag_view(tag) for tag in tags}
can_write = ModifyRepositoryPermission(namespace, repository).can()
can_admin = AdministerRepositoryPermission(namespace, repository).can()
active_builds = model.list_repository_builds(namespace, repository,
include_inactive=False)
2013-09-27 17:24:07 +00:00
return jsonify({
'namespace': namespace,
'name': repository,
'description': repo.description,
'tags': tag_dict,
'can_write': can_write,
'can_admin': can_admin,
'is_public': is_public,
'is_building': len(active_builds) > 0,
'is_organization': bool(organization)
2013-09-27 17:24:07 +00:00
})
2013-09-26 21:59:20 +00:00
2013-09-27 17:24:07 +00:00
abort(404) # Not fount
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/build/', methods=['GET'])
2013-10-26 21:41:29 +00:00
@api_login_required
@parse_repository_name
def get_repo_builds(namespace, repository):
permission = ModifyRepositoryPermission(namespace, repository)
if permission.can():
def build_view(build_obj):
if build_obj.status_url:
# Delegate the status to the build node
node_status = requests.get(build_obj.status_url).json()
node_status['id'] = build_obj.id
return node_status
# If there was no status url, do the best we can
return {
'id': build_obj.id,
'total_commands': None,
'total_images': None,
'current_command': None,
'current_image': None,
'image_completion_percent': None,
'status': build_obj.phase,
'message': None,
}
builds = model.list_repository_builds(namespace, repository)
return jsonify({
'builds': [build_view(build) for build in builds]
})
abort(403) # Permissions denied
@app.route('/api/filedrop/', methods=['POST'])
def get_filedrop_url():
2013-10-26 22:37:53 +00:00
mime_type = request.get_json()['mimeType']
(url, file_id) = user_files.prepare_for_drop(mime_type)
return jsonify({
'url': url,
'file_id': file_id
})
@app.route('/api/repository/<path:repository>/build/', methods=['POST'])
2013-10-26 21:41:29 +00:00
@api_login_required
@parse_repository_name
def request_repo_build(namespace, repository):
permission = ModifyRepositoryPermission(namespace, repository)
if permission.can():
logger.debug('User requested repository initialization.')
dockerfile_id = request.get_json()['file_id']
repo = model.get_repository(namespace, repository)
token = model.create_access_token(repo, 'write')
host = urlparse.urlparse(request.url).netloc
tag = '%s/%s/%s' % (host, repo.namespace, repo.name)
build_request = model.create_repository_build(repo, token, dockerfile_id,
tag)
dockerfile_build_queue.put(json.dumps({'build_id': build_request.id}))
return jsonify({
'started': True
})
abort(403) # Permissions denied
def role_view_org(repo_perm_obj, org_member):
return {
'role': repo_perm_obj.role.name,
'is_org_member': org_member,
}
def role_view(repo_perm_obj):
return {
'role': repo_perm_obj.role.name,
}
@app.route('/api/repository/<path:repository>/image/', methods=['GET'])
@parse_repository_name
def list_repository_images(namespace, repository):
permission = ReadRepositoryPermission(namespace, repository)
if permission.can() or model.repository_is_public(namespace, repository):
all_images = model.get_repository_images(namespace, repository)
all_tags = model.list_repository_tags(namespace, repository)
tags_by_image_id = defaultdict(list)
for tag in all_tags:
tags_by_image_id[tag.image.docker_image_id].append(tag.name)
def add_tags(image_json):
image_json['tags'] = tags_by_image_id[image_json['id']]
return image_json
return jsonify({
'images': [add_tags(image_view(image)) for image in all_images]
})
abort(403)
@app.route('/api/repository/<path:repository>/image/<image_id>',
methods=['GET'])
@parse_repository_name
def get_image(namespace, repository, image_id):
permission = ReadRepositoryPermission(namespace, repository)
if permission.can() or model.repository_is_public(namespace, repository):
image = model.get_repo_image(namespace, repository, image_id)
if not image:
abort(404)
return jsonify(image_view(image))
abort(403)
@app.route('/api/repository/<path:repository>/image/<image_id>/changes',
methods=['GET'])
@cache_control(max_age=60*60) # Cache for one hour
@parse_repository_name
def get_image_changes(namespace, repository, image_id):
permission = ReadRepositoryPermission(namespace, repository)
if permission.can() or model.repository_is_public(namespace, repository):
diffs_path = store.image_file_diffs_path(namespace, repository, image_id)
try:
response_json = store.get_content(diffs_path)
return make_response(response_json)
except IOError:
abort(404)
abort(403)
2013-09-28 00:03:07 +00:00
@app.route('/api/repository/<path:repository>/tag/<tag>/images',
methods=['GET'])
@parse_repository_name
def list_tag_images(namespace, repository, tag):
permission = ReadRepositoryPermission(namespace, repository)
2013-09-28 04:05:32 +00:00
if permission.can() or model.repository_is_public(namespace, repository):
tag_image = model.get_tag_image(namespace, repository, tag)
parent_images = model.get_parent_images(tag_image)
parents = list(parent_images)
parents.reverse()
all_images = [tag_image] + parents
2013-09-28 00:03:07 +00:00
return jsonify({
'images': [image_view(image) for image in all_images]
})
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/team/',
methods=['GET'])
@api_login_required
@parse_repository_name
def list_repo_team_permissions(namespace, repository):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
repo_perms = model.get_all_repo_teams(namespace, repository)
return jsonify({
'permissions': {repo_perm.team.name: role_view(repo_perm)
for repo_perm in repo_perms}
})
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/',
methods=['GET'])
@api_login_required
2013-09-27 17:24:07 +00:00
@parse_repository_name
def list_repo_user_permissions(namespace, repository):
2013-09-27 17:24:07 +00:00
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
repo_perms = model.get_all_repo_users(namespace, repository)
org_members = model.get_organization_member_set(namespace)
def process_perm(repo_perm):
return (repo_perm.user.username,
role_view_org(repo_perm,
repo_perm.user.username in org_members))
2013-09-27 17:24:07 +00:00
return jsonify({
'permissions': dict(process_perm(perm) for perm in repo_perms)
2013-09-27 17:24:07 +00:00
})
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
methods=['GET'])
@api_login_required
@parse_repository_name
def get_user_permissions(namespace, repository, username):
logger.debug('Get repo: %s/%s permissions for user %s' %
(namespace, repository, username))
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
perm = model.get_user_reponame_permission(username, namespace, repository)
org_members = model.get_organization_member_set(namespace)
return jsonify(role_view_org(perm, perm.user.username in org_members))
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
methods=['GET'])
@api_login_required
@parse_repository_name
def get_team_permissions(namespace, repository, teamname):
logger.debug('Get repo: %s/%s permissions for team %s' %
(namespace, repository, teamname))
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
perm = model.get_team_reponame_permission(username, namespace, repository)
return jsonify(role_view(perm))
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
methods=['PUT', 'POST'])
@api_login_required
2013-09-27 17:24:07 +00:00
@parse_repository_name
def change_user_permissions(namespace, repository, username):
2013-09-27 17:24:07 +00:00
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
new_permission = request.get_json()
logger.debug('Setting permission to: %s for user %s' %
(new_permission['role'], username))
try:
perm = model.set_user_repo_permission(username, namespace, repository,
new_permission['role'])
except model.DataModelException:
logger.warning('User tried to remove themselves as admin.')
abort(409)
org_members = model.get_organization_member_set(namespace)
resp = jsonify(role_view_org(perm, perm.user.username in org_members))
if request.method == 'POST':
resp.status_code = 201
return resp
abort(403) # Permission denied
2013-09-28 00:03:07 +00:00
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
methods=['PUT', 'POST'])
@api_login_required
@parse_repository_name
def change_team_permissions(namespace, repository, teamname):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
new_permission = request.get_json()
logger.debug('Setting permission to: %s for team %s' %
(new_permission['role'], teamname))
try:
perm = model.set_team_repo_permission(teamname, namespace, repository,
new_permission['role'])
except model.DataModelException:
logger.warning('User tried to remove themselves as admin.')
abort(409)
resp = jsonify(role_view(perm))
if request.method == 'POST':
resp.status_code = 201
return resp
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/permissions/user/<username>',
methods=['DELETE'])
@api_login_required
@parse_repository_name
def delete_user_permissions(namespace, repository, username):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
try:
model.delete_user_permission(username, namespace, repository)
except model.DataModelException:
logger.warning('User tried to remove themselves as admin.')
abort(409)
return make_response('Deleted', 204)
2013-09-27 17:24:07 +00:00
abort(403) # Permission denied
2013-10-02 04:48:03 +00:00
@app.route('/api/repository/<path:repository>/permissions/team/<teamname>',
methods=['DELETE'])
@api_login_required
@parse_repository_name
def delete_team_permissions(namespace, repository, teamname):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
try:
model.delete_team_permission(teamname, namespace, repository)
except model.DataModelException:
logger.warning('User tried to remove themselves as admin.')
abort(409)
return make_response('Deleted', 204)
abort(403) # Permission denied
def token_view(token_obj):
return {
'friendlyName': token_obj.friendly_name,
'code': token_obj.code,
'role': token_obj.role.name,
}
@app.route('/api/repository/<path:repository>/tokens/', methods=['GET'])
@api_login_required
@parse_repository_name
def list_repo_tokens(namespace, repository):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
tokens = model.get_repository_delegate_tokens(namespace, repository)
return jsonify({
'tokens': {token.code: token_view(token) for token in tokens}
})
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tokens/<code>', methods=['GET'])
@api_login_required
@parse_repository_name
def get_tokens(namespace, repository, code):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
perm = model.get_repo_delegate_token(namespace, repository, code)
return jsonify(token_view(perm))
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tokens/', methods=['POST'])
@api_login_required
@parse_repository_name
def create_token(namespace, repository):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
token_params = request.get_json()
token = model.create_delegate_token(namespace, repository,
token_params['friendlyName'])
resp = jsonify(token_view(token))
resp.status_code = 201
return resp
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tokens/<code>', methods=['PUT'])
@api_login_required
@parse_repository_name
def change_token(namespace, repository, code):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
new_permission = request.get_json()
logger.debug('Setting permission to: %s for code %s' %
(new_permission['role'], code))
token = model.set_repo_delegate_token_role(namespace, repository, code,
new_permission['role'])
resp = jsonify(token_view(token))
return resp
abort(403) # Permission denied
@app.route('/api/repository/<path:repository>/tokens/<code>',
methods=['DELETE'])
@api_login_required
@parse_repository_name
def delete_token(namespace, repository, code):
permission = AdministerRepositoryPermission(namespace, repository)
if permission.can():
model.delete_delegate_token(namespace, repository, code)
return make_response('Deleted', 204)
abort(403) # Permission denied
2013-10-08 15:29:42 +00:00
def subscription_view(stripe_subscription, used_repos):
2013-10-02 04:48:03 +00:00
return {
'currentPeriodStart': stripe_subscription.current_period_start,
'currentPeriodEnd': stripe_subscription.current_period_end,
2013-10-02 04:48:03 +00:00
'plan': stripe_subscription.plan.id,
'usedPrivateRepos': used_repos,
2013-10-02 04:48:03 +00:00
}
@app.route('/api/user/plan', methods=['PUT'])
@api_login_required
def subscribe():
# Amount in cents
amount = 500
request_data = request.get_json()
plan = request_data['plan']
user = current_user.db_user()
private_repos = model.get_private_repo_count(user.username)
2013-10-02 04:48:03 +00:00
if not user.stripe_id:
# Create the customer and plan simultaneously
card = request_data['token']
2013-10-02 04:48:03 +00:00
cus = stripe.Customer.create(email=user.email, plan=plan, card=card)
user.stripe_id = cus.id
user.save()
2013-10-08 15:29:42 +00:00
resp = jsonify(subscription_view(cus.subscription, private_repos))
2013-10-02 04:48:03 +00:00
resp.status_code = 201
return resp
else:
# Change the plan
cus = stripe.Customer.retrieve(user.stripe_id)
if plan == 'free':
cus.cancel_subscription()
cus.save()
response_json = {
'plan': 'free',
'usedPrivateRepos': private_repos,
}
2013-10-02 04:48:03 +00:00
else:
cus.plan = plan
2013-10-02 04:48:03 +00:00
# User may have been a previous customer who is resubscribing
if 'token' in request_data:
cus.card = request_data['token']
cus.save()
2013-10-02 04:48:03 +00:00
response_json = subscription_view(cus.subscription, private_repos)
return jsonify(response_json)
@app.route('/api/user/plan', methods=['GET'])
@api_login_required
def get_subscription():
user = current_user.db_user()
private_repos = model.get_private_repo_count(user.username)
if user.stripe_id:
cus = stripe.Customer.retrieve(user.stripe_id)
if cus.subscription:
return jsonify(subscription_view(cus.subscription, private_repos))
return jsonify({
'plan': 'free',
'usedPrivateRepos': private_repos,
2013-10-08 15:29:42 +00:00
})