Add team permissions support
This commit is contained in:
parent
97fa69a361
commit
1cd4fa8d9b
6 changed files with 120 additions and 39 deletions
|
@ -112,6 +112,10 @@ def create_team(name, org, team_role_name, description=''):
|
||||||
description=description)
|
description=description)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_team(name, org):
|
||||||
|
# TODO: have code to remove the team, and all its repo permissions, etc.
|
||||||
|
pass
|
||||||
|
|
||||||
def add_user_to_team(user, team):
|
def add_user_to_team(user, team):
|
||||||
return TeamMember.create(user=user, team=team)
|
return TeamMember.create(user=user, team=team)
|
||||||
|
|
||||||
|
@ -124,29 +128,16 @@ def remove_user_from_team(user, team):
|
||||||
raise InvalidTeamException('User does not belong to team.')
|
raise InvalidTeamException('User does not belong to team.')
|
||||||
|
|
||||||
|
|
||||||
def set_team_org_permission(team, org, team_role_name):
|
def get_team_org_role(team):
|
||||||
new_role = TeamRole.get(TeamRole.name == tean_role_name)
|
return TeamRole.get(TeamRole.id == team.role.id)
|
||||||
|
|
||||||
|
def set_team_org_permission(team, team_role_name):
|
||||||
|
new_role = TeamRole.get(TeamRole.name == team_role_name)
|
||||||
team.role = new_role
|
team.role = new_role
|
||||||
team.save()
|
team.save()
|
||||||
return team
|
return team
|
||||||
|
|
||||||
|
|
||||||
def set_team_org_permission(team, org, role_name):
|
|
||||||
new_role = Role.get(Role.name == role_name)
|
|
||||||
|
|
||||||
# Fetch any existing permission for this user on the repo
|
|
||||||
try:
|
|
||||||
perm = TeamPermission.get(TeamPermission.team == team,
|
|
||||||
TeamPermission.organization == org)
|
|
||||||
perm.role = new_role
|
|
||||||
perm.save()
|
|
||||||
return perm
|
|
||||||
except TeamPermission.DoesNotExist:
|
|
||||||
new_perm = TeamPermission.create(team=team, organization=org,
|
|
||||||
role=new_role)
|
|
||||||
return new_perm
|
|
||||||
|
|
||||||
|
|
||||||
def create_federated_user(username, email, service_name, service_id):
|
def create_federated_user(username, email, service_name, service_id):
|
||||||
new_user = create_user(username, None, email)
|
new_user = create_user(username, None, email)
|
||||||
new_user.verified = True
|
new_user.verified = True
|
||||||
|
|
|
@ -215,10 +215,10 @@ def get_matching_entities(prefix):
|
||||||
users = model.get_matching_users(prefix, organization)
|
users = model.get_matching_users(prefix, organization)
|
||||||
|
|
||||||
def team_view(team):
|
def team_view(team):
|
||||||
return {
|
result = {
|
||||||
'name': team.name,
|
'name': team.name,
|
||||||
'kind': 'team',
|
'kind': 'team',
|
||||||
'is_org_member': True,
|
'is_org_member': True
|
||||||
}
|
}
|
||||||
|
|
||||||
def user_view(user):
|
def user_view(user):
|
||||||
|
@ -242,11 +242,13 @@ user_files = UserRequestFiles(app.config['AWS_ACCESS_KEY'],
|
||||||
|
|
||||||
def team_view(orgname, t):
|
def team_view(orgname, t):
|
||||||
view_permission = ViewTeamPermission(orgname, t.name)
|
view_permission = ViewTeamPermission(orgname, t.name)
|
||||||
|
role = model.get_team_org_role(t).name
|
||||||
return {
|
return {
|
||||||
'id': t.id,
|
'id': t.id,
|
||||||
'name': t.name,
|
'name': t.name,
|
||||||
'description': t.description,
|
'description': t.description,
|
||||||
'can_view': view_permission.can()
|
'can_view': view_permission.can(),
|
||||||
|
'role': role
|
||||||
}
|
}
|
||||||
|
|
||||||
@app.route('/api/organization/<orgname>', methods=['GET'])
|
@app.route('/api/organization/<orgname>', methods=['GET'])
|
||||||
|
@ -255,11 +257,12 @@ def get_organization(orgname):
|
||||||
|
|
||||||
def org_view(o, teams):
|
def org_view(o, teams):
|
||||||
admin_org = AdministerOrganizationPermission(orgname)
|
admin_org = AdministerOrganizationPermission(orgname)
|
||||||
|
is_admin = admin_org.can()
|
||||||
return {
|
return {
|
||||||
'name': o.username,
|
'name': o.username,
|
||||||
'gravatar': compute_hash(o.email),
|
'gravatar': compute_hash(o.email),
|
||||||
'teams': {t.name : team_view(orgname, t) for t in teams},
|
'teams': {t.name : team_view(orgname, t) for t in teams},
|
||||||
'is_admin': admin_org.can()
|
'is_admin': is_admin
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_user.is_anonymous():
|
if current_user.is_anonymous():
|
||||||
|
@ -313,13 +316,25 @@ def update_organization_team(orgname, teamname):
|
||||||
if edit_permission.can():
|
if edit_permission.can():
|
||||||
team = None
|
team = None
|
||||||
|
|
||||||
|
json = request.get_json()
|
||||||
|
is_existing = False
|
||||||
try:
|
try:
|
||||||
team = model.get_organization_team(orgname, teamname)
|
team = model.get_organization_team(orgname, teamname)
|
||||||
|
is_existing = True
|
||||||
except:
|
except:
|
||||||
abort(404)
|
# Create the new team.
|
||||||
|
description = json['description'] if 'description' in json else ''
|
||||||
|
role = json['role'] if 'role' in json else 'member'
|
||||||
|
|
||||||
|
team = model.create_team(teamname, orgname, role, description)
|
||||||
|
|
||||||
|
if is_existing:
|
||||||
|
if 'description' in json:
|
||||||
|
team.description = json['description']
|
||||||
|
team.save()
|
||||||
|
if 'role' in json:
|
||||||
|
team = model.set_team_org_permission(team, json['role'])
|
||||||
|
|
||||||
team.description = request.get_json()['description']
|
|
||||||
team.save()
|
|
||||||
return jsonify(team_view(orgname, team))
|
return jsonify(team_view(orgname, team))
|
||||||
|
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
|
@ -1391,8 +1391,25 @@ p.editable:hover i {
|
||||||
}
|
}
|
||||||
|
|
||||||
.org-view .team-listing {
|
.org-view .team-listing {
|
||||||
margin: 10px;
|
padding: 4px;
|
||||||
padding: 10px;
|
}
|
||||||
|
|
||||||
|
.org-view .header-col {
|
||||||
|
color: #444;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.org-view .header-col dd {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.org-view .header-col .info-icon {
|
||||||
|
float: none;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.org-view .team-listing .btn-group {
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.org-view .team-listing i {
|
.org-view .team-listing i {
|
||||||
|
|
|
@ -1076,7 +1076,14 @@ function NewRepoCtrl($scope, $location, $http, $timeout, UserService, Restangula
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function OrgViewCtrl($scope, Restangular, $routeParams) {
|
function OrgViewCtrl($rootScope, $scope, Restangular, $routeParams) {
|
||||||
|
$('.info-icon').popover({
|
||||||
|
'trigger': 'hover',
|
||||||
|
'html': true
|
||||||
|
});
|
||||||
|
|
||||||
|
$rootScope.title = 'Loading...';
|
||||||
|
|
||||||
var orgname = $routeParams.orgname;
|
var orgname = $routeParams.orgname;
|
||||||
|
|
||||||
var loadOrganization = function() {
|
var loadOrganization = function() {
|
||||||
|
@ -1084,11 +1091,24 @@ function OrgViewCtrl($scope, Restangular, $routeParams) {
|
||||||
getOrganization.get().then(function(resp) {
|
getOrganization.get().then(function(resp) {
|
||||||
$scope.organization = resp;
|
$scope.organization = resp;
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
|
|
||||||
|
$rootScope.title = orgname;
|
||||||
}, function() {
|
}, function() {
|
||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.setRole = function(teamname, role) {
|
||||||
|
$scope.organization.teams[teamname].role = role;
|
||||||
|
|
||||||
|
var updateTeam = Restangular.one(getRestUrl('organization', orgname, 'team', teamname));
|
||||||
|
var data = $scope.organization.teams[teamname];
|
||||||
|
updateTeam.customPUT(data).then(function(resp) {
|
||||||
|
}, function() {
|
||||||
|
$('#cannotChangeTeamModal').modal({});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
loadOrganization();
|
loadOrganization();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,54 @@
|
||||||
<div class="org-view container" ng-show="!loading && organization">
|
<div class="org-view container" ng-show="!loading && organization">
|
||||||
<div class="organization-header" organization="organization"></div>
|
<div class="organization-header" organization="organization"></div>
|
||||||
|
|
||||||
<div class="team-listing" ng-repeat="(name, team) in organization.teams">
|
<div class="row visible-sm visible-md visible-lg">
|
||||||
<div class="team-title">
|
<div class="col-sm-8"></div>
|
||||||
<i class="fa fa-group"></i>
|
<div class="col-sm-4 header-col">
|
||||||
<span ng-show="team.can_view">
|
Team Permissions
|
||||||
<a href="/organization/{{ organization.name }}/teams/{{ team.name }}">{{ team.name }}</a>
|
<i class="info-icon fa fa-info-circle" data-placement="bottom" data-original-title="" title=""
|
||||||
</span>
|
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>"></i>
|
||||||
<span ng-show="!team.can_view">
|
</div>
|
||||||
{{ team.name }}
|
</div>
|
||||||
</span>
|
|
||||||
|
<div class="team-listing" ng-repeat="(name, team) in organization.teams">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="team-title">
|
||||||
|
<i class="fa fa-group"></i>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<div class="btn-group btn-group-sm col-sm-4" ng-show="organization.is_admin">
|
||||||
|
<button type="button" class="btn btn-default" ng-click="setRole(name, 'member')" ng-class="{admin: '', creator: '', member: 'active'}[team.role]">Member</button>
|
||||||
|
<button type="button" class="btn btn-default" ng-click="setRole(name, 'creator')" ng-class="{admin: '', creator: 'active', member: ''}[team.role]">Creator</button>
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="setRole(name, 'admin')" ng-class="{admin: 'active', creator: '', member: ''}[team.role]">Admin</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="team-description markdown-view" content="team.description" first-line-only="true"></div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal message dialog -->
|
||||||
|
<div class="modal fade" id="cannotChangeTeamModal">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
|
<h4 class="modal-title">Cannot change team</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
You do not have permission to change properties on teams.
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
<div class="btn-group btn-group-sm">
|
<div class="btn-group btn-group-sm">
|
||||||
<button type="button" class="btn btn-default" ng-click="setRole(name, 'read', 'user')" ng-class="{read: 'active', write: '', admin: ''}[permission.role]">Read only</button>
|
<button type="button" class="btn btn-default" ng-click="setRole(name, 'read', 'user')" ng-class="{read: 'active', write: '', admin: ''}[permission.role]">Read only</button>
|
||||||
<button type="button" class="btn btn-default" ng-click="setRole(name, 'write', 'user')" ng-class="{read: '', write: 'active', admin: ''}[permission.role]">Write</button>
|
<button type="button" class="btn btn-default" ng-click="setRole(name, 'write', 'user')" ng-class="{read: '', write: 'active', admin: ''}[permission.role]">Write</button>
|
||||||
<button type="button" class="btn btn-default" ng-click="setRole(name, 'admin', 'user')" ng-class="{read: '', write: '', admin: 'active'}[permission.role]">Admin</button>
|
<button type="button" class="btn btn-primary" ng-click="setRole(name, 'admin', 'user')" ng-class="{read: '', write: '', admin: 'active'}[permission.role]">Admin</button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
Reference in a new issue