"""
The Privacy API.
"""
from __future__ import annotations
import enum
from typing import Any, TYPE_CHECKING
from typing_extensions import override
from betty.json.linked_data import LinkedDataDumpableJsonLdObject, JsonLdObject
from betty.json.schema import Boolean
if TYPE_CHECKING:
from betty.serde.dump import DumpMapping, Dump
from betty.project import Project
[docs]
class Privacy(enum.Enum):
"""
The available privacy modes.
"""
#: The resource is explicitly made public.
PUBLIC = 1
#: The resource is explicitly made private.
PRIVATE = 2
#: The resource has no explicit privacy. This means that:
#:
#: - it may be changed at will
#: - when checking access, UNDETERMINED evaluates to PUBLIC.
UNDETERMINED = 3
[docs]
class HasPrivacy(LinkedDataDumpableJsonLdObject):
"""
A resource that has privacy.
"""
[docs]
def __init__(
self,
*args: Any,
privacy: Privacy | None = None,
public: bool | None = None,
private: bool | None = None,
**kwargs: Any,
):
super().__init__(*args, **kwargs)
if [privacy, public, private].count(None) < 2:
raise ValueError(
f"Only one of the `privacy`, `public`, and `private` arguments to {type(self)}.__init__() may be given at a time."
)
if privacy is not None:
self._privacy = privacy
elif public is True:
self._privacy = Privacy.PUBLIC
elif private is True:
self._privacy = Privacy.PRIVATE
else:
self._privacy = Privacy.UNDETERMINED
@property
def own_privacy(self) -> Privacy:
"""
The resource's own privacy.
This returns the value that was set for :py:attr:`betty.privacy.HasPrivacy.privacy` and ignores
computed privacies.
For access control and permissions checking, use :py:attr:`betty.privacy.HasPrivacy.privacy`.
"""
return self._privacy
def _get_effective_privacy(self) -> Privacy:
return self.own_privacy
@property
def privacy(self) -> Privacy:
"""
The resource's privacy.
"""
return self._get_effective_privacy()
@privacy.setter
def privacy(self, privacy: Privacy) -> None:
self._privacy = privacy
@privacy.deleter
def privacy(self) -> None:
self.privacy = Privacy.UNDETERMINED
@property
def private(self) -> bool:
"""
Whether this resource is private.
"""
return self.privacy is Privacy.PRIVATE
@private.setter
def private(self, private: True) -> None:
self.privacy = Privacy.PRIVATE
@property
def public(self) -> bool:
"""
Whether this resource is public.
"""
# Undetermined privacy defaults to public.
return self.privacy is not Privacy.PRIVATE
@public.setter
def public(self, public: True) -> None:
self.privacy = Privacy.PUBLIC
[docs]
class PrivacySchema(Boolean):
"""
A JSON Schema for privacy.
"""
[docs]
def __init__(self):
super().__init__(
def_name="privacy",
title="Privacy",
description="Whether this entity is private (true), or public (false).",
)
[docs]
def is_private(target: Any) -> bool:
"""
Check if the given target is private.
"""
if isinstance(target, HasPrivacy):
return target.private
return False
[docs]
def is_public(target: Any) -> bool:
"""
Check if the given target is public.
"""
if isinstance(target, HasPrivacy):
return target.public
return True
[docs]
def resolve_privacy(privacy: Privacy | HasPrivacy | None) -> Privacy:
"""
Resolve the privacy of a value.
"""
if privacy is None:
return Privacy.UNDETERMINED
if isinstance(privacy, Privacy):
return privacy
return privacy.privacy
[docs]
def merge_privacies(*privacies: Privacy | HasPrivacy | None) -> Privacy:
"""
Merge multiple privacies into one.
"""
privacies = {resolve_privacy(privacy) for privacy in privacies}
if Privacy.PRIVATE in privacies:
return Privacy.PRIVATE
if Privacy.UNDETERMINED in privacies:
return Privacy.UNDETERMINED
return Privacy.PUBLIC