lyrebird 1.0.0 → 2.0.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,250 +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_root_name
104
- assert_equal "Response", @root.name
105
- assert_equal "samlp", @root.namespace.prefix
106
- end
107
-
108
- def test_root_namespace
109
- assert_equal SAML_PROTOCOL_NS, @root.namespace.href
110
- end
111
-
112
- def test_saml_namespace_declared
113
- assert_equal SAML_ASSERTION_NS, @root.namespaces["xmlns:saml"]
114
- end
115
-
116
- def test_root_id
117
- assert @root["ID"].start_with?("_")
118
- end
119
-
120
- def test_root_version
121
- assert_equal "2.0", @root["Version"]
122
- end
123
-
124
- def test_root_issue_instant
125
- instant = Time.iso8601(@root["IssueInstant"])
126
- assert_in_delta Time.now.to_i, instant.to_i, 1
127
- end
128
-
129
- def test_destination_default
130
- assert_equal DEFAULTS.recipient, @root["Destination"]
131
- end
132
-
133
- def test_destination_override
134
- destination = "https://test.example.com/acs"
135
- refute_equal destination, DEFAULTS.recipient
136
- root = Response.new(destination: destination).document.root
137
- assert_equal destination, root["Destination"]
138
- end
139
-
140
- def test_in_response_to_default
141
- assert_equal DEFAULTS.in_response_to, @root["InResponseTo"]
142
- end
143
-
144
- def test_in_response_to_override
145
- in_response_to = "_test_request"
146
- refute_equal in_response_to, DEFAULTS.in_response_to
147
- root = Response.new(in_response_to: in_response_to).document.root
148
- assert_equal in_response_to, root["InResponseTo"]
149
- end
150
-
151
- def test_destination_omitted_when_nil
152
- root = Response.new(destination: nil).document.root
153
- assert_nil root["Destination"]
154
- end
155
-
156
- def test_in_response_to_omitted_when_nil
157
- root = Response.new(in_response_to: nil).document.root
158
- assert_nil root["InResponseTo"]
159
- end
160
-
161
- def test_issuer
162
- issuer = @root.at_xpath("saml:Issuer", NAMESPACES)
163
- assert_equal "Issuer", issuer.name
164
- assert_equal "saml", issuer.namespace.prefix
165
- assert_equal DEFAULTS.issuer, issuer.text
166
- end
167
-
168
- def test_issuer_override
169
- url = "https://test.idp.example.com"
170
- refute_equal url, DEFAULTS.issuer
171
- root = Response.new(issuer: url).document.root
172
- issuer = root.at_xpath("saml:Issuer", NAMESPACES)
173
- assert_equal url, issuer.text
174
- end
175
-
176
- def test_status
177
- status = @root.at_xpath("samlp:Status", NAMESPACES)
178
- assert_equal "Status", status.name
179
- assert_equal "samlp", status.namespace.prefix
180
- end
181
-
182
- def test_status_code
183
- status_code = @root.at_xpath("samlp:Status/samlp:StatusCode", NAMESPACES)
184
- assert_equal "StatusCode", status_code.name
185
- assert_equal "samlp", status_code.namespace.prefix
186
- assert_equal STATUS_SUCCESS, status_code["Value"]
187
- end
188
-
189
- def test_assertion_embedded
190
- assertion = @root.at_xpath("saml:Assertion", NAMESPACES)
191
- assert_equal "Assertion", assertion.name
192
- assert_equal "saml", assertion.namespace.prefix
193
- end
194
-
195
- def test_assertion_has_id
196
- assertion = @root.at_xpath("saml:Assertion", NAMESPACES)
197
- assert assertion["ID"].start_with?("_")
198
- end
199
-
200
- def test_assertion_inherits_issuer
201
- url = "https://test.idp.example.com"
202
- refute_equal url, DEFAULTS.issuer
203
- root = Response.new(issuer: url).document.root
204
- issuer = root.at_xpath("saml:Assertion/saml:Issuer", NAMESPACES)
205
- assert_equal url, issuer.text
206
- end
207
-
208
- def test_assertion_options_flow_through
209
- email = "test@example.com"
210
- refute_equal email, DEFAULTS.name_id
211
- root = Response.new(name_id: email).document.root
212
- xpath = "saml:Assertion/saml:Subject/saml:NameID"
213
- assert_equal email, root.at_xpath(xpath, NAMESPACES).text
214
- end
215
-
216
- def test_unsigned_by_default
217
- assert_nil @root.at_xpath("ds:Signature", NAMESPACES)
218
- assert_nil @root.at_xpath("saml:Assertion/ds:Signature", NAMESPACES)
219
- end
220
-
221
- def test_sign_with_signs_response_and_assertion
222
- root = Response.new(sign_with: Certificate.build).document.root
223
- refute_nil root.at_xpath("ds:Signature", NAMESPACES)
224
- refute_nil root.at_xpath("saml:Assertion/ds:Signature", NAMESPACES)
225
- end
226
-
227
- def test_not_encrypted_by_default
228
- assert_nil @root.at_xpath("saml:EncryptedAssertion", NAMESPACES)
229
- end
230
-
231
- def test_encrypt_with_creates_encrypted_assertion
232
- root = Response.new(encrypt_with: Certificate.build).document.root
233
- ea = root.at_xpath("saml:EncryptedAssertion", NAMESPACES)
234
- assert_equal "EncryptedAssertion", ea.name
235
- assert_equal "saml", ea.namespace.prefix
236
- end
237
-
238
- def test_encrypt_with_removes_plain_assertion
239
- root = Response.new(encrypt_with: Certificate.build).document.root
240
- assert_nil root.at_xpath("saml:Assertion", NAMESPACES)
241
- end
242
-
243
- def test_encrypt_with_contains_encrypted_data
244
- root = Response.new(encrypt_with: Certificate.build).document.root
245
- xpath = "saml:EncryptedAssertion/xenc:EncryptedData"
246
- ed = root.at_xpath(xpath, NAMESPACES)
247
- assert_equal "EncryptedData", ed.name
248
- end
249
- end
250
- end