RFC: Privacy Data Types - #11818
Conversation
Personal data values are wrapped in privacy data types that carry their origin (Source) and release the raw value only through resolve() with an explicitly stated Purpose. Every resolve is reported to a PrivacyLogger, forming the GDPR audit trail. - PrivacyDataType<T> interface + abstract base, first concrete type PostalAddress (compound over usr_data street/city/zipcode/country) - Sources: DbTableColumn(s), UserInput, ExternalApi, SessionData, LegacySource; KnownSources catalogue with named usr_data getters - Purposes: StoreInTable, DisplayToUser, PassToComponent, TechnicalProcessing, LegacyAccess (marks unmigrated call sites) - PrivacyLogger as contribute/seek collection with CompositeLogger; no persistent backend yet (storage/retention decision pending) - Services defined/implemented in the Data component bootstrap; types are created via an injectable Factory that binds the audit logger (no static named constructors)
Legacy code can access the privacy data type services via $DIC[\ILIAS\Data\Privacy\Services::class] until all consumers are migrated to the component bootstrap.
- Rules: NoRawValueAccessRule (no PrivacyDataType into var_dump/ json_encode/serialize etc.), StoreInTableTargetRule (StoreInTable requires a DbTarget), PreferKnownSourcesRule (no DbTableColumn(s) literals outside the KnownSources catalogue; escape hatch @privacy-undocumented) - Return type extension: PrivacyDataType<T>::resolve() is typed as T - PrivacyResolveCollector + report rule: every resolve() call site is collected with purpose and component and emitted into PHPStan's JSON output; KnownSources getters are evaluated so reports show the actual table/columns - Generator renders per-component privacy documentation (stored / displayed / passed / technical / unmigrated legacy access + GDPR summary) to PRIVACY_DATA.md; --overwrite-privacy-md switches to PRIVACY.md once the community decides to replace the handwritten files - CLI: scripts/Privacy/generate-privacy-docs.php [--run-phpstan] [--component=X] [--dry-run]
Profile\Data now holds the residential address as a single PostalAddress privacy value instead of four bare strings. The repository wraps the value on read (source usr_data street/city/ zipcode/country) and resolves it on store with a StoreInTable purpose, so every persistence of the address lands in the audit trail. Migrated consumers state their real purpose: - profile form fields (Street/City/ZipCode/Country/Location) resolve with DisplayToUser and write back with a UserInput source - PublicProfileGUI resolves with DisplayToUser for the profile page and the vCard export - the profile mail body resolves with PassToComponent(Mail) - the XML import parser writes with an ExternalApi(xml_import) source The string-based accessors on ilObjUser and Profile\Data remain as deprecated delegates that resolve with a LegacyAccess purpose (reads) or a LegacySource (writes) — nothing breaks, and every unmigrated access shows up in the audit trail and the generated report. Raw SQL reads of the address columns (user lists, search) bypass the wrapper and are left for later migration steps.
The user info overlays of the map GUIs resolve the postal address privacy type with an explicit DisplayToUser purpose instead of the deprecated string accessors.
The USER_STREET/CITY/ZIPCODE/COUNTRY placeholder values resolve the postal address privacy type with a PassToComponent(Certificate) purpose instead of the deprecated string accessors.
ilMStListUser holds the address as a PostalAddress privacy value; the list fetcher wraps the raw usr_data columns with the proper source. Table rendering and Excel/CSV export resolve with DisplayToUser purposes; the string accessors remain as deprecated legacy delegates.
The user country criterion resolves the postal address privacy type with a TechnicalProcessing purpose instead of the deprecated string accessor.
shibUser writes street/zipcode/country received from the identity provider into the postal address privacy type with an ExternalApi(shibboleth) source instead of the deprecated setters.
Output of scripts/Privacy/generate-privacy-docs.php for the components migrated to the postal address privacy type (User, Maps, Certificate, MyStaff, LegalDocuments).
DefaultMappings::conditionDefinitions() now pulls Purposes from the container in addition to ilObjDataCache. The TermsOfService and DataProtection consumer tests still expected a single offsetGet call.
|
Jour Fixe, 27 JUL 2026: Fabian gave an overview on the pull request and the concept behind the privacy data types. He highly appreciates to get feedback from authorities and the TB about this proposal to implement a general infrastructure for a ‚privacy by design‘ approach in ILIAS. |
|
VOTING In other words, whether you generally
That gives us an idea of how we should proceed from here. |
This PR proposes the general infrastructure für Data: Privacy Data Types and a first implementation for "Postal Adress".
Please have a look at the general information in https://github.com/srsolutionsag/ILIAS/blob/0cd06a7f741cf8f007dc3b5552868354b0029d7d/components/ILIAS/Data/src/Privacy/README.md
See the "request" for something like a vote as well: #11818 (comment)