Skip to content

[People · Phase 1] Let People manage bound roles, without preliminary people (rework of #73) #78

Description

@toto04

Context

#73 has the right core for Phase 1:

  • the dashboard binds a role to a People meaning (role.people_binding);
  • the backend reads who holds bound roles;
  • the backend applies People role commands, and Auth checks every assignment against the actor's own access.

After #73 was approved, #75 was merged into #73's branch (not into main). It added:

  • preliminary people, stored as user rows with no credentials (migration 0024_people_registration);
  • link proposals based on email, Telegram and name;
  • Master Admin approval to link a preliminary person to an account, and to undo the link.

We're dropping #75. People who haven't linked an account live only in the backend. Why:

  • We often don't know how someone will sign in: with Entra ID if they're a socio, otherwise with any Google account. The email and name we have may not match that account, so matching identities inside the IdP isn't reliable.
  • Placeholder users have to be excluded everywhere else in the IdP (directory, signing picker, deletion, counts), and linking or unlinking has to move roles between subjects. That's a lot of security-sensitive IdP code for no benefit.
  • It broke both ways of linking. In feat(people)!: manage Auth-backed people before and after their first login backend#99, invite acceptance and manual linking both send link-person with the invitee or the board member as the actor. Auth refuses unless the actor is Master Admin (src/auth/people-registration.ts:106 on the branch), so both paths fail with 403.

The roadmap is PoliNetworkOrg/admin#146; Phase 1 is PoliNetworkOrg/admin#143.

Work

  1. Reset feat/people-role-api to d698d3c. This drops b57a858 (feat(people): register people before login and expose role assignment dates #75) and the four commits after it: 1d08a15, ce9dbc3, f48b750, b4360ef.
  2. Remove Soci from GET /api/internal/people-roles. Return only the bound roles and the people who hold them: name, directory email, Telegram ID and username.
    • Remove the list of "every PoliNetwork Microsoft 365 account, bound role or not" and the azureId field. Soci isn't People's concern; Phase 2 will read Entra ID directly.
    • Optional: bring back the earliest assignment date per held bound role (since, from feat(people): register people before login and expose role assignment dates #75) if the dashboard shows "admin since". Re-implement it on its own.
  3. Add a capo binding kind for Capo Admins. It needs the pattern, the role_people_binding_check constraint, parsePeopleBinding/describePeopleBinding support and the command planner.
    • Appointing or ending a Capo Admin needs admin:access + people:appoint, like a Lead.
    • capo-admin inherits admin, so the existing inherited-binding check will also require people:write. That's intended.
  4. Turn the Master Admin check on binding into a permission. Today only Master Admin can bind a role (people-binding.ts, the People binding card and the MCP tool).
    • Add a managed permission, e.g. idp:people:bind (implies idp:roles:read), so the capability can be delegated like every other IdP admin right.
    • Master Admin keeps it implicitly.
    • The Master Admin exception to bounded delegation in peopleRoleChangeProblem can stay, since Master Admin holds every permission anyway.
  5. Make the delegation rules easy to extend. Today they're an inline ternary in peopleRoleChangeProblem. Restructure them as one rule per binding kind, saying who may assign and unassign admin, capo, team:*:member and team:*:lead.
    • The goal: "a Capo Admin may add or remove the admins under them" should later be a small, local change. We are not building it now.
    • Such a rule needs a scope Auth can verify (which admins are under which Capo). Don't design that now; just don't close the door. For example, keep the command format open to an optional context field.
  6. MCP: set_role_people_binding stays. Gate it with the new permission instead of Master Admin, and support capo.
  7. README: update the deployment steps to match [People · Phase 1] Configure People role bindings in the production IdP #79.

Contract the backend relies on (unchanged from #73)

  • POST /api/internal/people-roles, internal scope idp:people:roles.
    • Body: { id, actor, sub, reason, changes }.
    • Idempotent by id, and applied all-or-nothing.
    • 409 when the mapping is ambiguous, or when the requested state would still be held through another role.
  • Auth works out the actor's access itself. It refuses:
    • unbound roles;
    • giving yourself a role;
    • missing people:write / people:appoint / Lead of the team;
    • a role granting permissions the actor doesn't hold (bounded delegation).
  • sub is always a real login account. There are no preliminary subjects.
  • When an invite is accepted, the backend sends the person who made the assignment as the actor, not the invitee, because Auth refuses self-grants. Auth needs no special case for this, but the tests should cover it. See [People · Phase 1] Link records to accounts by email invite or manually backend#102.

Notes

  • trusted-admin inherits admin. People assigns admin and capo; trusted-admin stays hand-assigned in Auth for now. Don't also bind it as admin, or the planner answers 409 (ambiguous).
  • Roles that carry any idp:* permission can't be bound. In production that rules out it-core and it-lead. See [People · Phase 1] Configure People role bindings in the production IdP #79 for what to do about the IT team.

Done when

Activity

  1. added theissue type on Oct 29, 2026
  2. added
    peoplePeople: Group Admins, Capo Admins, Teams, Soci and census (see the People roadmap)
    on Oct 29, 2026
  3. coderabbitai commented on Oct 29, 2026

    @coderabbitai
    Contributor
    🔗 Related PRs

    #73 - feat(rbac): let People manage bound roles through the backend [open]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    peoplePeople: Group Admins, Capo Admins, Teams, Soci and census (see the People roadmap)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions