The account's primary number is a way back in #144
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
Reference
Postulo/postulo#144
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
What exists
The intrinsic link is already there, and it is worth being precise about its shape. A
PhoneNumberhas anowner— the account — and aholder, which is a generic relation toeither that person's
Profileor aContactat a company. So every row belongs to anaccount, and the account's own numbers are the ones whose holder is their profile. Exactly
one of those is
is_primary, enforced by a partial unique index rather than by a form.Numbers are unique across the instance, which was a deliberate choice made against the
alternative, and they are unverified, which was a consequence of having no channel to
verify them over.
The recovery machinery is already written to accept a new route.
recovery_routes()returns a list,
refuse_switching_off()reads it, and the docstring says a route that landsis added there and "the lock below opens by itself". Nothing needs redesigning to admit
one — which is why the interesting work is entirely in the two prerequisites.
What this asks for
The account's primary number as a way back in, joining email and the passkey.
Worth being careful about
The choice made in #90 turns out to have been the right one for this. Instance-wide
uniqueness was argued about and taken with its disclosure named; a number that identifies an
account for recovery has to be unique, so the rule that looked like tidiness is now
load-bearing. Worth recording that it was not chosen for this reason and holds anyway.
Primary is a property of a holder, not of an account. Each contact has a primary number
too, and a recovery lookup that reads
is_primarywithout filtering the holder would happilyfind a recruiter's mobile. Anything here filters on the profile first.
The number that gets somebody back in should probably not be the one on their CV. The
primary is what a document prints — a recruiter dials it. Making the same row the recovery
route couples "the number I publish" to "the number that proves I am me", and somebody who
changes the number on their CV changes their recovery route without being told. Either the
recovery number is chosen separately, or changing the primary says out loud what else it
changes.
Switching the plugin off must not remove a way back in. #90's rule is that off shows the
primary and keeps the rest; if the primary is a recovery route, off has to keep it working
as one while hiding the others. And the account-recovery interlock (#104, #103) must count it
regardless of any per-person plugin state, or an administrator switching the feature off for
somebody quietly removes their route — which is the failure #104 exists to prevent, arriving
by a different door.
Which reveals a boundary worth drawing. Recovery is instance policy; a feature plugin is
a per-person preference. A per-person switch that can silently change whether an account is
recoverable is the wrong shape. Either recovery reads only verified numbers regardless of the
plugin, or the plugin cannot be switched off for an account whose only route is a number —
the same lock, one level down.
Somebody with no verified number is unaffected, which is most people on the day this
lands, and the lock must stay shut for them.
_accounts_needing_email()counts accounts withnothing else; it grows a second clause rather than an assumption.
PhoneNumber.is_recovery, and on this release no number carries it anywhere — nothingcan confirm a number until an operator installs a gateway (#143), so the route exists and
refuses to pretend, the same shape the two prerequisites took.
The issue's own doubt, resolved the way it leaned. "The number that gets somebody back in
should probably not be the one on their CV" — it is not. A separate flag rather than a
sentence warning that changing the primary changes something else: the coupling is removed
instead of explained. One per account, by partial unique index, exactly as
is_primaryis.Primary is a property of a holder, and this one is not.
mine()filters on the holderbeing the person's own profile, so a recruiter's switchboard cannot be nominated.
clean()refuses that and refuses an unconfirmed number, on the model rather than only the form,
because the API, a management command and a shell all reach it. Editing the digits takes the
nomination away with the confirmation it rested on, in the same
save().The boundary the issue asked to be drawn, drawn the first way it offered. Recovery reads
a confirmed number regardless of the plugin. A per-person switch that could silently decide
whether an account is recoverable is the wrong shape — an administrator switching several
telephone numbers off for somebody would otherwise remove their way back in, which is the
failure #104 exists to prevent arriving through a different door. The plugin still governs
what is shown and used; two tests hold both halves.
accounts_needing_email()grew a clause rather than an assumption, and needs both: anominated confirmed number and a gateway that can reach one. Somebody with no number is
unaffected and the lock stays shut for them, which is every account on every instance today.
#90's instance-wide uniqueness holds. Worth recording as the issue asked: it was argued
for on other grounds and turns out to be load-bearing here, since a number that identifies an
account for recovery has to be unique.
An archive cannot carry a nomination in — the importer drops it with the confirmation it
depends on.
FORMAT_VERSIONis 7. The control appears in the interface only where theinstance could confirm a number at all, because a control nobody can use beside a promise
nobody can keep is worse than no control.
19 tests in
tests/test_recovery_number.py, a wiki section, and seven strings in all 39European catalogues.
Shipped in
ba4c3f9on0.3.0, withmainkept level.