class OmniAuth::Strategies::SAML

Constants

RUBYSAML_RESPONSE_OPTIONS

Public Class Methods

inherited(subclass) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 9
def self.inherited(subclass)
  OmniAuth::Strategy.included(subclass)
end

Public Instance Methods

callback_phase() click to toggle source
Calls superclass method
# File lib/omniauth/strategies/saml.rb, line 42
def callback_phase
  raise OmniAuth::Strategies::SAML::ValidationError.new("SAML response missing") unless request.params["SAMLResponse"]

  with_settings do |settings|
    # Call a fingerprint validation method if there's one
    validate_fingerprint(settings) if options.idp_cert_fingerprint_validator

    handle_response(request.params["SAMLResponse"], options_for_response_object, settings) do
      super
    end
  end
rescue OmniAuth::Strategies::SAML::ValidationError
  fail!(:invalid_ticket, $!)
rescue OneLogin::RubySaml::ValidationError
  fail!(:invalid_ticket, $!)
end
find_attribute_by(keys) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 113
def find_attribute_by(keys)
  keys.each do |key|
    return @attributes[key] if @attributes[key]
  end

  nil
end
other_phase() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 71
def other_phase
  if request_path_pattern.match(current_path)
    @env['omniauth.strategy'] ||= self
    setup_phase

    if on_subpath?(:metadata)
      other_phase_for_metadata
    elsif on_subpath?(:slo)
      other_phase_for_slo
    elsif on_subpath?(:spslo)
      other_phase_for_spslo
    else
      call_app!
    end
  else
    call_app!
  end
end
request_phase() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 34
def request_phase
  authn_request = OneLogin::RubySaml::Authrequest.new

  with_settings do |settings|
    redirect(authn_request.create(settings, additional_params_for_authn_request))
  end
end
response_fingerprint() click to toggle source

Obtain an idp certificate fingerprint from the response.

# File lib/omniauth/strategies/saml.rb, line 60
def response_fingerprint
  response = request.params["SAMLResponse"]
  response = (response =~ /^</) ? response : Base64.decode64(response)
  document = XMLSecurity::SignedDocument::new(response)
  cert_element = REXML::XPath.first(document, "//ds:X509Certificate", { "ds"=> 'http://www.w3.org/2000/09/xmldsig#' })
  base64_cert = cert_element.text
  cert_text = Base64.decode64(base64_cert)
  cert = OpenSSL::X509::Certificate.new(cert_text)
  Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(':')
end

Private Instance Methods

add_request_attributes_to(settings) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 276
def add_request_attributes_to(settings)
  settings.attribute_consuming_service.service_name options.attribute_service_name
  settings.sp_entity_id = options.sp_entity_id

  options.request_attributes.each do |attribute|
    settings.attribute_consuming_service.add_attribute attribute
  end
end
additional_params_for_authn_request() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 285
def additional_params_for_authn_request
  {}.tap do |additional_params|
    runtime_request_parameters = options.delete(:idp_sso_service_url_runtime_params)

    if runtime_request_parameters
      runtime_request_parameters.each_pair do |request_param_key, mapped_param_key|
        additional_params[mapped_param_key] = request.params[request_param_key.to_s] if request.params.has_key?(request_param_key.to_s)
      end
    end
  end
end
generate_logout_request(settings) click to toggle source

Create a SP initiated SLO: github.com/onelogin/ruby-saml#single-log-out

# File lib/omniauth/strategies/saml.rb, line 198
def generate_logout_request(settings)
  logout_request = OneLogin::RubySaml::Logoutrequest.new()

  # Since we created a new SAML request, save the transaction_id
  # to compare it with the response we get back
  session["saml_transaction_id"] = logout_request.uuid

  if settings.name_identifier_value.nil?
    settings.name_identifier_value = session["saml_uid"]
  end

  if settings.sessionindex.nil?
    settings.sessionindex = session["saml_session_index"]
  end

  logout_request.create(settings, RelayState: slo_relay_state)
end
handle_logout_request(raw_request, settings) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 179
def handle_logout_request(raw_request, settings)
  logout_request = OneLogin::RubySaml::SloLogoutrequest.new(raw_request, {}.merge(settings: settings).merge(get_params: @request.params))

  if logout_request.is_valid? &&
    logout_request.name_id == session["saml_uid"]

    # Actually log out this session
    options[:idp_slo_session_destroy].call @env, session

    # Generate a response to the IdP.
    logout_request_id = logout_request.id
    logout_response = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request_id, nil, RelayState: slo_relay_state)
    redirect(logout_response)
  else
    raise OmniAuth::Strategies::SAML::ValidationError.new("SAML failed to process LogoutRequest")
  end
end
handle_logout_response(raw_response, settings) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 164
def handle_logout_response(raw_response, settings)
  # After sending an SP initiated LogoutRequest to the IdP, we need to accept
  # the LogoutResponse, verify it, then actually delete our session.

  logout_response = OneLogin::RubySaml::Logoutresponse.new(raw_response, settings, :matches_request_id => session["saml_transaction_id"])
  logout_response.soft = false
  logout_response.validate

  session.delete("saml_uid")
  session.delete("saml_transaction_id")
  session.delete("saml_session_index")

  redirect(slo_relay_state)
end
handle_response(raw_response, opts, settings) { || ... } click to toggle source
# File lib/omniauth/strategies/saml.rb, line 131
def handle_response(raw_response, opts, settings)
  response = OneLogin::RubySaml::Response.new(raw_response, opts.merge(settings: settings))
  response.attributes["fingerprint"] = settings.idp_cert_fingerprint
  response.soft = false

  response.is_valid?
  @name_id = response.name_id
  @session_index = response.sessionindex
  @attributes = response.attributes
  @response_object = response

  session["saml_uid"] = @name_id
  session["saml_session_index"] = @session_index
  yield
end
on_subpath?(subpath) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 127
def on_subpath?(subpath)
  on_path?("#{request_path}/#{subpath}")
end
options_for_response_object() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 232
def options_for_response_object
  # filter options to select only extra parameters
  opts = options.select {|k,_| RUBYSAML_RESPONSE_OPTIONS.include?(k.to_sym)}

  # symbolize keys without activeSupport/symbolize_keys (ruby-saml use symbols)
  opts.inject({}) do |new_hash, (key, value)|
    new_hash[key.to_sym] = value
    new_hash
  end
end
other_phase_for_metadata() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 243
def other_phase_for_metadata
  with_settings do |settings|
    # omniauth does not set the strategy on the other_phase
    response = OneLogin::RubySaml::Metadata.new

    add_request_attributes_to(settings) if options.request_attributes.length > 0

    Rack::Response.new(response.generate(settings), 200, { "Content-Type" => "application/xml" }).finish
  end
end
other_phase_for_slo() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 254
def other_phase_for_slo
  with_settings do |settings|
    if request.params["SAMLResponse"]
      handle_logout_response(request.params["SAMLResponse"], settings)
    elsif request.params["SAMLRequest"]
      handle_logout_request(request.params["SAMLRequest"], settings)
    else
      raise OmniAuth::Strategies::SAML::ValidationError.new("SAML logout response/request missing")
    end
  end
end
other_phase_for_spslo() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 266
def other_phase_for_spslo
  if options.idp_slo_service_url
    with_settings do |settings|
      redirect(generate_logout_request(settings))
    end
  else
    Rack::Response.new("Not Implemented", 501, { "Content-Type" => "text/html" }).finish
  end
end
request_path_pattern() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 123
def request_path_pattern
  @request_path_pattern ||= %r{\A#{Regexp.quote(request_path)}(/|\z)}
end
slo_relay_state() click to toggle source
# File lib/omniauth/strategies/saml.rb, line 147
def slo_relay_state
  if request.params.has_key?("RelayState") && request.params["RelayState"] != ""
    request.params["RelayState"]
  else
    slo_default_relay_state = options.slo_default_relay_state
    if slo_default_relay_state.respond_to?(:call)
      if slo_default_relay_state.arity == 1
        slo_default_relay_state.call(request)
      else
        slo_default_relay_state.call
      end
    else
      slo_default_relay_state
    end
  end
end
validate_fingerprint(settings) click to toggle source
# File lib/omniauth/strategies/saml.rb, line 221
def validate_fingerprint(settings)
  fingerprint_exists = options.idp_cert_fingerprint_validator[response_fingerprint]

  unless fingerprint_exists
    raise OmniAuth::Strategies::SAML::ValidationError.new("Non-existent fingerprint")
  end

  # id_cert_fingerprint becomes the given fingerprint if it exists
  settings.idp_cert_fingerprint = fingerprint_exists
end
with_settings() { |settings| ... } click to toggle source
# File lib/omniauth/strategies/saml.rb, line 216
def with_settings
  options[:assertion_consumer_service_url] ||= callback_url
  yield OneLogin::RubySaml::Settings.new(options)
end