Projet

Général

Profil

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

Paul Marillonnet, 08 novembre 2018 11:11

Télécharger (16,1 ko)

Voir les différences:

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

 MANIFEST.in                                   |  2 +
 setup.py                                      |  1 +
 src/authentic2_journal/__init__.py            |  9 ++
 src/authentic2_journal/admin.py               | 13 +++
 src/authentic2_journal/api_serializers.py     | 19 ++++
 src/authentic2_journal/api_urls.py            |  5 ++
 src/authentic2_journal/api_views.py           | 23 +++++
 src/authentic2_journal/apps.py                |  8 ++
 src/authentic2_journal/logger.py              | 56 ++++++++++++
 src/authentic2_journal/migrations/__init__.py |  0
 src/authentic2_journal/models.py              | 90 +++++++++++++++++++
 src/authentic2_journal/tables.py              | 20 +++++
 .../authentic2_journal/user_actions.html      | 10 +++
 .../user_modifications.html                   | 10 +++
 src/authentic2_journal/urls.py                | 13 +++
 src/authentic2_journal/views.py               | 35 ++++++++
 tests/test_api.py                             | 15 ++++
 tests/test_views.py                           | 10 +++
 18 files changed, 339 insertions(+)
 create mode 100644 src/authentic2_journal/__init__.py
 create mode 100644 src/authentic2_journal/admin.py
 create mode 100644 src/authentic2_journal/api_serializers.py
 create mode 100644 src/authentic2_journal/api_urls.py
 create mode 100644 src/authentic2_journal/api_views.py
 create mode 100644 src/authentic2_journal/apps.py
 create mode 100644 src/authentic2_journal/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_actions.html
 create mode 100644 src/authentic2_journal/templates/authentic2_journal/user_modifications.html
 create mode 100644 src/authentic2_journal/urls.py
 create mode 100644 src/authentic2_journal/views.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_journal/__init__.py
1
# -*- coding: utf-8 -*-
2
class Plugin(object):
3
    def get_after_urls(self):
4
        from django.conf.urls import include, url
5

  
6
        return [url(r'^manager/journal/', include(__name__ + '.urls'))]
7

  
8
    def get_apps(self):
9
        return [__name__]
src/authentic2_journal/admin.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.contrib import admin
5

  
6
from authentic2_journal import models
7

  
8

  
9
class JournalAdmin(admin.ModelAdmin):
10
    list_display = ('label', 'kind')
11

  
12

  
13
admin.site.register(models.Journal, JournalAdmin)
src/authentic2_journal/api_serializers.py
1
# -*- coding: utf-8 -*-
2
from rest_framework import serializers
3

  
4
from authentic2_journal import models
5

  
6
class ReferenceSerializer(serializers.ModelSerializer):
7

  
8
    class Meta:
9
        model = models.Reference
10

  
11
    # TODO
12

  
13

  
14
class JournalSerializer(serializers.ModelSerializer):
15

  
16
    class Meta:
17
        model = models.Journal
18

  
19
    # TODO
src/authentic2_journal/api_urls.py
1
# -*- coding: utf-8 -*-
2

  
3
from authentic2_journal import api_views
4

  
5
urlpattern = api_views.router.urls
src/authentic2_journal/api_views.py
1
# -*- coding: utf-8 -*-
2

  
3
from authentic2_journal import serializers
4
from rest_framework import viewsets
5
from rest_framework import routers
6

  
7

  
8
class ReferenceViewSet(viewsets.ModelViewSet):
9
    serializer_class = serializers.ReferenceSerializer
10
    lookup_field = 'pk'
11

  
12
    # TODO
13

  
14

  
15
class JournalViewSet(viewsets.ModelViewSet):
16
    serializer_class = serializers.JournalSerializer
17
    lookup_field = 'pk'
18

  
19
    # TODO
20

  
21
router = routers.SimpleRouter()
22
router.register(r'references', ReferenceViewSet, base_name='a2-journal-references')
23
router.register(r'journals', JournalViewSet, base_name='a2-journal-journals')
src/authentic2_journal/apps.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.apps import AppConfig
5

  
6

  
7
class Authentic2JournalConfig(AppConfig):
8
    name = 'authentic2_journal'
src/authentic2_journal/logger.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models
5

  
6
from authentic2_journal.models import Line
7

  
8
class BaseLogger(models.Model):
9
    @classmethod
10
    def _log(cls, user, obj, content=None):
11
        Line.objects.create(
12
            kind=__name__,
13
            target_ct=obj.__class,
14
            target_id=obj.pk,
15
            content=content)
16

  
17

  
18
class AuthenticationLogger(BaseLogger):
19
    @classmethod
20
    def sso(cls, user, service, content=None):
21
        cls._log(user=user, obj=service, content=content)
22

  
23
    @classmethod
24
    def login(cls, user, provider, content=None):
25
        cls._log(user=user, obj=provider, content=content)
26

  
27
    @classmethod
28
    def logout(cls, user, provider, content=None):
29
        cls._log(user=user, obj=provider, content=content)
30

  
31
    @classmethod
32
    def register(cls, user, ou, content=None):
33
        cls._log(user=user, obj=ou, content=content)
34

  
35

  
36
class ModificationLogger(BaseLogger):
37
    @classmethod
38
    def create(cls, user, obj, content=None):
39
        cls._log(user=user, obj=obj, content=content)
40

  
41
    @classmethod
42
    def update(cls, user, obj, content=None):
43
        cls._log(user=user, obj=obj, content=content)
44

  
45
    @classmethod
46
    def modify_members(cls, user, obj, content=None):
47
        cls._log(user=user, obj=obj, content=content)
48

  
49
    @classmethod
50
    def delete(cls, user, obj, content=None):
51
        cls._log(user=user, obj=obj, content=content)
52

  
53
class GenericLogger(BaseLogger):
54
    @classmethod
55
    def generic_event(cls, user, obj, content=None):
56
        cls._log(user=user, obj=obj, content=content)
src/authentic2_journal/models.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models
5
try:
6
    from django.contrib.contenttypes.fields import GenericForeignKey
7
except ImportError:
8
    from django.contrib.contenttypes.generic import GenericForeignKey
9
from django.contrib.contenttypes.models import ContentType
10
from django.utils.translation import ugettext_lazy as _
11
from jsonfield import JSONField
12

  
13
from . import logger
14

  
15

  
16
KINDS = {
17
    'authentication': {
18
        'members': {
19
            'register': 1,
20
            'login': 2,
21
            'sso': 3,
22
            'logout': 4,
23
        },
24
        'logger': logger.AuthenticationLogger,
25
    },
26
    'modification': {
27
        'members': {
28
            'create': 5,
29
            'update': 6,
30
            'modify_members': 7,
31
            'delete': 8,
32
        },
33
        'logger': logger.ModificationLogger,
34
    },
35
    'default': {
36
        'members': {
37
            'generic_event': 9,
38
        }
39
    }
40
}
41

  
42

  
43
class Line(models.Model):
44
    timestamp = models.DateTimeField(auto_now=True,
45
            verbose_name=_('timestamp'))
46
    kind = models.PositiveIntegerField(default=0,
47
        choices={map(kind['members'].keys(), kind['members'].values()) for key, kind in KINDS.items()})
48
    # message = models.TextField()
49
    content = JSONField(blank=True,null=True,
50
            verbose_name=_('content'))
51

  
52
    def __init(self, *args, **kwargs):
53
        from . import Reference
54

  
55
        super(Line, self).__init(*args, **kwargs)
56
        Reference.objects.create(self)
57

  
58

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

  
68
    def __init__(self, line, *args, **kwargs):
69
        super(Reference, self).__init__(*args, **kwargs)
70
        # self.timestamp = line.timestamp # let auto_now ? XXX
71
        self.line = line
72

  
73

  
74
class Journal(models.Model):
75
    """
76
    # usage:
77
    authentication_journal = Journal.objects.get_or_create(kind="authentication")
78
    authentication_journal.log.sso(user, service, content=None)
79
    """
80
    from authentic2_journal import BaseLogger
81

  
82
    log = models.ForeignKey(BaseLogger)
83

  
84
    def __init__(self, *args, **kwargs):
85
        super(Journal, self).__init__(*args, **kwargs)
86
        kind = kwargs.get('kind')
87
        if kind and kind in KINDS:
88
            self.log = KINDS[kind].get('logger', GenericLogger)
89
        else:
90
            self.log = GenericLogger
src/authentic2_journal/tables.py
1
# -*- coding: utf-8 -*-
2
import django_tables2 as tables
3

  
4
from .models import Reference
5

  
6

  
7
class UserActionsTable(tables.Table):
8
    class Meta:
9
        attrs = {'class': 'main'}
10
        model = Reference
11
        exclude = ['id', 'actor']
12
        empty_text = _('None')
13

  
14

  
15
class UserModificationsTable(tables.Table):
16
    class Meta:
17
        attrs = {'class': 'main'}
18
        model = Reference
19
        exclude = ['id', 'subject']
20
        empty_text = _('None')
src/authentic2_journal/templates/authentic2_journal/user_actions.html
1
{% extends "authentic2/manager/base.html" %}
2
{% load i18n staticfiles django_tables2 %}
3

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

  
8
{% block content %}
9
  {% render_table table "authentic2/manager/table.html" %}
10
{% endblock %}
src/authentic2_journal/templates/authentic2_journal/user_modifications.html
1
{% extends "authentic2/manager/base.html" %}
2
{% load i18n staticfiles django_tables2 %}
3

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

  
8
{% block content %}
9
  {% render_table table "authentic2/manager/table.html" %}
10
{% endblock %}
src/authentic2_journal/urls.py
1
from django.conf.urls import url
2

  
3
from . import views
4

  
5

  
6
urlpatterns = [
7
    url('^users/(?P<pk>\d+)/actions/$',
8
        views.user_actions_journal,
9
        name='a2-journal-user-actions'),
10
    url('^users/(?P<pk>\d+)/modifications/$',
11
        views.user_modifications_journal,
12
        name='a2-journal-user-modifications'),
13
]
src/authentic2_journal/views.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.contrib.auth import get_user_model
5

  
6
from authentic2.views import SimpleSubTableView
7
from .tables import UserActionsTable, UserModificationsTable
8

  
9

  
10
class UserActionsJournal(SimpleSubTableView):
11
    model = get_user_model()
12
    table_class = UserActionsTable
13
    template_name = 'authentic2/manager/user_actions.html'
14
    permissions = ['custom_user.view_user']
15
    filter_table_by_perm = False
16

  
17
    def get_table_queryset(self):
18
        return [] # XXX
19

  
20

  
21
user_actions_journal = UserActionsJournal.as_view()
22

  
23

  
24
class UserModificationsJournal(SimpleSubTableView):
25
    model = get_user_model()
26
    table_class = UserModificationsTable
27
    template_name = 'authentic2_journal/user_modifications.html'
28
    permissions = ['custom_user.view_user']
29
    filter_table_by_perm = False
30

  
31
    def get_table_queryset(self):
32
        return [] # XXX
33

  
34

  
35
user_modifications_journal = UserModificationsJournal.as_view()
tests/test_api.py
934 934
    assert response.json['checks'][3]['result'] is True
935 935
    assert response.json['checks'][4]['label'] == 'must contain "ok"'
936 936
    assert response.json['checks'][4]['result'] is True
937

  
938

  
939
def test_user_actions_api(settings, db, simple_user, user_ou1, ou1):
940
    # TODO
941
    pass
942

  
943

  
944
def test_user_modifications_api(settings, db, simple_user, user_ou1, ou1):
945
    # TODO
946
    pass
947

  
948

  
949
def test_journal_api(settings, db, simple_user, user_ou1, ou1):
950
    # TODO
951
    pass
tests/test_views.py
46 46
    response = app.get(reverse('account_management'))
47 47
    assert response.status_code == 302
48 48
    assert response['Location'] == settings.A2_ACCOUNTS_URL
49

  
50

  
51
def test_user_actions_view(settings, db, simple_user, user_ou1, ou1):
52
    # TODO
53
    pass
54

  
55

  
56
def test_user_modifications_view(settings, db, simple_user, user_ou1, ou1):
57
    # TODO
58
    pass
49
-