Sharing and groups: lookup throttle, reauthentication, member visibility, races (SEC2 B) #998

Merged
tiagoagueda merged 1 commit from sec2-b-sharing-groups into main 2026-10-17 22:50:21 +00:00
Owner

Closes #943, Closes #944, Closes #945, Closes #946, Closes #947, Closes #948, Closes #989

  • #943: per-account throttle POSTULO_SHARE_LOOKUP_RATE (20/h; not spent for group: or empty). Email is kept; a hit shows the address as typed. To keep the grants list from revealing the username too, the typed wording is stored on the grant (Share.addressed_as, migration 0040) and shown instead. Miss wording is identical for unknown, deactivated and own address.
  • #944: reauthentication_required on PersonActiveView.post and PersonUsernameView.post (GET still opens the form).
  • #945: groups page change needs a recent sign-in, is logged with added/removed usernames; Your groups lists members.
  • #946: ownership.move passes also_share; MoveView also catches ShareRefused.
  • #947: giving to a deactivated username refused; plugin rules and group additions refuse a deactivated account; existing deactivated members stay and are marked; deactivation message says memberships and shares are kept.
  • #948: get_or_create in _grant, savepoint around group creation, non-numeric group id is a 404.
  • #989: group row locked (select_for_update, inside an atomic block) before the last-member check. A concurrent PostgreSQL test is not included; a test asserts the lock is taken.

Deviations: the addressed_as column (a small migration) was needed for the grants list; the PostgreSQL two-connection test for #989 is not written.

Tests: new tests in test_share_flow, test_groups_lifecycle, test_hardening_740, security/test_rate_limits; full unit suite passes (translation tests need compiled catalogues). Templates changed (share, groups), not JS; the browser job was not run locally.

🤖 Generated with Claude Code

Closes #943, Closes #944, Closes #945, Closes #946, Closes #947, Closes #948, Closes #989 - #943: per-account throttle POSTULO_SHARE_LOOKUP_RATE (20/h; not spent for `group:` or empty). Email is kept; a hit shows the address as typed. To keep the grants list from revealing the username too, the typed wording is stored on the grant (`Share.addressed_as`, migration 0040) and shown instead. Miss wording is identical for unknown, deactivated and own address. - #944: `reauthentication_required` on `PersonActiveView.post` and `PersonUsernameView.post` (GET still opens the form). - #945: groups page change needs a recent sign-in, is logged with added/removed usernames; Your groups lists members. - #946: `ownership.move` passes `also_share`; `MoveView` also catches `ShareRefused`. - #947: giving to a deactivated username refused; plugin rules and group additions refuse a deactivated account; existing deactivated members stay and are marked; deactivation message says memberships and shares are kept. - #948: `get_or_create` in `_grant`, savepoint around group creation, non-numeric group id is a 404. - #989: group row locked (`select_for_update`, inside an atomic block) before the last-member check. A concurrent PostgreSQL test is not included; a test asserts the lock is taken. Deviations: the `addressed_as` column (a small migration) was needed for the grants list; the PostgreSQL two-connection test for #989 is not written. Tests: new tests in test_share_flow, test_groups_lifecycle, test_hardening_740, security/test_rate_limits; full unit suite passes (translation tests need compiled catalogues). Templates changed (share, groups), not JS; the browser job was not run locally. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Close the sharing and groups findings of the second security review
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 13m2s
CI / Browser tests (Chromium) (pull_request) Successful in 29m5s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 4m37s
CI / Security audit (pull_request) Successful in 1m50s
CI / Database tests on PostgreSQL (pull_request) Successful in 9m12s
ae955ebdf8
The share form's lookup of a person by username or address now spends a per-account
allowance (POSTULO_SHARE_LOOKUP_RATE, 20/h; a group choice or an empty value is free), and a
hit names the address as typed: the sharer's chosen wording is kept on the grant
(Share.addressed_as) and shown in the grants list, so an address never reveals the username.
Deactivating an account and changing a username under People, and every change on the groups
page, ask for a recent sign-in. A membership change is logged with the usernames added and
removed, and Your groups lists the members. Giving a record whose company has a parent to a
group passes also_share through and catches a refusal. A record cannot be given to, a plugin
rule made for, or a group extended with a deactivated account (those who are members stay and
are marked). Grants use get_or_create, group creation a savepoint, a non-numeric group id is
a 404, and removing members locks the group row before the last-member check.

Closes #943
Closes #944
Closes #945
Closes #946
Closes #947
Closes #948
Closes #989

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
tiagoagueda force-pushed sec2-b-sharing-groups from ae955ebdf8
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 13m2s
CI / Browser tests (Chromium) (pull_request) Successful in 29m5s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 4m37s
CI / Security audit (pull_request) Successful in 1m50s
CI / Database tests on PostgreSQL (pull_request) Successful in 9m12s
to c81f3af99e
All checks were successful
CI / Unit tests and coverage (Python 3.14) (pull_request) Successful in 12m4s
CI / Checks: lint, migrations, catalogues, settings, built files (pull_request) Successful in 2m19s
CI / Database tests on PostgreSQL (pull_request) Successful in 4m14s
CI / Security audit (pull_request) Successful in 1m57s
CI / Browser tests (Chromium) (pull_request) Successful in 27m17s
2026-10-17 22:22:00 +00:00
Compare
Sign in to join this conversation.
No reviewers
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!998
No description provided.