Projet

Général

Profil

0001-base-grant-endpoint-permissions-using-roles-38365.patch

Valentin Deniaud, 11 décembre 2019 17:06

Télécharger (13,3 ko)

Voir les différences:

Subject: [PATCH] base: grant endpoint permissions using roles (#38365)

 passerelle/base/forms.py                      | 19 +++++++---
 .../migrations/0017_auto_20191211_1509.py     | 35 +++++++++++++++++++
 passerelle/base/models.py                     | 27 ++++++++++++--
 passerelle/base/templatetags/passerelle.py    |  5 ++-
 passerelle/base/urls.py                       |  7 +++-
 passerelle/base/views.py                      | 29 +++++++++++----
 .../includes/access-rights-table.html         | 11 ++++++
 passerelle/utils/__init__.py                  | 13 +++++++
 8 files changed, 130 insertions(+), 16 deletions(-)
 create mode 100644 passerelle/base/migrations/0017_auto_20191211_1509.py
passerelle/base/forms.py
1 1
from django import forms
2 2

  
3
from .models import ApiUser, AccessRight, AvailabilityParameters
3
from .models import ApiUser, AccessRight, RoleAccessRight, AvailabilityParameters
4 4

  
5 5

  
6 6
class ApiUserForm(forms.ModelForm):
......
9 9
        exclude = []
10 10

  
11 11

  
12
class AccessRightForm(forms.ModelForm):
12
class BaseAccessRightForm(forms.ModelForm):
13 13
    class Meta:
14
        model = AccessRight
15
        exclude = []
14
        abstract = True
16 15
        widgets = {
17 16
            'codename': forms.HiddenInput(),
18 17
            'resource_type': forms.HiddenInput(),
......
20 19
        }
21 20

  
22 21

  
22
class AccessRightForm(BaseAccessRightForm):
23
    class Meta(BaseAccessRightForm.Meta):
24
        model = AccessRight
25
        fields = ['apiuser', 'codename', 'resource_type', 'resource_pk']
26

  
27

  
28
class RoleAccessRightForm(BaseAccessRightForm):
29
    class Meta(BaseAccessRightForm.Meta):
30
        model = RoleAccessRight
31
        fields = ['role', 'codename', 'resource_type', 'resource_pk']
32

  
33

  
23 34
class AvailabilityParametersForm(forms.ModelForm):
24 35
    class Meta:
25 36
        model = AvailabilityParameters
passerelle/base/migrations/0017_auto_20191211_1509.py
1
# -*- coding: utf-8 -*-
2
# Generated by Django 1.11.18 on 2019-12-11 14:09
3
from __future__ import unicode_literals
4

  
5
from django.db import migrations, models
6
import django.db.models.deletion
7

  
8

  
9
class Migration(migrations.Migration):
10

  
11
    dependencies = [
12
        ('contenttypes', '0002_remove_content_type_name'),
13
        ('auth', '0008_alter_user_username_max_length'),
14
        ('base', '0016_auto_20191002_1443'),
15
    ]
16

  
17
    operations = [
18
        migrations.CreateModel(
19
            name='RoleAccessRight',
20
            fields=[
21
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22
                ('codename', models.CharField(max_length=100, verbose_name=b'codename')),
23
                ('resource_pk', models.PositiveIntegerField()),
24
                ('resource_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
25
                ('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='auth.Group', verbose_name='Role')),
26
            ],
27
            options={
28
                'permissions': (('view_accessright', 'Can view access right'),),
29
            },
30
        ),
31
        migrations.AlterUniqueTogether(
32
            name='roleaccessright',
33
            unique_together=set([('codename', 'resource_type', 'resource_pk', 'role')]),
34
        ),
35
    ]
passerelle/base/models.py
12 12

  
13 13
from django.apps import apps
14 14
from django.conf import settings
15
from django.contrib.auth.models import Group
15 16
from django.core.exceptions import ValidationError, ObjectDoesNotExist, PermissionDenied
16 17
from django.core.urlresolvers import reverse
17 18
from django.db import connection, models, transaction
......
565 566
            exc_info=exc_info)
566 567

  
567 568

  
568
class AccessRight(models.Model):
569
class BaseAccessRight(models.Model):
569 570
    codename = models.CharField(max_length=100, verbose_name='codename')
570 571
    resource_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
571 572
    resource_pk = models.PositiveIntegerField()
572 573
    resource = fields.GenericForeignKey('resource_type', 'resource_pk')
573
    apiuser = models.ForeignKey(ApiUser, verbose_name=_('API User'), on_delete=models.CASCADE)
574 574

  
575 575
    class Meta:
576
        abstract = True
576 577
        permissions = (
577 578
            ('view_accessright', 'Can view access right'),
578 579
        )
580

  
581

  
582
class AccessRight(BaseAccessRight):
583
    apiuser = models.ForeignKey(ApiUser, verbose_name=_('API User'), on_delete=models.CASCADE)
584

  
585
    class Meta(BaseAccessRight.Meta):
579 586
        unique_together = (
580 587
            ('codename', 'resource_type', 'resource_pk', 'apiuser'),
581 588
        )
582 589

  
583 590
    def __unicode__(self):
584
        return '%s (on %s <%s>) (for %s)' % (self.codename, self.resource_type, self.resource_pk, self.apiuser)
591
        return '%s (on %s <%s>) (for %s)' % (self.codename, self.resource_type,
592
                                             self.resource_pk, self.apiuser)
593

  
594

  
595
class RoleAccessRight(BaseAccessRight):
596
    role = models.ForeignKey(Group, verbose_name=_('Role'), on_delete=models.CASCADE)
597

  
598
    class Meta(BaseAccessRight.Meta):
599
        unique_together = (
600
            ('codename', 'resource_type', 'resource_pk', 'role'),
601
        )
602

  
603
    def __unicode__(self):
604
        return '%s (on %s <%s>) (for role %s)' % (self.codename, self.resource_type,
605
                                                  self.resource_pk, self.role.name)
585 606

  
586 607

  
587 608
class LoggingParameters(models.Model):
passerelle/base/templatetags/passerelle.py
29 29
from django.template.defaultfilters import stringfilter
30 30

  
31 31
from passerelle.utils import get_trusted_services
32
from ..models import AccessRight, ResourceLog
32
from ..models import AccessRight, RoleAccessRight, ResourceLog
33 33

  
34 34
register = template.Library()
35 35

  
......
38 38
def access_rights_table(context, resource, permission):
39 39
    resource_type = ContentType.objects.get_for_model(resource)
40 40
    rights = AccessRight.objects.filter(resource_type=resource_type, resource_pk=resource.id, codename=permission)
41
    role_rights = RoleAccessRight.objects.filter(resource_type=resource_type, resource_pk=resource.id,
42
                                                 codename=permission)
41 43
    context['permission'] = permission
42 44
    context['access_rights_list'] = rights
45
    context['role_access_rights_list'] = role_rights
43 46
    context['resource_type'] = resource_type.id
44 47
    context['resource_pk'] = resource.id
45 48
    context['trusted_services'] = get_trusted_services()
passerelle/base/urls.py
2 2

  
3 3
from .views import ApiUserCreateView, ApiUserUpdateView, ApiUserDeleteView, \
4 4
        ApiUserListView, AccessRightDeleteView, AccessRightCreateView, \
5
        LoggingParametersUpdateView, ManageAvailabilityView
5
        LoggingParametersUpdateView, ManageAvailabilityView, \
6
        RoleAccessRightDeleteView, RoleAccessRightCreateView
6 7

  
7 8
access_urlpatterns = [
8 9
    url(r'^$', ApiUserListView.as_view(), name='apiuser-list'),
......
14 15
        name='access-right-remove'),
15 16
    url(r'^accessright/add/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/(?P<codename>[\w,-]+)/',
16 17
        AccessRightCreateView.as_view(), name='access-right-add'),
18
    url(r'^(?P<pk>[\w,-]+)/roleremove$', RoleAccessRightDeleteView.as_view(),
19
        name='role-access-right-remove'),
20
    url(r'^roleaccessright/add/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/(?P<codename>[\w,-]+)/',
21
        RoleAccessRightCreateView.as_view(), name='role-access-right-add'),
17 22
    url(r'logging/parameters/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/$',
18 23
        LoggingParametersUpdateView.as_view(), name='logging-parameters'),
19 24
    url(r'manage/availability/(?P<resource_type>[\w,-]+)/(?P<resource_pk>[\w,-]+)/$',
passerelle/base/views.py
30 30
from django.utils.timezone import make_aware
31 31
from django.utils.translation import ugettext_lazy as _
32 32

  
33
from .models import ApiUser, AccessRight, LoggingParameters, ResourceStatus, Job
34
from .forms import ApiUserForm, AccessRightForm, AvailabilityParametersForm
33
from .models import ApiUser, AccessRight, RoleAccessRight, LoggingParameters, ResourceStatus, Job
34
from .forms import ApiUserForm, AccessRightForm, RoleAccessRightForm, AvailabilityParametersForm
35 35
from ..views import GenericConnectorMixin
36 36
from ..utils import get_trusted_services
37 37

  
......
97 97
        return context
98 98

  
99 99

  
100
class AccessRightDeleteView(DeleteView):
101
    model = AccessRight
100
class BaseAccessRightDeleteView(DeleteView):
102 101
    template_name = 'passerelle/manage/accessright_confirm_delete.html'
103 102

  
104 103
    def get_object(self):
105
        object = super(AccessRightDeleteView, self).get_object()
104
        object = super(BaseAccessRightDeleteView, self).get_object()
106 105
        self.resource = object.resource
107 106
        return object
108 107

  
......
110 109
        return self.resource.get_absolute_url()
111 110

  
112 111

  
113
class AccessRightCreateView(CreateView):
112
class AccessRightDeleteView(BaseAccessRightDeleteView):
114 113
    model = AccessRight
115
    form_class = AccessRightForm
114

  
115

  
116
class RoleAccessRightDeleteView(BaseAccessRightDeleteView):
117
    model = RoleAccessRight
118

  
119

  
120
class BaseAccessRightCreateView(CreateView):
116 121
    template_name = 'passerelle/manage/accessright_form.html'
117 122

  
118 123
    def get_initial(self):
......
126 131
        return self.object.resource.get_absolute_url()
127 132

  
128 133

  
134
class AccessRightCreateView(BaseAccessRightCreateView):
135
    model = AccessRight
136
    form_class = AccessRightForm
137

  
138

  
139
class RoleAccessRightCreateView(BaseAccessRightCreateView):
140
    model = RoleAccessRight
141
    form_class = RoleAccessRightForm
142

  
143

  
129 144
class LoggingParametersUpdateView(FormView):
130 145
    template_name = 'passerelle/manage/logging_parameters_form.html'
131 146

  
passerelle/templates/passerelle/includes/access-rights-table.html
23 23
  {% endif %}
24 24
</tr>
25 25
{% endfor %}
26
{% for object in role_access_rights_list %}
27
<tr>
28
  <td>{{ object.role.name }}</td>
29
  <td> - </td>
30
  <td> - </td>
31
  {% if perms.base.delete_accessright %}
32
  <td><a rel="popup" class="icon-remove-sign" href="{% url 'role-access-right-remove' pk=object.id %}"></a></td>
33
  {% endif %}
34
</tr>
35
{% endfor %}
26 36
{% for trusted_service in trusted_services %}
27 37
<tr>
28 38
  <td>{{ trusted_service.title }} ({{ trusted_service.verif_orig }})</td>
......
37 47
{% if perms.base.add_accessright %}
38 48
<p>
39 49
<a rel="popup" class="icon-plus button" href="{% url 'access-right-add' resource_type=resource_type resource_pk=resource_pk codename=permission %}">{% trans 'Add' %}</a>
50
<a rel="popup" class="icon-plus button" href="{% url 'role-access-right-add' resource_type=resource_type resource_pk=resource_pk codename=permission %}">{% trans 'Add role' %}</a>
40 51
</p>
41 52
{% endif %}
passerelle/utils/__init__.py
121 121
    return False
122 122

  
123 123

  
124
def has_perm(user, obj, resource_type, perm):
125
    from passerelle.base.models import RoleAccessRight
126

  
127
    if user.is_anonymous:
128
        return False
129
    rights = RoleAccessRight.objects.filter(resource_type=resource_type,
130
                                            resource_pk=obj.id, codename=perm)
131
    role_ids = [x.role.id for x in rights]
132
    return user.groups.filter(id__in=role_ids).exists()
133

  
134

  
124 135
def is_authorized(request, obj, perm):
125 136
    from passerelle.base.models import AccessRight
126 137

  
127 138
    if is_trusted(request):
128 139
        return True
129 140
    resource_type = ContentType.objects.get_for_model(obj)
141
    if not request.user.is_anonymous and has_perm(request.user, obj, resource_type, perm):
142
        return True
130 143
    rights = AccessRight.objects.filter(resource_type=resource_type, resource_pk=obj.id, codename=perm)
131 144
    users = [x.apiuser for x in rights]
132 145
    return set(users).intersection(get_request_users(request))
133
-