Skip to content

Commit

Permalink
Allow registering models via a setting
Browse files Browse the repository at this point in the history
Fix test failure due to app readiness
  • Loading branch information
AlanCoding committed Aug 8, 2024
1 parent 7887216 commit f13e0c6
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 6 deletions.
3 changes: 3 additions & 0 deletions ansible_base/lib/dynamic_config/settings_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ def get_dab_settings(

dab_data['MANAGE_ORGANIZATION_AUTH'] = True

# Alternative to permission_registry.register
dab_data['ANSIBLE_BASE_RBAC_MODEL_REGISTRY'] = {}

dab_data['ORG_ADMINS_CAN_SEE_ALL_USERS'] = True

if 'ansible_base.oauth2_provider' in installed_apps:
Expand Down
16 changes: 12 additions & 4 deletions ansible_base/rbac/permission_registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def __init__(self):
self._tracked_relationships = set()
self._trackers = dict()

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

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

self.apps = apps
self.apps_ready = True

# Finish registering models
if self.team_model not in self._registry:
self._registry.add(self.team_model)
self.register(self.team_model)

for model_name, kwargs in settings.ANSIBLE_BASE_RBAC_MODEL_REGISTRY.items():
model = apps.get_model(model_name)
if model not in self._registry:
self.register(model, **kwargs)

# This will lock-down the registry, raising an error for any other registrations
self.apps_ready = True

# Do no specify sender for create_dab_permissions, because that is passed as app_config
# and we want to create permissions for external apps, not the dab_rbac app
Expand Down
19 changes: 19 additions & 0 deletions docs/apps/rbac/for_app_developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@ model of `MyModel`, or `None`.
TODO: Update to allow ManyToMany parent relationship in addition to ForeignKey
https://github.com/ansible/django-ansible-base/issues/78

#### Registering via a Setting

If you don't want to register models in your Django models definition,
then you can do the same thing with a setting.

```
ANSIBLE_BASE_RBAC_MODEL_REGISTRY = {
"my_app.mymodel": {"parent_field_name": "organization"}
}
```

You might want to do this if `MyModel` isn't from your own app,
and you want to avoid changing import order without additional thought.

Models declared here are registered _in addition to_ the calls to
`permission_registry.register`.
Note that settings should never contain imported python objects.
The model is referenced by app_label.model_name string.

#### Django Model, Apps, and Permission Constraints

It is fine to register models from multiple apps, but among the registered models,
Expand Down
9 changes: 7 additions & 2 deletions test_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,10 +341,15 @@ class Meta:
organization = models.ForeignKey(Organization, on_delete=models.CASCADE, related_name='public_data')


permission_registry.register(Organization, Inventory, Credential, Namespace, Team, Cow, UUIDModel, PositionModel, WeirdPerm, PublicData)
# Intentionally, for testing purposes, we register these models in settings
# - Inventory
# - Credential
# - ImmutableTask, parent_field_name=None

permission_registry.register(Organization, Namespace, Team, Cow, UUIDModel, PositionModel, WeirdPerm, PublicData)
permission_registry.register(ParentName, parent_field_name='my_organization')
permission_registry.register(CollectionImport, parent_field_name='namespace')
permission_registry.register(InstanceGroup, ImmutableTask, parent_field_name=None)
permission_registry.register(InstanceGroup, parent_field_name=None)
# Note that these polymorphic UUID models may not be useful in practice
# since permissions would be double-tracked on the sub-class table and the original UUIDModel table
permission_registry.register(AutoExtraUUIDModel, ManualExtraUUIDModel, parent_field_name='uuidmodel_ptr')
Expand Down
5 changes: 5 additions & 0 deletions test_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@
ANSIBLE_BASE_JWT_MANAGED_ROLES.append("System Auditor") # noqa: F821 this is set by dynamic settings for jwt_consumer
ANSIBLE_BASE_ALLOW_SINGLETON_USER_ROLES = True
ANSIBLE_BASE_ALLOW_SINGLETON_TEAM_ROLES = True
ANSIBLE_BASE_RBAC_MODEL_REGISTRY = {
"test_app.inventory": {"parent_field_name": "organization"},
"test_app.credential": {},
"test_app.immutabletask": {"parent_field_name": None},
}

ANSIBLE_BASE_USER_VIEWSET = 'test_app.views.UserViewSet'

Expand Down

0 comments on commit f13e0c6

Please sign in to comment.