Skip to content

Commit 9f12376

Browse files
committed
Add namespace alias OneLogin = Object
1 parent 680b654 commit 9f12376

File tree

4 files changed

+218
-0
lines changed

4 files changed

+218
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
### 2.0.0
44
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Remove `OneLogin` namespace. The root namespace of the gem is now `RubySaml`.
5+
* [#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`.
56
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Change directly structure from `lib/onelogin/ruby-saml` to `lib/ruby_saml`.
67
* [#685](https://github.com/SAML-Toolkits/ruby-saml/pull/685) Move schema files from `lib/onelogin/schemas` to `lib/ruby_saml/schemas`.
78

UPGRADING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,21 @@
22

33
## Updating from 1.17.x to 2.0.0
44

5+
### Before Upgrading
6+
57
Before attempting to upgrade to `2.0.0`:
68
- Upgrade your project to minimum Ruby 3.0, JRuby 9.4, or TruffleRuby 22.
79
- Upgrade RubySaml to `1.17.x`. Note that RubySaml `1.17.x` is compatible with up to Ruby 3.3.
810

11+
### Root Namespace Changed to RubySaml
12+
913
RubySaml version `2.0.0` changes the root namespace from `OneLogin::RubySaml::` to just `RubySaml::`. This will require you
1014
to search your codebase for the string `OneLogin::` and remove it as appropriate. Aside from this namespace change,
1115
the class names themselves have intentionally been kept the same.
1216

17+
For backward compatibility, the alias `OneLogin = Object` has been set, so `OneLogin::RubySaml::` will still work.
18+
This alias will be removed in RubySaml version `2.1.0`.
19+
1320
## Updating from 1.12.x to 1.13.0
1421

1522
Version `1.13.0` adds `settings.idp_sso_service_binding` and `settings.idp_slo_service_binding`, and

lib/ruby_saml.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717
require 'ruby_saml/idp_metadata_parser'
1818
require 'ruby_saml/utils'
1919
require 'ruby_saml/version'
20+
21+
# @deprecated This alias will be removed in version 2.1.0
22+
OneLogin = Object

test/onelogin_alias_test.rb

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
require_relative 'test_helper'
2+
3+
require 'ruby_saml/metadata'
4+
5+
class OneloginAliasTest < Minitest::Test
6+
7+
describe 'legacy OneLogin namespace alias' do
8+
9+
describe 'equality with Object' do
10+
it "should be equal" do
11+
assert_equal OneLogin, Object
12+
assert_equal ::OneLogin, Object
13+
assert_equal OneLogin::RubySaml, OneLogin::RubySaml
14+
assert_equal ::OneLogin::RubySaml, ::RubySaml
15+
end
16+
end
17+
18+
describe 'Metadata' do
19+
let(:settings) { OneLogin::RubySaml::Settings.new }
20+
let(:xml_text) { OneLogin::RubySaml::Metadata.new.generate(settings, false) }
21+
let(:xml_doc) { REXML::Document.new(xml_text) }
22+
let(:spsso_descriptor) { REXML::XPath.first(xml_doc, "//md:SPSSODescriptor") }
23+
let(:acs) { REXML::XPath.first(xml_doc, "//md:AssertionConsumerService") }
24+
25+
before do
26+
settings.sp_entity_id = "https://example.com"
27+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
28+
settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
29+
end
30+
31+
it "generates Pretty Print Service Provider Metadata" do
32+
xml_text = OneLogin::RubySaml::Metadata.new.generate(settings, true)
33+
# assert correct xml declaration
34+
start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
35+
assert_equal xml_text[0..start.length-1],start
36+
assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
37+
assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", spsso_descriptor.attribute("protocolSupportEnumeration").value
38+
assert_equal "false", spsso_descriptor.attribute("AuthnRequestsSigned").value
39+
assert_equal "false", spsso_descriptor.attribute("WantAssertionsSigned").value
40+
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", REXML::XPath.first(xml_doc, "//md:NameIDFormat").text.strip
41+
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
42+
assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
43+
assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
44+
end
45+
end
46+
47+
describe 'Attributes' do
48+
let(:attributes) do
49+
OneLogin::RubySaml::Attributes.new({
50+
'email' => ['tom@hanks.com'],
51+
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname' => ['Tom'],
52+
'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname' => ['Hanks']
53+
})
54+
end
55+
56+
it 'fetches attributes' do
57+
assert_equal('tom@hanks.com', attributes.fetch('email'))
58+
assert_equal('tom@hanks.com', attributes.fetch(:email))
59+
assert_equal('Tom', attributes.fetch(/givenname/))
60+
assert_equal('Tom', attributes.fetch(/gi(.*)/))
61+
assert_nil(attributes.fetch(/^z.*/))
62+
assert_equal('Hanks', attributes.fetch(/surname/))
63+
end
64+
end
65+
66+
describe "Response" do
67+
let(:settings) { OneLogin::RubySaml::Settings.new }
68+
let(:response) { OneLogin::RubySaml::Response.new(response_document_without_recipient) }
69+
let(:response_without_attributes) { OneLogin::RubySaml::Response.new(response_document_without_attributes) }
70+
let(:response_with_multiple_attribute_statements) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_statements)) }
71+
let(:response_without_reference_uri) { OneLogin::RubySaml::Response.new(response_document_without_reference_uri) }
72+
let(:response_with_signed_assertion) { OneLogin::RubySaml::Response.new(response_document_with_signed_assertion) }
73+
let(:response_with_ds_namespace_at_the_root) { OneLogin::RubySaml::Response.new(response_document_with_ds_namespace_at_the_root)}
74+
let(:response_unsigned) { OneLogin::RubySaml::Response.new(response_document_unsigned) }
75+
let(:response_wrapped) { OneLogin::RubySaml::Response.new(response_document_wrapped) }
76+
let(:response_multiple_attr_values) { OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values)) }
77+
let(:response_valid_signed) { OneLogin::RubySaml::Response.new(response_document_valid_signed) }
78+
let(:response_valid_signed_without_recipient) { OneLogin::RubySaml::Response.new(response_document_valid_signed, {:skip_recipient_check => true })}
79+
let(:response_valid_signed_without_x509certificate) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate) }
80+
let(:response_no_id) { OneLogin::RubySaml::Response.new(read_invalid_response("no_id.xml.base64")) }
81+
let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
82+
let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
83+
let(:response_no_conditions) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64")) }
84+
let(:response_no_conditions_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64"), { :skip_conditions => true }) }
85+
let(:response_no_authnstatement) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64")) }
86+
let(:response_no_authnstatement_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64"), {:skip_authnstatement => true}) }
87+
let(:response_empty_destination) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64")) }
88+
let(:response_empty_destination_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64"), {:skip_destination => true}) }
89+
let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) }
90+
let(:response_no_statuscode) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status_code.xml.base64")) }
91+
let(:response_statuscode_responder) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responder.xml.base64")) }
92+
let(:response_statuscode_responder_and_msg) { OneLogin::RubySaml::Response.new(read_invalid_response("status_code_responer_and_msg.xml.base64")) }
93+
let(:response_double_statuscode) { OneLogin::RubySaml::Response.new(response_document_double_status_code) }
94+
let(:response_encrypted_attrs) { OneLogin::RubySaml::Response.new(response_document_encrypted_attrs) }
95+
let(:response_no_signed_elements) { OneLogin::RubySaml::Response.new(read_invalid_response("no_signature.xml.base64")) }
96+
let(:response_multiple_signed) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64")) }
97+
let(:response_audience_self_closed) { OneLogin::RubySaml::Response.new(read_response("response_audience_self_closed_tag.xml.base64")) }
98+
let(:response_invalid_audience) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64")) }
99+
let(:response_invalid_audience_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_audience.xml.base64"), {:skip_audience => true}) }
100+
let(:response_invalid_signed_element) { OneLogin::RubySaml::Response.new(read_invalid_response("response_invalid_signed_element.xml.base64")) }
101+
let(:response_invalid_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64")) }
102+
let(:response_invalid_issuer_message) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64")) }
103+
let(:response_no_issuer_response) { OneLogin::RubySaml::Response.new(read_invalid_response("no_issuer_response.xml.base64")) }
104+
let(:response_no_issuer_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("no_issuer_assertion.xml.base64")) }
105+
let(:response_no_nameid) { OneLogin::RubySaml::Response.new(read_invalid_response("no_nameid.xml.base64")) }
106+
let(:response_empty_nameid) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_nameid.xml.base64")) }
107+
let(:response_wrong_spnamequalifier) { OneLogin::RubySaml::Response.new(read_invalid_response("wrong_spnamequalifier.xml.base64")) }
108+
let(:response_duplicated_attributes) { OneLogin::RubySaml::Response.new(read_invalid_response("duplicated_attributes.xml.base64")) }
109+
let(:response_no_subjectconfirmation_data) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_data.xml.base64")) }
110+
let(:response_no_subjectconfirmation_method) { OneLogin::RubySaml::Response.new(read_invalid_response("no_subjectconfirmation_method.xml.base64")) }
111+
let(:response_invalid_subjectconfirmation_inresponse) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_inresponse.xml.base64")) }
112+
let(:response_invalid_subjectconfirmation_recipient) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_recipient.xml.base64")) }
113+
let(:response_invalid_subjectconfirmation_nb) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_nb.xml.base64")) }
114+
let(:response_invalid_subjectconfirmation_noa) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_subjectconfirmation_noa.xml.base64")) }
115+
let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) }
116+
let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) }
117+
118+
def generate_audience_error(expected, actual)
119+
s = actual.count > 1 ? 's' : '';
120+
return "Invalid Audience#{s}. The audience#{s} #{actual.join(',')}, did not match the expected audience #{expected}"
121+
end
122+
123+
it "raise an exception when response is initialized with nil" do
124+
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
125+
end
126+
127+
it "not filter available options only" do
128+
options = { :skip_destination => true, :foo => :bar }
129+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, options)
130+
assert_includes response.options.keys, :skip_destination
131+
assert_includes response.options.keys, :foo
132+
end
133+
134+
it "be able to parse a document which contains ampersands" do
135+
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
136+
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
137+
138+
ampersands_response = OneLogin::RubySaml::Response.new(ampersands_document)
139+
ampersands_response.settings = settings
140+
ampersands_response.settings.idp_cert_fingerprint = 'c51985d947f1be57082025050846eb27f6cab783'
141+
142+
assert !ampersands_response.is_valid?
143+
assert_includes ampersands_response.errors, "SAML Response must contain 1 assertion"
144+
end
145+
146+
describe "Prevent node text with comment attack (VU#475445)" do
147+
before do
148+
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack.xml.base64'))
149+
end
150+
151+
it "receives the full NameID when there is an injected comment" do
152+
assert_equal "support@onelogin.com", @response.name_id
153+
end
154+
155+
it "receives the full AttributeValue when there is an injected comment" do
156+
assert_equal "smith", @response.attributes["surname"]
157+
end
158+
end
159+
160+
describe "Another test to prevent with comment attack (VU#475445)" do
161+
before do
162+
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack2.xml.base64'), {:skip_recipient_check => true })
163+
@response.settings = settings
164+
@response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
165+
end
166+
167+
it "receives the full NameID when there is an injected comment, validates the response" do
168+
assert_equal "test@onelogin.com", @response.name_id
169+
end
170+
end
171+
172+
describe "Another test with CDATA injected" do
173+
before do
174+
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack3.xml.base64'), {:skip_recipient_check => true })
175+
@response.settings = settings
176+
@response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
177+
end
178+
179+
it "it normalizes CDATA but reject SAMLResponse due signature invalidation" do
180+
assert_equal "test@onelogin.com.evil.com", @response.name_id
181+
assert !@response.is_valid?
182+
assert_includes @response.errors, "Invalid Signature on SAML Response"
183+
end
184+
end
185+
186+
describe "Prevent XEE attack" do
187+
before do
188+
@response = OneLogin::RubySaml::Response.new(fixture(:attackxee))
189+
end
190+
191+
it "false when evil attack vector is present, soft = true" do
192+
@response.soft = true
193+
assert !@response.send(:validate_structure)
194+
assert_includes @response.errors, "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
195+
end
196+
197+
it "raise when evil attack vector is present, soft = false " do
198+
@response.soft = false
199+
200+
assert_raises(OneLogin::RubySaml::ValidationError) do
201+
@response.send(:validate_structure)
202+
end
203+
end
204+
end
205+
end
206+
end
207+
end

0 commit comments

Comments
 (0)