Skip to content

Commit 76dc468

Browse files
committed
Handle case of permissions created too early
1 parent 1e561d6 commit 76dc468

File tree

4 files changed

+104
-6
lines changed

4 files changed

+104
-6
lines changed

ansible_base/rbac/management/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ansible_base.rbac import permission_registry
99
from ansible_base.rbac.remote import get_resource_prefix
1010

11+
from ._old import create_permissions_as_operation
1112
from .create_types import create_DAB_contenttypes
1213

1314
logger = logging.getLogger(__name__)
@@ -30,10 +31,17 @@ def create_dab_permissions(app_config, verbosity=2, interactive=True, using=DEFA
3031
logger.warning('DAB RBAC app is installed but no models are registered')
3132
return
3233

34+
try:
35+
dab_ct_cls = apps.get_model("dab_rbac", "RoleDefinition")
36+
except LookupError:
37+
logger.warning('Skipping DAB RBAC type and permission creation initial migration has not happened')
38+
return
39+
3340
try:
3441
dab_ct_cls = apps.get_model("dab_rbac", "DABContentType")
3542
except LookupError:
36-
logger.warning('Skipping DAB RBAC type and permission creation since models are not available')
43+
logger.info('Running historical permission creation method')
44+
create_permissions_as_operation(apps, None)
3745
return
3846

3947
if not router.allow_migrate_model(using, dab_ct_cls):
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import logging
2+
3+
from django.apps import apps as global_apps
4+
from django.contrib.contenttypes.management import create_contenttypes
5+
from django.db import DEFAULT_DB_ALIAS, router
6+
7+
from ansible_base.rbac import permission_registry
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def create_permissions_as_operation(apps, schema_editor):
13+
"""This is a snapshot of the old logic to create permissions
14+
15+
This is a matter of historical record and should not ever be changed.
16+
If an app calls the DAB RBAC method to create permissions in a migration,
17+
and that happened before the current custom content type model,
18+
this is where we intend to arrive.
19+
20+
The logic should forevermore behave the same as it _used_ to.
21+
This avoids complicating the migration support matrix.
22+
Do not fix bugs in this method.
23+
24+
As an inspirational quote
25+
> You gotta put your past, behind you
26+
"""
27+
# NOTE: this is a snapshot version of the DAB RBAC permission creation logic
28+
# normally this runs post_migrate, but this exists to keep old logic
29+
for app_label in {'ansible', 'container', 'core', 'galaxy'}:
30+
app_config = global_apps.get_app_config(app_label)
31+
using = DEFAULT_DB_ALIAS
32+
33+
# Ensure that contenttypes are created for this app. Needed if
34+
# 'ansible_base.rbac' is in INSTALLED_APPS before
35+
# 'django.contrib.contenttypes'.
36+
create_contenttypes(app_config, verbosity=2, interactive=True, using=using, apps=apps)
37+
38+
try:
39+
app_config = apps.get_app_config(app_label)
40+
ContentType = apps.get_model("contenttypes", "ContentType")
41+
Permission = apps.get_model("dab_rbac", "DABPermission")
42+
except LookupError:
43+
return
44+
45+
if not router.allow_migrate_model(using, Permission):
46+
return
47+
48+
# This will hold the permissions we're looking for as (content_type, (codename, name))
49+
searched_perms = []
50+
# The codenames and ctypes that should exist.
51+
ctypes = set()
52+
for klass in app_config.get_models():
53+
if not permission_registry.is_registered(klass):
54+
continue
55+
# Force looking up the content types in the current database
56+
# before creating foreign keys to them.
57+
ctype = ContentType.objects.db_manager(using).get_for_model(klass, for_concrete_model=False)
58+
59+
ctypes.add(ctype)
60+
61+
for action in klass._meta.default_permissions:
62+
searched_perms.append(
63+
(
64+
ctype,
65+
(
66+
f"{action}_{klass._meta.model_name}",
67+
f"Can {action} {klass._meta.verbose_name_raw}",
68+
),
69+
)
70+
)
71+
for codename, name in klass._meta.permissions:
72+
searched_perms.append((ctype, (codename, name)))
73+
74+
# Find all the Permissions that have a content_type for a model we're
75+
# looking for. We don't need to check for codenames since we already have
76+
# a list of the ones we're going to create.
77+
all_perms = set(Permission.objects.using(using).filter(content_type__in=ctypes).values_list("content_type", "codename"))
78+
79+
perms = []
80+
for ct, (codename, name) in searched_perms:
81+
if (ct.pk, codename) not in all_perms:
82+
permission = Permission()
83+
permission._state.db = using
84+
permission.codename = codename
85+
permission.name = name
86+
permission.content_type = ct
87+
perms.append(permission)
88+
89+
Permission.objects.using(using).bulk_create(perms)
90+
for perm in perms:
91+
logger.debug("Adding permission '%s'" % perm)
92+
93+
logger.info('FINISHED CREATING PERMISSIONS')

ansible_base/rbac/service_api/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
from ansible_base.lib.utils.views.django_app_api import AnsibleBaseDjangoAppApiView
88
from ansible_base.lib.utils.views.permissions import try_add_oauth2_scope_permission
9-
from ansible_base.rest_filters.rest_framework import ansible_id_backend
109
from ansible_base.resource_registry.views import HasResourceRegistryPermissions
10+
from ansible_base.rest_filters.rest_framework import ansible_id_backend
1111

1212
from ..models import DABContentType, DABPermission, RoleTeamAssignment, RoleUserAssignment
1313
from . import serializers as service_serializers

ansible_base/resource_registry/rest_client.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,7 @@ def _make_request(
118118
content = resp.text
119119

120120
# Re-raise with more context
121-
raise requests.exceptions.HTTPError(
122-
f"{e}\nResponse content: {content}",
123-
response=resp
124-
) from None
121+
raise requests.exceptions.HTTPError(f"{e}\nResponse content: {content}", response=resp) from None
125122
return resp
126123

127124
def _get_request_dict(self, data: ResourceRequestBody):

0 commit comments

Comments
 (0)