Projet

Général

Profil

0003-create-import_site-and-export_site-commands-16514.patch

Emmanuel Cazenave, 09 avril 2018 14:20

Télécharger (95 ko)

Voir les différences:

Subject: [PATCH 3/3] create 'import_site' and 'export_site' commands (#16514)

 src/authentic2/a2_rbac/models.py                  |   36 +
 src/authentic2/data_transfer.py                   |  278 +++++
 src/authentic2/management/commands/export_site.py |   24 +
 src/authentic2/management/commands/import_site.py |   71 ++
 src/authentic2/utils.py                           |    5 +
 src/django_rbac/models.py                         |   11 +
 tests/data/export_site.json                       | 1361 +++++++++++++++++++++
 tests/test_a2_rbac.py                             |  158 ++-
 tests/test_data_transfer.py                       |  471 +++++++
 tests/test_import_export_site_cmd.py              |  132 ++
 10 files changed, 2546 insertions(+), 1 deletion(-)
 create mode 100644 src/authentic2/data_transfer.py
 create mode 100644 src/authentic2/management/commands/export_site.py
 create mode 100644 src/authentic2/management/commands/import_site.py
 create mode 100644 tests/data/export_site.json
 create mode 100644 tests/test_data_transfer.py
 create mode 100644 tests/test_import_export_site_cmd.py
src/authentic2/a2_rbac/models.py
92 92
    def cached(cls):
93 93
        return cls.objects.all()
94 94

  
95
    def export_json(self):
96
        return {
97
            'uuid': self.uuid, 'slug': self.slug, 'name': self.name,
98
            'description': self.description, 'default': self.default,
99
            'email_is_unique': self.email_is_unique,
100
            'username_is_unique': self.username_is_unique,
101
            'validate_emails': self.validate_emails
102
        }
103

  
95 104

  
96 105
OrganizationalUnit._meta.natural_key = [['uuid'], ['slug'], ['name']]
97 106

  
......
213 222
            'ou__slug': self.ou.slug if self.ou else None,
214 223
        }
215 224

  
225
    def export_json(self, attributes=False, parents=False, permissions=False):
226
        d = {
227
            'uuid': self.uuid, 'slug': self.slug, 'name': self.name,
228
            'description': self.description, 'external_id': self.external_id,
229
            'ou': self.ou and self.ou.natural_key_json(),
230
            'service': self.service and self.service.natural_key_json()
231
        }
232

  
233
        if attributes:
234
            for attribute in self.attributes.all():
235
                d.setdefault('attributes', []).append(attribute.to_json())
236

  
237
        if parents:
238
            RoleParenting = rbac_utils.get_role_parenting_model()
239
            for parenting in RoleParenting.objects.filter(child_id=self.id, direct=True):
240
                d.setdefault('parents', []).append(parenting.parent.natural_key_json())
241

  
242
        if permissions:
243
            for perm in self.permissions.all():
244
                d.setdefault('permissions', []).append(perm.export_json())
245

  
246
        return d
247

  
216 248

  
217 249
Role._meta.natural_key = [
218 250
    ['uuid'], ['slug', 'ou'], ['name', 'ou'], ['slug', 'service'], ['name', 'service']
......
250 282
            ('role', 'name', 'kind', 'value'),
251 283
        )
252 284

  
285
    def to_json(self):
286
        return {'name': self.name, 'kind': self.kind, 'value': self.value}
287

  
288

  
253 289
GenericRelation(Permission,
254 290
                content_type_field='target_ct',
255 291
                object_id_field='target_id').contribute_to_class(ContentType, 'admin_perms')
src/authentic2/data_transfer.py
1
from django.contrib.contenttypes.models import ContentType
2

  
3
from django_rbac.models import Operation
4
from django_rbac.utils import (
5
    get_ou_model,  get_role_model, get_role_parenting_model, get_permission_model)
6
from authentic2.a2_rbac.models import RoleAttribute
7
from authentic2.utils import update_model
8

  
9

  
10
def export_site():
11
    return {
12
        'roles': export_roles(get_role_model().objects.all()),
13
        'ous': export_ou(get_ou_model().objects.all())
14
    }
15

  
16

  
17
def export_ou(ou_query_set):
18
    return [ou.export_json() for ou in ou_query_set]
19

  
20

  
21
def export_roles(role_queryset):
22
    """ Serialize roles in role_queryset
23
    """
24
    return [
25
        role.export_json(attributes=True, parents=True, permissions=True)
26
        for role in role_queryset
27
    ]
28

  
29

  
30
def search_ou(ou_d):
31
    try:
32
        OU = get_ou_model()
33
        return OU.objects.get_by_natural_key_json(ou_d)
34
    except OU.DoesNotExist:
35
        return None
36

  
37

  
38
def search_role(role_d):
39
    Role = get_role_model()
40
    try:
41
        Role = get_role_model()
42
        return Role.objects.get_by_natural_key_json(role_d)
43
    except Role.DoesNotExist:
44
        return None
45

  
46

  
47
class ImportContext(object):
48
    """ Holds information on how to perform the import.
49

  
50
    ou_delete_orphans: if True any existing ou that is not found in the export will
51
                       be deleted
52

  
53
    role_delete_orphans: if True any existing role that is not found in the export will
54
                         be deleted
55

  
56

  
57
    role_attributes_update: for each role in the import data,
58
                            attributes  will deleted and re-created
59

  
60

  
61
    role_parentings_update: for each role in the import data,
62
                            parentings will deleted and re-created
63

  
64
    role_permissions_update: for each role in the import data,
65
                             permissions  will deleted and re-created
66
    """
67

  
68
    def __init__(
69
            self, role_delete_orphans=False, role_parentings_update=True,
70
            role_permissions_update=True, role_attributes_update=True,
71
            ou_delete_orphans=False):
72
        self.role_delete_orphans = role_delete_orphans
73
        self.ou_delete_orphans = ou_delete_orphans
74
        self.role_parentings_update = role_parentings_update
75
        self.role_permissions_update = role_permissions_update
76
        self.role_attributes_update = role_attributes_update
77

  
78

  
79
class DataImportError(Exception):
80
    pass
81

  
82

  
83
class RoleDeserializer(object):
84

  
85
    def __init__(self, d, import_context):
86
        self._import_context = import_context
87
        self._obj = None
88
        self._parents = None
89
        self._attributes = None
90
        self._permissions = None
91

  
92
        self._role_d = dict()
93
        for key, value in d.items():
94
            if key == 'parents':
95
                self._parents = value
96
            elif key == 'attributes':
97
                self._attributes = value
98
            elif key == 'permissions':
99
                self._permissions = value
100
            else:
101
                self._role_d[key] = value
102

  
103
    def deserialize(self):
104
        ou_d = self._role_d['ou']
105
        has_ou = bool(ou_d)
106
        ou = None if not has_ou else search_ou(ou_d)
107
        if has_ou and not ou:
108
            raise DataImportError(
109
                    "Can't import role because missing Organizational Unit : "
110
                    "%s" % ou_d)
111

  
112
        kwargs = self._role_d.copy()
113
        del kwargs['ou']
114
        del kwargs['service']
115
        if has_ou:
116
            kwargs['ou'] = ou
117

  
118
        obj = search_role(self._role_d)
119
        if obj:  # Role already exist
120
            self._obj = obj
121
            status = 'updated'
122
            update_model(self._obj, kwargs)
123
        else:  # Create role
124
            self._obj = get_role_model().objects.create(**kwargs)
125
            status = 'created'
126

  
127
        # Ensure admin role is created
128
        self._obj.get_admin_role()
129
        return self._obj, status
130

  
131
    def attributes(self):
132
        """ Update attributes (delete everything then create)
133
        """
134
        created, deleted = [], []
135
        for attr in self._obj.attributes.all():
136
            attr.delete()
137
            deleted.append(attr)
138
        # Create attributes
139
        if self._attributes:
140
            for attr_dict in self._attributes:
141
                attr_dict['role'] = self._obj
142
                created.append(RoleAttribute.objects.create(**attr_dict))
143

  
144
        return created, deleted
145

  
146
    def parentings(self):
147
        """ Update parentings (delete everything then create)
148
        """
149
        created, deleted = [], []
150
        Parenting = get_role_parenting_model()
151
        for parenting in Parenting.objects.filter(child=self._obj, direct=True):
152
            parenting.delete()
153
            deleted.append(parenting)
154

  
155
        if self._parents:
156
            for parent_d in self._parents:
157
                parent = search_role(parent_d)
158
                if not parent:
159
                    raise DataImportError("Could not find role : %s" % parent_d)
160
                created.append(Parenting.objects.create(
161
                    child=self._obj, direct=True, parent=parent))
162

  
163
        return created, deleted
164

  
165
    def permissions(self):
166
        """ Update permissions (delete everything then create)
167
        """
168
        created, deleted = [], []
169
        for perm in self._obj.permissions.all():
170
            perm.delete()
171
            deleted.append(perm)
172
        self._obj.permissions.clear()
173
        if self._permissions:
174
            for perm in self._permissions:
175
                op = Operation.objects.get_by_natural_key_json(perm['operation'])
176
                ou = get_ou_model().objects.get_by_natural_key_json(
177
                    perm['ou']) if perm['ou'] else None
178
                ct = ContentType.objects.get_by_natural_key_json(perm['target_ct'])
179
                target = ct.model_class().objects.get_by_natural_key_json(perm['target'])
180
                perm = get_permission_model().objects.create(
181
                    operation=op, ou=ou, target_ct=ct, target_id=target.pk)
182
                self._obj.permissions.add(perm)
183
                created.append(perm)
184

  
185
        return created, deleted
186

  
187

  
188
class ImportResult(object):
189

  
190
    def __init__(self):
191
        self.roles = {'created': [], 'updated': []}
192
        self.ous = {'created': [], 'updated': []}
193
        self.attributes = {'created': [], 'deleted': []}
194
        self.parentings = {'created': [], 'deleted': []}
195
        self.permissions = {'created': [], 'deleted': []}
196

  
197
    def update_roles(self, role, d_status):
198
        self.roles[d_status].append(role)
199

  
200
    def update_ous(self, ou, status):
201
        self.ous[status].append(ou)
202

  
203
    def _bulk_update(self, attrname, created, deleted):
204
        attr = getattr(self, attrname)
205
        attr['created'].extend(created)
206
        attr['deleted'].extend(deleted)
207

  
208
    def update_attributes(self, created, deleted):
209
        self._bulk_update('attributes', created, deleted)
210

  
211
    def update_parentings(self, created, deleted):
212
        self._bulk_update('parentings', created, deleted)
213

  
214
    def update_permissions(self, created, deleted):
215
        self._bulk_update('permissions', created, deleted)
216

  
217
    def to_str(self, verbose=False):
218
        res = ""
219
        for attr in ('roles', 'ous', 'parentings', 'permissions', 'attributes'):
220
            data = getattr(self, attr)
221
            for status in ('created', 'updated', 'deleted'):
222
                if status in data:
223
                    s_data = data[status]
224
                    res += "%s %s %s\n" % (len(s_data), attr, status)
225
        return res
226

  
227

  
228
def import_ou(ou_d):
229
    OU = get_ou_model()
230
    # ou = search_ou([ou_d['slug']])
231
    ou = search_ou(ou_d)
232
    if ou is None:
233
        ou = OU.objects.create(**ou_d)
234
        status = 'created'
235
    else:
236
        update_model(ou, ou_d)
237
        status = 'updated'
238
    # Ensure admin role is created
239
    ou.get_admin_role()
240
    return ou, status
241

  
242

  
243
def import_site(json_d, import_context):
244
    result = ImportResult()
245

  
246
    for ou_d in json_d.get('ous', []):
247
        result.update_ous(*import_ou(ou_d))
248

  
249
    roles_ds = [RoleDeserializer(role_d, import_context) for role_d in json_d.get('roles', [])
250
                if not role_d['slug'].startswith('_')]
251

  
252
    for ds in roles_ds:
253
        result.update_roles(*ds.deserialize())
254

  
255
    if import_context.role_attributes_update:
256
        for ds in roles_ds:
257
            result.update_attributes(*ds.attributes())
258

  
259
    if import_context.role_parentings_update:
260
        for ds in roles_ds:
261
            result.update_parentings(*ds.parentings())
262

  
263
    if import_context.role_permissions_update:
264
        for ds in roles_ds:
265
            result.update_permissions(*ds.permissions())
266

  
267
    if import_context.ou_delete_orphans:
268
        raise DataImportError(
269
            "Unsupported context value for ou_delete_orphans : %s" % (
270
                import_context.ou_delete_orphans))
271

  
272
    if import_context.role_delete_orphans:
273
        # FIXME : delete each role that is in DB but not in the export
274
        raise DataImportError(
275
            "Unsupported context value for role_delete_orphans : %s" % (
276
                import_context.role_delete_orphans))
277

  
278
    return result
src/authentic2/management/commands/export_site.py
1
import json
2
import sys
3

  
4
from django.core.management.base import BaseCommand
5

  
6
from authentic2.data_transfer import export_site
7
from django_rbac.utils import get_role_model
8

  
9

  
10
class Command(BaseCommand):
11
    help = 'Export site'
12

  
13
    def add_arguments(self, parser):
14
        parser.add_argument('--output', metavar='FILE', default=None,
15
                            help='name of a file to write output to')
16

  
17
    def handle(self, *args, **options):
18
        if options['output']:
19
            output, close = open(options['output'], 'w'), True
20
        else:
21
            output, close = sys.stdout, False
22
        json.dump(export_site(), output, indent=4)
23
        if close:
24
            output.close()
src/authentic2/management/commands/import_site.py
1
import contextlib
2
import json
3
import sys
4

  
5
from django.conf import settings
6
from django.core.management.base import BaseCommand
7
from django.db import transaction
8
from django.utils import translation
9

  
10
from authentic2.data_transfer import import_site, ImportContext
11

  
12

  
13
class DryRunException(Exception):
14
    pass
15

  
16

  
17
def create_context_args(options):
18
    kwargs = {}
19
    if options['option']:
20
        for context_op in options['option']:
21
            context_op = context_op.replace('-', '_')
22
            if context_op.startswith('no_'):
23
                kwargs[context_op[3:]] = False
24
            else:
25
                kwargs[context_op] = True
26
    return kwargs
27

  
28

  
29
#  Borrowed from https://bugs.python.org/issue10049#msg118599
30
@contextlib.contextmanager
31
def provision_contextm(dry_run, settings):
32
    if dry_run and 'hobo.agent.authentic2' in settings.INSTALLED_APPS:
33
        import hobo.agent.authentic2
34
        with hobo.agent.authentic2.provisionning.Provisionning():
35
            yield
36
    else:
37
        yield
38

  
39

  
40
class Command(BaseCommand):
41
    help = 'Import site'
42

  
43
    def add_arguments(self, parser):
44
        parser.add_argument(
45
            'filename', metavar='FILENAME', type=str, help='name of file to import')
46
        parser.add_argument(
47
            '--dry-run', action='store_true', dest='dry_run', help='Really perform the import')
48
        parser.add_argument(
49
            '-o', '--option', action='append', help='Import context options',
50
            choices=[
51
                'role-delete-orphans', 'ou-delete-orphans', 'no-role-permissions-update',
52
                'no-role-attributes-update', 'no-role-parentings-update'])
53

  
54
    def handle(self, filename, **options):
55
        translation.activate(settings.LANGUAGE_CODE)
56
        dry_run = options['dry_run']
57
        msg = "Dry run\n" if dry_run else "Real run\n"
58
        c_kwargs = create_context_args(options)
59
        try:
60
            with open(filename, 'r') as f:
61
                with provision_contextm(dry_run, settings):
62
                    with transaction.atomic():
63
                        sys.stdout.write(msg)
64
                        result = import_site(json.load(f), ImportContext(**c_kwargs))
65
                        if dry_run:
66
                            raise DryRunException()
67
        except DryRunException:
68
            pass
69
        sys.stdout.write(result.to_str())
70
        sys.stdout.write("Success\n")
71
        translation.deactivate()
src/authentic2/utils.py
1047 1047
        context=ctx,
1048 1048
        legacy_subject_templates=legacy_subject_templates,
1049 1049
        legacy_body_templates=legacy_body_templates)
1050

  
1051

  
1052
def update_model(obj, d):
1053
    for attr, value in d.items():
1054
        setattr(obj, attr, value)
src/django_rbac/models.py
114 114
    def __unicode__(self):
115 115
        return unicode(_(self.name))
116 116

  
117
    def export_json(self):
118
        return {'slug': self.slug, 'name': self.name}
119

  
117 120
    objects = managers.OperationManager()
118 121

  
119 122

  
......
145 148
                self.target and self.target_ct.natural_key(),
146 149
                self.target and self.target.natural_key()]
147 150

  
151
    def export_json(self):
152
        return {
153
            "operation": self.operation.natural_key_json(),
154
            "ou": self.ou and self.ou.natural_key_json(),
155
            'target_ct': self.target_ct.natural_key_json(),
156
            "target": self.target.natural_key_json()
157
        }
158

  
148 159
    def __unicode__(self):
149 160
        ct = ContentType.objects.get_for_id(self.target_ct_id)
150 161
        ct_ct = ContentType.objects.get_for_model(ContentType)
tests/data/export_site.json
1
{
2
    "ous": [
3
        {
4
            "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
5
            "name": "Collectivit\u00e9 par d\u00e9faut", 
6
            "slug": "default"
7
        }, 
8
        {
9
            "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
10
            "name": "ou deux", 
11
            "slug": "ou-deux"
12
        }, 
13
        {
14
            "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
15
            "name": "ou un ", 
16
            "slug": "ou-un"
17
        }
18
    ], 
19
    "roles": [
20
        {
21
            "uuid": "0b47461473144334a3d5266b91adfa55", 
22
            "service": {
23
                "ou": {
24
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
25
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
26
                    "slug": "default"
27
                }, 
28
                "name": "Hobo", 
29
                "slug": "hobo"
30
            }, 
31
            "attributes": [
32
                {
33
                    "kind": "string", 
34
                    "name": "is_superuser", 
35
                    "value": "true"
36
                }
37
            ], 
38
            "admin_scope_id": null, 
39
            "ou": {
40
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
41
                "name": "Collectivit\u00e9 par d\u00e9faut", 
42
                "slug": "default"
43
            }, 
44
            "description": "", 
45
            "admin_scope_ct_id": null, 
46
            "external_id": "", 
47
            "slug": "_a2-hobo-superuser", 
48
            "name": "Administrateur de Hobo"
49
        }, 
50
        {
51
            "uuid": "540fe45b326040b2b891fc350635e891", 
52
            "service": {
53
                "ou": {
54
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
55
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
56
                    "slug": "default"
57
                }, 
58
                "name": "Portail Citoyen", 
59
                "slug": "portal"
60
            }, 
61
            "attributes": [
62
                {
63
                    "kind": "string", 
64
                    "name": "is_superuser", 
65
                    "value": "true"
66
                }
67
            ], 
68
            "admin_scope_id": null, 
69
            "ou": {
70
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
71
                "name": "Collectivit\u00e9 par d\u00e9faut", 
72
                "slug": "default"
73
            }, 
74
            "description": "", 
75
            "admin_scope_ct_id": null, 
76
            "external_id": "", 
77
            "slug": "_a2-hobo-superuser", 
78
            "name": "Administrateur de Portail Citoyen"
79
        }, 
80
        {
81
            "uuid": "abd2d89a4e354cbc9b8fdc1d977c96da", 
82
            "service": {
83
                "ou": {
84
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
85
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
86
                    "slug": "default"
87
                }, 
88
                "name": "D\u00e9marches", 
89
                "slug": "services"
90
            }, 
91
            "attributes": [
92
                {
93
                    "kind": "string", 
94
                    "name": "is_superuser", 
95
                    "value": "true"
96
                }
97
            ], 
98
            "admin_scope_id": null, 
99
            "ou": {
100
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
101
                "name": "Collectivit\u00e9 par d\u00e9faut", 
102
                "slug": "default"
103
            }, 
104
            "description": "", 
105
            "admin_scope_ct_id": null, 
106
            "external_id": "", 
107
            "slug": "_a2-hobo-superuser", 
108
            "name": "Administrateur de D\u00e9marches"
109
        }, 
110
        {
111
            "uuid": "d064a245046743a0baac8513c009554d", 
112
            "service": {
113
                "ou": {
114
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
115
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
116
                    "slug": "default"
117
                }, 
118
                "name": "Portail Agent", 
119
                "slug": "portal-agent"
120
            }, 
121
            "attributes": [
122
                {
123
                    "kind": "string", 
124
                    "name": "is_superuser", 
125
                    "value": "true"
126
                }
127
            ], 
128
            "admin_scope_id": null, 
129
            "ou": {
130
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
131
                "name": "Collectivit\u00e9 par d\u00e9faut", 
132
                "slug": "default"
133
            }, 
134
            "description": "", 
135
            "admin_scope_ct_id": null, 
136
            "external_id": "", 
137
            "slug": "_a2-hobo-superuser", 
138
            "name": "Administrateur de Portail Agent"
139
        }, 
140
        {
141
            "uuid": "dce09d79743d496eaa50bbd96c04c65e", 
142
            "service": {
143
                "ou": {
144
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
145
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
146
                    "slug": "default"
147
                }, 
148
                "name": "Passerelle", 
149
                "slug": "passerelle"
150
            }, 
151
            "attributes": [
152
                {
153
                    "kind": "string", 
154
                    "name": "is_superuser", 
155
                    "value": "true"
156
                }
157
            ], 
158
            "admin_scope_id": null, 
159
            "ou": {
160
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
161
                "name": "Collectivit\u00e9 par d\u00e9faut", 
162
                "slug": "default"
163
            }, 
164
            "description": "", 
165
            "admin_scope_ct_id": null, 
166
            "external_id": "", 
167
            "slug": "_a2-hobo-superuser", 
168
            "name": "Administrateur de Passerelle"
169
        }, 
170
        {
171
            "uuid": "b043396ad9e942bcb6dd21a467e0f230", 
172
            "service": {
173
                "ou": {
174
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
175
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
176
                    "slug": "default"
177
                }, 
178
                "name": "Porte-documents", 
179
                "slug": "porte-doc"
180
            }, 
181
            "attributes": [
182
                {
183
                    "kind": "string", 
184
                    "name": "is_superuser", 
185
                    "value": "true"
186
                }
187
            ], 
188
            "admin_scope_id": null, 
189
            "ou": {
190
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
191
                "name": "Collectivit\u00e9 par d\u00e9faut", 
192
                "slug": "default"
193
            }, 
194
            "description": "", 
195
            "admin_scope_ct_id": null, 
196
            "external_id": "", 
197
            "slug": "_a2-hobo-superuser", 
198
            "name": "Administrateur de Porte-documents"
199
        }, 
200
        {
201
            "uuid": "bc6582d1fec549ff86c5ea6e91756e7b", 
202
            "service": {
203
                "ou": {
204
                    "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
205
                    "name": "Collectivit\u00e9 par d\u00e9faut", 
206
                    "slug": "default"
207
                }, 
208
                "name": "Agendas", 
209
                "slug": "agendas"
210
            }, 
211
            "attributes": [
212
                {
213
                    "kind": "string", 
214
                    "name": "is_superuser", 
215
                    "value": "true"
216
                }
217
            ], 
218
            "admin_scope_id": null, 
219
            "ou": {
220
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
221
                "name": "Collectivit\u00e9 par d\u00e9faut", 
222
                "slug": "default"
223
            }, 
224
            "description": "", 
225
            "admin_scope_ct_id": null, 
226
            "external_id": "", 
227
            "slug": "_a2-hobo-superuser", 
228
            "name": "Administrateur de Agendas"
229
        }, 
230
        {
231
            "uuid": "479ada08e2274067b969d290685f1496", 
232
            "service": null, 
233
            "permissions": [
234
                {
235
                    "operation": {
236
                        "slug": "change", 
237
                        "name": "Modifier"
238
                    }, 
239
                    "ou": null, 
240
                    "target_ct": [
241
                        "a2_rbac", 
242
                        "role"
243
                    ], 
244
                    "target": [
245
                        "_a2-managers-of-role-role-deux", 
246
                        [
247
                            "default"
248
                        ], 
249
                        null
250
                    ]
251
                }, 
252
                {
253
                    "operation": {
254
                        "slug": "view", 
255
                        "name": "Visualisation"
256
                    }, 
257
                    "ou": null, 
258
                    "target_ct": [
259
                        "contenttypes", 
260
                        "contenttype"
261
                    ], 
262
                    "target": [
263
                        "custom_user", 
264
                        "user"
265
                    ]
266
                }, 
267
                {
268
                    "operation": {
269
                        "slug": "admin", 
270
                        "name": "Administration"
271
                    }, 
272
                    "ou": {
273
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
274
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
275
                        "slug": "default"
276
                    }, 
277
                    "target_ct": [
278
                        "a2_rbac", 
279
                        "role"
280
                    ], 
281
                    "target": [
282
                        "role-deux", 
283
                        [
284
                            "default"
285
                        ], 
286
                        null
287
                    ]
288
                }
289
            ], 
290
            "admin_scope_id": 9, 
291
            "ou": {
292
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
293
                "name": "Collectivit\u00e9 par d\u00e9faut", 
294
                "slug": "default"
295
            }, 
296
            "description": "", 
297
            "admin_scope_ct_id": 36, 
298
            "external_id": "", 
299
            "slug": "_a2-managers-of-role-role-deux", 
300
            "name": "Administrateur du r\u00f4le \u00ab\u00a0role deux\u00a0\u00bb"
301
        }, 
302
        {
303
            "uuid": "6de808e99fb3442ea7bc4ae8d8c33a43", 
304
            "service": null, 
305
            "permissions": [
306
                {
307
                    "operation": {
308
                        "slug": "change", 
309
                        "name": "Modifier"
310
                    }, 
311
                    "ou": null, 
312
                    "target_ct": [
313
                        "a2_rbac", 
314
                        "role"
315
                    ], 
316
                    "target": [
317
                        "_a2-managers-of-role-role-un", 
318
                        [
319
                            "default"
320
                        ], 
321
                        null
322
                    ]
323
                }, 
324
                {
325
                    "operation": {
326
                        "slug": "view", 
327
                        "name": "Visualisation"
328
                    }, 
329
                    "ou": null, 
330
                    "target_ct": [
331
                        "contenttypes", 
332
                        "contenttype"
333
                    ], 
334
                    "target": [
335
                        "custom_user", 
336
                        "user"
337
                    ]
338
                }, 
339
                {
340
                    "operation": {
341
                        "slug": "admin", 
342
                        "name": "Administration"
343
                    }, 
344
                    "ou": {
345
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
346
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
347
                        "slug": "default"
348
                    }, 
349
                    "target_ct": [
350
                        "a2_rbac", 
351
                        "role"
352
                    ], 
353
                    "target": [
354
                        "role-un", 
355
                        [
356
                            "ou-un"
357
                        ], 
358
                        null
359
                    ]
360
                }
361
            ], 
362
            "admin_scope_id": 7, 
363
            "ou": {
364
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
365
                "name": "Collectivit\u00e9 par d\u00e9faut", 
366
                "slug": "default"
367
            }, 
368
            "description": "", 
369
            "admin_scope_ct_id": 36, 
370
            "external_id": "", 
371
            "slug": "_a2-managers-of-role-role-un", 
372
            "name": "Administrateur du r\u00f4le \u00ab\u00a0role un\u00a0\u00bb"
373
        }, 
374
        {
375
            "description": "role deux descr", 
376
            "slug": "role-deux", 
377
            "name": "role deux", 
378
            "uuid": "9e4f8189c5054c109ae25b8c633eca78", 
379
            "service": null, 
380
            "admin_scope_id": null, 
381
            "admin_scope_ct_id": null, 
382
            "parents": [
383
                {
384
                    "uuid": "aa83b6c8f7f2420ea855b6c06e5f7e80", 
385
                    "service": null, 
386
                    "admin_scope_id": 26, 
387
                    "ou": {
388
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
389
                        "name": "ou un ", 
390
                        "slug": "ou-un"
391
                    }, 
392
                    "description": "", 
393
                    "admin_scope_ct_id": 36, 
394
                    "external_id": "", 
395
                    "slug": "_a2-managers-of-role-role-un", 
396
                    "name": "Administrateur du r\u00f4le \u00ab\u00a0role un\u00a0\u00bb"
397
                }
398
            ], 
399
            "attributes": [
400
                {
401
                    "kind": "json", 
402
                    "name": "emails", 
403
                    "value": "[]"
404
                }, 
405
                {
406
                    "kind": "json", 
407
                    "name": "details", 
408
                    "value": "\"role deux detail\""
409
                }, 
410
                {
411
                    "kind": "json", 
412
                    "name": "emails_to_members", 
413
                    "value": "true"
414
                }
415
            ], 
416
            "ou": {
417
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
418
                "name": "Collectivit\u00e9 par d\u00e9faut", 
419
                "slug": "default"
420
            }, 
421
            "permissions": [
422
                {
423
                    "operation": {
424
                        "slug": "delete", 
425
                        "name": "Supprimer"
426
                    }, 
427
                    "ou": {
428
                        "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
429
                        "name": "ou deux", 
430
                        "slug": "ou-deux"
431
                    }, 
432
                    "target_ct": [
433
                        "contenttypes", 
434
                        "contenttype"
435
                    ], 
436
                    "target": [
437
                        "custom_user", 
438
                        "user"
439
                    ]
440
                }, 
441
                {
442
                    "operation": {
443
                        "slug": "search", 
444
                        "name": "Rechercher"
445
                    }, 
446
                    "ou": {
447
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
448
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
449
                        "slug": "default"
450
                    }, 
451
                    "target_ct": [
452
                        "contenttypes", 
453
                        "contenttype"
454
                    ], 
455
                    "target": [
456
                        "admin", 
457
                        "logentry"
458
                    ]
459
                }, 
460
                {
461
                    "operation": {
462
                        "slug": "view", 
463
                        "name": "Visualisation"
464
                    }, 
465
                    "ou": {
466
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
467
                        "name": "ou un ", 
468
                        "slug": "ou-un"
469
                    }, 
470
                    "target_ct": [
471
                        "contenttypes", 
472
                        "contenttype"
473
                    ], 
474
                    "target": [
475
                        "dashboard", 
476
                        "dashboardpreferences"
477
                    ]
478
                }
479
            ], 
480
            "external_id": ""
481
        }, 
482
        {
483
            "uuid": "486cf6897b9743b18f74c7047cf054d9", 
484
            "service": null, 
485
            "permissions": [
486
                {
487
                    "operation": {
488
                        "slug": "admin", 
489
                        "name": "Administration"
490
                    }, 
491
                    "ou": {
492
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
493
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
494
                        "slug": "default"
495
                    }, 
496
                    "target_ct": [
497
                        "contenttypes", 
498
                        "contenttype"
499
                    ], 
500
                    "target": [
501
                        "a2_rbac", 
502
                        "role"
503
                    ]
504
                }, 
505
                {
506
                    "operation": {
507
                        "slug": "view", 
508
                        "name": "Visualisation"
509
                    }, 
510
                    "ou": {
511
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
512
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
513
                        "slug": "default"
514
                    }, 
515
                    "target_ct": [
516
                        "contenttypes", 
517
                        "contenttype"
518
                    ], 
519
                    "target": [
520
                        "custom_user", 
521
                        "user"
522
                    ]
523
                }, 
524
                {
525
                    "operation": {
526
                        "slug": "search", 
527
                        "name": "Rechercher"
528
                    }, 
529
                    "ou": null, 
530
                    "target_ct": [
531
                        "a2_rbac", 
532
                        "organizationalunit"
533
                    ], 
534
                    "target": [
535
                        "default"
536
                    ]
537
                }
538
            ], 
539
            "admin_scope_id": 12, 
540
            "ou": {
541
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
542
                "name": "Collectivit\u00e9 par d\u00e9faut", 
543
                "slug": "default"
544
            }, 
545
            "description": "", 
546
            "admin_scope_ct_id": 36, 
547
            "external_id": "", 
548
            "slug": "_a2-administrateur-des-roles-default", 
549
            "name": "R\u00f4les - Collectivit\u00e9 par d\u00e9faut"
550
        }, 
551
        {
552
            "uuid": "d472b169995043ebbe6658b351a70f86", 
553
            "service": null, 
554
            "permissions": [
555
                {
556
                    "operation": {
557
                        "slug": "admin", 
558
                        "name": "Administration"
559
                    }, 
560
                    "ou": {
561
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
562
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
563
                        "slug": "default"
564
                    }, 
565
                    "target_ct": [
566
                        "contenttypes", 
567
                        "contenttype"
568
                    ], 
569
                    "target": [
570
                        "custom_user", 
571
                        "user"
572
                    ]
573
                }, 
574
                {
575
                    "operation": {
576
                        "slug": "search", 
577
                        "name": "Rechercher"
578
                    }, 
579
                    "ou": null, 
580
                    "target_ct": [
581
                        "a2_rbac", 
582
                        "organizationalunit"
583
                    ], 
584
                    "target": [
585
                        "default"
586
                    ]
587
                }
588
            ], 
589
            "admin_scope_id": 15, 
590
            "ou": {
591
                "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
592
                "name": "Collectivit\u00e9 par d\u00e9faut", 
593
                "slug": "default"
594
            }, 
595
            "description": "", 
596
            "admin_scope_ct_id": 36, 
597
            "external_id": "", 
598
            "slug": "_a2-administrateur-des-utilisateurs-default", 
599
            "name": "Utilisateurs - Collectivit\u00e9 par d\u00e9faut"
600
        }, 
601
        {
602
            "uuid": "69bd392d338f437eb908d16de3af9723", 
603
            "service": null, 
604
            "permissions": [
605
                {
606
                    "operation": {
607
                        "slug": "admin", 
608
                        "name": "Administration"
609
                    }, 
610
                    "ou": {
611
                        "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
612
                        "name": "ou deux", 
613
                        "slug": "ou-deux"
614
                    }, 
615
                    "target_ct": [
616
                        "contenttypes", 
617
                        "contenttype"
618
                    ], 
619
                    "target": [
620
                        "a2_rbac", 
621
                        "role"
622
                    ]
623
                }, 
624
                {
625
                    "operation": {
626
                        "slug": "view", 
627
                        "name": "Visualisation"
628
                    }, 
629
                    "ou": {
630
                        "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
631
                        "name": "ou deux", 
632
                        "slug": "ou-deux"
633
                    }, 
634
                    "target_ct": [
635
                        "contenttypes", 
636
                        "contenttype"
637
                    ], 
638
                    "target": [
639
                        "custom_user", 
640
                        "user"
641
                    ]
642
                }, 
643
                {
644
                    "operation": {
645
                        "slug": "search", 
646
                        "name": "Rechercher"
647
                    }, 
648
                    "ou": null, 
649
                    "target_ct": [
650
                        "a2_rbac", 
651
                        "organizationalunit"
652
                    ], 
653
                    "target": [
654
                        "ou-deux"
655
                    ]
656
                }
657
            ], 
658
            "admin_scope_id": 22, 
659
            "ou": {
660
                "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
661
                "name": "ou deux", 
662
                "slug": "ou-deux"
663
            }, 
664
            "description": "", 
665
            "admin_scope_ct_id": 36, 
666
            "external_id": "", 
667
            "slug": "_a2-administrateur-des-roles-ou-deux", 
668
            "name": "R\u00f4les - ou deux"
669
        }, 
670
        {
671
            "uuid": "7ca8d6f933054021a179a3d86ca19efb", 
672
            "service": null, 
673
            "permissions": [
674
                {
675
                    "operation": {
676
                        "slug": "admin", 
677
                        "name": "Administration"
678
                    }, 
679
                    "ou": {
680
                        "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
681
                        "name": "ou deux", 
682
                        "slug": "ou-deux"
683
                    }, 
684
                    "target_ct": [
685
                        "contenttypes", 
686
                        "contenttype"
687
                    ], 
688
                    "target": [
689
                        "custom_user", 
690
                        "user"
691
                    ]
692
                }, 
693
                {
694
                    "operation": {
695
                        "slug": "search", 
696
                        "name": "Rechercher"
697
                    }, 
698
                    "ou": null, 
699
                    "target_ct": [
700
                        "a2_rbac", 
701
                        "organizationalunit"
702
                    ], 
703
                    "target": [
704
                        "ou-deux"
705
                    ]
706
                }
707
            ], 
708
            "admin_scope_id": 25, 
709
            "ou": {
710
                "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
711
                "name": "ou deux", 
712
                "slug": "ou-deux"
713
            }, 
714
            "description": "", 
715
            "admin_scope_ct_id": 36, 
716
            "external_id": "", 
717
            "slug": "_a2-administrateur-des-utilisateurs-ou-deux", 
718
            "name": "Utilisateurs - ou deux"
719
        }, 
720
        {
721
            "uuid": "aa83b6c8f7f2420ea855b6c06e5f7e80", 
722
            "service": null, 
723
            "permissions": [
724
                {
725
                    "operation": {
726
                        "slug": "change", 
727
                        "name": "Modifier"
728
                    }, 
729
                    "ou": null, 
730
                    "target_ct": [
731
                        "a2_rbac", 
732
                        "role"
733
                    ], 
734
                    "target": [
735
                        "_a2-managers-of-role-role-un", 
736
                        [
737
                            "ou-un"
738
                        ], 
739
                        null
740
                    ]
741
                }, 
742
                {
743
                    "operation": {
744
                        "slug": "view", 
745
                        "name": "Visualisation"
746
                    }, 
747
                    "ou": null, 
748
                    "target_ct": [
749
                        "contenttypes", 
750
                        "contenttype"
751
                    ], 
752
                    "target": [
753
                        "custom_user", 
754
                        "user"
755
                    ]
756
                }, 
757
                {
758
                    "operation": {
759
                        "slug": "admin", 
760
                        "name": "Administration"
761
                    }, 
762
                    "ou": {
763
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
764
                        "name": "ou un ", 
765
                        "slug": "ou-un"
766
                    }, 
767
                    "target_ct": [
768
                        "a2_rbac", 
769
                        "role"
770
                    ], 
771
                    "target": [
772
                        "role-un", 
773
                        [
774
                            "ou-un"
775
                        ], 
776
                        null
777
                    ]
778
                }
779
            ], 
780
            "admin_scope_id": 26, 
781
            "ou": {
782
                "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
783
                "name": "ou un ", 
784
                "slug": "ou-un"
785
            }, 
786
            "description": "", 
787
            "admin_scope_ct_id": 36, 
788
            "external_id": "", 
789
            "slug": "_a2-managers-of-role-role-un", 
790
            "name": "Administrateur du r\u00f4le \u00ab\u00a0role un\u00a0\u00bb"
791
        }, 
792
        {
793
            "uuid": "241492ead56c4773abf97271ea88b0a9", 
794
            "service": null, 
795
            "permissions": [
796
                {
797
                    "operation": {
798
                        "slug": "admin", 
799
                        "name": "Administration"
800
                    }, 
801
                    "ou": {
802
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
803
                        "name": "ou un ", 
804
                        "slug": "ou-un"
805
                    }, 
806
                    "target_ct": [
807
                        "contenttypes", 
808
                        "contenttype"
809
                    ], 
810
                    "target": [
811
                        "a2_rbac", 
812
                        "role"
813
                    ]
814
                }, 
815
                {
816
                    "operation": {
817
                        "slug": "view", 
818
                        "name": "Visualisation"
819
                    }, 
820
                    "ou": {
821
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
822
                        "name": "ou un ", 
823
                        "slug": "ou-un"
824
                    }, 
825
                    "target_ct": [
826
                        "contenttypes", 
827
                        "contenttype"
828
                    ], 
829
                    "target": [
830
                        "custom_user", 
831
                        "user"
832
                    ]
833
                }, 
834
                {
835
                    "operation": {
836
                        "slug": "search", 
837
                        "name": "Rechercher"
838
                    }, 
839
                    "ou": null, 
840
                    "target_ct": [
841
                        "a2_rbac", 
842
                        "organizationalunit"
843
                    ], 
844
                    "target": [
845
                        "ou-un"
846
                    ]
847
                }
848
            ], 
849
            "admin_scope_id": 17, 
850
            "ou": {
851
                "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
852
                "name": "ou un ", 
853
                "slug": "ou-un"
854
            }, 
855
            "description": "", 
856
            "admin_scope_ct_id": 36, 
857
            "external_id": "", 
858
            "slug": "_a2-administrateur-des-roles-ou-un", 
859
            "name": "R\u00f4les - ou un "
860
        }, 
861
        {
862
            "description": "role un descr", 
863
            "slug": "role-un", 
864
            "name": "role un", 
865
            "uuid": "8bf850a1d43e4df4a8162d4108366f5c", 
866
            "service": null, 
867
            "admin_scope_id": null, 
868
            "admin_scope_ct_id": null, 
869
            "parents": [
870
                {
871
                    "uuid": "aa83b6c8f7f2420ea855b6c06e5f7e80", 
872
                    "service": null, 
873
                    "admin_scope_id": 26, 
874
                    "ou": {
875
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
876
                        "name": "ou un ", 
877
                        "slug": "ou-un"
878
                    }, 
879
                    "description": "", 
880
                    "admin_scope_ct_id": 36, 
881
                    "external_id": "", 
882
                    "slug": "_a2-managers-of-role-role-un", 
883
                    "name": "Administrateur du r\u00f4le \u00ab\u00a0role un\u00a0\u00bb"
884
                }, 
885
                {
886
                    "uuid": "9e4f8189c5054c109ae25b8c633eca78", 
887
                    "service": null, 
888
                    "admin_scope_id": null, 
889
                    "ou": {
890
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
891
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
892
                        "slug": "default"
893
                    }, 
894
                    "description": "role deux descr", 
895
                    "admin_scope_ct_id": null, 
896
                    "external_id": "", 
897
                    "slug": "role-deux", 
898
                    "name": "role deux"
899
                }
900
            ], 
901
            "attributes": [
902
                {
903
                    "kind": "json", 
904
                    "name": "details", 
905
                    "value": "\"role un detail\""
906
                }, 
907
                {
908
                    "kind": "json", 
909
                    "name": "emails", 
910
                    "value": "[]"
911
                }, 
912
                {
913
                    "kind": "json", 
914
                    "name": "emails_to_members", 
915
                    "value": "true"
916
                }
917
            ], 
918
            "ou": {
919
                "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
920
                "name": "ou un ", 
921
                "slug": "ou-un"
922
            }, 
923
            "external_id": ""
924
        }, 
925
        {
926
            "uuid": "1b8d7e5f421640f69af5692e876b96d9", 
927
            "service": null, 
928
            "permissions": [
929
                {
930
                    "operation": {
931
                        "slug": "admin", 
932
                        "name": "Administration"
933
                    }, 
934
                    "ou": {
935
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
936
                        "name": "ou un ", 
937
                        "slug": "ou-un"
938
                    }, 
939
                    "target_ct": [
940
                        "contenttypes", 
941
                        "contenttype"
942
                    ], 
943
                    "target": [
944
                        "custom_user", 
945
                        "user"
946
                    ]
947
                }, 
948
                {
949
                    "operation": {
950
                        "slug": "search", 
951
                        "name": "Rechercher"
952
                    }, 
953
                    "ou": null, 
954
                    "target_ct": [
955
                        "a2_rbac", 
956
                        "organizationalunit"
957
                    ], 
958
                    "target": [
959
                        "ou-un"
960
                    ]
961
                }
962
            ], 
963
            "admin_scope_id": 20, 
964
            "ou": {
965
                "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
966
                "name": "ou un ", 
967
                "slug": "ou-un"
968
            }, 
969
            "description": "", 
970
            "admin_scope_ct_id": 36, 
971
            "external_id": "", 
972
            "slug": "_a2-administrateur-des-utilisateurs-ou-un", 
973
            "name": "Utilisateurs - ou un "
974
        }, 
975
        {
976
            "description": "", 
977
            "slug": "_a2-manager", 
978
            "permissions": [
979
                {
980
                    "operation": {
981
                        "slug": "change", 
982
                        "name": "Modifier"
983
                    }, 
984
                    "ou": null, 
985
                    "target_ct": [
986
                        "a2_rbac", 
987
                        "role"
988
                    ], 
989
                    "target": [
990
                        "_a2-manager", 
991
                        null, 
992
                        null
993
                    ]
994
                }
995
            ], 
996
            "uuid": "daf3857c77bd41f0b10fa94a6e360f84", 
997
            "service": null, 
998
            "admin_scope_id": null, 
999
            "admin_scope_ct_id": null, 
1000
            "name": "Administrateur", 
1001
            "parents": [
1002
                {
1003
                    "uuid": "5d1ccc95259b4a0694e9c66ae04293c3", 
1004
                    "service": null, 
1005
                    "admin_scope_id": 4, 
1006
                    "ou": null, 
1007
                    "description": "", 
1008
                    "admin_scope_ct_id": 36, 
1009
                    "external_id": "", 
1010
                    "slug": "_a2-administrateur-des-utilisateurs", 
1011
                    "name": "Administrateur des utilisateurs"
1012
                }, 
1013
                {
1014
                    "uuid": "ab0a5e998a9f47dcad9a90729d4087ea", 
1015
                    "service": null, 
1016
                    "admin_scope_id": 5, 
1017
                    "ou": null, 
1018
                    "description": "", 
1019
                    "admin_scope_ct_id": 36, 
1020
                    "external_id": "", 
1021
                    "slug": "_a2-administrateur-des-roles", 
1022
                    "name": "Administrateur des r\u00f4les"
1023
                }, 
1024
                {
1025
                    "uuid": "9a05246e3f5041b8935f98051aa1054f", 
1026
                    "service": null, 
1027
                    "admin_scope_id": 6, 
1028
                    "ou": null, 
1029
                    "description": "", 
1030
                    "admin_scope_ct_id": 36, 
1031
                    "external_id": "", 
1032
                    "slug": "_a2-administrateur-des-entites", 
1033
                    "name": "Administrateur des entit\u00e9s"
1034
                }
1035
            ], 
1036
            "ou": null, 
1037
            "external_id": ""
1038
        }, 
1039
        {
1040
            "description": "", 
1041
            "slug": "_a2-managers-of-default", 
1042
            "permissions": [
1043
                {
1044
                    "operation": {
1045
                        "slug": "view", 
1046
                        "name": "Visualisation"
1047
                    }, 
1048
                    "ou": null, 
1049
                    "target_ct": [
1050
                        "a2_rbac", 
1051
                        "organizationalunit"
1052
                    ], 
1053
                    "target": [
1054
                        "default"
1055
                    ]
1056
                }
1057
            ], 
1058
            "uuid": "b2f44cfdedcb48b996ab2885bf024e7c", 
1059
            "service": null, 
1060
            "admin_scope_id": 11, 
1061
            "admin_scope_ct_id": 36, 
1062
            "name": "Administrateur de la collectivit\u00e9 \u00ab\u00a0Collectivit\u00e9 par d\u00e9faut\u00a0\u00bb", 
1063
            "parents": [
1064
                {
1065
                    "uuid": "486cf6897b9743b18f74c7047cf054d9", 
1066
                    "service": null, 
1067
                    "admin_scope_id": 12, 
1068
                    "ou": {
1069
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
1070
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
1071
                        "slug": "default"
1072
                    }, 
1073
                    "description": "", 
1074
                    "admin_scope_ct_id": 36, 
1075
                    "external_id": "", 
1076
                    "slug": "_a2-administrateur-des-roles-default", 
1077
                    "name": "R\u00f4les - Collectivit\u00e9 par d\u00e9faut"
1078
                }, 
1079
                {
1080
                    "uuid": "d472b169995043ebbe6658b351a70f86", 
1081
                    "service": null, 
1082
                    "admin_scope_id": 15, 
1083
                    "ou": {
1084
                        "uuid": "ba60d9e6c2874636883bdd604b23eab2", 
1085
                        "name": "Collectivit\u00e9 par d\u00e9faut", 
1086
                        "slug": "default"
1087
                    }, 
1088
                    "description": "", 
1089
                    "admin_scope_ct_id": 36, 
1090
                    "external_id": "", 
1091
                    "slug": "_a2-administrateur-des-utilisateurs-default", 
1092
                    "name": "Utilisateurs - Collectivit\u00e9 par d\u00e9faut"
1093
                }
1094
            ], 
1095
            "ou": null, 
1096
            "external_id": ""
1097
        }, 
1098
        {
1099
            "description": "", 
1100
            "slug": "_a2-managers-of-ou-deux", 
1101
            "permissions": [
1102
                {
1103
                    "operation": {
1104
                        "slug": "view", 
1105
                        "name": "Visualisation"
1106
                    }, 
1107
                    "ou": null, 
1108
                    "target_ct": [
1109
                        "a2_rbac", 
1110
                        "organizationalunit"
1111
                    ], 
1112
                    "target": [
1113
                        "ou-deux"
1114
                    ]
1115
                }
1116
            ], 
1117
            "uuid": "341e1030af4846f9be8f3e520a876220", 
1118
            "service": null, 
1119
            "admin_scope_id": 21, 
1120
            "admin_scope_ct_id": 36, 
1121
            "name": "Administrateur de la collectivit\u00e9 \u00ab\u00a0ou deux\u00a0\u00bb", 
1122
            "parents": [
1123
                {
1124
                    "uuid": "69bd392d338f437eb908d16de3af9723", 
1125
                    "service": null, 
1126
                    "admin_scope_id": 22, 
1127
                    "ou": {
1128
                        "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
1129
                        "name": "ou deux", 
1130
                        "slug": "ou-deux"
1131
                    }, 
1132
                    "description": "", 
1133
                    "admin_scope_ct_id": 36, 
1134
                    "external_id": "", 
1135
                    "slug": "_a2-administrateur-des-roles-ou-deux", 
1136
                    "name": "R\u00f4les - ou deux"
1137
                }, 
1138
                {
1139
                    "uuid": "7ca8d6f933054021a179a3d86ca19efb", 
1140
                    "service": null, 
1141
                    "admin_scope_id": 25, 
1142
                    "ou": {
1143
                        "uuid": "dd13db24a1754ed19288dcb147f9e73a", 
1144
                        "name": "ou deux", 
1145
                        "slug": "ou-deux"
1146
                    }, 
1147
                    "description": "", 
1148
                    "admin_scope_ct_id": 36, 
1149
                    "external_id": "", 
1150
                    "slug": "_a2-administrateur-des-utilisateurs-ou-deux", 
1151
                    "name": "Utilisateurs - ou deux"
1152
                }
1153
            ], 
1154
            "ou": null, 
1155
            "external_id": ""
1156
        }, 
1157
        {
1158
            "description": "", 
1159
            "slug": "_a2-managers-of-ou-un", 
1160
            "permissions": [
1161
                {
1162
                    "operation": {
1163
                        "slug": "view", 
1164
                        "name": "Visualisation"
1165
                    }, 
1166
                    "ou": null, 
1167
                    "target_ct": [
1168
                        "a2_rbac", 
1169
                        "organizationalunit"
1170
                    ], 
1171
                    "target": [
1172
                        "ou-un"
1173
                    ]
1174
                }
1175
            ], 
1176
            "uuid": "79a890b193914797b5699f5b43ccda01", 
1177
            "service": null, 
1178
            "admin_scope_id": 16, 
1179
            "admin_scope_ct_id": 36, 
1180
            "name": "Administrateur de la collectivit\u00e9 \u00ab\u00a0ou un \u00a0\u00bb", 
1181
            "parents": [
1182
                {
1183
                    "uuid": "241492ead56c4773abf97271ea88b0a9", 
1184
                    "service": null, 
1185
                    "admin_scope_id": 17, 
1186
                    "ou": {
1187
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
1188
                        "name": "ou un ", 
1189
                        "slug": "ou-un"
1190
                    }, 
1191
                    "description": "", 
1192
                    "admin_scope_ct_id": 36, 
1193
                    "external_id": "", 
1194
                    "slug": "_a2-administrateur-des-roles-ou-un", 
1195
                    "name": "R\u00f4les - ou un "
1196
                }, 
1197
                {
1198
                    "uuid": "1b8d7e5f421640f69af5692e876b96d9", 
1199
                    "service": null, 
1200
                    "admin_scope_id": 20, 
1201
                    "ou": {
1202
                        "uuid": "0c2eb94896e4440bb2ca53091948cdf4", 
1203
                        "name": "ou un ", 
1204
                        "slug": "ou-un"
1205
                    }, 
1206
                    "description": "", 
1207
                    "admin_scope_ct_id": 36, 
1208
                    "external_id": "", 
1209
                    "slug": "_a2-administrateur-des-utilisateurs-ou-un", 
1210
                    "name": "Utilisateurs - ou un "
1211
                }
1212
            ], 
1213
            "ou": null, 
1214
            "external_id": ""
1215
        }, 
1216
        {
1217
            "uuid": "9a05246e3f5041b8935f98051aa1054f", 
1218
            "service": null, 
1219
            "permissions": [
1220
                {
1221
                    "operation": {
1222
                        "slug": "admin", 
1223
                        "name": "Administration"
1224
                    }, 
1225
                    "ou": null, 
1226
                    "target_ct": [
1227
                        "contenttypes", 
1228
                        "contenttype"
1229
                    ], 
1230
                    "target": [
1231
                        "a2_rbac", 
1232
                        "organizationalunit"
1233
                    ]
1234
                }, 
1235
                {
1236
                    "operation": {
1237
                        "slug": "search", 
1238
                        "name": "Rechercher"
1239
                    }, 
1240
                    "ou": null, 
1241
                    "target_ct": [
1242
                        "contenttypes", 
1243
                        "contenttype"
1244
                    ], 
1245
                    "target": [
1246
                        "a2_rbac", 
1247
                        "organizationalunit"
1248
                    ]
1249
                }
1250
            ], 
1251
            "admin_scope_id": 6, 
1252
            "ou": null, 
1253
            "description": "", 
1254
            "admin_scope_ct_id": 36, 
1255
            "external_id": "", 
1256
            "slug": "_a2-administrateur-des-entites", 
1257
            "name": "Administrateur des entit\u00e9s"
1258
        }, 
1259
        {
1260
            "uuid": "ab0a5e998a9f47dcad9a90729d4087ea", 
1261
            "service": null, 
1262
            "permissions": [
1263
                {
1264
                    "operation": {
1265
                        "slug": "admin", 
1266
                        "name": "Administration"
1267
                    }, 
1268
                    "ou": null, 
1269
                    "target_ct": [
1270
                        "contenttypes", 
1271
                        "contenttype"
1272
                    ], 
1273
                    "target": [
1274
                        "a2_rbac", 
1275
                        "role"
1276
                    ]
1277
                }, 
1278
                {
1279
                    "operation": {
1280
                        "slug": "view", 
1281
                        "name": "Visualisation"
1282
                    }, 
1283
                    "ou": null, 
1284
                    "target_ct": [
1285
                        "contenttypes", 
1286
                        "contenttype"
1287
                    ], 
1288
                    "target": [
1289
                        "custom_user", 
1290
                        "user"
1291
                    ]
1292
                }, 
1293
                {
1294
                    "operation": {
1295
                        "slug": "search", 
1296
                        "name": "Rechercher"
1297
                    }, 
1298
                    "ou": null, 
1299
                    "target_ct": [
1300
                        "contenttypes", 
1301
                        "contenttype"
1302
                    ], 
1303
                    "target": [
1304
                        "a2_rbac", 
1305
                        "organizationalunit"
1306
                    ]
1307
                }
1308
            ], 
1309
            "admin_scope_id": 5, 
1310
            "ou": null, 
1311
            "description": "", 
1312
            "admin_scope_ct_id": 36, 
1313
            "external_id": "", 
1314
            "slug": "_a2-administrateur-des-roles", 
1315
            "name": "Administrateur des r\u00f4les"
1316
        }, 
1317
        {
1318
            "uuid": "5d1ccc95259b4a0694e9c66ae04293c3", 
1319
            "service": null, 
1320
            "permissions": [
1321
                {
1322
                    "operation": {
1323
                        "slug": "admin", 
1324
                        "name": "Administration"
1325
                    }, 
1326
                    "ou": null, 
1327
                    "target_ct": [
1328
                        "contenttypes", 
1329
                        "contenttype"
1330
                    ], 
1331
                    "target": [
1332
                        "custom_user", 
1333
                        "user"
1334
                    ]
1335
                }, 
1336
                {
1337
                    "operation": {
1338
                        "slug": "search", 
1339
                        "name": "Rechercher"
1340
                    }, 
1341
                    "ou": null, 
1342
                    "target_ct": [
1343
                        "contenttypes", 
1344
                        "contenttype"
1345
                    ], 
1346
                    "target": [
1347
                        "a2_rbac", 
1348
                        "organizationalunit"
1349
                    ]
1350
                }
1351
            ], 
1352
            "admin_scope_id": 4, 
1353
            "ou": null, 
1354
            "description": "", 
1355
            "admin_scope_ct_id": 36, 
1356
            "external_id": "", 
1357
            "slug": "_a2-administrateur-des-utilisateurs", 
1358
            "name": "Administrateur des utilisateurs"
1359
        }
1360
    ]
1361
}
tests/test_a2_rbac.py
1 1
import pytest
2 2

  
3
from django.contrib.contenttypes.models import ContentType
4
from django_rbac.utils import get_permission_model
5
from django_rbac.models import Operation
6
from authentic2.a2_rbac.models import Role, OrganizationalUnit as OU, RoleAttribute
3 7
from authentic2.models import Service
4
from authentic2.a2_rbac.models import Role, OrganizationalUnit as OU
8
from authentic2.utils import get_hex_uuid
5 9

  
6 10

  
7 11
def test_role_natural_key(db):
......
24 28
        Role.objects.get_by_natural_key(*r2.natural_key())
25 29
    with pytest.raises(Role.DoesNotExist):
26 30
        Role.objects.get_by_natural_key(*r4.natural_key())
31

  
32

  
33
def test_basic_role_export_json(db):
34
    role = Role.objects.create(
35
        name='basic role', slug='basic-role', description='basic role description')
36
    role_dict = role.export_json()
37
    assert role_dict['name'] == role.name
38
    assert role_dict['slug'] == role.slug
39
    assert role_dict['uuid'] == role.uuid
40
    assert role_dict['description'] == role.description
41
    assert role_dict['external_id'] == role.external_id
42
    assert role_dict['ou'] is None
43
    assert role_dict['service'] is None
44

  
45

  
46
def test_role_with_ou_export_json(db):
47
    ou = OU.objects.create(name='ou', slug='ou')
48
    role = Role.objects.create(name='some role', ou=ou)
49
    role_dict = role.export_json()
50
    assert role_dict['ou'] == {'uuid': ou.uuid, 'slug': ou.slug,  'name': ou.name}
51

  
52

  
53
def test_role_with_service_export_json(db):
54
    service = Service.objects.create(name='service name', slug='service-name')
55
    role = Role.objects.create(name='some role', service=service)
56
    role_dict = role.export_json()
57
    assert role_dict['service'] == {'slug': service.slug,  'ou': None}
58

  
59

  
60
def test_role_with_service_with_ou_export_json(db):
61
    ou = OU.objects.create(name='ou', slug='ou')
62
    service = Service.objects.create(name='service name', slug='service-name', ou=ou)
63
    role = Role.objects.create(name='some role', service=service)
64
    role_dict = role.export_json()
65
    assert role_dict['service'] == {
66
        'slug': service.slug,  'ou': {'uuid': ou.uuid, 'slug': 'ou', 'name': 'ou'}}
67

  
68

  
69
def test_role_with_attributes_export_json(db):
70
    role = Role.objects.create(name='some role')
71
    attr1 = RoleAttribute.objects.create(
72
        role=role, name='attr1_name', kind='string', value='attr1_value')
73
    attr2 = RoleAttribute.objects.create(
74
        role=role, name='attr2_name', kind='string', value='attr2_value')
75

  
76
    role_dict = role.export_json(attributes=True)
77
    attributes = role_dict['attributes']
78
    assert len(attributes) == 2
79

  
80
    expected_attr_names = set([attr1.name, attr2.name])
81
    for attr_dict in attributes:
82
        assert attr_dict['name'] in expected_attr_names
83
        expected_attr_names.remove(attr_dict['name'])
84
        target_attr = RoleAttribute.objects.filter(name=attr_dict['name']).first()
85
        assert attr_dict['kind'] == target_attr.kind
86
        assert attr_dict['value'] == target_attr.value
87

  
88

  
89
def test_role_with_parents_export_json(db):
90
    grand_parent_role = Role.objects.create(
91
        name='test grand parent role', slug='test-grand-parent-role')
92
    parent_1_role = Role.objects.create(
93
        name='test parent 1 role', slug='test-parent-1-role')
94
    parent_1_role.add_parent(grand_parent_role)
95
    parent_2_role = Role.objects.create(
96
        name='test parent 2 role', slug='test-parent-2-role')
97
    parent_2_role.add_parent(grand_parent_role)
98
    child_role = Role.objects.create(
99
        name='test child role', slug='test-child-role')
100
    child_role.add_parent(parent_1_role)
101
    child_role.add_parent(parent_2_role)
102

  
103
    child_role_dict = child_role.export_json(parents=True)
104
    assert child_role_dict['slug'] == child_role.slug
105
    parents = child_role_dict['parents']
106
    assert len(parents) == 2
107
    expected_slugs = set([parent_1_role.slug, parent_2_role.slug])
108
    for parent in parents:
109
        assert parent['slug'] in expected_slugs
110
        expected_slugs.remove(parent['slug'])
111

  
112
    grand_parent_role_dict = grand_parent_role.export_json(parents=True)
113
    assert grand_parent_role_dict['slug'] == grand_parent_role.slug
114
    assert 'parents' not in grand_parent_role_dict
115

  
116
    parent_1_role_dict = parent_1_role.export_json(parents=True)
117
    assert parent_1_role_dict['slug'] == parent_1_role.slug
118
    parents = parent_1_role_dict['parents']
119
    assert len(parents) == 1
120
    assert parents[0]['slug'] == grand_parent_role.slug
121

  
122
    parent_2_role_dict = parent_2_role.export_json(parents=True)
123
    assert parent_2_role_dict['slug'] == parent_2_role.slug
124
    parents = parent_2_role_dict['parents']
125
    assert len(parents) == 1
126
    assert parents[0]['slug'] == grand_parent_role.slug
127

  
128

  
129
def test_role_with_permission_export_json(db):
130
    some_ou = OU.objects.create(name='some ou', slug='some-ou')
131
    role = Role.objects.create(name='role name', slug='role-slug')
132
    other_role = Role.objects.create(
133
        name='other role name', slug='other-role-slug', uuid=get_hex_uuid(), ou=some_ou)
134
    ou = OU.objects.create(name='basic ou', slug='basic-ou', description='basic ou description')
135
    Permission = get_permission_model()
136
    op = Operation.objects.first()
137
    perm_saml = Permission.objects.create(
138
        operation=op, ou=ou,
139
        target_ct=ContentType.objects.get_for_model(ContentType),
140
        target_id=ContentType.objects.get(app_label="saml", model="libertyprovider").pk)
141
    role.permissions.add(perm_saml)
142
    perm_role = Permission.objects.create(
143
        operation=op, ou=None,
144
        target_ct=ContentType.objects.get_for_model(Role),
145
        target_id=other_role.pk)
146
    role.permissions.add(perm_role)
147

  
148
    export = role.export_json(permissions=True)
149
    permissions = export['permissions']
150
    assert len(permissions) == 2
151
    assert permissions[0] == {
152
        'operation': {'slug': 'add'},
153
        'ou': {'uuid': ou.uuid, 'slug': ou.slug, 'name': ou.name},
154
        'target_ct': {'app_label': u'contenttypes', 'model': u'contenttype'},
155
        'target': {'model': u'libertyprovider', 'app_label': u'saml'}
156
    }
157
    assert permissions[1] == {
158
        'operation': {'slug': 'add'},
159
        'ou': None,
160
        'target_ct': {'app_label': u'a2_rbac', 'model': u'role'},
161
        'target': {
162
            'slug': u'other-role-slug', 'service': None, 'uuid': other_role.uuid,
163
            'ou': {
164
                'slug': u'some-ou', 'uuid': some_ou.uuid, 'name': u'some ou'
165
            },
166
            'name': u'other role name'}
167
    }
168

  
169

  
170
def test_ou_export_json(db):
171
    ou = OU.objects.create(
172
        name='basic ou', slug='basic-ou', description='basic ou description',
173
        username_is_unique=True, email_is_unique=True, default=False, validate_emails=True)
174
    ou_dict = ou.export_json()
175
    assert ou_dict['name'] == ou.name
176
    assert ou_dict['slug'] == ou.slug
177
    assert ou_dict['uuid'] == ou.uuid
178
    assert ou_dict['description'] == ou.description
179
    assert ou_dict['username_is_unique'] == ou.username_is_unique
180
    assert ou_dict['email_is_unique'] == ou.email_is_unique
181
    assert ou_dict['default'] == ou.default
182
    assert ou_dict['validate_emails'] == ou.validate_emails
tests/test_data_transfer.py
1
import json
2

  
3
from django_rbac.utils import get_role_model, get_ou_model
4
import py
5
import pytest
6

  
7
from authentic2.a2_rbac.models import RoleParenting
8
from authentic2.data_transfer import (
9
    DataImportError, export_roles, import_site, export_ou, ImportContext,
10
    RoleDeserializer, search_role, import_ou)
11
from authentic2.utils import get_hex_uuid
12

  
13

  
14
Role = get_role_model()
15
OU = get_ou_model()
16

  
17

  
18
def test_export_basic_role(db):
19
    role = Role.objects.create(name='basic role', slug='basic-role', uuid=get_hex_uuid())
20
    query_set = Role.objects.filter(uuid=role.uuid)
21
    roles = export_roles(query_set)
22
    assert len(roles) == 1
23
    role_dict = roles[0]
24
    for key, value in role.export_json().items():
25
        assert role_dict[key] == value
26

  
27

  
28
def test_export_role_with_parents(db):
29
    grand_parent_role = Role.objects.create(
30
        name='test grand parent role', slug='test-grand-parent-role', uuid=get_hex_uuid())
31
    parent_1_role = Role.objects.create(
32
        name='test parent 1 role', slug='test-parent-1-role', uuid=get_hex_uuid())
33
    parent_1_role.add_parent(grand_parent_role)
34
    parent_2_role = Role.objects.create(
35
        name='test parent 2 role', slug='test-parent-2-role', uuid=get_hex_uuid())
36
    parent_2_role.add_parent(grand_parent_role)
37
    child_role = Role.objects.create(
38
        name='test child role', slug='test-child-role', uuid=get_hex_uuid())
39
    child_role.add_parent(parent_1_role)
40
    child_role.add_parent(parent_2_role)
41

  
42
    query_set = Role.objects.filter(slug__startswith='test').order_by('slug')
43
    roles = export_roles(query_set)
44
    assert len(roles) == 4
45

  
46
    child_role_dict = roles[0]
47
    assert child_role_dict['slug'] == child_role.slug
48
    parents = child_role_dict['parents']
49
    assert len(parents) == 2
50
    expected_slugs = set([parent_1_role.slug, parent_2_role.slug])
51
    for parent in parents:
52
        assert parent['slug'] in expected_slugs
53
        expected_slugs.remove(parent['slug'])
54

  
55
    grand_parent_role_dict = roles[1]
56
    assert grand_parent_role_dict['slug'] == grand_parent_role.slug
57

  
58
    parent_1_role_dict = roles[2]
59
    assert parent_1_role_dict['slug'] == parent_1_role.slug
60
    parents = parent_1_role_dict['parents']
61
    assert len(parents) == 1
62
    assert parents[0]['slug'] == grand_parent_role.slug
63

  
64
    parent_2_role_dict = roles[3]
65
    assert parent_2_role_dict['slug'] == parent_2_role.slug
66
    parents = parent_2_role_dict['parents']
67
    assert len(parents) == 1
68
    assert parents[0]['slug'] == grand_parent_role.slug
69

  
70

  
71
def test_export_ou(db):
72
    ou = OU.objects.create(name='ou name', slug='ou-slug', description='ou description')
73
    ous = export_ou(OU.objects.filter(name='ou name'))
74
    assert len(ous) == 1
75
    ou_d = ous[0]
76
    assert ou_d['name'] == ou.name
77
    assert ou_d['slug'] == ou.slug
78
    assert ou_d['description'] == ou.description
79

  
80

  
81
def test_search_role_by_uuid(db):
82
    uuid = get_hex_uuid()
83
    role_d = {'uuid': uuid, 'slug': 'role-slug'}
84
    role = Role.objects.create(**role_d)
85
    assert role == search_role({'uuid': uuid, 'slug': 'other-role-slug'})
86

  
87

  
88
def test_search_role_by_slug(db):
89
    role_d = {'uuid': get_hex_uuid(), 'slug': 'role-slug'}
90
    role = Role.objects.create(**role_d)
91
    assert role == search_role({
92
        'uuid': get_hex_uuid(), 'slug': 'role-slug',
93
        'ou': None, 'service': None})
94

  
95

  
96
def test_search_role_not_found(db):
97
    assert search_role(
98
        {
99
            'uuid': get_hex_uuid(), 'slug': 'role-slug', 'name': 'role name',
100
            'ou': None, 'service': None}) is None
101

  
102

  
103
def test_search_role_slug_not_unique(db):
104
    role1_d = {'uuid': get_hex_uuid(), 'slug': 'role-slug', 'name': 'role name'}
105
    role2_d = {'uuid': get_hex_uuid(), 'slug': 'role-slug', 'name': 'role name'}
106
    ou = OU.objects.create(name='some ou', slug='some-ou')
107
    role1 = Role.objects.create(ou=ou, **role1_d)
108
    Role.objects.create(**role2_d)
109
    assert role1 == search_role(role1.export_json())
110

  
111

  
112
def test_role_deserializer(db):
113
    rd = RoleDeserializer({
114
        'name': 'some role', 'description': 'some role description', 'slug': 'some-role',
115
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}, ImportContext())
116
    assert rd._parents is None
117
    assert rd._attributes is None
118
    assert rd._obj is None
119
    role, status = rd.deserialize()
120
    assert status == 'created'
121
    assert role.name == 'some role'
122
    assert role.description == 'some role description'
123
    assert role.slug == 'some-role'
124
    assert rd._obj == role
125

  
126

  
127
def test_role_deserializer_with_ou(db):
128
    ou = OU.objects.create(name='some ou', slug='some-ou')
129
    rd = RoleDeserializer({
130
        'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'some role description',
131
        'slug': 'some-role', 'ou': {'slug': 'some-ou'}, 'service': None}, ImportContext())
132
    role, status = rd.deserialize()
133
    assert role.ou == ou
134

  
135

  
136
def test_role_deserializer_missing_ou(db):
137
    rd = RoleDeserializer({
138
        'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'role description',
139
        'slug': 'some-role', 'ou': {'slug': 'some-ou'}, 'service': None},
140
            ImportContext())
141
    with pytest.raises(DataImportError):
142
        rd.deserialize()
143

  
144

  
145
def test_role_deserializer_update_ou(db):
146
    ou1 = OU.objects.create(name='ou 1', slug='ou-1')
147
    ou2 = OU.objects.create(name='ou 2', slug='ou-2')
148
    uuid = get_hex_uuid()
149
    existing_role = Role.objects.create(uuid=uuid, slug='some-role', ou=ou1)
150
    rd = RoleDeserializer({
151
        'uuid': uuid, 'name': 'some-role', 'slug': 'some-role',
152
        'ou': {'slug': 'ou-2'}, 'service': None}, ImportContext())
153
    role, status = rd.deserialize()
154
    assert role == existing_role
155
    assert role.ou == ou2
156

  
157

  
158
def test_role_deserializer_update_fields(db):
159
    uuid = get_hex_uuid()
160
    existing_role = Role.objects.create(uuid=uuid, slug='some-role', name='some role')
161
    rd = RoleDeserializer({
162
        'uuid': uuid, 'slug': 'some-role', 'name': 'some role changed',
163
        'ou': None, 'service': None}, ImportContext())
164
    role, status = rd.deserialize()
165
    assert role == existing_role
166
    assert role.name == 'some role changed'
167

  
168

  
169
def test_role_deserializer_with_attributes(db):
170

  
171
    attributes_data = {
172
        'attr1_name': dict(name='attr1_name', kind='string', value='attr1_value'),
173
        'attr2_name': dict(name='attr2_name', kind='string', value='attr2_value')
174
    }
175
    rd = RoleDeserializer({
176
        'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'some role description',
177
        'slug': 'some-role', 'attributes': list(attributes_data.values()),
178
        'ou': None, 'service': None}, ImportContext())
179
    role, status = rd.deserialize()
180
    created, deleted = rd.attributes()
181
    assert role.attributes.count() == 2
182
    assert len(created) == 2
183

  
184
    for attr in created:
185
        attr_dict = attributes_data[attr.name]
186
        assert attr_dict['name'] == attr.name
187
        assert attr_dict['kind'] == attr.kind
188
        assert attr_dict['value'] == attr.value
189
        del attributes_data[attr.name]
190

  
191

  
192
def test_role_deserializer_creates_admin_role(db):
193
    role_dict = {
194
        'name': 'some role', 'slug': 'some-role', 'uuid': get_hex_uuid(),
195
        'ou': None, 'service': None}
196
    rd = RoleDeserializer(role_dict, ImportContext())
197
    rd.deserialize()
198
    Role.objects.get(slug='_a2-managers-of-role-some-role')
199

  
200

  
201
def test_role_deserializer_parenting_existing_parent(db):
202
    parent_role_dict = {
203
        'name': 'grand parent role', 'slug': 'grand-parent-role', 'uuid': get_hex_uuid(),
204
        'ou': None, 'service': None}
205
    parent_role = Role.objects.create(**parent_role_dict)
206
    child_role_dict = {
207
        'name': 'child role', 'slug': 'child-role', 'parents': [parent_role_dict],
208
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
209

  
210
    rd = RoleDeserializer(child_role_dict, ImportContext())
211
    child_role, status = rd.deserialize()
212
    created, deleted = rd.parentings()
213

  
214
    assert len(created) == 1
215
    parenting = created[0]
216
    assert parenting.direct is True
217
    assert parenting.parent == parent_role
218
    assert parenting.child == child_role
219

  
220

  
221
def test_role_deserializer_parenting_non_existing_parent(db):
222
    parent_role_dict = {
223
        'name': 'grand parent role', 'slug': 'grand-parent-role', 'uuid': get_hex_uuid(),
224
        'ou': None, 'service': None}
225
    child_role_dict = {
226
        'name': 'child role', 'slug': 'child-role', 'parents': [parent_role_dict],
227
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
228
    rd = RoleDeserializer(child_role_dict, ImportContext())
229
    rd.deserialize()
230
    with pytest.raises(DataImportError) as excinfo:
231
        rd.parentings()
232

  
233
    assert "Could not find role" in str(excinfo.value)
234

  
235

  
236
def test_role_deserializer_permissions(db):
237
    ou = OU.objects.create(slug='some-ou')
238
    other_role_dict = {
239
        'name': 'other role', 'slug': 'other-role-slug', 'uuid': get_hex_uuid(), 'ou': ou}
240
    other_role = Role.objects.create(**other_role_dict)
241
    other_role_dict['permisison'] = {
242
        "operation": {
243
            "slug": "admin"
244
        },
245
        "ou": {
246
            "slug": "default",
247
            "name": "Collectivit\u00e9 par d\u00e9faut"
248
        },
249
        'target_ct': {'app_label': u'a2_rbac', 'model': u'role'},
250
        "target": {
251
            "slug": "role-deux",
252
            "ou": {
253
                "slug": "default",
254
                "name": "Collectivit\u00e9 par d\u00e9faut"
255
            },
256
            "service": None,
257
            "name": "role deux"
258
        }
259
    }
260
    some_role_dict = {
261
        'name': 'some role', 'slug': 'some-role', 'uuid': get_hex_uuid(),
262
        'ou': None, 'service': None}
263
    some_role_dict['permissions'] = [
264
        {
265
            'operation': {'slug': 'add'},
266
            'ou': None,
267
            'target_ct': {'app_label': u'a2_rbac', 'model': u'role'},
268
            'target': {
269
                "slug": u'other-role-slug', 'ou': {'slug': 'some-ou'}, 'service': None}
270
        }
271
    ]
272

  
273
    import_context = ImportContext()
274
    rd = RoleDeserializer(some_role_dict, import_context)
275
    rd.deserialize()
276
    perm_created, perm_deleted = rd.permissions()
277

  
278
    assert len(perm_created) == 1
279
    assert len(perm_deleted) == 0
280
    del some_role_dict['permissions']
281
    role = Role.objects.get(slug=some_role_dict['slug'])
282
    assert role.permissions.count() == 1
283
    perm = role.permissions.first()
284
    assert perm.operation.slug == 'add'
285
    assert not perm.ou
286
    assert perm.target == other_role
287

  
288
    # that one should delete permissions
289
    rd = RoleDeserializer(some_role_dict, import_context)
290
    role, _ = rd.deserialize()
291
    perm_created, perm_deleted = rd.permissions()
292
    assert role.permissions.count() == 0
293
    assert len(perm_created) == 0
294
    assert len(perm_deleted) == 1
295

  
296

  
297
def test_permission_on_role(db):
298
    perm_ou = OU.objects.create(slug='perm-ou', name='perm ou')
299
    perm_role = Role.objects.create(slug='perm-role', ou=perm_ou, name='perm role')
300

  
301
    some_role_dict = {
302
        'name': 'some role', 'slug': 'some-role-slug', 'ou': None, 'service': None}
303
    some_role_dict['permissions'] = [{
304
        "operation": {
305
            "slug": "admin"
306
        },
307
        "ou": {
308
            "slug": "perm-ou",
309
            "name": "perm-ou"
310
        },
311
        'target_ct': {'app_label': u'a2_rbac', 'model': u'role'},
312
        "target": {
313
            "slug": "perm-role",
314
            "ou": {
315
                "slug": "perm-ou",
316
                "name": "perm ou"
317
            },
318
            "service": None,
319
            "name": "perm role"
320
        }
321
    }]
322

  
323
    import_context = ImportContext()
324
    rd = RoleDeserializer(some_role_dict, import_context)
325
    rd.deserialize()
326
    perm_created, perm_deleted = rd.permissions()
327
    assert len(perm_created) == 1
328
    perm = perm_created[0]
329
    assert perm.target == perm_role
330
    assert perm.ou == perm_ou
331
    assert perm.operation.slug == 'admin'
332

  
333

  
334
def test_permission_on_contentype(db):
335
    perm_ou = OU.objects.create(slug='perm-ou', name='perm ou')
336
    some_role_dict = {
337
        'name': 'some role', 'slug': 'some-role-slug', 'ou': None, 'service': None}
338
    some_role_dict['permissions'] = [{
339
        "operation": {
340
            "slug": "admin"
341
        },
342
        "ou": {
343
            "slug": "perm-ou",
344
            "name": "perm-ou"
345
        },
346
        'target_ct': {"model": "contenttype", "app_label": "contenttypes"},
347
        "target": {"model": "logentry", "app_label": "admin"}
348
    }]
349

  
350
    import_context = ImportContext()
351
    rd = RoleDeserializer(some_role_dict, import_context)
352
    rd.deserialize()
353
    perm_created, perm_deleted = rd.permissions()
354
    assert len(perm_created) == 1
355
    perm = perm_created[0]
356
    assert perm.target.app_label == 'admin'
357
    assert perm.target.model == 'logentry'
358
    assert perm.ou == perm_ou
359

  
360

  
361
def import_ou_created(db):
362
    uuid = get_hex_uuid()
363
    ou_d = {'uuid': uuid, 'slug': 'ou-slug', 'name': 'ou name'}
364
    ou, status = import_ou(ou_d)
365
    assert status == 'created'
366
    assert ou.uuid == ou_d['uuid']
367
    assert ou.slug == ou_d['slug']
368
    assert ou.name == ou_d['name']
369

  
370

  
371
def import_ou_updated(db):
372
    ou = OU.objects.create(slug='some-ou', name='ou name')
373
    ou_d = {'uuid': ou.uuid, 'slug': ou.slug, 'name': 'new name'}
374
    ou_updated, status = import_ou(ou_d)
375
    assert status == 'updated'
376
    assert ou == ou_updated
377
    assert ou.name == 'new name'
378

  
379

  
380
def testi_import_site_empty():
381
    res = import_site({}, ImportContext())
382
    assert res.roles == {'created': [], 'updated': []}
383
    assert res.ous == {'created': [], 'updated': []}
384
    assert res.parentings == {'created': [], 'deleted': []}
385

  
386

  
387
def test_import_site_roles(db):
388
    parent_role_dict = {
389
        'name': 'grand parent role', 'slug': 'grand-parent-role', 'uuid': get_hex_uuid(),
390
        'ou': None, 'service': None}
391
    child_role_dict = {
392
        'name': 'child role', 'slug': 'child-role', 'parents': [parent_role_dict],
393
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
394
    roles = [
395
        parent_role_dict,
396
        child_role_dict
397
    ]
398
    res = import_site({'roles': roles}, ImportContext())
399
    created_roles = res.roles['created']
400
    assert len(created_roles) == 2
401
    parent_role = Role.objects.get(**parent_role_dict)
402
    del child_role_dict['parents']
403
    child_role = Role.objects.get(**child_role_dict)
404
    assert created_roles[0] == parent_role
405
    assert created_roles[1] == child_role
406

  
407
    assert len(res.parentings['created']) == 1
408
    assert res.parentings['created'][0] == RoleParenting.objects.get(
409
        child=child_role, parent=parent_role, direct=True)
410

  
411

  
412
def test_roles_import_ignore_technical_role(db):
413
    roles = [{
414
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
415
    res = import_site({'roles': roles}, ImportContext())
416
    assert res.roles == {'created': [], 'updated': []}
417

  
418

  
419
def test_roles_import_ignore_technical_role_with_service(db):
420
    roles = [{
421
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
422
    res = import_site({'roles': roles}, ImportContext())
423
    assert res.roles == {'created': [], 'updated': []}
424

  
425

  
426
def test_import_role_handle_manager_role_parenting(db):
427
    parent_role_dict = {
428
        'name': 'grand parent role', 'slug': 'grand-parent-role', 'uuid': get_hex_uuid(),
429
        'ou': None, 'service': None}
430
    parent_role_manager_dict = {
431
        'name': 'Administrateur du role grand parent role',
432
        'slug': '_a2-managers-of-role-grand-parent-role', 'uuid': get_hex_uuid(),
433
        'ou': None, 'service': None}
434
    child_role_dict = {
435
        'name': 'child role', 'slug': 'child-role',
436
        'parents': [parent_role_dict, parent_role_manager_dict],
437
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
438
    import_site({'roles': [child_role_dict, parent_role_dict]}, ImportContext())
439
    child = Role.objects.get(slug='child-role')
440
    manager = Role.objects.get(slug='_a2-managers-of-role-grand-parent-role')
441
    RoleParenting.objects.get(child=child, parent=manager, direct=True)
442

  
443

  
444
def test_import_roles_role_delete_orphans(db):
445
    roles = [{
446
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
447
    with pytest.raises(DataImportError):
448
        import_site({'roles': roles}, ImportContext(role_delete_orphans=True))
449

  
450

  
451
def test_import_ou(db):
452
    uuid = get_hex_uuid()
453
    name = 'ou name'
454
    ous = [{'uuid': uuid, 'slug': 'ou-slug', 'name': name}]
455
    res = import_site({'ous': ous}, ImportContext())
456
    assert len(res.ous['created']) == 1
457
    ou = res.ous['created'][0]
458
    assert ou.uuid == uuid
459
    assert ou.name == name
460
    Role.objects.get(slug='_a2-managers-of-ou-slug')
461

  
462

  
463
def test_import_ou_already_existing(db):
464
    uuid = get_hex_uuid()
465
    ou_d = {'uuid': uuid, 'slug': 'ou-slug', 'name': 'ou name'}
466
    ou = OU.objects.create(**ou_d)
467
    num_ous = OU.objects.count()
468
    res = import_site({'ous': [ou_d]}, ImportContext())
469
    assert len(res.ous['created']) == 0
470
    assert num_ous == OU.objects.count()
471
    assert ou == OU.objects.get(uuid=uuid)
tests/test_import_export_site_cmd.py
1
import __builtin__
2
import json
3

  
4
from django.core import management
5
import pytest
6

  
7
from django_rbac.utils import get_role_model
8

  
9

  
10
def dummy_export_site(*args):
11
    return {'roles': [{'name': 'role1'}]}
12

  
13

  
14
def test_export_role_cmd_stdout(db, capsys, monkeypatch):
15
    import authentic2.management.commands.export_site
16
    monkeypatch.setattr(
17
        authentic2.management.commands.export_site, 'export_site', dummy_export_site)
18
    management.call_command('export_site')
19
    out, err = capsys.readouterr()
20
    assert json.loads(out) == dummy_export_site()
21

  
22

  
23
def test_export_role_cmd_to_file(db, monkeypatch, tmpdir):
24
    import authentic2.management.commands.export_site
25
    monkeypatch.setattr(
26
        authentic2.management.commands.export_site, 'export_site', dummy_export_site)
27
    outfile = tmpdir.join('export.json')
28
    management.call_command('export_site', '--output', outfile.strpath)
29
    with outfile.open('r') as f:
30
        assert json.loads(f.read()) == dummy_export_site()
31

  
32

  
33
def test_import_site_cmd(db, tmpdir, monkeypatch):
34
    export_file = tmpdir.join('roles-export.json')
35
    with export_file.open('w'):
36
        export_file.write(json.dumps({'roles': []}))
37
    management.call_command('import_site', export_file.strpath)
38

  
39

  
40
def test_import_site_cmd_infos_on_stdout(db, tmpdir, monkeypatch, capsys):
41
    export_file = tmpdir.join('roles-export.json')
42
    with export_file.open('w'):
43
        export_file.write(json.dumps(
44
            {'roles': [{
45
                'uuid': 'dqfewrvesvews2532', 'slug': 'role-slug', 'name': 'role-name',
46
                'ou': None, 'service': None}]}))
47

  
48
    management.call_command('import_site', export_file.strpath)
49

  
50
    out, err = capsys.readouterr()
51
    assert "Real run" in out
52
    assert "1 roles created" in out
53
    assert "0 roles updated" in out
54

  
55

  
56
def test_import_site_transaction_rollback_on_error(db, tmpdir, monkeypatch, capsys):
57
    export_file = tmpdir.join('roles-export.json')
58
    with export_file.open('w'):
59
        export_file.write(json.dumps({'roles': []}))
60

  
61
    Role = get_role_model()
62

  
63
    def exception_import_site(*args):
64
        Role.objects.create(slug='role-slug')
65
        raise Exception()
66

  
67
    import authentic2.management.commands.import_site
68
    monkeypatch.setattr(
69
        authentic2.management.commands.import_site, 'import_site', exception_import_site)
70

  
71
    with pytest.raises(Exception):
72
        management.call_command('import_site', export_file.strpath)
73

  
74
    with pytest.raises(Role.DoesNotExist):
75
        Role.objects.get(slug='role-slug')
76

  
77

  
78
def test_import_site_transaction_rollback_on_dry_run(db, tmpdir, monkeypatch, capsys):
79
    export_file = tmpdir.join('roles-export.json')
80
    with export_file.open('w'):
81
        export_file.write(json.dumps(
82
            {'roles': [{
83
                'uuid': 'dqfewrvesvews2532', 'slug': 'role-slug', 'name': 'role-name',
84
                'ou': None, 'service': None}]}))
85

  
86
    Role = get_role_model()
87

  
88
    management.call_command('import_site', '--dry-run', export_file.strpath)
89

  
90
    with pytest.raises(Role.DoesNotExist):
91
        Role.objects.get(slug='role-slug')
92

  
93

  
94
def test_import_site_cmd_unhandled_context_option(db, tmpdir, monkeypatch, capsys):
95
    from authentic2.data_transfer import DataImportError
96

  
97
    export_file = tmpdir.join('roles-export.json')
98
    with export_file.open('w'):
99
        export_file.write(json.dumps(
100
            {'roles': [{
101
                'uuid': 'dqfewrvesvews2532', 'slug': 'role-slug', 'name': 'role-name',
102
                'ou': None, 'service': None}]}))
103

  
104
    get_role_model().objects.create(uuid='dqfewrvesvews2532', slug='role-slug', name='role-name')
105

  
106
    with pytest.raises(DataImportError):
107
        management.call_command(
108
            'import_site', '-o', 'role-delete-orphans', export_file.strpath)
109

  
110

  
111
def test_import_site_cmd_unknown_context_option(db, tmpdir, monkeypatch, capsys):
112
    from django.core.management.base import CommandError
113
    export_file = tmpdir.join('roles-export.json')
114
    with pytest.raises(CommandError):
115
        management.call_command('import_site', '-o', 'unknown-option', export_file.strpath)
116

  
117

  
118
def test_import_site_confirm_prompt_yes(db, tmpdir, monkeypatch):
119
    export_file = tmpdir.join('roles-export.json')
120
    with export_file.open('w'):
121
        export_file.write(json.dumps(
122
            {'roles': [{
123
                'uuid': 'dqfewrvesvews2532', 'slug': 'role-slug', 'name': 'role-name',
124
                'ou': None, 'service': None}]}))
125

  
126
    def yes_raw_input(*args, **kwargs):
127
        return 'yes'
128

  
129
    monkeypatch.setattr(__builtin__, 'raw_input', yes_raw_input)
130

  
131
    management.call_command('import_site', export_file.strpath, stdin='yes')
132
    assert get_role_model().objects.get(uuid='dqfewrvesvews2532')
0
-