diff --git a/entry/api_v2/serializers.py b/entry/api_v2/serializers.py index d83c0c0b8..e0cb419bc 100644 --- a/entry/api_v2/serializers.py +++ b/entry/api_v2/serializers.py @@ -87,6 +87,7 @@ class EntryAttributeType(TypedDict): id: Optional[int] type: int is_mandatory: bool + is_readable: bool value: EntryAttributeValue schema: EntityAttributeType @@ -142,6 +143,7 @@ class EntryAttributeTypeSerializer(serializers.Serializer): id = serializers.IntegerField(allow_null=True) type = serializers.IntegerField() is_mandatory = serializers.BooleanField() + is_readable = serializers.BooleanField() value = EntryAttributeValueSerializer() schema = EntityAttributeTypeSerializer() @@ -590,15 +592,23 @@ def get_default_attr_value(type: int) -> EntryAttributeValue: .order_by("index") ) + user: User = self.context["request"].user + attrinfo: List[EntryAttributeType] = [] for entity_attr in entity_attrs: attr = entity_attr.attr_list[0] if entity_attr.attr_list else None - value = get_attr_value(attr) if attr else get_default_attr_value(entity_attr.type) + is_readable = user.has_permission(entity_attr, ACLType.Readable) + value = ( + get_attr_value(attr) + if attr and is_readable + else get_default_attr_value(entity_attr.type) + ) attrinfo.append( { "id": attr.id if attr else None, "type": entity_attr.type, "is_mandatory": entity_attr.is_mandatory, + "is_readable": is_readable, "value": value, "schema": { "id": entity_attr.id, diff --git a/entry/tests/test_api_v2.py b/entry/tests/test_api_v2.py index a15800825..f3206e95c 100644 --- a/entry/tests/test_api_v2.py +++ b/entry/tests/test_api_v2.py @@ -129,6 +129,7 @@ def test_retrieve_entry(self): "value": {"as_string": "hoge"}, "id": entry.attrs.get(schema__name="val").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="val").schema.id, "name": "val", @@ -151,6 +152,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="ref").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="ref").schema.id, "name": "ref", @@ -175,6 +177,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="name").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="name").schema.id, "name": "name", @@ -188,6 +191,7 @@ def test_retrieve_entry(self): "value": {"as_boolean": False}, "id": entry.attrs.get(schema__name="bool").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="bool").schema.id, "name": "bool", @@ -201,6 +205,7 @@ def test_retrieve_entry(self): "value": {"as_string": "2018-12-31"}, "id": entry.attrs.get(schema__name="date").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="date").schema.id, "name": "date", @@ -219,6 +224,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="group").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="group").schema.id, "name": "group", @@ -239,6 +245,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="groups").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="groups").schema.id, "name": "groups", @@ -257,6 +264,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="role").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="role").schema.id, "name": "role", @@ -277,6 +285,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="roles").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="roles").schema.id, "name": "roles", @@ -290,6 +299,7 @@ def test_retrieve_entry(self): "value": {"as_string": "fuga"}, "id": entry.attrs.get(schema__name="text").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="text").schema.id, "name": "text", @@ -303,6 +313,7 @@ def test_retrieve_entry(self): "value": {"as_array_string": ["foo", "bar"]}, "id": entry.attrs.get(schema__name="vals").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="vals").schema.id, "name": "vals", @@ -327,6 +338,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="refs").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="refs").schema.id, "name": "refs", @@ -363,6 +375,7 @@ def test_retrieve_entry(self): }, "id": entry.attrs.get(schema__name="names").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="names").schema.id, "name": "names", @@ -376,6 +389,7 @@ def test_retrieve_entry(self): "value": {"as_string": AttrTypeStr.DEFAULT_VALUE}, "id": None, "is_mandatory": False, + "is_readable": True, "schema": { "id": self.entity.attrs.get(name="opt").id, "name": "opt", @@ -423,6 +437,46 @@ def test_retrieve_entry_without_permission(self): resp = self.client.get("/entry/api/v2/%d/" % entry.id) self.assertEqual(resp.status_code, 200) + # permission nothing entity attr + entity_attr = entry.attrs.get(schema__name="val").schema + entity_attr.is_public = False + entity_attr.save() + resp = self.client.get("/entry/api/v2/%d/" % entry.id) + self.assertEqual(resp.status_code, 200) + self.assertEqual( + next(filter(lambda x: x["schema"]["name"] == "val", resp.json()["attrs"])), + { + "type": AttrTypeValue["string"], + "value": {"as_string": ""}, + "id": entry.attrs.get(schema__name="val").id, + "is_mandatory": False, + "is_readable": False, + "schema": { + "id": entry.attrs.get(schema__name="val").schema.id, + "name": "val", + }, + }, + ) + + # permission readable entity attr + entity_attr.readable.roles.add(self.role) + resp = self.client.get("/entry/api/v2/%d/" % entry.id) + self.assertEqual(resp.status_code, 200) + self.assertEqual( + next(filter(lambda x: x["schema"]["name"] == "val", resp.json()["attrs"])), + { + "type": AttrTypeValue["string"], + "value": {"as_string": ""}, + "id": entry.attrs.get(schema__name="val").id, + "is_mandatory": False, + "is_readable": True, + "schema": { + "id": entry.attrs.get(schema__name="val").schema.id, + "name": "val", + }, + }, + ) + def test_retrieve_entry_with_invalid_param(self): resp = self.client.get("/entry/api/v2/%s/" % "hoge") self.assertEqual(resp.status_code, 404) @@ -516,6 +570,7 @@ def test_retrieve_entry_with_deleted_referrals(self): }, "id": entry.attrs.get(schema__name="ref").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="ref").schema.id, "name": "ref", @@ -533,6 +588,7 @@ def test_retrieve_entry_with_deleted_referrals(self): }, "id": entry.attrs.get(schema__name="name").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="name").schema.id, "name": "name", @@ -546,6 +602,7 @@ def test_retrieve_entry_with_deleted_referrals(self): "value": {"as_array_object": []}, "id": entry.attrs.get(schema__name="refs").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="refs").schema.id, "name": "refs", @@ -568,6 +625,7 @@ def test_retrieve_entry_with_deleted_referrals(self): }, "id": entry.attrs.get(schema__name="names").id, "is_mandatory": False, + "is_readable": True, "schema": { "id": entry.attrs.get(schema__name="names").schema.id, "name": "names", diff --git a/frontend/src/apiclient/autogenerated/models/EntryAttributeType.ts b/frontend/src/apiclient/autogenerated/models/EntryAttributeType.ts index bc5504f3b..655bda99b 100644 --- a/frontend/src/apiclient/autogenerated/models/EntryAttributeType.ts +++ b/frontend/src/apiclient/autogenerated/models/EntryAttributeType.ts @@ -50,6 +50,12 @@ export interface EntryAttributeType { * @memberof EntryAttributeType */ isMandatory: boolean; + /** + * + * @type {boolean} + * @memberof EntryAttributeType + */ + isReadable: boolean; /** * * @type {EntryAttributeValue} @@ -79,6 +85,7 @@ export function EntryAttributeTypeFromJSONTyped( id: json["id"], type: json["type"], isMandatory: json["is_mandatory"], + isReadable: json["is_readable"], value: EntryAttributeValueFromJSON(json["value"]), schema: EntityAttributeTypeFromJSON(json["schema"]), }; @@ -97,6 +104,7 @@ export function EntryAttributeTypeToJSON( id: value.id, type: value.type, is_mandatory: value.isMandatory, + is_readable: value.isReadable, value: EntryAttributeValueToJSON(value.value), schema: EntityAttributeTypeToJSON(value.schema), }; diff --git a/frontend/src/components/entry/EntryAttributes.tsx b/frontend/src/components/entry/EntryAttributes.tsx index e4b2f7abf..52c381fb6 100644 --- a/frontend/src/components/entry/EntryAttributes.tsx +++ b/frontend/src/components/entry/EntryAttributes.tsx @@ -6,6 +6,7 @@ import { TableCell, TableRow, Paper, + Typography, } from "@mui/material"; import { styled } from "@mui/material/styles"; import React, { FC } from "react"; @@ -56,9 +57,13 @@ export const EntryAttributes: FC = ({ attributes }) => { {attr.schema.name} - + {attr.isReadable ? ( + + ) : ( + Permission denied. + )} ))} diff --git a/frontend/src/services/entry/Edit.test.ts b/frontend/src/services/entry/Edit.test.ts index 250139192..22f35b30e 100644 --- a/frontend/src/services/entry/Edit.test.ts +++ b/frontend/src/services/entry/Edit.test.ts @@ -162,6 +162,7 @@ test("formalizeEntryInfo should return expect value", () => { }, type: djangoContext?.attrTypeValue.string, isMandatory: true, + isReadable: true, value: { asString: "", }, @@ -176,6 +177,7 @@ test("formalizeEntryInfo should return expect value", () => { }, type: djangoContext?.attrTypeValue.array_string, isMandatory: false, + isReadable: true, value: { asArrayString: [], }, @@ -190,6 +192,7 @@ test("formalizeEntryInfo should return expect value", () => { }, type: djangoContext?.attrTypeValue.array_named_object, isMandatory: true, + isReadable: true, value: { asArrayNamedObject: [], },