apps: ajoute des gestionnaires d'évènement pour les statistiques et le journal (fixes #17571 #16585)

This commit is contained in:
Benjamin Dauvergne 2017-11-16 10:53:05 +01:00
parent d8410facef
commit 85152539a6
4 changed files with 174 additions and 53 deletions

View File

@ -21,6 +21,7 @@ import django.apps
from django.conf import settings
from django.db import router, DEFAULT_DB_ALIAS
from django.utils.timezone import now
class AppConfig(django.apps.AppConfig):
@ -292,6 +293,19 @@ class AppConfig(django.apps.AppConfig):
serializer.get_phone_number_fc = get_phone_number_fc
serializer.fields['phone_number_fc'] = serializers.SerializerMethodField()
# override serializer.create to set the creation mode
old_create = serializer.create
def new_create(*args, **kwargs):
instance = old_create(*args, **kwargs)
request = serializer.context['request']
if hasattr(request.user, 'oidc_client'):
ou = request.user.oidc_client.ou
instance.attributes.creation_partner = ou.slug
instance.attributes.creation_mode = 'BO'
return instance
serializer.create = new_create
# execute after other modifiers
a2_hook_api_modify_serializer.order = 999
@ -452,3 +466,123 @@ class AppConfig(django.apps.AppConfig):
user_info['address_fc_formatted'] = address
user_info['phone_number_fc'] = fc_user_info.get('phone_number')
logging.info(u'modified user_info %s', user_info)
def a2_hook_event(self, name, **kwargs):
method_name = 'cut_event_' + name.replace('-', '_')
if hasattr(self, method_name):
getattr(self, method_name)(**kwargs)
def log_action(self, actor, message):
from . import models
models.Journal.objects.create(
actor=actor,
message=message)
def log_modification(self, actor, subject, message):
from . import models
models.Journal.objects.create(
actor=actor,
subject=subject,
message=message)
@property
def redis_client(self):
from django.core import cache
return cache.cache.client.get_client()
def stat(self, *args):
t = now()
key = 'stat__%s%02d__' % (t.date(), t.hour)
key += '__'.join(args)
self.redis_client.expire(key, 3600 * 24 * 7)
self.redis_client.incr(key)
def cut_event_login(self, user, how, service=None, **kwargs):
# log login for current user
if how.startswith('password'):
msg = u'connexion par mot de passe'
method = 'PWD'
elif how == 'france-connect':
msg = u'connexion par FranceConnect'
method = 'FC'
else:
msg = u'connexion how:%s'
method = how.upper()
if service:
msg += u' pour %s' % service
self.log_action(user, msg)
self.stat('login', method)
def cut_event_registration(self, user, view, form, token, service, **kwargs):
# log registration for current user
creation_mode = 'FO'
msg = u'création du compte en ligne'
if 'franceconnect' in token:
creation_mode = 'FC'
msg = u'création du compte via France Connect'
user.attributes.creation_mode = creation_mode
self.log_action(user, msg)
if service:
user.attributes.creation_partner = service
def cut_event_sso_request(self, idp, service, **kwargs):
self.stat('sso-request', service.slug)
def cut_event_sso_success(self, idp, service, user, **kwargs):
msg = u'connexion à %s' % service.name
self.log_action(user, msg)
self.stat('sso-success', service.slug)
def cut_event_cut_edit_core(self, user, **kwargs):
self.log_action(user, u'édition du profil coeur')
def cut_event_cut_edit_crown(self, user, **kwargs):
self.log_action(user, u'édition du profil couronne')
def cut_event_password_reset_confirm(self, user, **kwargs):
self.log_action(user, u'ré-initialisation du mot de passe')
def cut_event_change_email_confirm(self, user, **kwargs):
self.log_action(user, u'changement de l\'adresse de courriel')
def cut_event_delete_account(self, user, **kwargs):
self.log_action(user, u'demande de suppression du compte')
def cut_event_manager_action(self, user, action, instance, **kwargs):
msgs = {
'activate': u'ré-activation du compte',
'deactivate': u'suspension du compte',
'password_reset': u'envoi d\'un courriel de ré-initialisation du mot de passe',
'force_password_change': u'force un changement de mot de passe à '
u'la prochaine connexion',
'delete_password_reset': u'supprime l\'obligation de changement de mot de passe à '
u'la prochaine connexion',
}
if action.name in msgs:
self.log_modification(user, instance, msgs[action.name])
def cut_event_manager_add_user(self, user, instance, **kwargs):
self.log_modification(user, instance, u'création d\'un utilisateur')
def cut_event_manager_edit_user(self, user, instance, **kwargs):
if instance.ou and instance.ou.slug != 'usagers':
self.log_modification(user, instance, u'modification du profil')
def cut_event_manager_delete_user(self, user, instance, **kwargs):
self.log_action(self, user, u'suppression de l\'utilisateur %s' % instance)
def cut_event_manager_add_role_member(self, user, role, member, **kwargs):
self.log_modification(user, member, u'ajoute le rôle %s' % role)
def cut_event_manager_remove_role_member(self, user, role, member, **kwargs):
self.log_modification(user, member, u'retire le rôle %s' % role)
def cut_event_manager_cut_validate(self, user, instance, **kwargs):
if instance.attributes.validated:
msg = u'modification des données cœur'
else:
msg = u'validation du compte'
self.log_modification(user, instance, msg)
def cut_event_manager_view_user(self, user, instance, **kwargs):
self.log_modification(user, instance, u'fiche consultée')

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
('authentic2_cut', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='journal',
name='subject',
field=models.ForeignKey(related_name='subject_journal', verbose_name=b'Sujet', to=settings.AUTH_USER_MODEL, null=True),
),
]

View File

@ -14,6 +14,7 @@ class Journal(models.Model):
subject = models.ForeignKey(
settings.AUTH_USER_MODEL,
verbose_name='Sujet',
null=True,
related_name='subject_journal')
message = models.TextField(
verbose_name='Message')

View File

@ -24,9 +24,10 @@ from django.views.generic.base import TemplateView
from authentic2.views import EditProfile
from authentic2.manager.views import SimpleSubTableView
from authentic2.manager.user_views import UserEditView, UserDetailView
from authentic2 import hooks
from .custom_settings import CORE_ATTRIBUTES
from . import tables, models
from . import tables
class EditCoreView(EditProfile):
@ -45,6 +46,11 @@ class EditCoreView(EditProfile):
fields, labels = super(EditCoreView, cls).get_fields()
return [field for field in fields if field in cls.fields], labels
def form_valid(self, form):
response = super(EditCoreView, self).form_valid(self)
hooks.call_hooks('event', name='cut-edit-core', user=self.request.user, form=form)
return response
edit_core = EditCoreView.as_view()
@ -64,6 +70,11 @@ class EditCrownView(EditProfile):
fields, labels = super(EditCrownView, cls).get_fields()
return [field for field in fields if field not in cls.fields], labels
def form_valid(self, form):
response = super(EditCrownView, self).form_valid(self)
hooks.call_hooks('event', name='cut-edit-crown', user=self.request.user, form=form)
return response
edit_crown = EditCrownView.as_view()
@ -90,65 +101,22 @@ class UserEditCoreView(UserEditView):
def form_valid(self, form):
response = super(UserEditCoreView, self).form_valid(form)
hooks.call_hooks('event', user=self.request.user, name='manager-cut-validate',
instance=form.instance, form=form)
if not form.instance.attributes.validated:
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message='validation du compte')
form.instance.attributes.validated = True
form.instance.attributes.validation_context = 'office'
form.instance.attributes.validation_date = now().date()
messages.info(self.request, u'Le compte a été validé.')
msg = u'Le compte a été validé.'
else:
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message='modification des données cœur')
messages.info(self.request, u'Les données cœur ont été modifié.')
msg = u'Les données cœur ont été modifié.'
messages.info(self.request, msg)
return response
manager_user_edit_core = UserEditCoreView.as_view()
class ManagerUserDetailView(UserDetailView):
def action_force_password_change(self, request, *args, **kwargs):
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message=u'obligation de changement de mot de passe à la prochaine connexion')
return super(ManagerUserDetailView, self).action_force_password_change(
request, *args, **kwargs)
def action_activate(self, request, *args, **kwargs):
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message=u'ré-activation du compte')
return super(ManagerUserDetailView, self).action_activate(request, *args, **kwargs)
def action_deactivate(self, request, *args, **kwargs):
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message=u'suspension du compte')
return super(ManagerUserDetailView, self).action_deactivate(request, *args, **kwargs)
def action_password_reset(self, request, *args, **kwargs):
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message=u'demande de réinitialisation du mot de passe')
return super(ManagerUserDetailView, self).action_password_reset(request, *args, **kwargs)
def action_delete_password_change(self, request, *args, **kwargs):
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message=u'supprime l\'obligation de changement de mot de passe à la prochaine '
u'connexion')
return super(ManagerUserDetailView, self).action_delete_password_change(
request, *args, **kwargs)
def get(self, request, *args, **kwargs):
response = super(ManagerUserDetailView, self).get(request, *args, **kwargs)
# journalise les accès aux fiches une fois par heure et par session
@ -156,10 +124,8 @@ class ManagerUserDetailView(UserDetailView):
key = 'user-looked-%s-%s-%s' % (self.object, t.date(), t.time().hour)
if key not in request.session:
request.session[key] = True
models.Journal.objects.create(
actor=self.request.user,
subject=self.object,
message=u'fiche consultée')
hooks.call_hooks('event', name='manager-view-user', user=request.user,
instance=self.object, request=request, **kwargs)
return response