1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-27 09:25:10 +03:00

Finished security around user->permissions and team->permissions. Found bugs in permission controller that appeared as though some development had not been finished. User was denied access to edit/view when clicking button but not when entering URL directly in browser. Now non-privileged user can view permssions for Users and Teams to which access is granted. Add and Delete buttons are now hidden where access is disallowed and navigating directly to add page throws an error when Save button clicked.

Fixed issue where new CSS for adding asterisk to required fields caused login dialog fields to display multiple asterisks.
  Modified form generator to show help icon to the left of * on required fields.
This commit is contained in:
chouseknecht 2013-09-12 00:23:02 -04:00
parent 5f3eb290ef
commit e5c5fd3d80
11 changed files with 119 additions and 67 deletions

1
.gitignore vendored
View File

@ -20,3 +20,4 @@ coverage.xml
pep8.txt
.vagrant*
.tox
nohup.out

View File

@ -15,6 +15,8 @@ function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Res
var scope = view.inject(list, { mode: 'edit' }); // Inject our view
scope.selected = [];
CheckAccess({ scope: scope });
SearchInit({ scope: scope, set: 'permissions', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl });
scope.search(list.iterator);
@ -22,16 +24,14 @@ function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Res
LoadBreadCrumbs();
scope.addPermission = function() {
if (CheckAccess()) {
if (scope.PermissionAddAllowed) {
$location.path($location.path() + '/add');
}
}
scope.editPermission = function(id) {
if (CheckAccess()) {
$location.path($location.path() + '/' + id);
}
}
scope.deletePermission = function(id, name) {
var action = function() {
@ -49,7 +49,7 @@ function PermissionsList ($scope, $rootScope, $location, $log, $routeParams, Res
});
};
if (checkAccess()) {
if (scope.PermissionAddAllowed) {
Prompt({ hdr: 'Delete',
body: 'Are you sure you want to delete ' + name + '?',
action: action
@ -66,7 +66,7 @@ PermissionsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$route
function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ClearScope,
GetBasePath, ReturnToCaller, InventoryList, ProjectList, LookUpInit)
GetBasePath, ReturnToCaller, InventoryList, ProjectList, LookUpInit, CheckAccess)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@ -80,6 +80,7 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
var scope = generator.inject(form, {mode: 'add', related: false});
var master = {};
CheckAccess({ scope: scope })
generator.reset();
LoadBreadCrumbs();
@ -108,6 +109,7 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
// Save
scope.formSave = function() {
if (scope.PermissionAddAllowed) {
var data = {};
for (var fld in form.fields) {
data[fld] = scope[fld];
@ -122,6 +124,10 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
ProcessErrors(scope, data, status, PermissionsForm,
{ hdr: 'Error!', msg: 'Failed to create new permission. Post returned status: ' + status });
});
}
else {
Alert('Access Denied', 'You do not have access to create new permission objects. Please contact a system administrator.', 'alert-danger');
}
};
// Cancel
@ -146,13 +152,13 @@ function PermissionsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
PermissionsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ClearScope', 'GetBasePath',
'ReturnToCaller', 'InventoryList', 'ProjectList', 'LookUpInit'
'ReturnToCaller', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess'
];
function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, PermissionsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller,
ClearScope, Prompt, GetBasePath, InventoryList, ProjectList, LookUpInit)
ClearScope, Prompt, GetBasePath, InventoryList, ProjectList, LookUpInit, CheckAccess)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@ -169,6 +175,7 @@ function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
var master = {};
var relatedSets = {};
CheckAccess({ scope: scope })
// Retrieve detail record and prepopulate the form
Rest.setUrl(defaultUrl);
@ -215,6 +222,20 @@ function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
list: ProjectList,
field: 'project'
});
if (!scope.PermissionAddAllowed) {
// If not a privileged user, disable access
$('form[name="permission_form"]').find('select, input, button').each(function(index){
if ($(this).is('input') || $(this).is('select')) {
$(this).attr('readonly','readonly');
}
if ( $(this).is('input[type="checkbox"]') ||
$(this).is('input[type="radio"]') ||
$(this).is('button') ) {
$(this).attr('disabled','disabled');
}
});
}
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
@ -263,6 +284,6 @@ function PermissionsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
PermissionsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'PermissionsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller',
'ClearScope', 'Prompt', 'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit'
'ClearScope', 'Prompt', 'GetBasePath', 'InventoryList', 'ProjectList', 'LookUpInit', 'CheckAccess'
];

View File

@ -274,13 +274,8 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
scope.edit = function(set, id, name) {
$rootScope.flashMessage = null;
if (set == 'permissions') {
if (scope.PermissionAddAllowed) {
$location.path('/' + base + '/' + $routeParams.team_id + '/' + set + '/' + id);
}
else {
Alert('Access Denied', 'You do not have access to this function. Please contact your system administrator.');
}
}
else {
$location.path('/' + set + '/' + id);
}

View File

@ -201,11 +201,14 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
scope.PermissionAddAllowed = false;
// After the Organization is loaded, retrieve each related set
scope.$on('userLoaded', function() {
if (scope.removeUserLoaded) {
scope.removeUserLoaded();
}
scope.removeUserLoaded = scope.$on('userLoaded', function() {
for (var set in relatedSets) {
scope.search(relatedSets[set].iterator);
}
CheckAccess({ scope: scope }); //Does the user have access add Permissions?
CheckAccess({ scope: scope }); //Does the user have access to add/edit Permissions?
});
// Retrieve detail record and prepopulate the form
@ -311,13 +314,8 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
scope.edit = function(set, id, name) {
$rootScope.flashMessage = null;
if (set == 'permissions') {
if (scope.PermissionAddAllowed) {
$location.path('/users/' + $routeParams.user_id + '/permissions/' + id);
}
else {
Alert('Access Denied', 'You do not have access to this function. Please contact your system administrator.');
}
}
else {
$location.path('/' + set + '/' + id);
}

View File

@ -85,10 +85,10 @@ angular.module('JobTemplateFormDefinition', [])
forks: {
label: 'Forks',
id: 'forks-number',
type: 'number',
integer: true,
type: 'text',
/*integer: true,
min: 0,
spinner: true,
spinner: true,*/
"default": '0',
addRequired: false,
editRequired: false,

View File

@ -116,7 +116,8 @@ angular.module('TeamFormDefinition', [])
ngClick: "add('permissions')",
icon: 'icon-plus',
label: 'Add',
awToolTip: 'Add a permission for this user'
awToolTip: 'Add a permission for this user',
ngShow: 'PermissionAddAllowed'
}
},
@ -157,7 +158,8 @@ angular.module('TeamFormDefinition', [])
ngClick: "delete('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}', 'permissions')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Delete the permission'
awToolTip: 'Delete the permission',
ngShow: 'PermissionAddAllowed'
}
}

View File

@ -164,7 +164,7 @@ angular.module('UserFormDefinition', [])
icon: 'icon-plus',
label: 'Add',
awToolTip: 'Add a permission for this user',
ngShow: 'PermissionAddAllowed == true'
ngShow: 'PermissionAddAllowed'
}
},
@ -206,7 +206,8 @@ angular.module('UserFormDefinition', [])
ngClick: "delete('permissions', \{\{ permission.id \}\}, '\{\{ permission.name \}\}', 'permissions')",
icon: 'icon-trash',
"class": 'btn-danger',
awToolTip: 'Delete the permission'
awToolTip: 'Delete the permission',
ngShow: 'PermissionAddAllowed'
}
}

View File

@ -48,7 +48,8 @@ angular.module('PermissionListDefinition', [])
mode: 'all', // One of: edit, select, all
ngClick: 'addPermission()',
"class": 'btn-success btn-sm',
awToolTip: 'Add a new permission'
awToolTip: 'Add a new permission',
ngShow: 'PermissionAddAllowed'
}
},
@ -66,7 +67,8 @@ angular.module('PermissionListDefinition', [])
ngClick: "deletePermission(\{\{ permission.id \}\},'\{\{ permission.name \}\}')",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete permission'
awToolTip: 'Delete permission',
ngShow: 'PermissionAddAllowed'
}
}
});

View File

@ -49,7 +49,7 @@ body {
}
.prepend-asterisk:before {
content: "\002A";
content: "\002A\00A0";
}
.subtitle {
@ -82,6 +82,10 @@ hr {
border-color: #e3e3e3;
}
.help {
display: inline-block;
}
.nowrap {
white-space: nowrap;
}

View File

@ -93,7 +93,6 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
$('.form-control[required]').each(function() {
var label = $(this).parent().parent().find('label');
if (label && !label.hasClass('prepend-asterisk')) {
console.log('adding prepend');
label.addClass('prepend-asterisk');
}
});
@ -377,21 +376,24 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
//text fields
if (field.type == 'text' || field.type == 'password' || field.type == 'email') {
html += "<label ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += "class=\"control-label " + getLabelWidth();
html += "<div class=\"text-right " + getLabelWidth();
html += (field.labelClass) ? " " + field.labelClass : "";
html += "\" ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += ">\n";
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
html += "<label ";
html += "class=\"control-label";
html += "\" ";
html += (field.labelBind) ? "ng-bind=\"" + field.labelBind + "\" " : "";
html += "for=\"" + fld + '">';
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
html += (field.icon) ? this.icon(field.icon) : "";
html += field.label + '</label>' + "\n";
html += "</div>\n";
html += "<div ";
html += (field.controlNGClass) ? "ng-class=\"" + field.controlNGClass + "\" " : "";
html += "class=\"" + getFieldWidth() + "\">\n";
html += (field.clear || field.genMD5) ? "<div class=\"input-group\">\n" : "";
if (field.control === null || field.control === undefined || field.control) {
html += "<input ";
html += this.attr(field,'type');
@ -491,9 +493,15 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
if (field.type == 'textarea') {
if (field.label !== false) {
html += "<label class=\"control-label " + getLabelWidth() + "\" for=\"" + fld + '">';
html += "<div class=\"text-right " + getLabelWidth();
html += (field.labelClass) ? " " + field.labelClass : "";
html += "\" ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += ">\n";
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
html += "<label class=\"control-label\" for=\"" + fld + '">';
html += field.label + '</label>' + "\n";
html += "</div>\n";
html += "<div ";
html += (field.controlNGClass) ? "ng-class=\"" + field.controlNGClass + "\" " : "";
html += "class=\"" + getFieldWidth() + "\">\n";
@ -537,9 +545,15 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
//select field
if (field.type == 'select') {
html += "<label class=\"control-label " + getLabelWidth() + "\" for=\"" + fld + '">';
html += "<div class=\"text-right " + getLabelWidth();
html += (field.labelClass) ? " " + field.labelClass : "";
html += "\" ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += ">\n";
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
html += "<label class=\"control-label\" for=\"" + fld + '">';
html += field.label + '</label>' + "\n";
html += "</div>\n";
html += "<div ";
html += (field.controlNGClass) ? "ng-class=\"" + field.controlNGClass + "\" " : "";
html += "class=\"" + getFieldWidth() + "\">\n";
@ -563,7 +577,8 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
html += "</option>\n";
html += "</select>\n";
// Add error messages
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ) {
if ( (options.mode == 'add' && field.addRequired) || (options.mode == 'edit' && field.editRequired) ||
field.awRequiredWhen ) {
html += "<div class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
this.form.name + '_form.' + fld + ".$error.required\">A value is required!</div>\n";
}
@ -686,9 +701,15 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
//lookup type fields
if (field.type == 'lookup' && (field.excludeMode == undefined || field.excludeMode != options.mode)) {
html += "<label class=\"control-label " + getLabelWidth() + "\" for=\"" + fld + '">';
html += "<div class=\"text-right " + getLabelWidth();
html += (field.labelClass) ? " " + field.labelClass : "";
html += "\" ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += ">\n";
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
html += "<label class=\"control-label\" for=\"" + fld + '">';
html += field.label + '</label>' + "\n";
html += "</div>\n";
html += "<div ";
html += (field.controlNGClass) ? "ng-class=\"" + field.controlNGClass + "\" " : "";
html += "class=\"" + getFieldWidth() + "\">\n";
@ -727,13 +748,18 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
//custom fields
if (field.type == 'custom') {
html += "<label class=\"control-label " + getLabelWidth();
html += "<div class=\"text-right " + getLabelWidth();
html += (field.labelClass) ? " " + field.labelClass : "";
html += "\" for=\"" + fld + '">';
html += "\" ";
html += (field.labelNGClass) ? "ng-class=\"" + field.labelNGClass + "\" " : "";
html += ">\n";
html += (field.awPopOver) ? this.attr(field, 'awPopOver', fld) : "";
html += "<label class=\"control-label\" for=\"" + fld + '">';
html += (field.icon) ? this.icon(field.icon) : "";
html += (field.label) ? field.label : '';
html += '</label>' + "\n";
html += "</div>\n";
html += "<div ";
html += (field.controlNGClass) ? "ng-class=\"" + field.controlNGClass + "\" " : "";
html += "class=\"" + getFieldWidth() + "\">\n";
@ -1362,7 +1388,9 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
var action = form.related[itm].fieldActions[act];
html += "<button type=\"button\" class=\"btn btn-xs";
html += (action['class']) ? " " + action['class'] : "";
html += "\" " + this.attr(action,'ngClick');
html += "\" ";
html += this.attr(action,'ngClick');
html += this.attr(action, 'ngShow');
html += (action.awToolTip) ? this.attr(action,'awToolTip') : "";
html += (action.awToolTip) ? "data-placement=\"top\" " : "";
html += ">" + this.icon(action.icon);

View File

@ -167,7 +167,7 @@
<div class="login-alert" ng-show="(sessionExpired == true)">Your session timed out due to inactivity. Please sign in.</div>
<form id="login-form" name="loginForm" class="form-horizontal" autocomplete="off" novalidate >
<div class="form-group">
<label class="control-label col-lg-3">* Username</label>
<label class="control-label col-lg-3 prepend-asterisk">Username</label>
<div class="col-lg-8">
<input type="text" name="login_username" class="form-control" ng-model="login_username"
id="login-username" autocomplete="off" required>
@ -178,7 +178,7 @@
</div>
</div>
<div class="form-group">
<label class="control-label col-lg-3">* Password</label>
<label class="control-label col-lg-3 prepend-asterisk">Password</label>
<div class="col-lg-8">
<input type="password" name="login_password" id="login-password" class="form-control"
ng-model="login_password" required autocomplete="off">
@ -303,7 +303,7 @@
<div class="alert" ng-class="alertClass" ng-bind-html-unsafe="alertBody"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" class="btn btn-default">OK</a>
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" class="btn btn-primary">OK</a>
</div>
</div><!-- modal-content -->
</div><!-- modal-dialog -->
@ -321,7 +321,7 @@
<div class="alert" ng-class="alertClass2" ng-bind-html-unsafe="alertBody2"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" class="btn btn-default">OK</a>
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" class="btn btn-primary">OK</a>
</div>
</div><!-- modal-content -->
</div><!-- modal-dialog -->