diff --git a/data/model/permission.py b/data/model/permission.py index 52dcf40f1..340ab3835 100644 --- a/data/model/permission.py +++ b/data/model/permission.py @@ -17,14 +17,19 @@ def list_robot_permissions(robot_name): .where(User.username == robot_name, User.robot == True)) -def list_organization_member_permissions(organization): +def list_organization_member_permissions(organization, limit_to_user=None): query = (RepositoryPermission .select(RepositoryPermission, Repository, User) .join(Repository) .switch(RepositoryPermission) .join(User) - .where(Repository.namespace_user == organization) - .where(User.robot == False)) + .where(Repository.namespace_user == organization)) + + if limit_to_user is not None: + query = query.where(RepositoryPermission.user == limit_to_user) + else: + query = query.where(User.robot == False) + return query diff --git a/endpoints/api/organization.py b/endpoints/api/organization.py index 55da710a0..818851caf 100644 --- a/endpoints/api/organization.py +++ b/endpoints/api/organization.py @@ -278,6 +278,46 @@ class OrganizationMemberList(ApiResource): class OrganizationMember(ApiResource): """ Resource for managing individual organization members. """ + @require_scope(scopes.ORG_ADMIN) + @nickname('getOrganizationMember') + def get(self, orgname, membername): + """ Retrieves the details of a member of the organization. + """ + permission = AdministerOrganizationPermission(orgname) + if permission.can(): + # Lookup the user. + member = model.user.get_user(membername) + if not member: + raise NotFound() + + organization = model.user.get_user_or_org(orgname) + if not organization: + raise NotFound() + + # Lookup the user's information in the organization. + teams = list(model.team.get_user_teams_within_org(membername, organization)) + if not teams: + raise NotFound() + + repo_permissions = model.permission.list_organization_member_permissions(organization, member) + + def local_team_view(team): + return { + 'name': team.name, + 'avatar': avatar.get_data_for_team(team), + } + + return { + 'name': member.username, + 'kind': 'robot' if member.robot else 'user', + 'avatar': avatar.get_data_for_user(member), + 'teams': [local_team_view(team) for team in teams], + 'repositories': [permission.repository.name for permission in repo_permissions] + } + + raise Unauthorized() + + @require_scope(scopes.ORG_ADMIN) @nickname('removeOrganizationMember') def delete(self, orgname, membername): diff --git a/test/test_api_security.py b/test/test_api_security.py index 0f0b7994e..8b2acdf6f 100644 --- a/test/test_api_security.py +++ b/test/test_api_security.py @@ -1968,6 +1968,19 @@ class TestOrganizationMemberBuynlargeDevtable(ApiTestCase): ApiTestCase.setUp(self) self._set_url(OrganizationMember, orgname="buynlarge", membername="someuser") + def test_get_anonymous(self): + self._run_test('GET', 401, None, None) + + def test_get_freshuser(self): + self._run_test('GET', 403, 'freshuser', None) + + def test_get_reader(self): + self._run_test('GET', 403, 'reader', None) + + def test_get_devtable(self): + self._run_test('GET', 404, 'devtable', None) + + def test_delete_anonymous(self): self._run_test('DELETE', 401, None, None) diff --git a/test/test_api_usage.py b/test/test_api_usage.py index 28d1b211a..7d6d07046 100644 --- a/test/test_api_usage.py +++ b/test/test_api_usage.py @@ -878,6 +878,13 @@ class TestGetOrganizationMembers(ApiTestCase): assert READ_ACCESS_USER in membernames assert not NO_ACCESS_USER in membernames + for member in json['members']: + membername = member['name'] + response = self.getJsonResponse(OrganizationMember, + params=dict(orgname=ORGANIZATION, membername=membername)) + + self.assertEquals(member, response) + class TestRemoveOrganizationMember(ApiTestCase): def test_try_remove_only_admin(self):