Upgrade ruby-saml to 1.15.0.

This commit is contained in:
BoHung Chiu 2023-02-13 23:21:45 +08:00
parent e7ebebcd2e
commit 0d6c29d6d2
25 changed files with 308 additions and 125 deletions

View File

@ -0,0 +1,43 @@
name: ruby-saml CI
on: [push, pull_request]
jobs:
test:
name: Unit test
strategy:
fail-fast: false
matrix:
os: [ubuntu-20.04, macos-latest]
ruby-version: [2.1.9, 2.2.10, 2.3.8, 2.4.6, 2.5.8, 2.6.6, 2.7.2, 3.0.1, 3.1, 3.2, jruby-9.1.17.0, jruby-9.2.17.0, jruby-9.3.2.0, jruby-9.4.0.0, truffleruby]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Set up Ruby ${{ matrix.ruby-version }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- name: Install dependencies
run: bundle install
- name: Run tests
run: bundle exec rake
- name: Coveralls
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
parallel: true
flag-name: run-${{ matrix.ruby-version }}
finish:
needs: test
runs-on: ubuntu-latest
steps:
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.github_token }}
flag-name: run-${{ matrix.ruby-version }}
parallel-finished: true

View File

@ -1,4 +1,12 @@
# Ruby SAML Changelog # Ruby SAML Changelog
### 1.15.0 (Jan 04, 2023)
* [#650](https://github.com/SAML-Toolkits/ruby-saml/pull/650) Replace strip! by strip on compute_digest method
* [#638](https://github.com/SAML-Toolkits/ruby-saml/pull/638) Fix dateTime format for the validUntil attribute of the generated metadata
* [#576](https://github.com/SAML-Toolkits/ruby-saml/pull/576) Support idp cert multi with string keys
* [#567](https://github.com/SAML-Toolkits/ruby-saml/pull/567) Improve Code quality
* Add info about new repo, new maintainer, new security contact
* Fix tests, Adjust dependencies, Add ruby 3.2 and new jruby versions tests to the CI. Add coveralls support
### 1.14.0 (Feb 01, 2022) ### 1.14.0 (Feb 01, 2022)
* [#627](https://github.com/onelogin/ruby-saml/pull/627) Support escape downcasing for validating SLO Signatures of ADFS/Azure * [#627](https://github.com/onelogin/ruby-saml/pull/627) Support escape downcasing for validating SLO Signatures of ADFS/Azure
* [#633](https://github.com/onelogin/ruby-saml/pull/633) Support ability to change ID prefix * [#633](https://github.com/onelogin/ruby-saml/pull/633) Support ability to change ID prefix

View File

@ -1,4 +1,5 @@
Copyright (c) 2010-2016 OneLogin, Inc. Copyright (c) 2010-2022 OneLogin, Inc.
Copyright (c) 2023 IAM Digital Services, SL.
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation

View File

@ -1,6 +1,8 @@
# Ruby SAML # Ruby SAML
[![Build Status](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml/badge.svg?query=branch%3Amaster)](https://github.com/onelogin/ruby-saml/actions/workflows/test.yml?query=branch%3Amaster) [![ruby-saml CI](https://github.com/SAML-Toolkits/ruby-saml/actions/workflows/test.yml/badge.svg)](https://github.com/SAML-Toolkits/ruby-saml/actions/workflows/test.yml)
[![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master) [![Coverage Status](https://coveralls.io/repos/github/SAML-Toolkits/ruby-saml/badge.svg?branch=master)](https://coveralls.io/github/SAML-Toolkits/ruby-saml?branch=master)
[![Rubygem Version](https://badge.fury.io/rb/ruby-saml.svg)](https://badge.fury.io/rb/ruby-saml)
[![GitHub version](https://badge.fury.io/gh/SAML-Toolkits%2Fruby-saml.svg)](https://badge.fury.io/gh/SAML-Toolkits%2Fruby-saml) ![GitHub](https://img.shields.io/github/license/SAML-Toolkits/ruby-saml) ![Gem](https://img.shields.io/gem/dtv/ruby-saml?label=gem%20downloads%20latest) ![Gem](https://img.shields.io/gem/dt/ruby-saml?label=gem%20total%20downloads)
Ruby SAML minor and tiny versions may introduce breaking changes. Please read Ruby SAML minor and tiny versions may introduce breaking changes. Please read
[UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions. [UPGRADING.md](UPGRADING.md) for guidance on upgrading to new Ruby SAML versions.
@ -14,7 +16,7 @@ requests from identity providers.
SAML authorization is a two step process and you are expected to implement support for both. SAML authorization is a two step process and you are expected to implement support for both.
We created a demo project for Rails 4 that uses the latest version of this library: We created a demo project for Rails 4 that uses the latest version of this library:
[ruby-saml-example](https://github.com/onelogin/ruby-saml-example) [ruby-saml-example](https://github.com/saml-toolkits/ruby-saml-example)
### Supported Ruby Versions ### Supported Ruby Versions
@ -28,8 +30,12 @@ The following Ruby versions are covered by CI testing:
* 2.6.x * 2.6.x
* 2.7.x * 2.7.x
* 3.0.x * 3.0.x
* 3.1
* 3.2
* JRuby 9.1.x * JRuby 9.1.x
* JRuby 9.2.x * JRuby 9.2.x
* JRuby 9.3.X
* JRuby 9.4.0
* TruffleRuby (latest) * TruffleRuby (latest)
In addition, the following may work but are untested: In addition, the following may work but are untested:
@ -52,8 +58,7 @@ In addition, the following may work but are untested:
## Security Guidelines ## Security Guidelines
If you believe you have discovered a security vulnerability in this gem, please report it If you believe you have discovered a security vulnerability in this gem, please report it
at https://www.onelogin.com/security with a description. We follow responsible disclosure by mail to the maintainer: sixto.martin.garcia+security@gmail.com
guidelines, and will work with you to quickly find a resolution.
### Security Warning ### Security Warning
@ -87,7 +92,7 @@ Using `Gemfile`
gem 'ruby-saml', '~> 1.11.0' gem 'ruby-saml', '~> 1.11.0'
# or track master for bleeding-edge # or track master for bleeding-edge
gem 'ruby-saml', :github => 'onelogin/ruby-saml' gem 'ruby-saml', :github => 'saml-toolkit/ruby-saml'
``` ```
Using RubyGems Using RubyGems
@ -392,6 +397,51 @@ The `OneLogin::RubySaml::IdpMetadataParser` also provides the methods `#parse_to
Those return an Hash instead of a `Settings` object, which may be useful for configuring Those return an Hash instead of a `Settings` object, which may be useful for configuring
[omniauth-saml](https://github.com/omniauth/omniauth-saml), for instance. [omniauth-saml](https://github.com/omniauth/omniauth-saml), for instance.
### Validating Signature of Metadata and retrieve settings
Right now there is no method at ruby_saml to validate the signature of the metadata that gonna be parsed,
but it can be done as follows:
* Download the XML.
* Validate the Signature, providing the cert.
* Provide the XML to the parse method if the signature was validated
```
require "xml_security"
require "onelogin/ruby-saml/utils"
require "onelogin/ruby-saml/idp_metadata_parser"
url = "<url_to_the_metadata>"
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
uri = URI.parse(url)
raise ArgumentError.new("url must begin with http or https") unless /^https?/ =~ uri.scheme
http = Net::HTTP.new(uri.host, uri.port)
if uri.scheme == "https"
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
get = Net::HTTP::Get.new(uri.request_uri)
get.basic_auth uri.user, uri.password if uri.user
response = http.request(get)
xml = response.body
errors = []
doc = XMLSecurity::SignedDocument.new(xml, errors)
cert_str = "<include_cert_here>"
cert = OneLogin::RubySaml::Utils.format_cert("cert_str")
metadata_sign_cert = OpenSSL::X509::Certificate.new(cert)
valid = doc.validate_document_with_cert(metadata_sign_cert, true)
if valid
settings = idp_metadata_parser.parse(
xml,
entity_id: "<entity_id_of_the_entity_to_be_retrieved>"
)
else
print "Metadata Signarture failed to be verified with the cert provided"
end
## Retrieving Attributes ## Retrieving Attributes
If you are using `saml:AttributeStatement` to transfer data like the username, you can access all the attributes through `response.attributes`. It contains all the `saml:AttributeStatement`s with its 'Name' as an indifferent key and one or more `saml:AttributeValue`s as values. The value returned depends on the value of the If you are using `saml:AttributeStatement` to transfer data like the username, you can access all the attributes through `response.attributes`. It contains all the `saml:AttributeStatement`s with its 'Name' as an indifferent key and one or more `saml:AttributeValue`s as values. The value returned depends on the value of the

View File

@ -39,7 +39,7 @@ module OneLogin
saml_request = CGI.escape(params.delete("SAMLRequest")) saml_request = CGI.escape(params.delete("SAMLRequest"))
request_params = "#{params_prefix}SAMLRequest=#{saml_request}" request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
params.each_pair do |key, value| params.each_pair do |key, value|
request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}" request_params << "&#{key}=#{CGI.escape(value.to_s)}"
end end
raise SettingError.new "Invalid settings, idp_sso_service_url is not set!" if settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty? raise SettingError.new "Invalid settings, idp_sso_service_url is not set!" if settings.idp_sso_service_url.nil? or settings.idp_sso_service_url.empty?
@login_url = settings.idp_sso_service_url + request_params @login_url = settings.idp_sso_service_url + request_params

View File

@ -36,7 +36,7 @@ module OneLogin
saml_request = CGI.escape(params.delete("SAMLRequest")) saml_request = CGI.escape(params.delete("SAMLRequest"))
request_params = "#{params_prefix}SAMLRequest=#{saml_request}" request_params = "#{params_prefix}SAMLRequest=#{saml_request}"
params.each_pair do |key, value| params.each_pair do |key, value|
request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}" request_params << "&#{key}=#{CGI.escape(value.to_s)}"
end end
raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if settings.idp_slo_service_url.nil? or settings.idp_slo_service_url.empty? raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if settings.idp_slo_service_url.nil? or settings.idp_slo_service_url.empty?
@logout_url = settings.idp_slo_service_url + request_params @logout_url = settings.idp_slo_service_url + request_params

View File

@ -49,7 +49,7 @@ module OneLogin
root = meta_doc.add_element("md:EntityDescriptor", namespaces) root = meta_doc.add_element("md:EntityDescriptor", namespaces)
root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id
root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z') if valid_until root.attributes["validUntil"] = valid_until.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if valid_until
root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" if cache_duration root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" if cache_duration
root root
end end

View File

@ -741,7 +741,7 @@ module OneLogin
# @return [Boolean] True if the SessionNotOnOrAfter of the AuthnStatement is valid, otherwise (when expired) False if soft=True # @return [Boolean] True if the SessionNotOnOrAfter of the AuthnStatement is valid, otherwise (when expired) False if soft=True
# @raise [ValidationError] if soft == false and validation fails # @raise [ValidationError] if soft == false and validation fails
# #
def validate_session_expiration(soft = true) def validate_session_expiration
return true if session_expires_at.nil? return true if session_expires_at.nil?
now = Time.now.utc now = Time.now.utc

View File

@ -4,7 +4,6 @@ require 'base64'
require 'nokogiri' require 'nokogiri'
require 'rexml/document' require 'rexml/document'
require 'rexml/xpath' require 'rexml/xpath'
require 'thread'
require "onelogin/ruby-saml/error_handling" require "onelogin/ruby-saml/error_handling"
# Only supports SAML 2.0 # Only supports SAML 2.0
@ -69,14 +68,14 @@ module OneLogin
xml = Nokogiri::XML(document.to_s) do |config| xml = Nokogiri::XML(document.to_s) do |config|
config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS
end end
rescue Exception => error rescue StandardError => error
return false if soft return false if soft
raise ValidationError.new("XML load failed: #{error.message}") raise ValidationError.new("XML load failed: #{error.message}")
end end
SamlMessage.schema.validate(xml).map do |schema_error| SamlMessage.schema.validate(xml).map do |schema_error|
return false if soft return false if soft
raise ValidationError.new("#{schema_error.message}\n\n#{xml.to_s}") raise ValidationError.new("#{schema_error.message}\n\n#{xml}")
end end
end end

View File

@ -20,7 +20,7 @@ module OneLogin
end end
config.each do |k,v| config.each do |k,v|
acc = "#{k.to_s}=".to_sym acc = "#{k}=".to_sym
if respond_to? acc if respond_to? acc
value = v.is_a?(Hash) ? v.dup : v value = v.is_a?(Hash) ? v.dup : v
send(acc, value) send(acc, value)
@ -195,17 +195,13 @@ module OneLogin
certs = {:signing => [], :encryption => [] } certs = {:signing => [], :encryption => [] }
if idp_cert_multi.key?(:signing) and not idp_cert_multi[:signing].empty? [:signing, :encryption].each do |type|
idp_cert_multi[:signing].each do |idp_cert| certs_for_type = idp_cert_multi[type] || idp_cert_multi[type.to_s]
formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert) next if !certs_for_type || certs_for_type.empty?
certs[:signing].push(OpenSSL::X509::Certificate.new(formatted_cert))
end
end
if idp_cert_multi.key?(:encryption) and not idp_cert_multi[:encryption].empty? certs_for_type.each do |idp_cert|
idp_cert_multi[:encryption].each do |idp_cert|
formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert) formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
certs[:encryption].push(OpenSSL::X509::Certificate.new(formatted_cert)) certs[type].push(OpenSSL::X509::Certificate.new(formatted_cert))
end end
end end
@ -247,7 +243,6 @@ module OneLogin
OpenSSL::PKey::RSA.new(formatted_private_key) OpenSSL::PKey::RSA.new(formatted_private_key)
end end
private
def idp_binding_from_embed_sign def idp_binding_from_embed_sign
security[:embed_sign] ? Utils::BINDINGS[:post] : Utils::BINDINGS[:redirect] security[:embed_sign] ? Utils::BINDINGS[:post] : Utils::BINDINGS[:redirect]

View File

@ -41,7 +41,7 @@ module OneLogin
saml_response = CGI.escape(params.delete("SAMLResponse")) saml_response = CGI.escape(params.delete("SAMLResponse"))
response_params = "#{params_prefix}SAMLResponse=#{saml_response}" response_params = "#{params_prefix}SAMLResponse=#{saml_response}"
params.each_pair do |key, value| params.each_pair do |key, value|
response_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}" response_params << "&#{key}=#{CGI.escape(value.to_s)}"
end end
raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if url.nil? or url.empty? raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if url.nil? or url.empty?

View File

@ -1,5 +1,5 @@
module OneLogin module OneLogin
module RubySaml module RubySaml
VERSION = '1.14.0' VERSION = '1.15.0'
end end
end end

View File

@ -177,7 +177,7 @@ module XMLSecurity
def compute_digest(document, digest_algorithm) def compute_digest(document, digest_algorithm)
digest = digest_algorithm.digest(document) digest = digest_algorithm.digest(document)
Base64.encode64(digest).strip! Base64.encode64(digest).strip
end end
end end
@ -216,7 +216,7 @@ module XMLSecurity
if options[:fingerprint_alg] if options[:fingerprint_alg]
fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new
else else
fingerprint_alg = OpenSSL::Digest::SHA1.new fingerprint_alg = OpenSSL::Digest.new('SHA1')
end end
fingerprint = fingerprint_alg.hexdigest(cert.to_der) fingerprint = fingerprint_alg.hexdigest(cert.to_der)

View File

@ -6,17 +6,17 @@ Gem::Specification.new do |s|
s.version = OneLogin::RubySaml::VERSION s.version = OneLogin::RubySaml::VERSION
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["OneLogin LLC"] s.authors = ["SAML Toolkit", "Sixto Martin"]
s.email = ['contact@iamdigitalservices.com', 'sixto.martin.garcia@gmail.com']
s.date = Time.now.strftime("%Y-%m-%d") s.date = Time.now.strftime("%Y-%m-%d")
s.description = %q{SAML toolkit for Ruby on Rails} s.description = %q{SAML Ruby toolkit. Add SAML support to your Ruby software using this library}
s.email = %q{support@onelogin.com}
s.license = 'MIT' s.license = 'MIT'
s.extra_rdoc_files = [ s.extra_rdoc_files = [
"LICENSE", "LICENSE",
"README.md" "README.md"
] ]
s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
s.homepage = %q{https://github.com/onelogin/ruby-saml} s.homepage = %q{https://github.com/saml-toolkits/ruby-saml}
s.rdoc_options = ["--charset=UTF-8"] s.rdoc_options = ["--charset=UTF-8"]
s.require_paths = ["lib"] s.require_paths = ["lib"]
s.rubygems_version = %q{1.3.7} s.rubygems_version = %q{1.3.7}
@ -27,12 +27,18 @@ Gem::Specification.new do |s|
# Nokogiri's version dependent on the Ruby version, even though we would # Nokogiri's version dependent on the Ruby version, even though we would
# have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions. # have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions.
if defined?(JRUBY_VERSION) if defined?(JRUBY_VERSION)
if JRUBY_VERSION < '9.2.0.0' if JRUBY_VERSION < '9.1.7.0'
s.add_runtime_dependency('nokogiri', '>= 1.8.2', '<= 1.8.5') s.add_runtime_dependency('nokogiri', '>= 1.8.2', '<= 1.8.5')
s.add_runtime_dependency('jruby-openssl', '>= 0.9.8') s.add_runtime_dependency('jruby-openssl', '>= 0.9.8')
s.add_runtime_dependency('json', '< 2.3.0') s.add_runtime_dependency('json', '< 2.3.0')
elsif JRUBY_VERSION < '9.2.0.0'
s.add_runtime_dependency('nokogiri', '>= 1.9.1', '< 1.10.0')
elsif JRUBY_VERSION < '9.3.2.0'
s.add_runtime_dependency('nokogiri', '>= 1.11.4')
s.add_runtime_dependency('rexml')
else else
s.add_runtime_dependency('nokogiri', '>= 1.8.2') s.add_runtime_dependency('nokogiri', '>= 1.13.10')
s.add_runtime_dependency('rexml')
end end
elsif RUBY_VERSION < '1.9' elsif RUBY_VERSION < '1.9'
s.add_runtime_dependency('uuid') s.add_runtime_dependency('uuid')
@ -41,18 +47,35 @@ Gem::Specification.new do |s|
s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1') s.add_runtime_dependency('nokogiri', '>= 1.5.10', '<= 1.6.8.1')
s.add_runtime_dependency('json', '< 2.3.0') s.add_runtime_dependency('json', '< 2.3.0')
elsif RUBY_VERSION < '2.3' elsif RUBY_VERSION < '2.3'
s.add_runtime_dependency('nokogiri', '>= 1.7', '< 1.10.0') s.add_runtime_dependency('nokogiri', '~> 1.8.5')
elsif RUBY_VERSION < '2.5'
s.add_runtime_dependency('nokogiri', '~> 1.8.5')
s.add_runtime_dependency('rexml')
elsif RUBY_VERSION < '2.6'
s.add_runtime_dependency('nokogiri', '~> 1.8.5')
s.add_runtime_dependency('rexml')
else else
s.add_runtime_dependency('nokogiri', '>= 1.10.5') s.add_runtime_dependency('nokogiri', '~> 1.8.5')
s.add_runtime_dependency('rexml') s.add_runtime_dependency('rexml')
end end
s.add_development_dependency('coveralls') s.add_development_dependency('simplecov', '<0.22.0')
if RUBY_VERSION < '2.4.1'
s.add_development_dependency('simplecov-lcov', '<0.8.0')
else
s.add_development_dependency('simplecov-lcov', '>0.7.0')
end
s.add_development_dependency('minitest', '~> 5.5') s.add_development_dependency('minitest', '~> 5.5')
s.add_development_dependency('mocha', '~> 0.14') s.add_development_dependency('mocha', '~> 0.14')
if RUBY_VERSION < '2.0'
s.add_development_dependency('rake', '~> 10') s.add_development_dependency('rake', '~> 10')
else
s.add_development_dependency('rake', '>= 12.3.3')
end
s.add_development_dependency('shoulda', '~> 2.11') s.add_development_dependency('shoulda', '~> 2.11')
s.add_development_dependency('simplecov')
s.add_development_dependency('systemu', '~> 2') s.add_development_dependency('systemu', '~> 2')
if RUBY_VERSION < '2.1' if RUBY_VERSION < '2.1'

View File

@ -14,18 +14,18 @@ class RequestTest < Minitest::Test
it "create the deflated SAMLRequest URL parameter" do it "create the deflated SAMLRequest URL parameter" do
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
assert_match /^http:\/\/unauth\.com\/logout\?SAMLRequest=/, unauth_url assert_match(/^http:\/\/unauth\.com\/logout\?SAMLRequest=/, unauth_url)
inflated = decode_saml_request_payload(unauth_url) inflated = decode_saml_request_payload(unauth_url)
assert_match /^<samlp:LogoutRequest/, inflated assert_match(/^<samlp:LogoutRequest/, inflated)
end end
it "support additional params" do it "support additional params" do
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil }) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil })
assert_match /&hello=$/, unauth_url assert_match(/&hello=$/, unauth_url)
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" }) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" })
assert_match /&foo=bar$/, unauth_url assert_match(/&foo=bar$/, unauth_url)
end end
it "RelayState cases" do it "RelayState cases" do
@ -50,7 +50,7 @@ class RequestTest < Minitest::Test
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" }) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
inflated = decode_saml_request_payload(unauth_url) inflated = decode_saml_request_payload(unauth_url)
assert_match /<samlp:SessionIndex/, inflated assert_match(/<samlp:SessionIndex/, inflated)
assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
end end
@ -62,14 +62,14 @@ class RequestTest < Minitest::Test
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" }) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
inflated = decode_saml_request_payload(unauth_url) inflated = decode_saml_request_payload(unauth_url)
assert_match /<saml:NameID/, inflated assert_match(/<saml:NameID/, inflated)
assert_match %r(#{name_identifier_value}</saml:NameID>), inflated assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
end end
describe "when the target url doesn't contain a query string" do describe "when the target url doesn't contain a query string" do
it "create the SAMLRequest parameter correctly" do it "create the SAMLRequest parameter correctly" do
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
assert_match /^http:\/\/unauth.com\/logout\?SAMLRequest/, unauth_url assert_match(/^http:\/\/unauth.com\/logout\?SAMLRequest/, unauth_url)
end end
end end
@ -78,7 +78,7 @@ class RequestTest < Minitest::Test
settings.idp_slo_service_url = "http://example.com?field=value" settings.idp_slo_service_url = "http://example.com?field=value"
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, unauth_url assert_match(/^http:\/\/example.com\?field=value&SAMLRequest/, unauth_url)
end end
end end
@ -98,13 +98,13 @@ class RequestTest < Minitest::Test
it "creates request with ID prefixed with default '_'" do it "creates request with ID prefixed with default '_'" do
request = OneLogin::RubySaml::Logoutrequest.new request = OneLogin::RubySaml::Logoutrequest.new
assert_match /^_/, request.uuid assert_match(/^_/, request.uuid)
end end
it "creates request with ID is prefixed, when :id_prefix is passed" do it "creates request with ID is prefixed, when :id_prefix is passed" do
OneLogin::RubySaml::Utils::set_prefix("test") OneLogin::RubySaml::Utils::set_prefix("test")
request = OneLogin::RubySaml::Logoutrequest.new request = OneLogin::RubySaml::Logoutrequest.new
assert_match /^test/, request.uuid assert_match(/^test/, request.uuid)
OneLogin::RubySaml::Utils::set_prefix("_") OneLogin::RubySaml::Utils::set_prefix("_")
end end
end end

View File

@ -83,7 +83,7 @@ class MetadataTest < Minitest::Test
assert_equal xml_metadata[0..start.length-1],start assert_equal xml_metadata[0..start.length-1],start
doc_metadata = REXML::Document.new(xml_metadata) doc_metadata = REXML::Document.new(xml_metadata)
assert_equal valid_until.strftime('%Y-%m-%dT%H:%M:%S%z'), REXML::XPath.first(doc_metadata, "//md:EntityDescriptor").attribute("validUntil").value assert_equal valid_until.strftime('%Y-%m-%dT%H:%M:%SZ'), REXML::XPath.first(doc_metadata, "//md:EntityDescriptor").attribute("validUntil").value
assert_equal "PT604800S", REXML::XPath.first(doc_metadata, "//md:EntityDescriptor").attribute("cacheDuration").value assert_equal "PT604800S", REXML::XPath.first(doc_metadata, "//md:EntityDescriptor").attribute("cacheDuration").value
end end

View File

@ -14,7 +14,7 @@ class RequestTest < Minitest::Test
it "create the deflated SAMLRequest URL parameter" do it "create the deflated SAMLRequest URL parameter" do
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
@ -23,7 +23,7 @@ class RequestTest < Minitest::Test
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /^<samlp:AuthnRequest/, inflated assert_match(/^<samlp:AuthnRequest/, inflated)
end end
it "create the deflated SAMLRequest URL parameter including the Destination" do it "create the deflated SAMLRequest URL parameter including the Destination" do
@ -36,23 +36,23 @@ class RequestTest < Minitest::Test
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /<samlp:AuthnRequest[^<]* Destination='http:\/\/example.com'/, inflated assert_match(/<samlp:AuthnRequest[^<]* Destination='http:\/\/example.com'/, inflated)
end end
it "create the SAMLRequest URL parameter without deflating" do it "create the SAMLRequest URL parameter without deflating" do
settings.compress_request = false settings.compress_request = false
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
assert_match /^<samlp:AuthnRequest/, decoded assert_match(/^<samlp:AuthnRequest/, decoded)
end end
it "create the SAMLRequest URL parameter with IsPassive" do it "create the SAMLRequest URL parameter with IsPassive" do
settings.passive = true settings.passive = true
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
@ -61,13 +61,13 @@ class RequestTest < Minitest::Test
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /<samlp:AuthnRequest[^<]* IsPassive='true'/, inflated assert_match(/<samlp:AuthnRequest[^<]* IsPassive='true'/, inflated)
end end
it "create the SAMLRequest URL parameter with ProtocolBinding" do it "create the SAMLRequest URL parameter with ProtocolBinding" do
settings.protocol_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' settings.protocol_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
@ -76,13 +76,13 @@ class RequestTest < Minitest::Test
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /<samlp:AuthnRequest[^<]* ProtocolBinding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'/, inflated assert_match(/<samlp:AuthnRequest[^<]* ProtocolBinding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'/, inflated)
end end
it "create the SAMLRequest URL parameter with AttributeConsumingServiceIndex" do it "create the SAMLRequest URL parameter with AttributeConsumingServiceIndex" do
settings.attributes_index = 30 settings.attributes_index = 30
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
@ -90,13 +90,13 @@ class RequestTest < Minitest::Test
inflated = zstream.inflate(decoded) inflated = zstream.inflate(decoded)
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /<samlp:AuthnRequest[^<]* AttributeConsumingServiceIndex='30'/, inflated assert_match(/<samlp:AuthnRequest[^<]* AttributeConsumingServiceIndex='30'/, inflated)
end end
it "create the SAMLRequest URL parameter with ForceAuthn" do it "create the SAMLRequest URL parameter with ForceAuthn" do
settings.force_authn = true settings.force_authn = true
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
@ -104,13 +104,13 @@ class RequestTest < Minitest::Test
inflated = zstream.inflate(decoded) inflated = zstream.inflate(decoded)
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /<samlp:AuthnRequest[^<]* ForceAuthn='true'/, inflated assert_match(/<samlp:AuthnRequest[^<]* ForceAuthn='true'/, inflated)
end end
it "create the SAMLRequest URL parameter with NameID Format" do it "create the SAMLRequest URL parameter with NameID Format" do
settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS) zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
@ -118,15 +118,15 @@ class RequestTest < Minitest::Test
zstream.finish zstream.finish
zstream.close zstream.close
assert_match /<samlp:NameIDPolicy[^<]* AllowCreate='true'/, inflated assert_match(/<samlp:NameIDPolicy[^<]* AllowCreate='true'/, inflated)
assert_match /<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated assert_match(/<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated)
end end
it "create the SAMLRequest URL parameter with Subject" do it "create the SAMLRequest URL parameter with Subject" do
settings.name_identifier_value_requested = "testuser@example.com" settings.name_identifier_value_requested = "testuser@example.com"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example\.com\?SAMLRequest=/, auth_url assert_match(/^http:\/\/example\.com\?SAMLRequest=/, auth_url)
payload = CGI.unescape(auth_url.split("=").last) payload = CGI.unescape(auth_url.split("=").last)
decoded = Base64.decode64(payload) decoded = Base64.decode64(payload)
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS) zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
@ -141,10 +141,10 @@ class RequestTest < Minitest::Test
it "accept extra parameters" do it "accept extra parameters" do
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" }) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" })
assert_match /&hello=there$/, auth_url assert_match(/&hello=there$/, auth_url)
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => nil }) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => nil })
assert_match /&hello=$/, auth_url assert_match(/&hello=$/, auth_url)
end end
it "RelayState cases" do it "RelayState cases" do
@ -164,13 +164,13 @@ class RequestTest < Minitest::Test
it "creates request with ID prefixed with default '_'" do it "creates request with ID prefixed with default '_'" do
request = OneLogin::RubySaml::Authrequest.new request = OneLogin::RubySaml::Authrequest.new
assert_match /^_/, request.uuid assert_match(/^_/, request.uuid)
end end
it "creates request with ID is prefixed, when :id_prefix is passed" do it "creates request with ID is prefixed, when :id_prefix is passed" do
OneLogin::RubySaml::Utils::set_prefix("test") OneLogin::RubySaml::Utils::set_prefix("test")
request = OneLogin::RubySaml::Authrequest.new request = OneLogin::RubySaml::Authrequest.new
assert_match /^test/, request.uuid assert_match(/^test/, request.uuid)
OneLogin::RubySaml::Utils::set_prefix("_") OneLogin::RubySaml::Utils::set_prefix("_")
end end
@ -183,7 +183,7 @@ class RequestTest < Minitest::Test
err = assert_raises OneLogin::RubySaml::SettingError do err = assert_raises OneLogin::RubySaml::SettingError do
OneLogin::RubySaml::Authrequest.new.create(settings) OneLogin::RubySaml::Authrequest.new.create(settings)
end end
assert_match /idp_sso_service_url is not set/, err.message assert_match(/idp_sso_service_url is not set/, err.message)
end end
end end
@ -191,7 +191,7 @@ class RequestTest < Minitest::Test
it "create the SAMLRequest parameter correctly" do it "create the SAMLRequest parameter correctly" do
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example.com\?SAMLRequest/, auth_url assert_match(/^http:\/\/example.com\?SAMLRequest/, auth_url)
end end
end end
@ -200,42 +200,42 @@ class RequestTest < Minitest::Test
settings.idp_sso_service_url = "http://example.com?field=value" settings.idp_sso_service_url = "http://example.com?field=value"
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, auth_url assert_match(/^http:\/\/example.com\?field=value&SAMLRequest/, auth_url)
end end
end end
it "create the saml:AuthnContextClassRef element correctly" do it "create the saml:AuthnContextClassRef element correctly" do
settings.authn_context = 'secure/name/password/uri' settings.authn_context = 'secure/name/password/uri'
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s assert_match(/<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s)
end end
it "create multiple saml:AuthnContextClassRef elements correctly" do it "create multiple saml:AuthnContextClassRef elements correctly" do
settings.authn_context = ['secure/name/password/uri', 'secure/email/password/uri'] settings.authn_context = ['secure/name/password/uri', 'secure/email/password/uri']
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s assert_match(/<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s)
assert_match /<saml:AuthnContextClassRef>secure\/email\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s assert_match(/<saml:AuthnContextClassRef>secure\/email\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s)
end end
it "create the saml:AuthnContextClassRef with comparison exact" do it "create the saml:AuthnContextClassRef with comparison exact" do
settings.authn_context = 'secure/name/password/uri' settings.authn_context = 'secure/name/password/uri'
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
assert_match /<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/, auth_doc.to_s assert_match(/<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/, auth_doc.to_s)
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s assert_match(/<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s)
end end
it "create the saml:AuthnContextClassRef with comparison minimun" do it "create the saml:AuthnContextClassRef with comparison minimun" do
settings.authn_context = 'secure/name/password/uri' settings.authn_context = 'secure/name/password/uri'
settings.authn_context_comparison = 'minimun' settings.authn_context_comparison = 'minimun'
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
assert_match /<samlp:RequestedAuthnContext[\S ]+Comparison='minimun'/, auth_doc.to_s assert_match(/<samlp:RequestedAuthnContext[\S ]+Comparison='minimun'/, auth_doc.to_s)
assert_match /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s assert_match(/<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s)
end end
it "create the saml:AuthnContextDeclRef element correctly" do it "create the saml:AuthnContextDeclRef element correctly" do
settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport' settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
assert_match /<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/, auth_doc.to_s assert_match(/<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/, auth_doc.to_s)
end end
describe "#create_params signing with HTTP-POST binding" do describe "#create_params signing with HTTP-POST binding" do

View File

@ -411,7 +411,7 @@ class RubySamlTest < Minitest::Test
response.settings = settings response.settings = settings
error_msg = "Current time is on or after NotOnOrAfter condition" error_msg = "Current time is on or after NotOnOrAfter condition"
assert !response.is_valid? assert !response.is_valid?
assert_includes response.errors[0], "Current time is on or after NotOnOrAfter condition" assert_includes response.errors[0], error_msg
end end
it "return false when encountering a SAML Response with bad formatted" do it "return false when encountering a SAML Response with bad formatted" do
@ -457,7 +457,7 @@ class RubySamlTest < Minitest::Test
response_no_version.soft = true response_no_version.soft = true
error_msg = "Unsupported SAML version" error_msg = "Unsupported SAML version"
response_no_version.is_valid? response_no_version.is_valid?
assert_includes response_no_version.errors, "Unsupported SAML version" assert_includes response_no_version.errors, error_msg
end end
it "return true when a nil URI is given in the ds:Reference" do it "return true when a nil URI is given in the ds:Reference" do
@ -976,7 +976,17 @@ class RubySamlTest < Minitest::Test
:encryption => [] :encryption => []
} }
response_valid_signed.settings = settings response_valid_signed.settings = settings
res = response_valid_signed.send(:validate_signature) response_valid_signed.send(:validate_signature)
assert_empty response_valid_signed.errors
end
it "return true when at least a cert on idp_cert_multi is valid and keys are strings" do
settings.idp_cert_multi = {
"signing" => [ruby_saml_cert_text2, ruby_saml_cert_text],
"encryption" => []
}
response_valid_signed.settings = settings
assert response_valid_signed.send(:validate_signature)
assert_empty response_valid_signed.errors assert_empty response_valid_signed.errors
end end
@ -1217,7 +1227,7 @@ class RubySamlTest < Minitest::Test
settings.private_key = nil settings.private_key = nil
response_encrypted_attrs.settings = settings response_encrypted_attrs.settings = settings
assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedAttribute found and no SP private key found on the settings to decrypt it") do assert_raises(OneLogin::RubySaml::ValidationError, "An EncryptedAttribute found and no SP private key found on the settings to decrypt it") do
attrs = response_encrypted_attrs.attributes response_encrypted_attrs.attributes
end end
end end
@ -1355,6 +1365,7 @@ class RubySamlTest < Minitest::Test
describe '#xpath_first_from_signed_assertion' do describe '#xpath_first_from_signed_assertion' do
it 'not allow arbitrary code execution' do it 'not allow arbitrary code execution' do
$evalled = nil
malicious_response_document = fixture('response_eval', false) malicious_response_document = fixture('response_eval', false)
malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document) malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document)
malicious_response.send(:xpath_first_from_signed_assertion) malicious_response.send(:xpath_first_from_signed_assertion)
@ -1437,11 +1448,11 @@ class RubySamlTest < Minitest::Test
error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method" error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method"
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion) OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion)
end end
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
response2 = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings) OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
end end
settings.certificate = ruby_saml_cert_text settings.certificate = ruby_saml_cert_text
@ -1459,7 +1470,7 @@ class RubySamlTest < Minitest::Test
error_msg = "Neither PUB key nor PRIV key: nested asn1 error" error_msg = "Neither PUB key nor PRIV key: nested asn1 error"
assert_raises(OpenSSL::PKey::RSAError, error_msg) do assert_raises(OpenSSL::PKey::RSAError, error_msg) do
response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings) OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
end end
end end
@ -1553,7 +1564,7 @@ class RubySamlTest < Minitest::Test
error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it" error_msg = "An EncryptedAssertion found and no SP private key found on the settings to decrypt it"
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
decrypted = response.send(:decrypt_assertion, encrypted_assertion_node) response.send(:decrypt_assertion, encrypted_assertion_node)
end end
end end

View File

@ -257,11 +257,54 @@ class SettingsTest < Minitest::Test
assert_equal empty_multi, @settings.get_idp_cert_multi assert_equal empty_multi, @settings.get_idp_cert_multi
end end
it "returns the hash with certificates when values were valid" do it "returns partial hash when contains some values with string keys" do
certificates = ruby_saml_cert_text empty_multi = {
:signing => [],
:encryption => []
}
@settings.idp_cert_multi = { @settings.idp_cert_multi = {
:signing => [ruby_saml_cert_text], "signing" => []
:encryption => [ruby_saml_cert_text], }
assert_equal empty_multi, @settings.get_idp_cert_multi
@settings.idp_cert_multi = {
"encryption" => []
}
assert_equal empty_multi, @settings.get_idp_cert_multi
@settings.idp_cert_multi = {
"signing" => [],
"encryption" => []
}
assert_equal empty_multi, @settings.get_idp_cert_multi
@settings.idp_cert_multi = {
"yyy" => [],
"zzz" => []
}
assert_equal empty_multi, @settings.get_idp_cert_multi
end
it "returns the hash with certificates when values were valid" do
certificates = [ruby_saml_cert_text]
@settings.idp_cert_multi = {
:signing => certificates,
:encryption => certificates,
}
assert @settings.get_idp_cert_multi.kind_of? Hash
assert @settings.get_idp_cert_multi[:signing].kind_of? Array
assert @settings.get_idp_cert_multi[:encryption].kind_of? Array
assert @settings.get_idp_cert_multi[:signing][0].kind_of? OpenSSL::X509::Certificate
assert @settings.get_idp_cert_multi[:encryption][0].kind_of? OpenSSL::X509::Certificate
end
it "returns the hash with certificates when values were valid and with string keys" do
certificates = [ruby_saml_cert_text]
@settings.idp_cert_multi = {
"signing" => certificates,
"encryption" => certificates,
} }
assert @settings.get_idp_cert_multi.kind_of? Hash assert @settings.get_idp_cert_multi.kind_of? Hash

View File

@ -170,7 +170,7 @@ class RubySamlTest < Minitest::Test
Timecop.freeze Time.parse('2014-07-17T01:01:49Z') do Timecop.freeze Time.parse('2014-07-17T01:01:49Z') do
logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z' logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z'
assert !logout_request.send(:validate_not_on_or_after) assert !logout_request.send(:validate_not_on_or_after)
assert /Current time is on or after NotOnOrAfter/.match(logout_request.errors[0]) assert_match(/Current time is on or after NotOnOrAfter/, logout_request.errors[0])
end end
end end

View File

@ -20,21 +20,21 @@ class SloLogoutresponseTest < Minitest::Test
it "create the deflated SAMLResponse URL parameter" do it "create the deflated SAMLResponse URL parameter" do
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id) unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id)
assert_match /^http:\/\/unauth\.com\/logout\?SAMLResponse=/, unauth_url assert_match(/^http:\/\/unauth\.com\/logout\?SAMLResponse=/, unauth_url)
inflated = decode_saml_response_payload(unauth_url) inflated = decode_saml_response_payload(unauth_url)
assert_match /^<samlp:LogoutResponse/, inflated assert_match(/^<samlp:LogoutResponse/, inflated)
end end
it "support additional params" do it "support additional params" do
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :hello => nil }) unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :hello => nil })
assert_match /&hello=$/, unauth_url assert_match(/&hello=$/, unauth_url)
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :foo => "bar" }) unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :foo => "bar" })
assert_match /&foo=bar$/, unauth_url assert_match(/&foo=bar$/, unauth_url)
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :RelayState => "http://idp.example.com" }) unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :RelayState => "http://idp.example.com" })
assert_match /&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url assert_match(/&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url)
end end
it "RelayState cases" do it "RelayState cases" do
@ -55,45 +55,45 @@ class SloLogoutresponseTest < Minitest::Test
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id) unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id)
inflated = decode_saml_response_payload(unauth_url) inflated = decode_saml_response_payload(unauth_url)
assert_match /InResponseTo='_c0348950-935b-0131-1060-782bcb56fcaa'/, inflated assert_match(/InResponseTo='_c0348950-935b-0131-1060-782bcb56fcaa'/, inflated)
end end
it "set a custom successful logout message on the response" do it "set a custom successful logout message on the response" do
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, "Custom Logout Message") unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, "Custom Logout Message")
inflated = decode_saml_response_payload(unauth_url) inflated = decode_saml_response_payload(unauth_url)
assert_match /<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated assert_match(/<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated)
end end
it "set a custom logout message and an status on the response" do it "set a custom logout message and an status on the response" do
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, "Custom Logout Message", {}, "urn:oasis:names:tc:SAML:2.0:status:PartialLogout") unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, "Custom Logout Message", {}, "urn:oasis:names:tc:SAML:2.0:status:PartialLogout")
inflated = decode_saml_response_payload(unauth_url) inflated = decode_saml_response_payload(unauth_url)
assert_match /<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated assert_match(/<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated)
assert_match /<samlp:StatusCode Value='urn:oasis:names:tc:SAML:2.0:status:PartialLogout/, inflated assert_match(/<samlp:StatusCode Value='urn:oasis:names:tc:SAML:2.0:status:PartialLogout/, inflated)
end end
it "uses the response location when set" do it "uses the response location when set" do
settings.idp_slo_response_service_url = "http://unauth.com/logout/return" settings.idp_slo_response_service_url = "http://unauth.com/logout/return"
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id) unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id)
assert_match /^http:\/\/unauth\.com\/logout\/return\?SAMLResponse=/, unauth_url assert_match(/^http:\/\/unauth\.com\/logout\/return\?SAMLResponse=/, unauth_url)
inflated = decode_saml_response_payload(unauth_url) inflated = decode_saml_response_payload(unauth_url)
assert_match /Destination='http:\/\/unauth.com\/logout\/return'/, inflated assert_match(/Destination='http:\/\/unauth.com\/logout\/return'/, inflated)
end end
describe "playgin with preix" do describe "playgin with preix" do
it "creates request with ID prefixed with default '_'" do it "creates request with ID prefixed with default '_'" do
request = OneLogin::RubySaml::SloLogoutresponse.new request = OneLogin::RubySaml::SloLogoutresponse.new
assert_match /^_/, request.uuid assert_match(/^_/, request.uuid)
end end
it "creates request with ID is prefixed, when :id_prefix is passed" do it "creates request with ID is prefixed, when :id_prefix is passed" do
OneLogin::RubySaml::Utils::set_prefix("test") OneLogin::RubySaml::Utils::set_prefix("test")
request = OneLogin::RubySaml::SloLogoutresponse.new request = OneLogin::RubySaml::SloLogoutresponse.new
assert_match /^test/, request.uuid assert_match(/^test/, request.uuid)
OneLogin::RubySaml::Utils::set_prefix("_") OneLogin::RubySaml::Utils::set_prefix("_")
end end
end end
@ -147,8 +147,8 @@ class SloLogoutresponseTest < Minitest::Test
response_xml = Base64.decode64(params["SAMLResponse"]) response_xml = Base64.decode64(params["SAMLResponse"])
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/, response_xml assert_match(/<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/, response_xml)
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#sha1'\/>/, response_xml assert_match(/<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#sha1'\/>/, response_xml)
end end
it "create a signed logout response with 256 digest and signature methods" do it "create a signed logout response with 256 digest and signature methods" do
@ -159,8 +159,8 @@ class SloLogoutresponseTest < Minitest::Test
response_xml = Base64.decode64(params["SAMLResponse"]) response_xml = Base64.decode64(params["SAMLResponse"])
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/, response_xml assert_match(/<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/, response_xml)
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha256'\/>/, response_xml assert_match(/<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha256'\/>/, response_xml)
end end
it "create a signed logout response with 512 digest and signature method RSA_SHA384" do it "create a signed logout response with 512 digest and signature method RSA_SHA384" do
@ -172,8 +172,8 @@ class SloLogoutresponseTest < Minitest::Test
response_xml = Base64.decode64(params["SAMLResponse"]) response_xml = Base64.decode64(params["SAMLResponse"])
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha384'\/>/, response_xml assert_match(/<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha384'\/>/, response_xml)
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha512'\/>/, response_xml assert_match(/<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha512'\/>/, response_xml)
end end
end end

View File

@ -1,7 +1,10 @@
require 'simplecov' require 'simplecov'
require 'coveralls' require 'simplecov-lcov'
SimpleCov.formatter = Coveralls::SimpleCov::Formatter SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true
SimpleCov::Formatter::LcovFormatter.config.output_directory = 'coverage'
SimpleCov::Formatter::LcovFormatter.config.lcov_file_name = 'lcov.info'
SimpleCov.formatter = SimpleCov::Formatter::LcovFormatter
SimpleCov.start do SimpleCov.start do
add_filter "test/" add_filter "test/"
add_filter "vendor/" add_filter "vendor/"

View File

@ -183,18 +183,25 @@ class UtilsTest < Minitest::Test
end end
describe "#status_error_msg" do describe "#status_error_msg" do
it "returns a error msg with a status message" do it "returns a error msg with status_code and status message" do
error_msg = "The status code of the Logout Response was not Success" error_msg = "The status code of the Logout Response was not Success"
status_code = "urn:oasis:names:tc:SAML:2.0:status:Requester" status_code = "urn:oasis:names:tc:SAML:2.0:status:Requester"
status_message = "The request could not be performed due to an error on the part of the requester." status_message = "The request could not be performed due to an error on the part of the requester."
status_error_msg = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code, status_message) status_error_msg = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code, status_message)
assert_equal = "The status code of the Logout Response was not Success, was Requester -> The request could not be performed due to an error on the part of the requester.", status_error_msg assert_equal "The status code of the Logout Response was not Success, was Requester -> The request could not be performed due to an error on the part of the requester.", status_error_msg
end
status_error_msg2 = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code) it "returns a error msg with status_code" do
assert_equal = "The status code of the Logout Response was not Success, was Requester", status_error_msg2 error_msg = "The status code of the Logout Response was not Success"
status_code = "urn:oasis:names:tc:SAML:2.0:status:Requester"
status_error_msg = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code)
assert_equal "The status code of the Logout Response was not Success, was Requester", status_error_msg
end
status_error_msg3 = OneLogin::RubySaml::Utils.status_error_msg(error_msg) it "returns a error msg" do
assert_equal = "The status code of the Logout Response was not Success", status_error_msg3 error_msg = "The status code of the Logout Response was not Success"
status_error_msg = OneLogin::RubySaml::Utils.status_error_msg(error_msg)
assert_equal "The status code of the Logout Response was not Success", status_error_msg
end end
end end
@ -202,7 +209,7 @@ class UtilsTest < Minitest::Test
describe ".uuid" do describe ".uuid" do
it "returns a uuid starting with an underscore" do it "returns a uuid starting with an underscore" do
assert_match /^_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, OneLogin::RubySaml::Utils.uuid assert_match(/^_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/, OneLogin::RubySaml::Utils.uuid)
end end
it "doesn't return the same value twice" do it "doesn't return the same value twice" do

View File

@ -13,7 +13,7 @@ Gem::Specification.new do |s|
s.summary = "Summary of SsoLoginBox." s.summary = "Summary of SsoLoginBox."
s.description = "Description of SsoLoginBox." s.description = "Description of SsoLoginBox."
s.license = "MIT" s.license = "MIT"
s.add_dependency 'ruby-saml', '1.11.0' s.add_dependency 'ruby-saml-custom'
s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"] s.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.rdoc"]
s.test_files = Dir["test/**/*"] s.test_files = Dir["test/**/*"]
end end