Compare commits

..

No commits in common. "91314493141c3a265b40f79c1f3ccb228ff061b6" and "e7ebebcd2e91108af7549e395623ae45bbcc297b" have entirely different histories.

27 changed files with 130 additions and 320 deletions

View File

@ -1,6 +0,0 @@
#!/bin/bash
dir="$(dirname $0)"
adfs_fingerprint=`openssl s_client -connect adfs.ntu.edu.tw:443 < /dev/null 2>/dev/null | openssl x509 -fingerprint -noout -in /dev/stdin| cut -d '=' -f2`
if [ ! -z "$adfs_fingerprint" ]; then
echo "$adfs_fingerprint" > "$dir/adfs_fingerprint.txt"
fi

View File

@ -4,7 +4,7 @@ class SsoLoginBoxController < SessionsController
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
def sso_auth_page def sso_auth_page
session[:referer_url] = params[:referer_url] session[:referer_url] = params[:referer_url]
# puts ["session", session, session.to_hash] puts ["session", session, session.to_hash]
request = OneLogin::RubySaml::Authrequest.new request = OneLogin::RubySaml::Authrequest.new
redirect_to(request.create(saml_settings)) redirect_to(request.create(saml_settings))
end end
@ -21,7 +21,7 @@ class SsoLoginBoxController < SessionsController
# We validate the SAML Response and check if the user already exists in the system # We validate the SAML Response and check if the user already exists in the system
if response.is_valid? if response.is_valid?
attributes = response.attributes attributes = response.attributes
# puts ["attributes", attributes.inspect] puts ["attributes", attributes.inspect]
if true#["f", "s"].include?(attributes["AccountTypeCode"]) if true#["f", "s"].include?(attributes["AccountTypeCode"])
email = attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"] email = attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
member_name = attributes["ChineseName"] member_name = attributes["ChineseName"]
@ -39,7 +39,6 @@ class SsoLoginBoxController < SessionsController
end end
end end
if !user.nil? if !user.nil?
puts "Login #{user.user_name} success by sso!"
session[:sso_token] = user.id session[:sso_token] = user.id
session[:user_id] = user.id session[:user_id] = user.id
session[:login_referer] = nil session[:login_referer] = nil
@ -98,13 +97,13 @@ class SsoLoginBoxController < SessionsController
def saml_settings def saml_settings
settings = OneLogin::RubySaml::Settings.new settings = OneLogin::RubySaml::Settings.new
request_host = request.host request_host = request.host
settings.assertion_consumer_service_url = "https://#{request_host}/ntu_sso/response" settings.assertion_consumer_service_url = "https://#{request_host}/ntu_sso/response?referer_url=#{params[:referer_url]}"
settings.issuer = request_host settings.issuer = request_host
settings.idp_sso_target_url = "https://adfs.ntu.edu.tw/adfs/ls/" settings.idp_sso_target_url = "https://adfs.ntu.edu.tw/adfs/ls/"
# settings.idp_sso_target_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect # settings.idp_sso_target_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" # or :post, :redirect
settings.idp_slo_target_url = "https://adfs.ntu.edu.tw/adfs/ls/clearall.aspx?url=https://#{request_host}" settings.idp_slo_target_url = "https://adfs.ntu.edu.tw/adfs/ls/clearall.aspx?url=https://#{request_host}"
# settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect # settings.idp_slo_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" # or :post, :redirect
settings.idp_cert_fingerprint = (File.read('adfs_fingerprint.txt') rescue '') #"0A:27:FC:D5:CE:DC:D8:44:CC:A9:58:8A:42:D1:F4:DF:38:2E:4A:C3" settings.idp_cert_fingerprint = "0a:27:fc:d5:ce:dc:d8:44:cc:a9:58:8a:42:d1:f4:df:38:2e:4a:c3"
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1" settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
# settings.security[:signature_method] = XMLSecurity::Document::SHA256 # settings.security[:signature_method] = XMLSecurity::Document::SHA256
# 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"

View File

@ -1,43 +0,0 @@
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,12 +1,4 @@
# 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,5 +1,4 @@
Copyright (c) 2010-2022 OneLogin, Inc. Copyright (c) 2010-2016 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,8 +1,6 @@
# Ruby SAML # Ruby SAML
[![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) [![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/github/SAML-Toolkits/ruby-saml/badge.svg?branch=master)](https://coveralls.io/github/SAML-Toolkits/ruby-saml?branch=master) [![Coverage Status](https://coveralls.io/repos/onelogin/ruby-saml/badge.svg?branch=master)](https://coveralls.io/r/onelogin/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.
@ -16,7 +14,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/saml-toolkits/ruby-saml-example) [ruby-saml-example](https://github.com/onelogin/ruby-saml-example)
### Supported Ruby Versions ### Supported Ruby Versions
@ -30,12 +28,8 @@ 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:
@ -58,7 +52,8 @@ 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
by mail to the maintainer: sixto.martin.garcia+security@gmail.com at https://www.onelogin.com/security with a description. We follow responsible disclosure
guidelines, and will work with you to quickly find a resolution.
### Security Warning ### Security Warning
@ -92,7 +87,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 => 'saml-toolkit/ruby-saml' gem 'ruby-saml', :github => 'onelogin/ruby-saml'
``` ```
Using RubyGems Using RubyGems
@ -397,51 +392,6 @@ 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}=#{CGI.escape(value.to_s)}" request_params << "&#{key.to_s}=#{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}=#{CGI.escape(value.to_s)}" request_params << "&#{key.to_s}=#{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.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if valid_until root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z') 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 def validate_session_expiration(soft = true)
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,6 +4,7 @@ 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
@ -68,14 +69,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 StandardError => error rescue Exception => 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}") raise ValidationError.new("#{schema_error.message}\n\n#{xml.to_s}")
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_sym acc = "#{k.to_s}=".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,13 +195,17 @@ module OneLogin
certs = {:signing => [], :encryption => [] } certs = {:signing => [], :encryption => [] }
[:signing, :encryption].each do |type| if idp_cert_multi.key?(:signing) and not idp_cert_multi[:signing].empty?
certs_for_type = idp_cert_multi[type] || idp_cert_multi[type.to_s] idp_cert_multi[:signing].each do |idp_cert|
next if !certs_for_type || certs_for_type.empty?
certs_for_type.each do |idp_cert|
formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert) formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
certs[type].push(OpenSSL::X509::Certificate.new(formatted_cert)) certs[:signing].push(OpenSSL::X509::Certificate.new(formatted_cert))
end
end
if idp_cert_multi.key?(:encryption) and not idp_cert_multi[:encryption].empty?
idp_cert_multi[:encryption].each do |idp_cert|
formatted_cert = OneLogin::RubySaml::Utils.format_cert(idp_cert)
certs[:encryption].push(OpenSSL::X509::Certificate.new(formatted_cert))
end end
end end
@ -243,6 +247,7 @@ 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}=#{CGI.escape(value.to_s)}" response_params << "&#{key.to_s}=#{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.15.0' VERSION = '1.14.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.new('SHA1') fingerprint_alg = OpenSSL::Digest::SHA1.new
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 = ["SAML Toolkit", "Sixto Martin"] s.authors = ["OneLogin LLC"]
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 Ruby toolkit. Add SAML support to your Ruby software using this library} s.description = %q{SAML toolkit for Ruby on Rails}
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/saml-toolkits/ruby-saml} s.homepage = %q{https://github.com/onelogin/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,18 +27,12 @@ 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.1.7.0' if JRUBY_VERSION < '9.2.0.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.13.10') s.add_runtime_dependency('nokogiri', '>= 1.8.2')
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')
@ -47,35 +41,18 @@ 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.8.5') s.add_runtime_dependency('nokogiri', '>= 1.7', '< 1.10.0')
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.8.5') s.add_runtime_dependency('nokogiri', '>= 1.10.5')
s.add_runtime_dependency('rexml') s.add_runtime_dependency('rexml')
end end
s.add_development_dependency('simplecov', '<0.22.0') s.add_development_dependency('coveralls')
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')
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('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:%SZ'), REXML::XPath.first(doc_metadata, "//md:EntityDescriptor").attribute("validUntil").value assert_equal valid_until.strftime('%Y-%m-%dT%H:%M:%S%z'), 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], error_msg assert_includes response.errors[0], "Current time is on or after NotOnOrAfter condition"
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, error_msg assert_includes response_no_version.errors, "Unsupported SAML version"
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,17 +976,7 @@ class RubySamlTest < Minitest::Test
:encryption => [] :encryption => []
} }
response_valid_signed.settings = settings response_valid_signed.settings = settings
response_valid_signed.send(:validate_signature) res = 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
@ -1227,7 +1217,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
response_encrypted_attrs.attributes attrs = response_encrypted_attrs.attributes
end end
end end
@ -1365,7 +1355,6 @@ 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)
@ -1448,11 +1437,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
OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion) response = 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
OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings) response2 = 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
@ -1470,7 +1459,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
OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings) response = OneLogin::RubySaml::Response.new(signed_message_encrypted_unsigned_assertion, :settings => settings)
end end
end end
@ -1564,7 +1553,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
response.send(:decrypt_assertion, encrypted_assertion_node) decrypted = response.send(:decrypt_assertion, encrypted_assertion_node)
end end
end end

View File

@ -257,54 +257,11 @@ 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 partial hash when contains some values with string keys" do
empty_multi = {
:signing => [],
:encryption => []
}
@settings.idp_cert_multi = {
"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 it "returns the hash with certificates when values were valid" do
certificates = [ruby_saml_cert_text] certificates = ruby_saml_cert_text
@settings.idp_cert_multi = { @settings.idp_cert_multi = {
:signing => certificates, :signing => [ruby_saml_cert_text],
:encryption => certificates, :encryption => [ruby_saml_cert_text],
}
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_match(/Current time is on or after NotOnOrAfter/, logout_request.errors[0]) assert /Current time is on or after NotOnOrAfter/.match(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,10 +1,7 @@
require 'simplecov' require 'simplecov'
require 'simplecov-lcov' require 'coveralls'
SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true SimpleCov.formatter = Coveralls::SimpleCov::Formatter
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,25 +183,18 @@ class UtilsTest < Minitest::Test
end end
describe "#status_error_msg" do describe "#status_error_msg" do
it "returns a error msg with status_code and status message" do it "returns a error msg with a 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
it "returns a error msg with status_code" do status_error_msg2 = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code)
error_msg = "The status code of the Logout Response was not Success" assert_equal = "The status code of the Logout Response was not Success, was Requester", status_error_msg2
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
it "returns a error msg" do status_error_msg3 = OneLogin::RubySaml::Utils.status_error_msg(error_msg)
error_msg = "The status code of the Logout Response was not Success" assert_equal = "The status code of the Logout Response was not Success", status_error_msg3
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
@ -209,7 +202,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-custom' s.add_dependency 'ruby-saml', '1.11.0'
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