def save_model(self, request, obj, form, change): """ Audit project membership and qualifications when a user is saved. """ super(UserAdmin, self).save_model(request, obj, form, change) record_local_many_to_many_changes(request, obj, form, "projects") record_local_many_to_many_changes(request, obj, form, "qualifications") record_local_many_to_many_changes(request, obj, form, "physical_access_levels") record_active_state(request, obj, form, "is_active", not change)
def save_model(self, request, obj, form, change): """ Audit project membership and qualifications when a user is saved. """ super(UserAdmin, self).save_model(request, obj, form, change) record_local_many_to_many_changes(request, obj, form, "projects") record_local_many_to_many_changes(request, obj, form, "qualifications") record_local_many_to_many_changes(request, obj, form, "physical_access_levels") record_active_state(request, obj, form, "is_active", not change) if "backup_owner_on_tools" in form.changed_data: obj.backup_for_tools.set( form.cleaned_data["backup_owner_on_tools"]) if "superuser_on_tools" in form.changed_data: obj.superuser_for_tools.set( form.cleaned_data["superuser_on_tools"])
def create_or_modify_user(request, user_id): identity_service = get_identity_service() # Get access levels and sort by area category access_levels = list(PhysicalAccessLevel.objects.all().only( 'name', 'area')) access_level_for_sort = list( set([ ancestor for access in access_levels for ancestor in access.area.get_ancestors(include_self=True) ])) access_level_for_sort.sort(key=lambda x: x.tree_category()) area_access_levels = Area.objects.filter( id__in=[area.id for area in access_level_for_sort]) dict_area = {} for access in access_levels: dict_area.setdefault(access.area.id, []).append(access) dictionary = { 'projects': Project.objects.filter(active=True, account__active=True), 'tools': Tool.objects.filter(visible=True), 'area_access_dict': dict_area, 'area_access_levels': area_access_levels, 'one_year_from_now': timezone.now() + timedelta(days=365), 'identity_service_available': identity_service.get('available', False), 'identity_service_domains': identity_service.get('domains', []), } try: user = User.objects.get(id=user_id) except: user = None timeout = identity_service.get('timeout', 3) site_title = get_customization('site_title') if dictionary['identity_service_available']: try: result = requests.get(urljoin(identity_service['url'], '/areas/'), timeout=timeout) if result.status_code == HTTPStatus.OK: dictionary[ 'externally_managed_physical_access_levels'] = result.json( ) else: dictionary['identity_service_available'] = False warning_message = f"The identity service encountered a problem while attempting to return a list of externally managed areas. The administrator has been notified to resolve the problem." dictionary['warning'] = warning_message warning_message += ' The HTTP error was {}: {}'.format( result.status_code, result.text) users_logger.error(warning_message) except Exception as e: dictionary['identity_service_available'] = False warning_message = f"There was a problem communicating with the identity service. {site_title} is unable to retrieve the list of externally managed areas. The administrator has been notified to resolve the problem." dictionary['warning'] = warning_message warning_message += ' An exception was encountered: ' + type( e).__name__ + ' - ' + str(e) users_logger.error(warning_message) elif identity_service: # display warning if identity service is defined but disabled dictionary[ 'warning'] = 'The identity service is disabled. You will not be able to modify externally managed physical access levels, reset account passwords, or unlock accounts.' if request.method == 'GET': dictionary['form'] = UserForm(instance=user) try: if dictionary[ 'identity_service_available'] and user and user.is_active and user.domain: parameters = { 'username': user.username, 'domain': user.domain, } result = requests.get(identity_service['url'], parameters, timeout=timeout) if result.status_code == HTTPStatus.OK: dictionary['user_identity_information'] = result.json() elif result.status_code == HTTPStatus.NOT_FOUND: dictionary[ 'warning'] = "The identity service could not find username {} on the {} domain. Does the user's account reside on a different domain? If so, select that domain now and save the user information.".format( user.username, user.domain) else: dictionary['identity_service_available'] = False warning_message = 'The identity service encountered a problem while attempting to search for a user. The administrator has been notified to resolve the problem.' dictionary['warning'] = warning_message warning_message += ' The HTTP error was {}: {}'.format( result.status_code, result.text) users_logger.error(warning_message) except Exception as e: dictionary['identity_service_available'] = False warning_message = f"There was a problem communicating with the identity service. {site_title} is unable to search for a user. The administrator has been notified to resolve the problem." dictionary['warning'] = warning_message warning_message += ' An exception was encountered: ' + type( e).__name__ + ' - ' + str(e) users_logger.error(warning_message) return render(request, 'users/create_or_modify_user.html', dictionary) elif request.method == 'POST': form = UserForm(request.POST, instance=user) dictionary['form'] = form if not form.is_valid(): return render(request, 'users/create_or_modify_user.html', dictionary) # Remove the user account from the domain if it's deactivated, changed domain, or changed username... if dictionary['identity_service_available'] and user: no_longer_active = form.initial[ 'is_active'] is True and form.cleaned_data['is_active'] is False domain_switched = form.initial['domain'] != '' and form.initial[ 'domain'] != form.cleaned_data['domain'] username_changed = form.initial['username'] != form.cleaned_data[ 'username'] if no_longer_active or domain_switched or username_changed: parameters = { 'username': form.initial['username'], 'domain': form.initial['domain'], } try: result = requests.delete(identity_service['url'], data=parameters, timeout=timeout) # If the delete succeeds, or the user is not found, then everything is ok. if result.status_code not in (HTTPStatus.OK, HTTPStatus.NOT_FOUND): dictionary['identity_service_available'] = False users_logger.error( 'The identity service encountered a problem while attempting to delete a user. The HTTP error is {}: {}' .format(result.status_code, result.text)) dictionary[ 'warning'] = 'The user information was not modified because the identity service could not delete the corresponding domain account. The administrator has been notified to resolve the problem.' return render(request, 'users/create_or_modify_user.html', dictionary) except Exception as e: dictionary['identity_service_available'] = False users_logger.error( 'There was a problem communicating with the identity service while attempting to delete a user. An exception was encountered: ' + type(e).__name__ + ' - ' + str(e)) dictionary[ 'warning'] = 'The user information was not modified because the identity service could not delete the corresponding domain account. The administrator has been notified to resolve the problem.' return render(request, 'users/create_or_modify_user.html', dictionary) # Ensure the user account is added and configured correctly on the current domain if the user is active... if dictionary['identity_service_available'] and form.cleaned_data[ 'is_active']: parameters = { 'username': form.cleaned_data['username'], 'domain': form.cleaned_data['domain'], 'badge_number': form.cleaned_data.get('badge_number', ''), 'email': form.cleaned_data.get('email'), 'access_expiration': form.cleaned_data.get('access_expiration'), 'requested_areas': request.POST.getlist('externally_managed_access_levels'), } try: if len(parameters['requested_areas'] ) > 0 and not parameters['badge_number']: dictionary[ 'warning'] = 'A user must have a badge number in order to have area access. Please enter the badge number first, then grant access to areas.' return render(request, 'users/create_or_modify_user.html', dictionary) result = requests.put(identity_service['url'], data=parameters, timeout=timeout) if result.status_code == HTTPStatus.NOT_FOUND: dictionary[ 'warning'] = 'The username was not found on this domain. Did you spell the username correctly in this form and did you select the correct domain? Ensure the user exists on the domain in order to proceed.' return render(request, 'users/create_or_modify_user.html', dictionary) if result.status_code != HTTPStatus.OK: dictionary['identity_service_available'] = False users_logger.error( 'The identity service encountered a problem while attempting to modify a user. The HTTP error is {}: {}' .format(result.status_code, result.text)) dictionary[ 'warning'] = 'The user information was not modified because the identity service encountered a problem while creating the corresponding domain account. The administrator has been notified to resolve the problem.' return render(request, 'users/create_or_modify_user.html', dictionary) except Exception as e: dictionary['identity_service_available'] = False users_logger.error( 'There was a problem communicating with the identity service while attempting to modify a user. An exception was encountered: ' + type(e).__name__ + ' - ' + str(e)) dictionary[ 'warning'] = 'The user information was not modified because the identity service encountered a problem while creating the corresponding domain account. The administrator has been notified to resolve the problem.' return render(request, 'users/create_or_modify_user.html', dictionary) # Only save the user model for now, and wait to process the many-to-many relationships. # This way, many-to-many changes can be recorded. # See this web page for more information: # https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method user = form.save(commit=False) user.save() record_active_state(request, user, form, 'is_active', user_id == 'new') record_local_many_to_many_changes(request, user, form, 'qualifications') record_local_many_to_many_changes(request, user, form, 'physical_access_levels') record_local_many_to_many_changes(request, user, form, 'projects') form.save_m2m() message = f"{user} has been added successfully to {site_title}" if user_id == 'new' else f"{user} has been updated successfully" messages.success(request, message) return redirect('users') else: return HttpResponseBadRequest('Invalid method')