2013-09-23 16:37:40 +00:00
|
|
|
import logging
|
|
|
|
|
|
|
|
from flask import request, make_response, jsonify, abort
|
|
|
|
from flask.ext.login import login_required, current_user
|
|
|
|
from functools import wraps
|
|
|
|
|
2013-09-25 16:45:12 +00:00
|
|
|
from data import model
|
2013-09-23 16:37:40 +00:00
|
|
|
from app import app
|
2013-09-25 20:46:28 +00:00
|
|
|
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)
|
2013-09-23 16:37:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2013-09-27 22:16:26 +00:00
|
|
|
@app.errorhandler(model.DataModelException)
|
|
|
|
def handle_dme(ex):
|
|
|
|
return make_response(ex.message, 400)
|
|
|
|
|
|
|
|
|
2013-09-23 16:37:40 +00:00
|
|
|
@app.route('/api/')
|
|
|
|
def welcome():
|
|
|
|
return make_response('welcome', 200)
|
|
|
|
|
|
|
|
|
2013-09-26 23:59:58 +00:00
|
|
|
@app.route('/api/user/')
|
|
|
|
@login_required
|
|
|
|
def get_logged_in_user():
|
|
|
|
user = current_user.db_user
|
|
|
|
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),
|
2013-09-26 23:59:58 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
|
2013-09-27 23:21:54 +00:00
|
|
|
@app.route('/api/users/<prefix>', methods=['GET'])
|
|
|
|
def get_matching_users(prefix):
|
|
|
|
users = model.get_matching_users(prefix)
|
|
|
|
|
|
|
|
return jsonify({
|
|
|
|
'users': [user.username for user in users]
|
|
|
|
})
|
|
|
|
|
|
|
|
|
2013-09-23 16:37:40 +00:00
|
|
|
@app.route('/api/repository/', methods=['POST'])
|
|
|
|
@login_required
|
|
|
|
def create_repo_api():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2013-09-27 23:21:54 +00:00
|
|
|
@app.route('/api/repository/find/<prefix>', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
def match_repos_api(prefix):
|
|
|
|
def repo_view(repo):
|
|
|
|
return {
|
|
|
|
'namespace': repo.namespace,
|
|
|
|
'name': repo.name,
|
2013-09-28 21:11:10 +00:00
|
|
|
'description': repo.description
|
2013-09-27 23:21:54 +00:00
|
|
|
}
|
|
|
|
|
2013-09-28 03:25:57 +00:00
|
|
|
username = current_user.db_user.username
|
|
|
|
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'])
|
|
|
|
@login_required
|
|
|
|
def list_repos_api():
|
2013-09-28 04:05:32 +00:00
|
|
|
def repo_view(repo_obj):
|
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,
|
2013-09-23 16:37:40 +00:00
|
|
|
}
|
|
|
|
|
2013-09-28 04:05:32 +00:00
|
|
|
username = current_user.db_user.username
|
2013-09-25 20:46:28 +00:00
|
|
|
repos = [repo_view(repo)
|
2013-09-28 04:05:32 +00:00
|
|
|
for repo in model.get_visible_repositories(username)]
|
2013-09-23 16:37:40 +00:00
|
|
|
response = {
|
|
|
|
'repositories': repos
|
|
|
|
}
|
|
|
|
|
|
|
|
return jsonify(response)
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/api/repository/<path:repository>', methods=['PUT'])
|
|
|
|
@login_required
|
|
|
|
@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-09-28 21:11:10 +00:00
|
|
|
@app.route('/api/repository/<path:repository>/changevisibility', methods=['POST'])
|
|
|
|
@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)
|
|
|
|
|
|
|
|
|
2013-09-27 21:01:45 +00:00
|
|
|
def image_view(image):
|
|
|
|
return {
|
|
|
|
'id': image.image_id,
|
|
|
|
'created': image.created,
|
|
|
|
'comment': image.comment,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-23 16:37:40 +00:00
|
|
|
@app.route('/api/repository/<path:repository>', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@parse_repository_name
|
|
|
|
def get_repo_api(namespace, repository):
|
2013-09-27 18:56:14 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
permission = ReadRepositoryPermission(namespace, repository)
|
2013-09-28 21:11:10 +00:00
|
|
|
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()
|
2013-09-27 18:56:14 +00:00
|
|
|
can_admin = AdministerRepositoryPermission(namespace, repository).can()
|
2013-09-27 17:24:07 +00:00
|
|
|
return jsonify({
|
|
|
|
'namespace': namespace,
|
|
|
|
'name': repository,
|
|
|
|
'description': repo.description,
|
|
|
|
'tags': tag_dict,
|
|
|
|
'can_write': can_write,
|
2013-09-27 18:56:14 +00:00
|
|
|
'can_admin': can_admin,
|
2013-09-28 21:11:10 +00:00
|
|
|
'is_public': is_public
|
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
|
|
|
|
|
|
|
|
|
2013-09-27 18:56:14 +00:00
|
|
|
def role_view(repo_perm_obj):
|
|
|
|
return {
|
|
|
|
'role': repo_perm_obj.role.name
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-28 00:03:07 +00:00
|
|
|
@app.route('/api/repository/<path:repository>/tag/<tag>/images',
|
|
|
|
methods=['GET'])
|
2013-09-27 21:01:45 +00:00
|
|
|
@login_required
|
|
|
|
@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):
|
2013-09-27 21:01:45 +00:00
|
|
|
images = model.get_tag_images(namespace, repository, tag)
|
2013-09-28 00:03:07 +00:00
|
|
|
|
2013-09-27 21:01:45 +00:00
|
|
|
return jsonify({
|
|
|
|
'images': [image_view(image) for image in images]
|
|
|
|
})
|
|
|
|
|
|
|
|
abort(403) # Permission denied
|
|
|
|
|
|
|
|
|
2013-09-27 17:24:07 +00:00
|
|
|
@app.route('/api/repository/<path:repository>/permissions/', methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@parse_repository_name
|
|
|
|
def list_repo_permissions(namespace, repository):
|
|
|
|
permission = AdministerRepositoryPermission(namespace, repository)
|
|
|
|
if permission.can():
|
|
|
|
repo_perms = model.get_all_repo_users(namespace, repository)
|
|
|
|
|
|
|
|
return jsonify({
|
2013-09-28 00:03:07 +00:00
|
|
|
'permissions': {repo_perm.user.username: role_view(repo_perm)
|
2013-09-27 17:24:07 +00:00
|
|
|
for repo_perm in repo_perms}
|
|
|
|
})
|
|
|
|
|
|
|
|
abort(403) # Permission denied
|
|
|
|
|
2013-09-27 18:56:14 +00:00
|
|
|
|
|
|
|
@app.route('/api/repository/<path:repository>/permissions/<username>',
|
|
|
|
methods=['GET'])
|
|
|
|
@login_required
|
|
|
|
@parse_repository_name
|
|
|
|
def get_permissions(namespace, repository, username):
|
|
|
|
logger.debug('Get repo: %s/%s permissions for user %s' %
|
|
|
|
(namespace, repository, username))
|
|
|
|
permission = AdministerRepositoryPermission(namespace, repository)
|
|
|
|
if permission.can():
|
2013-09-27 19:53:39 +00:00
|
|
|
perm = model.get_user_reponame_permission(username, namespace, repository)
|
2013-09-27 18:56:14 +00:00
|
|
|
return jsonify(role_view(perm))
|
|
|
|
|
|
|
|
abort(403) # Permission denied
|
|
|
|
|
|
|
|
|
2013-09-27 17:24:07 +00:00
|
|
|
@app.route('/api/repository/<path:repository>/permissions/<username>',
|
2013-09-27 18:56:14 +00:00
|
|
|
methods=['PUT', 'POST'])
|
2013-09-27 17:24:07 +00:00
|
|
|
@login_required
|
|
|
|
@parse_repository_name
|
|
|
|
def change_permissions(namespace, repository, username):
|
|
|
|
permission = AdministerRepositoryPermission(namespace, repository)
|
|
|
|
if permission.can():
|
|
|
|
new_permission = request.get_json()
|
|
|
|
|
2013-09-27 18:56:14 +00:00
|
|
|
logger.debug('Setting permission to: %s for user %s' %
|
|
|
|
(new_permission['role'], username))
|
2013-09-27 19:53:39 +00:00
|
|
|
|
|
|
|
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)
|
2013-09-27 18:56:14 +00:00
|
|
|
|
|
|
|
resp = jsonify(role_view(perm))
|
|
|
|
if request.method == 'POST':
|
|
|
|
resp.status_code = 201
|
|
|
|
return resp
|
|
|
|
|
|
|
|
abort(403) # Permission denied
|
|
|
|
|
2013-09-28 00:03:07 +00:00
|
|
|
|
2013-09-27 18:56:14 +00:00
|
|
|
@app.route('/api/repository/<path:repository>/permissions/<username>',
|
|
|
|
methods=['DELETE'])
|
|
|
|
@login_required
|
|
|
|
@parse_repository_name
|
|
|
|
def delete_permissions(namespace, repository, username):
|
|
|
|
permission = AdministerRepositoryPermission(namespace, repository)
|
|
|
|
if permission.can():
|
2013-09-27 19:53:39 +00:00
|
|
|
try:
|
|
|
|
model.delete_user_permission(username, namespace, repository)
|
|
|
|
except model.DataModelException:
|
|
|
|
logger.warning('User tried to remove themselves as admin.')
|
|
|
|
abort(409)
|
|
|
|
|
2013-09-27 18:56:14 +00:00
|
|
|
return make_response('Deleted', 204)
|
2013-09-27 17:24:07 +00:00
|
|
|
|
2013-09-27 18:56:14 +00:00
|
|
|
abort(403) # Permission denied
|