Port permission prototypes and org members.
This commit is contained in:
parent
e4e4f8c553
commit
a667714d3d
4 changed files with 319 additions and 0 deletions
|
@ -177,6 +177,7 @@ import endpoints.api.build
|
||||||
import endpoints.api.discovery
|
import endpoints.api.discovery
|
||||||
import endpoints.api.image
|
import endpoints.api.image
|
||||||
import endpoints.api.permission
|
import endpoints.api.permission
|
||||||
|
import endpoints.api.prototype
|
||||||
import endpoints.api.repository
|
import endpoints.api.repository
|
||||||
import endpoints.api.repotoken
|
import endpoints.api.repotoken
|
||||||
import endpoints.api.search
|
import endpoints.api.search
|
||||||
|
|
|
@ -526,6 +526,7 @@ def prototype_view(proto, org_members):
|
||||||
'id': proto.uuid,
|
'id': proto.uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Ported
|
||||||
@api_bp.route('/organization/<orgname>/prototypes', methods=['GET'])
|
@api_bp.route('/organization/<orgname>/prototypes', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_organization_prototype_permissions(orgname):
|
def get_organization_prototype_permissions(orgname):
|
||||||
|
@ -564,6 +565,7 @@ def log_prototype_action(action_kind, orgname, prototype, **kwargs):
|
||||||
log_action(action_kind, orgname, log_params)
|
log_action(action_kind, orgname, log_params)
|
||||||
|
|
||||||
|
|
||||||
|
# Ported
|
||||||
@api_bp.route('/organization/<orgname>/prototypes', methods=['POST'])
|
@api_bp.route('/organization/<orgname>/prototypes', methods=['POST'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def create_organization_prototype_permission(orgname):
|
def create_organization_prototype_permission(orgname):
|
||||||
|
@ -612,6 +614,7 @@ def create_organization_prototype_permission(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
# Ported
|
||||||
@api_bp.route('/organization/<orgname>/prototypes/<prototypeid>',
|
@api_bp.route('/organization/<orgname>/prototypes/<prototypeid>',
|
||||||
methods=['DELETE'])
|
methods=['DELETE'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
|
@ -634,6 +637,7 @@ def delete_organization_prototype_permission(orgname, prototypeid):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
# Ported
|
||||||
@api_bp.route('/organization/<orgname>/prototypes/<prototypeid>',
|
@api_bp.route('/organization/<orgname>/prototypes/<prototypeid>',
|
||||||
methods=['PUT'])
|
methods=['PUT'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
|
@ -663,6 +667,7 @@ def update_organization_prototype_permission(orgname, prototypeid):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
# Ported
|
||||||
@api_bp.route('/organization/<orgname>/members', methods=['GET'])
|
@api_bp.route('/organization/<orgname>/members', methods=['GET'])
|
||||||
@api_login_required
|
@api_login_required
|
||||||
def get_organization_members(orgname):
|
def get_organization_members(orgname):
|
||||||
|
@ -692,6 +697,7 @@ def get_organization_members(orgname):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
# Ported
|
||||||
@api_bp.route('/organization/<orgname>/members/<membername>', methods=['GET'])
|
@api_bp.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):
|
||||||
|
|
|
@ -182,3 +182,67 @@ class OrgPrivateRepositories(ApiResource):
|
||||||
return data
|
return data
|
||||||
|
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
@resource('/v1/organization/<orgname>/members')
|
||||||
|
class OrgnaizationMemberList(ApiResource):
|
||||||
|
""" Resource for listing the members of an organization. """
|
||||||
|
@nickname('getOrganizationMembers')
|
||||||
|
def get(self, orgname):
|
||||||
|
""" List the members of the specified organization. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
try:
|
||||||
|
org = model.get_organization(orgname)
|
||||||
|
except model.InvalidOrganizationException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
# Loop to create the members dictionary. Note that the members collection
|
||||||
|
# will return an entry for *every team* a member is on, so we will have
|
||||||
|
# duplicate keys (which is why we pre-build the dictionary).
|
||||||
|
members_dict = {}
|
||||||
|
members = model.get_organization_members_with_teams(org)
|
||||||
|
for member in members:
|
||||||
|
if not member.user.username in members_dict:
|
||||||
|
members_dict[member.user.username] = {'name': member.user.username,
|
||||||
|
'kind': 'user',
|
||||||
|
'is_robot': member.user.robot,
|
||||||
|
'teams': []}
|
||||||
|
|
||||||
|
members_dict[member.user.username]['teams'].append(member.team.name)
|
||||||
|
|
||||||
|
return {'members': members_dict}
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
@resource('/v1/organization/<orgname>/members/<membername>')
|
||||||
|
class OrganizationMember(ApiResource):
|
||||||
|
""" Resource for managing individual organization members. """
|
||||||
|
@nickname('getOrganizationMember')
|
||||||
|
def get(self, orgname, membername):
|
||||||
|
""" Get information on the specific orgnaization member. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
try:
|
||||||
|
org = model.get_organization(orgname)
|
||||||
|
except model.InvalidOrganizationException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
member_dict = None
|
||||||
|
member_teams = model.get_organization_members_with_teams(org, membername=membername)
|
||||||
|
for member in member_teams:
|
||||||
|
if not member_dict:
|
||||||
|
member_dict = {'name': member.user.username,
|
||||||
|
'kind': 'user',
|
||||||
|
'is_robot': member.user.robot,
|
||||||
|
'teams': []}
|
||||||
|
|
||||||
|
member_dict['teams'].append(member.team.name)
|
||||||
|
|
||||||
|
if not member_dict:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
return {'member': member_dict}
|
||||||
|
|
||||||
|
abort(403)
|
248
endpoints/api/prototype.py
Normal file
248
endpoints/api/prototype.py
Normal file
|
@ -0,0 +1,248 @@
|
||||||
|
from flask import request
|
||||||
|
from flask.ext.restful import abort
|
||||||
|
|
||||||
|
from endpoints.api import (resource, nickname, ApiResource, validate_json_request, request_error,
|
||||||
|
log_action)
|
||||||
|
from auth.permissions import AdministerOrganizationPermission
|
||||||
|
from auth.auth_context import get_authenticated_user
|
||||||
|
from data import model
|
||||||
|
|
||||||
|
|
||||||
|
def prototype_view(proto, org_members):
|
||||||
|
def prototype_user_view(user):
|
||||||
|
return {
|
||||||
|
'name': user.username,
|
||||||
|
'is_robot': user.robot,
|
||||||
|
'kind': 'user',
|
||||||
|
'is_org_member': user.robot or user.username in org_members,
|
||||||
|
}
|
||||||
|
|
||||||
|
if proto.delegate_user:
|
||||||
|
delegate_view = prototype_user_view(proto.delegate_user)
|
||||||
|
else:
|
||||||
|
delegate_view = {
|
||||||
|
'name': proto.delegate_team.name,
|
||||||
|
'kind': 'team',
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'activating_user': (prototype_user_view(proto.activating_user)
|
||||||
|
if proto.activating_user else None),
|
||||||
|
'delegate': delegate_view,
|
||||||
|
'role': proto.role.name,
|
||||||
|
'id': proto.uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
def log_prototype_action(action_kind, orgname, prototype, **kwargs):
|
||||||
|
username = get_authenticated_user().username
|
||||||
|
log_params = {
|
||||||
|
'prototypeid': prototype.uuid,
|
||||||
|
'username': username,
|
||||||
|
'activating_username': (prototype.activating_user.username
|
||||||
|
if prototype.activating_user else None),
|
||||||
|
'role': prototype.role.name
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
log_params[key] = value
|
||||||
|
|
||||||
|
if prototype.delegate_user:
|
||||||
|
log_params['delegate_user'] = prototype.delegate_user.username
|
||||||
|
elif prototype.delegate_team:
|
||||||
|
log_params['delegate_team'] = prototype.delegate_team.name
|
||||||
|
|
||||||
|
log_action(action_kind, orgname, log_params)
|
||||||
|
|
||||||
|
|
||||||
|
@resource('/v1/organization/<orgname>/prototypes')
|
||||||
|
class PermissionPrototypeList(ApiResource):
|
||||||
|
""" Resource for listing and creating permission prototypes. """
|
||||||
|
schemas = {
|
||||||
|
'NewPrototype': {
|
||||||
|
'id': 'NewPrototype',
|
||||||
|
'type': 'object',
|
||||||
|
'description': 'Description of a new prototype',
|
||||||
|
'required': True,
|
||||||
|
'properties': {
|
||||||
|
'role': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'Role that should be applied to the delegate',
|
||||||
|
'required': True,
|
||||||
|
'enum': [
|
||||||
|
'read',
|
||||||
|
'write',
|
||||||
|
'admin',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'activating_user': {
|
||||||
|
'type': 'object',
|
||||||
|
'description': 'Repository creating user to whom the rule should apply',
|
||||||
|
'properties': {
|
||||||
|
'name': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The username for the activating_user',
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'delegate': {
|
||||||
|
'type': 'object',
|
||||||
|
'description': 'Information about the user or team to which the rule grants access',
|
||||||
|
'required': True,
|
||||||
|
'properties': {
|
||||||
|
'name': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The name for the delegate team or user',
|
||||||
|
'required': True,
|
||||||
|
},
|
||||||
|
'kind': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'Whether the delegate is a user or a team',
|
||||||
|
'required': True,
|
||||||
|
'enum': [
|
||||||
|
'user',
|
||||||
|
'team',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@nickname('getOrganizationPrototypePermissions')
|
||||||
|
def get(self, orgname):
|
||||||
|
""" List the existing prototypes for this organization. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
try:
|
||||||
|
org = model.get_organization(orgname)
|
||||||
|
except model.InvalidOrganizationException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
permissions = model.get_prototype_permissions(org)
|
||||||
|
org_members = model.get_organization_member_set(orgname)
|
||||||
|
return {'prototypes': [prototype_view(p, org_members) for p in permissions]}
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
@nickname('createOrganizationPrototypePermission')
|
||||||
|
@validate_json_request('NewPrototype')
|
||||||
|
def post(self, orgname):
|
||||||
|
""" Create a new permission prototype. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
try:
|
||||||
|
org = model.get_organization(orgname)
|
||||||
|
except model.InvalidOrganizationException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
details = request.get_json()
|
||||||
|
activating_username = None
|
||||||
|
|
||||||
|
if ('activating_user' in details and details['activating_user'] and
|
||||||
|
'name' in details['activating_user']):
|
||||||
|
activating_username = details['activating_user']['name']
|
||||||
|
|
||||||
|
delegate = details['delegate'] if 'delegate' in details else {}
|
||||||
|
delegate_kind = delegate.get('kind', None)
|
||||||
|
delegate_name = delegate.get('name', None)
|
||||||
|
|
||||||
|
delegate_username = delegate_name if delegate_kind == 'user' else None
|
||||||
|
delegate_teamname = delegate_name if delegate_kind == 'team' else None
|
||||||
|
|
||||||
|
activating_user = (model.get_user(activating_username)
|
||||||
|
if activating_username else None)
|
||||||
|
delegate_user = (model.get_user(delegate_username)
|
||||||
|
if delegate_username else None)
|
||||||
|
delegate_team = (model.get_organization_team(orgname, delegate_teamname)
|
||||||
|
if delegate_teamname else None)
|
||||||
|
|
||||||
|
if activating_username and not activating_user:
|
||||||
|
return request_error(message='Unknown activating user')
|
||||||
|
|
||||||
|
if not delegate_user and not delegate_team:
|
||||||
|
return request_error(message='Missing delegate user or team')
|
||||||
|
|
||||||
|
role_name = details['role']
|
||||||
|
|
||||||
|
prototype = model.add_prototype_permission(org, role_name, activating_user,
|
||||||
|
delegate_user, delegate_team)
|
||||||
|
log_prototype_action('create_prototype_permission', orgname, prototype)
|
||||||
|
org_members = model.get_organization_member_set(orgname)
|
||||||
|
return prototype_view(prototype, org_members)
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
@resource('/v1/organization/<orgname>/prototypes/<prototypeid>')
|
||||||
|
class PermissionPrototype(ApiResource):
|
||||||
|
""" Resource for managingin individual permission prototypes. """
|
||||||
|
schemas = {
|
||||||
|
'PrototypeUpdate': {
|
||||||
|
'id': 'PrototypeUpdate',
|
||||||
|
'type': 'object',
|
||||||
|
'description': 'Description of a the new prototype role',
|
||||||
|
'required': True,
|
||||||
|
'properties': {
|
||||||
|
'role': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'Role that should be applied to the permission',
|
||||||
|
'required': True,
|
||||||
|
'enum': [
|
||||||
|
'read',
|
||||||
|
'write',
|
||||||
|
'admin',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@nickname('deleteOrganizationPrototypePermission')
|
||||||
|
def delete(self, orgname, prototypeid):
|
||||||
|
""" Delete an existing permission prototype. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
try:
|
||||||
|
org = model.get_organization(orgname)
|
||||||
|
except model.InvalidOrganizationException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
prototype = model.delete_prototype_permission(org, prototypeid)
|
||||||
|
if not prototype:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
log_prototype_action('delete_prototype_permission', orgname, prototype)
|
||||||
|
|
||||||
|
return 'Deleted', 204
|
||||||
|
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
@nickname('updateOrganizationPrototypePermission')
|
||||||
|
@validate_json_request('PrototypeUpdate')
|
||||||
|
def put(self, orgname, prototypeid):
|
||||||
|
""" Update the role of an existing permission prototype. """
|
||||||
|
permission = AdministerOrganizationPermission(orgname)
|
||||||
|
if permission.can():
|
||||||
|
try:
|
||||||
|
org = model.get_organization(orgname)
|
||||||
|
except model.InvalidOrganizationException:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
existing = model.get_prototype_permission(org, prototypeid)
|
||||||
|
if not existing:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
details = request.get_json()
|
||||||
|
role_name = details['role']
|
||||||
|
prototype = model.update_prototype_permission(org, prototypeid, role_name)
|
||||||
|
if not prototype:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
log_prototype_action('modify_prototype_permission', orgname, prototype,
|
||||||
|
original_role=existing.role.name)
|
||||||
|
org_members = model.get_organization_member_set(orgname)
|
||||||
|
return prototype_view(prototype, org_members)
|
||||||
|
|
||||||
|
abort(403)
|
Reference in a new issue