From 1b4634c8c1c8cdda9163cf419c2fd2ccb7ce2c55 Mon Sep 17 00:00:00 2001 From: Ankitashinde0 <53168131+Ankitashinde0@users.noreply.github.com> Date: Tue, 26 Nov 2019 15:18:08 +0530 Subject: [PATCH 1/6] Update mixins.py --- src/auditlog/mixins.py | 87 +++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/src/auditlog/mixins.py b/src/auditlog/mixins.py index 5a0b829b..449efd8c 100644 --- a/src/auditlog/mixins.py +++ b/src/auditlog/mixins.py @@ -1,6 +1,12 @@ import json - +from django.utils.html import mark_safe +import datetime from django.conf import settings +from .middleware import AuditlogMiddleware +import threading +import time + + try: from django.core import urlresolvers except ImportError: @@ -9,17 +15,37 @@ from django.urls.exceptions import NoReverseMatch except ImportError: from django.core.urlresolvers import NoReverseMatch -from django.utils.html import format_html -from django.utils.safestring import mark_safe +try: + from django.utils.deprecation import MiddlewareMixin +except ImportError: + MiddlewareMixin = object MAX = 75 +threadlocal = threading.local() -class LogEntryAdminMixin(object): +class MiddlewareMixinclass(MiddlewareMixin): + def disp_remote_addr(self,obj): + return obj.remote_addr + disp_remote_addr.short_description = "IP Address" +class LogEntryAdminMixin(object): def created(self, obj): - return obj.timestamp.strftime('%Y-%m-%d %H:%M:%S') - created.short_description = 'Created' + print("------------Inside created()----------") + return obj.timestamp.strftime('%b. %d, %Y, %I:%M %p') #Displays date in diff format + created.short_description = 'Date' + + def entity_type(self,obj): + #entity type for KLC objects + if obj.content_type_id == 0: + return "N/A" + if obj.content_type_id == 8 or obj.content_type_id == 9: #Displays client for all models inside it + return "client" + if obj.content_type_id == 90 or obj.content_type_id == 10: #Displays user for all models inside it + return "user" + + return obj.content_type.model + entity_type.short_description = "Entity type" def user_url(self, obj): if obj.actor: @@ -28,10 +54,10 @@ def user_url(self, obj): try: link = urlresolvers.reverse(viewname, args=[obj.actor.id]) except NoReverseMatch: - return u'%s' % (obj.actor) - return format_html(u'{}', link, obj.actor) - - return 'system' + return (obj.actor) + return ( obj.actor) + return (obj.object_repr) #Previously returned system ,now changed to return object_repr(username) to display username in last_login entries + user_url.allow_tags = True #Returns user whose last_login is changed which is the username itself. user_url.short_description = 'User' def resource_url(self, obj): @@ -41,14 +67,27 @@ def resource_url(self, obj): args = [obj.object_pk] if obj.object_id is None else [obj.object_id] link = urlresolvers.reverse(viewname, args=args) except NoReverseMatch: + obj_store = obj.object_repr + print('obj_store---- in alice test----',obj_store) + print("type(obj_store-----1st in alice test----",type(obj_store)) + print(obj_store.isnumeric()) return obj.object_repr else: - return format_html(u'{}', link, obj.object_repr) - resource_url.short_description = 'Resource' + obj_store = str(obj.object_repr) + # print("type(obj_store-----",type(obj_store)) + # if (obj_store.isnumeric()): + # print("It is numeric-----") + return (obj.object_repr) + resource_url.allow_tags = True + resource_url.short_description = 'Entity name' def msg_short(self, obj): if obj.action == 2: - return '' # delete + # if obj.content_type_id == 108: + # return obj.changes + return 'Deleted object' # delete + if obj.action == 3 or obj.action == 4 or obj.action == 1 and obj.content_type_id == 108 and obj.additional_data == "Client_name": #to display changes for actions of download-3,KLC-4 + return obj.changes #and also when update action-1 is done for client name in Client Group changes = json.loads(obj.changes) s = '' if len(changes) == 1 else 's' fields = ', '.join(changes.keys()) @@ -56,17 +95,27 @@ def msg_short(self, obj): i = fields.rfind(' ', 0, MAX) fields = fields[:i] + ' ..' return '%d change%s: %s' % (len(changes), s, fields) - msg_short.short_description = 'Changes' + msg_short.short_description = 'Description' def msg(self, obj): if obj.action == 2: return '' # delete changes = json.loads(obj.changes) - msg = '' + msg = '
#FieldFromTo
' for i, field in enumerate(sorted(changes), 1): - value = [i, field] + (['***', '***'] if field == 'password' else changes[field]) - msg += format_html('', *value) - + value = [i, field] + (['***', '***'] if field == 'password' or field == 'kaseya_password' or field == 'webroot_password' or field == 'db_password' or field == 'nlm_server_password' else changes[field]) #to display values of password fields as **** + #changes for last_login field in logging entries + if field == 'last_login': + for i in range(len(changes[field])): #iterating list + ologin_date = changes[field][0] #storing value at index 0 as old login date + ologin_date = datetime.datetime.strptime(ologin_date, '%Y-%m-%d %H:%M:%S.%f') #converting string into datetime obj + ologin_date = ologin_date.strftime("%m/%d/%Y %I:%M %p") #converting date into string type + nlogin_date = changes[field][1] #storing value at index 1 as new login date + nlogin_date = datetime.datetime.strptime(nlogin_date, '%Y-%m-%d %H:%M:%S.%f') #converting string into datetime obj + nlogin_date = nlogin_date.strftime("%m/%d/%Y %I:%M %p") #converting date into string type + value = [i,field] + [ologin_date,nlogin_date] + msg += '' % tuple(value) msg += '
No.FieldFromTo
{}{}{}{}
%s%s%s%s
' - return mark_safe(msg) + return mark_safe(msg) #mark_safe is used to return html code in Python + msg.allow_tags = True msg.short_description = 'Changes' From 8321a1f59864a25be452a2e63204c8e9f189d4fa Mon Sep 17 00:00:00 2001 From: Ankitashinde0 <53168131+Ankitashinde0@users.noreply.github.com> Date: Tue, 26 Nov 2019 15:22:59 +0530 Subject: [PATCH 2/6] Update models.py --- src/auditlog/models.py | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/src/auditlog/models.py b/src/auditlog/models.py index 0832f2c1..82832bfd 100644 --- a/src/auditlog/models.py +++ b/src/auditlog/models.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldDoesNotExist -from django.db import models, DEFAULT_DB_ALIAS +from django.db import models from django.db.models import QuerySet, Q from django.utils import formats, timezone from django.utils.encoding import python_2_unicode_compatible, smart_text @@ -164,18 +164,22 @@ class Action: CREATE = 0 UPDATE = 1 DELETE = 2 + DOWNLOAD = 3 + KLC = 4 choices = ( - (CREATE, _("create")), - (UPDATE, _("update")), - (DELETE, _("delete")), + (CREATE, _("Create")), + (UPDATE, _("Update")), + (DELETE, _("Delete")), + (DOWNLOAD,_("Download")), + (KLC,_("KLC")) ) - content_type = models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE, related_name='+', verbose_name=_("content type")) + content_type = models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE,related_name='+', verbose_name=_("content type")) object_pk = models.CharField(db_index=True, max_length=255, verbose_name=_("object pk")) object_id = models.BigIntegerField(blank=True, db_index=True, null=True, verbose_name=_("object id")) - object_repr = models.TextField(verbose_name=_("object representation")) - action = models.PositiveSmallIntegerField(choices=Action.choices, verbose_name=_("action")) + object_repr = models.TextField(verbose_name=_("Entity name")) + action = models.PositiveSmallIntegerField(choices=Action.choices, verbose_name=_("Operation")) changes = models.TextField(blank=True, verbose_name=_("change message")) actor = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, blank=True, null=True, related_name='+', verbose_name=_("actor")) remote_addr = models.GenericIPAddressField(blank=True, null=True, verbose_name=_("remote address")) @@ -197,6 +201,10 @@ def __str__(self): fstring = _("Updated {repr:s}") elif self.action == self.Action.DELETE: fstring = _("Deleted {repr:s}") + elif self.action == self.Action.DOWNLOAD: + fstring = _("Downloaded {repr:s}") + elif self.action == self.Action.KLC: + fstring = _("Kaseya Live Connect {repr:s}") else: fstring = _("Logged {repr:s}") @@ -321,13 +329,9 @@ class AuditlogHistoryField(GenericRelation): :param pk_indexable: Whether the primary key for this model is not an :py:class:`int` or :py:class:`long`. :type pk_indexable: bool - :param delete_related: By default, including a generic relation into a model will cause all related objects to be - cascade-deleted when the parent object is deleted. Passing False to this overrides this behavior, retaining - the full auditlog history for the object. Defaults to True, because that's Django's default behavior. - :type delete_related: bool """ - def __init__(self, pk_indexable=True, delete_related=True, **kwargs): + def __init__(self, pk_indexable=True, **kwargs): kwargs['to'] = LogEntry if pk_indexable: @@ -336,22 +340,8 @@ def __init__(self, pk_indexable=True, delete_related=True, **kwargs): kwargs['object_id_field'] = 'object_pk' kwargs['content_type_field'] = 'content_type' - self.delete_related = delete_related super(AuditlogHistoryField, self).__init__(**kwargs) - def bulk_related_objects(self, objs, using=DEFAULT_DB_ALIAS): - """ - Return all objects related to ``objs`` via this ``GenericRelation``. - """ - if self.delete_related: - return super(AuditlogHistoryField, self).bulk_related_objects(objs, using) - - # When deleting, Collector.collect() finds related objects using this - # method. However, because we don't want to delete these related - # objects, we simply return an empty list. - return [] - - # South compatibility for AuditlogHistoryField try: from south.modelsinspector import add_introspection_rules From d1e1ac2161a6d107eac297850b72b91fc5f26a44 Mon Sep 17 00:00:00 2001 From: Ankitashinde0 <53168131+Ankitashinde0@users.noreply.github.com> Date: Tue, 26 Nov 2019 15:24:03 +0530 Subject: [PATCH 3/6] Update admin.py --- src/auditlog/admin.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/auditlog/admin.py b/src/auditlog/admin.py index a4c60fc1..837ccee2 100644 --- a/src/auditlog/admin.py +++ b/src/auditlog/admin.py @@ -1,18 +1,27 @@ from django.contrib import admin from .models import LogEntry from .mixins import LogEntryAdminMixin +from .mixins import MiddlewareMixinclass from .filters import ResourceTypeFilter +from .middleware import AuditlogMiddleware -class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin): - list_display = ['created', 'resource_url', 'action', 'msg_short', 'user_url'] - search_fields = ['timestamp', 'object_repr', 'changes', 'actor__first_name', 'actor__last_name'] - list_filter = ['action', ResourceTypeFilter] +class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin,MiddlewareMixinclass): + # list_display = ['created', 'resource_url', 'action', 'msg_short', 'user_url'] + list_display = ['created','user_url','action','entity_type','object_repr','msg_short','remote_addr'] + search_fields = ['changes','remote_addr','object_repr'] + # list_filter = ['action', ResourceTypeFilter] + list_filter = ['action',ResourceTypeFilter,'timestamp'] readonly_fields = ['created', 'resource_url', 'action', 'user_url', 'msg'] fieldsets = [ (None, {'fields': ['created', 'user_url', 'resource_url']}), ('Changes', {'fields': ['action', 'msg']}), ] + def has_add_permission(self, request): # remove "add" permission + return False + + # def has_delete_permission(self, request, obj=None): + # return False admin.site.register(LogEntry, LogEntryAdmin) From c31c926a4c40e852aa6a583ee09c92cd9efd152f Mon Sep 17 00:00:00 2001 From: Ankitashinde0 <53168131+Ankitashinde0@users.noreply.github.com> Date: Tue, 26 Nov 2019 17:40:42 +0530 Subject: [PATCH 4/6] Update mixins.py --- src/auditlog/mixins.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/auditlog/mixins.py b/src/auditlog/mixins.py index 449efd8c..b3dbb2ea 100644 --- a/src/auditlog/mixins.py +++ b/src/auditlog/mixins.py @@ -31,7 +31,6 @@ def disp_remote_addr(self,obj): class LogEntryAdminMixin(object): def created(self, obj): - print("------------Inside created()----------") return obj.timestamp.strftime('%b. %d, %Y, %I:%M %p') #Displays date in diff format created.short_description = 'Date' From 864e8b89944057f9529fb25df5d4f5409cae8a92 Mon Sep 17 00:00:00 2001 From: Ankitashinde0 <53168131+Ankitashinde0@users.noreply.github.com> Date: Tue, 3 Dec 2019 16:02:17 +0530 Subject: [PATCH 5/6] Update mixins.py --- src/auditlog/mixins.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/auditlog/mixins.py b/src/auditlog/mixins.py index b3dbb2ea..2918d405 100644 --- a/src/auditlog/mixins.py +++ b/src/auditlog/mixins.py @@ -73,17 +73,12 @@ def resource_url(self, obj): return obj.object_repr else: obj_store = str(obj.object_repr) - # print("type(obj_store-----",type(obj_store)) - # if (obj_store.isnumeric()): - # print("It is numeric-----") return (obj.object_repr) resource_url.allow_tags = True resource_url.short_description = 'Entity name' def msg_short(self, obj): if obj.action == 2: - # if obj.content_type_id == 108: - # return obj.changes return 'Deleted object' # delete if obj.action == 3 or obj.action == 4 or obj.action == 1 and obj.content_type_id == 108 and obj.additional_data == "Client_name": #to display changes for actions of download-3,KLC-4 return obj.changes #and also when update action-1 is done for client name in Client Group From 9a83b8c0d7ff4ec695876e61d196b8ed8f702f2b Mon Sep 17 00:00:00 2001 From: Ankitashinde0 <53168131+Ankitashinde0@users.noreply.github.com> Date: Tue, 3 Dec 2019 16:03:04 +0530 Subject: [PATCH 6/6] Update admin.py --- src/auditlog/admin.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/auditlog/admin.py b/src/auditlog/admin.py index 837ccee2..5b07d923 100644 --- a/src/auditlog/admin.py +++ b/src/auditlog/admin.py @@ -7,10 +7,8 @@ class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin,MiddlewareMixinclass): - # list_display = ['created', 'resource_url', 'action', 'msg_short', 'user_url'] list_display = ['created','user_url','action','entity_type','object_repr','msg_short','remote_addr'] search_fields = ['changes','remote_addr','object_repr'] - # list_filter = ['action', ResourceTypeFilter] list_filter = ['action',ResourceTypeFilter,'timestamp'] readonly_fields = ['created', 'resource_url', 'action', 'user_url', 'msg'] fieldsets = [ @@ -20,8 +18,5 @@ class LogEntryAdmin(admin.ModelAdmin, LogEntryAdminMixin,MiddlewareMixinclass): def has_add_permission(self, request): # remove "add" permission return False - - # def has_delete_permission(self, request, obj=None): - # return False admin.site.register(LogEntry, LogEntryAdmin)