AccountDeletion
in package
Account deletion requested by the user — the pure rules shared by the Api manager, the cron and the tests. No I/O.
Life cycle of cms_system_users:
- (OAuth account without a password only) confirmation pending —
deletion_token_*hold the hash of the e-mailed confirmation link (self::CONFIRM_TTL_SECONDS), no request yet, - deletion requested —
deletion_requested_atset,deletion_token_*hold the hash of the e-mailed cancel link valid until the end of the grace period,deletion_noncethe random version of this request; while the anonymization runs,deletion_claimed_atholds its claim, - anonymized —
deleted_atset, personal data wiped, the row stays for the references (orders, documents, posts).
Tokens follow PasswordResetToken: the plain token travels only in the e-mailed link, the database keeps its sha256.
Table of Contents
Constants
- ANONYMIZED_EMAIL_DOMAIN : string = 'anonymized.invalid'
- Domain of the anonymized e-mail; `.invalid` is reserved (RFC 2606), nothing is ever delivered.
- CLAIM_STALE_SECONDS : int = 900
- How long the anonymization claim of one run holds. A cancel is refused while the claim is fresh; a claim left behind by a crashed run goes stale after this and the account can be cancelled or claimed again (the module steps are idempotent).
- CONFIRM_TTL_SECONDS : int = 86400
- Validity of the confirmation link for an account without a password (24 h).
- DEFAULT_GRACE_DAYS : int = 7
- Grace period when the setting `account.deletion.graceDays` is missing or invalid.
- MAX_GRACE_DAYS : int = 365
- Upper bound of the setting — a typo must not keep a requested deletion for years.
Methods
- buildAnonymizedEmail() : string
- Unique placeholder e-mail of an anonymized account — frees the original address for a new registration while the unique index stays satisfied.
- buildAnonymizedUsername() : string
- buildClaimStaleBefore() : DateTime
- Claims taken before this moment are stale ({@see self::CLAIM_STALE_SECONDS}).
- buildConfirmExpiry() : DateTime
- buildDueBefore() : DateTime
- Requests made before this moment are past their grace period (cron cut-off).
- buildScheduledAt() : DateTime
- When the account requested at `$requestedAt` gets anonymized.
- buildUnusablePasswordHash() : string
- A value no password hash verifier accepts (not a bcrypt/argon string) — the anonymized account cannot sign in even if some gate were bypassed. Random, so it is not a constant anyone could recognise or reuse.
- generateRequestNonce() : string
- Random version of one request (`deletion_nonce`, 32 hex).
- generateToken() : string
- hashToken() : string
- isDue() : bool
- Has the grace period of a request made at `$requestedAt` passed?
- isTokenValid() : bool
- Stored token still usable? Empty hash, missing or past expiry never matches.
- normalizeGraceDays() : int
- Setting value → grace days within 1…{@see self::MAX_GRACE_DAYS}; anything else = default.
Constants
ANONYMIZED_EMAIL_DOMAIN
Domain of the anonymized e-mail; `.invalid` is reserved (RFC 2606), nothing is ever delivered.
public
string
ANONYMIZED_EMAIL_DOMAIN
= 'anonymized.invalid'
CLAIM_STALE_SECONDS
How long the anonymization claim of one run holds. A cancel is refused while the claim is fresh; a claim left behind by a crashed run goes stale after this and the account can be cancelled or claimed again (the module steps are idempotent).
public
int
CLAIM_STALE_SECONDS
= 900
CONFIRM_TTL_SECONDS
Validity of the confirmation link for an account without a password (24 h).
public
int
CONFIRM_TTL_SECONDS
= 86400
DEFAULT_GRACE_DAYS
Grace period when the setting `account.deletion.graceDays` is missing or invalid.
public
int
DEFAULT_GRACE_DAYS
= 7
MAX_GRACE_DAYS
Upper bound of the setting — a typo must not keep a requested deletion for years.
public
int
MAX_GRACE_DAYS
= 365
Methods
buildAnonymizedEmail()
Unique placeholder e-mail of an anonymized account — frees the original address for a new registration while the unique index stays satisfied.
public
static buildAnonymizedEmail(int $userId) : string
Parameters
- $userId : int
Return values
stringbuildAnonymizedUsername()
public
static buildAnonymizedUsername(int $userId) : string
Parameters
- $userId : int
Return values
stringbuildClaimStaleBefore()
Claims taken before this moment are stale ({@see self::CLAIM_STALE_SECONDS}).
public
static buildClaimStaleBefore(DateTimeInterface $now) : DateTime
Parameters
- $now : DateTimeInterface
Return values
DateTimebuildConfirmExpiry()
public
static buildConfirmExpiry(DateTimeInterface $now) : DateTime
Parameters
- $now : DateTimeInterface
Return values
DateTimebuildDueBefore()
Requests made before this moment are past their grace period (cron cut-off).
public
static buildDueBefore(DateTimeInterface $now, int $graceDays) : DateTime
Parameters
- $now : DateTimeInterface
- $graceDays : int
Return values
DateTimebuildScheduledAt()
When the account requested at `$requestedAt` gets anonymized.
public
static buildScheduledAt(DateTimeInterface $requestedAt, int $graceDays) : DateTime
Parameters
- $requestedAt : DateTimeInterface
- $graceDays : int
Return values
DateTimebuildUnusablePasswordHash()
A value no password hash verifier accepts (not a bcrypt/argon string) — the anonymized account cannot sign in even if some gate were bypassed. Random, so it is not a constant anyone could recognise or reuse.
public
static buildUnusablePasswordHash() : string
Return values
stringgenerateRequestNonce()
Random version of one request (`deletion_nonce`, 32 hex).
public
static generateRequestNonce() : string
A new request, a cancel, the claim of the anonymization and the anonymization itself replace
or clear it, so every ticket / claim bound to the previous value stops matching — even when
deletion_requested_at (second precision) ends up the same.
Return values
stringgenerateToken()
public
static generateToken() : string
Return values
stringhashToken()
public
static hashToken(string $plainToken) : string
Parameters
- $plainToken : string
Return values
stringisDue()
Has the grace period of a request made at `$requestedAt` passed?
public
static isDue(DateTimeInterface|null $requestedAt, int $graceDays, DateTimeInterface $now) : bool
Parameters
- $requestedAt : DateTimeInterface|null
- $graceDays : int
- $now : DateTimeInterface
Return values
boolisTokenValid()
Stored token still usable? Empty hash, missing or past expiry never matches.
public
static isTokenValid(string|null $storedHash, DateTimeInterface|null $expiresAt, DateTimeInterface $now) : bool
Parameters
- $storedHash : string|null
- $expiresAt : DateTimeInterface|null
- $now : DateTimeInterface
Return values
boolnormalizeGraceDays()
Setting value → grace days within 1…{@see self::MAX_GRACE_DAYS}; anything else = default.
public
static normalizeGraceDays(mixed $value) : int
Parameters
- $value : mixed