Skip to content

feat: reload OIDC policy at login - #2461

Merged
yohamta0 merged 4 commits into
mainfrom
agent/reload-oidc-policy-at-login
Jul 31, 2026
Merged

yohamta0 merged 4 commits into
mainfrom
agent/reload-oidc-policy-at-login

Conversation

@yohamta0

@yohamta0 yohamta0 commented Jul 30, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Reload the effective OIDC provisioning policy from the configuration files used at startup before each OIDC login.
  • Replace the active policy only after the new candidate is fully loaded and compiled.
  • Keep the users API's managed-role and workspace metadata aligned with the latest accepted policy.

Why

The OIDC role mapper was compiled once during server startup and retained by the provisioning service. Changes to auth.oidc.role_mapping therefore required restarting the server even though the policy is only consumed when an OIDC login is processed.

Loading at the authentication boundary avoids a file watcher, reload endpoint, or separate runtime configuration store. Retaining one last-known-good snapshot also prevents a malformed runtime edit from causing an OIDC login outage.

Behavior

  • auto_signup, allowed_domains, whitelist, and role_mapping are evaluated from current configuration on each login.
  • A valid candidate atomically replaces the active policy for that login and later logins.
  • An invalid or unreadable candidate is rejected and logged; the login continues with the latest valid policy.
  • Invalid startup configuration still prevents the server from starting.
  • Provider settings such as issuer, client credentials, callback URL, and scopes remain startup configuration.
  • Existing sessions continue to use stored authorization; after a successful login synchronizes new access, existing sessions observe it on their next request.

Testing

  • make fmt
  • go test ./internal/cmn/config ./internal/service/oidcprovision ./internal/service/frontend/api/v1 -count=1
  • make test TEST_TARGET='./internal/cmn/config ./internal/service/oidcprovision ./internal/service/frontend/api/v1'

Closes #2457

Summary by CodeRabbit

  • New Features

    • OIDC provisioning policies now reflect the latest configuration files and environment overrides.
    • OIDC login behavior can update dynamically without restarting the service.
    • User listings now report current OIDC workspace and role synchronization settings.
    • Multiple configuration files are tracked and reported.
  • Bug Fixes

    • Failed policy reloads now safely retain the last valid policy.
    • Invalid role mappings are detected and reported with clearer errors.

Copilot AI review requested due to automatic review settings July 30, 2026 14:57

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026 •

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7feb831b-aaa4-4cbd-9a85-bdc2773c26a3

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

OIDC provisioning policies can now be reloaded from merged configuration files during login. The active policy is retained when reloads fail, while server wiring and user-list API responses expose the effective policy and role mappings.

Changes

OIDC policy reload

Layer / File(s) Summary
Configuration policy loading
internal/cmn/config/config.go, internal/cmn/config/loader.go, internal/cmn/config/oidc_policy.go, internal/cmn/config/*_test.go
OIDC provisioning policies are derived from configuration, environment overrides, merged config files, defaults, and validated role/workspace mappings. Used configuration paths are tracked in ConfigFilesUsed.
Runtime provisioning policy
internal/service/oidcprovision/service.go, internal/service/oidcprovision/service_test.go
ProcessLogin reloads the current policy, applies it to access and role synchronization, and preserves the last valid policy after reload errors.
Server policy wiring
internal/service/frontend/server.go
Policy conversion helpers, dynamic loading callbacks, initial policy setup, and current-policy exposure are added to server initialization.
Current policy API reporting
internal/service/frontend/api/v1/api.go, internal/service/frontend/api/v1/users.go, internal/service/frontend/api/v1/users_internal_test.go
User-list OIDC synchronization fields are calculated from the current role mapping, with fallback behavior when loading fails or is unavailable.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LoginClient
  participant ProcessLogin
  participant ConfigPolicyLoader
  participant OIDCProvisioningService
  participant UsersAPI
  LoginClient->>ProcessLogin: submit OIDC claims
  ProcessLogin->>ConfigPolicyLoader: load current provisioning policy
  ConfigPolicyLoader-->>OIDCProvisioningService: return validated policy
  OIDCProvisioningService->>OIDCProvisioningService: apply access and role mapping
  UsersAPI->>OIDCProvisioningService: request current policy
  OIDCProvisioningService-->>UsersAPI: return effective role mapping
  UsersAPI-->>LoginClient: report OIDC synchronization state
Loading

Possibly related PRs

  • dagucloud/dagu#2019: Both modify OIDC login provisioning behavior in internal/service/oidcprovision/service.go.
  • dagucloud/dagu#2395: Adds related workspace grant mapping and OIDC synchronization decision plumbing.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.33% 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
Title check ✅ Passed The title is concise and clearly reflects the main change: reloading OIDC policy at login.
Description check ✅ Passed The description covers summary, motivation, behavior, testing, and linked issue closure, though the Changes and Checklist sections are not filled in.
Linked Issues check ✅ Passed The PR reloads OIDC policy at login so mapping changes apply without restarting, matching the issue’s requirement.
Out of Scope Changes check ✅ Passed The changes stay focused on OIDC policy reloads, API exposure, and supporting tests without clear unrelated additions.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/reload-oidc-policy-at-login

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.

Copilot AI review requested due to automatic review settings July 30, 2026 15:24

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@yohamta0
yohamta0 marked this pull request as ready for review July 30, 2026 15:33

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/service/oidcprovision/service.go`:
- Around line 250-279: Update Service.loadPolicy so s.config.LoadPolicy(ctx) and
newPolicySnapshot(loaded) execute without holding policyMu; acquire the lock
only when returning the current policy after either failure and when swapping
s.policy after successful parsing. Preserve the existing fallback and successful
reload behavior while preventing synchronous reload I/O from blocking concurrent
logins.
🪄 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 Plus

Run ID: 914a0be9-99be-49b4-a66d-7b7663fb7675

📥 Commits

Reviewing files that changed from the base of the PR and between cba1426 and 04e2bcb.

📒 Files selected for processing (11)
  • internal/cmn/config/config.go
  • internal/cmn/config/loader.go
  • internal/cmn/config/loader_test.go
  • internal/cmn/config/oidc_policy.go
  • internal/cmn/config/oidc_policy_test.go
  • internal/service/frontend/api/v1/api.go
  • internal/service/frontend/api/v1/users.go
  • internal/service/frontend/api/v1/users_internal_test.go
  • internal/service/frontend/server.go
  • internal/service/oidcprovision/service.go
  • internal/service/oidcprovision/service_test.go

Comment on lines +250 to +279
func (s *Service) loadPolicy(ctx context.Context) *policySnapshot {
s.policyMu.Lock()
defer s.policyMu.Unlock()

if s.config.LoadPolicy == nil {
return s.policy
}

loaded, err := s.config.LoadPolicy(ctx)
if err != nil {
s.logger.Warn("OIDC authorization policy reload rejected",
slog.String("error", err.Error()))
return s.policy
}
policy, err := newPolicySnapshot(loaded)
if err != nil {
s.logger.Warn("OIDC authorization policy reload rejected",
slog.String("error", err.Error()))
return s.policy
}
s.policy = policy
return policy
}

// CurrentPolicy returns the latest successfully loaded provisioning policy.
func (s *Service) CurrentPolicy() ProvisioningPolicy {
s.policyMu.Lock()
defer s.policyMu.Unlock()
return s.policy.ProvisioningPolicy
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Reload lock is held across synchronous file I/O, serializing all concurrent OIDC logins.

loadPolicy takes policyMu and keeps it locked while calling s.config.LoadPolicy(ctx), which (per the wiring in server.go) performs os.ReadFile + a full Viper re-parse of every tracked config file on every login (see internal/cmn/config/oidc_policy.go's Load()). Since every OIDC login goes through this path, concurrent logins fully serialize on synchronous disk I/O for the duration of the reload, rather than merely contending on an in-memory pointer swap.

Move the load/parse work outside the critical section and only take the lock to read the current policy on failure or to swap the pointer on success.

⚡ Proposed fix: shrink the lock scope around the reload
 func (s *Service) loadPolicy(ctx context.Context) *policySnapshot {
-	s.policyMu.Lock()
-	defer s.policyMu.Unlock()
-
 	if s.config.LoadPolicy == nil {
 		return s.policy
 	}
 
 	loaded, err := s.config.LoadPolicy(ctx)
 	if err != nil {
 		s.logger.Warn("OIDC authorization policy reload rejected",
 			slog.String("error", err.Error()))
+		s.policyMu.Lock()
+		defer s.policyMu.Unlock()
 		return s.policy
 	}
 	policy, err := newPolicySnapshot(loaded)
 	if err != nil {
 		s.logger.Warn("OIDC authorization policy reload rejected",
 			slog.String("error", err.Error()))
+		s.policyMu.Lock()
+		defer s.policyMu.Unlock()
 		return s.policy
 	}
+
+	s.policyMu.Lock()
+	defer s.policyMu.Unlock()
 	s.policy = policy
 	return policy
 }
🤖 Prompt for 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.

In `@internal/service/oidcprovision/service.go` around lines 250 - 279, Update
Service.loadPolicy so s.config.LoadPolicy(ctx) and newPolicySnapshot(loaded)
execute without holding policyMu; acquire the lock only when returning the
current policy after either failure and when swapping s.policy after successful
parsing. Preserve the existing fallback and successful reload behavior while
preventing synchronous reload I/O from blocking concurrent logins.

Copilot AI review requested due to automatic review settings July 30, 2026 23:27

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 30, 2026 23:56

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@yohamta0
yohamta0 merged commit 01c2336 into main Jul 31, 2026
11 checks passed
@yohamta0
yohamta0 deleted the agent/reload-oidc-policy-at-login branch July 31, 2026 01:48
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.

feat: Reload auth/OIDC role mapping without restarting the server

2 participants