lyrebird 1.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,243 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "test_helper"
4
- require "onelogin/ruby-saml"
5
-
6
- module Lyrebird
7
- class IntegrationTest < Minitest::Test
8
- def setup
9
- unless defined?(@@idp_cert)
10
- @@idp_cert = Certificate.build
11
- @@sp_cert = Certificate.build
12
- end
13
-
14
- @idp_cert = @@idp_cert
15
- @sp_cert = @@sp_cert
16
- end
17
-
18
- def test_ruby_saml_parses_signed_response
19
- name_id = "user@example.com"
20
-
21
- response = Response.build(sign_with: @idp_cert) do |r|
22
- r.name_id = name_id
23
- end
24
-
25
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
26
- s.idp_cert = @idp_cert.x509_pem
27
- s.sp_entity_id = DEFAULTS.audience
28
- s.assertion_consumer_service_url = DEFAULTS.recipient
29
- end
30
-
31
- saml_response = OneLogin::RubySaml::Response.new(
32
- response.mimic,
33
- settings: settings
34
- )
35
-
36
- assert saml_response.is_valid?, saml_response.errors
37
- assert_equal name_id, saml_response.nameid
38
- end
39
-
40
- def test_ruby_saml_decrypts_signed_and_encrypted_response
41
- name_id = "user@example.com"
42
- email = "test@example.com"
43
- role = "admin"
44
-
45
- response = Response.build(
46
- sign_with: @idp_cert,
47
- encrypt_with: @sp_cert
48
- ) do |r|
49
- r.name_id = name_id
50
-
51
- r.attributes do |a|
52
- a.email = email
53
- a.role = role
54
- end
55
- end
56
-
57
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
58
- s.idp_cert = @idp_cert.x509_pem
59
- s.private_key = @sp_cert.key_pem
60
- s.sp_entity_id = DEFAULTS.audience
61
- s.assertion_consumer_service_url = DEFAULTS.recipient
62
- end
63
-
64
- saml_response = OneLogin::RubySaml::Response.new(
65
- response.mimic,
66
- settings: settings
67
- )
68
-
69
- assert saml_response.is_valid?, saml_response.errors
70
- assert_equal name_id, saml_response.nameid
71
- assert_equal email, saml_response.attributes["email"]
72
- assert_equal role, saml_response.attributes["role"]
73
- end
74
-
75
- def test_ruby_saml_parses_idp_initiated_response
76
- name_id = "user@example.com"
77
- email = "test@example.com"
78
-
79
- response = Response.build(sign_with: @idp_cert) do |r|
80
- r.in_response_to = nil
81
- r.destination = nil
82
- r.name_id = name_id
83
-
84
- r.attributes do |a|
85
- a.email = email
86
- end
87
- end
88
-
89
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
90
- s.idp_cert = @idp_cert.x509_pem
91
- s.sp_entity_id = DEFAULTS.audience
92
- s.assertion_consumer_service_url = DEFAULTS.recipient
93
- end
94
-
95
- saml_response = OneLogin::RubySaml::Response.new(
96
- response.mimic,
97
- settings: settings
98
- )
99
-
100
- assert saml_response.is_valid?, saml_response.errors
101
- assert_equal name_id, saml_response.nameid
102
- assert_equal email, saml_response.attributes["email"]
103
- end
104
-
105
- def test_ruby_saml_parses_multi_value_attributes
106
- name_id = "user@example.com"
107
- groups = ["admin", "users", "developers"]
108
- roles = ["editor", "viewer"]
109
-
110
- response = Response.build(sign_with: @idp_cert) do |r|
111
- r.name_id = name_id
112
-
113
- r.attributes do |a|
114
- a.groups = groups
115
- a.roles = roles
116
- end
117
- end
118
-
119
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
120
- s.idp_cert = @idp_cert.x509_pem
121
- s.sp_entity_id = DEFAULTS.audience
122
- s.assertion_consumer_service_url = DEFAULTS.recipient
123
- end
124
-
125
- saml_response = OneLogin::RubySaml::Response.new(
126
- response.mimic,
127
- settings: settings
128
- )
129
-
130
- assert saml_response.is_valid?, saml_response.errors
131
- assert_equal name_id, saml_response.nameid
132
- assert_equal groups, saml_response.attributes.multi("groups")
133
- assert_equal roles, saml_response.attributes.multi("roles")
134
- end
135
-
136
- def test_ruby_saml_validates_custom_validity_period
137
- name_id = "user@example.com"
138
- not_before = Time.now.utc - 60
139
- valid_for = 600
140
-
141
- response = Response.build(sign_with: @idp_cert) do |r|
142
- r.name_id = name_id
143
- r.not_before = not_before
144
- r.valid_for = valid_for
145
- end
146
-
147
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
148
- s.idp_cert = @idp_cert.x509_pem
149
- s.sp_entity_id = DEFAULTS.audience
150
- s.assertion_consumer_service_url = DEFAULTS.recipient
151
- end
152
-
153
- saml_response = OneLogin::RubySaml::Response.new(
154
- response.mimic,
155
- settings: settings
156
- )
157
-
158
- assert saml_response.is_valid?, saml_response.errors
159
- assert_equal name_id, saml_response.nameid
160
- end
161
-
162
- def test_ruby_saml_rejects_expired_assertion
163
- name_id = "user@example.com"
164
-
165
- response = Response.build(sign_with: @idp_cert) do |r|
166
- r.name_id = name_id
167
- r.valid_for = -10
168
- end
169
-
170
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
171
- s.idp_cert = @idp_cert.x509_pem
172
- s.sp_entity_id = DEFAULTS.audience
173
- s.assertion_consumer_service_url = DEFAULTS.recipient
174
- end
175
-
176
- saml_response = OneLogin::RubySaml::Response.new(
177
- response.mimic,
178
- settings: settings
179
- )
180
-
181
- assert_operator Time.now.utc, :>=, saml_response.not_on_or_after
182
- refute saml_response.is_valid?
183
- end
184
-
185
- def test_ruby_saml_rejects_not_yet_valid_assertion
186
- name_id = "user@example.com"
187
- not_before = Time.now.utc + 600
188
-
189
- response = Response.build(sign_with: @idp_cert) do |r|
190
- r.name_id = name_id
191
- r.not_before = not_before
192
- end
193
-
194
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
195
- s.idp_cert = @idp_cert.x509_pem
196
- s.sp_entity_id = DEFAULTS.audience
197
- s.assertion_consumer_service_url = DEFAULTS.recipient
198
- end
199
-
200
- saml_response = OneLogin::RubySaml::Response.new(
201
- response.mimic,
202
- settings: settings
203
- )
204
-
205
- assert_operator Time.now.utc, :<, saml_response.not_before
206
- refute saml_response.is_valid?
207
- end
208
-
209
- def test_ruby_saml_rejects_tampered_signed_response
210
- original_email = "user@example.com"
211
- tampered_email = "attacker@evil.com"
212
-
213
- response = Response.build(sign_with: @idp_cert) do |r|
214
- r.name_id = "user@example.com"
215
-
216
- r.attributes do |a|
217
- a.email = original_email
218
- a.role = "user"
219
- end
220
- end
221
-
222
- doc = Nokogiri::XML(Base64.strict_decode64(response.mimic))
223
- xpath = "//saml:Attribute[@Name='email']/saml:AttributeValue"
224
- email_attr = doc.at_xpath(xpath, { "saml" => SAML_ASSERTION_NS })
225
- email_attr.content = tampered_email
226
- tampered = Base64.strict_encode64(doc.to_xml(save_with: 0))
227
-
228
- settings = OneLogin::RubySaml::Settings.new.tap do |s|
229
- s.idp_cert = @idp_cert.x509_pem
230
- s.sp_entity_id = DEFAULTS.audience
231
- s.assertion_consumer_service_url = DEFAULTS.recipient
232
- end
233
-
234
- saml_response = OneLogin::RubySaml::Response.new(
235
- tampered,
236
- settings: settings
237
- )
238
-
239
- refute saml_response.is_valid?
240
- assert_includes saml_response.errors.join(" "), "Signature"
241
- end
242
- end
243
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "test_helper"
4
-
5
- module Lyrebird
6
- class NamespacesTest < Minitest::Test
7
- def test_saml_assertion_ns
8
- assert_equal "urn:oasis:names:tc:SAML:2.0:assertion", SAML_ASSERTION_NS
9
- end
10
-
11
- def test_saml_protocol_ns
12
- assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", SAML_PROTOCOL_NS
13
- end
14
-
15
- def test_xmldsig_ns
16
- assert_equal "http://www.w3.org/2000/09/xmldsig#", XMLDSIG_NS
17
- end
18
-
19
- def test_enveloped_sig
20
- expected = "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
21
- assert_equal expected, ENVELOPED_SIG
22
- end
23
-
24
- def test_exc_c14n
25
- assert_equal "http://www.w3.org/2001/10/xml-exc-c14n#", EXC_C14N
26
- end
27
-
28
- def test_sha256_digest
29
- assert_equal "http://www.w3.org/2001/04/xmlenc#sha256", SHA256_DIGEST
30
- end
31
-
32
- def test_rsa_sha256
33
- expected = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
34
- assert_equal expected, RSA_SHA256
35
- end
36
-
37
- def test_cm_bearer
38
- assert_equal "urn:oasis:names:tc:SAML:2.0:cm:bearer", CM_BEARER
39
- end
40
-
41
- def test_attr_name_format
42
- expected = "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"
43
- assert_equal expected, ATTR_NAME_FORMAT
44
- end
45
-
46
- def test_status_success
47
- assert_equal "urn:oasis:names:tc:SAML:2.0:status:Success", STATUS_SUCCESS
48
- end
49
-
50
- def test_xmlenc_ns
51
- assert_equal "http://www.w3.org/2001/04/xmlenc#", XMLENC_NS
52
- end
53
-
54
- def test_aes256_cbc
55
- assert_equal "http://www.w3.org/2001/04/xmlenc#aes256-cbc", AES256_CBC
56
- end
57
-
58
- def test_rsa_oaep
59
- expected = "http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"
60
- assert_equal expected, RSA_OAEP
61
- end
62
- end
63
- end
@@ -1,258 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "test_helper"
4
-
5
- module Lyrebird
6
- class ResponseTest < Minitest::Test
7
- NAMESPACES = {
8
- "saml" => SAML_ASSERTION_NS,
9
- "samlp" => SAML_PROTOCOL_NS,
10
- "ds" => XMLDSIG_NS,
11
- "xenc" => XMLENC_NS
12
- }.freeze
13
-
14
- def setup
15
- @response = Response.new
16
- @root = @response.document.root
17
- end
18
-
19
- def test_build_with_defaults
20
- response = Response.build
21
- root = response.document.root
22
- issuer = root.at_xpath("saml:Issuer", NAMESPACES)
23
- assert_equal DEFAULTS.issuer, issuer.text
24
- end
25
-
26
- def test_build_with_kwargs
27
- issuer = "https://test.example.com"
28
- refute_equal issuer, DEFAULTS.issuer
29
- response = Response.build(issuer: issuer)
30
- root = response.document.root
31
- assert_equal issuer, root.at_xpath("saml:Issuer", NAMESPACES).text
32
- end
33
-
34
- def test_build_with_block
35
- issuer = "https://test.example.com"
36
- refute_equal issuer, DEFAULTS.issuer
37
- response = Response.build { |r| r.issuer = issuer }
38
- root = response.document.root
39
- assert_equal issuer, root.at_xpath("saml:Issuer", NAMESPACES).text
40
- end
41
-
42
- def test_build_with_kwargs_and_block
43
- issuer = "https://test.example.com"
44
- email = "test@example.com"
45
-
46
- refute_equal issuer, DEFAULTS.issuer
47
- refute_equal email, DEFAULTS.name_id
48
-
49
- response = Response.build(issuer: issuer) do |r|
50
- r.name_id = email
51
- end
52
-
53
- root = response.document.root
54
- assert_equal issuer, root.at_xpath("saml:Issuer", NAMESPACES).text
55
- xpath = "saml:Assertion/saml:Subject/saml:NameID"
56
- assert_equal email, root.at_xpath(xpath, NAMESPACES).text
57
- end
58
-
59
- def test_build_with_attributes_block
60
- email = "test@example.com"
61
- role = "admin"
62
-
63
- response = Response.build do |r|
64
- r.attributes do |a|
65
- a.email = email
66
- a.role = role
67
- end
68
- end
69
-
70
- root = response.document.root
71
- statement_xpath = "saml:Assertion/saml:AttributeStatement"
72
- statement = root.at_xpath(statement_xpath, NAMESPACES)
73
- email_xpath = "saml:Attribute[@Name='email']/saml:AttributeValue"
74
- role_xpath = "saml:Attribute[@Name='role']/saml:AttributeValue"
75
- email_element = statement.at_xpath(email_xpath, NAMESPACES)
76
- role_element = statement.at_xpath(role_xpath, NAMESPACES)
77
-
78
- assert_equal email, email_element.text
79
- assert_equal role, role_element.text
80
- end
81
-
82
- def test_build_with_attributes_hash
83
- email = "user@example.com"
84
-
85
- response = Response.build do |r|
86
- r.attributes = { email: email }
87
- end
88
-
89
- root = response.document.root
90
- statement_xpath = "saml:Assertion/saml:AttributeStatement"
91
- attr_xpath = "saml:Attribute[@Name='email']/saml:AttributeValue"
92
- statement = root.at_xpath(statement_xpath, NAMESPACES)
93
- assert_equal email, statement.at_xpath(attr_xpath, NAMESPACES).text
94
- end
95
-
96
- def test_mimic_returns_base64
97
- encoded = @response.mimic
98
- decoded = Base64.strict_decode64(encoded)
99
- assert decoded.include?("<samlp:Response")
100
- assert decoded.include?("<saml:Assertion")
101
- end
102
-
103
- def test_document_is_memoized
104
- assert_same @response.document, @response.document
105
- end
106
-
107
- def test_mimic_is_stable
108
- assert_equal @response.mimic, @response.mimic
109
- end
110
-
111
- def test_root_name
112
- assert_equal "Response", @root.name
113
- assert_equal "samlp", @root.namespace.prefix
114
- end
115
-
116
- def test_root_namespace
117
- assert_equal SAML_PROTOCOL_NS, @root.namespace.href
118
- end
119
-
120
- def test_saml_namespace_declared
121
- assert_equal SAML_ASSERTION_NS, @root.namespaces["xmlns:saml"]
122
- end
123
-
124
- def test_root_id
125
- assert @root["ID"].start_with?("_")
126
- end
127
-
128
- def test_root_version
129
- assert_equal "2.0", @root["Version"]
130
- end
131
-
132
- def test_root_issue_instant
133
- instant = Time.iso8601(@root["IssueInstant"])
134
- assert_in_delta Time.now.to_i, instant.to_i, 1
135
- end
136
-
137
- def test_destination_default
138
- assert_equal DEFAULTS.recipient, @root["Destination"]
139
- end
140
-
141
- def test_destination_override
142
- destination = "https://test.example.com/acs"
143
- refute_equal destination, DEFAULTS.recipient
144
- root = Response.new(destination: destination).document.root
145
- assert_equal destination, root["Destination"]
146
- end
147
-
148
- def test_in_response_to_default
149
- assert_equal DEFAULTS.in_response_to, @root["InResponseTo"]
150
- end
151
-
152
- def test_in_response_to_override
153
- in_response_to = "_test_request"
154
- refute_equal in_response_to, DEFAULTS.in_response_to
155
- root = Response.new(in_response_to: in_response_to).document.root
156
- assert_equal in_response_to, root["InResponseTo"]
157
- end
158
-
159
- def test_destination_omitted_when_nil
160
- root = Response.new(destination: nil).document.root
161
- assert_nil root["Destination"]
162
- end
163
-
164
- def test_in_response_to_omitted_when_nil
165
- root = Response.new(in_response_to: nil).document.root
166
- assert_nil root["InResponseTo"]
167
- end
168
-
169
- def test_issuer
170
- issuer = @root.at_xpath("saml:Issuer", NAMESPACES)
171
- assert_equal "Issuer", issuer.name
172
- assert_equal "saml", issuer.namespace.prefix
173
- assert_equal DEFAULTS.issuer, issuer.text
174
- end
175
-
176
- def test_issuer_override
177
- url = "https://test.idp.example.com"
178
- refute_equal url, DEFAULTS.issuer
179
- root = Response.new(issuer: url).document.root
180
- issuer = root.at_xpath("saml:Issuer", NAMESPACES)
181
- assert_equal url, issuer.text
182
- end
183
-
184
- def test_status
185
- status = @root.at_xpath("samlp:Status", NAMESPACES)
186
- assert_equal "Status", status.name
187
- assert_equal "samlp", status.namespace.prefix
188
- end
189
-
190
- def test_status_code
191
- status_code = @root.at_xpath("samlp:Status/samlp:StatusCode", NAMESPACES)
192
- assert_equal "StatusCode", status_code.name
193
- assert_equal "samlp", status_code.namespace.prefix
194
- assert_equal STATUS_SUCCESS, status_code["Value"]
195
- end
196
-
197
- def test_assertion_embedded
198
- assertion = @root.at_xpath("saml:Assertion", NAMESPACES)
199
- assert_equal "Assertion", assertion.name
200
- assert_equal "saml", assertion.namespace.prefix
201
- end
202
-
203
- def test_assertion_has_id
204
- assertion = @root.at_xpath("saml:Assertion", NAMESPACES)
205
- assert assertion["ID"].start_with?("_")
206
- end
207
-
208
- def test_assertion_inherits_issuer
209
- url = "https://test.idp.example.com"
210
- refute_equal url, DEFAULTS.issuer
211
- root = Response.new(issuer: url).document.root
212
- issuer = root.at_xpath("saml:Assertion/saml:Issuer", NAMESPACES)
213
- assert_equal url, issuer.text
214
- end
215
-
216
- def test_assertion_options_flow_through
217
- email = "test@example.com"
218
- refute_equal email, DEFAULTS.name_id
219
- root = Response.new(name_id: email).document.root
220
- xpath = "saml:Assertion/saml:Subject/saml:NameID"
221
- assert_equal email, root.at_xpath(xpath, NAMESPACES).text
222
- end
223
-
224
- def test_unsigned_by_default
225
- assert_nil @root.at_xpath("ds:Signature", NAMESPACES)
226
- assert_nil @root.at_xpath("saml:Assertion/ds:Signature", NAMESPACES)
227
- end
228
-
229
- def test_sign_with_signs_response_and_assertion
230
- root = Response.new(sign_with: Certificate.build).document.root
231
- refute_nil root.at_xpath("ds:Signature", NAMESPACES)
232
- refute_nil root.at_xpath("saml:Assertion/ds:Signature", NAMESPACES)
233
- end
234
-
235
- def test_not_encrypted_by_default
236
- assert_nil @root.at_xpath("saml:EncryptedAssertion", NAMESPACES)
237
- end
238
-
239
- def test_encrypt_with_creates_encrypted_assertion
240
- root = Response.new(encrypt_with: Certificate.build).document.root
241
- ea = root.at_xpath("saml:EncryptedAssertion", NAMESPACES)
242
- assert_equal "EncryptedAssertion", ea.name
243
- assert_equal "saml", ea.namespace.prefix
244
- end
245
-
246
- def test_encrypt_with_removes_plain_assertion
247
- root = Response.new(encrypt_with: Certificate.build).document.root
248
- assert_nil root.at_xpath("saml:Assertion", NAMESPACES)
249
- end
250
-
251
- def test_encrypt_with_contains_encrypted_data
252
- root = Response.new(encrypt_with: Certificate.build).document.root
253
- xpath = "saml:EncryptedAssertion/xenc:EncryptedData"
254
- ed = root.at_xpath(xpath, NAMESPACES)
255
- assert_equal "EncryptedData", ed.name
256
- end
257
- end
258
- end