Projet

Général

Profil

0001-add-journal-application-20695.patch

Paul Marillonnet, 20 décembre 2018 12:32

Télécharger (29,6 ko)

Voir les différences:

Subject: [PATCH 1/5] add journal application (#20695)

 MANIFEST.in                                   |   1 +
 setup.py                                      |   1 +
 src/authentic2/app_settings.py                |   1 +
 src/authentic2/settings.py                    |   1 +
 src/authentic2_journal/__init__.py            |  21 +++
 src/authentic2_journal/admin.py               |   0
 src/authentic2_journal/app_settings.py        |  38 +++++
 src/authentic2_journal/apps.py                |  25 +++
 src/authentic2_journal/kinds.py               | 104 ++++++++++++
 src/authentic2_journal/logger.py              |  21 +++
 src/authentic2_journal/models.py              |  71 ++++++++
 src/authentic2_journal/tables.py              |  35 ++++
 .../templates/authentic2_journal/create.html  |   2 +
 .../templates/authentic2_journal/delete.html  |   2 +
 .../templates/authentic2_journal/login.html   |   2 +
 .../templates/authentic2_journal/logout.html  |   2 +
 .../authentic2_journal/modify_members.html    |   2 +
 .../authentic2_journal/register.html          |   2 +
 .../templates/authentic2_journal/sso.html     |   2 +
 .../templates/authentic2_journal/update.html  |   2 +
 .../templatetags/__init__.py                  |   0
 .../templatetags/authentic2_journal.py        |  27 +++
 src/authentic2_journal/views.py               |  34 ++++
 tests/conftest.py                             |   6 +
 tests/test_journal.py                         | 158 ++++++++++++++++++
 25 files changed, 560 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/kinds.py
 create mode 100644 src/authentic2_journal/logger.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/create.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/delete.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/login.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/logout.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/modify_members.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/register.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/sso.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/update.html
 create mode 100644 src/authentic2_journal/templatetags/__init__.py
 create mode 100644 src/authentic2_journal/templatetags/authentic2_journal.py
 create mode 100644 src/authentic2_journal/views.py
 create mode 100644 tests/test_journal.py
MANIFEST.in
41 41
recursive-include src/authentic2_auth_saml/locale *.po *.mo
42 42
recursive-include src/authentic2_auth_oidc/locale *.po *.mo
43 43
recursive-include src/authentic2_idp_oidc/locale *.po *.mo
44
recursive-include src/authentic2_journal/locale *.po *.mo
44 45

  
45 46
recursive-include src/authentic2 README  xrds.xml *.txt yadis.xrdf
46 47
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/app_settings.py
207 207
    A2_ACCOUNTS_URL=Setting(default=None, definition='IdP has no account page, redirect to this one.'),
208 208
    A2_CACHE_ENABLED=Setting(default=True, definition='Disable all cache decorators for testing purpose.'),
209 209
    A2_ACCEPT_EMAIL_AUTHENTICATION=Setting(default=True, definition='Enable authentication by email'),
210
    A2_JOURNAL_EVENT_LOGGER=Setting(default=None, definition='Event logger class for the journal app.'),
210 211

  
211 212
)
212 213

  
src/authentic2/settings.py
124 124
    'authentic2.disco_service',
125 125
    'authentic2.manager',
126 126
    'authentic2_provisionning_ldap',
127
    'authentic2_journal',
127 128
    'authentic2',
128 129
    'django_rbac',
129 130
    '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_apps(self):
19
        return [__name__]
20

  
21
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'
src/authentic2_journal/kinds.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.contrib.contenttypes.models import ContentType
18
from django.template.loader import render_to_string, select_template
19

  
20

  
21
_kind_registry = {}
22

  
23

  
24
class KindMetaclass(type):
25
    def __init__(self, name, bases, dct):
26
        super(KindMetaclass, self).__init__(name, bases, dct)
27
        if name != "BaseKind":
28
            assert set(dct.keys()) >= set(['name', 'template_names'])
29
            assert dct['name'] not in _kind_registry
30
            _kind_registry[dct['name']] = self
31

  
32

  
33
class BaseKind(object):
34
    __metaclass__ = KindMetaclass
35
    long_template_names = []
36
    template_names = []
37

  
38
    def format(self, line):
39
        return select_template(self.template_names).render(context=line.extra_json)
40

  
41
    def long_format(self, line):
42
        if not self.long_template_names:
43
            return ''
44
        return select_template(self.long_template_names).render(context=line.extra_json)
45

  
46
    def log_real(self, **kwargs):
47
        from authentic2_journal.models import Line, get_kind
48
        kind = get_kind(self.name)
49
        if isinstance(kind, tuple):
50
            kind = kind[0]
51
        return Line.objects.create(kind=kind, extra_json=kwargs)
52

  
53
    def create_reference(self, line, obj, **kwargs):
54
        from authentic2_journal.models import Reference
55
        reference = Reference.objects.create(
56
                line=line,
57
                target_ct=ContentType.objects.get_for_model(obj),
58
                target_id=obj.pk)
59

  
60
    def log(self, user, obj):
61
        line = self.log_real(user_pk=user.pk, obj_pk=obj.pk, user='%s' % user, obj='%s' % obj)
62
        if line:
63
            self.create_reference(line, user)
64
            self.create_reference(line, obj)
65

  
66

  
67
class SSOKind(BaseKind):
68
    name = 'sso'
69
    template_names = ['authentic2_journal/sso.html']
70

  
71

  
72
class LoginKind(BaseKind):
73
    name = 'login'
74
    template_names = ['authentic2_journal/login.html']
75

  
76

  
77
class LogoutKind(BaseKind):
78
    name = 'logout'
79
    template_names = ['authentic2_journal/logout.html']
80

  
81

  
82
class RegisterKind(BaseKind):
83
    name = 'register'
84
    template_names = ['authentic2_journal/register.html']
85

  
86

  
87
class CreateKind(BaseKind):
88
    name = 'create'
89
    template_names = ['authentic2_journal/create.html']
90

  
91

  
92
class UpdateKind(BaseKind):
93
    name = 'update'
94
    template_names = ['authentic2_journal/update.html']
95

  
96

  
97
class ModifyMembersKind(BaseKind):
98
    name = 'modify_members'
99
    template_names = ['authentic2_journal/modify_members.html']
100

  
101

  
102
class DeleteKind(BaseKind):
103
    name = 'delete'
104
    template_names = ['authentic2_journal/delete.html']
src/authentic2_journal/logger.py
1
from authentic2_journal import kinds, models
2

  
3

  
4
class Logger(object):
5
    def __init__(self, kind):
6
        self.kind = kinds._kind_registry[kind]
7

  
8
    def __call__(self, *args, **kwargs):
9
        kind_model = models.get_kind(self.kind.name)
10
        if isinstance(kind_model, tuple):
11
            kind_model = kind_model[0]
12
        kind = kind_model.concrete()
13
        return self.kind.log(kind, *args, **kwargs)
14

  
15

  
16
class LoggerHub(object):
17
    def __getattr__(self, kind_name):
18
        return Logger(kind_name)
19

  
20

  
21
logger = LoggerHub()
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 authentic2.compat import JSONField
19
from authentic2.decorators import GlobalCache
20
from authentic2_journal import kinds
21
from django.db import models
22
try:
23
    from django.contrib.contenttypes.fields import GenericForeignKey
24
except ImportError:
25
    from django.contrib.contenttypes.generic import GenericForeignKey
26
from django.contrib.contenttypes.models import ContentType
27
from django.utils.translation import ugettext_lazy as _
28

  
29

  
30
class Kind(models.Model):
31
    name = models.CharField(max_length=128, null=True, verbose_name='name')
32

  
33
    @property
34
    def concrete(self):
35
        return kinds._kind_registry[self.name]
36

  
37

  
38
# get_kind() should be cached, like ContentType are, because they never vary
39
@GlobalCache
40
def get_kind(name):
41
    return Kind.objects.get_or_create(name=name)
42

  
43

  
44
class Line(models.Model):
45
    timestamp = models.DateTimeField(auto_now=True, verbose_name=_('timestamp'))
46
    kind = models.ForeignKey(Kind)
47
    extra_json = JSONField(blank=True, null=True, verbose_name=_('extra JSON'))
48

  
49
    def format(self):
50
        return self.kind.concrete().format(self)
51

  
52
    def long_format(self):
53
        return self.kind.concrete().long_format(self)
54

  
55

  
56
class Reference(models.Model):
57
    timestamp = models.DateTimeField(verbose_name=_('timestamp'))
58
    line = models.ForeignKey(Line)
59
    extra_json = JSONField(blank=False, null=False, verbose_name=_('additional description'))
60
    target_ct = models.ForeignKey(ContentType)
61
    target_id = models.IntegerField(default=0)
62
    target = GenericForeignKey('target_ct', 'target_id')
63

  
64
    def __init__(self, *args, **kwargs):
65
        line = kwargs.get('line')
66
        if line:
67
            kwargs.update({
68
                'timestamp': line.timestamp,
69
                'extra_json': line.extra_json
70
            })
71
        return super(Reference, self).__init__(*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

  
19
from authentic2_journal.models import Line
20
from django.utils.translation import ugettext_lazy as _
21

  
22

  
23
class UserEventsTable(tables.Table):
24
    def render_kind(self, value):
25
        return value.name
26

  
27
    def render_extra_json(self, value, record):
28
        return record.format() or value
29

  
30
    class Meta:
31
        attrs = {'class': 'main'}
32
        model = Line
33
        order_by = ('-timestamp', 'id')
34
        per_page = 10
35
        empty_text = _('No journal entry yet.')
src/authentic2_journal/templates/authentic2_journal/create.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed create on {{obj}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/delete.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed delete on {{obj}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/login.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed login on {{provider}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/logout.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed logout on {{provider}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/modify_members.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed modify members on {{role}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/register.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} registered on {{ou}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/sso.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed sso on {{service}}{% endblocktrans %}
src/authentic2_journal/templates/authentic2_journal/update.html
1
{% load i18n %}
2
{% blocktrans %}{{user}} performed update on {{obj}}{% endblocktrans %}
src/authentic2_journal/templatetags/authentic2_journal.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 import template
18

  
19
register = template.Library()
20

  
21
@register.filter
22
def to_representation(line):
23
    return line.format()
24

  
25
@register.filter
26
def to_long_representation(line):
27
    return line.long_format()
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.models import Reference
19
from django.contrib.auth import get_user_model
20
from django.contrib.contenttypes.models import ContentType
21

  
22

  
23
class ManagerBaseJournal(SimpleSubTableView):
24
    model = None
25
    table_class = None # TODO generic table class?
26
    template_name = '' # TODO generic template name?
27
    permissions = [] # TODO generic custom_user.view_object permission?
28
    filter_table_by_perm = False
29
    table_pagination = True
30

  
31
    def get_table_queryset(self):
32
        return [ref.line for ref in Reference.objects.filter(
33
                target_id=self.object.pk,
34
                target_ct=ContentType.objects.get_for_model(self.object))]
tests/conftest.py
344 344
@pytest.fixture
345 345
def media(settings, tmpdir):
346 346
    settings.MEDIA_ROOT = str(tmpdir.mkdir('media'))
347

  
348

  
349
@pytest.fixture
350
def event_logger(db):
351
    from authentic2_journal.logger import LoggerHub
352
    return LoggerHub()
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.models import Line, Reference
9
from django.contrib.auth import get_user_model
10
from django.contrib.contenttypes.models import ContentType
11
from django.db import transaction
12
from django_rbac.utils import get_ou_model, get_role_model
13
from utils import login
14

  
15

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

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

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

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

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

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

  
59

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

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

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

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

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

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

  
97

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

  
105

  
106
def test_line_retrieval_after_object_deletion(db, event_logger):
107
    email = u'toto@nowhere.null'
108
    role_name = role_slug = u'r1'
109

  
110
    assert not len(Line.objects.all())
111

  
112
    role = Role.objects.create(name=role_name, slug=role_slug)
113
    user = get_user_model().objects.create(email=email)
114
    event_logger.modify_members(user, role)
115
    user.delete()
116
    role.delete()
117

  
118
    entries_role = []
119
    entries_user = []
120
    for line in Line.objects.all():
121
        if role_name in line.extra_json['obj']:
122
            entries_role.append(line)
123
        if line.extra_json['user'].startswith(email):
124
            entries_user.append(line)
125
    assert len(entries_role)
126
    assert len(entries_user)
127

  
128

  
129
def test_reference_retrieval_after_objects_deletion(db, event_logger):
130
    email = 'toto@nowhere.null'
131
    role_name = role_slug = 'r1'
132

  
133
    assert not(Reference.objects.all())
134

  
135
    role = Role.objects.create(name=role_name, slug=role_slug)
136
    user = get_user_model().objects.create(email=email)
137
    event_logger.modify_members(user, role)
138
    user.delete()
139
    role.delete()
140

  
141
    entries_role = []
142
    entries_user = []
143
    for ref in Reference.objects.all():
144
        if role_name in ref.extra_json['obj']:
145
            entries_role.append(ref)
146
        if ref.extra_json.get('user', '').startswith(email):
147
            entries_user.append(ref)
148
    assert len(entries_role)
149
    assert len(entries_user)
150

  
151

  
152
def test_log_event_escalate_error(db, event_logger, app, admin, simple_user, ou1):
153
    try:
154
        event_logger.spoke_to(admin, simple_user)
155
    except:
156
        pass
157
    else:
158
        assert 0
0
-