add: support for role and group inheritance

This commit is contained in:
ziirish 2018-04-12 15:45:24 +02:00
parent ec4a1bff2b
commit b32c185725
No known key found for this signature in database
GPG key ID: 72DB229A64B54E46
12 changed files with 270 additions and 64 deletions

View file

@ -63,6 +63,7 @@
<tr>
<th>{{ _('Group name') }}</th>
<th>{{ _('Authorization Backend') }}</th>
<th>{{ _('Role') }}</th>
<th>{{ _('Members') }}</th>
<th>{{ _('Grants') }}</th>
<th>{{ _('Controls') }}</th>
@ -139,7 +140,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">{{ _('Cancel') }}</button>
<button type="button" class="btn btn-info" data-dismiss="modal" id="perform-group-delete">{{ _('Confirm') }}</button>
<button type="button" class="btn btn-danger" data-dismiss="modal" id="perform-group-delete">{{ _('Confirm') }}</button>
</div>
</div>
</div>
@ -214,23 +215,21 @@
</ui-select>
</div>
</div>
<div ng-if="mode == 'grant'">
<div class="form-group">
<div class="col-lg-3 col-lg-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="$parent.isAdmin"> {{ _("Is Admin") }}
</label>
</div>
<div class="form-group">
<div class="col-lg-3 col-lg-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="$parent.isAdmin"> {{ _("Is Admin") }}
</label>
</div>
</div>
<div class="form-group">
<div class="col-lg-3 col-lg-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="$parent.isModerator"> {{ _("Is Moderator") }}
</label>
</div>
</div>
<div class="form-group">
<div class="col-lg-3 col-lg-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="$parent.isModerator"> {{ _("Is Moderator") }}
</label>
</div>
</div>
</div>

View file

@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% block body %}
{% include "notifications.html" %}
<div class="col-sm-12 col-md-12 main">
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>

View file

@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% block body %}
{% include "notifications.html" %}
<div class="col-sm-12 col-md-12 main">
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>

View file

@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% block body %}
{% include "notifications.html" %}
<div class="col-sm-12 col-md-12 main">
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>
@ -32,6 +32,24 @@
</ui-select>
</div>
</div>
<div class="form-group">
<div class="col-lg-3 col-lg-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="isAdmin" ng-disabled="!isAdminEnabled"> {{ _("Is Admin") }}
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-lg-3 col-lg-offset-2">
<div class="checkbox">
<label>
<input type="checkbox" ng-model="isModerator" ng-disabled="!isModeratorEnabled"> {{ _("Is Moderator") }}
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-lg-10 col-lg-offset-2">
<button type="submit" class="btn btn-primary" ng-disabled="!updateGroup.$valid || !validGrantInput"><i class="fa fa-fw fa-floppy-o" aria-hidden="true"></i>{{ _('Save') }}</button>

View file

@ -1,7 +1,7 @@
{% extends "layout.html" %}
{% block body %}
{% include "notifications.html" %}
<div class="col-sm-12 col-md-12 main">
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
{% include "small_topbar.html" %}
<ul class="breadcrumb" style="margin-bottom: 5px;">
<li><a href="{{ url_for('view.home') }}">{{ _('Home') }}</a></li>

View file

@ -7,7 +7,6 @@ var _users = {};
var _groups = {};
var _acl_backends = {};
var _users_array = [];
var __promises = [];
var __globals_promises = [];
var app = angular.module('MainApp', ['ngSanitize', 'ui.select', 'mgcrea.ngStrap', 'datatables']);
@ -87,6 +86,38 @@ app.controller('AdminCtrl', ['$scope', '$http', '$q', '$scrollspy', 'DTOptionsBu
});
locals.push(l);
}
if ($scope.isAdmin) {
var l = $http({
url: '{{ url_for("api.acl_admins") }}',
method: 'PUT',
params: {
memberName: '@'+$scope.name,
backendName: $scope.acl_backend,
},
headers: { 'X-From-UI': true },
})
.catch(buiFail)
.then(function(resp2) {
notifAll(resp2.data);
});
locals.push(l);
}
if ($scope.isModerator) {
var l = $http({
url: '{{ url_for("api.acl_moderators") }}',
method: 'PUT',
params: {
memberName: '@'+$scope.name,
backendName: $scope.acl_backend,
},
headers: { 'X-From-UI': true },
})
.catch(buiFail)
.then(function(resp2) {
notifAll(resp2.data);
});
locals.push(l);
}
$q.all(locals).finally(function() {
_authorization_groups();
});
@ -287,6 +318,27 @@ var _groups_table = $('#table-groups').DataTable( {
return ret;
}
},
{
data: null,
render: function ( data, type, row ) {
if (type === 'filter' || type === 'sort') {
return data.roles;
}
var ret = '';
$.each(data.roles, function(i, role) {
inh = _groups[data.id][role+'_by'];
if (inh) {
ret += '<span data-toggle="tooltip" data-placement="auto top" title=\'{{ _("Inherited by: ") }}'+inh+'\' data-html="true">';
}
ret += '<span class="label label-warning">'+(inh ? '<em>' : '')+role+(inh ? '</em>' : '')+'</span>';
if (inh) {
ret += '</span>';
}
ret += '&nbsp;';
});
return ret;
}
},
{
data: 'members',
render: function ( data, type, row ) {
@ -374,14 +426,22 @@ var _users_table = $('#table-users').DataTable( {
}
},
{
data: 'roles',
data: null,
render: function ( data, type, row ) {
if (type === 'filter' || type === 'sort') {
return data;
return data.roles;
}
var ret = '';
$.each(data, function(i, role) {
ret += '<span class="label label-warning">'+role+'</span>&nbsp;';
$.each(data.roles, function(i, role) {
inh = _users[data.id][role+'_by'];
if (inh) {
ret += '<span data-toggle="tooltip" data-placement="auto top" title=\'{{ _("Inherited by: ") }}'+inh+'\' data-html="true">';
}
ret += '<span class="label label-warning">'+(inh ? '<em>' : '')+role+(inh ? '</em>' : '')+'</span>';
if (inh) {
ret += '</span>';
}
ret += '&nbsp;';
});
return ret;
}
@ -390,11 +450,23 @@ var _users_table = $('#table-users').DataTable( {
data: 'groups',
render: function ( data, type, row ) {
if (type === 'filter' || type === 'sort') {
return data;
var ret = [];
$.each(data, function(i, group) {
ret.push(group.name);
});
return ret;
}
var ret = '';
$.each(data, function(i, group) {
ret += '<span class="label label-primary">'+group+'</span>&nbsp;';
var inh = group.inherit;
if (inh) {
ret += '<span data-toggle="tooltip" data-placement="auto top" title=\'{{ _("Inherited by: ") }}'+inh+'\' data-html="true">';
}
ret += '<span class="label label-primary">'+(inh ? '<em>' : '')+group.name+(inh ? '</em>' : '')+'</span>';
if (inh) {
ret += '</span>';
}
ret += '&nbsp;';
});
return ret;
}
@ -452,8 +524,8 @@ var _authorization_users = function() {
$('#table-users-container').hide();
var __usernames = [];
var __top_promises = [];
var __promises = [];
var t = $.getJSON('{{ url_for("api.acl_grants") }}').done(function (grants) {
__promises = [];
$.each(grants, function(i, user) {
__usernames.push(user.id);
if (_users[user.id]) {
@ -472,6 +544,8 @@ var _authorization_users = function() {
backends: [user.backend],
roles: [],
groups: [],
admin_by: null,
moderator_by: null,
grants: [user.grant],
raw: [user],
};
@ -482,12 +556,14 @@ var _authorization_users = function() {
p = $.getJSON('{{ url_for("api.acl_is_admin", member="") }}'+user.id).done(function (data) {
if (data.admin) {
_users[user.id]['roles'].push('admin');
_users[user.id]['admin_by'] = data.inherit;
}
});
__promises.push(p);
p = $.getJSON('{{ url_for("api.acl_is_moderator", member="") }}'+user.id).done(function (data) {
if (data.moderator) {
_users[user.id]['roles'].push('moderator');
_users[user.id]['moderator_by'] = data.inherit;
}
});
__promises.push(p);
@ -506,15 +582,17 @@ var _authorization_users = function() {
});
if (redraw) {
$.when.apply( $, __globals_promises ).always(function() {
_users_table.clear();
_users_table.rows.add(_users_array).draw();
_users_table.fixedHeader.adjust();
$('#waiting-user-container').hide();
$('#table-users-container').show();
$.when.apply( $, __promises ).always(function() {
_users_table.clear();
_users_table.rows.add(_users_array).draw();
_users_table.fixedHeader.adjust();
$('#waiting-user-container').hide();
$('#table-users-container').show();
});
});
}
$.when.apply( $, __top_promises ).done(function() {
$.when.apply( $, __promises ).done(function() {
$.when.apply( $, __promises ).always(function() {
_users_array = [];
$.each(_users, function(key, value) {
_users_array.push(value);
@ -536,6 +614,7 @@ var _authorization_groups = function() {
$('#table-groups-container').hide();
var __groupnames = [];
var __top_promises = [];
var __promises = [];
var t = $.getJSON('{{ url_for("api.acl_groups") }}').done(function (groups) {
$.each(groups, function(i, group) {
__groupnames.push(group.id);
@ -554,10 +633,27 @@ var _authorization_groups = function() {
_groups[group.id] = {
id: group.id,
backends: [group.backend],
roles: [],
admin_by: null,
moderator_by: null,
members: group.members,
grants: [group.grant],
raw: [group],
};
var p = $.getJSON('{{ url_for("api.acl_is_admin", member="") }}@'+group.id).done(function (data) {
if (data.admin) {
_groups[group.id]['roles'].push('admin');
_groups[group.id]['admin_by'] = data.inherit;
}
});
__promises.push(p);
p = $.getJSON('{{ url_for("api.acl_is_moderator", member="") }}@'+group.id).done(function (data) {
if (data.moderator) {
_groups[group.id]['roles'].push('moderator');
_groups[group.id]['moderator_by'] = data.inherit;
}
});
__promises.push(p);
}
});
__top_promises.push(t);
@ -573,24 +669,28 @@ var _authorization_groups = function() {
});
if (redraw) {
$.when.apply( $, __globals_promises ).always(function() {
_groups_table.clear();
_groups_table.rows.add(_groups_array).draw();
_groups_table.fixedHeader.adjust();
$('#waiting-group-container').hide();
$('#table-groups-container').show();
$.when.apply( $, __promises ).always(function() {
_groups_table.clear();
_groups_table.rows.add(_groups_array).draw();
_groups_table.fixedHeader.adjust();
$('#waiting-group-container').hide();
$('#table-groups-container').show();
});
});
}
$.when.apply( $, __top_promises ).done(function() {
_groups_array = [];
$.each(_groups, function(key, value) {
_groups_array.push(value);
});
$.when.apply( $, __globals_promises ).always(function() {
_groups_table.clear();
_groups_table.rows.add(_groups_array).draw();
_groups_table.fixedHeader.adjust();
$('#waiting-group-container').hide();
$('#table-groups-container').show();
$.when.apply( $, __promises ).always(function() {
_groups_array = [];
$.each(_groups, function(key, value) {
_groups_array.push(value);
});
$.when.apply( $, __globals_promises ).always(function() {
_groups_table.clear();
_groups_table.rows.add(_groups_array).draw();
_groups_table.fixedHeader.adjust();
$('#waiting-group-container').hide();
$('#table-groups-container').show();
});
});
});
});

View file

@ -1,4 +1,5 @@
{% import 'macros.html' as macros %}
{% set gpname = "@"+group %}
var _admin = function() {
// do nothing
@ -20,6 +21,10 @@ app.controller('AdminCtrl', ['$scope', '$http', '$q', '$scrollspy', 'DTOptionsBu
$scope.grantValue = '{{ _("Loading, please wait...") }}';
$scope.isLoading = true;
$scope.orig = {};
$scope.isAdmin = false;
$scope.isAdminEnabled = false;
$scope.isModerator = false;
$scope.isModeratorEnabled = false;
vm.updateGroup = {};
vm.updateGroup.backendUsers = [];
@ -61,6 +66,19 @@ app.controller('AdminCtrl', ['$scope', '$http', '$q', '$scrollspy', 'DTOptionsBu
});
_g_promises.push(g);
$http.get('{{ url_for("api.acl_is_admin", backend=backend, member=gpname) }}', { headers: { 'X-From-UI': true } })
.then(function (response) {
$scope.isAdmin = response.data.admin;
$scope.orig['admin'] = response.data.admin;
$scope.isAdminEnabled = true;
});
$http.get('{{ url_for("api.acl_is_moderator", backend=backend, member=gpname) }}', { headers: { 'X-From-UI': true } })
.then(function (response) {
$scope.isModerator = response.data.moderator;
$scope.orig['moderator'] = response.data.moderator;
$scope.isModeratorEnabled = true;
});
$q.all(_g_promises).finally(function () {
var _all = _.map(vm.updateGroup.backendUsers, 'id');
var missing = _.difference(vm.updateGroup.groupMembers, _all);
@ -87,6 +105,44 @@ app.controller('AdminCtrl', ['$scope', '$http', '$q', '$scrollspy', 'DTOptionsBu
submit.html(sav);
submit.attr('disabled', false);
};
if ($scope.isAdmin !== $scope.orig.admin) {
disableSubmit();
var url = '{{ url_for("api.acl_admins", backend=backend, member=gpname) }}';
var method = 'PUT';
if (!$scope.isAdmin) {
method = 'DELETE';
}
var p = $http({
url: url,
method: method,
headers: { 'X-From-UI': true },
})
.catch(buiFail)
.then(function(response) {
$scope.orig.admin = $scope.isAdmin;
notifAll(response.data);
});
promises.push(p);
}
if ($scope.isModerator !== $scope.orig.moderator) {
disableSubmit();
var url = '{{ url_for("api.acl_moderators", backend=backend, member=gpname) }}';
var method = 'PUT';
if (!$scope.isModerator) {
method = 'DELETE';
}
var p = $http({
url: url,
method: method,
headers: { 'X-From-UI': true },
})
.catch(buiFail)
.then(function(response) {
$scope.orig.moderator = $scope.isModerator;
notifAll(response.data);
});
promises.push(p);
}
if ($scope.grantValue != $scope.orig.grantValue) {
disableSubmit();
var p = $http({

View file

@ -53,7 +53,7 @@
<div class="container-fluid">
<div class="row" {% if ng_controller %}ng-controller="{{ ng_controller }} as pctrl"{% endif %}>
{% if not live and not login and not about and not authentication and not authorization and not sessions -%}
{% if not live and not login and not about -%}
{% include "sidebar.html" %}
{% else -%}
{% set no_sidebar = True %}

View file

@ -2,7 +2,7 @@
<div id="navbar-config">
<h4>{{ _('Administration navigation') }}</h4>
<ul class="nav nav-sidebar">
<li {% if authentications %}class="active"{% endif %}><a href="{{ url_for('view.admin_authentications') }}"><i class="fa fa-fw fa-id-card-o" aria-hidden="true"></i>&nbsp;{{ _('Authentication') }}</a></li>
<li {% if authorizations %}class="active"{% endif %}><a href="{{ url_for('view.admin_authorizations') }}"><i class="fa fa-fw fa-lock" aria-hidden="true"></i>&nbsp;{{ _('Authorization') }}</a></li>
<li {% if authentications or sessions or authentication %}class="active"{% endif %}><a href="{{ url_for('view.admin_authentications') }}"><i class="fa fa-fw fa-id-card-o" aria-hidden="true"></i>&nbsp;{{ _('Authentication') }}</a></li>
<li {% if authorizations or authorization %}class="active"{% endif %}><a href="{{ url_for('view.admin_authorizations') }}"><i class="fa fa-fw fa-lock" aria-hidden="true"></i>&nbsp;{{ _('Authorization') }}</a></li>
</ul>
</div>