0002-a2_rbac-migrate-existing-operations-to-new-model-699.patch
src/authentic2/a2_rbac/migrations/0031_new_operation_model.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-10-11 14:14 |
|
2 | ||
3 |
import django.db.models.deletion |
|
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('a2_rbac', '0030_organizationalunit_min_password_strength'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.CreateModel( |
|
15 |
name='Operation', |
|
16 |
fields=[ |
|
17 |
( |
|
18 |
'id', |
|
19 |
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), |
|
20 |
), |
|
21 |
('slug', models.CharField(max_length=32, unique=True, verbose_name='slug')), |
|
22 |
], |
|
23 |
), |
|
24 |
migrations.AddField( |
|
25 |
model_name='permission', |
|
26 |
name='operation_new', |
|
27 |
field=models.ForeignKey( |
|
28 |
null=True, |
|
29 |
on_delete=django.db.models.deletion.CASCADE, |
|
30 |
to='a2_rbac.Operation', |
|
31 |
verbose_name='operation', |
|
32 |
), |
|
33 |
preserve_default=False, |
|
34 |
), |
|
35 |
migrations.AlterField( |
|
36 |
model_name='permission', |
|
37 |
name='operation', |
|
38 |
field=models.ForeignKey( |
|
39 |
on_delete=django.db.models.deletion.CASCADE, |
|
40 |
to='django_rbac.Operation', |
|
41 |
verbose_name='operation', |
|
42 |
null=True, |
|
43 |
), |
|
44 |
), |
|
45 |
] |
src/authentic2/a2_rbac/migrations/0032_copy_operations_data.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-10-11 13:36 |
|
2 | ||
3 |
from django.db import migrations |
|
4 | ||
5 | ||
6 |
def copy_operations_data(apps, schema_editor): |
|
7 |
OldOperation = apps.get_model('django_rbac', 'Operation') |
|
8 |
NewOperation = apps.get_model('a2_rbac', 'Operation') |
|
9 |
Permission = apps.get_model('a2_rbac', 'Permission') |
|
10 | ||
11 |
operation_map = {} |
|
12 |
for operation in OldOperation.objects.all(): |
|
13 |
operation_map[operation.pk] = NewOperation.objects.create(slug=operation.slug) |
|
14 | ||
15 |
for permission in Permission.objects.all(): |
|
16 |
permission.operation_new = operation_map[permission.operation_id] |
|
17 |
permission.save() |
|
18 | ||
19 | ||
20 |
def reverse_copy_operations_data(apps, schema_editor): |
|
21 |
OldOperation = apps.get_model('django_rbac', 'Operation') |
|
22 |
NewOperation = apps.get_model('a2_rbac', 'Operation') |
|
23 |
Permission = apps.get_model('a2_rbac', 'Permission') |
|
24 | ||
25 |
operation_map = {} |
|
26 |
for operation in NewOperation.objects.all(): |
|
27 |
operation_map[operation.pk] = OldOperation.objects.create(slug=operation.slug) |
|
28 | ||
29 |
for permission in Permission.objects.all(): |
|
30 |
permission.operation = operation_map[permission.operation_new_id] |
|
31 |
permission.save() |
|
32 | ||
33 | ||
34 |
class Migration(migrations.Migration): |
|
35 | ||
36 |
dependencies = [ |
|
37 |
('a2_rbac', '0031_new_operation_model'), |
|
38 |
('django_rbac', '0009_auto_20221004_1343'), |
|
39 |
] |
|
40 | ||
41 |
operations = [ |
|
42 |
migrations.RunPython(copy_operations_data, reverse_code=reverse_copy_operations_data), |
|
43 |
] |
src/authentic2/a2_rbac/migrations/0033_remove_old_operation_fk.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-10-11 14:35 |
|
2 | ||
3 |
import django |
|
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('a2_rbac', '0032_copy_operations_data'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.RemoveField( |
|
15 |
model_name='permission', |
|
16 |
name='operation', |
|
17 |
), |
|
18 |
migrations.RenameField( |
|
19 |
model_name='permission', |
|
20 |
old_name='operation_new', |
|
21 |
new_name='operation', |
|
22 |
), |
|
23 |
migrations.AlterField( |
|
24 |
model_name='permission', |
|
25 |
name='operation', |
|
26 |
field=models.ForeignKey( |
|
27 |
on_delete=django.db.models.deletion.CASCADE, to='a2_rbac.Operation', verbose_name='operation' |
|
28 |
), |
|
29 |
), |
|
30 |
] |
src/authentic2/a2_rbac/models.py | ||
---|---|---|
38 | 38 |
from authentic2.validators import HexaColourValidator |
39 | 39 |
from django_rbac import managers as rbac_managers |
40 | 40 |
from django_rbac import utils as rbac_utils |
41 |
from django_rbac.models import Operation |
|
42 | 41 | |
43 | 42 |
from . import app_settings, fields, managers |
44 | 43 | |
... | ... | |
266 | 265 | |
267 | 266 | |
268 | 267 |
class Permission(models.Model): |
269 |
operation = models.ForeignKey(to=Operation, verbose_name=_('operation'), on_delete=models.CASCADE) |
|
268 |
operation = models.ForeignKey( |
|
269 |
to='a2_rbac.Operation', verbose_name=_('operation'), on_delete=models.CASCADE |
|
270 |
) |
|
270 | 271 |
ou = models.ForeignKey( |
271 | 272 |
to=rbac_utils.get_ou_model_name(), |
272 | 273 |
verbose_name=_('organizational unit'), |
... | ... | |
718 | 719 |
return {'name': self.name, 'kind': self.kind, 'value': self.value} |
719 | 720 | |
720 | 721 | |
722 |
class Operation(models.Model): |
|
723 |
slug = models.CharField(max_length=32, verbose_name=_('slug'), unique=True) |
|
724 | ||
725 |
def natural_key(self): |
|
726 |
return [self.slug] |
|
727 | ||
728 |
def __str__(self): |
|
729 |
return str(self._registry.get(self.slug, self.slug)) |
|
730 | ||
731 |
def export_json(self): |
|
732 |
return {'slug': self.slug} |
|
733 | ||
734 |
@property |
|
735 |
def name(self): |
|
736 |
return str(self) |
|
737 | ||
738 |
@classmethod |
|
739 |
def register(cls, name, slug): |
|
740 |
cls._registry[slug] = name |
|
741 |
return cls(slug=slug) |
|
742 | ||
743 |
_registry = {} |
|
744 | ||
745 |
objects = rbac_managers.OperationManager() |
|
746 | ||
747 | ||
748 |
Operation._meta.natural_key = ['slug'] |
|
749 | ||
750 | ||
721 | 751 |
GenericRelation(Permission, content_type_field='target_ct', object_id_field='target_id').contribute_to_class( |
722 | 752 |
ContentType, 'admin_perms' |
723 | 753 |
) |
src/authentic2/data_transfer.py | ||
---|---|---|
24 | 24 |
from django.utils.text import format_lazy |
25 | 25 |
from django.utils.translation import gettext_lazy as _ |
26 | 26 | |
27 |
from authentic2.a2_rbac.models import OrganizationalUnit, Permission, Role, RoleAttribute, RoleParenting |
|
27 |
from authentic2.a2_rbac.models import ( |
|
28 |
Operation, |
|
29 |
OrganizationalUnit, |
|
30 |
Permission, |
|
31 |
Role, |
|
32 |
RoleAttribute, |
|
33 |
RoleParenting, |
|
34 |
) |
|
28 | 35 |
from authentic2.a2_rbac.utils import get_default_ou |
29 | 36 |
from authentic2.decorators import errorcollector |
30 | 37 |
from authentic2.utils.lazy import lazy_join |
31 |
from django_rbac.models import Operation |
|
32 | 38 | |
33 | 39 | |
34 | 40 |
def update_model(obj, d): |
src/authentic2/manager/forms.py | ||
---|---|---|
31 | 31 |
from django.utils.translation import pgettext |
32 | 32 |
from django_select2.forms import HeavySelect2Widget |
33 | 33 | |
34 |
from authentic2.a2_rbac.models import OrganizationalUnit, Permission, Role, RoleAttribute |
|
34 |
from authentic2.a2_rbac.models import Operation, OrganizationalUnit, Permission, Role, RoleAttribute
|
|
35 | 35 |
from authentic2.a2_rbac.utils import generate_slug, get_default_ou |
36 | 36 |
from authentic2.custom_user.backends import DjangoRBACBackend |
37 | 37 |
from authentic2.forms.fields import ( |
... | ... | |
46 | 46 |
from authentic2.passwords import generate_password, get_min_password_strength |
47 | 47 |
from authentic2.utils.misc import send_email_change_email, send_password_reset_mail, send_templated_mail |
48 | 48 |
from authentic2.validators import EmailValidator |
49 |
from django_rbac.models import Operation |
|
50 | 49 | |
51 | 50 |
from . import app_settings, fields, utils |
52 | 51 |
src/django_rbac/migrations/0010_delete_operation.py | ||
---|---|---|
1 |
# Generated by Django 2.2.26 on 2022-10-11 14:43 |
|
2 | ||
3 |
from django.db import migrations |
|
4 | ||
5 | ||
6 |
class Migration(migrations.Migration): |
|
7 | ||
8 |
dependencies = [ |
|
9 |
('django_rbac', '0009_auto_20221004_1343'), |
|
10 |
('a2_rbac', '0033_remove_old_operation_fk'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.DeleteModel( |
|
15 |
name='Operation', |
|
16 |
), |
|
17 |
] |
src/django_rbac/models.py | ||
---|---|---|
1 |
from django.db import models |
|
2 |
from django.utils.translation import gettext_lazy as _ |
|
3 | ||
4 |
from . import managers |
|
5 | ||
6 | ||
7 |
class Operation(models.Model): |
|
8 |
slug = models.CharField(max_length=32, verbose_name=_('slug'), unique=True) |
|
9 | ||
10 |
def natural_key(self): |
|
11 |
return [self.slug] |
|
12 | ||
13 |
def __str__(self): |
|
14 |
return str(self._registry.get(self.slug, self.slug)) |
|
15 | ||
16 |
def export_json(self): |
|
17 |
return {'slug': self.slug} |
|
18 | ||
19 |
@property |
|
20 |
def name(self): |
|
21 |
return str(self) |
|
22 | ||
23 |
@classmethod |
|
24 |
def register(cls, name, slug): |
|
25 |
cls._registry[slug] = name |
|
26 |
return cls(slug=slug) |
|
27 | ||
28 |
_registry = {} |
|
29 | ||
30 |
objects = managers.OperationManager() |
|
31 | ||
32 | ||
33 |
Operation._meta.natural_key = ['slug'] |
src/django_rbac/utils.py | ||
---|---|---|
77 | 77 | |
78 | 78 | |
79 | 79 |
def get_operation(operation_tpl): |
80 |
from . import models
|
|
80 |
from authentic2.a2_rbac import models
|
|
81 | 81 | |
82 | 82 |
operation, dummy = models.Operation.objects.get_or_create(slug=operation_tpl.slug) |
83 | 83 |
return operation |
tests/test_a2_rbac.py | ||
---|---|---|
19 | 19 |
from django.core.exceptions import ValidationError |
20 | 20 |
from django.core.management import call_command |
21 | 21 | |
22 |
from authentic2.a2_rbac.models import CHANGE_OP, MANAGE_MEMBERS_OP |
|
22 |
from authentic2.a2_rbac.models import CHANGE_OP, MANAGE_MEMBERS_OP, Operation
|
|
23 | 23 |
from authentic2.a2_rbac.models import OrganizationalUnit as OU |
24 | 24 |
from authentic2.a2_rbac.models import Permission, Role, RoleAttribute |
25 | 25 |
from authentic2.a2_rbac.utils import get_default_ou |
26 | 26 |
from authentic2.custom_user.models import User |
27 | 27 |
from authentic2.models import Service |
28 | 28 |
from authentic2.utils.misc import get_hex_uuid |
29 |
from django_rbac.models import Operation |
|
30 | 29 |
from tests.utils import login, request_select2, scoped_db_fixture |
31 | 30 | |
32 | 31 | |
... | ... | |
691 | 690 |
def test_direct(self, db, fixture): |
692 | 691 |
assert set(fixture.role.children(direct=True)) == {fixture.role, fixture.child} |
693 | 692 |
assert fixture.role.children(include_self=False, direct=True).get() == fixture.child |
693 | ||
694 | ||
695 |
def test_a2_rbac_operation_migration(migration, settings): |
|
696 |
migrate_from = [ |
|
697 |
('a2_rbac', '0030_organizationalunit_min_password_strength'), |
|
698 |
('django_rbac', '0009_auto_20221004_1343'), |
|
699 |
] |
|
700 |
migrate_to = [('a2_rbac', '0033_remove_old_operation_fk')] |
|
701 | ||
702 |
old_apps = migration.before(migrate_from) |
|
703 |
ContentType = old_apps.get_model('contenttypes', 'ContentType') |
|
704 |
Operation = old_apps.get_model('django_rbac', 'Operation') |
|
705 |
Permission = old_apps.get_model('a2_rbac', 'Permission') |
|
706 | ||
707 |
# check objects created by signal handlers |
|
708 |
base_operation = Operation.objects.get(slug='view') |
|
709 |
base_permission = Permission.objects.filter(operation=base_operation).first() |
|
710 | ||
711 |
# check other objects |
|
712 |
new_operation = Operation.objects.create(slug='test') |
|
713 |
Permission.objects.create( |
|
714 |
operation=new_operation, |
|
715 |
target_ct=ContentType.objects.get_for_model(ContentType), |
|
716 |
target_id=ContentType.objects.get_for_model(User).pk, |
|
717 |
) |
|
718 | ||
719 |
new_apps = migration.apply(migrate_to) |
|
720 |
ContentType = new_apps.get_model('contenttypes', 'ContentType') |
|
721 |
Operation = new_apps.get_model('a2_rbac', 'Operation') |
|
722 |
Permission = new_apps.get_model('a2_rbac', 'Permission') |
|
723 | ||
724 |
base_operation = Operation.objects.get(slug='view') |
|
725 |
assert ( |
|
726 |
Permission.objects.filter( |
|
727 |
operation_id=base_operation, |
|
728 |
target_ct_id=base_permission.target_ct.pk, |
|
729 |
target_id=base_permission.target_id, |
|
730 |
).count() |
|
731 |
== 1 |
|
732 |
) |
|
733 | ||
734 |
new_operation = Operation.objects.get(slug=new_operation.slug) |
|
735 |
assert ( |
|
736 |
Permission.objects.filter( |
|
737 |
operation=new_operation, |
|
738 |
target_ct=ContentType.objects.get_for_model(ContentType), |
|
739 |
target_id=ContentType.objects.get_for_model(User).pk, |
|
740 |
).count() |
|
741 |
== 1 |
|
742 |
) |
tests/test_commands.py | ||
---|---|---|
31 | 31 |
ADMIN_OP, |
32 | 32 |
MANAGE_MEMBERS_OP, |
33 | 33 |
VIEW_OP, |
34 |
Operation, |
|
34 | 35 |
OrganizationalUnit, |
35 | 36 |
Permission, |
36 | 37 |
Role, |
... | ... | |
40 | 41 |
from authentic2.custom_user.models import DeletedUser |
41 | 42 |
from authentic2.models import UserExternalId |
42 | 43 |
from authentic2_auth_oidc.models import OIDCAccount, OIDCProvider |
43 |
from django_rbac.models import Operation |
|
44 | 44 |
from django_rbac.utils import get_operation |
45 | 45 | |
46 | 46 |
from .utils import call_command, login |
tests/test_rbac.py | ||
---|---|---|
20 | 20 |
from django.db.models import Q |
21 | 21 |
from django.test.utils import CaptureQueriesContext |
22 | 22 | |
23 |
from authentic2.a2_rbac import models |
|
23 | 24 |
from authentic2.custom_user import backends |
24 |
from django_rbac import models, utils
|
|
25 |
from django_rbac import utils |
|
25 | 26 | |
26 | 27 |
OU = OrganizationalUnit = utils.get_ou_model() |
27 | 28 |
Permission = utils.get_permission_model() |
28 |
- |