Skip to content

Commit ea5662b

Browse files
committed
Manually set pk to next highest when pointer may not be reset
1 parent 41ca86f commit ea5662b

File tree

4 files changed

+48
-18
lines changed

4 files changed

+48
-18
lines changed

ansible_base/rbac/api/queries.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
from typing import Union
2+
13
from django.db.models import Model
24

35
from ..models import DABContentType, get_evaluation_model
6+
from ..remote import RemoteObject
47

58

6-
def assignment_qs_user_to_obj(actor: Model, obj: Model):
9+
def assignment_qs_user_to_obj(actor: Model, obj: Union[Model, RemoteObject]):
710
"""Querset of assignments (team or user) that grants the actor any form of permission to obj"""
811
evaluation_cls = get_evaluation_model(obj)
912
ct = DABContentType.objects.get_for_model(obj)
@@ -18,7 +21,7 @@ def assignment_qs_user_to_obj(actor: Model, obj: Model):
1821
return (global_assignment_qs | obj_assignment_qs).distinct()
1922

2023

21-
def assignment_qs_user_to_obj_perm(actor: Model, obj: Model, permission: Model):
24+
def assignment_qs_user_to_obj_perm(actor: Model, obj: Union[Model, RemoteObject], permission: Model):
2225
"""Queryset of assignments that grants this specific permission to this specific object"""
2326
evaluation_cls = get_evaluation_model(obj)
2427
ct = DABContentType.objects.get_for_model(obj)

ansible_base/rbac/management/__init__.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import re
23
from collections import defaultdict
34

45
from django.apps import apps as global_apps
@@ -42,6 +43,30 @@ def create_dab_permissions(app_config, verbosity=2, interactive=True, using=DEFA
4243
sync_DAB_permissions(verbosity=verbosity, using=using, apps=global_apps)
4344

4445

46+
def is_safe_identifier(name: str) -> bool:
47+
"""Returns True or False, name is a valid identifier in postgres, just for safety"""
48+
return re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', name) is not None
49+
50+
51+
def reset_ct_sequence(ct_cls):
52+
table = ct_cls._meta.db_table
53+
pk_column = ct_cls._meta.pk.column
54+
# This case should never be hit, but for database safety we have this
55+
if not is_safe_identifier(table) or not is_safe_identifier(pk_column):
56+
raise ValueError(f"Unsafe identifier: {table}.{pk_column}")
57+
58+
logger.info('Updating the serial sequence of DABContentType model')
59+
with connection.cursor() as cursor:
60+
cursor.execute(
61+
f"""
62+
SELECT setval(
63+
pg_get_serial_sequence(%s, %s),
64+
(SELECT MAX({pk_column}) FROM {table})
65+
)""",
66+
[table, pk_column],
67+
)
68+
69+
4570
def sync_DAB_permissions(verbosity=2, using=DEFAULT_DB_ALIAS, apps=global_apps):
4671
"""Idepotent method to set database types and permissions for DAB RBAC
4772
@@ -51,7 +76,7 @@ def sync_DAB_permissions(verbosity=2, using=DEFAULT_DB_ALIAS, apps=global_apps):
5176
DABContentType = apps.get_model("dab_rbac", "DABContentType")
5277
Permission = apps.get_model("dab_rbac", "DABPermission")
5378

54-
create_DAB_contenttypes(verbosity=verbosity, using=using, apps=apps)
79+
new_cts = create_DAB_contenttypes(verbosity=verbosity, using=using, apps=apps)
5580

5681
# This will hold the permissions we're looking for as (content_type, (codename, name))
5782
searched_perms = []
@@ -106,15 +131,5 @@ def sync_DAB_permissions(verbosity=2, using=DEFAULT_DB_ALIAS, apps=global_apps):
106131

107132
# Reset the sequence to avoid PK collision later
108133
if connection.vendor == 'postgresql':
109-
table = DABContentType._meta.db_table
110-
pk_column = DABContentType._meta.pk.column
111-
with connection.cursor() as cursor:
112-
cursor.execute(
113-
f"""
114-
SELECT setval(
115-
pg_get_serial_sequence(%s, %s),
116-
(SELECT MAX({pk_column}) FROM {table})
117-
)
118-
""",
119-
[table, pk_column],
120-
)
134+
if new_cts:
135+
reset_ct_sequence(DABContentType)

ansible_base/rbac/management/create_types.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ def create_DAB_contenttypes(
2121
verbosity=2,
2222
using=DEFAULT_DB_ALIAS,
2323
apps=global_apps,
24-
):
24+
) -> list:
2525
"""Create DABContentType for models in the given app.
2626
2727
This is significantly different from the ContentType post-migrate method,
2828
because that creates types for all apps, and so this is only called one app at a time.
2929
DAB RBAC runs its post_migration logic just once, because the model list
3030
comes from the permission registry.
31+
32+
Returns a list of the _new_ content types created
3133
"""
3234
DABContentType = apps.get_model("dab_rbac", "DABContentType")
3335
ContentType = apps.get_model("contenttypes", "ContentType")
@@ -52,9 +54,12 @@ def create_DAB_contenttypes(
5254
real_ct = ContentType.objects.get_for_model(model)
5355
if not DABContentType.objects.filter(id=real_ct.id).exists():
5456
ct_item_data['id'] = real_ct.id
57+
else:
58+
current_max_id = DABContentType.objects.order_by('-id').values_list('id', flat=True).first() or 0
59+
ct_item_data['id'] = current_max_id + 1
5560
ct_data.append(ct_item_data)
5661
if not ct_data:
57-
return
62+
return []
5863

5964
# To make usage earier in a transitional period, we will set the content type
6065
# of any new entries created here to the id of its corresponding ContentType
@@ -67,6 +72,7 @@ def create_DAB_contenttypes(
6772
for ct in cts:
6873
logger.debug("Adding DAB content type " f"'{ct.service}:{ct.app_label} | {ct.model}'")
6974

75+
updated_ct = 0
7076
for ct in DABContentType.objects.all():
7177
if not permission_registry.is_registered(ct.model_class()):
7278
logger.warning(f'{ct.model} is a stale content type in DAB RBAC')
@@ -76,3 +82,9 @@ def create_DAB_contenttypes(
7682
if ct.parent_content_type != parent_content_type:
7783
ct.parent_content_type = parent_content_type
7884
ct.save(update_fields=['parent_content_type'])
85+
updated_ct += 1
86+
if updated_ct:
87+
# If this happens outside of the migration when the entries were created, that would be notable
88+
logger.info(f'Updated the parent reference of {updated_ct} content types')
89+
90+
return cts

test_app/tests/rbac/api/test_access_lists.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import pytest
22

3-
from ansible_base.rbac.models import RoleDefinition
43
from ansible_base.lib.utils.response import get_relative_url
54
from ansible_base.rbac import permission_registry
5+
from ansible_base.rbac.models import RoleDefinition
66
from test_app.models import Team, User
77

88

0 commit comments

Comments
 (0)