Merge pull request #1975 from anoek/auditor-support

Add support for creating system auditors
This commit is contained in:
Akita Noek 2016-05-20 13:33:21 -04:00
commit ba19e0e105
7 changed files with 149 additions and 38 deletions

View File

@ -679,12 +679,13 @@ class UserSerializer(BaseSerializer):
password = serializers.CharField(required=False, default='', write_only=True,
help_text='Write-only field used to change the password.')
ldap_dn = serializers.CharField(source='profile.ldap_dn', read_only=True)
is_system_auditor = serializers.BooleanField(default=False)
class Meta:
model = User
fields = ('*', '-name', '-description', '-modified',
'-summary_fields', 'username', 'first_name', 'last_name',
'email', 'is_superuser', 'password', 'ldap_dn')
'email', 'is_superuser', 'is_system_auditor', 'password', 'ldap_dn')
def to_representation(self, obj):
ret = super(UserSerializer, self).to_representation(obj)

View File

@ -720,14 +720,31 @@ class OrganizationInventoriesList(SubListAPIView):
parent_model = Organization
relationship = 'inventories'
class OrganizationUsersList(SubListCreateAttachDetachAPIView):
class BaseUsersList(SubListCreateAttachDetachAPIView):
def post(self, request, *args, **kwargs):
ret = super(BaseUsersList, self).post( request, *args, **kwargs)
try:
if request.data.get('is_system_auditor', False):
# This is a faux-field that just maps to checking the system
# auditor role member list.. unfortunately this means we can't
# set it on creation, and thus needs to be set here.
user = User.objects.get(id=ret.data['id'])
user.is_system_auditor = request.data['is_system_auditor']
ret.data['is_system_auditor'] = request.data['is_system_auditor']
except AttributeError as exc:
print(exc)
pass
return ret
class OrganizationUsersList(BaseUsersList):
model = User
serializer_class = UserSerializer
parent_model = Organization
relationship = 'member_role.members'
class OrganizationAdminsList(SubListCreateAttachDetachAPIView):
class OrganizationAdminsList(BaseUsersList):
model = User
serializer_class = UserSerializer
@ -830,7 +847,7 @@ class TeamDetail(RetrieveUpdateDestroyAPIView):
model = Team
serializer_class = TeamSerializer
class TeamUsersList(SubListCreateAttachDetachAPIView):
class TeamUsersList(BaseUsersList):
model = User
serializer_class = UserSerializer
@ -1097,6 +1114,21 @@ class UserList(ListCreateAPIView):
model = User
serializer_class = UserSerializer
def post(self, request, *args, **kwargs):
ret = super(UserList, self).post( request, *args, **kwargs)
try:
if request.data.get('is_system_auditor', False):
# This is a faux-field that just maps to checking the system
# auditor role member list.. unfortunately this means we can't
# set it on creation, and thus needs to be set here.
user = User.objects.get(id=ret.data['id'])
user.is_system_auditor = request.data['is_system_auditor']
ret.data['is_system_auditor'] = request.data['is_system_auditor']
except AttributeError as exc:
print(exc)
pass
return ret
class UserMeList(ListAPIView):
model = User
@ -1228,17 +1260,25 @@ class UserDetail(RetrieveUpdateDestroyAPIView):
obj = self.get_object()
can_change = request.user.can_access(User, 'change', obj, request.data)
can_admin = request.user.can_access(User, 'admin', obj, request.data)
su_only_edit_fields = ('is_superuser', 'is_system_auditor')
admin_only_edit_fields = ('last_name', 'first_name', 'username', 'is_active')
fields_to_check = ()
if not request.user.is_superuser:
fields_to_check += su_only_edit_fields
if can_change and not can_admin:
admin_only_edit_fields = ('last_name', 'first_name', 'username',
'is_active', 'is_superuser')
changed = {}
for field in admin_only_edit_fields:
left = getattr(obj, field, None)
right = request.data.get(field, None)
if left is not None and right is not None and left != right:
changed[field] = (left, right)
if changed:
raise PermissionDenied('Cannot change %s.' % ', '.join(changed.keys()))
fields_to_check += admin_only_edit_fields
bad_changes = {}
for field in fields_to_check:
left = getattr(obj, field, None)
right = request.data.get(field, None)
if left is not None and right is not None and left != right:
bad_changes[field] = (left, right)
if bad_changes:
raise PermissionDenied('Cannot change %s.' % ', '.join(bad_changes.keys()))
def destroy(self, request, *args, **kwargs):
obj = self.get_object()

View File

@ -55,6 +55,20 @@ def user_get_admin_of_organizations(user):
User.add_to_class('organizations', user_get_organizations)
User.add_to_class('admin_of_organizations', user_get_admin_of_organizations)
@property
def user_is_system_auditor(user):
return Role.singleton('system_auditor').members.filter(id=user.id).exists()
@user_is_system_auditor.setter
def user_is_system_auditor(user, tf):
if user.id:
if tf:
Role.singleton('system_auditor').members.add(user)
else:
Role.singleton('system_auditor').members.remove(user)
User.add_to_class('is_system_auditor', user_is_system_auditor)
# Import signal handlers only after models have been defined.
import awx.main.signals # noqa

View File

@ -49,7 +49,7 @@
min-height: 40px;
}
.Form-title--is_superuser{
.Form-title--is_superuser, .Form-title--is_system_auditor, .Form-title--is_ldap_user{
height:15px;
color: @default-interface-txt;
background-color: @default-list-header-bg;
@ -61,7 +61,7 @@
margin-left: 10px;
text-transform: uppercase;
font-weight: 100;
position: absolute;
//position: absolute;
margin-top: 2.25px;
height: 16px;
}

View File

@ -10,6 +10,26 @@
* @description This controller's the Users page
*/
const user_type_options = [
{type: 'normal' , label: 'Normal User' },
{type: 'system_auditor' , label: 'System Auditor' },
{type: 'system_administrator', label: 'System Administrator' },
];
function user_type_sync($scope) {
return (type_option) => {
$scope.is_superuser = false;
$scope.is_system_auditor = false;
switch (type_option.type) {
case 'system_administrator':
$scope.is_superuser = true;
break;
case 'system_auditor':
$scope.is_system_auditor = true;
break;
}
};
}
export function UsersList($scope, $rootScope, $location, $log, $stateParams,
Rest, Alert, UserList, GenerateList, Prompt, SearchInit, PaginateInit,
@ -116,10 +136,13 @@ UsersList.$inject = ['$scope', '$rootScope', '$location', '$log',
];
export function UsersAdd($scope, $rootScope, $compile, $location, $log,
$stateParams, UserForm, GenerateForm, Rest, Alert, ProcessErrors,
ReturnToCaller, ClearScope, GetBasePath, LookUpInit, OrganizationList,
ResetForm, Wait, $state) {
ResetForm, Wait, CreateSelect2, $state) {
ClearScope();
@ -138,6 +161,15 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
generator.reset();
$scope.user_type_options = user_type_options;
$scope.user_type = user_type_options[0]
$scope.$watch('user_type', user_type_sync($scope));
CreateSelect2({
element: '#user_user_type',
multiple: false
});
// Configure the lookup dialog. If we're adding a user through the Organizations tab,
// default the Organization value.
LookUpInit({
@ -177,7 +209,8 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
data[fld] = $scope[fld];
}
}
data.is_superuser = data.is_superuser || false;
data.is_superuser = $scope.is_superuser;
data.is_system_auditor = $scope.is_system_auditor;
Wait('start');
Rest.post(data)
.success(function (data) {
@ -215,14 +248,14 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
UsersAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert',
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', '$state'
'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', 'CreateSelect2', '$state'
];
export function UsersEdit($scope, $rootScope, $location,
$stateParams, UserForm, GenerateForm, Rest, ProcessErrors,
RelatedSearchInit, RelatedPaginateInit, ClearScope,
GetBasePath, ResetForm, Wait, $state) {
GetBasePath, ResetForm, Wait, CreateSelect2 ,$state) {
ClearScope();
@ -237,6 +270,10 @@ export function UsersEdit($scope, $rootScope, $location,
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
generator.reset();
$scope.user_type_options = user_type_options;
$scope.user_type = user_type_options[0]
$scope.$watch('user_type', user_type_sync($scope));
var setScopeFields = function(data){
_(data)
.pick(function(value, key){
@ -278,6 +315,8 @@ export function UsersEdit($scope, $rootScope, $location,
data[key] = $scope[key];
}
});
data.is_superuser = $scope.is_superuser;
data.is_system_auditor = $scope.is_system_auditor;
return data;
};
@ -292,6 +331,24 @@ export function UsersEdit($scope, $rootScope, $location,
master.ldap_user = $scope.ldap_user;
$scope.socialAuthUser = (data.auth.length > 0) ? true : false;
$scope.user_type = $scope.user_type_options[0];
$scope.is_system_auditor = false;
$scope.is_superuser = false;
if (data.is_system_auditor) {
$scope.user_type = $scope.user_type_options[1];
$scope.is_system_auditor = true;
}
if (data.is_superuser) {
$scope.user_type = $scope.user_type_options[2];
$scope.is_superuser = true;
}
CreateSelect2({
element: '#user_user_type',
multiple: false
});
setScopeFields(data);
setScopeRelated(data, form.related);
@ -353,5 +410,5 @@ export function UsersEdit($scope, $rootScope, $location,
UsersEdit.$inject = ['$scope', '$rootScope', '$location',
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'ProcessErrors',
'RelatedSearchInit', 'RelatedPaginateInit', 'ClearScope', 'GetBasePath',
'ResetForm', 'Wait', '$state'
'ResetForm', 'Wait', 'CreateSelect2', '$state'
];

View File

@ -87,21 +87,14 @@ export default
associated: 'password',
autocomplete: false
},
is_superuser: {
label: 'Superuser <span style="text-transform:none;">(User has full system administration privileges)</span>',
type: 'checkbox',
trueValue: 'true',
falseValue: 'false',
"default": 'false',
ngShow: "current_user['is_superuser'] == true",
ngModel: 'is_superuser'
user_type: {
label: 'User Type',
type: 'select',
ngOptions: 'item as item.label for item in user_type_options track by item.type',
disableChooseOption: true,
ngModel: 'user_type',
ngShow: 'current_user["is_superuser"]',
},
ldap_user: {
label: 'Created by LDAP',
type: 'checkbox',
readonly: true,
awFeature: 'ldap'
}
},
buttons: {

View File

@ -918,6 +918,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "<button class='btn btn-default show_input_button Form-passwordButton' ";
html += buildId(field, fld + "_show_input_button", this.form);
html += "aw-tool-tip='Toggle the display of plaintext.' aw-tip-placement='top' ";
html += "tabindex='-1' ";
html += "ng-click='" + fld + "_field.toggleInput(\"#" + this.form.name + "_" + fld + "\")'";
html += (field.ngDisabled) ? "ng-disabled='" + field.ngDisabled + "'" : "";
html += ">\n" + field.showInputInnerHTML;
@ -1100,7 +1101,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "<div class=\"Form-dropDownContainer\">\n";
html += "<select ";
html += "ng-model=\"" + fld + '" ';
html += "ng-model=\"" + (field.ngModel ? field.ngModel : fld) + '" ';
html += 'name="' + fld + '" ';
html += "class=\"form-control Form-dropDown";
html += "\" ";
@ -1108,6 +1109,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += (field.ngChange) ? this.attr(field, 'ngChange') : "";
html += (field.ngDisabled) ? this.attr(field, 'ngDisabled'): "";
html += (field.ngRequired) ? this.attr(field, 'ngRequired') : "";
html += (field.ngInit) ? this.attr(field, 'ngInit') : "";
html += buildId(field, fld, this.form);
html += (options.mode === 'edit' && field.editRequired) ? "required " : "";
html += (options.mode === 'add' && field.addRequired) ? "required " : "";
@ -1120,7 +1122,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += field.awRequiredWhen.alwaysShowAsterisk ? "data-awrequired-always-show-asterisk=true " : "";
}
html += ">\n";
if(!field.multiSelect){
if(!field.multiSelect && !field.disableChooseOption){
html += "<option value=\"\">";
// Add a custom default select 'value' (default text)
html += (field.defaultText) ? field.defaultText : "Choose a " + field.label.toLowerCase();
@ -1468,7 +1470,11 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += (options.mode === 'edit') ? this.form.editTitle : this.form.addTitle;
if(this.form.name === "user"){
html+= "<span class=\"Form-title--is_superuser\" "+
"ng-if=is_superuser>System Administrator</span>";
"ng-show='is_superuser'>Admin</span>";
html+= "<span class=\"Form-title--is_system_auditor\" "+
"ng-show='is_system_auditor'>Auditor</span>";
html+= "<span class=\"Form-title--is_ldap_user\" "+
"ng-show='ldap_user'>LDAP</span>";
}
html += "</div>\n";
html += "<div class=\"Form-header--fields\">";