stoplight

View Source
from typing import List, Optional, Type

from tortoise import BaseDBAsyncClient, Model, Tortoise
from tortoise.exceptions import ConfigurationError
from tortoise.signals import pre_save

from .strategies import STRAT_TO_FUNC, MockTypes, Strategies


async def init_anonymizations(models: Optional[List[Type[Model]]] = None):
    """
    Register anonymization strategies for every model with a defined anonymities
    mapping. If models should be from discovery, this must be called after
    Tortoise initialization is complete.
    """
    # if models are not provided
    if not models:
        if not Tortoise._inited:
            raise ConfigurationError(
                "You have to call Tortoise.init() before registering model anonymities."
            )

        # extract the models from the ones discovered by Tortoise during initialization
        # https://tortoise.github.io/_modules/tortoise.html#Tortoise.describe_models
        models = []
        for app in Tortoise.apps.values():
            for model in app.values():
                models.append(model)

    # create a pre_save signal hook for all models with field <-> anonymization
    # strategy mappping
    pre_save(*[model for model in models if hasattr(model, "__anonymities__")])(
        anonymize
    )


async def anonymize(
    sender: Type[Model],
    instance: Model,
    db_client: Optional[BaseDBAsyncClient],
    updated_fields: List[str],
) -> None:
    for name, strategy, args in instance.__anonymities__:
        # ensure that tuples passed through anonymities are valid
        if not isinstance(name, str):
            raise ValueError(f"{name} is not a valid string field identifier.")
        elif not isinstance(strategy, Strategies):
            raise ValueError(f"{strategy} is not a valid anonymization Strategy.")
        elif not hasattr(instance, name):
            raise AttributeError(f"{sender} does not have a writable attribute {name}.")
        elif name == instance._meta.pk_attr:
            raise ValueError("You cannot anonymize primary keys.")

        # get the current value, create the anonymized value, and set it
        value = getattr(instance, name)
        nvalue = STRAT_TO_FUNC[strategy](value, *args)  # type: ignore
        setattr(instance, name, nvalue)


__all__ = ["init_anonymizations", "Strategies", "MockTypes"]
#   async def init_anonymizations(models: Optional[List[Type[tortoise.models.Model]]] = None):
View Source
async def init_anonymizations(models: Optional[List[Type[Model]]] = None):
    """
    Register anonymization strategies for every model with a defined anonymities
    mapping. If models should be from discovery, this must be called after
    Tortoise initialization is complete.
    """
    # if models are not provided
    if not models:
        if not Tortoise._inited:
            raise ConfigurationError(
                "You have to call Tortoise.init() before registering model anonymities."
            )

        # extract the models from the ones discovered by Tortoise during initialization
        # https://tortoise.github.io/_modules/tortoise.html#Tortoise.describe_models
        models = []
        for app in Tortoise.apps.values():
            for model in app.values():
                models.append(model)

    # create a pre_save signal hook for all models with field <-> anonymization
    # strategy mappping
    pre_save(*[model for model in models if hasattr(model, "__anonymities__")])(
        anonymize
    )

Register anonymization strategies for every model with a defined anonymities mapping. If models should be from discovery, this must be called after Tortoise initialization is complete.

#   class Strategies(enum.Enum):
View Source
class Strategies(Enum):
    """All supported anonymization strategies."""

    SUPPRESS = auto()
    """Suppresses the input field by returning a static anonymous string. Only works with string fields."""
    PARTIAL_SUPPRESS = auto()
    """
    Returns a partially suppressed string based on the given input pattern.
    The input pattern can consit of any character, but '*'s will overwrite
    the character in the input. The pattern and value must always be the same
    length.

    >> ex. the value "012 345 6789" with pattern "*** *** XXXX" returns "*** *** 6789"
    """
    MOCK = auto()
    """
    Returns a new value given the requested mocking type. Currently, only
    address, full name, and datetime, and date mocks are available. Fields must
    be the correct type in order to be mockable with the given method.
    """
    VARY = auto()
    """
    Returns a new value, with variance picked from a Gaussian distribution with a mean
    of the current value and a given standard deviation.

    If the value is a date or datetime, the variance is in days.
    """

All supported anonymization strategies.

#   SUPPRESS = <Strategies.SUPPRESS: 1>

Suppresses the input field by returning a static anonymous string. Only works with string fields.

#   PARTIAL_SUPPRESS = <Strategies.PARTIAL_SUPPRESS: 2>

Returns a partially suppressed string based on the given input pattern. The input pattern can consit of any character, but '*'s will overwrite the character in the input. The pattern and value must always be the same length.

ex. the value "012 345 6789" with pattern "* XXXX" returns " * 6789"

#   MOCK = <Strategies.MOCK: 3>

Returns a new value given the requested mocking type. Currently, only address, full name, and datetime, and date mocks are available. Fields must be the correct type in order to be mockable with the given method.

#   VARY = <Strategies.VARY: 4>

Returns a new value, with variance picked from a Gaussian distribution with a mean of the current value and a given standard deviation.

If the value is a date or datetime, the variance is in days.

Inherited Members
enum.Enum
name
value
#   class MockTypes(enum.Enum):
View Source
class MockTypes(Enum):
    """
    All supported mock types.
    
    Each type is a valid provider in the Faker library <https://faker.rtfd.org/en/master/providers.html>.
    """

    ADDRESS = auto()
    """Generates single-line full addresses in the United States."""
    DATETIME = auto()
    """Generates dates or datetimes within any UNIX epoch."""
    NAME = auto()
    """Generates random English full names."""

All supported mock types.

Each type is a valid provider in the Faker library https://faker.rtfd.org/en/master/providers.html.

#   ADDRESS = <MockTypes.ADDRESS: 1>

Generates single-line full addresses in the United States.

#   DATETIME = <MockTypes.DATETIME: 2>

Generates dates or datetimes within any UNIX epoch.

#   NAME = <MockTypes.NAME: 3>

Generates random English full names.

Inherited Members
enum.Enum
name
value