Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Identity and access

Explanation

Privacy and data-subject requests

How the privacy module registers data-subject requests, collects a person's data from every module, anonymises it, and replays erasures after a restore.

A data-subject request (SubjectRequest) records an application by a person to see, correct, erase, or object to the processing of their personal data. The privacy module keeps the register, the identity check, the deadline, and the outcome. It never reads another module's tables: it fans out two EventBus events, and each module that holds personal data answers for its own tables in its privacy_handlers.py. An erasure is also written to a ledger outside the database so that a restored backup cannot bring the data back unnoticed. The module overview is on the privacy module page.

In short

  • Four request types exist: access, rectification, erasure, objection (src/cluster_erp/modules/privacy/requests/models.py:13-18).
  • Four statuses exist: received, verified, completed, rejected; every transition is an action (src/cluster_erp/modules/privacy/requests/models.py:32-36, src/cluster_erp/modules/privacy/requests/services.py:196-527).
  • The due date is the receipt date plus the setting privacy_response_days (src/cluster_erp/modules/privacy/requests/models.py:61-62, src/cluster_erp/modules/privacy/requests/services.py:157-170, src/cluster_erp/config/settings.py:733).
  • CollectSubjectData gathers a copy and AnonymiseSubject erases by anonymisation; nine modules subscribe to both (src/cluster_erp/core/contracts.py:2385-2438).
  • A completed erasure is appended to a node-local ledger, sealed with the backup key when one is set, and replayed at every start-up (src/cluster_erp/core/erasure_ledger.py:1-16, src/cluster_erp/server/app.py:255-311).

How it works

Request lifecycle

The state diagram shows the statuses and the actions that move them.

stateDiagram-v2
    [*] --> received: create
    received --> verified: verify
    verified --> verified: export
    verified --> completed: anonymise, erasure only
    verified --> completed: complete
    received --> rejected: reject
    verified --> rejected: reject
    completed --> [*]
    rejected --> [*]

In prose:

  1. create gives the request a yearly business key, sets due_at, and starts at received (src/cluster_erp/modules/privacy/requests/services.py:157-172).
  2. verify records the identity method and moves received to verified (src/cluster_erp/modules/privacy/requests/services.py:198-207).
  3. prepare_export, allowed only while verified, collects the data and stores one JSON file as an attachment on the request (src/cluster_erp/modules/privacy/requests/services.py:247-272). export_file returns it (src/cluster_erp/modules/privacy/requests/services.py:274-295).
  4. anonymise, allowed only for an erasure request that is verified, requires the operator to type the request code, then erases and completes the request (src/cluster_erp/modules/privacy/requests/services.py:297-327).
  5. complete closes a verified request with a resolution note; reject closes a received or verified request with a reason (src/cluster_erp/modules/privacy/requests/services.py:507-527).
  6. A completed or rejected request cannot be edited (src/cluster_erp/modules/privacy/requests/services.py:174-186).

Collecting a copy

collect fetches the subject's e-mail addresses through GetPartyContactValues, emits CollectSubjectData with the party id, the optional user id, and those addresses, and keeps every answer that has sections (src/cluster_erp/modules/privacy/requests/services.py:209-245). Each answer has the shape {"module", "sections": [{"key", "label", "rows"}]} and must not contain secrets such as password hashes or tokens (src/cluster_erp/core/contracts.py:2385-2401).

Anonymising

_apply_erasure runs all modules in one transaction (src/cluster_erp/modules/privacy/requests/services.py:329-424):

  1. It emits AnonymiseSubject with the marker from privacy_anonymised_marker, a reason, and three operator decisions: include_employment_records, include_correspondence, and include_documents (src/cluster_erp/modules/privacy/requests/services.py:345-357, src/cluster_erp/modules/privacy/requests/schemas.py:70-73).
  2. Each handler overwrites identifying fields, deactivates the rows, and returns the affected record ids (src/cluster_erp/core/contracts.py:2410-2425).
  3. The service adds the attachments filed on the person's party record, removes reported files from storage, and retires their rows (src/cluster_erp/modules/privacy/requests/services.py:358-395).
  4. It replaces the change-history values of every affected record with a marker that names the request (src/cluster_erp/modules/privacy/requests/services.py:396-406).
  5. It stores a per-module summary, completes the request, and writes the ledger line (src/cluster_erp/modules/privacy/requests/services.py:407-424).

Modules that answer

Nine modules subscribe to both events, found with grep for CollectSubjectData in module.py files:

Module Handler file Subscription
auth src/cluster_erp/modules/auth/privacy_handlers.py src/cluster_erp/modules/auth/module.py:101-108
crm src/cluster_erp/modules/crm/privacy_handlers.py src/cluster_erp/modules/crm/module.py:110-113
documents src/cluster_erp/modules/documents/privacy_handlers.py src/cluster_erp/modules/documents/module.py:89-96
intranet src/cluster_erp/modules/intranet/privacy_handlers.py src/cluster_erp/modules/intranet/module.py:216-223
logs src/cluster_erp/modules/logs/privacy_handlers.py src/cluster_erp/modules/logs/module.py:55-62
mail src/cluster_erp/modules/mail/privacy_handlers.py src/cluster_erp/modules/mail/module.py:63-70
owned_organisations src/cluster_erp/modules/owned_organisations/privacy_handlers.py src/cluster_erp/modules/owned_organisations/module.py:81-88
parties src/cluster_erp/modules/parties/privacy_handlers.py src/cluster_erp/modules/parties/module.py:159-166
procurement src/cluster_erp/modules/procurement/privacy_handlers.py src/cluster_erp/modules/procurement/module.py:136-137

As an example, the auth handler returns the sign-in account without the password hash, and on erasure overwrites the user name and e-mail, detaches the employee link, deactivates the account, and stamps last_logout_at so that every token is refused (src/cluster_erp/modules/auth/privacy_handlers.py:1-104).

Erasure ledger

erasure_ledger.append writes one line per completed erasure into a per-database file in a folder under the backup_dir setting; the line holds the request code, party id, user id, subject e-mail addresses, the three decisions, and the time (src/cluster_erp/core/erasure_ledger.py:38-74, src/cluster_erp/modules/privacy/requests/services.py:426-457). With backup_encryption_key set, each line is sealed; without it, the line is plain JSON (src/cluster_erp/core/erasure_ledger.py:42-53). At start-up, _replay_erasure_ledger reads the ledger of every database the node serves, re-applies any erasure that the database no longer shows as completed, and then runs the retention purge (src/cluster_erp/server/app.py:255-311, src/cluster_erp/modules/privacy/requests/services.py:459-505). tests/test_erasure_ledger.py (4 tests) pins the sealing.

Why it is built this way

  • The module docstring states that the privacy module imports no other module; the modules that hold personal data answer for their own tables (src/cluster_erp/modules/privacy/module.py:16-24).
  • The request row stays after completion as evidence that the request was answered in time, and it is never hard-deleted (src/cluster_erp/modules/privacy/requests/models.py:40-46).
  • Anonymisation handlers must join the request session so that a failure anywhere rolls every module back (src/cluster_erp/core/contracts.py:2411-2425).
  • Employment records, correspondence, and documents stay by default because they commonly sit under a statutory retention duty (src/cluster_erp/core/contracts.py:2421-2433).
  • The ledger lives outside the database so that a restore cannot silently undo an erasure (src/cluster_erp/core/erasure_ledger.py:3-10).

Rules and boundaries

  • Every route requires a subject_request.* code: read, create, update, delete, verify, export, anonymise, complete, or reject (src/cluster_erp/modules/privacy/requests/routes.py:39-215).
  • Credential and special-category values are redacted in change history in any case; see change history.

Interaction with other parts

Limits and open points

  • A ledger write failure is logged and does not undo the erasure (src/cluster_erp/modules/privacy/requests/services.py:433-457).
  • Rectification has no automated step; it is closed with complete and a resolution note (src/cluster_erp/modules/privacy/requests/services.py:507-516).

Terms introduced

Term Code identifier Definition Turkish term Source
data-subject request SubjectRequest A registered application by a person about their personal data. İlgili Kişi Başvurusu src/cluster_erp/modules/privacy/requests/models.py:39
anonymisation AnonymiseSubject Erasure carried out by overwriting identifying fields with a marker and deactivating rows. anonimleştirme src/cluster_erp/core/contracts.py:2411
subject data collection CollectSubjectData The fan-out in which every module returns what it holds about a person. ilgili kişi verisi toplama src/cluster_erp/core/contracts.py:2385
erasure ledger erasure_ledger The append-only record of completed erasures, kept outside the database and replayed after a restore. silme defteri src/cluster_erp/core/erasure_ledger.py:1

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13