diff --git a/backend/sophon/core/serializers.py b/backend/sophon/core/serializers.py index 089a5cd..68e9a08 100644 --- a/backend/sophon/core/serializers.py +++ b/backend/sophon/core/serializers.py @@ -1,5 +1,5 @@ from django.contrib.auth.models import User -from rest_framework.serializers import Serializer, ModelSerializer, CharField +from rest_framework.serializers import Serializer, ModelSerializer class NoneSerializer(Serializer): @@ -29,3 +29,20 @@ class UserSerializer(ModelSerializer): "last_name", "email", ) + + +def dynamic_serializer(_model, _fields, _read_only_fields): + """ + :param _model: The model the serializer is for. + :param _fields: The fields to pass to the meta of the serializer class. + :param _read_only_fields: The read_only_fields to pass to the meta of the serializer class. + :return: a serializer class with the specified parameters. + """ + + class DynamicSerializer(ModelSerializer): + class Meta: + model = _model + fields = _fields + read_only_fields = _read_only_fields + + return DynamicSerializer diff --git a/backend/sophon/notebooks/models.py b/backend/sophon/notebooks/models.py index 69ae1ce..448fb1d 100644 --- a/backend/sophon/notebooks/models.py +++ b/backend/sophon/notebooks/models.py @@ -11,8 +11,10 @@ import docker.models.networks import docker.models.volumes from django.contrib.auth.models import User from django.db import models +from rest_framework.serializers import ModelSerializer from sophon.core.models import SophonGroupModel, ResearchGroup +from sophon.core.serializers import dynamic_serializer from sophon.notebooks.apache import db as apache_db from sophon.notebooks.apache import get_ephemeral_port, base_domain, http_protocol from sophon.notebooks.docker import client as docker_client @@ -128,18 +130,27 @@ class Notebook(SophonGroupModel): "legacy_notebook_url", } + @classmethod + def get_member_fields(cls) -> set[str]: + """ + :return: The :class:`set` of field names (as :class:`str`) that will be added to the serializer for a member of the group the notebook belongs to. + """ + return { + "jupyter_token", + } + @classmethod def get_editable_fields(cls) -> set[str]: return { "name", "locked_by", + "container_image", } @classmethod def get_administrable_fields(cls) -> set[str]: return { "project", - "container_image", } @classmethod @@ -151,6 +162,17 @@ class Notebook(SophonGroupModel): "container_image", } + def get_access_serializer(self, user: User) -> t.Type[ModelSerializer]: + access = super().get_access_serializer(user) + if self.can_edit(user): + # noinspection PyUnresolvedReferences + fields = tuple(set(access.Meta.fields).union(self.get_member_fields())) + # noinspection PyUnresolvedReferences + read_only_fields = tuple(set(access.Meta.read_only_fields).union(self.get_member_fields())) + return dynamic_serializer(_model=self.__class__, _fields=fields, _read_only_fields=read_only_fields) + else: + return access + @property def container_name(self) -> str: """