Projet

Général

Profil

0001-WIP-add-journal-application-20695.patch

Paul Marillonnet, 21 novembre 2018 18:08

Télécharger (27,4 ko)

Voir les différences:

Subject: [PATCH] WIP add journal application (#20695)

 MANIFEST.in                                   |   2 +
 setup.py                                      |   1 +
 src/authentic2/settings.py                    |   1 +
 src/authentic2_journal/__init__.py            |  26 +++
 src/authentic2_journal/admin.py               |   0
 src/authentic2_journal/app_settings.py        |  38 ++++
 src/authentic2_journal/apps.py                |  44 +++++
 src/authentic2_journal/event_logger.py        |  59 +++++++
 src/authentic2_journal/migrations/__init__.py |   0
 src/authentic2_journal/models.py              |  71 ++++++++
 src/authentic2_journal/tables.py              |  27 +++
 .../authentic2_journal/user_events.html       |  10 ++
 src/authentic2_journal/urls.py                |  26 +++
 src/authentic2_journal/utils.py               |  75 ++++++++
 src/authentic2_journal/views.py               |  45 +++++
 tests/test_journal.py                         | 166 ++++++++++++++++++
 16 files changed, 591 insertions(+)
 create mode 100644 src/authentic2_journal/__init__.py
 create mode 100644 src/authentic2_journal/admin.py
 create mode 100644 src/authentic2_journal/app_settings.py
 create mode 100644 src/authentic2_journal/apps.py
 create mode 100644 src/authentic2_journal/event_logger.py
 create mode 100644 src/authentic2_journal/migrations/__init__.py
 create mode 100644 src/authentic2_journal/models.py
 create mode 100644 src/authentic2_journal/tables.py
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/user_events.html
 create mode 100644 src/authentic2_journal/urls.py
 create mode 100644 src/authentic2_journal/utils.py
 create mode 100644 src/authentic2_journal/views.py
 create mode 100644 tests/test_journal.py
MANIFEST.in
22 22
recursive-include src/authentic2_auth_saml/templates/authentic2_auth_saml *.html
23 23
recursive-include src/authentic2_auth_oidc/templates/authentic2_auth_oidc *.html
24 24
recursive-include src/authentic2_idp_oidc/templates/authentic2_idp_oidc *.html
25
recursive-include src/authentic2_journal/templates/authentic2_journal *.html
25 26

  
26 27
recursive-include src/authentic2/vendor/totp_js/js *.js
27 28
recursive-include src/authentic2/saml/fixtures *.json
......
41 42
recursive-include src/authentic2_auth_saml/locale *.po *.mo
42 43
recursive-include src/authentic2_auth_oidc/locale *.po *.mo
43 44
recursive-include src/authentic2_idp_oidc/locale *.po *.mo
45
recursive-include src/authentic2_journal/locale *.po *.mo
44 46

  
45 47
recursive-include src/authentic2 README  xrds.xml *.txt yadis.xrdf
46 48
recursive-include src/authentic2_provisionning_ldap/tests *.ldif
setup.py
165 165
              'authentic2-idp-cas = authentic2_idp_cas:Plugin',
166 166
              'authentic2-idp-oidc = authentic2_idp_oidc:Plugin',
167 167
              'authentic2-provisionning-ldap = authentic2_provisionning_ldap:Plugin',
168
              'authentic2-journal = authentic2_journal:Plugin',
168 169
          ],
169 170
      })
src/authentic2/settings.py
126 126
    'authentic2.disco_service',
127 127
    'authentic2.manager',
128 128
    'authentic2_provisionning_ldap',
129
    'authentic2_journal',
129 130
    'authentic2',
130 131
    'django_rbac',
131 132
    'authentic2.a2_rbac',
src/authentic2_journal/__init__.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
class Plugin(object):
18
    def get_after_urls(self):
19
        from django.conf.urls import include, url
20
        from . import urls
21
        return [url(r'^manage/', include(urls))]
22

  
23
    def get_apps(self):
24
        return [__name__]
25

  
26
default_app_config = 'authentic2_journal.apps.Authentic2JournalConfig'
src/authentic2_journal/app_settings.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
class AppSettings(object):
18
    __DEFAULTS = {
19
            'ENABLE': False,
20
    }
21

  
22
    def __init__(self, prefix):
23
        self.prefix = prefix
24

  
25
    def _setting(self, name, dflt):
26
        from django.conf import settings
27
        return getattr(settings, self.prefix + name, dflt)
28

  
29
    def __getattr__(self, name):
30
        if name not in self.__DEFAULTS:
31
            raise AttributeError(name)
32
        return self._setting(name, self.__DEFAULTS[name])
33

  
34

  
35
import sys
36
app_settings = AppSettings('A2_JOURNAL_')
37
app_settings.__name__ = __name__
38
sys.modules[__name__] = app_settings
src/authentic2_journal/apps.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from __future__ import unicode_literals
18

  
19
from django.db import DEFAULT_DB_ALIAS, router
20
from django.apps import AppConfig
21

  
22

  
23
class Authentic2JournalConfig(AppConfig):
24
    name = 'authentic2_journal'
25
    verbose_name = 'Authentic 2 Journal Application'
26

  
27
    def ready(self):
28
        from django.db.models.signals import post_migrate
29

  
30
        post_migrate.connect(
31
            self.create_event_kinds,
32
            sender=self)
33

  
34
    def create_event_kinds(self, app_config, verbosity=2, interactive=True,
35
            using=DEFAULT_DB_ALIAS, **kwargs):
36
        from authentic2_journal.models import EventKind, KINDS
37

  
38
        if not router.allow_migrate(using, EventKind):
39
            return
40
        if EventKind.objects.filter(name__in=KINDS).count() == 9:
41
            return
42

  
43
        for kind in KINDS:
44
            EventKind.objects.get_or_create(name=kind)
src/authentic2_journal/event_logger.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from authentic2_journal.models import Line
18
from authentic2_journal.utils import new_line, new_reference, log_event
19
from django.utils.translation import ugettext_lazy as _
20

  
21

  
22
def sso(user, service):
23
    representation = _('SSO: user %s on service %s') % (user.email, service)
24
    log_event(user, service, 'action_sso', representation)
25

  
26

  
27
def login(user, provider):
28
    representation = _('LOGIN: user %s on provider %s') % (user.email, provider)
29
    log_event(user, provider, 'action_login', representation)
30

  
31

  
32
def logout(user, provider):
33
    representation = _('LOGOUT: user %s on provider %s') % (user.email, provider)
34
    log_event(user, provider, 'action_logout', representation)
35

  
36

  
37
def register(user, ou):
38
    representation = _('REGISTER: user %s in OU %s') % (user.email, ou)
39
    log_event(user, ou, 'action_register', representation)
40

  
41

  
42
def create(user, obj):
43
    representation = _('CREATE: user %s on %s') % (user.email, obj)
44
    log_event(user, obj, 'modification_create', representation)
45

  
46

  
47
def update(user, obj):
48
    representation = _('UPDATE: user %s on %s') % (user.email, obj)
49
    log_event(user, obj, 'modification_update', representation)
50

  
51

  
52
def modify_members(user, role):
53
    representation = _('MODIFY MEMBERS: user %s on role %s') % (user.email, role.slug)
54
    log_event(user, role, 'modification_modify_members', representation)
55

  
56

  
57
def delete(user, obj):
58
    representation = _('DELETE: user %s on %s') % (user.email, obj)
59
    log_event(user, obj, 'modification_delete', representation)
src/authentic2_journal/models.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from __future__ import unicode_literals
18
from django.db import models
19
try:
20
    from django.contrib.contenttypes.fields import GenericForeignKey
21
except ImportError:
22
    from django.contrib.contenttypes.generic import GenericForeignKey
23
try:
24
    from django.contrib.postgres.fields import JSONField # dj1.11 native JSON
25
except:
26
    from jsonfield import JSONField # django-jsonfield outer module
27
from django.contrib.contenttypes.models import ContentType
28
from django.utils.translation import ugettext_lazy as _
29

  
30

  
31
KINDS = (
32
    'action_register',
33
    'action_login',
34
    'action_sso',
35
    'action_logout',
36
    'modification_create',
37
    'modification_update',
38
    'modification_modify_members',
39
    'modification_delete',
40
    'generic_event'
41
)
42

  
43

  
44
def get_choices():
45
    return tuple((kind, kind) for kind in KINDS)
46

  
47

  
48
class EventKind(models.Model):
49
    name = models.CharField(
50
            verbose_name=_('name'), choices=get_choices(), max_length=256)
51

  
52

  
53
class Line(models.Model):
54
    timestamp = models.DateTimeField(auto_now=True, verbose_name=_('timestamp'))
55
    kind = models.ForeignKey(EventKind)
56
    extra = JSONField(blank=True,null=True, verbose_name=_('content'))
57

  
58

  
59
class Reference(models.Model):
60
    timestamp = models.DateTimeField(verbose_name=_('timestamp'))
61
    line = models.ForeignKey(Line)
62
    representation = models.TextField(max_length=256)
63
    target_ct = models.ForeignKey(ContentType)
64
    target_id = models.IntegerField(default=0)
65
    target = GenericForeignKey('target_ct', 'target_id')
66

  
67
    def __init__(self, *args, **kwargs):
68
        line = kwargs.get('line')
69
        if line:
70
            kwargs.update({'timestamp': line.timestamp})
71
        models.Model.__init__(self, *args, **kwargs)
src/authentic2_journal/tables.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import django_tables2 as tables
18
from authentic2_journal.models import Reference
19
from django.utils.translation import ugettext_lazy as _
20

  
21

  
22
class UserEventsTable(tables.Table):
23
    class Meta:
24
        attrs = {'class': 'main'}
25
        model = Reference
26
        exclude = ['line', 'target_id', 'target_ct']
27
        empty_text = _('None')
src/authentic2_journal/templates/authentic2_journal/user_events.html
1
{% extends "authentic2/manager/base.html" %}
2
{% load i18n staticfiles django_tables2 %}
3

  
4
{% block page_title %}
5
{% trans "User events journal" %}
6
{% endblock %}
7

  
8
{% block content %}
9
  {% render_table table "authentic2/manager/table.html" %}
10
{% endblock %}
src/authentic2_journal/urls.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from django.conf.urls import url
18

  
19
from . import views
20

  
21

  
22
urlpatterns = [
23
    url('^users/(?P<pk>\d+)/journal/$',
24
        views.user_journal,
25
        name='a2-journal-user'),
26
]
src/authentic2_journal/utils.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import logging
18

  
19
from authentic2_journal.models import EventKind, Line, Reference
20
from django.contrib.contenttypes.models import ContentType
21
from django.core.exceptions import ObjectDoesNotExist
22

  
23

  
24
def new_line(user, obj, kind='generic_event', extra={}):
25
    logger = logging.getLogger(__name__)
26
    try:
27
        kind = EventKind.objects.get(name=kind)
28
    except:
29
        logger.error('EventKind retrieval from model failed for event name %s' %
30
            kind)
31
    else:
32
        try:
33
            line = Line.objects.create(
34
                kind=kind,
35
                extra=extra
36
            )
37
        except:
38
            logger.error("Couldn't create event journal entry for user %s on "
39
                "object %s" % (user, obj))
40
        else:
41
            return line
42

  
43

  
44
def new_reference(line, obj, representation):
45
    logger = logging.getLogger(__name__)
46
    try:
47
        ct = ContentType.objects.get_for_model(obj)
48
    except ObjectDoesNotExist:
49
        logger.error('ContentType retrieval from model failed for object %s' %
50
            obj)
51
        logger.warning('No reference will be created for line %s' % line)
52
    else:
53
        try:
54
            reference = Reference.objects.create(
55
                    line=line,
56
                    representation=representation,
57
                    target_ct=ct,
58
                    target_id=obj.pk)
59
        except:
60
            logger.error("Couldn't create event reference for line %s and "
61
                "object %s" % (line, obj))
62
        else:
63
            return reference
64

  
65

  
66
def log_event(user, obj, kind, representation):
67
    extra = {
68
        'user_email': user.email,
69
        'user_pk': user.pk,
70
        'obj_representation': '%s' % obj,
71
        'kind': kind
72
    }
73
    line = new_line(user, obj, kind, extra)
74
    new_reference(line, user, representation)
75
    new_reference(line, obj, representation)
src/authentic2_journal/views.py
1
# authentic - Versatile identity management server
2
# Copyright (C) 2018 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
from authentic2.manager.views import SimpleSubTableView
18
from authentic2_journal.tables import UserEventsTable
19
from authentic2_journal.models import Reference
20
from django.contrib.auth import get_user_model
21
from django.contrib.contenttypes.models import ContentType
22
from django.db import models
23

  
24

  
25
class BaseJournal(SimpleSubTableView):
26
    model = models.Model
27
    table_class = None # TODO generic table class
28
    template_name = '' # TODO generic template name
29
    permissions = [] # TODO generic custom_user.view_object permission ?
30
    filter_table_by_perm = False
31

  
32
    def get_table_queryset(self):
33
        return Reference.objects.filter(
34
                target_id=self.object.pk,
35
                target_ct=ContentType.objects.get_for_model(self.object))
36

  
37

  
38
class UserJournal(BaseJournal):
39
    model = get_user_model()
40
    table_class = UserEventsTable
41
    template_name = 'authentic2_journal/user_events.html'
42
    permissions = ['custom_user.view_user']
43
    filter_table_by_perm = False
44

  
45
user_journal = UserJournal.as_view()
tests/test_journal.py
1
# -*- coding: utf-8 -*-
2

  
3
from authentic2.a2_rbac.models import Role
4
from authentic2.a2_rbac.utils import get_default_ou
5
from authentic2.models import Service
6
from authentic2.saml.models import (LibertyServiceProvider as SP,
7
    LibertyProvider as IdP)
8
from authentic2_journal.event_logger import (sso, login, logout, register,
9
        create, update, modify_members, delete)
10
from authentic2_journal.models import Line, Reference
11
from django.contrib.auth import get_user_model
12
from django.contrib.contenttypes.models import ContentType
13
from django.db import transaction
14
from django_rbac.utils import get_ou_model, get_role_model
15

  
16

  
17
def test_action_events(db, app, admin, simple_user, ou1):
18
    s1 = Service.objects.create(name='s1', slug='s1')
19
    idp1 = IdP.objects.create(
20
            name='idp',
21
            slug='idp',
22
            protocol_conformance=3)
23
    sp1 = SP.objects.create(liberty_provider=idp1, enabled=True)
24
    user_ct = ContentType.objects.get_for_model(get_user_model())
25
    ou_ct = ContentType.objects.get_for_model(get_ou_model())
26
    idp_ct = ContentType.objects.get_for_model(IdP)
27
    sp_ct = ContentType.objects.get_for_model(SP)
28

  
29
    assert not len(Line.objects.all())
30
    assert not len(Reference.objects.all())
31

  
32
    register(admin, ou1)
33
    assert len(Line.objects.all()) == 1
34
    assert len(Reference.objects.filter(
35
            target_id=admin.pk, target_ct=user_ct)) == 1
36
    assert len(Reference.objects.filter(
37
            target_id=ou1.pk, target_ct=ou_ct)) == 1
38

  
39
    sso(admin, sp1)
40
    assert len(Line.objects.all()) == 2
41
    assert len(Reference.objects.filter(
42
            target_id=admin.pk, target_ct=user_ct)) == 2
43
    assert len(Reference.objects.filter(
44
            target_id=sp1.pk, target_ct=sp_ct)) == 1
45

  
46
    login(admin, idp1)
47
    assert len(Line.objects.all()) == 3
48
    assert len(Reference.objects.filter(
49
            target_id=admin.pk, target_ct=user_ct)) == 3
50
    assert len(Reference.objects.filter(
51
            target_id=idp1.pk, target_ct=idp_ct)) == 1
52

  
53
    logout(admin, idp1)
54
    assert len(Line.objects.all()) == 4
55
    assert len(Reference.objects.filter(
56
            target_id=admin.pk, target_ct=user_ct)) == 4
57
    assert len(Reference.objects.filter(
58
            target_id=idp1.pk, target_ct=idp_ct)) == 2
59

  
60

  
61
def test_modification_events(db, admin, simple_user):
62
    r1 = Role.objects.create(name='r1', slug='r1')
63
    user_ct = ContentType.objects.get_for_model(get_user_model())
64
    ou_ct = ContentType.objects.get_for_model(get_ou_model())
65
    role_ct = ContentType.objects.get_for_model(get_role_model())
66

  
67
    assert not len(Line.objects.all())
68
    assert not len(Reference.objects.all())
69

  
70
    create(admin, simple_user)
71
    assert len(Line.objects.all()) == 1
72
    assert len(Reference.objects.filter(
73
            target_id=admin.pk, target_ct=user_ct)) == 1
74
    assert len(Reference.objects.filter(
75
            target_id=simple_user.pk, target_ct=user_ct)) == 1
76

  
77
    update(admin, simple_user)
78
    assert len(Line.objects.all()) == 2
79
    assert len(Reference.objects.filter(
80
            target_id=admin.pk, target_ct=user_ct)) == 2
81
    assert len(Reference.objects.filter(
82
            target_id=simple_user.pk, target_ct=user_ct)) == 2
83

  
84
    delete(admin, simple_user)
85
    assert len(Line.objects.all()) == 3
86
    assert len(Reference.objects.filter(
87
            target_id=admin.pk, target_ct=user_ct)) == 3
88
    assert len(Reference.objects.filter(
89
            target_id=simple_user.pk, target_ct=user_ct)) == 3
90

  
91
    modify_members(admin, r1)
92
    assert len(Line.objects.all()) == 4
93
    assert len(Reference.objects.filter(
94
            target_id=admin.pk, target_ct=user_ct)) == 4
95
    assert len(Reference.objects.filter(
96
            target_id=r1.pk, target_ct=role_ct)) == 1
97

  
98

  
99
def test_entries_timestamp_consistency(db, admin):
100
    r1 = Role.objects.create(name='r1', slug='r1')
101
    modify_members(admin, r1)
102
    line = Line.objects.first()
103
    for ref in Reference.objects.all():
104
        assert ref.timestamp == line.timestamp
105

  
106

  
107
def test_line_retrieval_on_object_deletion(db):
108
    email = 'toto@nowhere.null'
109
    role_name = role_slug = 'r1'
110

  
111
    entries_role = []
112
    entries_user = []
113
    for line in Line.objects.all():
114
        if role_name in line.extra['obj_representation']:
115
            entries_role.append(line)
116
        if email == line.extra['user_email']:
117
            entries_user.append(line)
118
    assert not len(entries_role)
119
    assert not len(entries_user)
120

  
121
    role = Role.objects.create(name=role_name, slug=role_slug)
122
    user = get_user_model().objects.create(email=email)
123
    modify_members(user, role)
124
    user.delete()
125
    role.delete()
126

  
127
    entries_role = []
128
    entries_user = []
129
    for line in Line.objects.all():
130
        if role_name in line.extra['obj_representation']:
131
            entries_role.append(line)
132
        if email == line.extra['user_email']:
133
            entries_user.append(line)
134
    assert len(entries_role)
135
    assert len(entries_user)
136

  
137

  
138
def test_reference_retrieval_after_objects_deletion(db):
139
    email = 'toto@nowhere.null'
140
    role_name = role_slug = 'r1'
141

  
142
    entries_role = []
143
    entries_user = []
144
    for ref in Reference.objects.all():
145
        if role_name in ref.representation:
146
            entries_role.append(ref)
147
        if email in ref.representation:
148
            entries_user.append(ref)
149
    assert not len(entries_role)
150
    assert not len(entries_user)
151

  
152
    role = Role.objects.create(name=role_name, slug=role_slug)
153
    user = get_user_model().objects.create(email=email)
154
    modify_members(user, role)
155
    user.delete()
156
    role.delete()
157

  
158
    entries_role = []
159
    entries_user = []
160
    for ref in Reference.objects.all():
161
        if role_name in ref.representation:
162
            entries_role.append(ref)
163
        if email in ref.representation:
164
            entries_user.append(ref)
165
    assert len(entries_role)
166
    assert len(entries_user)
0
-