Merge pull request #213 from coreos-inc/orgmember
Add a secondary tab to Teams for managing org members
This commit is contained in:
commit
cf4800c06c
8 changed files with 373 additions and 105 deletions
|
@ -414,6 +414,28 @@ def convert_user_to_organization(user, admin_user):
|
|||
return user
|
||||
|
||||
|
||||
def remove_organization_member(org, user):
|
||||
org_admins = [u.username for u in __get_org_admin_users(org)]
|
||||
if len(org_admins) == 1 and user.username in org_admins:
|
||||
raise DataModelException('Cannot remove user as they are the only organization admin')
|
||||
|
||||
with config.app_config['DB_TRANSACTION_FACTORY'](db):
|
||||
# Find and remove the user from any repositorys under the org.
|
||||
permissions = (RepositoryPermission.select(RepositoryPermission.id)
|
||||
.join(Repository)
|
||||
.where(Repository.namespace_user == org,
|
||||
RepositoryPermission.user == user))
|
||||
|
||||
RepositoryPermission.delete().where(RepositoryPermission.id << permissions).execute()
|
||||
|
||||
# Find and remove the user from any teams under the org.
|
||||
members = (TeamMember.select(TeamMember.id)
|
||||
.join(Team)
|
||||
.where(Team.organization == org, TeamMember.user == user))
|
||||
|
||||
TeamMember.delete().where(TeamMember.id << members).execute()
|
||||
|
||||
|
||||
def create_team(name, org, team_role_name, description=''):
|
||||
(username_valid, username_issue) = validate_username(name)
|
||||
if not username_valid:
|
||||
|
@ -428,6 +450,15 @@ def create_team(name, org, team_role_name, description=''):
|
|||
description=description)
|
||||
|
||||
|
||||
def __get_org_admin_users(org):
|
||||
return (User.select()
|
||||
.join(TeamMember)
|
||||
.join(Team)
|
||||
.join(TeamRole)
|
||||
.where(Team.organization == org, TeamRole.name == 'admin', User.robot == False)
|
||||
.distinct())
|
||||
|
||||
|
||||
def __get_user_admin_teams(org_name, teamname, username):
|
||||
Org = User.alias()
|
||||
user_teams = Team.select().join(TeamMember).join(User)
|
||||
|
@ -877,6 +908,23 @@ def verify_user(username_or_email, password):
|
|||
# We weren't able to authorize the user
|
||||
return None
|
||||
|
||||
def list_organization_member_permissions(organization):
|
||||
query = (RepositoryPermission.select(RepositoryPermission, Repository, User)
|
||||
.join(Repository)
|
||||
.switch(RepositoryPermission)
|
||||
.join(User)
|
||||
.where(Repository.namespace_user == organization)
|
||||
.where(User.robot == False))
|
||||
return query
|
||||
|
||||
|
||||
def list_organization_members_by_teams(organization):
|
||||
query = (TeamMember.select(Team, User)
|
||||
.annotate(Team)
|
||||
.annotate(User)
|
||||
.where(Team.organization == organization))
|
||||
return query
|
||||
|
||||
|
||||
def get_user_organizations(username):
|
||||
UserAlias = User.alias()
|
||||
|
@ -905,14 +953,6 @@ def get_organization_team(orgname, teamname):
|
|||
|
||||
return result[0]
|
||||
|
||||
|
||||
def get_organization_members_with_teams(organization, membername = None):
|
||||
joined = TeamMember.select().annotate(Team).annotate(User)
|
||||
query = joined.where(Team.organization == organization)
|
||||
if membername:
|
||||
query = query.where(User.username == membername)
|
||||
return query
|
||||
|
||||
def get_organization_team_members(teamid):
|
||||
joined = User.select().join(TeamMember).join(Team)
|
||||
query = joined.where(Team.id == teamid)
|
||||
|
|
|
@ -230,7 +230,7 @@ class OrganizationMemberList(ApiResource):
|
|||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationMembers')
|
||||
def get(self, orgname):
|
||||
""" List the members of the specified organization. """
|
||||
""" List the human members of the specified organization. """
|
||||
permission = AdministerOrganizationPermission(orgname)
|
||||
if permission.can():
|
||||
try:
|
||||
|
@ -242,21 +242,41 @@ class OrganizationMemberList(ApiResource):
|
|||
# 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)
|
||||
members = model.list_organization_members_by_teams(org)
|
||||
for member in members:
|
||||
if member.user.robot:
|
||||
continue
|
||||
|
||||
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': []}
|
||||
member_data = {
|
||||
'name': member.user.username,
|
||||
'kind': 'user',
|
||||
'avatar': avatar.get_data_for_user(member.user),
|
||||
'teams': [],
|
||||
'repositories': []
|
||||
}
|
||||
|
||||
members_dict[member.user.username]['teams'].append(member.team.name)
|
||||
members_dict[member.user.username] = member_data
|
||||
|
||||
return {'members': members_dict}
|
||||
members_dict[member.user.username]['teams'].append({
|
||||
'name': member.team.name,
|
||||
'avatar': avatar.get_data_for_team(member.team),
|
||||
})
|
||||
|
||||
# Loop to add direct repository permissions.
|
||||
for permission in model.list_organization_member_permissions(org):
|
||||
username = permission.user.username
|
||||
if not username in members_dict:
|
||||
continue
|
||||
|
||||
members_dict[username]['repositories'].append(permission.repository.name)
|
||||
|
||||
return {'members': members_dict.values()}
|
||||
|
||||
raise Unauthorized()
|
||||
|
||||
|
||||
|
||||
@resource('/v1/organization/<orgname>/members/<membername>')
|
||||
@path_param('orgname', 'The name of the organization')
|
||||
@path_param('membername', 'The username of the organization member')
|
||||
|
@ -264,31 +284,26 @@ class OrganizationMember(ApiResource):
|
|||
""" Resource for managing individual organization members. """
|
||||
|
||||
@require_scope(scopes.ORG_ADMIN)
|
||||
@nickname('getOrganizationMember')
|
||||
def get(self, orgname, membername):
|
||||
""" Get information on the specific organization member. """
|
||||
@nickname('removeOrganizationMember')
|
||||
def delete(self, orgname, membername):
|
||||
""" Removes a member from an organization, revoking all its repository
|
||||
priviledges and removing it from all teams in the organization.
|
||||
"""
|
||||
permission = AdministerOrganizationPermission(orgname)
|
||||
if permission.can():
|
||||
# Lookup the user.
|
||||
user = model.get_nonrobot_user(membername)
|
||||
if not user:
|
||||
raise NotFound()
|
||||
|
||||
try:
|
||||
org = model.get_organization(orgname)
|
||||
except model.InvalidOrganizationException:
|
||||
raise NotFound()
|
||||
|
||||
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:
|
||||
raise NotFound()
|
||||
|
||||
return {'member': member_dict}
|
||||
# Remove the user from the organization.
|
||||
model.remove_organization_member(org, user)
|
||||
return 'Deleted', 204
|
||||
|
||||
raise Unauthorized()
|
||||
|
||||
|
|
|
@ -4,7 +4,11 @@
|
|||
|
||||
.teams-manager .manager-header {
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-bottom: 10px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.teams-manager .manager-header i.fa {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.teams-manager .cor-options-menu {
|
||||
|
@ -12,6 +16,25 @@
|
|||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.teams-manager td .empty {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.teams-manager .cor-confirm-dialog .entity-reference .avatar {
|
||||
margin-left: 4px;
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.teams-manager .cor-confirm-dialog .entity-reference .entity-name {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.teams-manager .manager-header {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.teams-manager .control-col {
|
||||
padding-left: 55px;
|
||||
|
|
|
@ -1,65 +1,162 @@
|
|||
<div class="teams-manager-element">
|
||||
<div class="manager-header" header-title="Teams">
|
||||
<span class="popup-input-button" pattern="TEAM_PATTERN" placeholder="'Team Name'"
|
||||
<div class="manager-header" header-title="Teams and Membership">
|
||||
<div class="tab-header-controls hidden-xs">
|
||||
<div class="btn-group btn-group-sm" ng-show="organization.is_admin">
|
||||
<button class="btn"
|
||||
ng-class="!showingMembers ? 'btn-primary active' : 'btn-default'" ng-click="showMembers(false)">
|
||||
<i class="fa fa-group"></i>Teams View
|
||||
</button>
|
||||
<button class="btn"
|
||||
ng-class="showingMembers ? 'btn-info active' : 'btn-default'" ng-click="showMembers(true)">
|
||||
<i class="fa fa-user"></i>Members View
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="popup-input-button visible-xs" ng-if="!showingMembers"
|
||||
pattern="TEAM_PATTERN" placeholder="'Team Name'"
|
||||
submitted="createTeam(value)" ng-show="organization.is_admin">
|
||||
<i class="fa fa-plus" style="margin-right: 6px;"></i> Create New Team
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row hidden-xs">
|
||||
<div class="col-md-4 col-md-offset-8 col-sm-5 col-sm-offset-7 header-col" ng-show="organization.is_admin">
|
||||
<span class="header-text">Team Permissions</span>
|
||||
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
|
||||
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"
|
||||
data-html="true"
|
||||
data-trigger="hover"
|
||||
bs-popover></i>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Teams List -->
|
||||
<div ng-show="!showingMembers">
|
||||
<span class="popup-input-button hidden-xs"
|
||||
pattern="TEAM_PATTERN" placeholder="'Team Name'"
|
||||
submitted="createTeam(value)" ng-show="organization.is_admin"
|
||||
style="margin-bottom: 10px;">
|
||||
<i class="fa fa-plus" style="margin-right: 6px;"></i> Create New Team
|
||||
</span>
|
||||
|
||||
<div class="team-listing" ng-repeat="team in orderedTeams">
|
||||
<div id="team-{{team.name}}" class="row">
|
||||
<div class="col-sm-7 col-md-8">
|
||||
<div class="team-title">
|
||||
<span class="avatar" data="team.avatar" size="30"></span>
|
||||
<span ng-show="team.can_view">
|
||||
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}">{{ team.name }}</a>
|
||||
</span>
|
||||
<span ng-show="!team.can_view">
|
||||
{{ team.name }}
|
||||
</span>
|
||||
<div class="row hidden-xs">
|
||||
<div class="col-md-4 col-md-offset-8 col-sm-5 col-sm-offset-7 header-col" ng-show="organization.is_admin">
|
||||
<span class="header-text">Team Permissions</span>
|
||||
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" data-title=""
|
||||
data-content="Global permissions for the team and its members<br><br><dl><dt>Member</dt><dd>Permissions are assigned on a per repository basis</dd><dt>Creator</dt><dd>A team can create its own repositories</dd><dt>Admin</dt><dd>A team has full control of the organization</dd></dl>"
|
||||
data-html="true"
|
||||
data-trigger="hover"
|
||||
bs-popover></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-listing" ng-repeat="team in orderedTeams">
|
||||
<div id="team-{{team.name}}" class="row">
|
||||
<div class="col-sm-7 col-md-8">
|
||||
<div class="team-title">
|
||||
<span class="avatar" data="team.avatar" size="30"></span>
|
||||
<span ng-show="team.can_view">
|
||||
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}">{{ team.name }}</a>
|
||||
</span>
|
||||
<span ng-show="!team.can_view">
|
||||
{{ team.name }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="team-description markdown-view" content="team.description" first-line-only="true"></div>
|
||||
|
||||
<div class="team-member-list hidden-xs" ng-if="members[team.name]">
|
||||
<div class="cor-loader" ng-if="!members[team.name].members"></div>
|
||||
<span class="team-member"
|
||||
ng-repeat="member in members[team.name].members | orderBy:'is_robot' | limitTo: 20">
|
||||
<span data-title="{{ member.name }}" bs-tooltip>
|
||||
<a href="/user/{{ member.name }}" ng-if="!member.is_robot">
|
||||
<span class="avatar" data="member.avatar" size="26"></span>
|
||||
</a>
|
||||
<i class="fa ci-robot fa-lg" ng-if="member.is_robot"></i>
|
||||
</span>
|
||||
</span>
|
||||
<span class="team-member-more"
|
||||
ng-if="members[team.name].members.length > 20">+ {{ members[team.name].members.length - 20 }} more team members.</span>
|
||||
<span class="team-member-more"
|
||||
ng-if="members[team.name].members && !members[team.name].members.length">(Empty Team)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="team-description markdown-view" content="team.description" first-line-only="true"></div>
|
||||
<div class="col-sm-5 col-md-4 control-col" ng-show="organization.is_admin">
|
||||
<span class="role-group" current-role="team.role" pull-left="true"
|
||||
role-changed="setRole(role, team.name)" roles="teamRoles"></span>
|
||||
|
||||
<div class="team-member-list hidden-xs" ng-if="members[team.name]">
|
||||
<div class="cor-loader" ng-if="!members[team.name].members"></div>
|
||||
<span class="team-member"
|
||||
ng-repeat="member in members[team.name].members | orderBy:'is_robot' | limitTo: 20">
|
||||
<span data-title="{{ member.name }}" bs-tooltip>
|
||||
<a href="/user/{{ member.name }}" ng-if="!member.is_robot">
|
||||
<span class="avatar" data="member.avatar" size="26"></span>
|
||||
</a>
|
||||
<i class="fa ci-robot fa-lg" ng-if="member.is_robot"></i>
|
||||
<span class="cor-options-menu">
|
||||
<span class="cor-option" option-click="askDeleteTeam(team.name)">
|
||||
<i class="fa fa-times"></i> Delete Team {{ team.name }}
|
||||
</span>
|
||||
</span>
|
||||
<span class="team-member-more"
|
||||
ng-if="members[team.name].members.length > 20">+ {{ members[team.name].members.length - 20 }} more team members.</span>
|
||||
<span class="team-member-more"
|
||||
ng-if="members[team.name].members && !members[team.name].members.length">(Empty Team)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-5 col-md-4 control-col" ng-show="organization.is_admin">
|
||||
<span class="role-group" current-role="team.role" pull-left="true"
|
||||
role-changed="setRole(role, team.name)" roles="teamRoles"></span>
|
||||
|
||||
<span class="cor-options-menu">
|
||||
<span class="cor-option" option-click="askDeleteTeam(team.name)">
|
||||
<i class="fa fa-times"></i> Delete Team {{ team.name }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Members List -->
|
||||
<div ng-show="showingMembers">
|
||||
<div class="cor-loader" ng-if="!fullMemberList"></div>
|
||||
|
||||
<div class="filter-box" collection="fullMemberList" filter-model="memberFilter"
|
||||
filter-name="Organization Members"></div>
|
||||
|
||||
<div class="empty" ng-if="fullMemberList.length && !(fullMemberList | filter:memberFilter).length">
|
||||
<div class="empty-primary-msg">No organization members found matching filter.</div>
|
||||
<div class="empty-secondary-msg">
|
||||
Please change your filter to display members.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="cor-table" ng-if="(fullMemberList | filter:memberFilter).length">
|
||||
<thead>
|
||||
<td>Member Name</td>
|
||||
<td>Teams</td>
|
||||
<td>Direct Repository Permissions</td>
|
||||
<td class="options-col"></td>
|
||||
</thead>
|
||||
|
||||
<tbody ng-repeat="memberInfo in fullMemberList | filter:memberFilter | orderBy:'name'" bindonce>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="entity-reference" entity="memberInfo"></div>
|
||||
</td>
|
||||
<td>
|
||||
<span ng-repeat="team in memberInfo.teams"
|
||||
data-title="Team {{ team.name }}" bs-tooltip>
|
||||
<span class="anchor" href="/organization/{{ organization.name }}/teams/{{ team.name }}">
|
||||
<span class="avatar" size="24" data="team.avatar"></span>
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="empty" bo-if="memberInfo.repositories.length == 0">
|
||||
(No direct permissions on any repositories)
|
||||
</span>
|
||||
|
||||
<span class="member-perm-summary" bo-if="memberInfo.repositories.length > 0">
|
||||
Direct permissions on {{ memberInfo.repositories.length }}
|
||||
<span bo-if="memberInfo.repositories.length == 1">repository</span>
|
||||
<span bo-if="memberInfo.repositories.length > 1">repositories</span>
|
||||
under this organization
|
||||
</span>
|
||||
</td>
|
||||
<td class="options-col">
|
||||
<span class="cor-options-menu" ng-if="memberInfo.name != user.username">
|
||||
<span class="cor-option" option-click="askRemoveMember(memberInfo)">
|
||||
<i class="fa fa-times"></i> Remove From Organization
|
||||
</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Remove member confirm -->
|
||||
<div class="cor-confirm-dialog"
|
||||
dialog-context="removeMemberInfo"
|
||||
dialog-action="removeMember(info, callback)"
|
||||
dialog-title="Remove Organization Member"
|
||||
dialog-action-title="Remove Member">
|
||||
<div class="co-alert co-alert-info" style="margin-bottom: 10px;">
|
||||
<span class="entity-reference" entity="removeMemberInfo"></span> will be removed from all teams and repositories under this organization in which they are a
|
||||
member or have permissions.
|
||||
</div>
|
||||
|
||||
Continue with removal of <span class="entity-reference" entity="removeMemberInfo"></span>?
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,7 @@ angular.module('quay').directive('teamsManager', function () {
|
|||
'organization': '=organization',
|
||||
'isEnabled': '=isEnabled'
|
||||
},
|
||||
controller: function($scope, $element, ApiService, CreateService, $timeout) {
|
||||
controller: function($scope, $element, ApiService, CreateService, $timeout, UserService) {
|
||||
$scope.TEAM_PATTERN = TEAM_PATTERN;
|
||||
$scope.teamRoles = [
|
||||
{ 'id': 'member', 'title': 'Member', 'kind': 'default' },
|
||||
|
@ -20,8 +20,12 @@ angular.module('quay').directive('teamsManager', function () {
|
|||
{ 'id': 'admin', 'title': 'Admin', 'kind': 'primary' }
|
||||
];
|
||||
|
||||
UserService.updateUserIn($scope);
|
||||
|
||||
$scope.members = {};
|
||||
$scope.orderedTeams = [];
|
||||
$scope.showingMembers = false;
|
||||
$scope.fullMemberList = null;
|
||||
|
||||
var loadTeamMembers = function() {
|
||||
if (!$scope.organization || !$scope.isEnabled) { return; }
|
||||
|
@ -141,6 +145,47 @@ angular.module('quay').directive('teamsManager', function () {
|
|||
delete $scope.organization.teams[teamname];
|
||||
}, ApiService.errorDisplay('Cannot delete team'));
|
||||
};
|
||||
|
||||
$scope.showMembers = function(value) {
|
||||
$scope.showingMembers = value;
|
||||
if (value && !$scope.fullMemberList) {
|
||||
var params = {
|
||||
'orgname': $scope.organization.name
|
||||
};
|
||||
|
||||
ApiService.getOrganizationMembers(null, params).then(function(resp) {
|
||||
$scope.fullMemberList = resp['members'];
|
||||
}, ApiService.errorDisplay('Could not load full membership list'));
|
||||
}
|
||||
};
|
||||
|
||||
$scope.removeMember = function(memberInfo, callback) {
|
||||
var params = {
|
||||
'orgname': $scope.organization.name,
|
||||
'membername': memberInfo.name
|
||||
};
|
||||
|
||||
var errorHandler = ApiService.errorDisplay('Could not remove member', function() {
|
||||
callback(false);
|
||||
});
|
||||
|
||||
ApiService.removeOrganizationMember(null, params).then(function(resp) {
|
||||
// Reset the state of the directive.
|
||||
$scope.members = {};
|
||||
$scope.orderedTeams = [];
|
||||
$scope.fullMemberList = null;
|
||||
|
||||
loadOrderedTeams();
|
||||
loadTeamMembers();
|
||||
$scope.showMembers(true);
|
||||
|
||||
callback(true);
|
||||
}, errorHandler)
|
||||
};
|
||||
|
||||
$scope.askRemoveMember = function(memberInfo) {
|
||||
$scope.removeMemberInfo = $.extend({}, memberInfo);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
<span class="cor-tab" tab-active="true" tab-title="Repositories" tab-target="#repos">
|
||||
<i class="fa fa-hdd-o"></i>
|
||||
</span>
|
||||
<span class="cor-tab" tab-title="Teams" tab-target="#teams" tab-init="showTeams()">
|
||||
<span class="cor-tab" tab-title="Teams and Membership" tab-target="#teams" tab-init="showTeams()">
|
||||
<i class="fa fa-users"></i>
|
||||
</span>
|
||||
<span class="cor-tab" tab-title="Robot Accounts" tab-target="#robots" tab-init="showRobots()"
|
||||
|
|
|
@ -1966,19 +1966,19 @@ class TestPermissionPrototypeBuynlargeL24b(ApiTestCase):
|
|||
class TestOrganizationMemberBuynlargeDevtable(ApiTestCase):
|
||||
def setUp(self):
|
||||
ApiTestCase.setUp(self)
|
||||
self._set_url(OrganizationMember, orgname="buynlarge", membername="devtable")
|
||||
self._set_url(OrganizationMember, orgname="buynlarge", membername="someuser")
|
||||
|
||||
def test_get_anonymous(self):
|
||||
self._run_test('GET', 401, None, None)
|
||||
def test_delete_anonymous(self):
|
||||
self._run_test('DELETE', 401, None, None)
|
||||
|
||||
def test_get_freshuser(self):
|
||||
self._run_test('GET', 403, 'freshuser', None)
|
||||
def test_delete_freshuser(self):
|
||||
self._run_test('DELETE', 403, 'freshuser', None)
|
||||
|
||||
def test_get_reader(self):
|
||||
self._run_test('GET', 403, 'reader', None)
|
||||
def test_delete_reader(self):
|
||||
self._run_test('DELETE', 403, 'reader', None)
|
||||
|
||||
def test_get_devtable(self):
|
||||
self._run_test('GET', 200, 'devtable', None)
|
||||
def test_delete_devtable(self):
|
||||
self._run_test('DELETE', 404, 'devtable', None)
|
||||
|
||||
|
||||
class TestOrgRobotBuynlargeZ7pd(ApiTestCase):
|
||||
|
|
|
@ -802,28 +802,76 @@ class TestUpdateOrganizationPrototypes(ApiTestCase):
|
|||
self.assertEquals('admin', json['role'])
|
||||
|
||||
|
||||
class TestGetOrganiaztionMembers(ApiTestCase):
|
||||
class TestGetOrganizationMembers(ApiTestCase):
|
||||
def test_getmembers(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
json = self.getJsonResponse(OrganizationMemberList,
|
||||
params=dict(orgname=ORGANIZATION))
|
||||
|
||||
assert ADMIN_ACCESS_USER in json['members']
|
||||
assert READ_ACCESS_USER in json['members']
|
||||
assert not NO_ACCESS_USER in json['members']
|
||||
membernames = [member['name'] for member in json['members']]
|
||||
assert ADMIN_ACCESS_USER in membernames
|
||||
assert READ_ACCESS_USER in membernames
|
||||
assert not NO_ACCESS_USER in membernames
|
||||
|
||||
def test_getspecificmember(self):
|
||||
|
||||
class TestRemoveOrganizationMember(ApiTestCase):
|
||||
def test_try_remove_only_admin(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
json = self.getJsonResponse(OrganizationMember,
|
||||
params=dict(orgname=ORGANIZATION,
|
||||
membername=ADMIN_ACCESS_USER))
|
||||
self.deleteResponse(OrganizationMember,
|
||||
params=dict(orgname=ORGANIZATION, membername=ADMIN_ACCESS_USER),
|
||||
expected_code=400)
|
||||
|
||||
self.assertEquals(ADMIN_ACCESS_USER, json['member']['name'])
|
||||
self.assertEquals('user', json['member']['kind'])
|
||||
def test_remove_member(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
assert 'owners' in json['member']['teams']
|
||||
json = self.getJsonResponse(OrganizationMemberList,
|
||||
params=dict(orgname=ORGANIZATION))
|
||||
|
||||
membernames = [member['name'] for member in json['members']]
|
||||
assert ADMIN_ACCESS_USER in membernames
|
||||
assert READ_ACCESS_USER in membernames
|
||||
|
||||
self.deleteResponse(OrganizationMember,
|
||||
params=dict(orgname=ORGANIZATION, membername=READ_ACCESS_USER))
|
||||
|
||||
json = self.getJsonResponse(OrganizationMemberList,
|
||||
params=dict(orgname=ORGANIZATION))
|
||||
|
||||
membernames = [member['name'] for member in json['members']]
|
||||
assert ADMIN_ACCESS_USER in membernames
|
||||
assert not READ_ACCESS_USER in membernames
|
||||
|
||||
|
||||
def test_remove_member_repo_permission(self):
|
||||
self.login(ADMIN_ACCESS_USER)
|
||||
|
||||
# Add read user as a direct permission on the admin user's repo.
|
||||
model.set_user_repo_permission(READ_ACCESS_USER, ADMIN_ACCESS_USER, 'simple', 'read')
|
||||
|
||||
# Verify the user has a permission on the admin user's repo.
|
||||
admin_perms = [p.user.username for p in model.get_all_repo_users(ADMIN_ACCESS_USER, 'simple')]
|
||||
assert READ_ACCESS_USER in admin_perms
|
||||
|
||||
# Add read user as a direct permission on the org repo.
|
||||
model.set_user_repo_permission(READ_ACCESS_USER, ORGANIZATION, ORG_REPO, 'read')
|
||||
|
||||
# Verify the user has a permission on the org repo.
|
||||
org_perms = [p.user.username for p in model.get_all_repo_users(ORGANIZATION, ORG_REPO)]
|
||||
assert READ_ACCESS_USER in org_perms
|
||||
|
||||
# Remove the user from the org.
|
||||
self.deleteResponse(OrganizationMember,
|
||||
params=dict(orgname=ORGANIZATION, membername=READ_ACCESS_USER))
|
||||
|
||||
# Verify that the user's permission on the org repo is gone, but it is still
|
||||
# present on the other repo.
|
||||
org_perms = [p.user.username for p in model.get_all_repo_users(ORGANIZATION, ORG_REPO)]
|
||||
assert not READ_ACCESS_USER in org_perms
|
||||
|
||||
admin_perms = [p.user.username for p in model.get_all_repo_users(ADMIN_ACCESS_USER, 'simple')]
|
||||
assert READ_ACCESS_USER in admin_perms
|
||||
|
||||
|
||||
class TestGetOrganizationPrivateAllowed(ApiTestCase):
|
||||
|
|
Reference in a new issue