authentik/passbook/audit/models.py

75 lines
2.7 KiB
Python

"""passbook audit models"""
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.contrib.postgres.fields import JSONField
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext as _
from ipware import get_client_ip
from structlog import get_logger
from passbook.lib.models import UUIDModel
LOGGER = get_logger()
class AuditEntry(UUIDModel):
"""An individual audit log entry"""
ACTION_LOGIN = 'login'
ACTION_LOGIN_FAILED = 'login_failed'
ACTION_LOGOUT = 'logout'
ACTION_AUTHORIZE_APPLICATION = 'authorize_application'
ACTION_SUSPICIOUS_REQUEST = 'suspicious_request'
ACTION_SIGN_UP = 'sign_up'
ACTION_PASSWORD_RESET = 'password_reset' # noqa # nosec
ACTION_INVITE_CREATED = 'invitation_created'
ACTION_INVITE_USED = 'invitation_used'
ACTIONS = (
(ACTION_LOGIN, ACTION_LOGIN),
(ACTION_LOGIN_FAILED, ACTION_LOGIN_FAILED),
(ACTION_LOGOUT, ACTION_LOGOUT),
(ACTION_AUTHORIZE_APPLICATION, ACTION_AUTHORIZE_APPLICATION),
(ACTION_SUSPICIOUS_REQUEST, ACTION_SUSPICIOUS_REQUEST),
(ACTION_SIGN_UP, ACTION_SIGN_UP),
(ACTION_PASSWORD_RESET, ACTION_PASSWORD_RESET),
(ACTION_INVITE_CREATED, ACTION_INVITE_CREATED),
(ACTION_INVITE_USED, ACTION_INVITE_USED),
)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
action = models.TextField(choices=ACTIONS)
date = models.DateTimeField(auto_now_add=True)
app = models.TextField()
context = JSONField(default=dict, blank=True)
request_ip = models.GenericIPAddressField()
created = models.DateTimeField(auto_now_add=True)
@staticmethod
def create(action, request, **kwargs):
"""Create AuditEntry from arguments"""
client_ip, _ = get_client_ip(request)
if not hasattr(request, 'user'):
user = None
else:
user = request.user
if isinstance(user, AnonymousUser):
user = kwargs.get('user', None)
entry = AuditEntry.objects.create(
action=action,
user=user,
# User 255.255.255.255 as fallback if IP cannot be determined
request_ip=client_ip or '255.255.255.255',
context=kwargs)
LOGGER.debug("Logged %s from %s (%s)", action, user, client_ip)
return entry
def save(self, *args, **kwargs):
if not self._state.adding:
raise ValidationError("you may not edit an existing %s" % self._meta.model_name)
super().save(*args, **kwargs)
class Meta:
verbose_name = _('Audit Entry')
verbose_name_plural = _('Audit Entries')