From 14528d17efbcc1a0569284f2b4ce5eb39521db9f Mon Sep 17 00:00:00 2001 From: Ben Radler Date: Fri, 21 Feb 2014 12:10:34 -0800 Subject: [PATCH] Change namespacing from Onelogin::Saml to Onelogin::Rubysaml - add changelog - update readme.md with upgrading instructions --- README.md | 135 +++++++++++---------- changelog.md | 7 ++ lib/onelogin/ruby-saml/authrequest.rb | 2 +- lib/onelogin/ruby-saml/logging.rb | 2 +- lib/onelogin/ruby-saml/logoutrequest.rb | 2 +- lib/onelogin/ruby-saml/logoutresponse.rb | 2 +- lib/onelogin/ruby-saml/metadata.rb | 4 +- lib/onelogin/ruby-saml/response.rb | 2 +- lib/onelogin/ruby-saml/settings.rb | 2 +- lib/onelogin/ruby-saml/validation_error.rb | 2 +- lib/onelogin/ruby-saml/version.rb | 4 +- lib/xml_security.rb | 8 +- ruby-saml.gemspec | 2 +- test/logoutrequest_test.rb | 30 ++--- test/logoutresponse_test.rb | 38 +++--- test/request_test.rb | 34 +++--- test/response_test.rb | 104 ++++++++-------- test/responses/logoutresponse_fixtures.rb | 4 +- test/settings_test.rb | 4 +- test/xml_security_test.rb | 18 +-- 20 files changed, 209 insertions(+), 197 deletions(-) create mode 100644 changelog.md diff --git a/README.md b/README.md index 7acb0c759..c2585a4d4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Ruby SAML [![Build Status](https://secure.travis-ci.org/onelogin/ruby-saml.png)](http://travis-ci.org/onelogin/ruby-saml) +## Updating from 0.7.x to 0.8.x +Version `0.8.0` changes the namespace of the gem from `Onelogin::Saml` to `Onelogin::RubySaml`. Please update your implementations of the gem accordingly. + +## Overview + The Ruby SAML library is for implementing the client side of a SAML authorization, i.e. it provides a means for managing authorization initialization and confirmation requests from identity providers. SAML authorization is a two step process and you are expected to implement support for both. @@ -9,92 +14,92 @@ SAML authorization is a two step process and you are expected to implement suppo This is the first request you will get from the identity provider. It will hit your application at a specific URL (that you've announced as being your SAML initialization point). The response to this initialization, is a redirect back to the identity provider, which can look something like this (ignore the saml_settings method call for now): ```ruby - def init - request = Onelogin::Saml::Authrequest.new - redirect_to(request.create(saml_settings)) - end +def init + request = Onelogin::RubySaml::Authrequest.new + redirect_to(request.create(saml_settings)) +end ``` Once you've redirected back to the identity provider, it will ensure that the user has been authorized and redirect back to your application for final consumption, this is can look something like this (the authorize_success and authorize_failure methods are specific to your application): ```ruby - def consume - response = Onelogin::Saml::Response.new(params[:SAMLResponse]) - response.settings = saml_settings - - if response.is_valid? && user = current_account.users.find_by_email(response.name_id) - authorize_success(user) - else - authorize_failure(user) - end - end +def consume + response = Onelogin::RubySaml::Response.new(params[:SAMLResponse]) + response.settings = saml_settings + + if response.is_valid? && user = current_account.users.find_by_email(response.name_id) + authorize_success(user) + else + authorize_failure(user) + end +end ``` In the above there are a few assumptions in place, one being that the response.name_id is an email address. This is all handled with how you specify the settings that are in play via the saml_settings method. That could be implemented along the lines of this: ```ruby - def saml_settings - settings = Onelogin::Saml::Settings.new - - settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize" - settings.issuer = request.host - settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}" - settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint - settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - # Optional for most SAML IdPs - settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" - - settings - end +def saml_settings + settings = Onelogin::RubySaml::Settings.new + + settings.assertion_consumer_service_url = "http://#{request.host}/saml/finalize" + settings.issuer = request.host + settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}" + settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint + settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" + # Optional for most SAML IdPs + settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" + + settings +end ``` What's left at this point, is to wrap it all up in a controller and point the initialization and consumption URLs in OneLogin at that. A full controller example could look like this: ```ruby - # This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application. - class SamlController < ApplicationController - def init - request = Onelogin::Saml::Authrequest.new - redirect_to(request.create(saml_settings)) - end +# This controller expects you to use the URLs /saml/init and /saml/consume in your OneLogin application. +class SamlController < ApplicationController + def init + request = Onelogin::RubySaml::Authrequest.new + redirect_to(request.create(saml_settings)) + end - def consume - response = Onelogin::Saml::Response.new(params[:SAMLResponse]) - response.settings = saml_settings + def consume + response = Onelogin::RubySaml::Response.new(params[:SAMLResponse]) + response.settings = saml_settings - if response.is_valid? && user = current_account.users.find_by_email(response.name_id) - authorize_success(user) - else - authorize_failure(user) - end + if response.is_valid? && user = current_account.users.find_by_email(response.name_id) + authorize_success(user) + else + authorize_failure(user) end + end - private + private - def saml_settings - settings = Onelogin::Saml::Settings.new + def saml_settings + settings = Onelogin::RubySaml::Settings.new - settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume" - settings.issuer = request.host - settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}" - settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint - settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - # Optional for most SAML IdPs - settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" + settings.assertion_consumer_service_url = "http://#{request.host}/saml/consume" + settings.issuer = request.host + settings.idp_sso_target_url = "https://app.onelogin.com/saml/signon/#{OneLoginAppId}" + settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint + settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" + # Optional for most SAML IdPs + settings.authn_context = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" - settings - end + settings end +end ``` If are using saml:AttributeStatement to transfare metadata, like the user name, you can access all the attributes through response.attributes. It contains all the saml:AttributeStatement with its 'Name' as a indifferent key and the one saml:AttributeValue as value. ```ruby - response = Onelogin::Saml::Response.new(params[:SAMLResponse]) - response.settings = saml_settings +response = Onelogin::RubySaml::Response.new(params[:SAMLResponse]) +response.settings = saml_settings - response.attributes[:username] +response.attributes[:username] ``` ## Service Provider Metadata @@ -102,20 +107,20 @@ contains all the saml:AttributeStatement with its 'Name' as a indifferent key an To form a trusted pair relationship with the IdP, the SP (you) need to provide metadata XML to the IdP for various good reasons. (Caching, certificate lookups, relaying party permissions, etc) -The class Onelogin::Saml::Metadata takes care of this by reading the Settings and returning XML. All +The class Onelogin::RubySaml::Metadata takes care of this by reading the Settings and returning XML. All you have to do is add a controller to return the data, then give this URL to the IdP administrator. The metdata will be polled by the IdP every few minutes, so updating your settings should propagate to the IdP settings. ```ruby - class SamlController < ApplicationController - # ... the rest of your controller definitions ... - def metadata - settings = Account.get_saml_settings - meta = Onelogin::Saml::Metadata.new - render :xml => meta.generate(settings) - end +class SamlController < ApplicationController + # ... the rest of your controller definitions ... + def metadata + settings = Account.get_saml_settings + meta = Onelogin::RubySaml::Metadata.new + render :xml => meta.generate(settings) end +end ``` ## Clock Drift @@ -127,7 +132,7 @@ First, ensure that both systems synchronize their clocks, using for example the Even then you may experience intermittent issues though, because the clock of the Identity Provider may drift slightly ahead of your system clocks. To allow for a small amount of clock drift you can initialize the response passing in an option named `:allowed_clock_drift`. Its value must be given in a number (and/or fraction) of seconds. The value given is added to the current time at which the response is validated before it's tested against the `NotBefore` assertion. For example: ```ruby - response = Onelogin::Saml::Response.new(params[:SAMLResponse], :allowed_clock_drift => 1) +response = Onelogin::RubySaml::Response.new(params[:SAMLResponse], :allowed_clock_drift => 1) ``` Make sure to keep the value as comfortably small as possible to keep security risks to a minimum. diff --git a/changelog.md b/changelog.md new file mode 100644 index 000000000..32f96988c --- /dev/null +++ b/changelog.md @@ -0,0 +1,7 @@ +# RubySaml Changelog + +### 0.8.0 (Feb 21, 2014) +Changed namespace of the gem from `Onelogin::Saml` to `Onelogin::RubySaml`. Please update your implementations of the gem accordingly. + +### 0.7.3 (Feb 20, 2014) +Updated gem dependencies to be compatible with Ruby 1.8.7-p374 and 1.9.3-p448. Removed unnecessary `canonix` gem dependency. diff --git a/lib/onelogin/ruby-saml/authrequest.rb b/lib/onelogin/ruby-saml/authrequest.rb index 0f744e5b2..8e86d154d 100644 --- a/lib/onelogin/ruby-saml/authrequest.rb +++ b/lib/onelogin/ruby-saml/authrequest.rb @@ -6,7 +6,7 @@ require "rexml/xpath" module Onelogin - module Saml + module RubySaml include REXML class Authrequest def create(settings, params = {}) diff --git a/lib/onelogin/ruby-saml/logging.rb b/lib/onelogin/ruby-saml/logging.rb index 331b1b7b5..47a820a10 100644 --- a/lib/onelogin/ruby-saml/logging.rb +++ b/lib/onelogin/ruby-saml/logging.rb @@ -1,6 +1,6 @@ # Simplistic log class when we're running in Rails module Onelogin - module Saml + module RubySaml class Logging def self.debug(message) return if !!ENV["ruby-saml/testing"] diff --git a/lib/onelogin/ruby-saml/logoutrequest.rb b/lib/onelogin/ruby-saml/logoutrequest.rb index 32a577bd5..a59610987 100644 --- a/lib/onelogin/ruby-saml/logoutrequest.rb +++ b/lib/onelogin/ruby-saml/logoutrequest.rb @@ -4,7 +4,7 @@ require "cgi" module Onelogin - module Saml + module RubySaml include REXML class Logoutrequest diff --git a/lib/onelogin/ruby-saml/logoutresponse.rb b/lib/onelogin/ruby-saml/logoutresponse.rb index e3e2ca178..a6a15fcbd 100644 --- a/lib/onelogin/ruby-saml/logoutresponse.rb +++ b/lib/onelogin/ruby-saml/logoutresponse.rb @@ -5,7 +5,7 @@ require "open-uri" module Onelogin - module Saml + module RubySaml class Logoutresponse ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion" diff --git a/lib/onelogin/ruby-saml/metadata.rb b/lib/onelogin/ruby-saml/metadata.rb index 46c67ea43..ed498ec57 100644 --- a/lib/onelogin/ruby-saml/metadata.rb +++ b/lib/onelogin/ruby-saml/metadata.rb @@ -3,11 +3,11 @@ require "uri" # Class to return SP metadata based on the settings requested. -# Return this XML in a controller, then give that URL to the the +# Return this XML in a controller, then give that URL to the the # IdP administrator. The IdP will poll the URL and your settings # will be updated automatically module Onelogin - module Saml + module RubySaml include REXML class Metadata def generate(settings) diff --git a/lib/onelogin/ruby-saml/response.rb b/lib/onelogin/ruby-saml/response.rb index 5927bf2fc..f300fca4b 100644 --- a/lib/onelogin/ruby-saml/response.rb +++ b/lib/onelogin/ruby-saml/response.rb @@ -4,7 +4,7 @@ # Only supports SAML 2.0 module Onelogin - module Saml + module RubySaml class Response ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion" diff --git a/lib/onelogin/ruby-saml/settings.rb b/lib/onelogin/ruby-saml/settings.rb index f4af8ef37..fcbe43d6b 100644 --- a/lib/onelogin/ruby-saml/settings.rb +++ b/lib/onelogin/ruby-saml/settings.rb @@ -1,5 +1,5 @@ module Onelogin - module Saml + module RubySaml class Settings def initialize(overrides = {}) config = DEFAULTS.merge(overrides) diff --git a/lib/onelogin/ruby-saml/validation_error.rb b/lib/onelogin/ruby-saml/validation_error.rb index 5bd839d80..b9d655b29 100644 --- a/lib/onelogin/ruby-saml/validation_error.rb +++ b/lib/onelogin/ruby-saml/validation_error.rb @@ -1,5 +1,5 @@ module Onelogin - module Saml + module RubySaml class ValidationError < StandardError end end diff --git a/lib/onelogin/ruby-saml/version.rb b/lib/onelogin/ruby-saml/version.rb index 2b87f853e..51177e290 100644 --- a/lib/onelogin/ruby-saml/version.rb +++ b/lib/onelogin/ruby-saml/version.rb @@ -1,5 +1,5 @@ module Onelogin - module Saml - VERSION = '0.7.3' + module RubySaml + VERSION = '0.8.0' end end diff --git a/lib/xml_security.rb b/lib/xml_security.rb index 403adc4a4..07d606a37 100644 --- a/lib/xml_security.rb +++ b/lib/xml_security.rb @@ -47,7 +47,7 @@ def initialize(response) def validate_document(idp_cert_fingerprint, soft = true) # get cert from response cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG }) - raise Onelogin::Saml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)") unless cert_element + raise Onelogin::RubySaml::ValidationError.new("Certificate element missing in response (ds:X509Certificate)") unless cert_element base64_cert = cert_element.text cert_text = Base64.decode64(base64_cert) cert = OpenSSL::X509::Certificate.new(cert_text) @@ -56,7 +56,7 @@ def validate_document(idp_cert_fingerprint, soft = true) fingerprint = Digest::SHA1.hexdigest(cert.to_der) if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase - return soft ? false : (raise Onelogin::Saml::ValidationError.new("Fingerprint mismatch")) + return soft ? false : (raise Onelogin::RubySaml::ValidationError.new("Fingerprint mismatch")) end validate_signature(base64_cert, soft) @@ -102,7 +102,7 @@ def validate_signature(base64_cert, soft = true) digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text) unless digests_match?(hash, digest_value) - return soft ? false : (raise Onelogin::Saml::ValidationError.new("Digest mismatch")) + return soft ? false : (raise Onelogin::RubySaml::ValidationError.new("Digest mismatch")) end end @@ -117,7 +117,7 @@ def validate_signature(base64_cert, soft = true) signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG})) unless cert.public_key.verify(signature_algorithm.new, signature, canon_string) - return soft ? false : (raise Onelogin::Saml::ValidationError.new("Key validation error")) + return soft ? false : (raise Onelogin::RubySaml::ValidationError.new("Key validation error")) end return true diff --git a/ruby-saml.gemspec b/ruby-saml.gemspec index a907df61f..5945aaee1 100644 --- a/ruby-saml.gemspec +++ b/ruby-saml.gemspec @@ -3,7 +3,7 @@ require 'onelogin/ruby-saml/version' Gem::Specification.new do |s| s.name = 'ruby-saml' - s.version = Onelogin::Saml::VERSION + s.version = Onelogin::RubySaml::VERSION s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= s.authors = ["OneLogin LLC"] diff --git a/test/logoutrequest_test.rb b/test/logoutrequest_test.rb index a60c5e56a..ac0e8ae96 100644 --- a/test/logoutrequest_test.rb +++ b/test/logoutrequest_test.rb @@ -3,13 +3,13 @@ class RequestTest < Test::Unit::TestCase context "Logoutrequest" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new should "create the deflated SAMLRequest URL parameter" do settings.idp_slo_target_url = "http://unauth.com/logout" settings.name_identifier_value = "f00f00" - unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings) + unauth_url = Onelogin::RubySaml::Logoutrequest.new.create(settings) assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLRequest=/ inflated = decode_saml_request_payload(unauth_url) @@ -19,10 +19,10 @@ class RequestTest < Test::Unit::TestCase should "support additional params" do - unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :hello => nil }) + unauth_url = Onelogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil }) assert unauth_url =~ /&hello=$/ - unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :foo => "bar" }) + unauth_url = Onelogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" }) assert unauth_url =~ /&foo=bar$/ end @@ -31,7 +31,7 @@ class RequestTest < Test::Unit::TestCase sessionidx = UUID.new.generate settings.sessionindex = sessionidx - unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :name_id => "there" }) + unauth_url = Onelogin::RubySaml::Logoutrequest.new.create(settings, { :name_id => "there" }) inflated = decode_saml_request_payload(unauth_url) assert_match / "there" }) + unauth_url = Onelogin::RubySaml::Logoutrequest.new.create(settings, { :name_id => "there" }) inflated = decode_saml_request_payload(unauth_url) assert_match / :bar} ) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} ) assert !logoutresponse.options.empty? end should "support base64 encoded responses" do expected_response = valid_response - logoutresponse = Onelogin::Saml::Logoutresponse.new(Base64.encode64(expected_response), settings) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings) assert_equal expected_response, logoutresponse.response end @@ -32,7 +32,7 @@ class RubySamlTest < Test::Unit::TestCase should "validate the response" do in_relation_to_request_id = random_id - logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings) assert logoutresponse.validate @@ -47,14 +47,14 @@ class RubySamlTest < Test::Unit::TestCase expected_request_id = "_some_other_expected_uuid" opts = { :matches_request_id => expected_request_id} - logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response, settings, opts) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(valid_response, settings, opts) assert !logoutresponse.validate assert_not_equal expected_request_id, logoutresponse.in_response_to end should "invalidate responses with wrong request status" do - logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response, settings) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings) assert !logoutresponse.validate assert !logoutresponse.success? @@ -65,7 +65,7 @@ class RubySamlTest < Test::Unit::TestCase should "validates good responses" do in_relation_to_request_id = random_id - logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings) logoutresponse.validate! end @@ -75,34 +75,34 @@ class RubySamlTest < Test::Unit::TestCase expected_request_id = "_some_other_expected_id" opts = { :matches_request_id => expected_request_id} - logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response, settings, opts) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(valid_response, settings, opts) - assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! } + assert_raises(Onelogin::RubySaml::ValidationError) { logoutresponse.validate! } end should "raise validation error for wrong request status" do - logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response, settings) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings) - assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! } + assert_raises(Onelogin::RubySaml::ValidationError) { logoutresponse.validate! } end should "raise validation error when in bad state" do # no settings - logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response) - assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! } + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(unsuccessful_response) + assert_raises(Onelogin::RubySaml::ValidationError) { logoutresponse.validate! } end should "raise validation error when in lack of issuer setting" do bad_settings = settings bad_settings.issuer = nil - logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response, bad_settings) - assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! } + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(unsuccessful_response, bad_settings) + assert_raises(Onelogin::RubySaml::ValidationError) { logoutresponse.validate! } end should "raise error for invalid xml" do - logoutresponse = Onelogin::Saml::Logoutresponse.new(invalid_xml_response, settings) + logoutresponse = Onelogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings) - assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! } + assert_raises(Onelogin::RubySaml::ValidationError) { logoutresponse.validate! } end end diff --git a/test/request_test.rb b/test/request_test.rb index a962c6189..ed2603d7c 100644 --- a/test/request_test.rb +++ b/test/request_test.rb @@ -4,9 +4,9 @@ class RequestTest < Test::Unit::TestCase context "Authrequest" do should "create the deflated SAMLRequest URL parameter" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_sso_target_url = "http://example.com" - auth_url = Onelogin::Saml::Authrequest.new.create(settings) + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings) assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/ payload = CGI.unescape(auth_url.split("=").last) decoded = Base64.decode64(payload) @@ -20,9 +20,9 @@ class RequestTest < Test::Unit::TestCase end should "create the deflated SAMLRequest URL parameter including the Destination" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_sso_target_url = "http://example.com" - auth_url = Onelogin::Saml::Authrequest.new.create(settings) + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings) payload = CGI.unescape(auth_url.split("=").last) decoded = Base64.decode64(payload) @@ -35,10 +35,10 @@ class RequestTest < Test::Unit::TestCase end should "create the SAMLRequest URL parameter without deflating" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.compress_request = false settings.idp_sso_target_url = "http://example.com" - auth_url = Onelogin::Saml::Authrequest.new.create(settings) + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings) assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/ payload = CGI.unescape(auth_url.split("=").last) decoded = Base64.decode64(payload) @@ -47,10 +47,10 @@ class RequestTest < Test::Unit::TestCase end should "create the SAMLRequest URL parameter with IsPassive" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_sso_target_url = "http://example.com" settings.passive = true - auth_url = Onelogin::Saml::Authrequest.new.create(settings) + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings) assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/ payload = CGI.unescape(auth_url.split("=").last) decoded = Base64.decode64(payload) @@ -64,32 +64,32 @@ class RequestTest < Test::Unit::TestCase end should "accept extra parameters" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_sso_target_url = "http://example.com" - auth_url = Onelogin::Saml::Authrequest.new.create(settings, { :hello => "there" }) + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" }) assert auth_url =~ /&hello=there$/ - auth_url = Onelogin::Saml::Authrequest.new.create(settings, { :hello => nil }) + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings, { :hello => nil }) assert auth_url =~ /&hello=$/ end context "when the target url doesn't contain a query string" do should "create the SAMLRequest parameter correctly" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_sso_target_url = "http://example.com" - - auth_url = Onelogin::Saml::Authrequest.new.create(settings) + + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings) assert auth_url =~ /^http:\/\/example.com\?SAMLRequest/ end end context "when the target url contains a query string" do should "create the SAMLRequest parameter correctly" do - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_sso_target_url = "http://example.com?field=value" - - auth_url = Onelogin::Saml::Authrequest.new.create(settings) + + auth_url = Onelogin::RubySaml::Authrequest.new.create(settings) assert auth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/ end end diff --git a/test/response_test.rb b/test/response_test.rb index cf5cb75d9..3928708bd 100644 --- a/test/response_test.rb +++ b/test/response_test.rb @@ -4,40 +4,40 @@ class RubySamlTest < Test::Unit::TestCase context "Response" do should "raise an exception when response is initialized with nil" do - assert_raises(ArgumentError) { Onelogin::Saml::Response.new(nil) } + assert_raises(ArgumentError) { Onelogin::RubySaml::Response.new(nil) } end should "be able to parse a document which contains ampersands" do XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true) - Onelogin::Saml::Response.any_instance.stubs(:validate_conditions).returns(true) + Onelogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true) - response = Onelogin::Saml::Response.new(ampersands_response) - settings = Onelogin::Saml::Settings.new + response = Onelogin::RubySaml::Response.new(ampersands_response) + settings = Onelogin::RubySaml::Settings.new settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783' response.settings = settings response.validate! end should "adapt namespace" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert !response.name_id.nil? - response = Onelogin::Saml::Response.new(response_document_2) + response = Onelogin::RubySaml::Response.new(response_document_2) assert !response.name_id.nil? - response = Onelogin::Saml::Response.new(response_document_3) + response = Onelogin::RubySaml::Response.new(response_document_3) assert !response.name_id.nil? end should "default to raw input when a response is not Base64 encoded" do decoded = Base64.decode64(response_document_2) - response = Onelogin::Saml::Response.new(decoded) + response = Onelogin::RubySaml::Response.new(decoded) assert response.document end context "Assertion" do should "only retreive an assertion with an ID that matches the signature's reference URI" do - response = Onelogin::Saml::Response.new(wrapped_response_2) + response = Onelogin::RubySaml::Response.new(wrapped_response_2) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_cert_fingerprint = signature_fingerprint_1 response.settings = settings assert response.name_id.nil? @@ -46,8 +46,8 @@ class RubySamlTest < Test::Unit::TestCase context "#validate!" do should "raise when encountering a condition that prevents the document from being valid" do - response = Onelogin::Saml::Response.new(response_document) - assert_raise(Onelogin::Saml::ValidationError) do + response = Onelogin::RubySaml::Response.new(response_document) + assert_raise(Onelogin::RubySaml::ValidationError) do response.validate! end end @@ -55,20 +55,20 @@ class RubySamlTest < Test::Unit::TestCase context "#is_valid?" do should "return false when response is initialized with blank data" do - response = Onelogin::Saml::Response.new('') + response = Onelogin::RubySaml::Response.new('') assert !response.is_valid? end should "return false if settings have not been set" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert !response.is_valid? end should "return true when the response is initialized with valid data" do - response = Onelogin::Saml::Response.new(response_document_4) + response = Onelogin::RubySaml::Response.new(response_document_4) response.stubs(:conditions).returns(nil) assert !response.is_valid? - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new assert !response.is_valid? response.settings = settings assert !response.is_valid? @@ -77,18 +77,18 @@ class RubySamlTest < Test::Unit::TestCase end should "should be idempotent when the response is initialized with invalid data" do - response = Onelogin::Saml::Response.new(response_document_4) + response = Onelogin::RubySaml::Response.new(response_document_4) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new response.settings = settings assert !response.is_valid? assert !response.is_valid? end should "should be idempotent when the response is initialized with valid data" do - response = Onelogin::Saml::Response.new(response_document_4) + response = Onelogin::RubySaml::Response.new(response_document_4) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new response.settings = settings settings.idp_cert_fingerprint = signature_fingerprint_1 assert response.is_valid? @@ -96,18 +96,18 @@ class RubySamlTest < Test::Unit::TestCase end should "return true when using certificate instead of fingerprint" do - response = Onelogin::Saml::Response.new(response_document_4) + response = Onelogin::RubySaml::Response.new(response_document_4) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new response.settings = settings settings.idp_cert = signature_1 assert response.is_valid? end should "not allow signature wrapping attack" do - response = Onelogin::Saml::Response.new(response_document_4) + response = Onelogin::RubySaml::Response.new(response_document_4) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_cert_fingerprint = signature_fingerprint_1 response.settings = settings assert response.is_valid? @@ -115,9 +115,9 @@ class RubySamlTest < Test::Unit::TestCase end should "support dynamic namespace resolution on signature elements" do - response = Onelogin::Saml::Response.new(fixture("no_signature_ns.xml")) + response = Onelogin::RubySaml::Response.new(fixture("no_signature_ns.xml")) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new response.settings = settings settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA" XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true) @@ -125,18 +125,18 @@ class RubySamlTest < Test::Unit::TestCase end should "validate ADFS assertions" do - response = Onelogin::Saml::Response.new(fixture(:adfs_response_sha256)) + response = Onelogin::RubySaml::Response.new(fixture(:adfs_response_sha256)) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA" response.settings = settings assert response.validate! end should "validate the digest" do - response = Onelogin::Saml::Response.new(r1_response_document_6) + response = Onelogin::RubySaml::Response.new(r1_response_document_6) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_cert = Base64.decode64(r1_signature_2) response.settings = settings assert response.validate! @@ -144,112 +144,112 @@ class RubySamlTest < Test::Unit::TestCase should "validate SAML 2.0 XML structure" do resp_xml = Base64.decode64(response_document_4).gsub(/emailAddress/,'test') - response = Onelogin::Saml::Response.new(Base64.encode64(resp_xml)) + response = Onelogin::RubySaml::Response.new(Base64.encode64(resp_xml)) response.stubs(:conditions).returns(nil) - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new settings.idp_cert_fingerprint = signature_fingerprint_1 response.settings = settings - assert_raises(Onelogin::Saml::ValidationError, 'Digest mismatch'){ response.validate! } + assert_raises(Onelogin::RubySaml::ValidationError, 'Digest mismatch'){ response.validate! } end end context "#name_id" do should "extract the value of the name id element" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert_equal "support@onelogin.com", response.name_id - response = Onelogin::Saml::Response.new(response_document_3) + response = Onelogin::RubySaml::Response.new(response_document_3) assert_equal "someone@example.com", response.name_id end should "be extractable from an OpenSAML response" do - response = Onelogin::Saml::Response.new(fixture(:open_saml)) + response = Onelogin::RubySaml::Response.new(fixture(:open_saml)) assert_equal "someone@example.org", response.name_id end should "be extractable from a Simple SAML PHP response" do - response = Onelogin::Saml::Response.new(fixture(:simple_saml_php)) + response = Onelogin::RubySaml::Response.new(fixture(:simple_saml_php)) assert_equal "someone@example.com", response.name_id end end context "#check_conditions" do should "check time conditions" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert !response.send(:validate_conditions, true) - response = Onelogin::Saml::Response.new(response_document_6) + response = Onelogin::RubySaml::Response.new(response_document_6) assert response.send(:validate_conditions, true) time = Time.parse("2011-06-14T18:25:01.516Z") Time.stubs(:now).returns(time) - response = Onelogin::Saml::Response.new(response_document_5) + response = Onelogin::RubySaml::Response.new(response_document_5) assert response.send(:validate_conditions, true) end should "optionally allow for clock drift" do # The NotBefore condition in the document is 2011-06-14T18:21:01.516Z Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z")) - response = Onelogin::Saml::Response.new(response_document_5, :allowed_clock_drift => 0.515) + response = Onelogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.515) assert !response.send(:validate_conditions, true) Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z")) - response = Onelogin::Saml::Response.new(response_document_5, :allowed_clock_drift => 0.516) + response = Onelogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.516) assert response.send(:validate_conditions, true) end end context "#attributes" do should "extract the first attribute in a hash accessed via its symbol" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert_equal "demo", response.attributes[:uid] end should "extract the first attribute in a hash accessed via its name" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert_equal "demo", response.attributes["uid"] end should "extract all attributes" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert_equal "demo", response.attributes[:uid] assert_equal "value", response.attributes[:another_value] end should "work for implicit namespaces" do - response = Onelogin::Saml::Response.new(response_document_3) + response = Onelogin::RubySaml::Response.new(response_document_3) assert_equal "someone@example.com", response.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"] end should "not raise on responses without attributes" do - response = Onelogin::Saml::Response.new(response_document_4) + response = Onelogin::RubySaml::Response.new(response_document_4) assert_equal Hash.new, response.attributes end end context "#session_expires_at" do should "extract the value of the SessionNotOnOrAfter attribute" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert response.session_expires_at.is_a?(Time) - response = Onelogin::Saml::Response.new(response_document_2) + response = Onelogin::RubySaml::Response.new(response_document_2) assert response.session_expires_at.nil? end end context "#issuer" do should "return the issuer inside the response assertion" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer end should "return the issuer inside the response" do - response = Onelogin::Saml::Response.new(response_document_2) + response = Onelogin::RubySaml::Response.new(response_document_2) assert_equal "wibble", response.issuer end end context "#success" do should "find a status code that says success" do - response = Onelogin::Saml::Response.new(response_document) + response = Onelogin::RubySaml::Response.new(response_document) response.success? end end diff --git a/test/responses/logoutresponse_fixtures.rb b/test/responses/logoutresponse_fixtures.rb index 18472a1bf..316a21da2 100644 --- a/test/responses/logoutresponse_fixtures.rb +++ b/test/responses/logoutresponse_fixtures.rb @@ -52,7 +52,7 @@ def invalid_xml_response end def settings - @settings ||= Onelogin::Saml::Settings.new( + @settings ||= Onelogin::RubySaml::Settings.new( { :assertion_consumer_service_url => "http://app.muda.no/sso/consume", :assertion_consumer_logout_service_url => "http://app.muda.no/sso/consume_logout", @@ -64,4 +64,4 @@ def settings :name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient", } ) -end \ No newline at end of file +end diff --git a/test/settings_test.rb b/test/settings_test.rb index 28f49a149..fd91b8fe5 100644 --- a/test/settings_test.rb +++ b/test/settings_test.rb @@ -4,7 +4,7 @@ class SettingsTest < Test::Unit::TestCase context "Settings" do setup do - @settings = Onelogin::Saml::Settings.new + @settings = Onelogin::RubySaml::Settings.new end should "should provide getters and settings" do accessors = [ @@ -35,7 +35,7 @@ class SettingsTest < Test::Unit::TestCase :passive => true, :protocol_binding => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' } - @settings = Onelogin::Saml::Settings.new(config) + @settings = Onelogin::RubySaml::Settings.new(config) config.each do |k,v| assert_equal v, @settings.send(k) diff --git a/test/xml_security_test.rb b/test/xml_security_test.rb index 01fe7a8fb..41e8d57f6 100644 --- a/test/xml_security_test.rb +++ b/test/xml_security_test.rb @@ -15,7 +15,7 @@ class XmlSecurityTest < Test::Unit::TestCase end should "should run validate with throwing NS related exceptions" do - assert_raise(Onelogin::Saml::ValidationError) do + assert_raise(Onelogin::RubySaml::ValidationError) do @document.validate_signature(@base64cert, false) end end @@ -27,14 +27,14 @@ class XmlSecurityTest < Test::Unit::TestCase end should "should raise Fingerprint mismatch" do - exception = assert_raise(Onelogin::Saml::ValidationError) do + exception = assert_raise(Onelogin::RubySaml::ValidationError) do @document.validate_document("no:fi:ng:er:pr:in:t", false) end assert_equal("Fingerprint mismatch", exception.message) end should "should raise Digest mismatch" do - exception = assert_raise(Onelogin::Saml::ValidationError) do + exception = assert_raise(Onelogin::RubySaml::ValidationError) do @document.validate_signature(@base64cert, false) end assert_equal("Digest mismatch", exception.message) @@ -46,7 +46,7 @@ class XmlSecurityTest < Test::Unit::TestCase "b9xsAXLsynugg3Wc1CI3kpWku+0=") document = XMLSecurity::SignedDocument.new(response) base64cert = document.elements["//ds:X509Certificate"].text - exception = assert_raise(Onelogin::Saml::ValidationError) do + exception = assert_raise(Onelogin::RubySaml::ValidationError) do document.validate_signature(base64cert, false) end assert_equal("Key validation error", exception.message) @@ -56,7 +56,7 @@ class XmlSecurityTest < Test::Unit::TestCase response = Base64.decode64(response_document) response.sub!(/.*<\/ds:X509Certificate>/, "") document = XMLSecurity::SignedDocument.new(response) - exception = assert_raise(Onelogin::Saml::ValidationError) do + exception = assert_raise(Onelogin::RubySaml::ValidationError) do document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test end assert_equal("Certificate element missing in response (ds:X509Certificate)", exception.message) @@ -106,10 +106,10 @@ class XmlSecurityTest < Test::Unit::TestCase should_eventually 'support inclusive canonicalization' do - response = Onelogin::Saml::Response.new(fixture("tdnf_response.xml")) + response = Onelogin::RubySaml::Response.new(fixture("tdnf_response.xml")) response.stubs(:conditions).returns(nil) assert !response.is_valid? - settings = Onelogin::Saml::Settings.new + settings = Onelogin::RubySaml::Settings.new assert !response.is_valid? response.settings = settings assert !response.is_valid? @@ -130,8 +130,8 @@ class XmlSecurityTest < Test::Unit::TestCase context "StarfieldTMS" do setup do - @response = Onelogin::Saml::Response.new(fixture(:starfield_response)) - @response.settings = Onelogin::Saml::Settings.new( + @response = Onelogin::RubySaml::Response.new(fixture(:starfield_response)) + @response.settings = Onelogin::RubySaml::Settings.new( :idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D" ) end