audit: remove foreign key to user, save user data as json
This commit is contained in:
parent
cbcdaaf532
commit
8358574484
59
passbook/audit/migrations/0003_auto_20200917_1155.py
Normal file
59
passbook/audit/migrations/0003_auto_20200917_1155.py
Normal file
|
@ -0,0 +1,59 @@
|
|||
# Generated by Django 3.1.1 on 2020-09-17 11:55
|
||||
from django.apps.registry import Apps
|
||||
from django.db import migrations, models
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
|
||||
import passbook.audit.models
|
||||
|
||||
|
||||
def convert_user_to_json(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||
Event = apps.get_model("passbook_audit", "Event")
|
||||
|
||||
db_alias = schema_editor.connection.alias
|
||||
for event in Event.objects.all():
|
||||
event.delete()
|
||||
# Because event objects cannot be updated, we have to re-create them
|
||||
event.pk = None
|
||||
event.user_json = (
|
||||
passbook.audit.models.get_user(event.user) if event.user else {}
|
||||
)
|
||||
event._state.adding = True
|
||||
event.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("passbook_audit", "0002_auto_20200918_2116"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="event",
|
||||
name="action",
|
||||
field=models.TextField(
|
||||
choices=[
|
||||
("LOGIN", "login"),
|
||||
("LOGIN_FAILED", "login_failed"),
|
||||
("LOGOUT", "logout"),
|
||||
("AUTHORIZE_APPLICATION", "authorize_application"),
|
||||
("SUSPICIOUS_REQUEST", "suspicious_request"),
|
||||
("SIGN_UP", "sign_up"),
|
||||
("PASSWORD_RESET", "password_reset"),
|
||||
("INVITE_CREATED", "invitation_created"),
|
||||
("INVITE_USED", "invitation_used"),
|
||||
("IMPERSONATION_STARTED", "impersonation_started"),
|
||||
("IMPERSONATION_ENDED", "impersonation_ended"),
|
||||
("CUSTOM", "custom"),
|
||||
]
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="event", name="user_json", field=models.JSONField(default=dict),
|
||||
),
|
||||
migrations.RunPython(convert_user_to_json),
|
||||
migrations.RemoveField(model_name="event", name="user",),
|
||||
migrations.RenameField(
|
||||
model_name="event", old_name="user_json", new_name="user"
|
||||
),
|
||||
]
|
|
@ -12,13 +12,14 @@ from django.db.models.base import Model
|
|||
from django.http import HttpRequest
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.debug import SafeExceptionReporterFilter
|
||||
from guardian.shortcuts import get_anonymous_user
|
||||
from guardian.utils import get_anonymous_user
|
||||
from structlog import get_logger
|
||||
|
||||
from passbook.core.middleware import (
|
||||
SESSION_IMPERSONATE_ORIGINAL_USER,
|
||||
SESSION_IMPERSONATE_USER,
|
||||
)
|
||||
from passbook.core.models import User
|
||||
from passbook.lib.utils.http import get_client_ip
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
@ -53,6 +54,22 @@ def model_to_dict(model: Model) -> Dict[str, Any]:
|
|||
}
|
||||
|
||||
|
||||
def get_user(user: User, original_user: Optional[User] = None) -> Dict[str, Any]:
|
||||
"""Convert user object to dictionary, optionally including the original user"""
|
||||
if isinstance(user, AnonymousUser):
|
||||
user = get_anonymous_user()
|
||||
user_data = {
|
||||
"username": user.username,
|
||||
"pk": user.pk,
|
||||
"email": user.email,
|
||||
}
|
||||
if original_user:
|
||||
original_data = get_user(original_user)
|
||||
original_data["on_behalf_of"] = user_data
|
||||
return original_data
|
||||
return user_data
|
||||
|
||||
|
||||
def sanitize_dict(source: Dict[Any, Any]) -> Dict[Any, Any]:
|
||||
"""clean source of all Models that would interfere with the JSONField.
|
||||
Models are replaced with a dictionary of {
|
||||
|
@ -101,9 +118,7 @@ class Event(models.Model):
|
|||
"""An individual audit log event"""
|
||||
|
||||
event_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL
|
||||
)
|
||||
user = models.JSONField(default=dict)
|
||||
action = models.TextField(choices=EventAction.as_choices())
|
||||
date = models.DateTimeField(auto_now_add=True)
|
||||
app = models.TextField()
|
||||
|
@ -142,17 +157,17 @@ class Event(models.Model):
|
|||
Events independently from requests.
|
||||
`user` arguments optionally overrides user from requests."""
|
||||
if hasattr(request, "user"):
|
||||
if isinstance(request.user, AnonymousUser):
|
||||
self.user = get_anonymous_user()
|
||||
else:
|
||||
self.user = request.user
|
||||
self.user = get_user(
|
||||
request.user,
|
||||
request.session.get(SESSION_IMPERSONATE_ORIGINAL_USER, None),
|
||||
)
|
||||
if user:
|
||||
self.user = user
|
||||
self.user = get_user(user)
|
||||
# Check if we're currently impersonating, and add that user
|
||||
if hasattr(request, "session"):
|
||||
if SESSION_IMPERSONATE_ORIGINAL_USER in request.session:
|
||||
self.user = request.session[SESSION_IMPERSONATE_ORIGINAL_USER]
|
||||
self.context["on_behalf_of"] = model_to_dict(
|
||||
self.user = get_user(request.session[SESSION_IMPERSONATE_ORIGINAL_USER])
|
||||
self.user["on_behalf_of"] = get_user(
|
||||
request.session[SESSION_IMPERSONATE_USER]
|
||||
)
|
||||
# User 255.255.255.255 as fallback if IP cannot be determined
|
||||
|
|
|
@ -57,7 +57,9 @@ def on_user_logged_out(sender, request: HttpRequest, user: User, **_):
|
|||
# pylint: disable=unused-argument
|
||||
def on_user_write(sender, request: HttpRequest, user: User, data: Dict[str, Any], **_):
|
||||
"""Log User write"""
|
||||
thread = EventNewThread(EventAction.CUSTOM, request, **data)
|
||||
thread = EventNewThread(
|
||||
EventAction.CUSTOM, request, caller="stages/user_write", **data
|
||||
)
|
||||
thread.user = user
|
||||
thread.run()
|
||||
|
||||
|
|
|
@ -40,12 +40,28 @@
|
|||
</div>
|
||||
</th>
|
||||
<td role="cell">
|
||||
<code>{{ entry.context }}</code>
|
||||
<div>
|
||||
<div>
|
||||
<code>{{ entry.context }}</code>
|
||||
</div>
|
||||
{% if entry.user.on_behalf_of %}
|
||||
<small>
|
||||
{% blocktrans with username=entry.user.on_behalf_of.username %}
|
||||
On behalf of {{ username }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
{{ entry.user }}
|
||||
</span>
|
||||
<div>
|
||||
<div>{{ entry.user.username }}</div>
|
||||
<small>
|
||||
{% blocktrans with pk=entry.user.pk %}
|
||||
ID: {{ pk }}
|
||||
{% endblocktrans %}
|
||||
</small>
|
||||
</div>
|
||||
</td>
|
||||
<td role="cell">
|
||||
<span>
|
||||
|
|
Reference in a new issue