2016-11-07 14 views
1

Позвольте мне предисловие к этому вопросу, сказав, что я новичок в SAML и не понимаю, как это работает.Rails/Devise/SAML Метаданные Неправильные (не работает с PingFederate)

установки параметров

Я использую devise_saml_authenticatable камень с приложением Rails 4 для достижения единого входа. Приложение Rails выступает в качестве поставщика услуг (SP). Для того, чтобы проверить мою установку, я создал учетную запись разработчика OneLogin и настроить SAML Test Connector (IdP w/attr w/ sign response) используя следующие атрибуты:

Tab Конфигурация

ССО Вкладка

  • Эмитент URL: https://app.onelogin.com/saml/metadata/589819
  • SAML 2.0 конечных точек (HTTP): https://mysubdomain.onelogin.com/trust/saml2/http-post/sso/589819
  • SLO конечных точек (HTTP): https://mysubdomain.onelogin.com/trust/saml2/http-redirect/slo/589819
  • SAML Алгоритм подписи: SHA-1
  • SHA Отпечаток пальца: 60: 9D: 18: 56: B9: 80: D4: 25: 63: C1: CC: 57: 6D: B9: 06: 7C: 78: BB: 2C: F1
  • X.509 Сертификат:

    ----- BEGIN CERTIFICATE ----- MIIEFzCCAv + gAwIBAgIUQYRVa1MQpUh0gJaznmXSF/SPqnowDQYJKoZIhvcNAQEF BQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAoMCEZpcm1QbGF5MRUwEwYDVQQLDAxP bmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgOTI1MzEwHhcN MTYwOTIxMTU0NzQwWhcNMjEwOTIyMTU0NzQwWjBYMQswCQYDVQQGEwJVUzERMA8G A1UECgwIRmlybVBsYXkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW T25lTG9naW4gQWNjb3VudCA5MjUzMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC AQoCggEBALGVgocBj0ciHM3uKlWIcofPhOtzfJw1XpAdNynAvPtbCl7WE5 + sLBoQ ZF + oZ7Dl + wRW6DHMJCl9DdKcOaQA6/gr5bwt78IzZ8hWMoKQEPih + E0km6rKLYA8 M52vxtJxGs8Iqx60QvPEePQFM ОА + xg73OExfM7W5LnXwNz/Pxgsr3lBif5oCC76j SaTCFroV + TSjfOaYMW/lZrsS79KRIzA9I5XwUBe3bC8bsfQmZXgddCrkQUNSGGaS 7/jtFUlQ94 + LAL + l3yoAiNAE6 + mt48qqmyLfkKibXvnZ8dwuO272wpY4fEM + vFRy pYrTajqvhY3hYIq8dLw3ominE5VECl8CAwEAAaOB2DCB1TAMBgNVHRMBAf8EAjAA MB0GA1UdDgQWBBSxiuvTPxwOhh2pupID + tuyKCeceTCBlQYDVR0jBIGNMIGKgBSx iuvTPxwOhh2pupID + tuyKCeceaFcpFowWDELMAkGA1UEBhMCVVMxETAPBgNVBAoM CEZpcm1QbGF5MRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxv Z2luIEFjY291bnQgOTI1MzGCFEGEVWtTEKVIdICWs55l0hf0j6p6MA4GA1UdDwEB /wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAYBe + 5d3zpLZ7fcf3l3rXYeIxcpN + 9D2YZCbxsrBhY2Am4YE9nN + RaJXeDqeRBNtpayCZVxfHnXexRo1n7wxwTmosiydi 9yE7SY2xZf + 3feQreF25atnn4tzVhxYONaX1njZMIt/TNa7A9aeDfHS D + vwSuYYB hGxKT6HOkEAEBiXCZ/FcVNiB0D8bRwQhiJ3BTzXDfqHrmq8QYdn3Ejlqo62vMl6W XeMXUoyv6cUc64Ap6E + XtEQI1E8YB5R8GtTs3Y1Oa2dD6yWyCyVJ20 + Hi7IWAqXC EfqstqXB7FoQ2rAt39cepnu1SOarvEYDMwYIaVNF3hoyodBybJJsAwAnCQ == ----- END CERTIFICATE -----

В моей devise.rb я имею следующую конфигурацию:

config.saml_create_user = false 
config.saml_update_user = true 
config.saml_default_user_key = :email 
config.saml_session_index_key = :session_index 
config.saml_use_subject = true 
config.idp_settings_adapter = IdPSettingsAdapter 
config.idp_entity_id_reader = DeviseSamlAuthenticatable::DefaultIdpEntityIdReader 

вот мой IdPSettingsAdapter:

class IdPSettingsAdapter 
    def self.settings(idp_entity_id) 
     company = Company.find_by(idp_entity_id: idp_entity_id) 

     if company.present? 
      { 
       assertion_consumer_service_url: company.assertion_consumer_service_url, 
       assertion_consumer_service_binding: company.assertion_consumer_service_binding, 
       name_identifier_format: company.name_identifier_format, 
       issuer: company.issuer, 
       idp_entity_id: company.idp_entity_id, 
       authn_context: company.authn_context, 
       idp_slo_target_url: company.idp_slo_target_url, 
       idp_sso_target_url: company.idp_sso_target_url, 
       idp_cert_fingerprint: company.idp_cert_fingerprint 
      } 
     else 
      {} 
     end 
    end 
end 

Обратите внимание, что моя модель пользователя Contact принадлежит_Company и что настройки SSO хранятся в модели Company.

Вот мои SAML маршруты:

devise_for :contacts, skip: :saml_authenticatable, controllers: { 
    registrations: "registrations", 
    sessions: "sessions", 
    passwords: "passwords", 
    confirmations: "confirmations" 
} 
devise_scope :contact do 
    get '/sign_in' => 'sessions#new' 
    get '/sign_out' => 'sessions#destroy' 

    # SSO Routes 
    get 'saml/sign_in' => 'saml_sessions#new', as: :new_user_sso_session 
    post 'saml/auth' => 'saml_sessions#create', as: :user_sso_session 
    get 'saml/sign_out' => 'saml_sessions#destroy', as: :destroy_user_sso_session 
    get 'saml/metadata' => 'saml_sessions#metadata', as: :metadata_user_sso_session 
    match 'saml/idp_sign_out' => 'saml_sessions#idp_sign_out', via: [:get, :post] 
end 

Наконец вот моя SamlSessionsController:

require "ruby-saml" 

class SamlSessionsController < SessionsController 
    include DeviseSamlAuthenticatable::SamlConfig 
    skip_before_filter :verify_authenticity_token, raise: false 
    before_action :authorize_viewer, except: [:metadata] 
    protect_from_forgery with: :null_session, except: :create 

    def new 
     idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id 
     request = OneLogin::RubySaml::Authrequest.new 
     action = request.create(saml_config(idp_entity_id)) 
     redirect_to action 
    end 

    def metadata 
     idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id 
     meta = OneLogin::RubySaml::Metadata.new 
     render :xml => meta.generate(saml_config(idp_entity_id)), content_type: 'application/samlmetadata+xml' 
    end 

    def create 
     @idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id 
     response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_config(@idp_entity_id)) 

     if !response.is_valid? 
      puts "SAML FAILED WITH ERROR: " 
      puts response.errors 
     end 
     super 
    end 

    def idp_sign_out 
     company = Company.friendly.find(request.subdomain.downcase) 
     idp_entity_id = Company.friendly.find(@_request.env['HTTP_HOST'].split('.')[0]).idp_entity_id 

     if params[:SAMLRequest] && Devise.saml_session_index_key 
      saml_config = saml_config(idp_entity_id) 
      logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: saml_config(idp_entity_id)) 
      resource_class.reset_session_key_for(logout_request.name_id) 

      # binding.pry 
      sign_out current_contact if contact_signed_in? 
      redirect_to company.after_slo_url.present? ? company.after_slo_url : 'https://' + company.issuer 
      # redirect_to generate_idp_logout_response(saml_config(idp_entity_id), logout_request.id) 
     elsif params[:SAMLResponse] 
      #Currently Devise handles the session invalidation when the request is made. 
      #To support a true SP initiated logout response, the request ID would have to be tracked and session invalidated 
      #based on that. 
      if Devise.saml_sign_out_success_url 
       redirect_to Devise.saml_sign_out_success_url 
      else 
       redirect_to action: :new 
      end 
     else 
      head :invalid_request 
     end 
    end 

    protected 

    # Override devise to send user to IdP logout for SLO 
    def after_sign_out_path_for(_) 
     request = OneLogin::RubySaml::Logoutrequest.new 
     request.create(saml_config) 
    end 

    def generate_idp_logout_response(saml_config, logout_request_id) 
     OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil) 
    end 
end 

Проблема

Когда я вручную сохранить карту настройки от моего OneLogin адаптера к моему Company (см. скриншот), я могу аутентифицироваться как пользователь моего приложения, используя OneLogin в качестве поставщика удостоверений (IdP). Однако теперь мне нужно предоставить клиенту метаданные XML, представляющие настройку приложения. Когда я перехожу на /saml/metadata.xml, я получаю следующую конфигурацию, которая, согласно моему клиенту, неверна. Клиент не представил никаких подробностей о том, в чем проблема. Они используют PingFederate, если это имеет значение.

<?xml version='1.0' encoding='UTF-8'?> 
<md:EntityDescriptor ID='_a3581975-b73d-4784-a106-bafd61e15f87' xmlns:md='urn:oasis:names:tc:SAML:2.0:metadata'> 
    <md:SPSSODescriptor AuthnRequestsSigned='false' WantAssertionsSigned='false' protocolSupportEnumeration='urn:oasis:names:tc:SAML:2.0:protocol'> 
     <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> 
     <md:AssertionConsumerService Binding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' Location='https://mysubdomain.myapp.local:3000/saml/auth' index='0' isDefault='true'/> 
    </md:SPSSODescriptor> 
</md:EntityDescriptor> 

enter image description here

Мой вопрос, что я здесь делаю неправильно и как я могу это исправить? Как я уже сказал, я едва понимаю, как работает SAML под капотом.

+0

Без какой-либо информации об ошибке, как мы можем помочь вам? – smartin

+0

Я понимаю, что, однако, клиент не предлагал больше подробностей о том, что проблема, только то, что XML был некорректным. Мне интересно, отсутствует ли информация или неправильная настройка. – ACIDSTEALTH

ответ

1

В XML-метаданных не определено EntityID. При попытке проверить XML на validation tool вы получите

Line: 2 | Столбец: 0 -> Элемент '{urn: oasis: names: tc: SAML: 2.0: metadata} EntityDescriptor': атрибут 'entityID' требуется, но отсутствует.

Если вы просмотрите код ruby-saml, EntityID будет добавлен в XML метаданных, если задан settings.issuer. Можете ли вы проверить, предоставлены ли эти данные? Возможно, company.issuer, который я вижу в классе IdPSettingsAdapter, имеет пустое значение.

+0

Вот и все! Мне не хватало 'entityID', у меня было несколько других частей данных, установленных неправильно (значение« эмитента »было неправильным), но теперь я получил правильную настройку, и она работает. благодаря – ACIDSTEALTH