def return_queryset_if_user_has_permissions(queryset, user, *models, customer_queryset=None): """ Checks whether the user has permissions to access the queryset. 1. If the passed queryset is falsy, we return None. 2. If the user making the request is a customer, the elements on the queryset belonging to it are returned. 4. If the user is not a customer, but the user has permissions to view the necessary models, the whole queryset is returned. 5. If the user has no permissions to view it, an error is raised. :param queryset: Django QuerySet being accessed :param user: the user from the current request / session :param models: Django models that user has to have permissions to :param customer_queryset: [Optional] If the customer property is nested on the model, a custom filtered queryset can be passed :return: None | Django QuerySet | Exception raised """ from users.utils import is_customer, user_has_view_permission if is_customer(user): if customer_queryset: return customer_queryset return queryset.filter(customer__user=user) if user_has_view_permission(*models)(user): return queryset raise VenepaikkaGraphQLError( _("You do not have permission to perform this action."))
def return_node_if_user_has_permissions(node, user, *models): """ Checks whether the user has permissions to access this node / model. 1. If the passed node is falsy, we return None. 2. Otherwise, try to get the user this node belongs to (get_node_user) 3. If the node belongs to the user making the request, the node / model is returned. 4. If the node does not belong to the user, but the user has permissions to view the necessary models, the node / model is still returned. 5. If the node does not belong to the user and the user has no permissions to view it, an error is raised. :param node: Graphene Node or Django Model being accessed :param user: the user from the current request / session :param models: Django models that user has to have permissions to :return: None | Django Model or Graphene Node | Exception raised """ from users.utils import user_has_view_permission if not node: return None node_user = get_node_user(node) if (node_user and node_user == user) or user_has_view_permission(*models)(user): return node else: raise VenepaikkaGraphQLError( _("You do not have permission to perform this action."))
def get_node(cls, info, id): node = super().get_node(info, id) user = info.context.user # The BerthSwitchOffer node does not have a customer, so it has to be retrieved # from the node.customer and cannot use return_node_if_user_has_permissions if user_has_view_permission( BerthSwitchOffer, BerthLease, BerthApplication)(user) or ( node.lease.customer and hasattr(node.lease.customer, "user") and node.lease.customer.user == user): return node raise PermissionError( _("You do not have permission to perform this action."))
def resolve_piers(info, **kwargs): min_width = kwargs.get("min_berth_width") min_length = kwargs.get("min_berth_length") application_global_id = kwargs.get("for_application") harbor_id = kwargs.get("harbor_id") # Filter out piers with no berths that fit the # passed dimensions only if the dimensions were given. # Otherwise, return the whole list of piers. has_dimensions_filter = min_width or min_length if has_dimensions_filter and application_global_id: raise VenepaikkaGraphQLError( _("You cannot filter by dimension (width, length) and application a the same time." )) if application_global_id: user = info.context.user if (user and user.is_authenticated and user_has_view_permission(BerthApplication)(user)): application = get_node_from_global_id( info, application_global_id, only_type=BerthApplicationNode) min_width = application.boat_width # NOTE: not setting min_length here on purpose. # See VEN-1251: berths that are too short need to be shown in the UI else: raise VenepaikkaGraphQLError( _("You do not have permission to perform this action")) if harbor_id: query = Pier.objects.filter(harbor_id=harbor_id) else: query = Pier.objects.all() suitable_berth_types = BerthType.objects.all() if min_width: suitable_berth_types = suitable_berth_types.filter( width__gte=min_width) if min_length: suitable_berth_types = suitable_berth_types.filter( length__gte=min_length) berth_queryset = Berth.objects.select_related("berth_type").filter( berth_type__in=suitable_berth_types) query = query.prefetch_related( Prefetch("berths", queryset=berth_queryset), "suitable_boat_types", "harbor__translations", "harbor__availability_level__translations", "harbor__municipality__translations", ).select_related("harbor", "harbor__availability_level", "harbor__municipality") if has_dimensions_filter: dimensions_filter = Q() if min_width: dimensions_filter &= Q(berths__berth_type__width__gte=min_width) if min_length: dimensions_filter &= Q(berths__berth_type__length__gte=min_length) query = query.annotate( berth_count=Count("berths", filter=dimensions_filter)).filter( berth_count__gt=0) return query
class UpdateBerthApplication(graphene.ClientIDMutation): class Input(UpdateBerthApplicationInput): pass berth_application = graphene.Field(BerthApplicationNode) @classmethod def validate_application_status(cls, application, info, input): if application.status != ApplicationStatus.PENDING: if is_customer(info.context.user): raise VenepaikkaGraphQLError( _("Cannot modify the application once it has been processed" )) # If the input receives explicitly customerId: None if "customer_id" in input and input.get("customer_id") is None: raise VenepaikkaGraphQLError( _("Customer cannot be disconnected from processed applications" )) def get_nodes_to_check(info, **input): application = get_node_from_global_id(info, input.get("id"), only_type=BerthApplicationNode, nullable=True) return [application] @classmethod @check_user_is_authorised( get_nodes_to_check=get_nodes_to_check, model_checks=[ user_has_view_permission(CustomerProfile, BerthLease), user_has_change_permission(BerthApplication), ], ) @transaction.atomic def mutate_and_get_payload(cls, root, info, **input): from customers.schema import ProfileNode application = get_node_from_global_id(info, input.pop("id"), only_type=BerthApplicationNode) cls.validate_application_status(application, info, input) # This allows for getting explicit None values if "customer_id" in input: customer_id = input.pop("customer_id", None) if is_customer(info.context.user): raise VenepaikkaGraphQLError( _("A customer cannot modify the customer connected to the application" )) input["customer"] = get_node_from_global_id(info, customer_id, only_type=ProfileNode, nullable=True) if remove_choices := input.pop("remove_choices", []): for choice_id in remove_choices: try: choice = HarborChoice.objects.get(id=from_global_id( choice_id, node_type=HarborChoiceType)) choice.delete() except HarborChoice.DoesNotExist: pass if add_choices := input.pop("add_choices", []): for choice in add_choices: HarborChoice.objects.get_or_create( harbor_id=from_global_id(choice.get("harbor_id")), priority=choice.get("priority"), application=application, )