feat(web): add audit log entries for org membership changes#1165
feat(web): add audit log entries for org membership changes#1165brendan-kellam wants to merge 4 commits intomainfrom
Conversation
Adds three new audit actions covering the full membership lifecycle: - org.member_added — fires from all five UserToOrg-creation paths (initial owner, auto-add on signup, magic invite-link join, email invite redemption, join-request approval). Two of those paths were previously silent. - org.member_removed — fires when an admin removes a member via the Settings UI. - org.member_left — fires when a user leaves the org themselves. Each event uses a consistent (actor=user, target=user) shape so the membership history can be reconstructed with a single query per state transition. Existing audits (user.invite_accepted, user.join_request_approved, user.owner_created) are preserved as semantic detail.
This comment has been minimized.
This comment has been minimized.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds three organization membership audit events ( Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant ServerAction as Server Action Handler
participant AuditSvc as AuditService
participant DB as Database
participant Email as Email/SMTP
Client->>ServerAction: trigger approveAccountRequest / join / redeem / leave / remove
ServerAction->>DB: perform membership change transaction
DB-->>ServerAction: transaction result
ServerAction->>AuditSvc: createAudit(action: org.member_*)
AuditSvc-->>DB: persist audit record
AuditSvc-->>ServerAction: confirmation
ServerAction->>Email: send notification (if applicable)
Email-->>ServerAction: send result / errors
ServerAction-->>Client: response
Possibly related PRs
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
docs/docs/configuration/audit-logs.mdx (1)
148-150: ⚡ Quick winClarify actor/target semantics for these new membership events.
The
user/userrows are accurate on types, butorg.member_addedandorg.member_removeddo not always use the same user ID on both sides. In the approval/removal flows the actor is the admin and the target is the affected member, so a short note here would prevent readers from inferringactorId === targetIdfor every one of these events.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/docs/configuration/audit-logs.mdx` around lines 148 - 150, Update the docs for the membership events (`org.member_added`, `org.member_removed`, `org.member_left`) to clarify actor vs target semantics: explicitly state that for `org.member_added` and `org.member_removed` the actor is typically the admin performing the approval/removal and the target is the affected member (so actorId != targetId), whereas for `org.member_left` the actor and target are the same user (self-initiated, actorId === targetId); add a short footnote or parenthetical note next to those table rows so readers don’t assume actorId === targetId for all three events.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/web/src/actions.ts`:
- Around line 1051-1059: The approval audit writes (calls to
auditService.createAudit for "user.join_request_approved" and
"org.member_added") must be performed before the email side-effect (the call(s)
to render() and sendMail()), so move the auditService.createAudit calls to occur
immediately after addUserToOrganization() (and before render/sendMail), or
alternatively wrap render/sendMail in a try/catch that logs/suppresses email
errors without reverting the endpoint error response; update the code paths
using requestId, request.requestedById, user.id, org.id and ensure the
user.join_request_approved and org.member_added audits are created prior to any
potential throw from render()/sendMail().
---
Nitpick comments:
In `@docs/docs/configuration/audit-logs.mdx`:
- Around line 148-150: Update the docs for the membership events
(`org.member_added`, `org.member_removed`, `org.member_left`) to clarify actor
vs target semantics: explicitly state that for `org.member_added` and
`org.member_removed` the actor is typically the admin performing the
approval/removal and the target is the affected member (so actorId != targetId),
whereas for `org.member_left` the actor and target are the same user
(self-initiated, actorId === targetId); add a short footnote or parenthetical
note next to those table rows so readers don’t assume actorId === targetId for
all three events.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b4ac4407-4234-4546-aac8-a8e03407825d
📒 Files selected for processing (6)
CHANGELOG.mddocs/docs/configuration/audit-logs.mdxpackages/web/src/actions.tspackages/web/src/app/invite/actions.tspackages/web/src/features/userManagement/actions.tspackages/web/src/lib/authUtils.ts
Move user.join_request_approved and org.member_added audit writes to occur immediately after addUserToOrganization() and before the email send. This ensures the audit trail is complete even if render() or sendMail() throws. Wrapped the email block in try/catch so email failures are logged without propagating as errors. Co-authored-by: Brendan Kellam <brendan-kellam@users.noreply.github.com>
Summary
Adds three new audit actions so the membership lifecycle is fully captured in the audit log:
org.member_added— fires from all fiveUserToOrg-creation paths:onCreateUser, alongside the existinguser.owner_created)memberApprovalRequired = false(previously silent)joinOrganization(previously silent)user.invite_accepted)user.join_request_approved)org.member_removed— fires when an admin removes a member via Settings → Members.org.member_left— fires when a user leaves the org themselves.All three use a consistent
(actor=user, target=user)shape so the full membership history can be reconstructed with one filter per state transition (org.member_added/org.member_removed/org.member_left). The existing per-path audits (user.invite_accepted,user.join_request_approved,user.owner_created) are preserved as semantic detail — they describe how a user joined, whileorg.member_addedrecords that they joined.The
metadata.messageon each event records the specific path (initial owner, no-approval signup, invite link, invite ID, approving admin) so detail isn't lost.createGuestUseris intentionally not audited — it upserts the singleton system Guest user, not a real membership grant.Test plan
user.owner_created+org.member_added(target = self).memberApprovalRequired = false, sign up a second user via SSO → expectorg.member_added(target = self).org.member_added(target = self).user.invite_accepted+org.member_added.user.join_request_approved+org.member_added(actor = admin, target = approved user).org.member_removed.org.member_left.org.member_removeddoes not fire if the removal transaction fails (e.g. last-owner guard).docs/docs/configuration/audit-logs.mdxlists the three new actions.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Chores