diff --git a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue index 32f8c8846..5fce380e7 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/security/components/SamlSettings.vue @@ -113,7 +113,16 @@ const saveSamlSettings = async settings => { useAlert(t('SECURITY_SETTINGS.SAML.API.DISABLED')); } } catch (error) { - useAlert(t('SECURITY_SETTINGS.SAML.API.ERROR')); + // Handle backend validation errors + if (error.response?.data?.errors) { + const errorMessages = error.response.data.errors; + const firstError = Array.isArray(errorMessages) + ? errorMessages[0] + : errorMessages; + useAlert(firstError); + } else { + useAlert(t('SECURITY_SETTINGS.SAML.API.ERROR')); + } throw error; } finally { isSubmitting.value = false; diff --git a/config/locales/en.yml b/config/locales/en.yml index 0a5e6d832..7722d484e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -101,6 +101,8 @@ en: invalid_value: Invalid value. The values provided for %{attribute_name} are invalid custom_attribute_definition: key_conflict: The provided key is not allowed as it might conflict with default attributes. + account_saml_settings: + invalid_certificate: must be a valid X.509 certificate in PEM format reports: period: Reporting period %{since} to %{until} utc_warning: The report generated is in UTC timezone diff --git a/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb b/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb index 3c9ec6ef3..d41760e46 100644 --- a/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb +++ b/enterprise/app/controllers/api/v1/accounts/saml_settings_controller.rb @@ -7,11 +7,19 @@ class Api::V1::Accounts::SamlSettingsController < Api::V1::Accounts::BaseControl def create @saml_settings = AccountSamlSettings.new(saml_settings_params.merge(account: Current.account)) - @saml_settings.save! + if @saml_settings.save + render :show + else + render json: { errors: @saml_settings.errors.full_messages }, status: :unprocessable_entity + end end def update - @saml_settings.update!(saml_settings_params) + if @saml_settings.update(saml_settings_params) + render :show + else + render json: { errors: @saml_settings.errors.full_messages }, status: :unprocessable_entity + end end def destroy diff --git a/enterprise/app/models/account_saml_settings.rb b/enterprise/app/models/account_saml_settings.rb index 25e8aeffe..b2f7bfe7b 100644 --- a/enterprise/app/models/account_saml_settings.rb +++ b/enterprise/app/models/account_saml_settings.rb @@ -23,6 +23,7 @@ class AccountSamlSettings < ApplicationRecord validates :sso_url, presence: true validates :certificate, presence: true validates :idp_entity_id, presence: true + validate :certificate_must_be_valid_x509 before_validation :set_sp_entity_id, if: :sp_entity_id_needs_generation? @@ -56,4 +57,12 @@ class AccountSamlSettings < ApplicationRecord def installation_name GlobalConfigService.load('INSTALLATION_NAME', 'Chatwoot') end + + def certificate_must_be_valid_x509 + return if certificate.blank? + + OpenSSL::X509::Certificate.new(certificate) + rescue OpenSSL::X509::CertificateError + errors.add(:certificate, I18n.t('errors.account_saml_settings.invalid_certificate')) + end end