Skip to content

feat: add audit logging to SetOrganizationMemberRole RPC#1519

Merged
whoAbhishekSah merged 4 commits intomainfrom
feat/audit-set-org-member-role
Apr 9, 2026
Merged

feat: add audit logging to SetOrganizationMemberRole RPC#1519
whoAbhishekSah merged 4 commits intomainfrom
feat/audit-set-org-member-role

Conversation

@whoAbhishekSah
Copy link
Copy Markdown
Member

@whoAbhishekSah whoAbhishekSah commented Apr 8, 2026

Summary

  • Add structured audit record (organization.role_changed) and event-based audit (app.organization.member.role.set) to SetOrganizationMemberRole
  • Consistent with existing audit logging in AddMember and RemoveUsers org membership operations
  • Audit record captures org (resource), user (target), email, and new role_id in metadata

Test plan

  • Verify audit record is created when changing a member's role
  • Verify event audit log emits app.organization.member.role.set
  • Verify audit includes correct org title, user title, email, and role_id

🤖 Generated with Claude Code

SetOrganizationMemberRole had no audit logging unlike AddMember and
RemoveUsers. This adds both structured audit records and event-based
audit logging consistent with other org membership operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Apr 9, 2026 3:16am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Chores
    • Audit logging for organization and project member role changes now records structured events including organization, member identity, and role metadata; related event names were updated.
  • Tests
    • Unit tests expanded to validate audit-record creation and the updated role-change logging behavior.

Walkthrough

Renamed two audit event constants for member role changes and added audit recording when an organization member's role is set: service now creates an audit record and emits an auditor log after replacing org-level policies; tests and a project handler audit call were updated to use the new event names.

Changes

Cohort / File(s) Summary
Audit core constants
core/audit/audit.go
Renamed event constants: OrgMemberRoleSetEvent -> OrgMemberRoleChangedEvent ("app.organization.member.role_changed"), and ProjectMemberRoleSetEvent -> ProjectMemberRoleChangedEvent ("app.project.member.role_changed"). Alignment/spacing adjusted.
Audit record constants
pkg/auditrecord/consts.go
Added OrganizationMemberRoleChangedEvent ("organization.role_changed") to audit record event constants.
Organization service
core/organization/service.go
SetMemberRole now, after replaceUserOrgPolicies, fetches org and user, creates an audit record (auditRecordRepository.Create) with org/user metadata and role_id, and emits an auditor log; surfaces errors from these operations.
Organization service tests
core/organization/service_test.go
TestService_SetMemberRole setup signature extended to accept *mocks.AuditRecordRepository; success cases expect repeated repo.GetByID/userSvc.GetByID, and assert auditRepo.Create receives pkgAuditRecord.OrganizationMemberRoleChangedEvent and matching record fields.
Project handler
internal/api/v1beta1connect/project.go
Updated ConnectHandler.SetProjectMemberRole audit log to use audit.ProjectMemberRoleChangedEvent instead of the previous ProjectMemberRoleSetEvent, keeping same target and attributes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • AmanGIT07
  • rsbh

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
core/organization/service.go (2)

396-405: Redundant database lookups for organization and user.

Both org and user are already fetched during validation in validateSetMemberRoleRequest (lines 520, 525). Consider refactoring to pass these entities through the call chain to avoid duplicate database queries.

♻️ Suggested approach

Modify SetMemberRole to return the validated entities from validateSetMemberRoleRequest, or restructure so validation results are reusable for audit logging.

-func (s Service) validateSetMemberRoleRequest(ctx context.Context, orgID, userID, newRoleID string) error {
+func (s Service) validateSetMemberRoleRequest(ctx context.Context, orgID, userID, newRoleID string) (Organization, user.User, error) {
 	org, err := s.Get(ctx, orgID)
 	if err != nil {
-		return err
+		return Organization{}, user.User{}, err
 	}
 
-	_, err = s.userService.GetByID(ctx, userID)
+	usr, err := s.userService.GetByID(ctx, userID)
 	if err != nil {
-		return err
+		return Organization{}, user.User{}, err
 	}
 	// ... rest of validation
-	return nil
+	return org, usr, nil
 }

430-433: Consider using LogWithAttrs for richer event logging.

The project handler for SetProjectMemberRole (see internal/api/v1beta1connect/project.go:400-404) uses LogWithAttrs to include metadata like role_id. For consistency, consider doing the same here:

♻️ Add attributes to audit event
-	audit.GetAuditor(ctx, orgID).Log(audit.OrgMemberRoleSetEvent, audit.Target{
+	audit.GetAuditor(ctx, orgID).LogWithAttrs(audit.OrgMemberRoleSetEvent, audit.Target{
 		ID:   userID,
 		Type: schema.UserPrincipal,
+	}, map[string]string{
+		"role_id": newRoleID,
 	})

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 94310a44-78d2-43cb-a439-f539a6c8da85

📥 Commits

Reviewing files that changed from the base of the PR and between 862e133 and 5c0386d.

📒 Files selected for processing (3)
  • core/audit/audit.go
  • core/organization/service.go
  • pkg/auditrecord/consts.go

@whoAbhishekSah
Copy link
Copy Markdown
Member Author

Test Results

Steps

  1. Created a new org "PR 1519 Test Org" as alice
  2. Added bob and charlie to the org using AddOrganizationUsers
  3. Called SetOrganizationMemberRole twice:
    • Set bob's role to Organization Manager (e57e1ba4-21fd-43a4-8aca-aa560afb32cf)
    • Set charlie's role to Organization Viewer (afe94f10-1508-4379-88b1-2c328cb2b769)
  4. Queried ListAuditRecords via AdminService

Results

Two organization.role_changed audit records were created:

{
  "event": "organization.role_changed",
  "actor": {
    "id": "08006078-ed38-4aca-bea3-9c6dff865116",
    "type": "app/user",
    "name": "alice_raystack_org"
  },
  "resource": {
    "id": "a9a62b93-ae6f-4aba-9c32-73281c49da73",
    "type": "organization",
    "name": "PR 1519 Test Org"
  },
  "target": {
    "id": "998d7ebe-cd2c-4f0c-a560-7dd2a7d1a62c",
    "type": "user",
    "metadata": {
      "email": "bob@raystack.org",
      "role_id": "e57e1ba4-21fd-43a4-8aca-aa560afb32cf"
    }
  },
  "org_id": "a9a62b93-ae6f-4aba-9c32-73281c49da73",
  "org_name": "PR 1519 Test Org"
}
{
  "event": "organization.role_changed",
  "actor": {
    "id": "08006078-ed38-4aca-bea3-9c6dff865116",
    "type": "app/user",
    "name": "alice_raystack_org"
  },
  "resource": {
    "id": "a9a62b93-ae6f-4aba-9c32-73281c49da73",
    "type": "organization",
    "name": "PR 1519 Test Org"
  },
  "target": {
    "id": "6a51c542-7ca9-4b23-8709-74145013d919",
    "type": "user",
    "metadata": {
      "email": "charlie@raystack.org",
      "role_id": "afe94f10-1508-4379-88b1-2c328cb2b769"
    }
  },
  "org_id": "a9a62b93-ae6f-4aba-9c32-73281c49da73",
  "org_name": "PR 1519 Test Org"
}

Verified

  • Audit record created with event organization.role_changed
  • Org title captured in resource.name
  • User ID captured in target.id
  • Email captured in target.metadata.email
  • Role ID captured in target.metadata.role_id
  • org_id and org_name populated correctly

Fix gofmt alignment in metadata map literal and update SetMemberRole
test cases to mock the new audit record repository and user/org
GetByID calls added by audit logging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6f85ab42-bcb2-46e7-be3f-f598d3fbd7e3

📥 Commits

Reviewing files that changed from the base of the PR and between 5c0386d and 5b80d52.

📒 Files selected for processing (2)
  • core/organization/service.go
  • core/organization/service_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/organization/service.go

@coveralls
Copy link
Copy Markdown

coveralls commented Apr 8, 2026

Coverage Report for CI Build 24170478234

Coverage increased (+0.04%) to 41.189%

Details

  • Coverage increased (+0.04%) from the base build.
  • Patch coverage: 7 uncovered changes across 2 files (31 of 38 lines covered, 81.58%).
  • No coverage regressions found.

Uncovered Changes

File Changed Covered %
core/organization/service.go 37 31 83.78%
internal/api/v1beta1connect/project.go 1 0 0.0%

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 36335
Covered Lines: 14966
Line Coverage: 41.19%
Coverage Strength: 11.88 hits per line

💛 - Coveralls

Replace mock.Anything with mock.MatchedBy to assert event type,
resource ID, target ID, email, role_id, and org_id in audit records.
Populate user mock with title and email for realistic assertions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/organization/service_test.go (1)

490-536: Consider adding org title/name verification to audit assertions.

The audit assertions comprehensively cover Event, Resource.ID, Target.ID, email, role_id, and OrgID. However, per the PR objectives, the audit record should also capture org_name and org title in resource.name.

The org mock currently only sets ID and State, and the mock.MatchedBy closure doesn't verify ar.OrgName or ar.Resource.Name. Adding these would ensure full coverage of the audit contract.

🔧 Suggested enhancement
-				repo.EXPECT().GetByID(ctx, orgID).Return(organization.Organization{ID: orgID, State: organization.Enabled}, nil).Times(2)
+				repo.EXPECT().GetByID(ctx, orgID).Return(organization.Organization{
+					ID:    orgID,
+					Name:  "test-org",
+					Title: "Test Organization",
+					State: organization.Enabled,
+				}, nil).Times(2)

And in the mock.MatchedBy assertion:

 					return ar.Event == pkgAuditRecord.OrganizationMemberRoleChangedEvent &&
 						ar.Resource.ID == orgID &&
+						ar.Resource.Name == "Test Organization" &&
 						ar.Target.ID == userID &&
 						ar.Target.Metadata["email"] == "test-user@acme.dev" &&
 						ar.Target.Metadata["role_id"] == memberRoleID &&
-						ar.OrgID == orgID
+						ar.OrgID == orgID &&
+						ar.OrgName == "test-org"

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 887b31b6-efec-45e5-835b-ca81d7189c67

📥 Commits

Reviewing files that changed from the base of the PR and between 5b80d52 and 63a4675.

📒 Files selected for processing (1)
  • core/organization/service_test.go

@whoAbhishekSah whoAbhishekSah enabled auto-merge (squash) April 8, 2026 11:27
…harden test assertions

- Use LogWithAttrs with role_id attribute for richer event logging
- Rename event to app.organization.member.role_changed to match
  structured audit record naming convention
- Align project event from app.project.member.role.set to
  app.project.member.role_changed for consistency
- Add org Name/Title to mock and verify Resource.Name in audit assertions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/organization/service.go (1)

397-405: Consider reusing org/user from validation to reduce duplicate fetches.

Both org and user are already fetched in validateSetMemberRoleRequest (lines 522, 527). Returning them from validation or caching in the method could eliminate two extra database calls per role change.

♻️ Potential approach

Modify validateSetMemberRoleRequest to return the fetched entities:

func (s Service) validateSetMemberRoleRequest(ctx context.Context, orgID, userID, newRoleID string) (Organization, user.User, error) {
    org, err := s.Get(ctx, orgID)
    if err != nil {
        return Organization{}, user.User{}, err
    }

    usr, err := s.userService.GetByID(ctx, userID)
    if err != nil {
        return Organization{}, user.User{}, err
    }
    // ... role validation ...
    return org, usr, nil
}

Then use the returned values in SetMemberRole for audit logging.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c342840-67ce-42b1-8c6c-b7909f7769aa

📥 Commits

Reviewing files that changed from the base of the PR and between 63a4675 and 35f5b81.

📒 Files selected for processing (4)
  • core/audit/audit.go
  • core/organization/service.go
  • core/organization/service_test.go
  • internal/api/v1beta1connect/project.go
✅ Files skipped from review due to trivial changes (1)
  • core/organization/service_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/audit/audit.go

@whoAbhishekSah whoAbhishekSah merged commit 1d962ad into main Apr 9, 2026
8 checks passed
@whoAbhishekSah whoAbhishekSah deleted the feat/audit-set-org-member-role branch April 9, 2026 03:21
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.

3 participants