0001-a2_rbac-move-role-attributes-to-real-model-fields-69.patch
src/authentic2/a2_rbac/admin.py | ||
---|---|---|
38 | 38 |
return super().get_queryset(request).filter(direct=True) |
39 | 39 | |
40 | 40 | |
41 |
class RoleAttributeInline(admin.TabularInline): |
|
42 |
model = models.RoleAttribute |
|
43 | ||
44 | ||
45 | 41 |
class RoleAdmin(admin.ModelAdmin): |
46 | 42 |
inlines = [RoleChildInline, RoleParentInline] |
47 | 43 |
fields = ( |
... | ... | |
62 | 58 |
list_display = ('__str__', 'slug', 'ou', 'service', 'admin_scope') |
63 | 59 |
list_select_related = True |
64 | 60 |
list_filter = ['ou', 'service'] |
65 |
inlines = [RoleAttributeInline] |
|
66 | 61 | |
67 | 62 | |
68 | 63 |
class OrganizationalUnitAdmin(admin.ModelAdmin): |
src/authentic2/a2_rbac/migrations/0034_new_role_fields.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-10-25 09:35 |
|
2 | ||
3 |
import django |
|
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('a2_rbac', '0033_remove_old_operation_fk'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='role', |
|
16 |
name='details', |
|
17 |
field=models.TextField(blank=True, verbose_name='Role details (frontoffice)'), |
|
18 |
), |
|
19 |
migrations.AddField( |
|
20 |
model_name='role', |
|
21 |
name='emails', |
|
22 |
field=django.contrib.postgres.fields.ArrayField( |
|
23 |
base_field=models.EmailField(max_length=254), default=list, size=None |
|
24 |
), |
|
25 |
), |
|
26 |
migrations.AddField( |
|
27 |
model_name='role', |
|
28 |
name='emails_to_members', |
|
29 |
field=models.BooleanField(default=True, verbose_name='Emails to members'), |
|
30 |
), |
|
31 |
migrations.AddField( |
|
32 |
model_name='role', |
|
33 |
name='is_superuser', |
|
34 |
field=models.BooleanField(default=False), |
|
35 |
), |
|
36 |
] |
src/authentic2/a2_rbac/migrations/0035_populate_role_fields.py | ||
---|---|---|
1 |
import json |
|
2 | ||
3 |
from django.db import migrations |
|
4 | ||
5 | ||
6 |
def populate_role_fields(apps, schema_editor): |
|
7 |
Role = apps.get_model('a2_rbac', 'Role') |
|
8 | ||
9 |
fields = {'details', 'emails', 'emails_to_members', 'is_superuser'} |
|
10 |
roles = list(Role.objects.all().prefetch_related('attributes')) |
|
11 |
for role in roles: |
|
12 |
for attribute in role.attributes.all(): |
|
13 |
if attribute.name not in fields: |
|
14 |
continue |
|
15 |
try: |
|
16 |
value = json.loads(attribute.value) |
|
17 |
except json.JSONDecodeError: |
|
18 |
continue |
|
19 | ||
20 |
if attribute.name == 'emails': |
|
21 |
if not isinstance(value, list): |
|
22 |
continue |
|
23 |
value = [x[:254] for x in value] |
|
24 | ||
25 |
if attribute.name == 'details' and not isinstance(value, str): |
|
26 |
continue |
|
27 | ||
28 |
if attribute.name in ('emails_to_members', 'is_superuser') and not isinstance(value, bool): |
|
29 |
continue |
|
30 | ||
31 |
setattr(role, attribute.name, value) |
|
32 | ||
33 |
Role.objects.bulk_update(roles, fields, batch_size=1000) |
|
34 | ||
35 | ||
36 |
def reverse_populate_role_fields(apps, schema_editor): |
|
37 |
Role = apps.get_model('a2_rbac', 'Role') |
|
38 |
RoleAttribute = apps.get_model('a2_rbac', 'RoleAttribute') |
|
39 | ||
40 |
fields = ['details', 'emails', 'emails_to_members'] |
|
41 |
attributes = [] |
|
42 |
for role in Role.objects.all(): |
|
43 |
for field in fields: |
|
44 |
attributes.append( |
|
45 |
RoleAttribute( |
|
46 |
role_id=role.pk, name=field, kind='json', value=json.dumps(getattr(role, field)) |
|
47 |
) |
|
48 |
) |
|
49 | ||
50 |
RoleAttribute.objects.bulk_create(attributes, batch_size=1000) |
|
51 | ||
52 | ||
53 |
class Migration(migrations.Migration): |
|
54 | ||
55 |
dependencies = [ |
|
56 |
('a2_rbac', '0034_new_role_fields'), |
|
57 |
] |
|
58 | ||
59 |
operations = [ |
|
60 |
migrations.RunPython(populate_role_fields, reverse_code=reverse_populate_role_fields), |
|
61 |
] |
src/authentic2/a2_rbac/migrations/0036_delete_roleattribute.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-10-25 10:33 |
|
2 | ||
3 |
from django.db import migrations |
|
4 | ||
5 | ||
6 |
class Migration(migrations.Migration): |
|
7 | ||
8 |
dependencies = [ |
|
9 |
('a2_rbac', '0035_populate_role_fields'), |
|
10 |
] |
|
11 | ||
12 |
operations = [ |
|
13 |
migrations.DeleteModel( |
|
14 |
name='RoleAttribute', |
|
15 |
), |
|
16 |
] |
src/authentic2/a2_rbac/models.py | ||
---|---|---|
23 | 23 |
from django.contrib.auth import get_user_model |
24 | 24 |
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation |
25 | 25 |
from django.contrib.contenttypes.models import ContentType |
26 |
from django.contrib.postgres.fields import ArrayField |
|
26 | 27 |
from django.core.exceptions import ValidationError |
27 | 28 |
from django.core.validators import MinValueValidator |
28 | 29 |
from django.db import models |
... | ... | |
385 | 386 |
to=rbac_utils.get_permission_model_name(), related_name='roles', blank=True |
386 | 387 |
) |
387 | 388 |
name = models.TextField(verbose_name=_('name')) |
389 |
details = models.TextField(_('Role details (frontoffice)'), blank=True) |
|
390 |
emails = ArrayField(models.EmailField(), default=list) |
|
391 |
emails_to_members = models.BooleanField(_('Emails to members'), default=True) |
|
392 |
is_superuser = models.BooleanField(default=False) |
|
388 | 393 |
admin_scope_ct = models.ForeignKey( |
389 | 394 |
to='contenttypes.ContentType', |
390 | 395 |
null=True, |
... | ... | |
737 | 742 |
return '{} {}> {}'.format(self.parent.name, '-' if self.direct else '~', self.child.name) |
738 | 743 | |
739 | 744 | |
740 |
class RoleAttribute(models.Model): |
|
741 |
KINDS = (('string', _('string')),) |
|
742 |
role = models.ForeignKey( |
|
743 |
to=Role, verbose_name=_('role'), related_name='attributes', on_delete=models.CASCADE |
|
744 |
) |
|
745 |
name = models.CharField(max_length=64, verbose_name=_('name')) |
|
746 |
kind = models.CharField(max_length=32, choices=KINDS, verbose_name=_('kind')) |
|
747 |
value = models.TextField(verbose_name=_('value')) |
|
748 | ||
749 |
class Meta: |
|
750 |
verbose_name = 'role attribute' |
|
751 |
verbose_name_plural = _('role attributes') |
|
752 |
unique_together = (('role', 'name', 'kind', 'value'),) |
|
753 | ||
754 |
def to_json(self): |
|
755 |
return {'name': self.name, 'kind': self.kind, 'value': self.value} |
|
756 | ||
757 | ||
758 | 745 |
class Operation(models.Model): |
759 | 746 |
slug = models.CharField(max_length=32, verbose_name=_('slug'), unique=True) |
760 | 747 |
src/authentic2/app_settings.py | ||
---|---|---|
101 | 101 |
'authentic2.attributes_ng.sources.function', |
102 | 102 |
'authentic2.attributes_ng.sources.django_user', |
103 | 103 |
'authentic2.attributes_ng.sources.ldap', |
104 |
'authentic2.attributes_ng.sources.service_roles', |
|
105 | 104 |
), |
106 | 105 |
definition='List of attribute backend classes or modules', |
107 | 106 |
), |
src/authentic2/attributes_ng/sources/service_roles.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 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.utils.translation import gettext_lazy as _ |
|
18 | ||
19 |
from authentic2.a2_rbac.models import Role |
|
20 | ||
21 |
from ...decorators import to_list |
|
22 |
from ...models import Service |
|
23 | ||
24 | ||
25 |
@to_list |
|
26 |
def get_instances(ctx): |
|
27 |
return [None] |
|
28 | ||
29 | ||
30 |
@to_list |
|
31 |
def get_attribute_names(instance, ctx): |
|
32 |
service = ctx.get('service') |
|
33 |
if not isinstance(service, Service): |
|
34 |
return |
|
35 |
names = [] |
|
36 |
for service_role in Role.objects.filter(service=service).prefetch_related('attributes'): |
|
37 |
for service_role_attribute in service_role.attributes.all(): |
|
38 |
if service_role_attribute.name in names: |
|
39 |
continue |
|
40 |
names.append(service_role_attribute.name) |
|
41 |
names.sort() |
|
42 |
for name in names: |
|
43 |
yield (name, '%s (%s)' % (name, _('role attribute'))) |
|
44 | ||
45 | ||
46 |
def get_dependencies(instance, ctx): |
|
47 |
return ( |
|
48 |
'user', |
|
49 |
'service', |
|
50 |
) |
|
51 | ||
52 | ||
53 |
def get_attributes(instance, ctx): |
|
54 |
user = ctx.get('user') |
|
55 |
service = ctx.get('service') |
|
56 |
if not user or not service: |
|
57 |
return ctx |
|
58 |
ctx = ctx.copy() |
|
59 |
roles = Role.objects.for_user(user).filter(service=service).prefetch_related('attributes') |
|
60 |
for service_role in roles: |
|
61 |
for service_role_attribute in service_role.attributes.all(): |
|
62 |
name = service_role_attribute.name |
|
63 |
value = service_role_attribute.value |
|
64 |
values = ctx.get(name, []) |
|
65 |
if not isinstance(values, (list, tuple, set)): |
|
66 |
values = [values] |
|
67 |
values = set(values) |
|
68 |
if value not in values: |
|
69 |
values.add(value) |
|
70 |
ctx[name] = values |
|
71 |
return ctx |
src/authentic2/data_transfer.py | ||
---|---|---|
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 | |
18 |
import json |
|
18 | 19 |
import uuid |
19 | 20 |
from functools import wraps |
20 | 21 | |
... | ... | |
24 | 25 |
from django.utils.text import format_lazy |
25 | 26 |
from django.utils.translation import gettext_lazy as _ |
26 | 27 | |
27 |
from authentic2.a2_rbac.models import ( |
|
28 |
Operation, |
|
29 |
OrganizationalUnit, |
|
30 |
Permission, |
|
31 |
Role, |
|
32 |
RoleAttribute, |
|
33 |
RoleParenting, |
|
34 |
) |
|
28 |
from authentic2.a2_rbac.models import Operation, OrganizationalUnit, Permission, Role, RoleParenting |
|
35 | 29 |
from authentic2.a2_rbac.utils import get_default_ou |
36 | 30 |
from authentic2.decorators import errorcollector |
37 | 31 |
from authentic2.utils.lazy import lazy_join |
... | ... | |
110 | 104 | |
111 | 105 |
def export_roles(context): |
112 | 106 |
"""Serialize roles in role_queryset""" |
113 |
return [role.export_json(attributes=True, parents=True, permissions=True) for role in context.role_qs]
|
|
107 |
return [role.export_json(parents=True, permissions=True) for role in context.role_qs] |
|
114 | 108 | |
115 | 109 | |
116 | 110 |
def search_ou(ou_d): |
... | ... | |
144 | 138 |
be deleted |
145 | 139 | |
146 | 140 | |
147 |
role_attributes_update: for each role in the import data, |
|
141 |
role_attributes_update: legacy, for each role in the import data,
|
|
148 | 142 |
attributes will deleted and re-created |
149 | 143 | |
150 | 144 | |
... | ... | |
277 | 271 | |
278 | 272 |
@wraps_validationerror |
279 | 273 |
def attributes(self): |
280 |
"""Update attributes (delete everything then create)"""
|
|
274 |
"""Compatibility with old import files, set Role fields using attributes data"""
|
|
281 | 275 |
created, deleted = [], [] |
282 |
for attr in self._obj.attributes.all(): |
|
283 |
attr.delete() |
|
284 |
deleted.append(attr) |
|
285 | 276 |
# Create attributes |
286 | 277 |
if self._attributes: |
287 | 278 |
for attr_dict in self._attributes: |
288 |
attr_dict['role'] = self._obj |
|
289 |
created.append(RoleAttribute.objects.create(**attr_dict)) |
|
279 |
setattr(self._obj, attr_dict['name'], json.loads(attr_dict['value'])) |
|
290 | 280 | |
291 | 281 |
return created, deleted |
292 | 282 |
src/authentic2/manager/forms.py | ||
---|---|---|
31 | 31 |
from django.utils.translation import pgettext |
32 | 32 |
from django_select2.forms import HeavySelect2Widget |
33 | 33 | |
34 |
from authentic2.a2_rbac.models import Operation, OrganizationalUnit, Permission, Role, RoleAttribute
|
|
34 |
from authentic2.a2_rbac.models import Operation, OrganizationalUnit, Permission, Role |
|
35 | 35 |
from authentic2.a2_rbac.utils import generate_slug, get_default_ou |
36 | 36 |
from authentic2.custom_user.backends import DjangoRBACBackend |
37 | 37 |
from authentic2.forms.fields import ( |
... | ... | |
603 | 603 | |
604 | 604 | |
605 | 605 |
class RoleEditForm(SlugMixin, HideOUFieldMixin, LimitQuerysetFormMixin, CssClass, forms.ModelForm): |
606 |
ou = forms.ModelChoiceField( |
|
607 |
queryset=OrganizationalUnit.objects, required=True, label=_('Organizational unit') |
|
608 |
) |
|
609 |
details = forms.CharField( |
|
610 |
label=_('Role details (frontoffice)'), widget=forms.Textarea, initial='', required=False |
|
611 |
) |
|
612 | 606 |
emails = CommaSeparatedCharField( |
613 | 607 |
label=_('Emails'), |
614 | 608 |
item_validators=[EmailValidator()], |
615 | 609 |
required=False, |
616 | 610 |
help_text=_('Emails must be separated by commas.'), |
617 | 611 |
) |
618 |
emails_to_members = forms.BooleanField(required=False, initial=True, label=_('Emails to members')) |
|
619 | 612 | |
620 | 613 |
class Meta: |
621 | 614 |
model = Role |
622 |
fields = ('name', 'slug', 'ou', 'description') |
|
615 |
fields = ('name', 'slug', 'ou', 'description', 'details', 'emails', 'emails_to_members')
|
|
623 | 616 |
widgets = { |
624 | 617 |
'name': forms.TextInput(), |
625 | 618 |
} |
626 | 619 | |
627 | 620 |
def __init__(self, *args, **kwargs): |
628 |
instance = kwargs.get('instance') |
|
629 |
if instance: |
|
630 |
fields = [x.name for x in Role._meta.get_fields()] |
|
631 |
initial = kwargs.setdefault('initial', {}) |
|
632 |
role_attributes = RoleAttribute.objects.filter(role=instance, kind='json') |
|
633 |
for role_attribute in role_attributes: |
|
634 |
if role_attribute.name in fields: |
|
635 |
continue |
|
636 |
initial[role_attribute.name] = json.loads(role_attribute.value) |
|
637 | 621 |
super().__init__(*args, **kwargs) |
638 | ||
639 |
def save(self, commit=True): |
|
640 |
fields = [x.name for x in Role._meta.get_fields()] |
|
641 |
assert commit |
|
642 |
instance = super().save(commit=commit) |
|
643 |
for field in self.cleaned_data: |
|
644 |
if field in fields: |
|
645 |
continue |
|
646 |
value = json.dumps(self.cleaned_data[field]) |
|
647 |
ra, created = RoleAttribute.objects.get_or_create( |
|
648 |
role=instance, name=field, kind='json', defaults={'value': value} |
|
649 |
) |
|
650 |
if not created and ra.value != value: |
|
651 |
ra.value = value |
|
652 |
ra.save() |
|
653 |
instance.save() |
|
654 |
return instance |
|
622 |
if 'ou' in self.fields: |
|
623 |
self.fields['ou'].required = True |
|
655 | 624 | |
656 | 625 | |
657 | 626 |
class OUEditForm(SlugMixin, CssClass, forms.ModelForm): |
tests/test_a2_rbac.py | ||
---|---|---|
21 | 21 | |
22 | 22 |
from authentic2.a2_rbac.models import CHANGE_OP, MANAGE_MEMBERS_OP, Operation |
23 | 23 |
from authentic2.a2_rbac.models import OrganizationalUnit as OU |
24 |
from authentic2.a2_rbac.models import Permission, Role, RoleAttribute
|
|
24 |
from authentic2.a2_rbac.models import Permission, Role |
|
25 | 25 |
from authentic2.a2_rbac.utils import get_default_ou |
26 | 26 |
from authentic2.custom_user.models import User |
27 | 27 |
from authentic2.models import Service |
... | ... | |
182 | 182 |
assert role_dict['service'] == {'slug': service.slug, 'ou': {'uuid': ou.uuid, 'slug': 'ou', 'name': 'ou'}} |
183 | 183 | |
184 | 184 | |
185 |
def test_role_with_attributes_export_json(db): |
|
186 |
role = Role.objects.create(name='some role') |
|
187 |
attr1 = RoleAttribute.objects.create(role=role, name='attr1_name', kind='string', value='attr1_value') |
|
188 |
attr2 = RoleAttribute.objects.create(role=role, name='attr2_name', kind='string', value='attr2_value') |
|
189 | ||
190 |
role_dict = role.export_json(attributes=True) |
|
191 |
attributes = role_dict['attributes'] |
|
192 |
assert len(attributes) == 2 |
|
193 | ||
194 |
expected_attr_names = {attr1.name, attr2.name} |
|
195 |
for attr_dict in attributes: |
|
196 |
assert attr_dict['name'] in expected_attr_names |
|
197 |
expected_attr_names.remove(attr_dict['name']) |
|
198 |
target_attr = RoleAttribute.objects.filter(name=attr_dict['name']).first() |
|
199 |
assert attr_dict['kind'] == target_attr.kind |
|
200 |
assert attr_dict['value'] == target_attr.value |
|
201 | ||
202 | ||
203 | 185 |
def test_role_with_parents_export_json(db): |
204 | 186 |
grand_parent_role = Role.objects.create(name='test grand parent role', slug='test-grand-parent-role') |
205 | 187 |
parent_1_role = Role.objects.create(name='test parent 1 role', slug='test-parent-1-role') |
... | ... | |
740 | 722 |
).count() |
741 | 723 |
== 1 |
742 | 724 |
) |
725 | ||
726 | ||
727 |
def test_a2_rbac_role_attribute_migration(migration, settings): |
|
728 |
migrate_from = [('a2_rbac', '0034_new_role_fields')] |
|
729 |
migrate_to = [('a2_rbac', '0036_delete_roleattribute')] |
|
730 | ||
731 |
old_apps = migration.before(migrate_from) |
|
732 |
Role = old_apps.get_model('a2_rbac', 'Role') |
|
733 |
RoleAttribute = old_apps.get_model('a2_rbac', 'RoleAttribute') |
|
734 | ||
735 |
role = Role.objects.create(name='role', slug='1') |
|
736 |
RoleAttribute.objects.create(role=role, kind='json', name='details', value='"abc"') |
|
737 |
RoleAttribute.objects.create(role=role, kind='json', name='emails', value='["a@a.com", "b@b.com"]') |
|
738 |
RoleAttribute.objects.create(role=role, kind='json', name='emails_to_members', value='false') |
|
739 |
RoleAttribute.objects.create(role=role, kind='string', name='is_superuser', value='true') |
|
740 | ||
741 |
role = Role.objects.create(name='role_default_values', slug='2') |
|
742 |
RoleAttribute.objects.create(role=role, kind='json', name='details', value='""') |
|
743 |
RoleAttribute.objects.create(role=role, kind='json', name='emails', value='[]') |
|
744 |
RoleAttribute.objects.create(role=role, kind='json', name='emails_to_members', value='true') |
|
745 |
RoleAttribute.objects.create(role=role, kind='string', name='is_superuser', value='false') |
|
746 | ||
747 |
role = Role.objects.create(name='role_no_attribute', slug='3') |
|
748 | ||
749 |
role = Role.objects.create(name='role_bad_attributes', slug='4') |
|
750 |
RoleAttribute.objects.create(role=role, kind='json', name='details', value='bad') |
|
751 |
RoleAttribute.objects.create(role=role, kind='json', name='emails', value='true') |
|
752 |
RoleAttribute.objects.create(role=role, kind='json', name='emails_to_members', value='bad') |
|
753 |
RoleAttribute.objects.create(role=role, kind='string', name='unknown', value='xxx') |
|
754 | ||
755 |
role = Role.objects.create(name='role_one_attribute', slug='5') |
|
756 |
RoleAttribute.objects.create(role=role, kind='json', name='details', value='"xxx"') |
|
757 | ||
758 |
new_apps = migration.apply(migrate_to) |
|
759 |
Role = new_apps.get_model('a2_rbac', 'Role') |
|
760 | ||
761 |
role = Role.objects.get(name='role') |
|
762 |
assert role.details == 'abc' |
|
763 |
assert role.emails == ['a@a.com', 'b@b.com'] |
|
764 |
assert role.emails_to_members is False |
|
765 |
assert role.is_superuser is True |
|
766 | ||
767 |
role = Role.objects.get(name='role_default_values') |
|
768 |
assert role.details == '' |
|
769 |
assert role.emails == [] |
|
770 |
assert role.emails_to_members is True |
|
771 |
assert role.is_superuser is False |
|
772 | ||
773 |
role = Role.objects.get(name='role_no_attribute') |
|
774 |
assert role.details == '' |
|
775 |
assert role.emails == [] |
|
776 |
assert role.emails_to_members is True |
|
777 |
assert role.is_superuser is False |
|
778 | ||
779 |
role = Role.objects.get(name='role_bad_attributes') |
|
780 |
assert role.details == '' |
|
781 |
assert role.emails == [] |
|
782 |
assert role.emails_to_members is True |
|
783 |
assert role.is_superuser is False |
|
784 | ||
785 |
role = Role.objects.get(name='role_one_attribute') |
|
786 |
assert role.details == 'xxx' |
|
787 |
assert role.emails == [] |
|
788 |
assert role.emails_to_members is True |
|
789 |
assert role.is_superuser is False |
tests/test_data_transfer.py | ||
---|---|---|
238 | 238 |
def test_role_deserializer_with_attributes(db): |
239 | 239 | |
240 | 240 |
attributes_data = { |
241 |
'attr1_name': dict(name='attr1_name', kind='string', value='attr1_value'),
|
|
242 |
'attr2_name': dict(name='attr2_name', kind='string', value='attr2_value'),
|
|
241 |
'is_superuser': dict(name='is_superuser', kind='string', value='true'),
|
|
242 |
'emails': dict(name='emails', kind='json', value='["a@a.com"]'),
|
|
243 | 243 |
} |
244 | 244 |
rd = RoleDeserializer( |
245 | 245 |
{ |
... | ... | |
254 | 254 |
ImportContext(), |
255 | 255 |
) |
256 | 256 |
role, status = rd.deserialize() |
257 |
created, dummy = rd.attributes()
|
|
257 |
rd.attributes() |
|
258 | 258 |
assert status == 'created' |
259 |
assert role.attributes.count() == 2 |
|
260 |
assert len(created) == 2 |
|
261 | ||
262 |
for attr in created: |
|
263 |
attr_dict = attributes_data[attr.name] |
|
264 |
assert attr_dict['name'] == attr.name |
|
265 |
assert attr_dict['kind'] == attr.kind |
|
266 |
assert attr_dict['value'] == attr.value |
|
267 |
del attributes_data[attr.name] |
|
259 |
assert role.is_superuser is True |
|
260 |
assert role.emails == ['a@a.com'] |
|
268 | 261 | |
269 | 262 | |
270 | 263 |
def test_role_deserializer_creates_admin_role(db): |
tests/test_idp_saml2.py | ||
---|---|---|
173 | 173 | |
174 | 174 |
# Admin role |
175 | 175 |
self.admin_role = Role.objects.create( |
176 |
name='Administrator', slug='administrator', service=self.provider |
|
176 |
name='Administrator', slug='administrator', service=self.provider, is_superuser=True
|
|
177 | 177 |
) |
178 |
self.admin_role.attributes.create(name='superuser', kind='string', value='true') |
|
179 | 178 | |
180 | 179 |
# SAML attributes mapping |
181 | 180 |
self.saml_first_name_attribute = self.provider.attributes.create( |
... | ... | |
957 | 956 |
service_role = Role.objects.create( |
958 | 957 |
name='Role of service', slug='role-of-service', ou=ou1, service=add_attributes_all.provider |
959 | 958 |
) |
960 | ||
961 |
service_role.attributes.create(name='is_admin', kind='string', value='true') |
|
962 | 959 |
user_ou1.roles.add(service_role) |
963 | 960 | |
964 |
add_attributes_all.get_definitions.return_value.append( |
|
965 |
SAMLAttribute(name_format='basic', name='is_admin', attribute_name='is_admin'), |
|
966 |
) |
|
967 | ||
968 | 961 |
attributes = add_attributes_all(user_ou1) |
969 | 962 |
assert attributes == { |
970 | 963 |
'a2_role_names': {'Role of service', 'role_ou2'}, |
... | ... | |
999 | 992 |
'django_user_password': {'abba0b6ff456806bab66baed93e6d9c4'}, |
1000 | 993 |
'django_user_username': {'john.doe'}, |
1001 | 994 |
'django_user_uuid': {user_ou1.uuid}, |
1002 |
'is_admin': {'true'}, |
|
1003 | 995 |
} |
1004 | 996 | |
1005 | 997 |
tests/test_manager.py | ||
---|---|---|
166 | 166 |
resp.form['emails'] = 'test@example.com' |
167 | 167 |
resp.form['emails_to_members'] = False |
168 | 168 |
resp = resp.form.submit().follow() |
169 |
assert set(simple_role.attributes.values_list('name', 'value')) == { |
|
170 |
('emails_to_members', 'false'),
|
|
171 |
('emails', '["test@example.com"]'),
|
|
172 |
('details', '"xxx"'),
|
|
173 |
}
|
|
169 | ||
170 |
simple_role.refresh_from_db()
|
|
171 |
assert simple_role.details == 'xxx'
|
|
172 |
assert simple_role.emails == ['test@example.com']
|
|
173 |
assert simple_role.emails_to_members is False
|
|
174 | 174 | |
175 | 175 |
resp = app.get('/manage/roles/%s/edit/' % simple_role.pk) |
176 | 176 |
resp.form['emails'] = 'test@example.com, hop@example.com' |
177 | 177 |
resp = resp.form.submit().follow() |
178 |
emails = simple_role.attributes.get(name='emails') |
|
179 |
assert set(json.loads(emails.value)) == {'test@example.com', 'hop@example.com'} |
|
178 | ||
179 |
simple_role.refresh_from_db() |
|
180 |
assert set(simple_role.emails) == {'test@example.com', 'hop@example.com'} |
|
180 | 181 | |
181 | 182 |
resp = app.get('/manage/roles/%s/edit/' % simple_role.pk) |
182 | 183 |
resp.form['emails'] = 'xxx' |
183 |
- |