diff --git a/ruby-saml-custom/.github/workflows/test.yml b/ruby-saml-custom/.github/workflows/test.yml new file mode 100644 index 0000000..8181d37 --- /dev/null +++ b/ruby-saml-custom/.github/workflows/test.yml @@ -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 diff --git a/ruby-saml-custom/CHANGELOG.md b/ruby-saml-custom/CHANGELOG.md index d045328..97a7032 100644 --- a/ruby-saml-custom/CHANGELOG.md +++ b/ruby-saml-custom/CHANGELOG.md @@ -1,4 +1,12 @@ # 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) * [#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 diff --git a/ruby-saml-custom/LICENSE b/ruby-saml-custom/LICENSE index dbbca9c..c141165 100644 --- a/ruby-saml-custom/LICENSE +++ b/ruby-saml-custom/LICENSE @@ -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 obtaining a copy of this software and associated documentation diff --git a/ruby-saml-custom/README.md b/ruby-saml-custom/README.md index 2bbb402..ce6f695 100644 --- a/ruby-saml-custom/README.md +++ b/ruby-saml-custom/README.md @@ -1,6 +1,8 @@ # 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) -[![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/ruby-saml?branch=master) +[![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/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 [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. 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 @@ -28,8 +30,12 @@ The following Ruby versions are covered by CI testing: * 2.6.x * 2.7.x * 3.0.x +* 3.1 +* 3.2 * JRuby 9.1.x * JRuby 9.2.x +* JRuby 9.3.X +* JRuby 9.4.0 * TruffleRuby (latest) In addition, the following may work but are untested: @@ -52,8 +58,7 @@ In addition, the following may work but are untested: ## Security Guidelines 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 -guidelines, and will work with you to quickly find a resolution. +by mail to the maintainer: sixto.martin.garcia+security@gmail.com ### Security Warning @@ -87,7 +92,7 @@ Using `Gemfile` gem 'ruby-saml', '~> 1.11.0' # or track master for bleeding-edge -gem 'ruby-saml', :github => 'onelogin/ruby-saml' +gem 'ruby-saml', :github => 'saml-toolkit/ruby-saml' ``` 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 [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 = "" +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 = "" +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: "" + ) +else + print "Metadata Signarture failed to be verified with the cert provided" +end + + ## 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 diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/authrequest.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/authrequest.rb index 5e2c7c3..d604eac 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/authrequest.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/authrequest.rb @@ -39,7 +39,7 @@ module OneLogin saml_request = CGI.escape(params.delete("SAMLRequest")) request_params = "#{params_prefix}SAMLRequest=#{saml_request}" 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 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 diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/logoutrequest.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/logoutrequest.rb index b1ef784..c6d96da 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/logoutrequest.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/logoutrequest.rb @@ -36,7 +36,7 @@ module OneLogin saml_request = CGI.escape(params.delete("SAMLRequest")) request_params = "#{params_prefix}SAMLRequest=#{saml_request}" 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 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 diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/metadata.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/metadata.rb index a6adcf8..e6b5bda 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/metadata.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/metadata.rb @@ -49,7 +49,7 @@ module OneLogin root = meta_doc.add_element("md:EntityDescriptor", namespaces) root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid 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 end diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/response.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/response.rb index 73e7e52..b356f0a 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/response.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/response.rb @@ -741,7 +741,7 @@ module OneLogin # @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 # - def validate_session_expiration(soft = true) + def validate_session_expiration return true if session_expires_at.nil? now = Time.now.utc diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/saml_message.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/saml_message.rb index d09f55e..bd88c7e 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/saml_message.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/saml_message.rb @@ -4,7 +4,6 @@ require 'base64' require 'nokogiri' require 'rexml/document' require 'rexml/xpath' -require 'thread' require "onelogin/ruby-saml/error_handling" # Only supports SAML 2.0 @@ -69,14 +68,14 @@ module OneLogin xml = Nokogiri::XML(document.to_s) do |config| config.options = XMLSecurity::BaseDocument::NOKOGIRI_OPTIONS end - rescue Exception => error + rescue StandardError => error return false if soft raise ValidationError.new("XML load failed: #{error.message}") end SamlMessage.schema.validate(xml).map do |schema_error| 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 diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/settings.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/settings.rb index ba49821..1c61fde 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/settings.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/settings.rb @@ -20,7 +20,7 @@ module OneLogin end config.each do |k,v| - acc = "#{k.to_s}=".to_sym + acc = "#{k}=".to_sym if respond_to? acc value = v.is_a?(Hash) ? v.dup : v send(acc, value) @@ -195,17 +195,13 @@ module OneLogin certs = {:signing => [], :encryption => [] } - if idp_cert_multi.key?(:signing) and not idp_cert_multi[:signing].empty? - idp_cert_multi[:signing].each do |idp_cert| - formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert) - certs[:signing].push(OpenSSL::X509::Certificate.new(formatted_cert)) - end - end + [:signing, :encryption].each do |type| + certs_for_type = idp_cert_multi[type] || idp_cert_multi[type.to_s] + next if !certs_for_type || certs_for_type.empty? - if idp_cert_multi.key?(:encryption) and not idp_cert_multi[:encryption].empty? - idp_cert_multi[:encryption].each do |idp_cert| + certs_for_type.each do |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 @@ -247,7 +243,6 @@ module OneLogin OpenSSL::PKey::RSA.new(formatted_private_key) end - private def idp_binding_from_embed_sign security[:embed_sign] ? Utils::BINDINGS[:post] : Utils::BINDINGS[:redirect] diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/slo_logoutresponse.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/slo_logoutresponse.rb index 838c1ca..b3f2243 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/slo_logoutresponse.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/slo_logoutresponse.rb @@ -41,7 +41,7 @@ module OneLogin saml_response = CGI.escape(params.delete("SAMLResponse")) response_params = "#{params_prefix}SAMLResponse=#{saml_response}" 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 raise SettingError.new "Invalid settings, idp_slo_service_url is not set!" if url.nil? or url.empty? diff --git a/ruby-saml-custom/lib/onelogin/ruby-saml/version.rb b/ruby-saml-custom/lib/onelogin/ruby-saml/version.rb index 4ec6097..5bb2a4e 100644 --- a/ruby-saml-custom/lib/onelogin/ruby-saml/version.rb +++ b/ruby-saml-custom/lib/onelogin/ruby-saml/version.rb @@ -1,5 +1,5 @@ module OneLogin module RubySaml - VERSION = '1.14.0' + VERSION = '1.15.0' end end diff --git a/ruby-saml-custom/lib/ruby-saml-custom.rb b/ruby-saml-custom/lib/ruby-saml.rb similarity index 100% rename from ruby-saml-custom/lib/ruby-saml-custom.rb rename to ruby-saml-custom/lib/ruby-saml.rb diff --git a/ruby-saml-custom/lib/xml_security.rb b/ruby-saml-custom/lib/xml_security.rb index 3d9e662..1b1b322 100644 --- a/ruby-saml-custom/lib/xml_security.rb +++ b/ruby-saml-custom/lib/xml_security.rb @@ -177,7 +177,7 @@ module XMLSecurity def compute_digest(document, digest_algorithm) digest = digest_algorithm.digest(document) - Base64.encode64(digest).strip! + Base64.encode64(digest).strip end end @@ -216,7 +216,7 @@ module XMLSecurity if options[:fingerprint_alg] fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(options[:fingerprint_alg]).new else - fingerprint_alg = OpenSSL::Digest::SHA1.new + fingerprint_alg = OpenSSL::Digest.new('SHA1') end fingerprint = fingerprint_alg.hexdigest(cert.to_der) diff --git a/ruby-saml-custom/ruby-saml-custom.gemspec b/ruby-saml-custom/ruby-saml-custom.gemspec index f79ea90..153d438 100644 --- a/ruby-saml-custom/ruby-saml-custom.gemspec +++ b/ruby-saml-custom/ruby-saml-custom.gemspec @@ -6,17 +6,17 @@ Gem::Specification.new do |s| s.version = OneLogin::RubySaml::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.description = %q{SAML toolkit for Ruby on Rails} - s.email = %q{support@onelogin.com} + s.description = %q{SAML Ruby toolkit. Add SAML support to your Ruby software using this library} s.license = 'MIT' s.extra_rdoc_files = [ "LICENSE", "README.md" ] 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.require_paths = ["lib"] 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 # have liked to constrain Ruby 1.8.7 to install only the 1.5.x versions. 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('jruby-openssl', '>= 0.9.8') 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 - s.add_runtime_dependency('nokogiri', '>= 1.8.2') + s.add_runtime_dependency('nokogiri', '>= 1.13.10') + s.add_runtime_dependency('rexml') end elsif RUBY_VERSION < '1.9' 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('json', '< 2.3.0') 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 - s.add_runtime_dependency('nokogiri', '>= 1.10.5') + s.add_runtime_dependency('nokogiri', '~> 1.8.5') s.add_runtime_dependency('rexml') 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('mocha', '~> 0.14') - s.add_development_dependency('rake', '~> 10') + + if RUBY_VERSION < '2.0' + 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('simplecov') s.add_development_dependency('systemu', '~> 2') if RUBY_VERSION < '2.1' diff --git a/ruby-saml-custom/test/logoutrequest_test.rb b/ruby-saml-custom/test/logoutrequest_test.rb index 4e37aef..fe32e22 100644 --- a/ruby-saml-custom/test/logoutrequest_test.rb +++ b/ruby-saml-custom/test/logoutrequest_test.rb @@ -14,18 +14,18 @@ class RequestTest < Minitest::Test it "create the deflated SAMLRequest URL parameter" do 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) - assert_match /^ nil }) - assert_match /&hello=$/, unauth_url + assert_match(/&hello=$/, unauth_url) unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" }) - assert_match /&foo=bar$/, unauth_url + assert_match(/&foo=bar$/, unauth_url) end it "RelayState cases" do @@ -50,7 +50,7 @@ class RequestTest < Minitest::Test unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" }) inflated = decode_saml_request_payload(unauth_url) - assert_match /), inflated end @@ -62,14 +62,14 @@ class RequestTest < Minitest::Test unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" }) inflated = decode_saml_request_payload(unauth_url) - assert_match /), inflated end describe "when the target url doesn't contain a query string" do it "create the SAMLRequest parameter correctly" do 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 @@ -78,7 +78,7 @@ class RequestTest < Minitest::Test settings.idp_slo_service_url = "http://example.com?field=value" 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 @@ -98,13 +98,13 @@ class RequestTest < Minitest::Test it "creates request with ID prefixed with default '_'" do request = OneLogin::RubySaml::Logoutrequest.new - assert_match /^_/, request.uuid + assert_match(/^_/, request.uuid) end it "creates request with ID is prefixed, when :id_prefix is passed" do OneLogin::RubySaml::Utils::set_prefix("test") request = OneLogin::RubySaml::Logoutrequest.new - assert_match /^test/, request.uuid + assert_match(/^test/, request.uuid) OneLogin::RubySaml::Utils::set_prefix("_") end end diff --git a/ruby-saml-custom/test/metadata_test.rb b/ruby-saml-custom/test/metadata_test.rb index 9f9f3d1..b2cdae5 100644 --- a/ruby-saml-custom/test/metadata_test.rb +++ b/ruby-saml-custom/test/metadata_test.rb @@ -83,7 +83,7 @@ class MetadataTest < Minitest::Test assert_equal xml_metadata[0..start.length-1],start 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 end diff --git a/ruby-saml-custom/test/request_test.rb b/ruby-saml-custom/test/request_test.rb index b2c061a..5936cc9 100644 --- a/ruby-saml-custom/test/request_test.rb +++ b/ruby-saml-custom/test/request_test.rb @@ -14,7 +14,7 @@ class RequestTest < Minitest::Test it "create the deflated SAMLRequest URL parameter" do 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) decoded = Base64.decode64(payload) @@ -23,7 +23,7 @@ class RequestTest < Minitest::Test zstream.finish zstream.close - assert_match /^ "there" }) - assert_match /&hello=there$/, auth_url + assert_match(/&hello=there$/, auth_url) auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => nil }) - assert_match /&hello=$/, auth_url + assert_match(/&hello=$/, auth_url) end it "RelayState cases" do @@ -164,13 +164,13 @@ class RequestTest < Minitest::Test it "creates request with ID prefixed with default '_'" do request = OneLogin::RubySaml::Authrequest.new - assert_match /^_/, request.uuid + assert_match(/^_/, request.uuid) end it "creates request with ID is prefixed, when :id_prefix is passed" do OneLogin::RubySaml::Utils::set_prefix("test") request = OneLogin::RubySaml::Authrequest.new - assert_match /^test/, request.uuid + assert_match(/^test/, request.uuid) OneLogin::RubySaml::Utils::set_prefix("_") end @@ -183,7 +183,7 @@ class RequestTest < Minitest::Test err = assert_raises OneLogin::RubySaml::SettingError do OneLogin::RubySaml::Authrequest.new.create(settings) 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 @@ -191,7 +191,7 @@ class RequestTest < Minitest::Test it "create the SAMLRequest parameter correctly" do 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 @@ -200,42 +200,42 @@ class RequestTest < Minitest::Test settings.idp_sso_service_url = "http://example.com?field=value" 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 it "create the saml:AuthnContextClassRef element correctly" do settings.authn_context = 'secure/name/password/uri' auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) - assert_match /secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s + assert_match(/secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s) end it "create multiple saml:AuthnContextClassRef elements correctly" do settings.authn_context = ['secure/name/password/uri', 'secure/email/password/uri'] auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) - assert_match /secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s - assert_match /secure\/email\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s + assert_match(/secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s) + assert_match(/secure\/email\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s) end it "create the saml:AuthnContextClassRef with comparison exact" do settings.authn_context = 'secure/name/password/uri' auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) - assert_match /secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s + assert_match(/secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s) end it "create the saml:AuthnContextClassRef with comparison minimun" do settings.authn_context = 'secure/name/password/uri' settings.authn_context_comparison = 'minimun' auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings) - assert_match /secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s + assert_match(/secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/, auth_doc.to_s) end it "create the saml:AuthnContextDeclRef element correctly" do 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) - assert_match /urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/, auth_doc.to_s + assert_match(/urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/, auth_doc.to_s) end describe "#create_params signing with HTTP-POST binding" do diff --git a/ruby-saml-custom/test/response_test.rb b/ruby-saml-custom/test/response_test.rb index a51f348..aaa55a7 100644 --- a/ruby-saml-custom/test/response_test.rb +++ b/ruby-saml-custom/test/response_test.rb @@ -411,7 +411,7 @@ class RubySamlTest < Minitest::Test response.settings = settings error_msg = "Current time is on or after NotOnOrAfter condition" 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 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 error_msg = "Unsupported SAML version" response_no_version.is_valid? - assert_includes response_no_version.errors, "Unsupported SAML version" + assert_includes response_no_version.errors, error_msg end it "return true when a nil URI is given in the ds:Reference" do @@ -976,7 +976,17 @@ class RubySamlTest < Minitest::Test :encryption => [] } 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 end @@ -1217,7 +1227,7 @@ class RubySamlTest < Minitest::Test settings.private_key = nil 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 - attrs = response_encrypted_attrs.attributes + response_encrypted_attrs.attributes end end @@ -1355,6 +1365,7 @@ class RubySamlTest < Minitest::Test describe '#xpath_first_from_signed_assertion' do it 'not allow arbitrary code execution' do + $evalled = nil malicious_response_document = fixture('response_eval', false) malicious_response = OneLogin::RubySaml::Response.new(malicious_response_document) 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" 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 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 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" 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 @@ -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" 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 diff --git a/ruby-saml-custom/test/settings_test.rb b/ruby-saml-custom/test/settings_test.rb index 1882645..6d14d25 100644 --- a/ruby-saml-custom/test/settings_test.rb +++ b/ruby-saml-custom/test/settings_test.rb @@ -257,11 +257,54 @@ class SettingsTest < Minitest::Test 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 + it "returns partial hash when contains some values with string keys" do + empty_multi = { + :signing => [], + :encryption => [] + } + @settings.idp_cert_multi = { - :signing => [ruby_saml_cert_text], - :encryption => [ruby_saml_cert_text], + "signing" => [] + } + 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 diff --git a/ruby-saml-custom/test/slo_logoutrequest_test.rb b/ruby-saml-custom/test/slo_logoutrequest_test.rb index 617ec22..5825a62 100644 --- a/ruby-saml-custom/test/slo_logoutrequest_test.rb +++ b/ruby-saml-custom/test/slo_logoutrequest_test.rb @@ -170,7 +170,7 @@ class RubySamlTest < Minitest::Test Timecop.freeze Time.parse('2014-07-17T01:01:49Z') do logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z' 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 diff --git a/ruby-saml-custom/test/slo_logoutresponse_test.rb b/ruby-saml-custom/test/slo_logoutresponse_test.rb index c80d11b..e79bbe1 100644 --- a/ruby-saml-custom/test/slo_logoutresponse_test.rb +++ b/ruby-saml-custom/test/slo_logoutresponse_test.rb @@ -20,21 +20,21 @@ class SloLogoutresponseTest < Minitest::Test it "create the deflated SAMLResponse URL parameter" do 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) - assert_match /^ 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" }) - 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" }) - assert_match /&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url + assert_match(/&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url) end it "RelayState cases" do @@ -55,45 +55,45 @@ class SloLogoutresponseTest < Minitest::Test unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id) 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 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") inflated = decode_saml_response_payload(unauth_url) - assert_match /Custom Logout Message<\/samlp:StatusMessage>/, inflated + assert_match(/Custom Logout Message<\/samlp:StatusMessage>/, inflated) end 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") inflated = decode_saml_response_payload(unauth_url) - assert_match /Custom Logout Message<\/samlp:StatusMessage>/, inflated - assert_match /([a-zA-Z0-9/+=]+)], response_xml - assert_match //, response_xml - assert_match //, response_xml + assert_match(//, response_xml) + assert_match(//, response_xml) end 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"]) assert_match %r[([a-zA-Z0-9/+=]+)], response_xml - assert_match //, response_xml - assert_match //, response_xml + assert_match(//, response_xml) + assert_match(//, response_xml) end 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"]) assert_match %r[([a-zA-Z0-9/+=]+)], response_xml - assert_match //, response_xml - assert_match //, response_xml + assert_match(//, response_xml) + assert_match(//, response_xml) end end diff --git a/ruby-saml-custom/test/test_helper.rb b/ruby-saml-custom/test/test_helper.rb index e81f6ea..83dd904 100644 --- a/ruby-saml-custom/test/test_helper.rb +++ b/ruby-saml-custom/test/test_helper.rb @@ -1,7 +1,10 @@ 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 add_filter "test/" add_filter "vendor/" diff --git a/ruby-saml-custom/test/utils_test.rb b/ruby-saml-custom/test/utils_test.rb index e603d80..36d0bd9 100644 --- a/ruby-saml-custom/test/utils_test.rb +++ b/ruby-saml-custom/test/utils_test.rb @@ -183,18 +183,25 @@ class UtilsTest < Minitest::Test end 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" 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_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) - assert_equal = "The status code of the Logout Response was not Success, was Requester", status_error_msg2 + it "returns a error msg with status_code" do + 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) - assert_equal = "The status code of the Logout Response was not Success", status_error_msg3 + it "returns a error msg" do + 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 @@ -202,7 +209,7 @@ class UtilsTest < Minitest::Test describe ".uuid" 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 it "doesn't return the same value twice" do diff --git a/sso_login_box.gemspec b/sso_login_box.gemspec index 23ad0f0..efef997 100644 --- a/sso_login_box.gemspec +++ b/sso_login_box.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |s| s.summary = "Summary of SsoLoginBox." s.description = "Description of SsoLoginBox." 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.test_files = Dir["test/**/*"] end