Install
Works with Drupal: ^11.3 || ^12Using Composer to manage Drupal site dependencies
Alternative installation files
Release notes
WARNING: THERE IS NO MIGRATION PATH FROM A RELEASE BEFORE 1.0.0-ALPHA12.
Do not update a site running alpha11 or earlier to this release. Personal Data Vault ships no update hooks, and none is coming until the first beta release. A site on alpha11 or earlier must uninstall the Personal Data Vault modules and install 1.0.0-alpha15 fresh, as the alpha12 release notes describe. From alpha12, alpha13 or alpha14, follow the steps below.
BEFORE YOU UPDATE
- Drush 13.7 or later.
drush pdv:rotate-keysis now a Symfony Console command, which Drush discovers from 13.7. Composer refuses this release next to Drush 13.0 to 13.6: update Drush first. Drupal 11 already needs Drush 13. - A secret store on plain http must be allowed. With PDV Vault, the store address must use https, as the vault connection of a consumer site already must. Plain http is accepted for a loopback host, or for a host listed in settings.php, such as a docker service on a trusted network:
$settings['pdv_vault.allow_http_hosts'] = ['openbao'];. Without it, every vault read and write fails. - Add the new index by hand. A new install creates it; an installed site runs, once:
drush php:eval "\Drupal::database()->schema()->addIndex('pdv_subject_key', 'idx__pdv_subject_key__owner', ['owner_uid'], ['fields' => ['owner_uid' => ['type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE]]]);" - Then rebuild the cache, and import the French translations of every Personal Data Vault module.
Security: a vault read could undo passphrase protection
Recording vault activity saved the whole subject-key record the request had loaded. When the owner turned passphrase protection on in another request meanwhile, the next read wrote the old record back: the key was protected by the Master KEK alone again. A dormancy reminder and a Master KEK re-wrap could do the same. Each now writes only its own columns, and only while the record is still the one it read; a re-wrap that meets a change is left to the next rotation run (Issue 3620305).
Security: passphrases
- A new passphrase needs at least 12 characters. The site sets its own minimum under Minimum passphrase length on the Personal Data Vault settings, never below 8 nor above 128, and the passphrase field says it. Passphrases already set keep working.
- Removing protection checks the current passphrase against the unlock form's limit (
flood.unlock_limit), on the same counter: it is no longer a second, unlimited way to guess it. - Passphrases are typed into password fields: a screen reader no longer reads them aloud. Browsers may offer to save them again.
Security: secrets sent in clear
PDV Vault sent the store token and every subject key to its address whatever the scheme; it now requires https, as above. A redirect could also carry a secret elsewhere: a consumer site's calls to the vault and its token request follow no redirect any more, and PDV Vault follows one, as a standby store node sending a request on to the active one, only to an address the same https rule allows (Issue 3620305).
Privacy: a webform submission edited by an administrator
The PDV Webform handler acted on every submission as if it were the current user's own. An administrator editing someone's submission saw it filled from the administrator's own vault, and saving wrote the submitter's answers into it. It now prefills only a new submission, and checks write access and saves back only for the current user's own submission, once it is submitted: a resumed draft keeps what was entered, and saving a draft writes nothing to the vault (Issue 3620309).
Several tenants: requests are handled in their consumer's tenant
A store request from a consumer of another tenant than the default one could not be reviewed, and approving it created the item in the default tenant, where no key opens it. The consent pages now act in the tenant of the request's consumer, through the tenant context. The owner's vault page lists the requests of the tenant it runs in only, and the owner's grant and trust forms offer that tenant's consumers. A store request is declined under the lock its approval takes, so the two cannot interleave (Issue 3620306, Issue 3620310).
Cross-site API
- Kind access (
GET /pdv-api/user/{handle}/kind-access) answered 500 on a locked passphrase vault. It decrypts nothing now. - Two record updates at once could lose the fields of one of them. Updates are serialized; the second gets a 409 to retry, now listed in the API contract.
- Refusals (403, 400, 409, 413, 429) carry
Cache-Control: no-store, privateand the vault'sX-Correlation-ID, as the contract promised. - On a consumer site, an unreachable vault no longer turns the unlock return page into a 500.
- The consent landing answers 400, not 500, to a
statelonger than 64 URL-safe characters or areturn_urllonger than 1024. - After an unlock, the owner returns to the page they left with its query string, also on a site installed under a base path.
(Issue 3620307, Issue 3620309)
Item kinds in use
A kind in use keeps its type, and is not limited to a tenant that would leave some of its items outside, whatever saves it: its form, a configuration import or code. A kind is also not deleted, or limited to a tenant, while a store request of it waits for approval (Issue 3620309).
Master KEK rotation
- While the target Master KEK cannot be used, for example because its store is down, a rotation waits instead of setting every subject aside and stopping "incomplete".
drush pdv:rotate-keyssays so. - A target key missing from the key repository broke the status report, the Vault subjects page, the rotation form and drush. The status report now shows it as an unusable Master KEK.
Settings, ECA and accessibility
- A dormancy reminder line that is not a whole number of days is refused instead of dropped.
- An allowed return-URL origin must be
scheme://host[:port], with no path, and origins compare in lower case. - The ECA owner tokens are filled on read and write events too.
- The vault page's View link and the file form's current-file link say, to a screen reader, that they open a new tab.
- A trust given again after a refusal is audited as created, and a change of its expiry as the new
pdv.trust.updated.
(Issue 3620305, Issue 3620309)
Faster pages, lighter cron
The owner's vault page decrypts its items with one key unwrap, not one per item: with PDV Vault, one request to the secret store instead of one per item. A file read unwraps once, and the operator's inspection page decrypts nothing, as it says. Measured in the kernel query probe against alpha14: the vault page sends 11 queries instead of 62, the consent approval page 19 instead of 40, a record update 15 instead of 24, a file read 5 instead of 12. The garbage collector reads large tables a range at a time instead of whole on every cron run. Uninstalling the vault or the consumer module now removes what they keep in state and key-value storage, the consumer's vault access tokens included (Issue 3620306, Issue 3620308).
For developers
- Moved:
Drupal\pdv_client\HttpsPolicyisDrupal\pdv\HttpsPolicy;allows()isisAllowedAddress($url, $allow_setting), withPdvVaultConnectionInterface::ALLOW_HTTP_HOSTS_SETTINGandVaultTransit::ALLOW_HTTP_HOSTS_SETTING. - Replaced:
Drupal\pdv_engine\Drush\Commands\RotationCommandsbyRotateKeysCommand. - New:
Vault::lockOwnerKind(),Vault::readWithMetadata(),SubjectKeyManager::isMasterKekUsable()andrecordDormancyWarning(),MasterKekRotator::isTargetUsable(),ItemKindRepository::countStoreRequests(),countItemsOutsideTenant()andisInUse(),ConsumerContext::setActingConsumerOfRequest(),ConsumerTenantResolver::getTenantIdOfConsumer(), the servicepdv_client.http_client, the exceptionsSubjectKeyChangedExceptionandRedirectRefusedException, the settingpassphrase_min_length. GrantManager::denyRequest()refuses a store request: useStoreRequestManager::denyOffer().- Removed, unused:
Vault::loadOwnerItems(),SubjectKeyManager::unlockSession(),lockSession(),changePassphrase()andrewrap(),GrantManager::revokeByIdentity(),TrustManager::revokeByIdentity(),HandleStore::getLinkedUsersByConnection(). - Renamed, verb first:
getRecordKinds(),getRecordKindIds(),buildRotationStatus(),buildUnlockPrompt(),buildWriteGrantError(),getHandoffParkStore(),getManageReturnUrl(),submitBatch(),sendTransitRequest(),buildSaveToVaultElement(). - New constructor arguments:
SubjectKeyManager(@database,@cache_tags.invalidator),ConsumerContext(@entity_type.manager),GrantManagerandStoreRequestManager(ConsumerContext),GarbageCollector(@keyvalue),PdvRequirements(SubjectKeyManagerinstead of the key repository and the wrap plugin manager),VaultPassphraseForm(ConsumerFloodGuard), and several forms (ConsumerContextor the tenant context).