Skip to content

fix(core/api): gate user + entity mutations#82

Merged
BK1031 merged 1 commit into
mainfrom
bk1031/harden-user-entity-gates
Jun 17, 2026
Merged

fix(core/api): gate user + entity mutations#82
BK1031 merged 1 commit into
mainfrom
bk1031/harden-user-entity-gates

Conversation

@BK1031

@BK1031 BK1031 commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Stacked on top of #81 — base is `bk1031/harden-group-gates`. The diff shown here is just this PR's gates.

Audit holes covered

CRITICAL:

  • `POST /core/entity/:entityID/email-auth` — upsert email + password on any account (account-takeover primitive)

HIGH:

  • `POST /core/entity` (CreateEntity) — fabricate identities
  • `POST /users` (CreateOrUpdateUser) — fabricate users / hijack profiles
  • `DELETE /users/:id` (DeleteUser) — delete any user

MEDIUM:

  • `POST /core/entity/logins` (CreateEntityLogin) — backdate audit log
  • `POST /core/entity/:entityID/phone-auth` — link phone to any account
  • `POST /core/entity/:entityID/external-auth` — claim any account via DISCORD/GITHUB
  • `PATCH /core/entity/:entityID/external-auth/:provider` — overwrite provider metadata

Gate model

All entity-mutation endpoints take `sentinel:all` only — they're internal primitives. The discord onboarding service and oauth login flow both carry sentinel:all on their SAs after PR #79, so existing flows are unbroken.

CreateOrUpdateUser splits create vs. update:

  • Create → admin or sentinel:all
  • Update → self (`sub` or `entity_id` matches) OR user:write scope OR admin OR sentinel:all

DeleteUser → admin or sentinel:all (no self-delete path here).

Test plan

  • Existing discord onboarding still works (creates entity, email-auth, phone-auth, external-auth)
  • Existing Discord login still refreshes external-auth metadata
  • Unauthed POST to any of these endpoints → 401
  • Authed-but-not-admin user editing their own profile via POST /users → 200
  • Same user editing someone else's profile → 401/403

Audit pass continues. This PR covers the user + onboarding endpoints:
every entity-fabrication and account-takeover primitive in core.

user.go
  - CreateOrUpdateUser: create requires admin or sentinel:all (no
    arbitrary user fabrication); update accepts the user themselves
    via sub/user_id or the user:write scope (third-party app proxy),
    plus the admin/internal overrides.
  - DeleteUser: admin or sentinel:all only. No self-delete path
    through this endpoint — account closure would go through a
    different flow with proper cleanup.

onboarding.go (all sentinel:all only)
  - CreateEntity: root identity creation, called by discord
    onboarding via its SA bearer.
  - CreateEntityEmailAuth: upserts email + password in one call —
    the entire account-takeover primitive. Discord onboarding is the
    only legitimate caller today; future password-reset flows would
    layer their own token-mediated check before reaching here.
  - CreateEntityPhoneAuth / CreateEntityExternalAuth: same trust
    level — anyone able to write external_id rows can claim any
    entity.
  - UpdateEntityExternalAuthMetadata: refreshes cached provider
    profile data; called by the oauth-discord-login flow.

entity.go
  - CreateEntityLogin: audit log of token issuance. Reserved for the
    oauth service — admins/users shouldn't be backdating their own
    entries.

Internal callers (discord onboarding, oauth login, etc.) already
carry sentinel:all after PR #79, so existing flows keep working.
Anything outside Sentinel that was hitting these endpoints
unauthenticated will now get a 403.
Base automatically changed from bk1031/harden-group-gates to main June 17, 2026 08:30
@BK1031 BK1031 merged commit 0ba1eb7 into main Jun 17, 2026
15 checks passed
@BK1031 BK1031 deleted the bk1031/harden-user-entity-gates branch June 17, 2026 08:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant