import typing from collections import defaultdict from django.db.models.query import QuerySet from rest_framework import serializers from apps.authentication.api.v1.serializers import serializer as auth_serializer from apps.authorization.models import ( Role, Permissions, UserRelations, Page ) class PageSerializer(serializers.ModelSerializer): """ Serialize every front-end page """ class Meta: model = Page fields = [ 'id', 'name', 'code', 'is_active' ] def to_representation(self, instance): """ custom output of serializer """ representation = super().to_representation(instance) representation['permissions'] = PermissionSerializer( Permissions.objects.filter(page=instance), many=True ).data return representation class PermissionSerializer(serializers.ModelSerializer): """ Serialize permissions """ class Meta: model = Permissions fields = [ 'id', 'name', 'description', 'category', 'page', 'is_active', 'modify_state' ] def to_representation(self, instance): representation = super().to_representation(instance) representation['page'] = instance.page.name return representation @classmethod def permissions_structure_output(cls, permissions) -> typing.List[dict]: """ set a structure for permissions """ if isinstance(permissions, QuerySet): permissions = permissions.select_related('page') grouped = defaultdict(set) modifier_states = defaultdict(set) for permission in permissions: if getattr(permission, 'is_active', True): page_name = getattr(permission.page, 'name', None) if page_name: grouped[page_name].add(permission.name) grouped[f'modify_state_{page_name}'].add(permission.modify_state) modifier_states[page_name].add(permission.modify_state) structure = [] for page, access in grouped.items(): if page.startswith('modify_state_'): continue modify_states = grouped.get(f'modify_state_{page}', set()) if False not in modify_states: continue structure.append({ 'page_name': page, 'page_access': sorted(list(access)), }) return structure class RoleSerializer(serializers.ModelSerializer): """ Serialize roles of user """ class Meta: model = Role fields = [ 'id', 'role_name', 'parent_role', 'description', 'type', 'permissions' # noqa ] extra_kwargs = { 'permissions': {'required': False} # permissions not required for some roles # noqa } def to_representation(self, instance): """ using @to_representation for many_to_many permissions in response """ representation = super().to_representation(instance) representation['type'] = auth_serializer.OrganizationTypeSerializer(instance.type).data representation['permissions'] = PermissionSerializer(instance.permissions, many=True).data if instance.parent_role: representation['parent_role'] = {'name': instance.parent_role.role_name, 'id': instance.parent_role.id} return representation class UserRelationSerializer(serializers.ModelSerializer): """ Serialize relations of user like: organizations, roles, permissions """ class Meta: model = UserRelations fields = [ 'id', 'user', 'organization', 'role', 'permissions', # noqa ] extra_kwargs = { 'organization': { 'required': False }, 'permissions': { # noqa 'required': False }, 'role': { 'required': False } } def to_representation(self, instance): """ custom output for serializer """ representation = super().to_representation(instance) if isinstance(instance, UserRelations): if instance.user: representation['user'] = auth_serializer.UserSerializer(instance.user).data if instance.organization: representation['organization'] = { "id": instance.organization.id, "name": instance.organization.name, "ownership_code": instance.organization.ownership_code } if instance.role: representation['role'] = { "id": instance.role.id, "role_name": instance.role.role_name, 'type': { 'key': instance.role.type.key } } if instance.permissions: # noqa # set permissions by a default structure like: # 'page permission':[element permissions] permissions = instance.permissions.filter(is_active=True) representation['permissions'] = PermissionSerializer.permissions_structure_output( list(permissions) + list(instance.role.permissions.all()) ) return representation def update(self, instance, validated_data): """ update user relation object """ instance.role = validated_data.get('role', instance.role) instance.organization = validated_data.get('organization', instance.organization) instance.save() if validated_data.get('permissions'): instance.permissions.clear() instance.permissions.add(*(validated_data.get('permissions', instance.permissions))) return instance