%global _empty_manifest_terminate_build 0 Name: python-django-scrubber Version: 1.2.0 Release: 1 Summary: Data Anonymizer for Django License: BSD URL: https://github.com/regiohelden/django-scrubber Source0: https://mirrors.aliyun.com/pypi/web/packages/da/d9/f70f44de002bf2e0368482050c14971e1f4230b91e2161417ef016d3207d/django-scrubber-1.2.0.tar.gz BuildArch: noarch Requires: python3-django-model-utils Requires: python3-faker %description # Django Scrubber [![Build Status](https://github.com/RegioHelden/django-scrubber/workflows/Build/badge.svg)](https://github.com/RegioHelden/django-scrubber/actions) [![PyPI](https://img.shields.io/pypi/v/django-scrubber.svg)](https://pypi.org/project/django-scrubber/) [![Downloads](https://pepy.tech/badge/django-scrubber)](https://pepy.tech/project/django-scrubber) `django_scrubber` is a django app meant to help you anonymize your project's database data. It destructively alters data directly on the DB and therefore **should not be used on production**. The main use case is providing developers with realistic data to use during development, without having to distribute your customers' or users' potentially sensitive information. To accomplish this, `django_scrubber` should be plugged in a step during the creation of your database dumps. Simply mark the fields you want to anonymize and call the `scrub_data` management command. Data will be replaced based on different *scrubbers* (see below), which define how the anonymous content will be generated. If you want to be sure that you don't forget any fields in the ongoing development progress, you can use the management command `scrub_validation` in your CI/CD pipeline to check for any missing fields. ## Installation Simply run: ``` pip install django-scrubber ``` And add `django_scrubber` to your django `INSTALLED_APPS`. I.e.: in `settings.py` add: ``` INSTALLED_APPS = [ ... 'django_scrubber', ... ] ``` ## Scrubbing data In order to scrub data, i.e.: to replace DB data with anonymized versions, `django-scrubber` must know which models and fields it should act on, and how the data should be replaced. There are a few different ways to select which data should be scrubbed, namely: explicitly per model field; or globally per name or field type. Adding scrubbers directly to model, matching scrubbers to fields by name: ```python class MyModel(Model): somefield = CharField() class Scrubbers: somefield = scrubbers.Hash('somefield') ``` Adding scrubbers globally, either by field name or field type: ```python # (in settings.py) SCRUBBER_GLOBAL_SCRUBBERS = { 'name': scrubbers.Hash, EmailField: scrubbers.Hash, } ``` Model scrubbers override field-name scrubbers, which in turn override field-type scrubbers. To disable global scrubbing in some specific model, simply set the respective field scrubber to `None`. Scrubbers defined for non-existing fields will raise a warning but not fail the scubbing process. Which mechanism will be used to scrub the selected data is determined by using one of the provided scrubbers in `django_scrubber.scrubbers`. See below for a list. Alternatively, values may be anything that can be used as a value in a `QuerySet.update()` call (like `Func` instances, string literals, etc), or any `callable` that returns such an object when called with a `Field` object as argument. By default, `django_scrubber` will affect all models from all registered apps. This may lead to issues with third-party apps if the global scrubbers are too general. This can be avoided with the `SCRUBBER_APPS_LIST` setting. Using this, you might for instance split your `INSTALLED_APPS` into multiple `SYSTEM_APPS` and `LOCAL_APPS`, then set `SCRUBBER_APPS_LIST = LOCAL_APPS`, to scrub only your own apps. Finally just run `./manage.py scrub_data` to **destructively** scrub the registered fields. ### Arguments to the scrub_data command `--model` Scrub only a single model (format .) `--keep-sessions` Will NOT truncate all (by definition critical) session data. `--remove-fake-data` Will truncate the database table storing preprocessed data for the Faker library. ## Built-In scrubbers ### Empty/Null The simplest scrubbers: replace the field's content with the empty string or `NULL`, respectively. ```python class Scrubbers: somefield = scrubbers.Empty someother = scrubbers.Null ``` These scrubbers have no options. ### Keeper When running the validation or want to work in strict mode, you maybe want to actively decide to keep certain data instead of scrubbing them. In this case, you can just define `scrubbers.Keep`. ```python class Scrubbers: non_critical_field = scrubbers.Keep ``` These scrubber doesn't have any options. ### Hash Simple hashing of content: ```python class Scrubbers: somefield = scrubbers.Hash # will use the field itself as source someotherfield = scrubbers.Hash('somefield') # can optionally pass a different field name as hashing source ``` Currently, this uses the MD5 hash which is supported in a wide variety of DB engines. Additionally, since security is not the main objective, a shorter hash length has a lower risk of being longer than whatever field it is supposed to replace. ### Lorem Simple scrubber meant to replace `TextField` with a static block of text. Has no options. ```python class Scrubbers: somefield = scrubbers.Lorem ``` ### Concat Wrapper around `django.db.functions.Concat` to enable simple concatenation of scrubbers. This is useful if you want to ensure a fields uniqueness through composition of, for instance, the `Hash` and `Faker` (see below) scrubbers. The following will generate random email addresses by hashing the user-part and using `faker` for the domain part: ```python class Scrubbers: email = scrubbers.Concat(scrubbers.Hash('email'), models.Value('@'), scrubbers.Faker('domain_name')) ``` ### Faker Replaces content with the help of [faker](https://pypi.python.org/pypi/Faker). ```python class Scrubbers: first_name = scrubbers.Faker('first_name') last_name = scrubbers.Faker('last_name') past_date = scrubbers.Faker('past_date', start_date="-30d", tzinfo=None) ``` The replacements are done on the database-level and should therefore be able to cope with large amounts of data with reasonable performance. The `Faker` scrubber requires at least one argument: the faker provider used to generate random data. All [faker providers](https://faker.readthedocs.io/en/latest/providers.html) are supported, and you can also register your own custom providers.
Any remaining arguments will be passed through to that provider. Please refer to the faker docs if a provider accepts arguments and what to do with them. #### Locales Faker will be initialized with the current django `LANGUAGE_CODE` and will populate the DB with localized data. If you want localized scrubbing, simply set it to some other value. #### Idempotency By default, the faker instance used to populate the DB uses a fixed random seed, in order to ensure different scrubbings of the same data generate the same output. This is particularly useful if the scrubbed data is imported as a dump by developers, since changing data during troubleshooting would otherwise be confusing. This behaviour can be changed by setting `SCRUBBER_RANDOM_SEED=None`, which ensures every scrubbing will generate random source data. #### Limitations Scrubbing unique fields may lead to `IntegrityError`s, since there is no guarantee that the random content will not be repeated. Playing with different settings for `SCRUBBER_RANDOM_SEED` and `SCRUBBER_ENTRIES_PER_PROVIDER` may alleviate the problem. Unfortunately, for performance reasons, the source data for scrubbing with faker is added to the database, and arbitrarily increasing `SCRUBBER_ENTRIES_PER_PROVIDER` will significantly slow down scrubbing (besides still not guaranteeing uniqueness). When using `django < 2.1` and working on `sqlite` a bug within django causes field-specific scrubbing ( e.g. `date_object`) to fail. Please consider using a different database backend or upgrade to the latest django version. ## Scrubbing third-party models Sometimes you just don't have control over some code, but you still want to scrub the data of a given model. A good example is the Django user model. It contains sensitive data, and you would have to overwrite the whole model just to add the scrubber metaclass. That's the way to go: 1. Define your Scrubber class **somewhere** in your codebase (like a `scrubbers.py`) ```python # scrubbers.py class UserScrubbers: scrubbers.Faker('de_DE') first_name = scrubbers.Faker('first_name') last_name = scrubbers.Faker('last_name') username = scrubbers.Faker('uuid4') password = scrubbers.Faker('sha1') last_login = scrubbers.Null email = scrubbers.Concat(first_name, models.Value('.'), last_name, models.Value('@'), models.Value(settings.SCRUBBER_DOMAIN)) ```` 2. Set up a mapping between your third-party model and your scrubber class ```python # settings.py SCRUBBER_MAPPING = { "auth.User": "apps.account.scrubbers.UserScrubbers", } ``` ## Settings ### `SCRUBBER_GLOBAL_SCRUBBERS`: Dictionary of global scrubbers. Keys should be either field names as strings or field type classes. Values should be one of the scrubbers provided in `django_scrubber.scrubbers`. Example: ```python SCRUBBER_GLOBAL_SCRUBBERS = { 'name': scrubbers.Hash, EmailField: scrubbers.Hash, } ``` ### `SCRUBBER_RANDOM_SEED`: The seed used when generating random content by the Faker scrubber. Setting this to `None` means each scrubbing will generate different data. (default: 42) ### `SCRUBBER_ENTRIES_PER_PROVIDER`: Number of entries to use as source for Faker scrubber. Increasing this value will increase the randomness of generated data, but decrease performance. (default: 1000) ### `SCRUBBER_SKIP_UNMANAGED`: Do not attempt to scrub models which are not managed by the ORM. (default: True) ### `SCRUBBER_APPS_LIST`: Only scrub models belonging to these specific django apps. If unset, will scrub all installed apps. (default: None) ### `SCRUBBER_ADDITIONAL_FAKER_PROVIDERS`: Add additional fake providers to be used by Faker. Must be noted as full dotted path to the provider class. (default: empty list) ### `SCRUBBER_FAKER_LOCALE`: Set an alternative locale for Faker used during the scrubbing process. (default: None, falls back to Django's default locale) ### `SCRUBBER_MAPPING`: Define a class and a mapper which does not have to live inside the given model. Useful, if you have no control over the models code you'd like to scrub. ### `SCRUBBER_STRICT_MODE`: When strict mode is activated, you have to define a scrubbing policy for every field of every type defined in `SCRUBBER_REQUIRED_FIELD_TYPES`. If you have unscrubbed fields and this flag is active, you can't run `pyhton manage.py scrub_data`. ### `SCRUBBER_REQUIRED_FIELD_TYPES`: Defaults to all text-based Django model fields. Usually, privacy-relevant data is only stored in text-fields, numbers and booleans (usually) can't contain sensitive personal data. These fields will be checked when running `python manage.py scub_validation`. (default: (models.CharField, models.TextField, models.URLField, models.JSONField, models.GenericIPAddressField, models.EmailField,)) ### `SCRUBBER_REQUIRED_FIELD_MODEL_WHITELIST`: Whitelists a list of models which will not be checked during `scrub_validation` and when activating the strict mode. Defaults to the non-privacy-related Django base models. (default: ['auth.Group', 'auth.Permission', 'contenttypes.ContentType', 'sessions.Session', 'sites.Site', 'django_scrubber.FakeData',)) ````python SCRUBBER_MAPPING = { "auth.User": "my_app.scrubbers.UserScrubbers", } ```` (default: {}) ## Logging Scrubber uses the default django logger. The logger name is ``django_scrubber.scrubbers``. So if you want to log - for example - to the console, you could set up the logger like this: ```` LOGGING['loggers']['django_scrubber'] = { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', } ```` ## Making a new release [bumpversion](https://github.com/peritus/bumpversion) is used to manage releases. Add your changes to the [CHANGELOG](./CHANGELOG.md) and run `bumpversion `, then push (including tags) # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [1.2.0] - 2023-04-01 ### Changed - Added scrubber validation - Thanks @GitRon - Added strict mode - Thanks @GitRon ## [1.1.0] - 2022-07-11 ### Changed - Invalid fields on scrubbers will no longer raise exception but just trigger warnings now - Author list completed ## [1.0.0] - 2022-07-11 ### Changed - Meta data for python package improved - Thanks @GitRon ## [0.9.0] - 2022-06-27 ### Added - Add functionality to scrub third party models like the Django user model, see https://github.com/RegioHelden/django-scrubber#scrubbing-third-party-models - Thanks @GitRon - Add tests for Python 3.10 - Thanks @costela ## [0.8.0] - 2022-05-01 ### Added - Add `keep-sessions` argument to scrub_data command. Will NOT truncate all (by definition critical) session data. Thanks @GitRon - Add `remove-fake-data` argument to scrub_data command. Will truncate the database table storing preprocessed data for the Faker library. Thanks @GitRon - Add Django 3.2 and 4.0 to test matrix ### Changed - Remove Python 3.6 from test matrix - Remove Django 2.2 and 3.1 from test matrix ## [0.7.0] - 2022-02-24 ### Changed - Remove upper boundary for Faker as they release non-breaking major upgrades way too often, please pin a working release in your own app ## [0.6.2] - 2022-02-08 ### Changed - Support faker 12.x ## [0.6.1] - 2022-01-25 ### Changed - Support faker 11.x ## [0.6.0] - 2021-10-18 ### Added - Add support to override Faker locale in scrubber settings ### Changed - Publish coverage only on main repository ## [0.5.6] - 2021-10-08 ### Changed - Pin psycopg2 in CI ti 2.8.6 as 2.9+ is incompatible with Django 2.2 ## [0.5.5] - 2021-10-08 ### Changed - Support faker 9.x ## [0.5.4] - 2021-04-13 ### Changed - Support faker 8.x ## [0.5.3] - 2021-02-04 ### Changed - Support faker 6.x ## [0.5.2] - 2021-01-12 ### Changed - Add tests for Python 3.9 - Add tests for Django 3.1 - Support faker 5.x - Update dev package requirements ## [0.5.1] - 2020-10-16 ### Changed - Fix travis setup ## [0.5.0] - 2020-10-16 ### Added - Support for django-model-utils 4.x.x ### Changed - Add compatibility for Faker 3.x.x, remove support for Faker < 0.8.0 - Remove support for Python 2.7 and 3.5 - Remove support for Django 1.x ## [0.4.4] - 2019-12-11 ### Fixed - add the same version restrictions on faker to setup.py ## [0.4.3] - 2019-12-04 ### Added - add empty and null scrubbers ### Changed - make `Lorem` scrubber lazy, matching README ### Fixed - set more stringent version requirements (faker >= 3 breaks builds) ## [0.4.1] - 2019-11-16 ### Fixed - correctly clear fake data model to fix successive calls to `scrub_data` (thanks [Benedikt Bauer](https://github.com/mastacheata)) ## [0.4.0] - 2019-11-13 ### Added - `Faker` scrubber now supports passing arbitrary arguments to faker providers and also non-text fields (thanks [Benedikt Bauer](https://github.com/mastacheata) and [Ronny Vedrilla](https://github.com/GitRon)) ## [0.3.1] - 2018-09-10 ### Fixed - [#9](https://github.com/RegioHelden/django-scrubber/pull/9) `Hash` scrubber choking on fields with `max_length=None` - Thanks to [Charlie Denton](https://github.com/meshy) ## [0.3.0] - 2018-09-06 ### Added - Finally added some basic tests (thanks [Marco De Felice](https://github.com/md-f)) - `Hash` scrubber can now also be used on sqlite ### Changed - **BREAKING**: scrubbers that are lazily initialized now receive `Field` instances as parameters, instead of field names. If you have custom scrubbers depending on the previous behavior, these should be updated. Accessing the field's name from the object instance is trivial: `field_instance.name`. E.g.: if you have `some_field = MyCustomScrubber` in any of your models' `Scrubbers`, this class must accept a `Field` instance as first parameter. Note that explicitly intializing any of the built-in scrubbers with field names is still supported, so if you were just using built-in scrubbers, you should not be affected by this change. - related to the above, `FuncField` derived classes can now do connection-based setup by implementing the `connection_setup` method. This is mostly useful for doing different things based on the DB vendor, and is used to implement `MD5()` on sqlite (see added feature above) - Ignore proxy models when scrubbing (thanks [Marco De Felice](https://github.com/md-f)) - Expand tests to include python 3.7 and django 2.1 ## [0.2.1] - 2018-08-14 ### Added - Option to scrub only one model from the management command - Support loading additional faker providers by config setting SCRUBBER\_ADDITIONAL\_FAKER\_PROVIDERS ### Changed - Switched changelog format to the one proposed on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [0.2.0] - 2018-08-13 ### Added - scrubbers.Concat to make simple concatenation of scrubbers possible ## [0.1.4] - 2018-08-13 ### Changed - Make our README look beautiful on PyPI ## [0.1.3] - 2018-08-13 ### Fixed - [#1](https://github.com/RegioHelden/django-scrubber/pull/1) badly timed import - Thanks to [Charlie Denton](https://github.com/meshy) ## [0.1.2] - 2018-06-22 ### Changed - Use bumpversion and travis to make new releases - rename project: django\_scrubber → django-scrubber ## [0.1.0] - 2018-06-22 ### Added - Initial release %package -n python3-django-scrubber Summary: Data Anonymizer for Django Provides: python-django-scrubber BuildRequires: python3-devel BuildRequires: python3-setuptools BuildRequires: python3-pip %description -n python3-django-scrubber # Django Scrubber [![Build Status](https://github.com/RegioHelden/django-scrubber/workflows/Build/badge.svg)](https://github.com/RegioHelden/django-scrubber/actions) [![PyPI](https://img.shields.io/pypi/v/django-scrubber.svg)](https://pypi.org/project/django-scrubber/) [![Downloads](https://pepy.tech/badge/django-scrubber)](https://pepy.tech/project/django-scrubber) `django_scrubber` is a django app meant to help you anonymize your project's database data. It destructively alters data directly on the DB and therefore **should not be used on production**. The main use case is providing developers with realistic data to use during development, without having to distribute your customers' or users' potentially sensitive information. To accomplish this, `django_scrubber` should be plugged in a step during the creation of your database dumps. Simply mark the fields you want to anonymize and call the `scrub_data` management command. Data will be replaced based on different *scrubbers* (see below), which define how the anonymous content will be generated. If you want to be sure that you don't forget any fields in the ongoing development progress, you can use the management command `scrub_validation` in your CI/CD pipeline to check for any missing fields. ## Installation Simply run: ``` pip install django-scrubber ``` And add `django_scrubber` to your django `INSTALLED_APPS`. I.e.: in `settings.py` add: ``` INSTALLED_APPS = [ ... 'django_scrubber', ... ] ``` ## Scrubbing data In order to scrub data, i.e.: to replace DB data with anonymized versions, `django-scrubber` must know which models and fields it should act on, and how the data should be replaced. There are a few different ways to select which data should be scrubbed, namely: explicitly per model field; or globally per name or field type. Adding scrubbers directly to model, matching scrubbers to fields by name: ```python class MyModel(Model): somefield = CharField() class Scrubbers: somefield = scrubbers.Hash('somefield') ``` Adding scrubbers globally, either by field name or field type: ```python # (in settings.py) SCRUBBER_GLOBAL_SCRUBBERS = { 'name': scrubbers.Hash, EmailField: scrubbers.Hash, } ``` Model scrubbers override field-name scrubbers, which in turn override field-type scrubbers. To disable global scrubbing in some specific model, simply set the respective field scrubber to `None`. Scrubbers defined for non-existing fields will raise a warning but not fail the scubbing process. Which mechanism will be used to scrub the selected data is determined by using one of the provided scrubbers in `django_scrubber.scrubbers`. See below for a list. Alternatively, values may be anything that can be used as a value in a `QuerySet.update()` call (like `Func` instances, string literals, etc), or any `callable` that returns such an object when called with a `Field` object as argument. By default, `django_scrubber` will affect all models from all registered apps. This may lead to issues with third-party apps if the global scrubbers are too general. This can be avoided with the `SCRUBBER_APPS_LIST` setting. Using this, you might for instance split your `INSTALLED_APPS` into multiple `SYSTEM_APPS` and `LOCAL_APPS`, then set `SCRUBBER_APPS_LIST = LOCAL_APPS`, to scrub only your own apps. Finally just run `./manage.py scrub_data` to **destructively** scrub the registered fields. ### Arguments to the scrub_data command `--model` Scrub only a single model (format .) `--keep-sessions` Will NOT truncate all (by definition critical) session data. `--remove-fake-data` Will truncate the database table storing preprocessed data for the Faker library. ## Built-In scrubbers ### Empty/Null The simplest scrubbers: replace the field's content with the empty string or `NULL`, respectively. ```python class Scrubbers: somefield = scrubbers.Empty someother = scrubbers.Null ``` These scrubbers have no options. ### Keeper When running the validation or want to work in strict mode, you maybe want to actively decide to keep certain data instead of scrubbing them. In this case, you can just define `scrubbers.Keep`. ```python class Scrubbers: non_critical_field = scrubbers.Keep ``` These scrubber doesn't have any options. ### Hash Simple hashing of content: ```python class Scrubbers: somefield = scrubbers.Hash # will use the field itself as source someotherfield = scrubbers.Hash('somefield') # can optionally pass a different field name as hashing source ``` Currently, this uses the MD5 hash which is supported in a wide variety of DB engines. Additionally, since security is not the main objective, a shorter hash length has a lower risk of being longer than whatever field it is supposed to replace. ### Lorem Simple scrubber meant to replace `TextField` with a static block of text. Has no options. ```python class Scrubbers: somefield = scrubbers.Lorem ``` ### Concat Wrapper around `django.db.functions.Concat` to enable simple concatenation of scrubbers. This is useful if you want to ensure a fields uniqueness through composition of, for instance, the `Hash` and `Faker` (see below) scrubbers. The following will generate random email addresses by hashing the user-part and using `faker` for the domain part: ```python class Scrubbers: email = scrubbers.Concat(scrubbers.Hash('email'), models.Value('@'), scrubbers.Faker('domain_name')) ``` ### Faker Replaces content with the help of [faker](https://pypi.python.org/pypi/Faker). ```python class Scrubbers: first_name = scrubbers.Faker('first_name') last_name = scrubbers.Faker('last_name') past_date = scrubbers.Faker('past_date', start_date="-30d", tzinfo=None) ``` The replacements are done on the database-level and should therefore be able to cope with large amounts of data with reasonable performance. The `Faker` scrubber requires at least one argument: the faker provider used to generate random data. All [faker providers](https://faker.readthedocs.io/en/latest/providers.html) are supported, and you can also register your own custom providers.
Any remaining arguments will be passed through to that provider. Please refer to the faker docs if a provider accepts arguments and what to do with them. #### Locales Faker will be initialized with the current django `LANGUAGE_CODE` and will populate the DB with localized data. If you want localized scrubbing, simply set it to some other value. #### Idempotency By default, the faker instance used to populate the DB uses a fixed random seed, in order to ensure different scrubbings of the same data generate the same output. This is particularly useful if the scrubbed data is imported as a dump by developers, since changing data during troubleshooting would otherwise be confusing. This behaviour can be changed by setting `SCRUBBER_RANDOM_SEED=None`, which ensures every scrubbing will generate random source data. #### Limitations Scrubbing unique fields may lead to `IntegrityError`s, since there is no guarantee that the random content will not be repeated. Playing with different settings for `SCRUBBER_RANDOM_SEED` and `SCRUBBER_ENTRIES_PER_PROVIDER` may alleviate the problem. Unfortunately, for performance reasons, the source data for scrubbing with faker is added to the database, and arbitrarily increasing `SCRUBBER_ENTRIES_PER_PROVIDER` will significantly slow down scrubbing (besides still not guaranteeing uniqueness). When using `django < 2.1` and working on `sqlite` a bug within django causes field-specific scrubbing ( e.g. `date_object`) to fail. Please consider using a different database backend or upgrade to the latest django version. ## Scrubbing third-party models Sometimes you just don't have control over some code, but you still want to scrub the data of a given model. A good example is the Django user model. It contains sensitive data, and you would have to overwrite the whole model just to add the scrubber metaclass. That's the way to go: 1. Define your Scrubber class **somewhere** in your codebase (like a `scrubbers.py`) ```python # scrubbers.py class UserScrubbers: scrubbers.Faker('de_DE') first_name = scrubbers.Faker('first_name') last_name = scrubbers.Faker('last_name') username = scrubbers.Faker('uuid4') password = scrubbers.Faker('sha1') last_login = scrubbers.Null email = scrubbers.Concat(first_name, models.Value('.'), last_name, models.Value('@'), models.Value(settings.SCRUBBER_DOMAIN)) ```` 2. Set up a mapping between your third-party model and your scrubber class ```python # settings.py SCRUBBER_MAPPING = { "auth.User": "apps.account.scrubbers.UserScrubbers", } ``` ## Settings ### `SCRUBBER_GLOBAL_SCRUBBERS`: Dictionary of global scrubbers. Keys should be either field names as strings or field type classes. Values should be one of the scrubbers provided in `django_scrubber.scrubbers`. Example: ```python SCRUBBER_GLOBAL_SCRUBBERS = { 'name': scrubbers.Hash, EmailField: scrubbers.Hash, } ``` ### `SCRUBBER_RANDOM_SEED`: The seed used when generating random content by the Faker scrubber. Setting this to `None` means each scrubbing will generate different data. (default: 42) ### `SCRUBBER_ENTRIES_PER_PROVIDER`: Number of entries to use as source for Faker scrubber. Increasing this value will increase the randomness of generated data, but decrease performance. (default: 1000) ### `SCRUBBER_SKIP_UNMANAGED`: Do not attempt to scrub models which are not managed by the ORM. (default: True) ### `SCRUBBER_APPS_LIST`: Only scrub models belonging to these specific django apps. If unset, will scrub all installed apps. (default: None) ### `SCRUBBER_ADDITIONAL_FAKER_PROVIDERS`: Add additional fake providers to be used by Faker. Must be noted as full dotted path to the provider class. (default: empty list) ### `SCRUBBER_FAKER_LOCALE`: Set an alternative locale for Faker used during the scrubbing process. (default: None, falls back to Django's default locale) ### `SCRUBBER_MAPPING`: Define a class and a mapper which does not have to live inside the given model. Useful, if you have no control over the models code you'd like to scrub. ### `SCRUBBER_STRICT_MODE`: When strict mode is activated, you have to define a scrubbing policy for every field of every type defined in `SCRUBBER_REQUIRED_FIELD_TYPES`. If you have unscrubbed fields and this flag is active, you can't run `pyhton manage.py scrub_data`. ### `SCRUBBER_REQUIRED_FIELD_TYPES`: Defaults to all text-based Django model fields. Usually, privacy-relevant data is only stored in text-fields, numbers and booleans (usually) can't contain sensitive personal data. These fields will be checked when running `python manage.py scub_validation`. (default: (models.CharField, models.TextField, models.URLField, models.JSONField, models.GenericIPAddressField, models.EmailField,)) ### `SCRUBBER_REQUIRED_FIELD_MODEL_WHITELIST`: Whitelists a list of models which will not be checked during `scrub_validation` and when activating the strict mode. Defaults to the non-privacy-related Django base models. (default: ['auth.Group', 'auth.Permission', 'contenttypes.ContentType', 'sessions.Session', 'sites.Site', 'django_scrubber.FakeData',)) ````python SCRUBBER_MAPPING = { "auth.User": "my_app.scrubbers.UserScrubbers", } ```` (default: {}) ## Logging Scrubber uses the default django logger. The logger name is ``django_scrubber.scrubbers``. So if you want to log - for example - to the console, you could set up the logger like this: ```` LOGGING['loggers']['django_scrubber'] = { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', } ```` ## Making a new release [bumpversion](https://github.com/peritus/bumpversion) is used to manage releases. Add your changes to the [CHANGELOG](./CHANGELOG.md) and run `bumpversion `, then push (including tags) # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [1.2.0] - 2023-04-01 ### Changed - Added scrubber validation - Thanks @GitRon - Added strict mode - Thanks @GitRon ## [1.1.0] - 2022-07-11 ### Changed - Invalid fields on scrubbers will no longer raise exception but just trigger warnings now - Author list completed ## [1.0.0] - 2022-07-11 ### Changed - Meta data for python package improved - Thanks @GitRon ## [0.9.0] - 2022-06-27 ### Added - Add functionality to scrub third party models like the Django user model, see https://github.com/RegioHelden/django-scrubber#scrubbing-third-party-models - Thanks @GitRon - Add tests for Python 3.10 - Thanks @costela ## [0.8.0] - 2022-05-01 ### Added - Add `keep-sessions` argument to scrub_data command. Will NOT truncate all (by definition critical) session data. Thanks @GitRon - Add `remove-fake-data` argument to scrub_data command. Will truncate the database table storing preprocessed data for the Faker library. Thanks @GitRon - Add Django 3.2 and 4.0 to test matrix ### Changed - Remove Python 3.6 from test matrix - Remove Django 2.2 and 3.1 from test matrix ## [0.7.0] - 2022-02-24 ### Changed - Remove upper boundary for Faker as they release non-breaking major upgrades way too often, please pin a working release in your own app ## [0.6.2] - 2022-02-08 ### Changed - Support faker 12.x ## [0.6.1] - 2022-01-25 ### Changed - Support faker 11.x ## [0.6.0] - 2021-10-18 ### Added - Add support to override Faker locale in scrubber settings ### Changed - Publish coverage only on main repository ## [0.5.6] - 2021-10-08 ### Changed - Pin psycopg2 in CI ti 2.8.6 as 2.9+ is incompatible with Django 2.2 ## [0.5.5] - 2021-10-08 ### Changed - Support faker 9.x ## [0.5.4] - 2021-04-13 ### Changed - Support faker 8.x ## [0.5.3] - 2021-02-04 ### Changed - Support faker 6.x ## [0.5.2] - 2021-01-12 ### Changed - Add tests for Python 3.9 - Add tests for Django 3.1 - Support faker 5.x - Update dev package requirements ## [0.5.1] - 2020-10-16 ### Changed - Fix travis setup ## [0.5.0] - 2020-10-16 ### Added - Support for django-model-utils 4.x.x ### Changed - Add compatibility for Faker 3.x.x, remove support for Faker < 0.8.0 - Remove support for Python 2.7 and 3.5 - Remove support for Django 1.x ## [0.4.4] - 2019-12-11 ### Fixed - add the same version restrictions on faker to setup.py ## [0.4.3] - 2019-12-04 ### Added - add empty and null scrubbers ### Changed - make `Lorem` scrubber lazy, matching README ### Fixed - set more stringent version requirements (faker >= 3 breaks builds) ## [0.4.1] - 2019-11-16 ### Fixed - correctly clear fake data model to fix successive calls to `scrub_data` (thanks [Benedikt Bauer](https://github.com/mastacheata)) ## [0.4.0] - 2019-11-13 ### Added - `Faker` scrubber now supports passing arbitrary arguments to faker providers and also non-text fields (thanks [Benedikt Bauer](https://github.com/mastacheata) and [Ronny Vedrilla](https://github.com/GitRon)) ## [0.3.1] - 2018-09-10 ### Fixed - [#9](https://github.com/RegioHelden/django-scrubber/pull/9) `Hash` scrubber choking on fields with `max_length=None` - Thanks to [Charlie Denton](https://github.com/meshy) ## [0.3.0] - 2018-09-06 ### Added - Finally added some basic tests (thanks [Marco De Felice](https://github.com/md-f)) - `Hash` scrubber can now also be used on sqlite ### Changed - **BREAKING**: scrubbers that are lazily initialized now receive `Field` instances as parameters, instead of field names. If you have custom scrubbers depending on the previous behavior, these should be updated. Accessing the field's name from the object instance is trivial: `field_instance.name`. E.g.: if you have `some_field = MyCustomScrubber` in any of your models' `Scrubbers`, this class must accept a `Field` instance as first parameter. Note that explicitly intializing any of the built-in scrubbers with field names is still supported, so if you were just using built-in scrubbers, you should not be affected by this change. - related to the above, `FuncField` derived classes can now do connection-based setup by implementing the `connection_setup` method. This is mostly useful for doing different things based on the DB vendor, and is used to implement `MD5()` on sqlite (see added feature above) - Ignore proxy models when scrubbing (thanks [Marco De Felice](https://github.com/md-f)) - Expand tests to include python 3.7 and django 2.1 ## [0.2.1] - 2018-08-14 ### Added - Option to scrub only one model from the management command - Support loading additional faker providers by config setting SCRUBBER\_ADDITIONAL\_FAKER\_PROVIDERS ### Changed - Switched changelog format to the one proposed on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [0.2.0] - 2018-08-13 ### Added - scrubbers.Concat to make simple concatenation of scrubbers possible ## [0.1.4] - 2018-08-13 ### Changed - Make our README look beautiful on PyPI ## [0.1.3] - 2018-08-13 ### Fixed - [#1](https://github.com/RegioHelden/django-scrubber/pull/1) badly timed import - Thanks to [Charlie Denton](https://github.com/meshy) ## [0.1.2] - 2018-06-22 ### Changed - Use bumpversion and travis to make new releases - rename project: django\_scrubber → django-scrubber ## [0.1.0] - 2018-06-22 ### Added - Initial release %package help Summary: Development documents and examples for django-scrubber Provides: python3-django-scrubber-doc %description help # Django Scrubber [![Build Status](https://github.com/RegioHelden/django-scrubber/workflows/Build/badge.svg)](https://github.com/RegioHelden/django-scrubber/actions) [![PyPI](https://img.shields.io/pypi/v/django-scrubber.svg)](https://pypi.org/project/django-scrubber/) [![Downloads](https://pepy.tech/badge/django-scrubber)](https://pepy.tech/project/django-scrubber) `django_scrubber` is a django app meant to help you anonymize your project's database data. It destructively alters data directly on the DB and therefore **should not be used on production**. The main use case is providing developers with realistic data to use during development, without having to distribute your customers' or users' potentially sensitive information. To accomplish this, `django_scrubber` should be plugged in a step during the creation of your database dumps. Simply mark the fields you want to anonymize and call the `scrub_data` management command. Data will be replaced based on different *scrubbers* (see below), which define how the anonymous content will be generated. If you want to be sure that you don't forget any fields in the ongoing development progress, you can use the management command `scrub_validation` in your CI/CD pipeline to check for any missing fields. ## Installation Simply run: ``` pip install django-scrubber ``` And add `django_scrubber` to your django `INSTALLED_APPS`. I.e.: in `settings.py` add: ``` INSTALLED_APPS = [ ... 'django_scrubber', ... ] ``` ## Scrubbing data In order to scrub data, i.e.: to replace DB data with anonymized versions, `django-scrubber` must know which models and fields it should act on, and how the data should be replaced. There are a few different ways to select which data should be scrubbed, namely: explicitly per model field; or globally per name or field type. Adding scrubbers directly to model, matching scrubbers to fields by name: ```python class MyModel(Model): somefield = CharField() class Scrubbers: somefield = scrubbers.Hash('somefield') ``` Adding scrubbers globally, either by field name or field type: ```python # (in settings.py) SCRUBBER_GLOBAL_SCRUBBERS = { 'name': scrubbers.Hash, EmailField: scrubbers.Hash, } ``` Model scrubbers override field-name scrubbers, which in turn override field-type scrubbers. To disable global scrubbing in some specific model, simply set the respective field scrubber to `None`. Scrubbers defined for non-existing fields will raise a warning but not fail the scubbing process. Which mechanism will be used to scrub the selected data is determined by using one of the provided scrubbers in `django_scrubber.scrubbers`. See below for a list. Alternatively, values may be anything that can be used as a value in a `QuerySet.update()` call (like `Func` instances, string literals, etc), or any `callable` that returns such an object when called with a `Field` object as argument. By default, `django_scrubber` will affect all models from all registered apps. This may lead to issues with third-party apps if the global scrubbers are too general. This can be avoided with the `SCRUBBER_APPS_LIST` setting. Using this, you might for instance split your `INSTALLED_APPS` into multiple `SYSTEM_APPS` and `LOCAL_APPS`, then set `SCRUBBER_APPS_LIST = LOCAL_APPS`, to scrub only your own apps. Finally just run `./manage.py scrub_data` to **destructively** scrub the registered fields. ### Arguments to the scrub_data command `--model` Scrub only a single model (format .) `--keep-sessions` Will NOT truncate all (by definition critical) session data. `--remove-fake-data` Will truncate the database table storing preprocessed data for the Faker library. ## Built-In scrubbers ### Empty/Null The simplest scrubbers: replace the field's content with the empty string or `NULL`, respectively. ```python class Scrubbers: somefield = scrubbers.Empty someother = scrubbers.Null ``` These scrubbers have no options. ### Keeper When running the validation or want to work in strict mode, you maybe want to actively decide to keep certain data instead of scrubbing them. In this case, you can just define `scrubbers.Keep`. ```python class Scrubbers: non_critical_field = scrubbers.Keep ``` These scrubber doesn't have any options. ### Hash Simple hashing of content: ```python class Scrubbers: somefield = scrubbers.Hash # will use the field itself as source someotherfield = scrubbers.Hash('somefield') # can optionally pass a different field name as hashing source ``` Currently, this uses the MD5 hash which is supported in a wide variety of DB engines. Additionally, since security is not the main objective, a shorter hash length has a lower risk of being longer than whatever field it is supposed to replace. ### Lorem Simple scrubber meant to replace `TextField` with a static block of text. Has no options. ```python class Scrubbers: somefield = scrubbers.Lorem ``` ### Concat Wrapper around `django.db.functions.Concat` to enable simple concatenation of scrubbers. This is useful if you want to ensure a fields uniqueness through composition of, for instance, the `Hash` and `Faker` (see below) scrubbers. The following will generate random email addresses by hashing the user-part and using `faker` for the domain part: ```python class Scrubbers: email = scrubbers.Concat(scrubbers.Hash('email'), models.Value('@'), scrubbers.Faker('domain_name')) ``` ### Faker Replaces content with the help of [faker](https://pypi.python.org/pypi/Faker). ```python class Scrubbers: first_name = scrubbers.Faker('first_name') last_name = scrubbers.Faker('last_name') past_date = scrubbers.Faker('past_date', start_date="-30d", tzinfo=None) ``` The replacements are done on the database-level and should therefore be able to cope with large amounts of data with reasonable performance. The `Faker` scrubber requires at least one argument: the faker provider used to generate random data. All [faker providers](https://faker.readthedocs.io/en/latest/providers.html) are supported, and you can also register your own custom providers.
Any remaining arguments will be passed through to that provider. Please refer to the faker docs if a provider accepts arguments and what to do with them. #### Locales Faker will be initialized with the current django `LANGUAGE_CODE` and will populate the DB with localized data. If you want localized scrubbing, simply set it to some other value. #### Idempotency By default, the faker instance used to populate the DB uses a fixed random seed, in order to ensure different scrubbings of the same data generate the same output. This is particularly useful if the scrubbed data is imported as a dump by developers, since changing data during troubleshooting would otherwise be confusing. This behaviour can be changed by setting `SCRUBBER_RANDOM_SEED=None`, which ensures every scrubbing will generate random source data. #### Limitations Scrubbing unique fields may lead to `IntegrityError`s, since there is no guarantee that the random content will not be repeated. Playing with different settings for `SCRUBBER_RANDOM_SEED` and `SCRUBBER_ENTRIES_PER_PROVIDER` may alleviate the problem. Unfortunately, for performance reasons, the source data for scrubbing with faker is added to the database, and arbitrarily increasing `SCRUBBER_ENTRIES_PER_PROVIDER` will significantly slow down scrubbing (besides still not guaranteeing uniqueness). When using `django < 2.1` and working on `sqlite` a bug within django causes field-specific scrubbing ( e.g. `date_object`) to fail. Please consider using a different database backend or upgrade to the latest django version. ## Scrubbing third-party models Sometimes you just don't have control over some code, but you still want to scrub the data of a given model. A good example is the Django user model. It contains sensitive data, and you would have to overwrite the whole model just to add the scrubber metaclass. That's the way to go: 1. Define your Scrubber class **somewhere** in your codebase (like a `scrubbers.py`) ```python # scrubbers.py class UserScrubbers: scrubbers.Faker('de_DE') first_name = scrubbers.Faker('first_name') last_name = scrubbers.Faker('last_name') username = scrubbers.Faker('uuid4') password = scrubbers.Faker('sha1') last_login = scrubbers.Null email = scrubbers.Concat(first_name, models.Value('.'), last_name, models.Value('@'), models.Value(settings.SCRUBBER_DOMAIN)) ```` 2. Set up a mapping between your third-party model and your scrubber class ```python # settings.py SCRUBBER_MAPPING = { "auth.User": "apps.account.scrubbers.UserScrubbers", } ``` ## Settings ### `SCRUBBER_GLOBAL_SCRUBBERS`: Dictionary of global scrubbers. Keys should be either field names as strings or field type classes. Values should be one of the scrubbers provided in `django_scrubber.scrubbers`. Example: ```python SCRUBBER_GLOBAL_SCRUBBERS = { 'name': scrubbers.Hash, EmailField: scrubbers.Hash, } ``` ### `SCRUBBER_RANDOM_SEED`: The seed used when generating random content by the Faker scrubber. Setting this to `None` means each scrubbing will generate different data. (default: 42) ### `SCRUBBER_ENTRIES_PER_PROVIDER`: Number of entries to use as source for Faker scrubber. Increasing this value will increase the randomness of generated data, but decrease performance. (default: 1000) ### `SCRUBBER_SKIP_UNMANAGED`: Do not attempt to scrub models which are not managed by the ORM. (default: True) ### `SCRUBBER_APPS_LIST`: Only scrub models belonging to these specific django apps. If unset, will scrub all installed apps. (default: None) ### `SCRUBBER_ADDITIONAL_FAKER_PROVIDERS`: Add additional fake providers to be used by Faker. Must be noted as full dotted path to the provider class. (default: empty list) ### `SCRUBBER_FAKER_LOCALE`: Set an alternative locale for Faker used during the scrubbing process. (default: None, falls back to Django's default locale) ### `SCRUBBER_MAPPING`: Define a class and a mapper which does not have to live inside the given model. Useful, if you have no control over the models code you'd like to scrub. ### `SCRUBBER_STRICT_MODE`: When strict mode is activated, you have to define a scrubbing policy for every field of every type defined in `SCRUBBER_REQUIRED_FIELD_TYPES`. If you have unscrubbed fields and this flag is active, you can't run `pyhton manage.py scrub_data`. ### `SCRUBBER_REQUIRED_FIELD_TYPES`: Defaults to all text-based Django model fields. Usually, privacy-relevant data is only stored in text-fields, numbers and booleans (usually) can't contain sensitive personal data. These fields will be checked when running `python manage.py scub_validation`. (default: (models.CharField, models.TextField, models.URLField, models.JSONField, models.GenericIPAddressField, models.EmailField,)) ### `SCRUBBER_REQUIRED_FIELD_MODEL_WHITELIST`: Whitelists a list of models which will not be checked during `scrub_validation` and when activating the strict mode. Defaults to the non-privacy-related Django base models. (default: ['auth.Group', 'auth.Permission', 'contenttypes.ContentType', 'sessions.Session', 'sites.Site', 'django_scrubber.FakeData',)) ````python SCRUBBER_MAPPING = { "auth.User": "my_app.scrubbers.UserScrubbers", } ```` (default: {}) ## Logging Scrubber uses the default django logger. The logger name is ``django_scrubber.scrubbers``. So if you want to log - for example - to the console, you could set up the logger like this: ```` LOGGING['loggers']['django_scrubber'] = { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', } ```` ## Making a new release [bumpversion](https://github.com/peritus/bumpversion) is used to manage releases. Add your changes to the [CHANGELOG](./CHANGELOG.md) and run `bumpversion `, then push (including tags) # Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [1.2.0] - 2023-04-01 ### Changed - Added scrubber validation - Thanks @GitRon - Added strict mode - Thanks @GitRon ## [1.1.0] - 2022-07-11 ### Changed - Invalid fields on scrubbers will no longer raise exception but just trigger warnings now - Author list completed ## [1.0.0] - 2022-07-11 ### Changed - Meta data for python package improved - Thanks @GitRon ## [0.9.0] - 2022-06-27 ### Added - Add functionality to scrub third party models like the Django user model, see https://github.com/RegioHelden/django-scrubber#scrubbing-third-party-models - Thanks @GitRon - Add tests for Python 3.10 - Thanks @costela ## [0.8.0] - 2022-05-01 ### Added - Add `keep-sessions` argument to scrub_data command. Will NOT truncate all (by definition critical) session data. Thanks @GitRon - Add `remove-fake-data` argument to scrub_data command. Will truncate the database table storing preprocessed data for the Faker library. Thanks @GitRon - Add Django 3.2 and 4.0 to test matrix ### Changed - Remove Python 3.6 from test matrix - Remove Django 2.2 and 3.1 from test matrix ## [0.7.0] - 2022-02-24 ### Changed - Remove upper boundary for Faker as they release non-breaking major upgrades way too often, please pin a working release in your own app ## [0.6.2] - 2022-02-08 ### Changed - Support faker 12.x ## [0.6.1] - 2022-01-25 ### Changed - Support faker 11.x ## [0.6.0] - 2021-10-18 ### Added - Add support to override Faker locale in scrubber settings ### Changed - Publish coverage only on main repository ## [0.5.6] - 2021-10-08 ### Changed - Pin psycopg2 in CI ti 2.8.6 as 2.9+ is incompatible with Django 2.2 ## [0.5.5] - 2021-10-08 ### Changed - Support faker 9.x ## [0.5.4] - 2021-04-13 ### Changed - Support faker 8.x ## [0.5.3] - 2021-02-04 ### Changed - Support faker 6.x ## [0.5.2] - 2021-01-12 ### Changed - Add tests for Python 3.9 - Add tests for Django 3.1 - Support faker 5.x - Update dev package requirements ## [0.5.1] - 2020-10-16 ### Changed - Fix travis setup ## [0.5.0] - 2020-10-16 ### Added - Support for django-model-utils 4.x.x ### Changed - Add compatibility for Faker 3.x.x, remove support for Faker < 0.8.0 - Remove support for Python 2.7 and 3.5 - Remove support for Django 1.x ## [0.4.4] - 2019-12-11 ### Fixed - add the same version restrictions on faker to setup.py ## [0.4.3] - 2019-12-04 ### Added - add empty and null scrubbers ### Changed - make `Lorem` scrubber lazy, matching README ### Fixed - set more stringent version requirements (faker >= 3 breaks builds) ## [0.4.1] - 2019-11-16 ### Fixed - correctly clear fake data model to fix successive calls to `scrub_data` (thanks [Benedikt Bauer](https://github.com/mastacheata)) ## [0.4.0] - 2019-11-13 ### Added - `Faker` scrubber now supports passing arbitrary arguments to faker providers and also non-text fields (thanks [Benedikt Bauer](https://github.com/mastacheata) and [Ronny Vedrilla](https://github.com/GitRon)) ## [0.3.1] - 2018-09-10 ### Fixed - [#9](https://github.com/RegioHelden/django-scrubber/pull/9) `Hash` scrubber choking on fields with `max_length=None` - Thanks to [Charlie Denton](https://github.com/meshy) ## [0.3.0] - 2018-09-06 ### Added - Finally added some basic tests (thanks [Marco De Felice](https://github.com/md-f)) - `Hash` scrubber can now also be used on sqlite ### Changed - **BREAKING**: scrubbers that are lazily initialized now receive `Field` instances as parameters, instead of field names. If you have custom scrubbers depending on the previous behavior, these should be updated. Accessing the field's name from the object instance is trivial: `field_instance.name`. E.g.: if you have `some_field = MyCustomScrubber` in any of your models' `Scrubbers`, this class must accept a `Field` instance as first parameter. Note that explicitly intializing any of the built-in scrubbers with field names is still supported, so if you were just using built-in scrubbers, you should not be affected by this change. - related to the above, `FuncField` derived classes can now do connection-based setup by implementing the `connection_setup` method. This is mostly useful for doing different things based on the DB vendor, and is used to implement `MD5()` on sqlite (see added feature above) - Ignore proxy models when scrubbing (thanks [Marco De Felice](https://github.com/md-f)) - Expand tests to include python 3.7 and django 2.1 ## [0.2.1] - 2018-08-14 ### Added - Option to scrub only one model from the management command - Support loading additional faker providers by config setting SCRUBBER\_ADDITIONAL\_FAKER\_PROVIDERS ### Changed - Switched changelog format to the one proposed on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) ## [0.2.0] - 2018-08-13 ### Added - scrubbers.Concat to make simple concatenation of scrubbers possible ## [0.1.4] - 2018-08-13 ### Changed - Make our README look beautiful on PyPI ## [0.1.3] - 2018-08-13 ### Fixed - [#1](https://github.com/RegioHelden/django-scrubber/pull/1) badly timed import - Thanks to [Charlie Denton](https://github.com/meshy) ## [0.1.2] - 2018-06-22 ### Changed - Use bumpversion and travis to make new releases - rename project: django\_scrubber → django-scrubber ## [0.1.0] - 2018-06-22 ### Added - Initial release %prep %autosetup -n django-scrubber-1.2.0 %build %py3_build %install %py3_install install -d -m755 %{buildroot}/%{_pkgdocdir} if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi pushd %{buildroot} if [ -d usr/lib ]; then find usr/lib -type f -printf "\"/%h/%f\"\n" >> filelist.lst fi if [ -d usr/lib64 ]; then find usr/lib64 -type f -printf "\"/%h/%f\"\n" >> filelist.lst fi if [ -d usr/bin ]; then find usr/bin -type f -printf "\"/%h/%f\"\n" >> filelist.lst fi if [ -d usr/sbin ]; then find usr/sbin -type f -printf "\"/%h/%f\"\n" >> filelist.lst fi touch doclist.lst if [ -d usr/share/man ]; then find usr/share/man -type f -printf "\"/%h/%f.gz\"\n" >> doclist.lst fi popd mv %{buildroot}/filelist.lst . mv %{buildroot}/doclist.lst . %files -n python3-django-scrubber -f filelist.lst %dir %{python3_sitelib}/* %files help -f doclist.lst %{_docdir}/* %changelog * Thu Jun 08 2023 Python_Bot - 1.2.0-1 - Package Spec generated