Exempt auth controllers from read-only organization enforcement (MR A)

What does this MR do and why?

This is MR A of the Org Read-Only Mode authentication exemption work tracked in #602813 (closed).

MR B (block new-user-row creation in Gitlab::Auth::OAuth::User during read-only mode) is a separate follow-up MR. MR C (audit/block cascading auth-time writes into org-owned state, per ADR 010's "Cascading writes" rule) will be tracked as a follow-up on the same work item.

Problem

!241161 (merged) added enforce_read_only_organization as a before_action in ApplicationController. It blocks all POST/PATCH/PUT/DELETE requests when Current.organization.read_only? and the organization_read_only_enforcement feature flag is enabled.

Because authentication controllers inherit from ApplicationController, sign-in / SSO / token POSTs are currently blocked when an organization is read-only — which wrongly locks users out of obtaining a session to READ a read-only organization's data.

ADR 010 (Organization Read-Only Mode, section 'Authentication exemption') requires authentication endpoints to remain available during read-only.

Solution

Add skip_before_action :enforce_read_only_organization to the authentication controllers, mirroring how GraphqlController already does this. Skips are action-scoped (only:) wherever a controller mixes auth-plane and non-auth actions.

Controllers exempted:

Controller Scope Reason
SessionsController whole controller Sign-in / sign-out — session row, audit event, last-used timestamp
OmniauthCallbacksController whole controller SSO/SAML/OmniAuth callbacks — completes the SSO handshake
Ldap::OmniauthCallbacksController inherited Inherits the skip from OmniauthCallbacksController
Groups::OmniauthCallbacksController inherited Inherits the skip from OmniauthCallbacksController
JwtController whole controller Container registry JWT auth
PasswordsController :create, :update Password reset is required to complete sign-in for users who lost credentials
ConfirmationsController :create Resending the confirmation email is part of completing authentication
Users::TermsController :accept, :decline Terms acceptance is a hard gate wedged into the sign-in flow (enforce_terms!); blocking it locks users out entirely
Users::BaseIdentityVerificationController (EE) :verify_email_code, :send_phone_verification_code, :verify_phone_verification_code Identity verification is a sign-in gate
Groups::SsoController (EE) :saml only Render-only SSO sign-in landing page. #unlink is a real identity write and stays enforced (has a negative spec)
SmartcardController (EE) :auth Smartcard authentication

Controllers NOT exempted (and why):

Controller Reason
Oauth::TokensController Inherits from Doorkeeper::ApplicationMetalController via Gitlab::BaseDoorkeeperController, NOT from ApplicationController — the before_action is never registered there, so no skip is needed
Groups::SsoController#unlink (EE) Unlinking an identity is an org-owned write, not part of completing authentication — remains enforced

Every exemption ships with a regression spec asserting the write action succeeds (no read-only flash/redirect) while the organization is read-only, and negative specs assert non-exempt actions remain enforced.

References

Rollout safety

The organization_read_only_enforcement feature flag must remain disabled until MR B lands: with OmniauthCallbacksController exempted and no JIT-provisioning block yet, a first-time SSO/SAML sign-in against a read-only Organization could create a new users row on the source Cell, which ADR 010 explicitly forbids (orphan-row risk at cutover).

Screenshots or screen recordings

N/A — backend-only change, no UI impact.

Before After
N/A N/A

How to set up and validate locally

  1. Enable the feature flag in rails console:
    Feature.enable(:organization_read_only_enforcement)
  2. Put an organization into read-only mode:
    org = Organizations::Organization.find(1)
    org.start_read_only(read_only_reason: 'migration')
    org.confirm_read_only
  3. Attempt to sign in at /users/sign_in — should succeed (not blocked by read-only enforcement).
  4. Without this fix, the POST to /users/sign_in would return a 503 or redirect with a read-only flash message.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Edited by Chen Zhang

Merge request reports

Loading
Loading