Skip to content

Commit

Permalink
New pivot, remove extra association bases, rely on viewset filter_que…
Browse files Browse the repository at this point in the history
…ryset
  • Loading branch information
AlanCoding committed May 8, 2024
1 parent 5cf2772 commit cb38c1c
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 43 deletions.
61 changes: 18 additions & 43 deletions ansible_base/lib/routers/association_resource_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def get_queryset(self):
return getattr(parent_instance, self.association_fk).all()


class BasicAssociationSerializer(serializers.Serializer):
class AssociationSerializerBase(serializers.Serializer):
"""Serializer used for associating related objects, where all those related objects are allowed
It is expected that subclasses will set target_queryset which gives the queryset
Expand All @@ -75,27 +75,8 @@ def __init__(self, *args, **kwargs):
self.fields['instances'] = serializers.PrimaryKeyRelatedField(queryset=self.get_queryset_on_init(request), many=True)


class FilteredAssociationSerializer(BasicAssociationSerializer):
"""Serializer used for associating related objects, with DAB RBAC filtering applied
This applies a filtered queryset to instances so that only those the request user can view
are considered, and objects the user can not see will result in a 400 as if it did not exist"""

def get_queryset_on_init(self, request) -> QuerySet:
cls = self.target_queryset.model
return cls.access_qs(request.user, queryset=self.target_queryset)


class UserAssociationSerializer(BasicAssociationSerializer):
"""Serializer used for associating related users, using DAB RBAC rules for user visibility"""

def get_queryset_on_init(self, request) -> QuerySet:
# Use in-line import because not all apps may be using RBAC
from ansible_base.rbac.policies import visible_users

return visible_users(request.user, queryset=self.target_queryset)


# Registry contains subclasses of AssociationSerializerBase indexed by name
# this prevents duplicate names which would cause schema to not render correctly
serializer_registry = {}


Expand Down Expand Up @@ -151,33 +132,27 @@ def disassociate(self, request, **kwargs):

return Response(status=status.HTTP_204_NO_CONTENT)

def get_association_parent_serializer_class(self, cls: Type[Model]) -> Type[serializers.Serializer]:
if self.action == 'disassociate':
return BasicAssociationSerializer
def get_association_queryset(self) -> QuerySet:
if self.queryset:
qs = self.queryset
cls = self.queryset.model
else:
cls = self.serializer_class.Meta.model
qs = cls.objects.all()

# Implied that action is associate
if 'ansible_base.rbac' in settings.INSTALLED_APPS and permission_registry.is_registered(cls):
return FilteredAssociationSerializer
elif 'ansible_base.rbac' in settings.INSTALLED_APPS and cls._meta.model_name == 'user':
return UserAssociationSerializer
return BasicAssociationSerializer
if self.action == 'associate':
return self.filter_queryset(qs)
return qs

def get_serializer_class(self):
if self.action in ('disassociate', 'associate'):
if self.queryset:
qs = self.queryset
cls = self.queryset.model
else:
cls = self.serializer_class.Meta.model
qs = cls.objects.all()
qs = self.get_association_queryset()

# qs = self.filter_queryset(qs)
if parent_cls := self.get_association_parent_serializer_class(cls):
cls_name = f'{qs.model.__name__}{parent_cls.__name__}'
cls_name = f'{self.__name__}AssociationSerializer'

if cls_name not in serializer_registry:
serializer_registry[cls_name] = type(cls_name, (parent_cls,), {'target_queryset': qs})
return serializer_registry[cls_name]
if cls_name not in serializer_registry:
serializer_registry[cls_name] = type(cls_name, (AssociationSerializerBase,), {'target_queryset': qs})
return serializer_registry[cls_name]

return super().get_serializer_class()

Expand Down
6 changes: 6 additions & 0 deletions test_app/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@
# in the filter_queryset method, but in some endpoints we show all users
class RelatedUserViewSet(views.UserViewSet):
def filter_queryset(self, qs):
"Do not filter users for the related list view, /organizations/42/users/"
return self.apply_optimizations(qs)

def get_association_queryset(self):
"Use RBAC filter when associating new users, /organizations/42/users/associate/"
qs = super.get_assocation_queryset()
return super().filter_queryset(qs)


router.register(
r'related_fields_test_models',
Expand Down

0 comments on commit cb38c1c

Please sign in to comment.