Files
sure/app/controllers/admin/sso_providers_controller.rb
LPW 320e087a22 Add support for displaying and managing legacy SSO providers (#628)
* feat: add support for displaying and managing legacy SSO providers

- Introduced UI section for environment/YAML-configured SSO providers.
- Added warnings and guidance on migrating legacy providers to database-backed configuration.
- Enhanced localization with new keys for legacy provider management.
- Updated form and toggle components for improved usability.

* Expand SSO documentation: add SAML 2.0 support, JIT provisioning settings, super-admin setup steps, audit logging, and user administration details.

* Update JIT provisioning docs: clarify role mapping behavior and add examples; note new `logout_idp` audit log event.

---------

Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
2026-01-13 09:37:19 +01:00

164 lines
4.8 KiB
Ruby

# frozen_string_literal: true
module Admin
class SsoProvidersController < Admin::BaseController
before_action :set_sso_provider, only: %i[show edit update destroy toggle test_connection]
def index
authorize SsoProvider
@sso_providers = policy_scope(SsoProvider).order(:name)
# Load runtime providers (from YAML/env) that might not be in the database
# This helps show users that legacy providers are active but not manageable via UI
@runtime_providers = Rails.configuration.x.auth.sso_providers || []
db_provider_names = @sso_providers.pluck(:name)
@legacy_providers = @runtime_providers.reject { |p| db_provider_names.include?(p[:name].to_s) }
end
def show
authorize @sso_provider
end
def new
@sso_provider = SsoProvider.new
authorize @sso_provider
end
def create
@sso_provider = SsoProvider.new(processed_params)
authorize @sso_provider
# Auto-generate redirect_uri if not provided
if @sso_provider.redirect_uri.blank? && @sso_provider.name.present?
@sso_provider.redirect_uri = "#{request.base_url}/auth/#{@sso_provider.name}/callback"
end
if @sso_provider.save
log_provider_change(:create, @sso_provider)
clear_provider_cache
redirect_to admin_sso_providers_path, notice: t(".success")
else
render :new, status: :unprocessable_entity
end
end
def edit
authorize @sso_provider
end
def update
authorize @sso_provider
# Auto-update redirect_uri if name changed
params_hash = processed_params.to_h
if params_hash[:name].present? && params_hash[:name] != @sso_provider.name
params_hash[:redirect_uri] = "#{request.base_url}/auth/#{params_hash[:name]}/callback"
end
if @sso_provider.update(params_hash)
log_provider_change(:update, @sso_provider)
clear_provider_cache
redirect_to admin_sso_providers_path, notice: t(".success")
else
render :edit, status: :unprocessable_entity
end
end
def destroy
authorize @sso_provider
@sso_provider.destroy!
log_provider_change(:destroy, @sso_provider)
clear_provider_cache
redirect_to admin_sso_providers_path, notice: t(".success")
end
def toggle
authorize @sso_provider
@sso_provider.update!(enabled: !@sso_provider.enabled)
log_provider_change(:toggle, @sso_provider)
clear_provider_cache
notice = @sso_provider.enabled? ? t(".success_enabled") : t(".success_disabled")
redirect_to admin_sso_providers_path, notice: notice
end
def test_connection
authorize @sso_provider
tester = SsoProviderTester.new(@sso_provider)
result = tester.test!
render json: {
success: result.success?,
message: result.message,
details: result.details
}
end
private
def set_sso_provider
@sso_provider = SsoProvider.find(params[:id])
end
def sso_provider_params
params.require(:sso_provider).permit(
:strategy,
:name,
:label,
:icon,
:enabled,
:issuer,
:client_id,
:client_secret,
:redirect_uri,
:scopes,
:prompt,
settings: [
:default_role, :scopes, :prompt,
# SAML settings
:idp_metadata_url, :idp_sso_url, :idp_slo_url,
:idp_certificate, :idp_cert_fingerprint, :name_id_format,
role_mapping: {}
]
)
end
# Process params to convert role_mapping comma-separated strings to arrays
def processed_params
result = sso_provider_params.to_h
if result[:settings].present? && result[:settings][:role_mapping].present?
result[:settings][:role_mapping] = result[:settings][:role_mapping].transform_values do |v|
# Convert comma-separated string to array, removing empty values
v.to_s.split(",").map(&:strip).reject(&:blank?)
end
# Remove empty role mappings
result[:settings][:role_mapping] = result[:settings][:role_mapping].reject { |_, v| v.empty? }
result[:settings].delete(:role_mapping) if result[:settings][:role_mapping].empty?
end
result
end
def log_provider_change(action, provider)
Rails.logger.info(
"[Admin::SsoProviders] #{action.to_s.upcase} - " \
"user_id=#{Current.user.id} " \
"provider_id=#{provider.id} " \
"provider_name=#{provider.name} " \
"strategy=#{provider.strategy} " \
"enabled=#{provider.enabled}"
)
end
def clear_provider_cache
ProviderLoader.clear_cache
Rails.logger.info("[Admin::SsoProviders] Provider cache cleared by user_id=#{Current.user.id}")
end
end
end