123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- class OmniAuth::Strategies::SamlDatabase < OmniAuth::Strategies::SAML
- option :name, 'saml'
- def self.setup
- auth_saml_credentials = Setting.get('auth_saml_credentials') || {}
- http_type = Setting.get('http_type')
- fqdn = Setting.get('fqdn')
- # Use meta URL as entity id/issues as it is best practice.
- # See: https://community.zammad.org/t/saml-oidc-third-party-authentication/2533/13
- entity_id = "#{http_type}://#{fqdn}/auth/saml/metadata"
- assertion_consumer_service_url = "#{http_type}://#{fqdn}/auth/saml/callback"
- single_logout_service_url = "#{http_type}://#{fqdn}/auth/saml/slo"
- config = auth_saml_credentials.compact_blank
- .merge(
- assertion_consumer_service_url: assertion_consumer_service_url,
- sp_entity_id: entity_id,
- single_logout_service_url: single_logout_service_url,
- idp_slo_session_destroy: proc { |env, session| destroy_session(env, session) },
- )
- apply_security_settings(config)
- config
- end
- def self.destroy_session(env, session)
- session.delete('saml_uid')
- session.delete('saml_transaction_id')
- session.delete('saml_session_index')
- @_current_user = nil
- env['rack.session.options'][:expire_after] = nil
- session.destroy
- end
- def initialize(app, *args, &)
- args[0] = self.class.setup
- super
- end
- def self.apply_security_settings(settings)
- security = settings.delete(:security) || {}
- private_key = settings.delete(:private_key) || ''
- private_key_secret = settings.delete(:private_key_secret) || ''
- certificate = settings.delete(:certificate) || ''
- return if !check_security_settings(settings, security, private_key, private_key_secret, certificate)
- apply_security_default_settings(settings)
- apply_sign_only_settings(settings, security)
- apply_encrypt_only_settings(settings, security)
- true
- end
- def self.check_security_settings(settings, security, private_key, private_key_secret, certificate)
- return false if security.blank? || security.eql?('off')
- return false if private_key.blank? || certificate.blank?
- begin
- pkey = OpenSSL::PKey.read(private_key, private_key_secret)
- rescue
- return false
- end
- settings[:private_key] = pkey.to_pem
- settings[:certificate] = certificate
- true
- end
- def self.apply_security_default_settings(settings)
- settings[:security] = {
- digest_method: XMLSecurity::Document::SHA256,
- signature_method: XMLSecurity::Document::RSA_SHA256,
- authn_requests_signed: true,
- logout_requests_signed: true,
- want_assertions_signed: true,
- want_assertions_encrypted: true,
- }
- true
- end
- def self.apply_encrypt_only_settings(settings, security)
- return if !security.eql?('encrypt')
- settings[:security][:authn_requests_signed] = false
- settings[:security][:logout_requests_signed] = false
- settings[:security][:want_assertions_signed] = false
- true
- end
- def self.apply_sign_only_settings(settings, security)
- return if !security.eql?('sign')
- settings[:security][:want_assertions_encrypted] = false
- true
- end
- private_class_method %i[
- apply_security_settings
- check_security_settings
- apply_security_default_settings
- apply_encrypt_only_settings
- apply_sign_only_settings
- ].freeze
- private
- def handle_logout_response(raw_response, settings)
- logout_response = OneLogin::RubySaml::Logoutresponse.new(raw_response, settings, matches_request_id: session['saml_transaction_id'])
- logout_response.soft = false
- logout_response.validate
- redirect_path = if session['omniauth.origin']&.include?('/mobile')
- '/mobile'
- elsif session['omniauth.origin']&.include?('/desktop')
- '/desktop'
- else
- '/'
- end
- self.class.destroy_session(env, session)
- redirect "#{Setting.get('http_type')}://#{Setting.get('fqdn')}#{redirect_path}"
- end
- end
|