mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 11:20:39 -03:30
Merge pull request #1975 from anoek/auditor-support
Add support for creating system auditors
This commit is contained in:
commit
ba19e0e105
@ -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)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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'
|
||||
];
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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\">";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user