Skip to content

Commit 20f43da

Browse files
authored
Allow registering models via a setting (ansible#533)
1 parent f9efc89 commit 20f43da

File tree

5 files changed

+45
-5
lines changed

5 files changed

+45
-5
lines changed

ansible_base/lib/dynamic_config/settings_logic.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ def get_dab_settings(
223223

224224
dab_data['MANAGE_ORGANIZATION_AUTH'] = True
225225

226+
# Alternative to permission_registry.register
227+
dab_data['ANSIBLE_BASE_RBAC_MODEL_REGISTRY'] = {}
228+
226229
dab_data['ORG_ADMINS_CAN_SEE_ALL_USERS'] = True
227230

228231
if 'ansible_base.resource_registry' in installed_apps:

ansible_base/rbac/permission_registry.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def __init__(self):
3232
self._tracked_relationships = set()
3333
self._trackers = dict()
3434

35-
def register(self, *args, parent_field_name='organization'):
35+
def register(self, *args: Type[Model], parent_field_name: Optional[str] = 'organization'):
3636
if self.apps_ready:
3737
raise RuntimeError('Cannot register model to permission_registry after apps are ready')
3838
for cls in args:
@@ -142,16 +142,24 @@ def create_managed_roles(self, apps) -> list[tuple[Model, bool]]:
142142
ret.append((rd, created))
143143
return ret
144144

145-
def call_when_apps_ready(self, apps, app_config):
145+
def call_when_apps_ready(self, apps, app_config) -> None:
146146
from ansible_base.rbac import triggers
147147
from ansible_base.rbac.evaluations import bound_has_obj_perm, bound_singleton_permissions, connect_rbac_methods
148148
from ansible_base.rbac.management import create_dab_permissions
149149

150150
self.apps = apps
151-
self.apps_ready = True
152151

152+
# Finish registering models
153153
if self.team_model not in self._registry:
154-
self._registry.add(self.team_model)
154+
self.register(self.team_model)
155+
156+
for model_name, kwargs in settings.ANSIBLE_BASE_RBAC_MODEL_REGISTRY.items():
157+
model = apps.get_model(model_name)
158+
if model not in self._registry:
159+
self.register(model, **kwargs)
160+
161+
# This will lock-down the registry, raising an error for any other registrations
162+
self.apps_ready = True
155163

156164
# Do no specify sender for create_dab_permissions, because that is passed as app_config
157165
# and we want to create permissions for external apps, not the dab_rbac app

docs/apps/rbac/for_app_developers.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ model of `MyModel`, or `None`.
140140
TODO: Update to allow ManyToMany parent relationship in addition to ForeignKey
141141
https://github.com/ansible/django-ansible-base/issues/78
142142

143+
#### Registering via a Setting
144+
145+
If you don't want to register models in your Django models definition,
146+
then you can do the same thing with a setting.
147+
148+
```
149+
ANSIBLE_BASE_RBAC_MODEL_REGISTRY = {
150+
"my_app.mymodel": {"parent_field_name": "organization"}
151+
}
152+
```
153+
154+
You might want to do this if `MyModel` isn't from your own app,
155+
and you want to avoid changing import order without additional thought.
156+
157+
Models declared here are registered _in addition to_ the calls to
158+
`permission_registry.register`.
159+
Note that settings should never contain imported python objects.
160+
The model is referenced by app_label.model_name string.
161+
143162
#### Django Model, Apps, and Permission Constraints
144163

145164
It is fine to register models from multiple apps, but among the registered models,

test_app/models.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,12 @@ class Meta:
353353
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='public_data')
354354

355355

356-
permission_registry.register(Organization, Inventory, Credential, Namespace, Team, Cow, UUIDModel, PositionModel, WeirdPerm, PublicData)
356+
# Intentionally, for testing purposes, we register these models in settings
357+
# - Inventory
358+
# - Credential
359+
# - ImmutableTask, parent_field_name=None
360+
361+
permission_registry.register(Organization, Namespace, Team, Cow, UUIDModel, PositionModel, WeirdPerm, PublicData)
357362
permission_registry.register(ParentName, parent_field_name='my_organization')
358363
permission_registry.register(CollectionImport, parent_field_name='namespace')
359364
permission_registry.register(InstanceGroup, ImmutableTask, LogEntry, parent_field_name=None)

test_app/settings.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@
185185
ANSIBLE_BASE_JWT_MANAGED_ROLES.append("System Auditor") # noqa: F821 this is set by dynamic settings for jwt_consumer
186186
ANSIBLE_BASE_ALLOW_SINGLETON_USER_ROLES = True
187187
ANSIBLE_BASE_ALLOW_SINGLETON_TEAM_ROLES = True
188+
ANSIBLE_BASE_RBAC_MODEL_REGISTRY = {
189+
"test_app.inventory": {"parent_field_name": "organization"},
190+
"test_app.credential": {},
191+
"test_app.immutabletask": {"parent_field_name": None},
192+
}
188193
ANSIBLE_BASE_OAUTH2_PROVIDER_PERMISSIONS_CHECK_IGNORED_VIEWS = ["drf_spectacular.views.SpectacularSwaggerView"]
189194
ALLOW_SHARED_RESOURCE_CUSTOM_ROLES = True # Allow making custom roles with org change permission, for example
190195
ALLOW_LOCAL_ASSIGNING_JWT_ROLES = False

0 commit comments

Comments
 (0)