355 lines
12 KiB
Ruby
355 lines
12 KiB
Ruby
require 'simplecov'
|
|
require 'coveralls'
|
|
|
|
SimpleCov.formatter = Coveralls::SimpleCov::Formatter
|
|
SimpleCov.start do
|
|
add_filter "test/"
|
|
add_filter "vendor/"
|
|
add_filter "lib/onelogin/ruby-saml/logging.rb"
|
|
end
|
|
|
|
require 'stringio'
|
|
require 'rubygems'
|
|
require 'bundler'
|
|
require 'minitest/autorun'
|
|
require 'mocha/setup'
|
|
require 'timecop'
|
|
|
|
Bundler.require :default, :test
|
|
|
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
|
|
require 'onelogin/ruby-saml/logging'
|
|
|
|
TEST_LOGGER = Logger.new(StringIO.new)
|
|
OneLogin::RubySaml::Logging.logger = TEST_LOGGER
|
|
|
|
class Minitest::Test
|
|
def fixture(document, base64 = true)
|
|
response = Dir.glob(File.join(File.dirname(__FILE__), "responses", "#{document}*")).first
|
|
if base64 && response =~ /\.xml$/
|
|
Base64.encode64(File.read(response))
|
|
else
|
|
File.read(response)
|
|
end
|
|
end
|
|
|
|
def read_response(response)
|
|
File.read(File.join(File.dirname(__FILE__), "responses", response))
|
|
end
|
|
|
|
def read_invalid_response(response)
|
|
File.read(File.join(File.dirname(__FILE__), "responses", "invalids", response))
|
|
end
|
|
|
|
def read_logout_request(request)
|
|
File.read(File.join(File.dirname(__FILE__), "logout_requests", request))
|
|
end
|
|
|
|
def read_certificate(certificate)
|
|
File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
|
|
end
|
|
|
|
def response_document_valid_signed
|
|
@response_document_valid_signed ||= read_response("valid_response.xml.base64")
|
|
end
|
|
|
|
def response_document_valid_signed_without_x509certificate
|
|
@response_document_valid_signed_without_x509certificate ||= read_response("valid_response_without_x509certificate.xml.base64")
|
|
end
|
|
|
|
def response_document_without_recipient
|
|
@response_document_without_recipient ||= read_response("response_with_undefined_recipient.xml.base64")
|
|
end
|
|
|
|
def response_document_without_recipient_with_time_updated
|
|
doc = Base64.decode64(response_document_without_recipient)
|
|
doc.gsub!(/NotBefore=\"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z\"/, "NotBefore=\"#{(Time.now-300).getutc.strftime("%Y-%m-%dT%XZ")}\"")
|
|
doc.gsub!(/NotOnOrAfter=\"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z\"/, "NotOnOrAfter=\"#{(Time.now+300).getutc.strftime("%Y-%m-%dT%XZ")}\"")
|
|
Base64.encode64(doc)
|
|
end
|
|
|
|
def response_document_without_attributes
|
|
@response_document_without_attributes ||= read_response("response_without_attributes.xml.base64")
|
|
end
|
|
|
|
def response_document_without_reference_uri
|
|
@response_document_without_reference_uri ||= read_response("response_without_reference_uri.xml.base64")
|
|
end
|
|
|
|
def response_document_with_signed_assertion
|
|
@response_document_with_signed_assertion ||= read_response("response_with_signed_assertion.xml.base64")
|
|
end
|
|
|
|
def response_document_with_signed_assertion_2
|
|
@response_document_with_signed_assertion_2 ||= read_response("response_with_signed_assertion_2.xml.base64")
|
|
end
|
|
|
|
def response_document_with_ds_namespace_at_the_root
|
|
@response_document_with_ds_namespace_at_the_root ||= read_response("response_with_ds_namespace_at_the_root.xml.base64")
|
|
end
|
|
|
|
def response_document_unsigned
|
|
@response_document_unsigned ||= read_response("response_unsigned_xml_base64")
|
|
end
|
|
|
|
def response_document_with_saml2_namespace
|
|
@response_document_with_saml2_namespace ||= read_response("response_with_saml2_namespace.xml.base64")
|
|
end
|
|
|
|
def ampersands_document
|
|
@ampersands_response ||= read_response("response_with_ampersands.xml.base64")
|
|
end
|
|
|
|
def response_document_no_cert_and_encrypted_attrs
|
|
@response_document_no_cert_and_encrypted_attrs ||= Base64.encode64(read_response("response_no_cert_and_encrypted_attrs.xml"))
|
|
end
|
|
|
|
def response_document_wrapped
|
|
@response_document_wrapped ||= read_response("response_wrapped.xml.base64")
|
|
end
|
|
|
|
def response_document_assertion_wrapped
|
|
@response_document_assertion_wrapped ||= read_response("response_assertion_wrapped.xml.base64")
|
|
end
|
|
|
|
def response_document_encrypted_nameid
|
|
@response_document_encrypted_nameid ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_encrypted_nameid.xml.base64'))
|
|
end
|
|
|
|
def signed_message_encrypted_unsigned_assertion
|
|
@signed_message_encrypted_unsigned_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_unsigned_assertion.xml.base64'))
|
|
end
|
|
|
|
def signed_message_encrypted_signed_assertion
|
|
@signed_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_signed_assertion.xml.base64'))
|
|
end
|
|
|
|
def unsigned_message_encrypted_signed_assertion
|
|
@unsigned_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'unsigned_message_encrypted_signed_assertion.xml.base64'))
|
|
end
|
|
|
|
def unsigned_message_encrypted_unsigned_assertion
|
|
@unsigned_message_encrypted_unsigned_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'unsigned_message_encrypted_unsigned_assertion.xml.base64'))
|
|
end
|
|
|
|
def response_document_encrypted_attrs
|
|
@response_document_encrypted_attrs ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_encrypted_attrs.xml.base64'))
|
|
end
|
|
|
|
def response_document_double_status_code
|
|
@response_document_double_status_code ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_double_status_code.xml.base64'))
|
|
end
|
|
|
|
def signature_fingerprint_1
|
|
@signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
|
|
end
|
|
|
|
# certificate used on response_with_undefined_recipient
|
|
def signature_1
|
|
@signature1 ||= read_certificate("certificate1")
|
|
end
|
|
|
|
# certificate used on response_document_with_signed_assertion_2
|
|
def certificate_without_head_foot
|
|
@certificate_without_head_foot ||= read_certificate("certificate_without_head_foot")
|
|
end
|
|
|
|
def idp_metadata_descriptor
|
|
@idp_metadata_descriptor ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor.xml'))
|
|
end
|
|
|
|
def idp_metadata_descriptor2
|
|
@idp_metadata_descriptor2 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_2.xml'))
|
|
end
|
|
|
|
def idp_metadata_descriptor3
|
|
@idp_metadata_descriptor3 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_3.xml'))
|
|
end
|
|
|
|
def idp_metadata_descriptor4
|
|
@idp_metadata_descriptor4 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_4.xml'))
|
|
end
|
|
|
|
def idp_metadata_descriptor5
|
|
@idp_metadata_descriptor5 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_5.xml'))
|
|
end
|
|
|
|
def idp_metadata_descriptor6
|
|
@idp_metadata_descriptor6 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_6.xml'))
|
|
end
|
|
|
|
def no_idp_metadata_descriptor
|
|
@no_idp_metadata_descriptor ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'no_idp_descriptor.xml'))
|
|
end
|
|
|
|
def idp_metadata_multiple_descriptors
|
|
@idp_metadata_multiple_descriptors ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_multiple_descriptors.xml'))
|
|
end
|
|
|
|
def idp_metadata_multiple_descriptors2
|
|
@idp_metadata_multiple_descriptors2 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_multiple_descriptors_2.xml'))
|
|
end
|
|
|
|
def idp_metadata_multiple_certs
|
|
@idp_metadata_multiple_descriptors ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_metadata_multi_certs.xml'))
|
|
end
|
|
|
|
def idp_metadata_multiple_signing_certs
|
|
@idp_metadata_multiple_signing_certs ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_metadata_multi_signing_certs.xml'))
|
|
end
|
|
|
|
def idp_metadata_same_sign_and_encrypt_cert
|
|
@idp_metadata_same_sign_and_encrypt_cert ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_metadata_same_sign_and_encrypt_cert.xml'))
|
|
end
|
|
|
|
def idp_metadata_different_sign_and_encrypt_cert
|
|
@idp_metadata_different_sign_and_encrypt_cert ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_metadata_different_sign_and_encrypt_cert.xml'))
|
|
end
|
|
|
|
def idp_different_slo_response_location
|
|
@idp_different_slo_response_location ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_different_slo_response_location.xml'))
|
|
end
|
|
|
|
def idp_without_slo_response_location
|
|
@idp_without_slo_response_location ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_without_slo_response_location.xml'))
|
|
end
|
|
|
|
def logout_request_document
|
|
unless @logout_request_document
|
|
xml = read_logout_request("slo_request.xml")
|
|
deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
|
|
@logout_request_document = Base64.encode64(deflated)
|
|
end
|
|
@logout_request_document
|
|
end
|
|
|
|
def logout_request_document_with_name_id_format
|
|
unless @logout_request_document_with_name_id_format
|
|
xml = read_logout_request("slo_request_with_name_id_format.xml")
|
|
deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
|
|
@logout_request_document_with_name_id_format = Base64.encode64(deflated)
|
|
end
|
|
@logout_request_document_with_name_id_format
|
|
end
|
|
|
|
def logout_request_xml_with_session_index
|
|
@logout_request_xml_with_session_index ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request_with_session_index.xml'))
|
|
end
|
|
|
|
def invalid_logout_request_document
|
|
unless @invalid_logout_request_document
|
|
xml = File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'invalid_slo_request.xml'))
|
|
deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
|
|
@invalid_logout_request_document = Base64.encode64(deflated)
|
|
end
|
|
@invalid_logout_request_document
|
|
end
|
|
|
|
def logout_request_base64
|
|
@logout_request_base64 ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request.xml.base64'))
|
|
end
|
|
|
|
def logout_request_deflated_base64
|
|
@logout_request_deflated_base64 ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request_deflated.xml.base64'))
|
|
end
|
|
|
|
def ruby_saml_cert
|
|
@ruby_saml_cert ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
|
|
end
|
|
|
|
def ruby_saml_cert2
|
|
@ruby_saml_cert2 ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text2)
|
|
end
|
|
|
|
def ruby_saml_cert_fingerprint
|
|
@ruby_saml_cert_fingerprint ||= Digest::SHA1.hexdigest(ruby_saml_cert.to_der).scan(/../).join(":")
|
|
end
|
|
|
|
def ruby_saml_cert_text
|
|
read_certificate("ruby-saml.crt")
|
|
end
|
|
|
|
def ruby_saml_cert_text2
|
|
read_certificate("ruby-saml-2.crt")
|
|
end
|
|
|
|
def ruby_saml_key
|
|
@ruby_saml_key ||= OpenSSL::PKey::RSA.new(ruby_saml_key_text)
|
|
end
|
|
|
|
def ruby_saml_key_text
|
|
read_certificate("ruby-saml.key")
|
|
end
|
|
|
|
#
|
|
# logoutresponse fixtures
|
|
#
|
|
def random_id
|
|
"_#{OneLogin::RubySaml::Utils.uuid}"
|
|
end
|
|
|
|
#
|
|
# decodes a base64 encoded SAML response for use in SloLogoutresponse tests
|
|
#
|
|
def decode_saml_response_payload(unauth_url)
|
|
payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
|
|
decoded = Base64.decode64(payload)
|
|
|
|
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
inflated = zstream.inflate(decoded)
|
|
zstream.finish
|
|
zstream.close
|
|
inflated
|
|
end
|
|
|
|
#
|
|
# decodes a base64 encoded SAML request for use in Logoutrequest tests
|
|
#
|
|
def decode_saml_request_payload(unauth_url)
|
|
payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
|
|
decoded = Base64.decode64(payload)
|
|
|
|
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
inflated = zstream.inflate(decoded)
|
|
zstream.finish
|
|
zstream.close
|
|
inflated
|
|
end
|
|
|
|
SCHEMA_DIR = File.expand_path(File.join(__FILE__, '../../lib/schemas'))
|
|
|
|
#
|
|
# validate an xml document against the given schema
|
|
#
|
|
def validate_xml!(document, schema)
|
|
Dir.chdir(SCHEMA_DIR) do
|
|
xsd = if schema.is_a? Nokogiri::XML::Schema
|
|
schema
|
|
else
|
|
Nokogiri::XML::Schema(File.read(schema))
|
|
end
|
|
|
|
xml = if document.is_a? Nokogiri::XML::Document
|
|
document
|
|
else
|
|
Nokogiri::XML(document) { |c| c.strict }
|
|
end
|
|
|
|
result = xsd.validate(xml)
|
|
|
|
if result.length != 0
|
|
raise "Schema validation failed! XSD validation errors: #{result.join(", ")}"
|
|
else
|
|
true
|
|
end
|
|
end
|
|
end
|
|
|
|
# Allows to emulate Azure AD request behavior
|
|
def downcased_escape(str)
|
|
CGI.escape(str).gsub(/%[A-Fa-f0-9]{2}/) { |match| match.downcase }
|
|
end
|
|
end
|