From 50ee508b61d080a2c17a18ffaeb96bc738d51bcf Mon Sep 17 00:00:00 2001 From: amoose Date: Mon, 27 Apr 2015 09:45:29 -0700 Subject: [PATCH] Updates logout req builder --- lib/onelogin/ruby-saml/logoutrequest.rb | 16 ++++-- lib/onelogin/ruby-saml/saml_message.rb | 76 +++++++++++++++---------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/lib/onelogin/ruby-saml/logoutrequest.rb b/lib/onelogin/ruby-saml/logoutrequest.rb index b3b593d86..a3e7d7d1d 100644 --- a/lib/onelogin/ruby-saml/logoutrequest.rb +++ b/lib/onelogin/ruby-saml/logoutrequest.rb @@ -1,6 +1,7 @@ require "uuid" require "onelogin/ruby-saml/logging" +require "onelogin/ruby-saml/saml_message" module OneLogin module RubySaml @@ -24,7 +25,10 @@ def create(settings, params={}) end def create_params(settings, params={}) - params = {} if params.nil? + # The method expects :RelayState but sometimes we get 'RelayState' instead. + # Based on the HashWithIndifferentAccess value in Rails we could experience + # conflicts so this line will solve them. + relay_state = params[:RelayState] || params['RelayState'] request_doc = create_logout_request_xml_doc(settings) request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values @@ -39,10 +43,10 @@ def create_params(settings, params={}) request_params = {"SAMLRequest" => base64_request} if settings.security[:logout_requests_signed] && !settings.security[:embed_sign] && settings.private_key - params['SigAlg'] = XMLSecurity::Document::SHA1 + params['SigAlg'] = settings.security[:signature_method] url_string = "SAMLRequest=#{CGI.escape(base64_request)}" - url_string += "&RelayState=#{CGI.escape(params['RelayState'])}" if params['RelayState'] - url_string += "&SigAlg=#{CGI.escape(params['SigAlg'])}" + url_string << "&RelayState=#{CGI.escape(relay_state)}" if relay_state + url_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}" private_key = settings.get_sp_key() signature = private_key.sign(XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method]).new, url_string) params['Signature'] = encode(signature) @@ -88,7 +92,7 @@ def create_logout_request_xml_doc(settings) sessionindex.text = settings.sessionindex end - # embebed sign + # embed signature if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign] private_key = settings.get_sp_key() cert = settings.get_sp_cert() @@ -99,4 +103,4 @@ def create_logout_request_xml_doc(settings) end end end -end +end \ No newline at end of file diff --git a/lib/onelogin/ruby-saml/saml_message.rb b/lib/onelogin/ruby-saml/saml_message.rb index 9e9d78d40..e3f9f305e 100644 --- a/lib/onelogin/ruby-saml/saml_message.rb +++ b/lib/onelogin/ruby-saml/saml_message.rb @@ -1,8 +1,10 @@ require 'cgi' require 'zlib' require 'base64' -require "rexml/document" -require "rexml/xpath" +require 'nokogiri' +require 'rexml/document' +require 'rexml/xpath' +require 'thread' module OneLogin module RubySaml @@ -12,15 +14,22 @@ class SamlMessage ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion" PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol" - def valid_saml?(document, soft = true) - Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do - @schema = Nokogiri::XML::Schema(IO.read('saml-schema-protocol-2.0.xsd')) - @xml = Nokogiri::XML(document.to_s) + BASE64_FORMAT = %r(\A[A-Za-z0-9+/]{4}*[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=?\Z) + + def self.schema + @schema ||= Mutex.new.synchronize do + Dir.chdir(File.expand_path("../../../schemas", __FILE__)) do + ::Nokogiri::XML::Schema(File.read("saml-schema-protocol-2.0.xsd")) + end end - if soft - @schema.validate(@xml).map{ return false } - else - @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") } + end + + def valid_saml?(document, soft = true) + xml = Nokogiri::XML(document.to_s) + + SamlMessage.schema.validate(xml).map do |error| + break false if soft + validation_error("#{error.message}\n\n#{xml.to_s}") end end @@ -30,22 +39,29 @@ def validation_error(message) private + ## + # Take a SAML object provided by +saml+, determine its status and return + # a decoded XML as a String. + # + # Since SAML decided to use the RFC1951 and therefor has no zlib markers, + # the only reliable method of deciding whether we have a zlib stream or not + # is to try and inflate it and fall back to the base64 decoded string if + # the stream contains errors. def decode_raw_saml(saml) - if saml =~ /^