Skip to content

Commit

Permalink
bug 1310220: UI for User Roles (#206). r=bhearsum
Browse files Browse the repository at this point in the history
  • Loading branch information
njirap authored and bhearsum committed Jan 20, 2017
1 parent 8990958 commit 870a1dc
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 46 deletions.
3 changes: 2 additions & 1 deletion auslib/admin/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

from auslib.admin.views.csrf import CSRFView
from auslib.admin.views.permissions import UsersView, PermissionsView, \
SpecificPermissionView, UserRolesView, UserRoleView, \
SpecificPermissionView, UserRolesView, UserRoleView, AllRolesView, \
PermissionScheduledChangesView, PermissionScheduledChangeView, \
EnactPermissionScheduledChangeView, PermissionScheduledChangeHistoryView, \
PermissionScheduledChangeSignoffsView
Expand Down Expand Up @@ -76,6 +76,7 @@ def add_security_headers(response):
# these requests.
app.add_url_rule("/csrf_token", view_func=CSRFView.as_view("csrf"))
app.add_url_rule("/users", view_func=UsersView.as_view("users"))
app.add_url_rule("/users/roles", view_func=AllRolesView.as_view("all_users_roles"))
app.add_url_rule("/users/<username>/permissions", view_func=PermissionsView.as_view("user_permissions"))
app.add_url_rule("/users/<username>/permissions/<permission>", view_func=SpecificPermissionView.as_view("specific_permission"))
app.add_url_rule("/users/<username>/roles", view_func=UserRolesView.as_view("user_roles"))
Expand Down
11 changes: 10 additions & 1 deletion auslib/admin/views/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ def get(self, username):
return jsonify({"roles": roles})


class AllRolesView(AdminView):
"""/users/roles"""

def get(self):
roles = dbo.permissions.getAllRoles()
return jsonify({"roles": roles})


class UserRoleView(AdminView):
"""/users/:username/roles/:role"""

Expand All @@ -197,7 +205,8 @@ def _put(self, username, role, changed_by, transaction):

@requirelogin
def _delete(self, username, role, changed_by, transaction):
if role not in dbo.permissions.getUserRoles(username):
roles = [r['role'] for r in dbo.permissions.getUserRoles(username)]
if role not in roles:
return Response(status=404)

form = DbEditableForm(request.args)
Expand Down
17 changes: 13 additions & 4 deletions auslib/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2116,8 +2116,10 @@ def assertOptionsExist(self, permission, options):
raise ValueError('Unknown option "%s" for permission "%s"' % (opt, permission))

def getAllUsers(self, transaction=None):
res = self.select(columns=[self.username], distinct=True, transaction=transaction)
return [r['username'] for r in res]
res_permissions = self.select(columns=[self.username], distinct=True, transaction=transaction)
res_roles = self.user_roles.select(columns=[self.user_roles.username], transaction=transaction)
res = res_roles + res_permissions
return list(set([r['username'] for r in res]))

def getAllPermissions(self, transaction=None):
ret = defaultdict(dict)
Expand Down Expand Up @@ -2237,7 +2239,13 @@ def getOptions(self, username, permission, transaction=None):
raise ValueError('Permission "%s" doesn\'t exist' % permission)

def getUserRoles(self, username, transaction=None):
res = self.user_roles.select(where=[self.user_roles.username == username], columns=[self.user_roles.role], distinct=True, transaction=transaction)
res = self.user_roles.select(where=[self.user_roles.username == username],
columns=[self.user_roles.role, self.user_roles.data_version],
distinct=True, transaction=transaction)
return [{"role": r["role"], "data_version": r["data_version"]} for r in res]

def getAllRoles(self, transaction=None):
res = self.user_roles.select(columns=[self.user_roles.role], distinct=True, transaction=transaction)
return [r["role"] for r in res]

def isAdmin(self, username, transaction=None):
Expand Down Expand Up @@ -2271,7 +2279,8 @@ def hasPermission(self, username, thing, action, product=None, transaction=None)
return False

def hasRole(self, username, role, transaction=None):
return role in self.getUserRoles(username, transaction)
roles_list = [r['role'] for r in self.getUserRoles(username, transaction)]
return role in roles_list


class Dockerflow(AUSTable):
Expand Down
11 changes: 9 additions & 2 deletions auslib/test/admin/views/test_permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,15 @@ class TestUserRolesAPI_JSON(ViewTest):
def testGetRoles(self):
ret = self._get("/users/bill/roles")
self.assertStatusCode(ret, 200)
got = set(json.loads(ret.data)["roles"])
self.assertEquals(got, set(["releng", "qa"]))
got = json.loads(ret.data)["roles"]
self.assertEquals(got, [{"role": "qa", "data_version": 1},
{"role": "releng", "data_version": 1}])

def testGetAllRoles(self):
ret = self._get("/users/roles")
self.assertStatusCode(ret, 200)
got = json.loads(ret.data)["roles"]
self.assertEqual(got, ['releng', 'qa', 'relman'])

def testGetRolesMissingUserReturnsEmptyList(self):
ret = self.client.get("/users/dean/roles")
Expand Down
2 changes: 1 addition & 1 deletion auslib/test/test_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -3683,7 +3683,7 @@ def testHasPermissionNotAllowedByProduct(self):

def testGetUserRoles(self):
got = self.permissions.getUserRoles("bob")
self.assertEquals(set(got), set(["releng", "dev"]))
self.assertEquals(sorted(got), sorted([{'data_version': 1, 'role': u'releng'}, {'data_version': 1, 'role': u'dev'}]))

def testGetUserRolesNonExistantUser(self):
got = self.permissions.getUserRoles("kirk")
Expand Down
6 changes: 6 additions & 0 deletions ui/app/css/style.less
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ button {
padding-bottom: 10px;
padding-left: 20px;
}

.nav-pills > li.active > a {
color: #428bca !important;
background-color: #fff !important;
box-shadow: inset 0 -5px 0 0 #ddd;
}
124 changes: 123 additions & 1 deletion ui/app/js/controllers/user_edit_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function ($scope, $modalInstance, CSRF, Permissions, user, users) {

$scope.loading = true;
$scope.users = users;

$scope.currentItemTab = 1;
$scope.is_edit = true;
$scope.original_user = user;
$scope.user = angular.copy(user);
Expand All @@ -31,12 +31,101 @@ function ($scope, $modalInstance, CSRF, Permissions, user, users) {
$scope.loading = false;
});

$scope.user.roles = [];
Permissions.getUserRoles(user.username)
.success(function(response) {
$scope.user.roles = response.roles;
})
.error(function(response) {
if (typeof response === 'object') {
$scope.errors = response;
sweetAlert(
"Form submission error",
"See fields highlighted in red.",
"error"
);
} else if (typeof response === 'string') {
sweetAlert(
"Form submission error",
"Unable to submit successfully.\n" +
"(" + response+ ")",
"error"
);
}
});

$scope.roles_list = [];
Permissions.getAllRoles()
.success(function(response) {
$scope.roles_list = response.roles;
})
.error(function(response) {
if (typeof response === 'object') {
$scope.errors = response;
sweetAlert(
"Form submission error",
"See fields highlighted in red.",
"error"
);
} else if (typeof response === 'string') {
sweetAlert(
"Form submission error",
"Unable to submit successfully.\n" +
"(" + response+ ")",
"error"
);
}
});

$scope.saving = false;

$scope.$watchCollection('permission', function(value) {
value.options = value.options_as_json;
});

$scope.grantRole = function() {
$scope.saving = true;
CSRF.getToken()
.then(function(csrf_token) {
Permissions.grantRole($scope.user.username, $scope.role.role, $scope.role.data_version, csrf_token)
.success(function(response) {
$scope.role.data_version = response.new_data_version;
$scope.user.roles.push($scope.role);

if (!($scope.role.role in $scope.roles_list)) {
$scope.roles_list.push($scope.role.role);
}
// reset the add form
$scope.role = {
role: '',
data_version: ''
};
sweetAlert("Saved", "Role granted.", "success");
})
.error(function(response) {
if (typeof response === 'object') {
$scope.errors = response;
sweetAlert(
"Form submission error",
"See fields highlighted in red.",
"error"
);
} else if (typeof response === 'string') {
sweetAlert(
"Form submission error",
"Unable to submit successfully.\n" +
"(" + response+ ")",
"error"
);
}
})
.finally(function() {
$scope.saving = false;
});
});

};

$scope.addPermission = function() {
// $scope.permission.options = $scope.permission.options_as_json;
$scope.saving = true;
Expand Down Expand Up @@ -121,6 +210,39 @@ function ($scope, $modalInstance, CSRF, Permissions, user, users) {

};

$scope.revokeRole = function(role) {
$scope.saving = true;
CSRF.getToken()
.then(function(csrf_token) {
Permissions.revokeRole($scope.user.username, role, csrf_token)
.success(function(response) {
$scope.user.roles.splice($scope.user.roles.indexOf(role), 1);
sweetAlert("Deleted", "Role deleted.", "success");
})
.error(function(response) {
if (typeof response === 'object') {
$scope.errors = response;
sweetAlert(
"Form submission error",
"See fields highlighted in red.",
"error"
);
} else if (typeof response === 'string') {
sweetAlert(
"Form submission error",
"Unable to submit successfully.\n" +
"(" + response+ ")",
"error"
);
}
})
.finally(function() {
$scope.saving = false;
});
});
$scope.saving = false;
};

$scope.updatePermission = function(permission) {
$scope.saving = true;
CSRF.getToken()
Expand Down
25 changes: 25 additions & 0 deletions ui/app/js/services/permissions_service.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,31 @@ angular.module("app").factory('Permissions', function($http, $q) {
url += '&csrf_token=' + encodeURIComponent(csrf_token);
return $http.delete(url);
},
getUserRoles: function(username) {
// What comes back from the server is a dict like this:
// {'roles': ['role1', 'role2'...]} if the user has roles
// otherwise the value of the dict will be an empty array:
// {'roles': []}
// when the user has no roles
var url = '/api/users/' + encodeURIComponent(username) + '/roles';
return $http.get(url);
},
getAllRoles: function() {
return $http.get('/api/users/roles');
},
grantRole: function(username, role, data_version, csrf_token) {
var url = '/api/users/' + encodeURIComponent(username) + '/roles/';
url += encodeURIComponent(role);
return $http.put(url);
},
revokeRole: function(username, role, csrf_token) {
var url = '/api/users/' + encodeURIComponent(username) + '/roles/';
url += encodeURIComponent(role.role);
url += '?data_version=' + role.data_version;
url += '&csrf_token=' + encodeURIComponent(csrf_token);
return $http.delete(url);
},

};
return service;

Expand Down

0 comments on commit 870a1dc

Please sign in to comment.