data_transfer: add export context (#29162)
This commit is contained in:
parent
5815547369
commit
002dff7acf
|
@ -12,23 +12,47 @@ def update_model(obj, d):
|
||||||
obj.save()
|
obj.save()
|
||||||
|
|
||||||
|
|
||||||
def export_site():
|
class ExportContext(object):
|
||||||
return {
|
_role_qs = None
|
||||||
'roles': export_roles(get_role_model().objects.all()),
|
_ou_qs = None
|
||||||
'ous': export_ou(get_ou_model().objects.all())
|
export_roles = None
|
||||||
}
|
export_ous = None
|
||||||
|
|
||||||
|
def __init__(self, role_qs=None, ou_qs=None, export_roles=True, export_ous=True):
|
||||||
|
self._role_qs = role_qs
|
||||||
|
self._ou_qs = ou_qs
|
||||||
|
self.export_roles = export_roles
|
||||||
|
self.export_ous = export_ous
|
||||||
|
|
||||||
|
@property
|
||||||
|
def role_qs(self):
|
||||||
|
return self._role_qs or get_role_model().objects.all()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ou_qs(self):
|
||||||
|
return self._ou_qs or get_ou_model().objects.all()
|
||||||
|
|
||||||
|
|
||||||
def export_ou(ou_query_set):
|
def export_site(context=None):
|
||||||
return [ou.export_json() for ou in ou_query_set]
|
context = context or ExportContext()
|
||||||
|
d = {}
|
||||||
|
if context.export_roles:
|
||||||
|
d['roles'] = export_roles(context)
|
||||||
|
if context.export_ous:
|
||||||
|
d['ous'] = export_ous(context)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
def export_roles(role_queryset):
|
def export_ous(context):
|
||||||
|
return [ou.export_json() for ou in context.ou_qs]
|
||||||
|
|
||||||
|
|
||||||
|
def export_roles(context):
|
||||||
""" Serialize roles in role_queryset
|
""" Serialize roles in role_queryset
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
role.export_json(attributes=True, parents=True, permissions=True)
|
role.export_json(attributes=True, parents=True, permissions=True)
|
||||||
for role in role_queryset
|
for role in context.role_qs
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,9 +95,16 @@ class ImportContext(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, role_delete_orphans=False, role_parentings_update=True,
|
self,
|
||||||
role_permissions_update=True, role_attributes_update=True,
|
import_roles=True,
|
||||||
|
import_ous=True,
|
||||||
|
role_delete_orphans=False,
|
||||||
|
role_parentings_update=True,
|
||||||
|
role_permissions_update=True,
|
||||||
|
role_attributes_update=True,
|
||||||
ou_delete_orphans=False):
|
ou_delete_orphans=False):
|
||||||
|
self.import_roles = import_roles
|
||||||
|
self.import_ous = import_ous
|
||||||
self.role_delete_orphans = role_delete_orphans
|
self.role_delete_orphans = role_delete_orphans
|
||||||
self.ou_delete_orphans = ou_delete_orphans
|
self.ou_delete_orphans = ou_delete_orphans
|
||||||
self.role_parentings_update = role_parentings_update
|
self.role_parentings_update = role_parentings_update
|
||||||
|
@ -86,7 +117,6 @@ class DataImportError(Exception):
|
||||||
|
|
||||||
|
|
||||||
class RoleDeserializer(object):
|
class RoleDeserializer(object):
|
||||||
|
|
||||||
def __init__(self, d, import_context):
|
def __init__(self, d, import_context):
|
||||||
self._import_context = import_context
|
self._import_context = import_context
|
||||||
self._obj = None
|
self._obj = None
|
||||||
|
@ -114,8 +144,8 @@ class RoleDeserializer(object):
|
||||||
"Can't import role because missing Organizational Unit: %s" % ou_d)
|
"Can't import role because missing Organizational Unit: %s" % ou_d)
|
||||||
|
|
||||||
kwargs = self._role_d.copy()
|
kwargs = self._role_d.copy()
|
||||||
del kwargs['ou']
|
kwargs.pop('ou', None)
|
||||||
del kwargs['service']
|
kwargs.pop('service', None)
|
||||||
if has_ou:
|
if has_ou:
|
||||||
kwargs['ou'] = ou
|
kwargs['ou'] = ou
|
||||||
|
|
||||||
|
@ -246,42 +276,45 @@ def import_ou(ou_d):
|
||||||
return ou, status
|
return ou, status
|
||||||
|
|
||||||
|
|
||||||
def import_site(json_d, import_context):
|
def import_site(json_d, import_context=None):
|
||||||
|
import_context = import_context or ImportContext()
|
||||||
result = ImportResult()
|
result = ImportResult()
|
||||||
|
|
||||||
if not isinstance(json_d, dict):
|
if not isinstance(json_d, dict):
|
||||||
raise DataImportError('Export file is invalid: not a dictionnary')
|
raise DataImportError('Export file is invalid: not a dictionnary')
|
||||||
|
|
||||||
for ou_d in json_d.get('ous', []):
|
if import_context.import_ous:
|
||||||
result.update_ous(*import_ou(ou_d))
|
for ou_d in json_d.get('ous', []):
|
||||||
|
result.update_ous(*import_ou(ou_d))
|
||||||
|
|
||||||
roles_ds = [RoleDeserializer(role_d, import_context) for role_d in json_d.get('roles', [])
|
if import_context.import_roles:
|
||||||
if not role_d['slug'].startswith('_')]
|
roles_ds = [RoleDeserializer(role_d, import_context) for role_d in json_d.get('roles', [])
|
||||||
|
if not role_d['slug'].startswith('_')]
|
||||||
|
|
||||||
for ds in roles_ds:
|
|
||||||
result.update_roles(*ds.deserialize())
|
|
||||||
|
|
||||||
if import_context.role_attributes_update:
|
|
||||||
for ds in roles_ds:
|
for ds in roles_ds:
|
||||||
result.update_attributes(*ds.attributes())
|
result.update_roles(*ds.deserialize())
|
||||||
|
|
||||||
if import_context.role_parentings_update:
|
if import_context.role_attributes_update:
|
||||||
for ds in roles_ds:
|
for ds in roles_ds:
|
||||||
result.update_parentings(*ds.parentings())
|
result.update_attributes(*ds.attributes())
|
||||||
|
|
||||||
if import_context.role_permissions_update:
|
if import_context.role_parentings_update:
|
||||||
for ds in roles_ds:
|
for ds in roles_ds:
|
||||||
result.update_permissions(*ds.permissions())
|
result.update_parentings(*ds.parentings())
|
||||||
|
|
||||||
if import_context.ou_delete_orphans:
|
if import_context.role_permissions_update:
|
||||||
raise DataImportError(
|
for ds in roles_ds:
|
||||||
"Unsupported context value for ou_delete_orphans: %s" % (
|
result.update_permissions(*ds.permissions())
|
||||||
import_context.ou_delete_orphans))
|
|
||||||
|
|
||||||
if import_context.role_delete_orphans:
|
if import_context.ou_delete_orphans:
|
||||||
# FIXME: delete each role that is in DB but not in the export
|
raise DataImportError(
|
||||||
raise DataImportError(
|
"Unsupported context value for ou_delete_orphans : %s" % (
|
||||||
"Unsupported context value for role_delete_orphans: %s" % (
|
import_context.ou_delete_orphans))
|
||||||
import_context.role_delete_orphans))
|
|
||||||
|
if import_context.role_delete_orphans:
|
||||||
|
# FIXME : delete each role that is in DB but not in the export
|
||||||
|
raise DataImportError(
|
||||||
|
"Unsupported context value for role_delete_orphans : %s" % (
|
||||||
|
import_context.role_delete_orphans))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
import json
|
|
||||||
|
|
||||||
from django_rbac.utils import get_role_model, get_ou_model
|
from django_rbac.utils import get_role_model, get_ou_model
|
||||||
import py
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from authentic2.a2_rbac.models import RoleParenting
|
from authentic2.a2_rbac.models import RoleParenting
|
||||||
from authentic2.data_transfer import (
|
from authentic2.data_transfer import (
|
||||||
DataImportError, export_roles, import_site, export_ou, ImportContext,
|
export_site,
|
||||||
RoleDeserializer, search_role, import_ou)
|
ExportContext,
|
||||||
|
DataImportError,
|
||||||
|
export_roles,
|
||||||
|
import_site,
|
||||||
|
export_ous,
|
||||||
|
ImportContext,
|
||||||
|
RoleDeserializer,
|
||||||
|
search_role,
|
||||||
|
import_ou)
|
||||||
from authentic2.utils import get_hex_uuid
|
from authentic2.utils import get_hex_uuid
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,7 +23,7 @@ OU = get_ou_model()
|
||||||
def test_export_basic_role(db):
|
def test_export_basic_role(db):
|
||||||
role = Role.objects.create(name='basic role', slug='basic-role', uuid=get_hex_uuid())
|
role = Role.objects.create(name='basic role', slug='basic-role', uuid=get_hex_uuid())
|
||||||
query_set = Role.objects.filter(uuid=role.uuid)
|
query_set = Role.objects.filter(uuid=role.uuid)
|
||||||
roles = export_roles(query_set)
|
roles = export_roles(ExportContext(role_qs=query_set))
|
||||||
assert len(roles) == 1
|
assert len(roles) == 1
|
||||||
role_dict = roles[0]
|
role_dict = roles[0]
|
||||||
for key, value in role.export_json().items():
|
for key, value in role.export_json().items():
|
||||||
|
@ -40,7 +45,7 @@ def test_export_role_with_parents(db):
|
||||||
child_role.add_parent(parent_2_role)
|
child_role.add_parent(parent_2_role)
|
||||||
|
|
||||||
query_set = Role.objects.filter(slug__startswith='test').order_by('slug')
|
query_set = Role.objects.filter(slug__startswith='test').order_by('slug')
|
||||||
roles = export_roles(query_set)
|
roles = export_roles(ExportContext(role_qs=query_set))
|
||||||
assert len(roles) == 4
|
assert len(roles) == 4
|
||||||
|
|
||||||
child_role_dict = roles[0]
|
child_role_dict = roles[0]
|
||||||
|
@ -68,9 +73,9 @@ def test_export_role_with_parents(db):
|
||||||
assert parents[0]['slug'] == grand_parent_role.slug
|
assert parents[0]['slug'] == grand_parent_role.slug
|
||||||
|
|
||||||
|
|
||||||
def test_export_ou(db):
|
def test_export_ous(db):
|
||||||
ou = OU.objects.create(name='ou name', slug='ou-slug', description='ou description')
|
ou = OU.objects.create(name='ou name', slug='ou-slug', description='ou description')
|
||||||
ous = export_ou(OU.objects.filter(name='ou name'))
|
ous = export_ous(ExportContext(ou_qs=OU.objects.filter(name='ou name')))
|
||||||
assert len(ous) == 1
|
assert len(ous) == 1
|
||||||
ou_d = ous[0]
|
ou_d = ous[0]
|
||||||
assert ou_d['name'] == ou.name
|
assert ou_d['name'] == ou.name
|
||||||
|
@ -137,7 +142,7 @@ def test_role_deserializer_missing_ou(db):
|
||||||
rd = RoleDeserializer({
|
rd = RoleDeserializer({
|
||||||
'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'role description',
|
'uuid': get_hex_uuid(), 'name': 'some role', 'description': 'role description',
|
||||||
'slug': 'some-role', 'ou': {'slug': 'some-ou'}, 'service': None},
|
'slug': 'some-role', 'ou': {'slug': 'some-ou'}, 'service': None},
|
||||||
ImportContext())
|
ImportContext())
|
||||||
with pytest.raises(DataImportError):
|
with pytest.raises(DataImportError):
|
||||||
rd.deserialize()
|
rd.deserialize()
|
||||||
|
|
||||||
|
@ -476,3 +481,45 @@ def test_import_ou_already_existing(db):
|
||||||
assert len(res.ous['created']) == 0
|
assert len(res.ous['created']) == 0
|
||||||
assert num_ous == OU.objects.count()
|
assert num_ous == OU.objects.count()
|
||||||
assert ou == OU.objects.get(uuid=uuid)
|
assert ou == OU.objects.get(uuid=uuid)
|
||||||
|
|
||||||
|
|
||||||
|
def test_import_context_flags(db):
|
||||||
|
ous = [{'uuid': get_hex_uuid(), 'slug': 'ou-slug', 'name': 'ou name'}]
|
||||||
|
roles = [{
|
||||||
|
'name': 'other role',
|
||||||
|
'slug': 'other-role-slug',
|
||||||
|
'uuid': get_hex_uuid(),
|
||||||
|
'ou': {'slug': 'ou-slug'},
|
||||||
|
}]
|
||||||
|
d = {'ous': ous, 'roles': roles}
|
||||||
|
import_site(d, ImportContext(import_roles=False, import_ous=False))
|
||||||
|
assert Role.objects.exclude(slug__startswith='_').count() == 0
|
||||||
|
assert OU.objects.exclude(slug='default').count() == 0
|
||||||
|
with pytest.raises(DataImportError) as e:
|
||||||
|
import_site(d, ImportContext(import_roles=True, import_ous=False))
|
||||||
|
assert 'missing Organizational' in e.value.args[0]
|
||||||
|
assert Role.objects.exclude(slug__startswith='_').count() == 0
|
||||||
|
assert OU.objects.exclude(slug='default').count() == 0
|
||||||
|
import_site(d, ImportContext(import_roles=False, import_ous=True))
|
||||||
|
assert Role.objects.exclude(slug__startswith='_').count() == 0
|
||||||
|
assert OU.objects.exclude(slug='default').count() == 1
|
||||||
|
import_site(d, ImportContext(import_roles=True, import_ous=True))
|
||||||
|
assert Role.objects.exclude(slug__startswith='_').count() == 1
|
||||||
|
assert OU.objects.exclude(slug='default').count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_export_site(db):
|
||||||
|
ou = OU.objects.create(name='ou')
|
||||||
|
role = Role.objects.create(name='role', ou=ou)
|
||||||
|
d = export_site()
|
||||||
|
assert len([ou for ou in d['ous'] if ou['slug'] != 'default']) == 1
|
||||||
|
assert len([role for role in d['roles'] if role['slug'][0] != '_']) == 1
|
||||||
|
d = export_site(ExportContext(ou_qs=OU.objects.filter(name='ou')))
|
||||||
|
assert len(d['ous']) == 1
|
||||||
|
d = export_site(ExportContext(role_qs=Role.objects.filter(name='role')))
|
||||||
|
assert len(d['roles']) == 1
|
||||||
|
d = export_site(ExportContext(export_roles=False))
|
||||||
|
assert 'roles' not in d
|
||||||
|
d = export_site(ExportContext(export_ous=False))
|
||||||
|
assert 'ous' not in d
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue