Skip to content

fix(auth): propagate per-group fetch errors in GetUserPermissions - #920

Merged
cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/auth-permission-error-propagation
Jun 5, 2026
Merged

cristim merged 2 commits into
feat/multicloud-web-frontendfrom
fix/auth-permission-error-propagation

Conversation

@cristim

@cristim cristim commented Jun 2, 2026 •

Copy link
Copy Markdown
Member

Summary

  • GetUserPermissions and collectGroupsAndAccounts in internal/auth/service_group.go were swallowing per-group store errors: on a transient DB error fetching one group they logged a warning and continued, then returned the permission union of the remaining groups.
  • This silently under-grants permissions and masks a DB outage as a permission denial (callers cannot distinguish "user genuinely lacks permission" from "lookup degraded").
  • Fix: propagate any non-nil error from GetGroup immediately so callers fail closed with an error. A nil group (store returns nil, nil for a deleted group) is still skipped without error.
  • Update collectGroupsAndAccounts to return error; BuildAuthContext now checks and propagates it.
  • Remove the now-unused logging import.
  • Update the existing test that asserted the old swallow-and-continue behavior; add two new regression tests (one for GetUserPermissions, one for BuildAuthContext) asserting that a per-group fetch error propagates.

closes #918

Test plan

  • go test ./internal/auth/... ./internal/api/... - 1895 tests pass
  • gofmt -l clean on touched files
  • go vet ./internal/auth/... ./internal/api/... clean
  • New regression tests: TestService_GetUserPermissions/propagates_per-group_fetch_error_instead_of_returning_partial_permissions and TestService_BuildAuthContext/propagates_per-group_fetch_error_instead_of_returning_partial_context
  • Existing test TestService_ErrorPaths/GetUserPermissions_with_store_error_on_group updated to match new correct behavior

Summary by CodeRabbit

  • Bug Fixes

    • Improved authorization system reliability: permission and group lookups now properly report errors on transient failures instead of silently skipping groups. This ensures the system fails safely and completely rather than returning incomplete permission data.
  • Tests

    • Added regression tests to verify error propagation in permission and authorization context retrieval.

Any transient store error fetching a group in GetUserPermissions or
collectGroupsAndAccounts is now returned immediately rather than logged
and skipped. Callers therefore fail closed with an error instead of
silently receiving a partial permission union computed from the
remaining groups (closes #918).

A deleted/missing group (store returns nil, nil) is still skipped
without error, preserving the existing behavior for that case.

Remove unused logging import; update existing test that asserted the
old swallow-and-continue behavior.
@cristim cristim added triaged Item has been triaged priority/p3 Polish / idea / may never ship severity/medium Moderate harm urgency/this-sprint Within the current sprint impact/all-users Affects every user effort/s Hours type/bug Defect labels Jun 2, 2026
@coderabbitai

coderabbitai Bot commented Jun 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cristim, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 51 minutes and 27 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b8969e5b-86d0-42e3-b771-4c282efd5039

📥 Commits

Reviewing files that changed from the base of the PR and between db6a989 and 99a83d7.

📒 Files selected for processing (2)
  • internal/auth/service_group.go
  • internal/auth/service_group_test.go
📝 Walkthrough

Walkthrough

This PR hardens the authorization service to fail-closed with explicit errors when group fetches fail. GetUserPermissions and BuildAuthContext now propagate transient store errors immediately instead of logging warnings and continuing with partial permission unions, ensuring callers detect failures and avoid silent under-granting.

Changes

Fail-Closed Error Propagation for Group Lookups

Layer / File(s) Summary
Error propagation in permission/group resolution
internal/auth/service_group.go
Removed the logging dependency used by warn-and-continue patterns. Added documentation clarifying fail-closed behavior. GetUserPermissions now returns an error wrapping the group ID when a group lookup fails. BuildAuthContext and collectGroupsAndAccounts refactored to return errors and stop on group fetch failures, while still skipping nil (deleted/missing) groups.
Test coverage for error propagation
internal/auth/service_group_test.go, internal/auth/service_test.go
Added regression test in TestService_GetUserPermissions verifying error return and nil permissions on group fetch failure. Added analogous regression test in TestService_BuildAuthContext. Updated existing TestService_ErrorPaths subtest to assert error and nil permissions when per-group fetch fails.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 Where once a warning whispered soft and low,
Now errors shine when stores fail to show,
No partial truths, no silent sleight—
Just fail-closed safety, firm and tight! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: propagating per-group fetch errors in GetUserPermissions, which is the primary objective of the PR.
Linked Issues check ✅ Passed All coding objectives from #918 are met: errors from per-group GetGroup calls are propagated instead of swallowed; nil groups are still skipped; new regression tests verify error propagation.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the issue: modifying GetUserPermissions and BuildAuthContext error handling, removing unused logging import, and adding regression tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/auth-permission-error-propagation

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

@cristim

cristim commented Jun 2, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/auth/service_group_test.go`:
- Around line 409-435: Add sibling test cases next to the existing "propagates
per-group fetch error..." (for GetUserPermissions) and the BuildAuthContext
test: mock Store.GetGroup to return (nil, pgx.ErrNoRows) for the missing group
ID instead of assert.AnError; then assert the call does not return an error and
that the missing group is simply skipped (i.e. permissions/auth context reflect
only the present groups and no partial/error result is produced). Use the same
test setup symbols (MockStore, GetUserByID, GetGroup,
service.GetUserPermissions, and service.BuildAuthContext) and mirror the
existing assertions but expect no error and the returned permissions/auth
context exclude the pgx.ErrNoRows group.

In `@internal/auth/service_group.go`:
- Around line 73-79: collectGroupsAndAccounts is treating any non-nil error from
s.store.GetGroup as fatal even though PostgresStore.scanGroup may return (nil,
pgx.ErrNoRows) for deleted groups; update collectGroupsAndAccounts in
internal/auth/service_group.go to treat pgx.ErrNoRows as a non-fatal "skip" by
checking errors.Is(err, pgx.ErrNoRows) and continue, mirroring the
comment/behavior in Service.GetUserPermissions and existing tests, so only other
errors are returned.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 71e133e1-beee-49f6-8d1b-61ccffb24cba

📥 Commits

Reviewing files that changed from the base of the PR and between 1dbeef7 and db6a989.

📒 Files selected for processing (3)
  • internal/auth/service_group.go
  • internal/auth/service_group_test.go
  • internal/auth/service_test.go

Comment thread internal/auth/service_group_test.go
Comment thread internal/auth/service_group.go
…tGroupsAndAccounts

PostgresStore.GetGroup returns (nil, pgx.ErrNoRows) when a group row no
longer exists. Without an explicit check, that error was treated as a
transient failure and propagated, causing the entire permission lookup to
fail for users whose group list includes a deleted group.

Add errors.Is(err, pgx.ErrNoRows) guards in both GetUserPermissions and
collectGroupsAndAccounts so a deleted group is skipped (same as the
existing nil-group path), while any other store error still propagates.

Add pgx.ErrNoRows test cases for both functions to cover the deleted-group
skip path explicitly (CR #920 minor finding).
@cristim

cristim commented Jun 2, 2026

Copy link
Copy Markdown
Member Author

Addressed both CR findings (commit 99a83d7):

MAJOR (service_group.go): Added errors.Is(err, pgx.ErrNoRows) guards in both GetUserPermissions and collectGroupsAndAccounts. PostgresStore.GetGroup returns (nil, pgx.ErrNoRows) for a deleted/missing group; without this check that error propagated fatally. Now the not-found path is treated identically to (nil, nil) (skip the group), while all other errors still propagate.

MINOR (service_group_test.go): Added two sibling test cases covering the pgx.ErrNoRows skip path:

  • TestService_GetUserPermissions/skips deleted group (pgx.ErrNoRows) without error
  • TestService_BuildAuthContext/skips deleted group (pgx.ErrNoRows) without error

Both assert no error is returned and the result reflects only the groups that resolved successfully.

All 1897 tests pass; gofmt and go vet clean.

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the latest PR diff
gh pr view 920 --repo LeanerCloud/CUDly --json files,headRefName,baseRefName 2>/dev/null || echo "Not accessible"

# Get the PR diff
gh pr diff 920 --repo LeanerCloud/CUDly 2>/dev/null | head -300

Length of output: 10506


🏁 Script executed:

#!/bin/bash
# Check the current state of service_group.go
cat internal/auth/service_group.go 2>/dev/null || echo "File not found"

Length of output: 7932


🏁 Script executed:

#!/bin/bash
# Check service_group_test.go for new test cases
cat internal/auth/service_group_test.go 2>/dev/null || echo "File not found"

# Check if there's a store abstraction wrapping pgx errors
rg -n "ErrNoRows\|pgx" internal/auth/ --type go -C2 2>/dev/null

Length of output: 42091


🏁 Script executed:

#!/bin/bash
# Check how PostgresStore.GetGroup returns errors - look for store interface/implementation
rg -rn "GetGroup\|ErrNoRows\|ErrNotFound" --type go -C3 internal/ 2>/dev/null | head -100

Length of output: 436


🏁 Script executed:

#!/bin/bash
# Find the store implementation to check GetGroup error behavior
fd -e go . internal/ | xargs grep -l "GetGroup\|ErrNoRows" 2>/dev/null

# Also check for any store interface or postgres store files
fd -e go . --search-path internal/ 2>/dev/null | head -50

Length of output: 3521


🏁 Script executed:

#!/bin/bash
# Search more broadly for ErrNoRows and pgx usage across the codebase
rg -rn "ErrNoRows" --type go -C3 2>/dev/null | head -80

# Also look for postgres store GetGroup implementation
rg -rn "func.*GetGroup" --type go -C5 2>/dev/null | head -60

Length of output: 7909


🏁 Script executed:

#!/bin/bash
# Look at the postgres store GetGroup implementation
cat internal/auth/store_postgres.go | grep -A 30 "func.*GetGroup"

Length of output: 974


🏁 Script executed:

#!/bin/bash
# Check for any store-level ErrNoRows handling in auth package
rg -n "ErrNoRows\|pgx\.Err" internal/auth/store_postgres.go -C3 2>/dev/null

# Also check the store interface
cat internal/auth/interfaces.go | grep -A 5 "GetGroup"

Length of output: 633


🏁 Script executed:

#!/bin/bash
# Find the scanGroup function to understand how ErrNoRows is handled
rg -n "func.*scanGroup\|ErrNoRows\|pgx\." internal/auth/store_postgres.go -C4 2>/dev/null

Length of output: 413


🏁 Script executed:

#!/bin/bash
# Get the full store_postgres.go to find scanGroup
grep -n "scanGroup\|ErrNoRows\|pgx\." internal/auth/store_postgres.go | head -40

Length of output: 887


🏁 Script executed:

#!/bin/bash
# Get the scanGroup function implementation
awk '/func.*scanGroup/,/^func/' internal/auth/store_postgres.go | head -30

Length of output: 244


🏁 Script executed:

#!/bin/bash
# Get full scanGroup implementation
sed -n '916,960p' internal/auth/store_postgres.go

Length of output: 1254



@cristim The updated fix looks correct and complete. Here's a summary of what I verified:

scanGroup confirms the sentinel — PostgresStore.scanGroup (line 935-936 of store_postgres.go) explicitly returns (nil, pgx.ErrNoRows) for a missing row and never returns (nil, nil), so the errors.Is(err, pgx.ErrNoRows) guard is exactly the right check for the deleted-group path.

Dual guard is sound — keeping both the pgx.ErrNoRows skip and the nil group check is correct: the former handles the real store; the latter is cheap defensive coverage for any mock implementation that returns (nil, nil).

pgx.ErrNoRows is idiomatic here — the sentinel is used consistently throughout store_postgres.go (lines 621, 878, 935, 977) and in credentials/store.go and config/store_postgres.go, so there's no abstraction-layer concern with referencing it directly in the service layer.

Tests are thorough — the four added cases (two propagation, two ErrNoRows-skip) cover the critical paths with clear regression annotations pointing back to #918.

LGTM. Triggering a full review of the latest commit.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim

cristim commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 4, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cristim
cristim merged commit 69b857a into feat/multicloud-web-frontend Jun 5, 2026
4 checks passed
@cristim
cristim deleted the fix/auth-permission-error-propagation branch July 27, 2026 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

effort/s Hours impact/all-users Affects every user priority/p3 Polish / idea / may never ship severity/medium Moderate harm triaged Item has been triaged type/bug Defect urgency/this-sprint Within the current sprint

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant