Projet

Général

Profil

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

Emmanuel Cazenave, 04 avril 2018 18:38

Télécharger (96,5 ko)

Voir les différences:

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

Handle only OU and Role.
 src/authentic2/a2_rbac/models.py                  |   40 +
 src/authentic2/data_transfer.py                   |  301 +++++
 src/authentic2/management/commands/export_site.py |   24 +
 src/authentic2/management/commands/import_site.py |  112 ++
 src/authentic2/models.py                          |    5 +
 src/authentic2/utils.py                           |    5 +
 src/django_rbac/models.py                         |   11 +
 tests/data/export_site.json                       | 1361 +++++++++++++++++++++
 tests/test_a2_rbac.py                             |  156 ++-
 tests/test_data_transfer.py                       |  396 ++++++
 tests/test_import_export_site_cmd.py              |  153 +++
 11 files changed, 2563 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, full=False):
96
        d = {'uuid': self.uuid, 'slug': self.slug, 'name': self.name}
97
        if full:
98
            extension = {
99
                'description': self.description, 'default': self.default,
100
                'email_is_unique': self.email_is_unique,
101
                'username_is_unique': self.username_is_unique,
102
                'validate_emails': self.validate_emails
103
            }
104
            d.update(extension)
105
        return d
106

  
95 107

  
96 108
class Permission(PermissionAbstractBase):
97 109
    class Meta:
......
207 219
            'ou__slug': self.ou.slug if self.ou else None,
208 220
        }
209 221

  
222
    def export_json(self, attributes=False, parents=False, permissions=False):
223
        d = {
224
            'uuid': self.uuid, 'slug': self.slug, 'name': self.name,
225
            'description': self.description, 'admin_scope_id': self.admin_scope_id,
226
            'admin_scope_ct_id': self.admin_scope_ct_id, 'external_id': self.external_id,
227
            'ou': self.ou and self.ou.export_json(False),
228
            'service': self.service and self.service.export_json(False)
229
        }
230

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

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

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

  
244
        return d
245

  
210 246

  
211 247
class RoleParenting(RoleParentingAbstractBase):
212 248
    class Meta(RoleParentingAbstractBase.Meta):
......
239 275
            ('role', 'name', 'kind', 'value'),
240 276
        )
241 277

  
278
    def to_json(self):
279
        return {'name': self.name, 'kind': self.kind, 'value': self.value}
280

  
281

  
242 282
GenericRelation(Permission,
243 283
                content_type_field='target_ct',
244 284
                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 build_ou_natural_key(d):
31
    return None if d['ou'] is None else [d['ou']['slug']]
32

  
33

  
34
def build_role_natural_key(role_d):
35
    role_slug = role_d['slug']
36
    ou_nk = build_ou_natural_key(role_d)
37
    service_nk = None if role_d['service'] is None else [
38
        build_ou_natural_key(role_d['service']), role_d['service']['slug']]
39
    return [role_slug, ou_nk, service_nk]
40

  
41

  
42
def search_ou(ou_natural_key):
43
    """ ou_natural_key: ['ou-slug'] or None
44
    """
45
    if ou_natural_key:
46
        try:
47
            OU = get_ou_model()
48
            return OU.objects.get_by_natural_key(*ou_natural_key)
49
        except OU.DoesNotExist:
50
            pass
51
    return None
52

  
53

  
54
def search_role(role_d):
55
    Role = get_role_model()
56
    try:
57
        return Role.objects.get(uuid=role_d['uuid'])
58
    except Role.DoesNotExist:
59
        pass
60
    try:
61
        return Role.objects.get_by_natural_key(*build_role_natural_key(role_d))
62
    except Role.DoesNotExist:
63
        return None
64

  
65

  
66
class ImportContext(object):
67
    """ Holds information on how to perform the import.
68

  
69
    ou_delete_orphans: if True any existing ou that is not found in the export will
70
                       be deleted
71

  
72
    role_delete_orphans: if True any existing role that is not found in the export will
73
                         be deleted
74

  
75

  
76
    role_attributes_update: for each role in the import data,
77
                            attributes  will deleted and re-created
78

  
79

  
80
    role_parentings_update: for each role in the import data,
81
                            parentings will deleted and re-created
82

  
83
    role_permissions_update: for each role in the import data,
84
                             permissions  will deleted and re-created
85
    """
86

  
87
    def __init__(
88
            self, role_delete_orphans=False, role_parentings_update=True,
89
            role_permissions_update=True, role_attributes_update=True,
90
            ou_delete_orphans=False):
91
        self.role_delete_orphans = role_delete_orphans
92
        self.ou_delete_orphans = ou_delete_orphans
93
        self.role_parentings_update = role_parentings_update
94
        self.role_permissions_update = role_permissions_update
95
        self.role_attributes_update = role_attributes_update
96

  
97

  
98
class DataImportError(Exception):
99
    pass
100

  
101

  
102
class RoleDeserializer(object):
103

  
104
    def __init__(self, d, import_context):
105
        self._import_context = import_context
106
        self._obj = None
107
        self._ou = None
108
        self._parents = None
109
        self._attributes = None
110
        self._permissions = None
111

  
112
        self._role_d = dict()
113
        for key, value in d.items():
114
            if key == 'parents':
115
                self._parents = value
116
            elif key == 'attributes':
117
                self._attributes = value
118
            elif key == 'permissions':
119
                self._permissions = value
120
            else:
121
                self._role_d[key] = value
122

  
123
        ou_natural_key = build_ou_natural_key(self._role_d)
124
        if ou_natural_key:
125
            self._ou = search_ou(ou_natural_key)
126
            if self._ou is None:
127
                raise DataImportError(
128
                    "Can't import role because missing Organizational Unit : "
129
                    "%s" % ou_natural_key[0])
130

  
131
    def deserialize(self):
132
        kwargs = self._role_d.copy()
133
        obj = search_role(self._role_d)
134
        del kwargs['ou']
135
        del kwargs['service']
136
        if obj:  # Role already exist
137
            self._obj = obj
138
            status = 'updated'
139
            update_model(self._obj, kwargs)
140
            if self._ou and (self._obj.ou != self._ou):
141
                # Need to update ou
142
                self._obj.ou = self._ou
143
                self._obj.save()
144
        else:  # Create role
145
            self._obj = get_role_model().objects.create(**kwargs)
146
            if self._ou:
147
                self._obj.ou = self._ou
148
                self._obj.save()
149
            status = 'created'
150

  
151
        # Ensure admin role is created
152
        self._obj.get_admin_role()
153
        return self._obj, status
154

  
155
    def attributes(self):
156
        """ Update attributes (delete everything then create)
157
        """
158
        created, deleted = [], []
159
        for attr in self._obj.attributes.all():
160
            attr.delete()
161
            deleted.append(attr)
162
        # Create attributes
163
        if self._attributes:
164
            for attr_dict in self._attributes:
165
                attr_dict['role'] = self._obj
166
                created.append(RoleAttribute.objects.create(**attr_dict))
167

  
168
        return created, deleted
169

  
170
    def parentings(self):
171
        """ Update parentings (delete everything then create)
172
        """
173
        created, deleted = [], []
174
        Parenting = get_role_parenting_model()
175
        for parenting in Parenting.objects.filter(child=self._obj, direct=True):
176
            parenting.delete()
177
            deleted.append(parenting)
178

  
179
        if self._parents:
180
            for parent_d in self._parents:
181
                parent = search_role(parent_d)
182
                if not parent:
183
                    raise DataImportError("Could not find role : %s" % parent_d)
184
                created.append(Parenting.objects.create(
185
                    child=self._obj, direct=True, parent=parent))
186

  
187
        return created, deleted
188

  
189
    def permissions(self):
190
        """ Update permissions (delete everything then create)
191
        """
192
        created, deleted = [], []
193
        for perm in self._obj.permissions.all():
194
            perm.delete()
195
            deleted.append(perm)
196
        self._obj.permissions.clear()
197
        if self._permissions:
198
            for perm in self._permissions:
199
                op = Operation.objects.get_by_natural_key(perm['operation']['slug'])
200
                ou = get_ou_model().objects.get_by_natural_key(
201
                    perm['ou']['slug']) if perm['ou'] else None
202
                ct = ContentType.objects.get_by_natural_key(*perm['target_ct'])
203
                target = ct.model_class().objects.get_by_natural_key(*perm['target'])
204
                perm = get_permission_model().objects.create(
205
                    operation=op, ou=ou, target_ct=ct, target_id=target.pk)
206
                self._obj.permissions.add(perm)
207
                created.append(perm)
208

  
209
        return created, deleted
210

  
211

  
212
class ImportResult(object):
213

  
214
    def __init__(self):
215
        self.roles = {'created': [], 'updated': []}
216
        self.ous = {'created': [], 'updated': []}
217
        self.attributes = {'created': [], 'deleted': []}
218
        self.parentings = {'created': [], 'deleted': []}
219
        self.permissions = {'created': [], 'deleted': []}
220

  
221
    def update_roles(self, role, d_status):
222
        self.roles[d_status].append(role)
223

  
224
    def update_ous(self, ou, status):
225
        self.ous[status].append(ou)
226

  
227
    def _bulk_update(self, attrname, created, deleted):
228
        attr = getattr(self, attrname)
229
        attr['created'].extend(created)
230
        attr['deleted'].extend(deleted)
231

  
232
    def update_attributes(self, created, deleted):
233
        self._bulk_update('attributes', created, deleted)
234

  
235
    def update_parentings(self, created, deleted):
236
        self._bulk_update('parentings', created, deleted)
237

  
238
    def update_permissions(self, created, deleted):
239
        self._bulk_update('permissions', created, deleted)
240

  
241
    def to_str(self, verbose=False):
242
        res = ""
243
        for attr in ('roles', 'ous', 'parentings', 'permissions', 'attributes'):
244
            data = getattr(self, attr)
245
            for status in ('created', 'updated', 'deleted'):
246
                if status in data:
247
                    s_data = data[status]
248
                    res += "%s %s %s\n" % (len(s_data), attr, status)
249
        return res
250

  
251

  
252
def import_ou(ou_d):
253
    OU = get_ou_model()
254
    ou = search_ou([ou_d['slug']])
255
    if ou is None:
256
        ou = OU.objects.create(**ou_d)
257
        status = 'created'
258
    else:
259
        update_model(ou, ou_d)
260
        status = 'updated'
261
    # Ensure admin role is created
262
    ou.get_admin_role()
263
    return ou, status
264

  
265

  
266
def import_site(json_d, import_context):
267
    result = ImportResult()
268

  
269
    for ou_d in json_d.get('ous', []):
270
        result.update_ous(*import_ou(ou_d))
271

  
272
    roles_ds = [RoleDeserializer(role_d, import_context) for role_d in json_d.get('roles', [])
273
                if not role_d['slug'].startswith('_')]
274

  
275
    for ds in roles_ds:
276
        result.update_roles(*ds.deserialize())
277

  
278
    if import_context.role_attributes_update:
279
        for ds in roles_ds:
280
            result.update_attributes(*ds.attributes())
281

  
282
    if import_context.role_parentings_update:
283
        for ds in roles_ds:
284
            result.update_parentings(*ds.parentings())
285

  
286
    if import_context.role_permissions_update:
287
        for ds in roles_ds:
288
            result.update_permissions(*ds.permissions())
289

  
290
    if import_context.ou_delete_orphans:
291
        raise DataImportError(
292
            "Unsupported context value for ou_delete_orphans : %s" % (
293
                import_context.ou_delete_orphans))
294

  
295
    if import_context.role_delete_orphans:
296
        # FIXME : delete each role that is in DB but not in the export
297
        raise DataImportError(
298
            "Unsupported context value for role_delete_orphans : %s" % (
299
                import_context.role_delete_orphans))
300

  
301
    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
# Borrowed from http://code.activestate.com/recipes/577058/
18
def query_yes_no(question, default="yes"):
19
    """Ask a yes/no question via raw_input() and return their answer.
20

  
21
    "question" is a string that is presented to the user.
22
    "default" is the presumed answer if the user just hits <Enter>.
23
        It must be "yes" (the default), "no" or None (meaning
24
        an answer is required of the user).
25

  
26
    The "answer" return value is True for "yes" or False for "no".
27
    """
28
    valid = {"yes": True, "y": True, "ye": True,
29
             "no": False, "n": False}
30
    if default is None:
31
        prompt = " [y/n] "
32
    elif default == "yes":
33
        prompt = " [Y/n] "
34
    elif default == "no":
35
        prompt = " [y/N] "
36
    else:
37
        raise ValueError("invalid default answer: '%s'" % default)
38

  
39
    while True:
40
        sys.stdout.write(question + prompt)
41
        choice = raw_input().lower()
42
        if default is not None and choice == '':
43
            return valid[default]
44
        elif choice in valid:
45
            return valid[choice]
46
        else:
47
            sys.stdout.write("Please respond with 'yes' or 'no' "
48
                             "(or 'y' or 'n').\n")
49

  
50

  
51
def create_context_args(options):
52
    kwargs = {}
53
    if options['option']:
54
        for context_op in options['option']:
55
            context_op = context_op.replace('-', '_')
56
            if context_op.startswith('no_'):
57
                kwargs[context_op[3:]] = False
58
            else:
59
                kwargs[context_op] = True
60
    return kwargs
61

  
62

  
63
#  Borrowed from https://bugs.python.org/issue10049#msg118599
64
@contextlib.contextmanager
65
def provision_contextm(dry_run, settings):
66
    if dry_run and 'hobo.agent.authentic2' in settings.INSTALLED_APPS:
67
        import hobo.agent.authentic2
68
        with hobo.agent.authentic2.provisionning.Provisionning():
69
            yield
70
    else:
71
        yield
72

  
73

  
74
class Command(BaseCommand):
75
    help = 'Import site'
76

  
77
    def add_arguments(self, parser):
78
        parser.add_argument(
79
            'filename', metavar='FILENAME', type=str, help='name of file to import')
80
        parser.add_argument(
81
            '--dry-run', action='store_true', dest='dry_run', help='Really perform the import')
82
        parser.add_argument(
83
            '--noinput', '--no-input', action='store_true', dest='skip_confirm',
84
            help='Skip confirmation prompt')
85
        parser.add_argument(
86
            '-o', '--option', action='append', help='Import context options',
87
            choices=[
88
                'role-delete-orphans', 'ou-delete-orphans', 'no-role-permissions-update',
89
                'no-role-attributes-update', 'no-role-parentings-update'])
90

  
91
    def handle(self, filename, **options):
92
        translation.activate(settings.LANGUAGE_CODE)
93
        dry_run = options['dry_run']
94
        skip_confirm = options['skip_confirm']
95
        if not (dry_run or skip_confirm):
96
            if not query_yes_no("Do you really want to perform the import ?"):
97
                sys.exit()
98
        msg = "Dry run\n" if dry_run else "Real run\n"
99
        c_kwargs = create_context_args(options)
100
        try:
101
            with open(filename, 'r') as f:
102
                with provision_contextm(dry_run, settings):
103
                    with transaction.atomic():
104
                        sys.stdout.write(msg)
105
                        result = import_site(json.load(f), ImportContext(**c_kwargs))
106
                        if dry_run:
107
                            raise DryRunException()
108
        except DryRunException:
109
            pass
110
        sys.stdout.write(result.to_str())
111
        sys.stdout.write("Success\n")
112
        translation.deactivate()
src/authentic2/models.py
406 406
            'roles': [role.to_json() for role in roles],
407 407
        }
408 408

  
409
    def export_json(self, full=False):
410
        if full:
411
            raise NotImplementedError()
412
        return {'name': self.name, 'slug': self.slug, 'ou': self.ou and self.ou.export_json(False)}
413

  
409 414

  
410 415
class AuthorizedRole(models.Model):
411 416
    service = models.ForeignKey(Service, on_delete=models.CASCADE)
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

  
......
142 145
                self.target and self.target_ct.natural_key(),
143 146
                self.target and self.target.natural_key()]
144 147

  
148
    def export_json(self):
149
        return {
150
            "operation": self.operation.export_json(),
151
            "ou": self.ou and self.ou.export_json(full=False),
152
            "target_ct": self.target and self.target_ct.natural_key(),
153
            "target": self.target and self.target.natural_key()
154
        }
155

  
145 156
    def __unicode__(self):
146 157
        ct = ContentType.objects.get_for_id(self.target_ct_id)
147 158
        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['admin_scope_id'] == role.admin_scope_id
42
    assert role_dict['external_id'] == role.external_id
43
    assert role_dict['ou'] is None
44
    assert role_dict['service'] is None
45

  
46

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

  
53

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

  
60

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

  
70

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

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

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

  
90

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

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

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

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

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

  
130

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

  
150
    export = role.export_json(permissions=True)
151
    permissions = export['permissions']
152
    assert len(permissions) == 2
153

  
154
    assert permissions[0] == {
155
        'operation': {'slug': 'add', 'name': 'Add'},
156
        'ou': ou.export_json(full=False),
157
        'target_ct': (u'contenttypes', u'contenttype'),
158
        'target': (u'saml', u'libertyprovider')
159
    }
160
    assert permissions[1] == {
161
        'operation': {'slug': 'add', 'name': 'Add'},
162
        'ou': None,
163
        'target_ct': (u'a2_rbac', u'role'),
164
        'target': [u'other-role-slug', ['some-ou'], None]
165
    }
166

  
167

  
168
def test_ou_export_json(db):
169
    ou = OU.objects.create(
170
        name='basic ou', slug='basic-ou', description='basic ou description',
171
        username_is_unique=True, email_is_unique=True, default=False, validate_emails=True)
172
    ou_dict = ou.export_json(full=True)
173
    assert ou_dict['name'] == ou.name
174
    assert ou_dict['slug'] == ou.slug
175
    assert ou_dict['uuid'] == ou.uuid
176
    assert ou_dict['description'] == ou.description
177
    assert ou_dict['username_is_unique'] == ou.username_is_unique
178
    assert ou_dict['email_is_unique'] == ou.email_is_unique
179
    assert ou_dict['default'] == ou.default
180
    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, build_role_natural_key)
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')
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

  
79

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

  
86

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

  
94

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

  
101

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

  
110

  
111
def test_role_deserializer(db):
112
    rd = RoleDeserializer({
113
        'name': 'some role', 'description': 'some role description', 'slug': 'some-role',
114
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}, ImportContext())
115
    assert rd._ou is None
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
    with pytest.raises(DataImportError):
138
        RoleDeserializer({
139
            'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'role description',
140
            'slug': 'some-role', 'ou': {'slug': 'some-ou'}, 'service': None},
141
            ImportContext())
142

  
143

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

  
156

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

  
167

  
168
def test_role_deserializer_with_attributes(db):
169

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

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

  
190

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

  
199

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

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

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

  
219

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

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

  
234

  
235
def test_role_deserializer_permissions(db):
236
    ou = OU.objects.create(slug='some-ou')
237
    other_role_dict = {
238
        'name': 'other role', 'slug': 'other-role-slug', 'uuid': get_hex_uuid(), 'ou': ou}
239
    other_role = Role.objects.create(**other_role_dict)
240
    some_role_dict = {
241
        'name': 'some role', 'slug': 'some-role', 'uuid': get_hex_uuid(),
242
        'ou': None, 'service': None}
243
    some_role_dict['permissions'] = [
244
        {
245
            'operation': {'slug': 'add'},
246
            'ou': None,
247
            'target_ct': (u'a2_rbac', u'role'),
248
            'target': [u'other-role-slug', ['some-ou'], None]
249
        }
250
    ]
251

  
252
    import_context = ImportContext()
253
    rd = RoleDeserializer(some_role_dict, import_context)
254
    rd.deserialize()
255
    perm_created, perm_deleted = rd.permissions()
256

  
257
    assert len(perm_created) == 1
258
    assert len(perm_deleted) == 0
259
    del some_role_dict['permissions']
260
    role = Role.objects.get(slug=some_role_dict['slug'])
261
    assert role.permissions.count() == 1
262
    perm = role.permissions.first()
263
    assert perm.operation.slug == 'add'
264
    assert not perm.ou
265
    assert perm.target == other_role
266

  
267
    # that one should delete permissions
268
    rd = RoleDeserializer(some_role_dict, import_context)
269
    role, _ = rd.deserialize()
270
    perm_created, perm_deleted = rd.permissions()
271
    assert role.permissions.count() == 0
272
    assert len(perm_created) == 0
273
    assert len(perm_deleted) == 1
274

  
275

  
276
def import_ou_created(db):
277
    uuid = get_hex_uuid()
278
    ou_d = {'uuid': uuid, 'slug': 'ou-slug', 'name': 'ou name'}
279
    ou, status = import_ou(ou_d)
280
    assert status == 'created'
281
    assert ou.uuid == ou_d['uuid']
282
    assert ou.slug == ou_d['slug']
283
    assert ou.name == ou_d['name']
284

  
285

  
286
def import_ou_updated(db):
287
    ou = OU.objects.create(slug='some-ou', name='ou name')
288
    ou_d = {'uuid': ou.uuid, 'slug': ou.slug, 'name': 'new name'}
289
    ou_updated, status = import_ou(ou_d)
290
    assert status == 'updated'
291
    assert ou == ou_updated
292
    assert ou.name == 'new name'
293

  
294

  
295
def testi_import_site_empty():
296
    res = import_site({}, ImportContext())
297
    assert res.roles == {'created': [], 'updated': []}
298
    assert res.ous == {'created': [], 'updated': []}
299
    assert res.parentings == {'created': [], 'deleted': []}
300

  
301

  
302
def test_import_site_roles(db):
303
    parent_role_dict = {
304
        'name': 'grand parent role', 'slug': 'grand-parent-role', 'uuid': get_hex_uuid(),
305
        'ou': None, 'service': None}
306
    child_role_dict = {
307
        'name': 'child role', 'slug': 'child-role', 'parents': [parent_role_dict],
308
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
309
    roles = [
310
        parent_role_dict,
311
        child_role_dict
312
    ]
313
    res = import_site({'roles': roles}, ImportContext())
314
    created_roles = res.roles['created']
315
    assert len(created_roles) == 2
316
    parent_role = Role.objects.get(**parent_role_dict)
317
    del child_role_dict['parents']
318
    child_role = Role.objects.get(**child_role_dict)
319
    assert created_roles[0] == parent_role
320
    assert created_roles[1] == child_role
321

  
322
    assert len(res.parentings['created']) == 1
323
    assert res.parentings['created'][0] == RoleParenting.objects.get(
324
        child=child_role, parent=parent_role, direct=True)
325

  
326

  
327
def test_roles_import_ignore_technical_role(db):
328
    roles = [{
329
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
330
    res = import_site({'roles': roles}, ImportContext())
331
    assert res.roles == {'created': [], 'updated': []}
332

  
333

  
334
def test_roles_import_ignore_technical_role_with_service(db):
335
    roles = [{
336
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
337
    res = import_site({'roles': roles}, ImportContext())
338
    assert res.roles == {'created': [], 'updated': []}
339

  
340

  
341
def test_import_role_handle_manager_role_parenting(db):
342
    parent_role_dict = {
343
        'name': 'grand parent role', 'slug': 'grand-parent-role', 'uuid': get_hex_uuid(),
344
        'ou': None, 'service': None}
345
    parent_role_manager_dict = {
346
        'name': 'Administrateur du role grand parent role',
347
        'slug': '_a2-managers-of-role-grand-parent-role', 'uuid': get_hex_uuid(),
348
        'ou': None, 'service': None}
349
    child_role_dict = {
350
        'name': 'child role', 'slug': 'child-role',
351
        'parents': [parent_role_dict, parent_role_manager_dict],
352
        'uuid': get_hex_uuid(), 'ou': None, 'service': None}
353
    import_site({'roles': [child_role_dict, parent_role_dict]}, ImportContext())
354
    child = Role.objects.get(slug='child-role')
355
    manager = Role.objects.get(slug='_a2-managers-of-role-grand-parent-role')
356
    RoleParenting.objects.get(child=child, parent=manager, direct=True)
357

  
358

  
359
def test_import_roles_role_delete_orphans(db):
360
    roles = [{
361
        'name': 'some role', 'description': 'some role description', 'slug': '_some-role'}]
362
    with pytest.raises(DataImportError):
363
        import_site({'roles': roles}, ImportContext(role_delete_orphans=True))
364

  
365

  
366
def test_import_ou(db):
367
    uuid = get_hex_uuid()
368
    name = 'ou name'
369
    ous = [{'uuid': uuid, 'slug': 'ou-slug', 'name': name}]
370
    res = import_site({'ous': ous}, ImportContext())
371
    assert len(res.ous['created']) == 1
372
    ou = res.ous['created'][0]
373
    assert ou.uuid == uuid
374
    assert ou.name == name
375
    Role.objects.get(slug='_a2-managers-of-ou-slug')
376

  
377

  
378
def test_import_ou_already_existing(db):
379
    uuid = get_hex_uuid()
380
    ou_d = {'uuid': uuid, 'slug': 'ou-slug', 'name': 'ou name'}
381
    ou = OU.objects.create(**ou_d)
382
    num_ous = OU.objects.count()
383
    res = import_site({'ous': [ou_d]}, ImportContext())
384
    assert len(res.ous['created']) == 0
385
    assert num_ous == OU.objects.count()
386
    assert ou == OU.objects.get(uuid=uuid)
387

  
388

  
389
def test_import_site_raw(db):
390
    data_dir = py.path.local(__file__).dirpath().join('data')
391
    export_site = data_dir.join('export_site.json')
392
    with export_site.open('r') as f:
393
        json_d = json.load(f)
394
    result = import_site(json_d, ImportContext())
395
    role = Role.objects.get(slug='role-deux')
396
    assert role.permissions.count() == 3
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', '--noinput', 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', '--noinput', 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', '--noinput', 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', '--noinput', '-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', '--noinput', '-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')
133

  
134

  
135
def test_import_site_confirm_prompt_no(db, monkeypatch, tmpdir):
136
    export_file = tmpdir.join('roles-export.json')
137
    with export_file.open('w'):
138
        export_file.write(json.dumps(
139
            {'roles': [{
140
                'uuid': 'dqfewrvesvews2532', 'slug': 'role-slug', 'name': 'role-name',
141
                'ou': None, 'service': None}]}))
142

  
143
    def no_raw_input(*args, **kwargs):
144
        return 'no'
145

  
146
    monkeypatch.setattr(__builtin__, 'raw_input', no_raw_input)
147

  
148
    with pytest.raises(SystemExit):
149
        management.call_command('import_site', export_file.strpath)
150

  
151
    Role = get_role_model()
152
    with pytest.raises(Role.DoesNotExist):
153
        Role.objects.get(uuid='dqfewrvesvews2532')
0
-