Skip to content

Commit

Permalink
Add namespace alias OneLogin = Object
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyshields committed Jul 8, 2024
1 parent 680b654 commit 9f12376
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### 2.0.0
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Remove `OneLogin` namespace. The root namespace of the gem is now `RubySaml`.
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Create namespace alias `OneLogin = Object` for backward compatibility, to be removed in version `2.1.0`.
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Change directly structure from `lib/onelogin/ruby-saml` to `lib/ruby_saml`.
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Move schema files from `lib/onelogin/schemas` to `lib/ruby_saml/schemas`.

Expand Down
7 changes: 7 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@

## Updating from 1.17.x to 2.0.0

### Before Upgrading

Before attempting to upgrade to `2.0.0`:
- Upgrade your project to minimum Ruby 3.0, JRuby 9.4, or TruffleRuby 22.
- Upgrade RubySaml to `1.17.x`. Note that RubySaml `1.17.x` is compatible with up to Ruby 3.3.

### Root Namespace Changed to RubySaml

RubySaml version `2.0.0` changes the root namespace from `OneLogin::RubySaml::` to just `RubySaml::`. This will require you
to search your codebase for the string `OneLogin::` and remove it as appropriate. Aside from this namespace change,
the class names themselves have intentionally been kept the same.

For backward compatibility, the alias `OneLogin = Object` has been set, so `OneLogin::RubySaml::` will still work.
This alias will be removed in RubySaml version `2.1.0`.

## Updating from 1.12.x to 1.13.0

Version `1.13.0` adds `settings.idp_sso_service_binding` and `settings.idp_slo_service_binding`, and
Expand Down
3 changes: 3 additions & 0 deletions lib/ruby_saml.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@
require 'ruby_saml/idp_metadata_parser'
require 'ruby_saml/utils'
require 'ruby_saml/version'

# @deprecated This alias will be removed in version 2.1.0
OneLogin = Object
207 changes: 207 additions & 0 deletions test/onelogin_alias_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
require_relative 'test_helper'

require 'ruby_saml/metadata'

class OneloginAliasTest < Minitest::Test

describe 'legacy OneLogin namespace alias' do

describe 'equality with Object' do
it "should be equal" do
assert_equal OneLogin, Object
assert_equal ::OneLogin, Object
assert_equal OneLogin::RubySaml, OneLogin::RubySaml
assert_equal ::OneLogin::RubySaml, ::RubySaml
end
end

describe 'Metadata' do
let(:settings) { OneLogin::RubySaml::Settings.new }
let(:xml_text) { OneLogin::RubySaml::Metadata.new.generate(settings, false) }
let(:xml_doc) { REXML::Document.new(xml_text) }
let(:spsso_descriptor) { REXML::XPath.first(xml_doc, "//md:SPSSODescriptor") }
let(:acs) { REXML::XPath.first(xml_doc, "//md:AssertionConsumerService") }

before do
settings.sp_entity_id = "https://example.com"
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
end

it "generates Pretty Print Service Provider Metadata" do
xml_text = OneLogin::RubySaml::Metadata.new.generate(settings, true)
# assert correct xml declaration
start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
assert_equal xml_text[0..start.length-1],start
assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", spsso_descriptor.attribute("protocolSupportEnumeration").value
assert_equal "false", spsso_descriptor.attribute("AuthnRequestsSigned").value
assert_equal "false", spsso_descriptor.attribute("WantAssertionsSigned").value
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", REXML::XPath.first(xml_doc, "//md:NameIDFormat").text.strip
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
end
end

describe 'Attributes' do
let(:attributes) do
OneLogin::RubySaml::Attributes.new({
'email' => ['[email protected]'],
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname' => ['Tom'],
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname' => ['Hanks']
})
end

it 'fetches attributes' do
assert_equal('[email protected]', attributes.fetch('email'))
assert_equal('[email protected]', attributes.fetch(:email))
assert_equal('Tom', attributes.fetch(/givenname/))
assert_equal('Tom', attributes.fetch(/gi(.*)/))
assert_nil(attributes.fetch(/^z.*/))
assert_equal('Hanks', attributes.fetch(/surname/))
end
end

describe "Response" do
let(:settings) { OneLogin::RubySaml::Settings.new }
let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
let(:response_with_multiple_attribute_statements) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_statements)) }
let(:response_without_reference_uri) { OneLogin::RubySaml::Response.new(response_document_without_reference_uri) }
let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
let(:response_with_ds_namespace_at_the_root) { OneLogin::RubySaml::Response.new(response_document_with_ds_namespace_at_the_root)}
let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
let(:response_multiple_attr_values) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) }
let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
let(:response_valid_signed_without_recipient) { OneLogin::RubySaml::Response.new(response_document_valid_signed, {:skip_recipient_check => true })}
let(:response_valid_signed_without_x509certificate) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) }
let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) }
let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
let(:response_no_conditions) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64")) }
let(:response_no_conditions_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64"), { :skip_conditions => true }) }
let(:response_no_authnstatement) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64")) }
let(:response_no_authnstatement_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64"), {:skip_authnstatement => true}) }
let(:response_empty_destination) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64")) }
let(:response_empty_destination_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64"), {:skip_destination => true}) }
let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) }
let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) }
let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) }
let(:response_statuscode_responder_and_msg) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responer_and_msg.xml.base64")) }
let(:response_double_statuscode) { OneLogin::RubySaml::Response.new(response_document_double_status_code) }
let(:response_encrypted_attrs) { OneLogin::RubySaml::Response.new(response_document_encrypted_attrs) }
let(:response_no_signed_elements) { OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) }
let(:response_multiple_signed) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) }
let(:response_audience_self_closed) { OneLogin::RubySaml::Response.new(read_response("response_audience_self_closed_tag.xml.base64")) }
let(:response_invalid_audience) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64")) }
let(:response_invalid_audience_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64"), {:skip_audience => true}) }
let(:response_invalid_signed_element) { OneLogin::RubySaml::Response.new(read_invalid_response("response_invalid_signed_element.xml.base64")) }
let(:response_invalid_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64")) }
let(:response_invalid_issuer_message) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64")) }
let(:response_no_issuer_response) { OneLogin::RubySaml::Response.new(read_invalid_response("no_issuer_response.xml.base64")) }
let(:response_no_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("no_issuer_assertion.xml.base64")) }
let(:response_no_nameid) { OneLogin::RubySaml::Response.new(read_invalid_response("no_nameid.xml.base64")) }
let(:response_empty_nameid) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_nameid.xml.base64")) }
let(:response_wrong_spnamequalifier) { OneLogin::RubySaml::Response.new(read_invalid_response("wrong_spnamequalifier.xml.base64")) }
let(:response_duplicated_attributes) { OneLogin::RubySaml::Response.new(read_invalid_response("duplicated_attributes.xml.base64")) }
let(:response_no_subjectconfirmation_data) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_data.xml.base64")) }
let(:response_no_subjectconfirmation_method) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_method.xml.base64")) }
let(:response_invalid_subjectconfirmation_inresponse) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_inresponse.xml.base64")) }
let(:response_invalid_subjectconfirmation_recipient) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_recipient.xml.base64")) }
let(:response_invalid_subjectconfirmation_nb) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_nb.xml.base64")) }
let(:response_invalid_subjectconfirmation_noa) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64")) }
let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) }
let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) }

def generate_audience_error(expected, actual)
s = actual.count > 1 ? 's' : '';
return "Invalid Audience#{s}. The audience#{s} #{actual.join(',')}, did not match the expected audience #{expected}"
end

it "raise an exception when response is initialized with nil" do
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
end

it "not filter available options only" do
options = { :skip_destination => true, :foo => :bar }
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, options)
assert_includes response.options.keys, :skip_destination
assert_includes response.options.keys, :foo
end

it "be able to parse a document which contains ampersands" do
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)

ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document)
ampersands_response.settings = settings
ampersands_response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'

assert !ampersands_response.is_valid?
assert_includes ampersands_response.errors, "SAML Response must contain 1 assertion"
end

describe "Prevent node text with comment attack (VU#475445)" do
before do
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack.xml.base64'))
end

it "receives the full NameID when there is an injected comment" do
assert_equal "[email protected]", @response.name_id
end

it "receives the full AttributeValue when there is an injected comment" do
assert_equal "smith", @response.attributes["surname"]
end
end

describe "Another test to prevent with comment attack (VU#475445)" do
before do
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack2.xml.base64'), {:skip_recipient_check => true })
@response.settings = settings
@response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
end

it "receives the full NameID when there is an injected comment, validates the response" do
assert_equal "[email protected]", @response.name_id
end
end

describe "Another test with CDATA injected" do
before do
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack3.xml.base64'), {:skip_recipient_check => true })
@response.settings = settings
@response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
end

it "it normalizes CDATA but reject SAMLResponse due signature invalidation" do
assert_equal "[email protected]", @response.name_id
assert !@response.is_valid?
assert_includes @response.errors, "Invalid Signature on SAML Response"
end
end

describe "Prevent XEE attack" do
before do
@response = OneLogin::RubySaml::Response.new(fixture(:attackxee))
end

it "false when evil attack vector is present, soft = true" do
@response.soft = true
assert !@response.send(:validate_structure)
assert_includes @response.errors, "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
end

it "raise when evil attack vector is present, soft = false " do
@response.soft = false

assert_raises(OneLogin::RubySaml::ValidationError) do
@response.send(:validate_structure)
end
end
end
end
end
end

0 comments on commit 9f12376

Please sign in to comment.