Step-up auth: Enforce visibility constraints - only allow for private groups/namespaces

Proposal

Step-up authentication should only be applicable to private groups. Group maintainers/owners can currently configure step-up auth on any visibility level, creating a semantic inconsistency:

  • Public/Internal groups: Accessible to unauthenticated or all authenticated users → step-up auth is meaningless
  • Private groups: Only authorized users can access → step-up auth provides meaningful additional security

This proposal adds validation to ensure step-up authentication can only be configured on private groups and prevents visibility changes that would violate this constraint.

This issue builds on top of Issue: Step-up auth: Group-based OIDC Step-up Authenti... (#556943)

🛠️ with ❤️ at Siemens

Related Issues and Merge Requests

Problem Statement

  1. No model-level validation: Group owners can configure step_up_auth_required_oauth_provider on public/internal groups, which is semantically invalid
  2. No visibility change protection: Private groups with step-up auth can be changed to public/internal, breaking the security model
  3. Project inheritance concerns: Projects may have different visibility than their parent group with step-up auth, creating ambiguity about enforcement
  4. Controller-only enforcement: Current implementation (!199800 (merged)) in app/controllers/concerns/enforces_step_up_authentication_for_namespace.rb checks visibility at runtime but doesn't prevent invalid configurations
  5. Unclear semantics: Group owners don't receive clear guidance that step-up auth requires private visibility

Proposed Solution

1. Model Validation

  • Add validation to app/models/namespace_setting.rb preventing step_up_auth_required_oauth_provider on non-private groups
  • Add validation to app/models/namespace.rb preventing visibility changes from private when step-up auth is configured

2. API/GraphQL Validation

  • Update lib/api/groups.rb and Mutations::Groups::Update to return 400 Bad Request with clear error messages explaining the visibility constraint

3. UI Validation

  • Disable step-up auth configuration for non-private groups in app/assets/javascripts/pages/groups/settings/*
  • Show informational message: "Step-up authentication is only available for private groups"
  • Prevent visibility changes when step-up auth is configured with warning message

4. Documentation

  • Group settings and step-up auth feature docs: explain private-only requirement
  • API docs: document validation error responses
  • Troubleshooting guide: visibility-related configuration errors
  • Administrator FAQ: why the constraint exists

Implementation Checklist

  • Model validation prevents setting step_up_auth_required_oauth_provider on non-private groups

    • Add validation to app/models/namespace_setting.rb (~10 lines)
    • Add RSpec tests to spec/models/namespace_setting_spec.rb (validation on private vs non-private groups, error messages)
  • Model validation prevents changing visibility from private to public/internal when step-up auth is configured

    • Add validation to app/models/namespace.rb (~15 lines)
    • Add RSpec tests to spec/models/namespace_spec.rb (visibility change validation, edge cases)
  • API and GraphQL return clear error messages (400 Bad Request) with explanation

    • Update lib/api/groups.rb for API error handling (~10 lines)
    • Update GraphQL mutations: Mutations::Groups::Update (~10 lines)
    • Add API tests to spec/requests/api/groups_spec.rb (error responses, proper status codes)
    • Add GraphQL tests for mutation validation
  • Group settings UI disables step-up auth options for non-private groups

    • Disable step-up auth UI elements for non-private groups (~30 lines in app/assets/javascripts/pages/groups/settings/*)
    • Add informational tooltips and messages (~20 lines)
  • Group settings UI prevents visibility changes when step-up auth is configured

    • Prevent visibility changes via UI when step-up auth configured
    • Add JavaScript validation for immediate user feedback
    • Add feature tests to spec/features/groups/settings/step_up_auth_spec.rb (UI behavior, warning messages)
  • Documentation updated across all relevant areas (feature docs, API docs, troubleshooting)

    • Update group settings documentation (step-up auth section with visibility requirement)
    • Update API documentation with validation error responses
    • Add troubleshooting section for visibility constraints
    • Create administrator FAQ entry explaining why step-up auth requires private visibility
    • Add examples of valid and invalid configurations
  • Comprehensive test coverage for all validation scenarios

    • Model validation tests cover all edge cases
    • API/GraphQL tests verify error handling
    • Feature tests validate UI behavior
    • Test coverage > 95% for all new validation code

Open Questions:

  1. Project Inheritance (High Priority) - Should projects under step-up auth groups be forced to private visibility? What happens when project visibility differs from parent? Review: app/models/project.rb

  2. Existing Configurations - Are there invalid configurations? Query to check:

    SELECT g.id, g.name, g.visibility_level, ns.step_up_auth_required_oauth_provider
    FROM namespaces g INNER JOIN namespace_settings ns ON g.id = ns.namespace_id
    WHERE ns.step_up_auth_required_oauth_provider IS NOT NULL
      AND g.visibility_level != 0 AND g.type = 'Group';

    Note: Migration not needed (feature behind flag)

  3. API/GraphQL Coverage - Which endpoints besides PUT /api/v4/groups/:id and Mutations::Groups::Update need validation?

  4. Bulk Operations - How should group transfers, bulk visibility changes, and import/export handle step-up auth constraints? Review: app/models/concerns/routable.rb

  5. Subgroups - Should subgroups inherit step-up auth settings? Should subgroup visibility be constrained by parent's step-up auth?

Technical Notes:

  • Builds on #556943 (Group protection) and !199800 (merged) (final integration and testing)
  • Feature behind omniauth_step_up_auth_for_namespace flag (not released, no migration needed)
  • Defense in depth: Keep controller-level checks from !199800 (merged) alongside model validations
  • Validation messages must be user-friendly and actionable

Edited by 🤖 GitLab Bot 🤖