manager: show deleted users informations in journal (#51808)

This commit is contained in:
Benjamin Dauvergne 2021-03-09 23:03:00 +01:00
parent 5e29c79224
commit a921332fb2
5 changed files with 64 additions and 41 deletions

View File

@ -15,7 +15,7 @@
{% endif %} {% endif %}
<tr data-event-id="{{ event.id }}" data-event-cursor="{{ event.cursor }}" data-event-type="{{ event.type.name }}"> <tr data-event-id="{{ event.id }}" data-event-cursor="{{ event.cursor }}" data-event-type="{{ event.type.name }}">
<td class="journal-list--timestamp-column">{% block event-timestamp %}{{ event.timestamp }}{% endblock %}</td> <td class="journal-list--timestamp-column">{% block event-timestamp %}{{ event.timestamp }}{% endblock %}</td>
<td class="journal-list--user-column" {% if event.user %}data-user-id="{{ event.user.id }}"{% endif %}>{% block event-user %}{{ event.user.get_full_name|default:"-" }}{% endblock %}</td> <td class="journal-list--user-column" {% if event.user %}data-user-id="{{ event.user.id }}"{% endif %}>{% block event-user %}{% firstof event.user.get_full_name event.user "-" %}{% endblock %}</td>
<td class="journal-list--session-column">{% block event-session %}{{ event.session_id_shortened|default:"-" }}{% endblock %}</td> <td class="journal-list--session-column">{% block event-session %}{{ event.session_id_shortened|default:"-" }}{% endblock %}</td>
<td class="journal-list--message-column">{% block event-message %}{{ event.message|default:"-" }}{% endblock %}</td> <td class="journal-list--message-column">{% block event-message %}{{ event.message|default:"-" }}{% endblock %}</td>
</tr> </tr>

View File

@ -414,13 +414,19 @@ class DeletedUser(models.Model):
) )
cls.objects.filter(deleted__lt=threshold).delete() cls.objects.filter(deleted__lt=threshold).delete()
def __str__(self): def __repr__(self):
return 'DeletedUser(old_id=%s, old_uuid=%s…, old_email=%s)' % ( return 'DeletedUser(old_id=%s, old_uuid=%s…, old_email=%s)' % (
self.old_user_id or '-', self.old_user_id or '-',
(self.old_uuid or '')[:6], (self.old_uuid or '')[:6],
self.old_email or '-', self.old_email or '-',
) )
def __str__(self):
data = ['#%d' % self.old_user_id]
if self.old_email:
data.append(self.old_email)
return _('deleted user (%s)') % ', '.join(data)
class Meta: class Meta:
verbose_name = _('deleted user') verbose_name = _('deleted user')
verbose_name_plural = _('deleted users') verbose_name_plural = _('deleted users')

View File

@ -23,6 +23,7 @@ from authentic2.backends.ldap_backend import (
LDAP_DEACTIVATION_REASON_NOT_PRESENT, LDAP_DEACTIVATION_REASON_NOT_PRESENT,
LDAP_DEACTIVATION_REASON_OLD_SOURCE, LDAP_DEACTIVATION_REASON_OLD_SOURCE,
) )
from authentic2.custom_user.models import DeletedUser
from authentic2.journal_event_types import EventTypeWithService, get_attributes_label from authentic2.journal_event_types import EventTypeWithService, get_attributes_label
from django_rbac.utils import get_role_model from django_rbac.utils import get_role_model
@ -30,6 +31,12 @@ User = get_user_model()
Role = get_role_model() Role = get_role_model()
def user_to_str(user):
if hasattr(user, 'get_full_name'):
return user.get_full_name()
return str(user)
class ManagerUserCreation(EventTypeDefinition): class ManagerUserCreation(EventTypeDefinition):
name = 'manager.user.creation' name = 'manager.user.creation'
label = _('user creation') label = _('user creation')
@ -40,13 +47,13 @@ class ManagerUserCreation(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
# user journal page # user journal page
if context and context == user: if context and context == user:
return _('creation by administrator') return _('creation by administrator')
elif user: elif user:
# manager gloabal journal page # manager gloabal journal page
return _('creation of user "%s"') % user.get_full_name() return _('creation of user "%s"') % user_to_str(user)
return super().get_message(event, context) return super().get_message(event, context)
@ -60,13 +67,13 @@ class ManagerUserProfileEdit(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
new = event.get_data('new') or {} new = event.get_data('new') or {}
edited_attributes = ', '.join(get_attributes_label(new)) or '' edited_attributes = ', '.join(get_attributes_label(new)) or ''
if context and context == user: if context and context == user:
return _('edit by administrator (%s)') % edited_attributes return _('edit by administrator (%s)') % edited_attributes
elif user: elif user:
user_full_name = user.get_full_name() user_full_name = user_to_str(user)
return _('edit of user "{0}" ({1})').format(user_full_name, edited_attributes) return _('edit of user "{0}" ({1})').format(user_full_name, edited_attributes)
return super().get_message(event, context) return super().get_message(event, context)
@ -85,12 +92,12 @@ class ManagerUserEmailChangeRequest(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
new_email = event.get_data('email') new_email = event.get_data('email')
if context and context == user: if context and context == user:
return _('email change for email address "%s" requested by administrator') % new_email return _('email change for email address "%s" requested by administrator') % new_email
elif user: elif user:
user_full_name = user.get_full_name() user_full_name = user_to_str(user)
return _('email change of user "{0}" for email address "{1}"').format(user_full_name, new_email) return _('email change of user "{0}" for email address "{1}"').format(user_full_name, new_email)
return super().get_message(event, context) return super().get_message(event, context)
@ -109,7 +116,7 @@ class ManagerUserPasswordChange(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
send_mail = event.get_data('send_mail') send_mail = event.get_data('send_mail')
if context and context == user: if context and context == user:
if send_mail: if send_mail:
@ -117,7 +124,7 @@ class ManagerUserPasswordChange(EventTypeDefinition):
else: else:
return _('password change by administrator') return _('password change by administrator')
elif user: elif user:
user_full_name = user.get_full_name() user_full_name = user_to_str(user)
if send_mail: if send_mail:
return _('password change of user "%s" and notification by mail') % user_full_name return _('password change of user "%s" and notification by mail') % user_full_name
else: else:
@ -137,12 +144,12 @@ class ManagerUserPasswordResetRequest(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
email = event.get_data('email') email = event.get_data('email')
if context and context == user: if context and context == user:
return _('password reset request by administrator sent to "%s"') % email return _('password reset request by administrator sent to "%s"') % email
elif user: elif user:
return _('password reset request of "{0}" sent to "{1}"').format(user.get_full_name(), email) return _('password reset request of "{0}" sent to "{1}"').format(user_to_str(user), email)
return super().get_message(event, context) return super().get_message(event, context)
@ -156,11 +163,11 @@ class ManagerUserPasswordChangeForce(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
if context and context == user: if context and context == user:
return _('mandatory password change at next login set by administrator') return _('mandatory password change at next login set by administrator')
elif user: elif user:
return _('mandatory password change at next login set for user "%s"') % user.get_full_name() return _('mandatory password change at next login set for user "%s"') % user_to_str(user)
return super().get_message(event, context) return super().get_message(event, context)
@ -174,11 +181,11 @@ class ManagerUserPasswordChangeUnforce(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
if context and context == user: if context and context == user:
return _('mandatory password change at next login unset by administrator') return _('mandatory password change at next login unset by administrator')
elif user: elif user:
return _('mandatory password change at next login unset for user "%s"') % user.get_full_name() return _('mandatory password change at next login unset for user "%s"') % user_to_str(user)
return super().get_message(event, context) return super().get_message(event, context)
@ -192,11 +199,11 @@ class ManagerUserActivation(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
if context and context == user: if context and context == user:
return _('activation by administrator') return _('activation by administrator')
elif user: elif user:
return _('activation of user "%s"') % user.get_full_name() return _('activation of user "%s"') % user_to_str(user)
return super().get_message(event, context) return super().get_message(event, context)
@ -211,7 +218,7 @@ class ManagerUserDeactivation(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
reason = event.get_data('reason') reason = event.get_data('reason')
if context and context == user: if context and context == user:
if reason == LDAP_DEACTIVATION_REASON_NOT_PRESENT: if reason == LDAP_DEACTIVATION_REASON_NOT_PRESENT:
@ -222,21 +229,15 @@ class ManagerUserDeactivation(EventTypeDefinition):
return _('deactivation by administrator') return _('deactivation by administrator')
elif user: elif user:
if reason == LDAP_DEACTIVATION_REASON_NOT_PRESENT: if reason == LDAP_DEACTIVATION_REASON_NOT_PRESENT:
return ( return _(
_( 'automatic deactivation of user "%s" because the associated LDAP account does not exist anymore'
'automatic deactivation of user "%s" because the associated LDAP account does not exist anymore' ) % user_to_str(user)
)
% user.get_full_name()
)
elif reason == LDAP_DEACTIVATION_REASON_OLD_SOURCE: elif reason == LDAP_DEACTIVATION_REASON_OLD_SOURCE:
return ( return _(
_( 'automatic deactivation of user "%s" because the associated LDAP source has been deleted'
'automatic deactivation of user "%s" because the associated LDAP source has been deleted' ) % user_to_str(user)
)
% user.get_full_name()
)
else: else:
return _('deactivation of user "%s"') % user.get_full_name() return _('deactivation of user "%s"') % user_to_str(user)
return super().get_message(event, context) return super().get_message(event, context)
@ -250,11 +251,11 @@ class ManagerUserDeletion(EventTypeDefinition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
(user,) = event.get_typed_references(User) (user,) = event.get_typed_references((DeletedUser, User))
if context and context == user: if context and context == user:
return _('deletion by administrator') return _('deletion by administrator')
elif user: elif user:
return _('deletion of user "%s"') % user.get_full_name() return _('deletion of user "%s"') % user_to_str(user)
return super().get_message(event, context) return super().get_message(event, context)
@ -269,7 +270,7 @@ class ManagerUserSSOAuthorizationDeletion(EventTypeWithService):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
# first reference is to the service # first reference is to the service
__, user = event.get_typed_references(None, User) __, user = event.get_typed_references(None, (DeletedUser, User))
service_name = cls.get_service_name(event) service_name = cls.get_service_name(event)
if context and context == user: if context and context == user:
return _('deletion of authorization of single sign on with "{service}" by administrator').format( return _('deletion of authorization of single sign on with "{service}" by administrator').format(
@ -278,7 +279,7 @@ class ManagerUserSSOAuthorizationDeletion(EventTypeWithService):
elif user: elif user:
return _('deletion of authorization of single sign on with "{service}" of user "{user}"').format( return _('deletion of authorization of single sign on with "{service}" of user "{user}"').format(
service=service_name, service=service_name,
user=user.get_full_name(), user=user_to_str(user),
) )
return super().get_message(event, context) return super().get_message(event, context)
@ -357,7 +358,7 @@ class ManagerRoleMembershipGrant(RoleEventsMixin):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
role, member = event.get_typed_references(Role, User) role, member = event.get_typed_references(Role, (DeletedUser, User))
role = role or event.get_data('role_name') role = role or event.get_data('role_name')
member = member or event.get_data('member_name') member = member or event.get_data('member_name')
if context == member: if context == member:
@ -379,7 +380,7 @@ class ManagerRoleMembershipRemoval(RoleEventsMixin):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
role, member = event.get_typed_references(Role, User) role, member = event.get_typed_references(Role, (DeletedUser, User))
role = role or event.get_data('role_name') role = role or event.get_data('role_name')
member = member or event.get_data('member_name') member = member or event.get_data('member_name')
if context == member: if context == member:
@ -491,14 +492,14 @@ class ManagerRoleAdministratorUserAddition(RoleEventsMixin):
@classmethod @classmethod
def record(cls, user, session, role, admin_user): def record(cls, user, session, role, admin_user):
data = { data = {
'admin_user_name': admin_user.get_full_name(), 'admin_user_name': user_to_str(admin_user),
'admin_user_uuid': admin_user.uuid, 'admin_user_uuid': admin_user.uuid,
} }
super().record(user=user, session=session, role=role, references=[admin_user], data=data) super().record(user=user, session=session, role=role, references=[admin_user], data=data)
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
role, admin_user = event.get_typed_references(Role, User) role, admin_user = event.get_typed_references(Role, (DeletedUser, User))
role = role or event.get_data('role_name') role = role or event.get_data('role_name')
admin_user = admin_user or event.get_data('admin_user_name') admin_user = admin_user or event.get_data('admin_user_name')
if context == role: if context == role:
@ -517,7 +518,7 @@ class ManagerRoleAdministratorUserRemoval(ManagerRoleAdministratorUserAddition):
@classmethod @classmethod
def get_message(cls, event, context): def get_message(cls, event, context):
role, admin_user = event.get_typed_references(Role, User) role, admin_user = event.get_typed_references(Role, (DeletedUser, User))
role = role or event.get_data('role_name') role = role or event.get_data('role_name')
admin_user = admin_user or event.get_data('admin_user_name') admin_user = admin_user or event.get_data('admin_user_name')
if context == role: if context == role:

View File

@ -125,6 +125,12 @@ class JournalForm(JournalForm):
return qs return qs
def prefetcher(self, model, pks):
if not issubclass(model, User):
return
for deleted_user in DeletedUser.objects.filter(old_user_id__in=pks):
yield deleted_user.old_user_id, deleted_user
class BaseJournalView(views.TitleMixin, views.MediaMixin, views.MultipleOUMixin, JournalView): class BaseJournalView(views.TitleMixin, views.MediaMixin, views.MultipleOUMixin, JournalView):
template_name = 'authentic2/manager/journal.html' template_name = 'authentic2/manager/journal.html'

View File

@ -1047,3 +1047,13 @@ def test_search(app, superuser, events):
'addition of user "user (111111)" as administrator of role "role1"', 'addition of user "user (111111)" as administrator of role "role1"',
'removal of role "role2" as administrator of role "role1"', 'removal of role "role2" as administrator of role "role1"',
] ]
def test_delete_user(app, superuser, events):
old_user_id = events['user'].id
events['user'].delete()
response = login(app, user=superuser, path="/manage/journal/")
response.form.set('search', events['user'].email)
response = response.form.submit()
assert len(response.pyquery('tbody tr')) == 20
assert 'deletion of user &quot;deleted user (#%s, user@example.com)&quot;' % old_user_id in str(response)