Skip to content

chore(auth): evaluate an audit trail for group permission mutations (event-sourcing proposal from #1737) #170

Description

@cristim

Deferred from PR LeanerCloud/cloud-commitments-cli#1737, where CodeRabbit proposed it. Filing so the idea is tracked rather than dismissed.

The suggestion

Route group mutations through recorded domain events instead of writing the group row directly: introduce an event store, define group events (created / permissions-changed / accounts-changed / deleted), append on each mutation, and rebuild group state from a projection.

Why it was not done in LeanerCloud/cloud-commitments-cli#1737

That PR closes LeanerCloud/cloud-commitments-cli#1550, a p1 privilege escalation. Its whole change is a grant ceiling plus two guards on an existing write path. Event sourcing is an architectural programme, not a fix: it introduces an event store, an append path, a projection, replay and versioning semantics, a migration for existing groups, and a new consistency model for every reader of groups. Landing it inside a security fix would have made the security change unreviewable, and the escalation would have stayed open for the duration.

What it would actually buy

Worth stating honestly, because the case is not zero:

What it would cost

  • Every reader of the groups table becomes a projection consumer, or the projection must be kept synchronously consistent — which removes most of the benefit.
  • Replay/versioning semantics have to be designed and maintained.
  • A migration has to synthesise an initial event per existing group.
  • The seeded, system_managed groups are owned by SQL migrations; those writes bypass the API entirely and would need their own story.

Suggested smaller step first

If the goal is auditability rather than event sourcing as such, a group_permission_audit table written in the same transaction as UpdateGroup — actor, group, before, after, timestamp — delivers most of the value at a fraction of the cost and no change to how groups are read. Worth evaluating that before committing to a projection-based model.

Related: LeanerCloud/cloud-commitments-cli#1629 (the silent-drop failure this would have made recoverable), LeanerCloud/cloud-commitments-cli#1737.

Findings from the 2026-09-02 codebase audit

Added by an automated audit of 3c0f8ac94048a2c36fce5ccddee54e6c4849a5cd (tip of origin/main). Each item below was reported by one reviewer and independently confirmed by a second that did not write it. Full report: docs/audits/codebase-audit-2026-09-02.md.

A03-024 (low)

One premise here needs correcting before the design work starts. The note says groups.created_by exists but lacks a changed_by for later edits; in fact created_by is never populated either. CreateGroupAPI receives the real actorUserID from the handler (handler_groups.go:53) and then passes a literal "" to CreateGroup for every caller (internal/auth/service_api.go:311-313), with a comment explaining that the column is a UUID FK and actorUserID may be the non-UUID admin-API-key sentinel. Only the sentinel case (group_ceiling.go:40) actually cannot be stored, so human admins leave created_by NULL for no reason. The one-line fix is to pass actorUserID when it is not AdminAPIKeyActorID and "" only for the sentinel, which gives the audit trail a real creation record to build on rather than a column that is uniformly empty. Audit finding A03-024.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions