Closes #1571: extras_useraction to log remote_addr

This commit is contained in:
johnhu 2017-10-12 09:03:43 +00:00
parent 6378b95e0b
commit 246d9b8620
7 changed files with 71 additions and 34 deletions

View File

@ -25,6 +25,7 @@ from utilities.views import (
BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, ComponentDeleteView, BulkComponentCreateView, BulkDeleteView, BulkEditView, BulkImportView, ComponentCreateView, ComponentDeleteView,
ComponentEditView, ObjectDeleteView, ObjectEditView, ObjectListView, ComponentEditView, ObjectDeleteView, ObjectEditView, ObjectListView,
) )
from utilities.utils import get_ip_address
from . import filters, forms, tables from . import filters, forms, tables
from .models import ( from .models import (
CONNECTION_STATUS_CONNECTED, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device, CONNECTION_STATUS_CONNECTED, ConsolePort, ConsolePortTemplate, ConsoleServerPort, ConsoleServerPortTemplate, Device,
@ -1030,7 +1031,7 @@ def consoleport_connect(request, pk):
escape(consoleport.cs_port.name), escape(consoleport.cs_port.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, consoleport, msg) UserAction.objects.log_edit(request.user, consoleport, msg, get_ip_address(request))
return redirect('dcim:device', pk=consoleport.device.pk) return redirect('dcim:device', pk=consoleport.device.pk)
else: else:
@ -1075,7 +1076,7 @@ def consoleport_disconnect(request, pk):
escape(cs_port.name), escape(cs_port.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, consoleport, msg) UserAction.objects.log_edit(request.user, consoleport, msg, get_ip_address(request))
return redirect('dcim:device', pk=consoleport.device.pk) return redirect('dcim:device', pk=consoleport.device.pk)
else: else:
@ -1150,7 +1151,7 @@ def consoleserverport_connect(request, pk):
escape(consoleserverport.name), escape(consoleserverport.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, consoleport, msg) UserAction.objects.log_edit(request.user, consoleport, msg, get_ip_address(request))
return redirect('dcim:device', pk=consoleserverport.device.pk) return redirect('dcim:device', pk=consoleserverport.device.pk)
else: else:
@ -1195,7 +1196,7 @@ def consoleserverport_disconnect(request, pk):
escape(consoleserverport.name), escape(consoleserverport.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, consoleport, msg) UserAction.objects.log_edit(request.user, consoleport, msg, get_ip_address(request))
return redirect('dcim:device', pk=consoleserverport.device.pk) return redirect('dcim:device', pk=consoleserverport.device.pk)
else: else:
@ -1269,7 +1270,7 @@ def powerport_connect(request, pk):
escape(powerport.power_outlet.name), escape(powerport.power_outlet.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, powerport, msg) UserAction.objects.log_edit(request.user, powerport, msg, get_ip_address(request))
return redirect('dcim:device', pk=powerport.device.pk) return redirect('dcim:device', pk=powerport.device.pk)
else: else:
@ -1314,7 +1315,7 @@ def powerport_disconnect(request, pk):
escape(power_outlet.name), escape(power_outlet.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, powerport, msg) UserAction.objects.log_edit(request.user, powerport, msg, get_ip_address(request))
return redirect('dcim:device', pk=powerport.device.pk) return redirect('dcim:device', pk=powerport.device.pk)
else: else:
@ -1389,7 +1390,7 @@ def poweroutlet_connect(request, pk):
escape(poweroutlet.name), escape(poweroutlet.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, powerport, msg) UserAction.objects.log_edit(request.user, powerport, msg, get_ip_address(request))
return redirect('dcim:device', pk=poweroutlet.device.pk) return redirect('dcim:device', pk=poweroutlet.device.pk)
else: else:
@ -1434,7 +1435,7 @@ def poweroutlet_disconnect(request, pk):
escape(poweroutlet.name), escape(poweroutlet.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, powerport, msg) UserAction.objects.log_edit(request.user, powerport, msg, get_ip_address(request))
return redirect('dcim:device', pk=poweroutlet.device.pk) return redirect('dcim:device', pk=poweroutlet.device.pk)
else: else:
@ -1709,7 +1710,7 @@ def interfaceconnection_add(request, pk):
escape(interfaceconnection.interface_b.name), escape(interfaceconnection.interface_b.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, interfaceconnection, msg) UserAction.objects.log_edit(request.user, interfaceconnection, msg, get_ip_address(request))
if '_addanother' in request.POST: if '_addanother' in request.POST:
base_url = reverse('dcim:interfaceconnection_add', kwargs={'pk': device.pk}) base_url = reverse('dcim:interfaceconnection_add', kwargs={'pk': device.pk})
@ -1757,7 +1758,7 @@ def interfaceconnection_delete(request, pk):
escape(interfaceconnection.interface_b.name), escape(interfaceconnection.interface_b.name),
) )
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_edit(request.user, interfaceconnection, msg) UserAction.objects.log_edit(request.user, interfaceconnection, msg, get_ip_address(request))
if form.cleaned_data['device']: if form.cleaned_data['device']:
return redirect('dcim:device', pk=form.cleaned_data['device'].pk) return redirect('dcim:device', pk=form.cleaned_data['device'].pk)
else: else:

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.1 on 2017-10-12 07:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('extras', '0008_reports'),
]
operations = [
migrations.AddField(
model_name='useraction',
name='ip_address',
field=models.GenericIPAddressField(null=True, unpack_ipv4=True),
),
]

View File

@ -414,44 +414,46 @@ class ReportResult(models.Model):
class UserActionManager(models.Manager): class UserActionManager(models.Manager):
# Actions affecting a single object # Actions affecting a single object
def log_action(self, user, obj, action, message): def log_action(self, user, obj, action, message, ip_address):
self.model.objects.create( self.model.objects.create(
content_type=ContentType.objects.get_for_model(obj), content_type=ContentType.objects.get_for_model(obj),
object_id=obj.pk, object_id=obj.pk,
user=user, user=user,
action=action, action=action,
message=message, message=message,
ip_address=ip_address,
) )
def log_create(self, user, obj, message=''): def log_create(self, user, obj, message, ip_address):
self.log_action(user, obj, ACTION_CREATE, message) self.log_action(user, obj, ACTION_CREATE, message, ip_address)
def log_edit(self, user, obj, message=''): def log_edit(self, user, obj, message, ip_address):
self.log_action(user, obj, ACTION_EDIT, message) self.log_action(user, obj, ACTION_EDIT, message, ip_address)
def log_delete(self, user, obj, message=''): def log_delete(self, user, obj, message, ip_address):
self.log_action(user, obj, ACTION_DELETE, message) self.log_action(user, obj, ACTION_DELETE, message, ip_address)
# Actions affecting multiple objects # Actions affecting multiple objects
def log_bulk_action(self, user, content_type, action, message): def log_bulk_action(self, user, content_type, action, message, ip_address):
self.model.objects.create( self.model.objects.create(
content_type=content_type, content_type=content_type,
user=user, user=user,
action=action, action=action,
message=message, message=message,
ip_address=ip_address,
) )
def log_import(self, user, content_type, message=''): def log_import(self, user, content_type, message, ip_address):
self.log_bulk_action(user, content_type, ACTION_IMPORT, message) self.log_bulk_action(user, content_type, ACTION_IMPORT, message, ip_address)
def log_bulk_create(self, user, content_type, message=''): def log_bulk_create(self, user, content_type, message, ip_address):
self.log_bulk_action(user, content_type, ACTION_BULK_CREATE, message) self.log_bulk_action(user, content_type, ACTION_BULK_CREATE, message, ip_address)
def log_bulk_edit(self, user, content_type, message=''): def log_bulk_edit(self, user, content_type, message, ip_address):
self.log_bulk_action(user, content_type, ACTION_BULK_EDIT, message) self.log_bulk_action(user, content_type, ACTION_BULK_EDIT, message, ip_address)
def log_bulk_delete(self, user, content_type, message=''): def log_bulk_delete(self, user, content_type, message, ip_address):
self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message) self.log_bulk_action(user, content_type, ACTION_BULK_DELETE, message, ip_address)
@python_2_unicode_compatible @python_2_unicode_compatible
@ -465,6 +467,7 @@ class UserAction(models.Model):
object_id = models.PositiveIntegerField(blank=True, null=True) object_id = models.PositiveIntegerField(blank=True, null=True)
action = models.PositiveSmallIntegerField(choices=ACTION_CHOICES) action = models.PositiveSmallIntegerField(choices=ACTION_CHOICES)
message = models.TextField(blank=True) message = models.TextField(blank=True)
ip_address = models.GenericIPAddressField(null=True, unpack_ipv4=True)
objects = UserActionManager() objects = UserActionManager()

View File

@ -9,6 +9,7 @@ from django.views.generic import View
from utilities.forms import ConfirmationForm from utilities.forms import ConfirmationForm
from utilities.views import ObjectDeleteView, ObjectEditView from utilities.views import ObjectDeleteView, ObjectEditView
from utilities.utils import get_ip_address
from .forms import ImageAttachmentForm from .forms import ImageAttachmentForm
from .models import ImageAttachment, ReportResult, UserAction from .models import ImageAttachment, ReportResult, UserAction
from .reports import get_report, get_reports from .reports import get_report, get_reports
@ -113,6 +114,6 @@ class ReportRunView(PermissionRequiredMixin, View):
result = 'failed' if report.failed else 'passed' result = 'failed' if report.failed else 'passed'
msg = "Ran report {} ({})".format(report.full_name, result) msg = "Ran report {} ({})".format(report.full_name, result)
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
UserAction.objects.log_create(request.user, report.result, msg) UserAction.objects.log_create(request.user, report.result, msg, get_ip_address(request))
return redirect('extras:report', name=report.full_name) return redirect('extras:report', name=report.full_name)

View File

@ -8,6 +8,7 @@
<tr> <tr>
<th>Time</th> <th>Time</th>
<th>Action</th> <th>Action</th>
<th>IP Address</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -15,6 +16,7 @@
<tr> <tr>
<td>{{ action.time|date:'SHORT_DATETIME_FORMAT' }}</td> <td>{{ action.time|date:'SHORT_DATETIME_FORMAT' }}</td>
<td>{{ action.icon }} {{ action.message|safe }}</td> <td>{{ action.icon }} {{ action.message|safe }}</td>
<td>{% if action.ip_address %}{{ action.ip_address }}{% endif %}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -37,3 +37,12 @@ def foreground_color(bg_color):
return '000000' return '000000'
else: else:
return 'ffffff' return 'ffffff'
def get_ip_address(request):
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip_address = x_forwarded_for.split(',')[0]
else:
ip_address = request.META.get('REMOTE_ADDR')
return ip_address

View File

@ -22,6 +22,7 @@ from django.views.generic import View
from extras.models import CustomField, CustomFieldValue, ExportTemplate, UserAction from extras.models import CustomField, CustomFieldValue, ExportTemplate, UserAction
from utilities.forms import BootstrapMixin, CSVDataField from utilities.forms import BootstrapMixin, CSVDataField
from utilities.utils import get_ip_address
from .error_handlers import handle_protectederror from .error_handlers import handle_protectederror
from .forms import ConfirmationForm from .forms import ConfirmationForm
from .paginator import EnhancedPaginator from .paginator import EnhancedPaginator
@ -210,9 +211,9 @@ class ObjectEditView(GetReturnURLMixin, View):
msg = '{} {}'.format(msg, escape(obj)) msg = '{} {}'.format(msg, escape(obj))
messages.success(request, mark_safe(msg)) messages.success(request, mark_safe(msg))
if obj_created: if obj_created:
UserAction.objects.log_create(request.user, obj, msg) UserAction.objects.log_create(request.user, obj, msg, get_ip_address(request))
else: else:
UserAction.objects.log_edit(request.user, obj, msg) UserAction.objects.log_edit(request.user, obj, msg, get_ip_address(request))
if '_addanother' in request.POST: if '_addanother' in request.POST:
return redirect(request.get_full_path()) return redirect(request.get_full_path())
@ -275,7 +276,7 @@ class ObjectDeleteView(GetReturnURLMixin, View):
msg = 'Deleted {} {}'.format(self.model._meta.verbose_name, obj) msg = 'Deleted {} {}'.format(self.model._meta.verbose_name, obj)
messages.success(request, msg) messages.success(request, msg)
UserAction.objects.log_delete(request.user, obj, msg) UserAction.objects.log_delete(request.user, obj, msg, get_ip_address(request))
return_url = form.cleaned_data.get('return_url') return_url = form.cleaned_data.get('return_url')
if return_url is not None and is_safe_url(url=return_url, host=request.get_host()): if return_url is not None and is_safe_url(url=return_url, host=request.get_host()):
@ -355,7 +356,7 @@ class BulkCreateView(View):
# If we make it to this point, validation has succeeded on all new objects. # If we make it to this point, validation has succeeded on all new objects.
msg = "Added {} {}".format(len(new_objs), model._meta.verbose_name_plural) msg = "Added {} {}".format(len(new_objs), model._meta.verbose_name_plural)
messages.success(request, msg) messages.success(request, msg)
UserAction.objects.log_bulk_create(request.user, ContentType.objects.get_for_model(model), msg) UserAction.objects.log_bulk_create(request.user, ContentType.objects.get_for_model(model), msg, get_ip_address(request))
if '_addanother' in request.POST: if '_addanother' in request.POST:
return redirect(request.path) return redirect(request.path)
@ -440,7 +441,7 @@ class BulkImportView(View):
if new_objs: if new_objs:
msg = 'Imported {} {}'.format(len(new_objs), new_objs[0]._meta.verbose_name_plural) msg = 'Imported {} {}'.format(len(new_objs), new_objs[0]._meta.verbose_name_plural)
messages.success(request, msg) messages.success(request, msg)
UserAction.objects.log_import(request.user, ContentType.objects.get_for_model(new_objs[0]), msg) UserAction.objects.log_import(request.user, ContentType.objects.get_for_model(new_objs[0]), msg, get_ip_address(request))
return render(request, "import_success.html", { return render(request, "import_success.html", {
'table': obj_table, 'table': obj_table,
@ -536,7 +537,7 @@ class BulkEditView(View):
if updated_count: if updated_count:
msg = 'Updated {} {}'.format(updated_count, self.cls._meta.verbose_name_plural) msg = 'Updated {} {}'.format(updated_count, self.cls._meta.verbose_name_plural)
messages.success(self.request, msg) messages.success(self.request, msg)
UserAction.objects.log_bulk_edit(request.user, ContentType.objects.get_for_model(self.cls), msg) UserAction.objects.log_bulk_edit(request.user, ContentType.objects.get_for_model(self.cls), msg, get_ip_address(request))
return redirect(return_url) return redirect(return_url)
else: else:
@ -671,7 +672,7 @@ class BulkDeleteView(View):
msg = 'Deleted {} {}'.format(deleted_count, self.cls._meta.verbose_name_plural) msg = 'Deleted {} {}'.format(deleted_count, self.cls._meta.verbose_name_plural)
messages.success(request, msg) messages.success(request, msg)
UserAction.objects.log_bulk_delete(request.user, ContentType.objects.get_for_model(self.cls), msg) UserAction.objects.log_bulk_delete(request.user, ContentType.objects.get_for_model(self.cls), msg, get_ip_address(request))
return redirect(return_url) return redirect(return_url)
else: else: