Migrate teams and orgs.

This commit is contained in:
jakedt 2014-03-14 14:20:51 -04:00
parent ab60a10a93
commit e4e4f8c553
5 changed files with 375 additions and 2 deletions

View file

@ -181,6 +181,8 @@ import endpoints.api.repository
import endpoints.api.repotoken
import endpoints.api.search
import endpoints.api.tag
import endpoints.api.team
import endpoints.api.trigger
import endpoints.api.organization
import endpoints.api.user
import endpoints.api.webhook

View file

@ -407,6 +407,7 @@ def team_view(orgname, team):
}
# Ported
@api_bp.route('/organization/', methods=['POST'])
@api_login_required
@internal_api_call
@ -454,6 +455,7 @@ def org_view(o, teams):
return view
# Ported
@api_bp.route('/organization/<orgname>', methods=['GET'])
@api_login_required
def get_organization(orgname):
@ -470,6 +472,7 @@ def get_organization(orgname):
abort(403)
# Ported
@api_bp.route('/organization/<orgname>', methods=['PUT'])
@api_login_required
@org_api_call('change_user_details')
@ -718,6 +721,7 @@ def get_organization_member(orgname, membername):
abort(403)
# Ported
@api_bp.route('/organization/<orgname>/private', methods=['GET'])
@api_login_required
@internal_api_call
@ -758,6 +762,7 @@ def member_view(member):
}
# Ported
@api_bp.route('/organization/<orgname>/team/<teamname>',
methods=['PUT', 'POST'])
@api_login_required
@ -804,6 +809,7 @@ def update_organization_team(orgname, teamname):
abort(403)
# Ported
@api_bp.route('/organization/<orgname>/team/<teamname>',
methods=['DELETE'])
@api_login_required
@ -817,6 +823,7 @@ def delete_organization_team(orgname, teamname):
abort(403)
# Ported
@api_bp.route('/organization/<orgname>/team/<teamname>/members',
methods=['GET'])
@api_login_required
@ -840,6 +847,7 @@ def get_organization_team_members(orgname, teamname):
abort(403)
# Ported
@api_bp.route('/organization/<orgname>/team/<teamname>/members/<membername>',
methods=['PUT', 'POST'])
@api_login_required
@ -869,6 +877,7 @@ def update_organization_team_member(orgname, teamname, membername):
abort(403)
# Ported
@api_bp.route('/organization/<orgname>/team/<teamname>/members/<membername>',
methods=['DELETE'])
@api_login_required

View file

@ -0,0 +1,184 @@
import logging
import stripe
from flask import request
from flask.ext.restful import abort
from endpoints.api import resource, nickname, ApiResource, validate_json_request, request_error
from endpoints.api.team import team_view
from auth.permissions import (AdministerOrganizationPermission, OrganizationMemberPermission,
CreateRepositoryPermission)
from auth.auth_context import get_authenticated_user
from data import model
from data.plans import get_plan
from util.gravatar import compute_hash
logger = logging.getLogger(__name__)
def org_view(o, teams):
admin_org = AdministerOrganizationPermission(o.username)
is_admin = admin_org.can()
view = {
'name': o.username,
'email': o.email if is_admin else '',
'gravatar': compute_hash(o.email),
'teams': {t.name : team_view(o.username, t) for t in teams},
'is_admin': is_admin
}
if is_admin:
view['invoice_email'] = o.invoice_email
return view
@resource('/v1/organization/', methods=['POST'])
class OrganizationList(ApiResource):
""" Resource for creating organizations. """
schemas = {
'NewOrg': {
'id': 'NewOrg',
'type': 'object',
'description': 'Description of a new organization.',
'required': True,
'properties': {
'name': {
'type': 'string',
'description': 'Organization username',
'required': True,
},
'email': {
'type': 'string',
'description': 'Organization contact email',
'required': True,
},
},
},
}
@nickname('createOrganization')
@validate_json_request('NewOrg')
def post(self):
""" Create a new organization. """
org_data = request.get_json()
existing = None
try:
existing = model.get_organization(org_data['name'])
except model.InvalidOrganizationException:
pass
if not existing:
try:
existing = model.get_user(org_data['name'])
except model.InvalidUserException:
pass
if existing:
msg = 'A user or organization with this name already exists'
return request_error(message=msg)
try:
model.create_organization(org_data['name'], org_data['email'], get_authenticated_user())
return 'Created', 201
except model.DataModelException as ex:
return request_error(exception=ex)
@resource('/v1/organization/<orgname>', methods=['GET'])
class Organization(ApiResource):
""" Resource for managing organizations. """
schemas = {
'UpdateOrg': {
'id': 'UpdateOrg',
'type': 'object',
'description': 'Description of updates for an existing organization',
'required': True,
'properties': {
'email': {
'type': 'string',
'description': 'Organization contact email',
},
'invoice_email': {
'type': 'boolean',
'description': 'Whether the organization desires to receive emails for invoices',
},
},
},
}
@nickname('getOrganization')
def get(self, orgname):
""" Get the details for the specified organization """
permission = OrganizationMemberPermission(orgname)
if permission.can():
try:
org = model.get_organization(orgname)
except model.InvalidOrganizationException:
abort(404)
teams = model.get_teams_within_org(org)
return org_view(org, teams)
abort(403)
# @org_api_call('change_user_details')
@nickname('changeOrganizationDetails')
@validate_json_request('UpdateOrg')
def put(self, orgname):
""" Change the details for the specified organization. """
try:
org = model.get_organization(orgname)
except model.InvalidOrganizationException:
abort(404)
org_data = request.get_json()
if 'invoice_email' in org_data:
logger.debug('Changing invoice_email for organization: %s', org.username)
model.change_invoice_email(org, org_data['invoice_email'])
if 'email' in org_data and org_data['email'] != org.email:
new_email = org_data['email']
if model.find_user_by_email(new_email):
return request_error(message='E-mail address already used')
logger.debug('Changing email address for organization: %s', org.username)
model.update_email(org, new_email)
teams = model.get_teams_within_org(org)
return org_view(org, teams)
@resource('/v1/organization/<orgname>/private')
class OrgPrivateRepositories(ApiResource):
""" Custom verb to compute whether additional private repositories are available. """
# @org_api_call('get_user_private_allowed')
@nickname('getOrganizationPrivateAllowed')
def get(self, orgname):
permission = CreateRepositoryPermission(orgname)
if permission.can():
organization = model.get_organization(orgname)
private_repos = model.get_private_repo_count(organization.username)
data = {
'privateAllowed': False
}
if organization.stripe_id:
cus = stripe.Customer.retrieve(organization.stripe_id)
if cus.subscription:
repos_allowed = 0
plan = get_plan(cus.subscription.plan.id)
if plan:
repos_allowed = plan['privateRepos']
data['privateAllowed'] = (private_repos < repos_allowed)
if AdministerOrganizationPermission(orgname).can():
data['privateCount'] = private_repos
return data
abort(403)

View file

@ -31,7 +31,7 @@ class RepositoryTokenList(RepositoryParamResource):
'properties': {
'friendlyName': {
'type': 'string',
'description': 'Friendly name to help identify the token.',
'description': 'Friendly name to help identify the token',
'required': True,
},
},
@ -72,7 +72,7 @@ class RepositoryToken(RepositoryParamResource):
'TokenPermission': {
'id': 'TokenPermission',
'type': 'object',
'description': 'Description of a token permission.',
'description': 'Description of a token permission',
'required': True,
'properties': {
'role': {

178
endpoints/api/team.py Normal file
View file

@ -0,0 +1,178 @@
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, ViewTeamPermission
from auth.auth_context import get_authenticated_user
from data import model
def team_view(orgname, team):
view_permission = ViewTeamPermission(orgname, team.name)
role = model.get_team_org_role(team).name
return {
'id': team.id,
'name': team.name,
'description': team.description,
'can_view': view_permission.can(),
'role': role
}
def member_view(member):
return {
'name': member.username,
'kind': 'user',
'is_robot': member.robot,
}
@resource('/v1/organization/<orgname>/team/<teamname>',
methods=['PUT', 'POST'])
class OrganizationTeam(ApiResource):
""" Resource for manging an organization's teams. """
schemas = {
'TeamDescription': {
'id': 'TeamDescription',
'type': 'object',
'description': 'Description of a team',
'required': True,
'properties': {
'role': {
'type': 'string',
'description': 'Org wide permissions that should apply to the team',
'required': True,
'enum': [
'member',
'creator',
'admin',
],
},
'description': {
'type': 'string',
'description': 'Markdown description for the team',
'required': True,
},
},
},
}
@nickname('updateOrganizationTeam')
@validate_json_request('TeamDescription')
def put(self, orgname, teamname):
""" Update the org-wide permission for the specified team. """
edit_permission = AdministerOrganizationPermission(orgname)
if edit_permission.can():
team = None
details = request.get_json()
is_existing = False
try:
team = model.get_organization_team(orgname, teamname)
is_existing = True
except model.InvalidTeamException:
# Create the new team.
description = details['description'] if 'description' in details else ''
role = details['role'] if 'role' in details else 'member'
org = model.get_organization(orgname)
team = model.create_team(teamname, org, role, description)
log_action('org_create_team', orgname, {'team': teamname})
if is_existing:
if ('description' in details and
team.description != details['description']):
team.description = details['description']
team.save()
log_action('org_set_team_description', orgname,
{'team': teamname, 'description': team.description})
if 'role' in details:
role = model.get_team_org_role(team).name
if role != details['role']:
team = model.set_team_org_permission(team, details['role'],
get_authenticated_user().username)
log_action('org_set_team_role', orgname, {'team': teamname, 'role': details['role']})
return team_view(orgname, team), 200 # 201 for post
abort(403)
@nickname('deleteOrganizationTeam')
def delete(self, orgname, teamname):
""" Delete the specified team. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
model.remove_team(orgname, teamname, get_authenticated_user().username)
log_action('org_delete_team', orgname, {'team': teamname})
return 'Deleted', 204
abort(403)
@resource('/v1/organization/<orgname>/team/<teamname>/members')
class TeamMemberList(ApiResource):
""" Resource for managing the list of members for a team. """
@nickname('getOrganizationTeamMembers')
def get(self, orgname, teamname):
""" Retrieve the list of members for the specified team. """
view_permission = ViewTeamPermission(orgname, teamname)
edit_permission = AdministerOrganizationPermission(orgname)
if view_permission.can():
team = None
try:
team = model.get_organization_team(orgname, teamname)
except model.InvalidTeamException:
abort(404)
members = model.get_organization_team_members(team.id)
return {
'members': {m.username : member_view(m) for m in members},
'can_edit': edit_permission.can()
}
abort(403)
@resource('/v1/organization/<orgname>/team/<teamname>/members/<membername>')
class TeamMember(ApiResource):
""" Resource for managing individual members of a team. """
@nickname('updateOrganizationTeamMember')
def put(self, orgname, teamname, membername):
""" Add a member to an existing team. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
team = None
user = None
# Find the team.
try:
team = model.get_organization_team(orgname, teamname)
except model.InvalidTeamException:
abort(404)
# Find the user.
user = model.get_user(membername)
if not user:
return request_error(message='Unknown user')
# Add the user to the team.
model.add_user_to_team(user, team)
log_action('org_add_team_member', orgname, {'member': membername, 'team': teamname})
return member_view(user)
abort(403)
@nickname('deleteOrganizationTeamMember')
def delete(self, orgname, teamname, membername):
""" Delete an existing member of a team. """
permission = AdministerOrganizationPermission(orgname)
if permission.can():
# Remote the user from the team.
invoking_user = get_authenticated_user().username
model.remove_user_from_team(orgname, teamname, membername, invoking_user)
log_action('org_remove_team_member', orgname, {'member': membername, 'team': teamname})
return 'Deleted', 204
abort(403)