Telephone numbers: a sync plugin learns which numbers are held on the instance without spending the #142 allowance #333

Closed
opened 2026-10-01 06:31:28 +00:00 by tiagoagueda · 2 comments
Owner

Found on 2026-09-30, reading postulo-dav against the core at 0a5cad587 and following every caller of phone_numbers.taken_elsewhere.

#142 bounded how often one account may learn that a telephone number is already recorded somewhere on the instance: each such answer spends one unit of POSTULO_NUMBER_RATE (20 an hour by default) through phone_numbers.collision_noticed. The profile and contact forms, the API's add_contact and the candidate-file importer ask through it. The plugin surface does not: what it hands a plugin is the bare question.

  • Where:
    • src/postulo/plugins/api.py:78, 271: phone_number_is_taken is core.phone_numbers.taken_elsewhere itself. It takes a number and an exclude_pk, no person, and spends nothing. The wiki's Writing a plugin lists it as "Writing one back, leaving the primary flag and the uniqueness rules to Postulo".
    • postulo-dav, src/postulo_dav/sync.py:158-162 (apply_card): asks it for every card it pulls, and leaves a taken number off the contact without a note.
    • For comparison, src/postulo/resume/candidate.py:840-866 (_somebody_elses) spends the allowance and, once it is spent, refuses every further number in the same words, "because telling them apart is the answer".
  • What happens: a person who syncs an address book they control puts a card per number in it. After one run, each contact's page shows whether its number was kept. A number left off is one some other row on the instance holds, another account's own number included. A run asks about as many numbers as the book holds, and a sync runs as often as every fifteen minutes, so the 20-an-hour limit does not apply at all.
  • Who can trigger it: any account that may use a sync plugin on an instance where one is installed. It reveals that a number is held here, not by whom, which is the disclosure #142 set out to limit.
  • Not affected: core/importer.py:277 asks the same bare question, but only an operator runs manage.py import_data.
  • Fix: the surface stops handing out the bare question.
    1. phone_number_is_taken takes the person asking, spends their allowance when the answer is yes, and once it is spent answers every number alike, as _somebody_elses does. Or it leaves the surface, and save_phone_number reports a refusal itself, which is what postulo/postulo#328 proposes for the row services ("hearing it spends the #142 allowance as a form does").
    2. Either way it is a change to a name the surface promises, so it is a Deprecated entry first and a Removed one in a later release, as the surface's own promise says (plugins/api.py), with its line in Writing a plugin (tests/test_wiki_surface.py) and a test in tests/test_plugin_surface.py. postulo-dav then follows, with a note in the run's report when a number was left off.
    3. If postulo/postulo#329 limits instance-wide uniqueness to a person's own numbers, a contact's number no longer collides with anything and this question disappears for contacts. The two should be decided together.

Related: #142 and #90 (closed), which set the rule and its limit.

Done when

  • A test syncs more cards with numbers held elsewhere than the allowance covers, and shows that the answers stop telling taken from free once it is spent.
  • No name on postulo.plugins.api answers whether a number is held on the instance without naming who asks.
  • postulo-dav's report says when a card's number was left off.

Found while filing the extension and DAV plugin improvements of 2026-09-30.

Found on 2026-09-30, reading postulo-dav against the core at `0a5cad587` and following every caller of `phone_numbers.taken_elsewhere`. #142 bounded how often one account may learn that a telephone number is already recorded somewhere on the instance: each such answer spends one unit of `POSTULO_NUMBER_RATE` (20 an hour by default) through `phone_numbers.collision_noticed`. The profile and contact forms, the API's `add_contact` and the candidate-file importer ask through it. The plugin surface does not: what it hands a plugin is the bare question. - **Where:** - `src/postulo/plugins/api.py:78, 271`: `phone_number_is_taken` is `core.phone_numbers.taken_elsewhere` itself. It takes a number and an `exclude_pk`, no person, and spends nothing. The wiki's *Writing a plugin* lists it as "Writing one back, leaving the primary flag and the uniqueness rules to Postulo". - postulo-dav, `src/postulo_dav/sync.py:158-162` (`apply_card`): asks it for every card it pulls, and leaves a taken number off the contact without a note. - For comparison, `src/postulo/resume/candidate.py:840-866` (`_somebody_elses`) spends the allowance and, once it is spent, refuses every further number in the same words, "because telling them apart is the answer". - **What happens:** a person who syncs an address book they control puts a card per number in it. After one run, each contact's page shows whether its number was kept. A number left off is one some other row on the instance holds, another account's own number included. A run asks about as many numbers as the book holds, and a sync runs as often as every fifteen minutes, so the 20-an-hour limit does not apply at all. - **Who can trigger it:** any account that may use a sync plugin on an instance where one is installed. It reveals that a number is held here, not by whom, which is the disclosure #142 set out to limit. - **Not affected:** `core/importer.py:277` asks the same bare question, but only an operator runs `manage.py import_data`. - **Fix:** the surface stops handing out the bare question. 1. `phone_number_is_taken` takes the person asking, spends their allowance when the answer is yes, and once it is spent answers every number alike, as `_somebody_elses` does. Or it leaves the surface, and `save_phone_number` reports a refusal itself, which is what postulo/postulo#328 proposes for the row services ("hearing it spends the #142 allowance as a form does"). 2. Either way it is a change to a name the surface promises, so it is a *Deprecated* entry first and a *Removed* one in a later release, as the surface's own promise says (`plugins/api.py`), with its line in *Writing a plugin* (`tests/test_wiki_surface.py`) and a test in `tests/test_plugin_surface.py`. postulo-dav then follows, with a note in the run's report when a number was left off. 3. If postulo/postulo#329 limits instance-wide uniqueness to a person's own numbers, a contact's number no longer collides with anything and this question disappears for contacts. The two should be decided together. Related: #142 and #90 (closed), which set the rule and its limit. ## Done when - A test syncs more cards with numbers held elsewhere than the allowance covers, and shows that the answers stop telling taken from free once it is spent. - No name on `postulo.plugins.api` answers whether a number is held on the instance without naming who asks. - postulo-dav's report says when a card's number was left off. Found while filing the extension and DAV plugin improvements of 2026-09-30.
tiagoagueda added this to the 0.5.0 milestone 2026-10-01 06:31:28 +00:00
Author
Owner

Core side is finished (the allowance, taken_by_asking, the deprecation, the surface test and the wiki rows). What keeps this open is the plugin's half, which is in the other repo: postulo/postulo-dav#26 now carries the exact request (pass asked_by=<account being synced>, and say in the run report which cards had their number left off). The bare form stays, deprecated, until a release carries the Deprecated line; its removal is scheduled for the release after. Close this when postulo-dav#26 lands.

Core side is finished (the allowance, `taken_by_asking`, the deprecation, the surface test and the wiki rows). What keeps this open is the plugin's half, which is in the other repo: postulo/postulo-dav#26 now carries the exact request (pass `asked_by=<account being synced>`, and say in the run report which cards had their number left off). The bare form stays, deprecated, until a release carries the Deprecated line; its removal is scheduled for the release after. Close this when postulo-dav#26 lands.
Author
Owner

Fixed on main in 894bbcf2b: a plugin's question about held telephone numbers is now charged to the account asking (phone_number_is_taken takes the account, and asking without it is deprecated). Its CHANGELOG entry ends in (#333).

Fixed on main in 894bbcf2b: a plugin's question about held telephone numbers is now charged to the account asking (phone_number_is_taken takes the account, and asking without it is deprecated). Its CHANGELOG entry ends in (#333).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#333
No description provided.