saml2 3.1.2 → 3.1.4
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.
- checksums.yaml +4 -4
- data/Rakefile +6 -4
- data/exe/bulk_verify_responses +94 -0
- data/lib/saml2/assertion.rb +7 -7
- data/lib/saml2/attribute/x500.rb +31 -28
- data/lib/saml2/attribute.rb +53 -49
- data/lib/saml2/attribute_consuming_service.rb +29 -31
- data/lib/saml2/authn_request.rb +54 -47
- data/lib/saml2/authn_statement.rb +31 -20
- data/lib/saml2/base.rb +72 -63
- data/lib/saml2/bindings/http_post.rb +7 -7
- data/lib/saml2/bindings/http_redirect.rb +37 -33
- data/lib/saml2/bindings.rb +1 -1
- data/lib/saml2/conditions.rb +19 -16
- data/lib/saml2/contact.rb +19 -18
- data/lib/saml2/endpoint.rb +14 -11
- data/lib/saml2/entity.rb +27 -27
- data/lib/saml2/identity_provider.rb +13 -10
- data/lib/saml2/indexed_object.rb +15 -12
- data/lib/saml2/key.rb +43 -34
- data/lib/saml2/localized_name.rb +11 -10
- data/lib/saml2/logout_request.rb +8 -8
- data/lib/saml2/logout_response.rb +4 -4
- data/lib/saml2/message.rb +24 -20
- data/lib/saml2/name_id.rb +45 -41
- data/lib/saml2/namespaces.rb +8 -8
- data/lib/saml2/organization.rb +11 -10
- data/lib/saml2/organization_and_contacts.rb +5 -5
- data/lib/saml2/request.rb +3 -3
- data/lib/saml2/requested_authn_context.rb +4 -4
- data/lib/saml2/response.rb +45 -33
- data/lib/saml2/role.rb +11 -11
- data/lib/saml2/schemas.rb +13 -10
- data/lib/saml2/service_provider.rb +11 -12
- data/lib/saml2/signable.rb +23 -18
- data/lib/saml2/sso.rb +5 -5
- data/lib/saml2/status.rb +9 -7
- data/lib/saml2/status_response.rb +5 -5
- data/lib/saml2/subject.rb +28 -28
- data/lib/saml2/version.rb +1 -1
- data/lib/saml2.rb +7 -7
- metadata +78 -122
- data/spec/fixtures/FederationMetadata.xml +0 -670
- data/spec/fixtures/authnrequest.xml +0 -12
- data/spec/fixtures/certificate.pem +0 -24
- data/spec/fixtures/entities.xml +0 -13
- data/spec/fixtures/external-uri-reference-response.xml +0 -48
- data/spec/fixtures/identity_provider.xml +0 -46
- data/spec/fixtures/noconditions_response.xml +0 -1
- data/spec/fixtures/othercertificate.pem +0 -25
- data/spec/fixtures/privatekey.key +0 -27
- data/spec/fixtures/response_assertion_signed_reffed_from_response.xml +0 -6
- data/spec/fixtures/response_signed.xml +0 -46
- data/spec/fixtures/response_tampered_certificate.xml +0 -25
- data/spec/fixtures/response_tampered_signature.xml +0 -46
- data/spec/fixtures/response_with_attribute_signed.xml +0 -46
- data/spec/fixtures/response_with_encrypted_assertion.xml +0 -58
- data/spec/fixtures/response_with_rsa_key_value.xml +0 -1
- data/spec/fixtures/response_with_signed_assertion_and_encrypted_subject.xml +0 -116
- data/spec/fixtures/response_without_keyinfo.xml +0 -1
- data/spec/fixtures/service_provider.xml +0 -79
- data/spec/fixtures/test3-response.xml +0 -9
- data/spec/fixtures/test6-response.xml +0 -10
- data/spec/fixtures/test7-response.xml +0 -10
- data/spec/fixtures/xml_missigned_assertion.xml +0 -84
- data/spec/fixtures/xml_signature_wrapping_attack_duplicate_ids.xml +0 -11
- data/spec/fixtures/xml_signature_wrapping_attack_response_attributes.xml +0 -45
- data/spec/fixtures/xml_signature_wrapping_attack_response_nameid.xml +0 -44
- data/spec/fixtures/xslt-transform-response.xml +0 -57
- data/spec/lib/attribute_consuming_service_spec.rb +0 -129
- data/spec/lib/attribute_spec.rb +0 -149
- data/spec/lib/authn_request_spec.rb +0 -52
- data/spec/lib/bindings/http_redirect_spec.rb +0 -183
- data/spec/lib/conditions_spec.rb +0 -74
- data/spec/lib/entity_spec.rb +0 -58
- data/spec/lib/identity_provider_spec.rb +0 -43
- data/spec/lib/indexed_object_spec.rb +0 -71
- data/spec/lib/key_spec.rb +0 -23
- data/spec/lib/logout_request_spec.rb +0 -33
- data/spec/lib/logout_response_spec.rb +0 -33
- data/spec/lib/message_spec.rb +0 -23
- data/spec/lib/response_spec.rb +0 -293
- data/spec/lib/service_provider_spec.rb +0 -76
- data/spec/lib/signable_spec.rb +0 -15
- data/spec/spec_helper.rb +0 -8
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../spec_helper'
|
4
|
-
|
5
|
-
module SAML2
|
6
|
-
describe LogoutRequest do
|
7
|
-
let(:idp) { Entity.parse(fixture('identity_provider.xml')).roles.first }
|
8
|
-
|
9
|
-
let(:logout_request) {
|
10
|
-
LogoutRequest.initiate(idp,
|
11
|
-
NameID.new('issuer'),
|
12
|
-
NameID.new('jacob',
|
13
|
-
name_qualifier: "a",
|
14
|
-
sp_name_qualifier: "b"),
|
15
|
-
"abc")
|
16
|
-
}
|
17
|
-
|
18
|
-
it "should generate valid XML" do
|
19
|
-
xml = logout_request.to_s
|
20
|
-
expect(Schemas.protocol.validate(Nokogiri::XML(xml))).to eq []
|
21
|
-
end
|
22
|
-
|
23
|
-
it "parses" do
|
24
|
-
# yup, I'm lazy
|
25
|
-
new_request = LogoutRequest.parse(logout_request.to_s)
|
26
|
-
expect(new_request.issuer.id).to eq 'issuer'
|
27
|
-
expect(new_request.name_id.id).to eq 'jacob'
|
28
|
-
expect(new_request.name_id.name_qualifier).to eq 'a'
|
29
|
-
expect(new_request.name_id.sp_name_qualifier).to eq 'b'
|
30
|
-
expect(new_request.session_index).to eq ['abc']
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../spec_helper'
|
4
|
-
|
5
|
-
module SAML2
|
6
|
-
describe LogoutResponse do
|
7
|
-
let(:idp) { Entity.parse(fixture('identity_provider.xml')).roles.first }
|
8
|
-
|
9
|
-
let(:logout_request) {
|
10
|
-
LogoutRequest.initiate(idp,
|
11
|
-
NameID.new('issuer'),
|
12
|
-
NameID.new('jacob',
|
13
|
-
name_qualifier: "a",
|
14
|
-
sp_name_qualifier: "b"),
|
15
|
-
"abc")
|
16
|
-
}
|
17
|
-
let(:logout_response) {
|
18
|
-
LogoutResponse.respond_to(logout_request, idp, NameID.new('issuer2'))
|
19
|
-
}
|
20
|
-
|
21
|
-
it "should generate valid XML" do
|
22
|
-
xml = logout_response.to_s
|
23
|
-
expect(Schemas.protocol.validate(Nokogiri::XML(xml))).to eq []
|
24
|
-
end
|
25
|
-
|
26
|
-
it "parses" do
|
27
|
-
# yup, I'm lazy
|
28
|
-
new_response = LogoutResponse.parse(logout_response.to_s)
|
29
|
-
expect(new_response.issuer.id).to eq 'issuer2'
|
30
|
-
expect(new_response.status.code).to eq Status::SUCCESS
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
data/spec/lib/message_spec.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../spec_helper'
|
4
|
-
|
5
|
-
module SAML2
|
6
|
-
describe Message do
|
7
|
-
describe '.parse' do
|
8
|
-
it 'complains about invalid XML' do
|
9
|
-
expect { Message.parse("garbage") }.to raise_error(CorruptMessage)
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'complains about getting the wrong type if calling on a subclass, and you get a different type' do
|
13
|
-
expect { Response.parse(fixture('authnrequest.xml')) }.to raise_error(UnexpectedMessage)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe '.from_xml' do
|
18
|
-
it "complains about unknown messages" do
|
19
|
-
expect { Message.parse("<Garbage></Garbage>") }.to raise_error(UnknownMessage)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/spec/lib/response_spec.rb
DELETED
@@ -1,293 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../spec_helper'
|
4
|
-
|
5
|
-
module SAML2
|
6
|
-
describe Response do
|
7
|
-
let(:sp_entity) { Entity.parse(fixture('service_provider.xml')) }
|
8
|
-
let(:sp) { sp_entity.roles.first }
|
9
|
-
|
10
|
-
let(:request) do
|
11
|
-
request = AuthnRequest.parse(fixture('authnrequest.xml'))
|
12
|
-
request.resolve(sp)
|
13
|
-
request
|
14
|
-
end
|
15
|
-
|
16
|
-
let(:response) do
|
17
|
-
Response.respond_to(request,
|
18
|
-
NameID.new('issuer'),
|
19
|
-
NameID.new('jacob', NameID::Format::PERSISTENT))
|
20
|
-
end
|
21
|
-
|
22
|
-
it "should generate valid XML" do
|
23
|
-
xml = response.to_s
|
24
|
-
expect(Schemas.protocol.validate(Nokogiri::XML(xml))).to eq []
|
25
|
-
end
|
26
|
-
|
27
|
-
def freeze_response
|
28
|
-
response.instance_variable_set(:@id, "_9a15e699-2d04-4ba7-a521-cfa4dcd21f44")
|
29
|
-
assertion = response.assertions.first
|
30
|
-
assertion.instance_variable_set(:@id, "_cdfc3faf-90ad-462f-880d-677483210684")
|
31
|
-
response.instance_variable_set(:@issue_instant, Time.parse("2015-02-12T22:51:29Z"))
|
32
|
-
assertion.instance_variable_set(:@issue_instant, Time.parse("2015-02-12T22:51:29Z"))
|
33
|
-
assertion.conditions.not_before = Time.parse("2015-02-12T22:51:24Z")
|
34
|
-
assertion.conditions.not_on_or_after = Time.parse("2015-02-12T22:51:59Z")
|
35
|
-
assertion.statements.first.authn_instant = Time.parse("2015-02-12T22:51:29Z")
|
36
|
-
confirmation = assertion.subject.confirmation
|
37
|
-
confirmation.not_on_or_after = Time.parse("2015-02-12T22:54:29Z")
|
38
|
-
confirmation.recipient = response.destination
|
39
|
-
confirmation.in_response_to = response.in_response_to
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should generate a valid signature" do
|
43
|
-
freeze_response
|
44
|
-
response.sign(fixture('certificate.pem'), fixture('privatekey.key'))
|
45
|
-
expect(Schemas.protocol.validate(response.to_xml)).to eq []
|
46
|
-
# verifiable on the command line with:
|
47
|
-
# xmlsec1 --verify --pubkey-cert-pem certificate.pem --privkey-pem privatekey.key --id-attr:ID urn:oasis:names:tc:SAML:2.0:assertion:Assertion response_signed.xml
|
48
|
-
expect(response.to_s).to eq fixture('response_signed.xml')
|
49
|
-
end
|
50
|
-
|
51
|
-
it "should generate a valid signature when attributes are present" do
|
52
|
-
freeze_response
|
53
|
-
response.assertions.first.statements << sp.attribute_consuming_services.default.create_statement('givenName' => 'cody')
|
54
|
-
response.sign(fixture('certificate.pem'), fixture('privatekey.key'))
|
55
|
-
expect(response.to_s).to eq fixture('response_with_attribute_signed.xml')
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should generate valid XML for IdP initiated response" do
|
59
|
-
response = Response.initiate(sp, NameID.new('issuer'),
|
60
|
-
NameID.new('jacob', NameID::Format::PERSISTENT))
|
61
|
-
expect(Schemas.protocol.validate(Nokogiri::XML(response.to_s))).to eq []
|
62
|
-
end
|
63
|
-
|
64
|
-
it "parses a serialized assertion" do
|
65
|
-
response2 = Message.parse(response.to_s)
|
66
|
-
expect(response2.in_response_to).not_to be_nil
|
67
|
-
expect(response2.in_response_to).to eq response.in_response_to
|
68
|
-
expect(response2.assertions.length).to eq 1
|
69
|
-
expect(response2.assertions.first.subject.name_id.id).to eq 'jacob'
|
70
|
-
end
|
71
|
-
|
72
|
-
it "doesn't validate a response with XSLT transforms" do
|
73
|
-
response = Response.parse(fixture("xslt-transform-response.xml"))
|
74
|
-
expect(response).to be_valid_schema
|
75
|
-
expect(response.assertions.first.valid_signature?(fingerprint: 'bc71f7bacb36011694405dd0e2beafcc069de45f')).to eq false
|
76
|
-
end
|
77
|
-
|
78
|
-
it "doesn't validate a response with external URI reference in the signature" do
|
79
|
-
response = Response.parse(fixture("external-uri-reference-response.xml"))
|
80
|
-
expect(response).to be_valid_schema
|
81
|
-
expect(response.assertions.first.valid_signature?(fingerprint: 'bc71f7bacb36011694405dd0e2beafcc069de45f')).to eq false
|
82
|
-
end
|
83
|
-
|
84
|
-
it "can decrypt an EncryptedAssertion" do
|
85
|
-
# verifiable on the command line with:
|
86
|
-
# xmlsec1 decrypt --privkey-pem privatekey.key response_with_encrypted_assertion.xml
|
87
|
-
response = Response.parse(fixture("response_with_encrypted_assertion.xml"))
|
88
|
-
expect(response.decrypt(fixture("privatekey.key"))).to eq true
|
89
|
-
expect(response.assertions.length).to eq 1
|
90
|
-
expect(response.assertions.first.subject.name_id.id).to eq 'jacob'
|
91
|
-
end
|
92
|
-
|
93
|
-
it "allows non-ascii characters in attributes" do
|
94
|
-
response = Response.parse(fixture("test6-response.xml"))
|
95
|
-
|
96
|
-
attributes = response.assertions.first.attribute_statements.first.to_h
|
97
|
-
expect(attributes['eduPersonAffiliation']).to eq 'member'
|
98
|
-
expect(attributes['givenName']).to eq 'Canvas'
|
99
|
-
expect(attributes['displayName']).to eq 'Canvas Üser'
|
100
|
-
end
|
101
|
-
|
102
|
-
# see CVE-2017-11428
|
103
|
-
it "returns the full content of the NameID, even if a comment-insertion attack allows it to still validate the signature" do
|
104
|
-
response = Response.parse(fixture("test7-response.xml"))
|
105
|
-
# this file is a copy of test6-response.xml, with a comment inserted into the NameID
|
106
|
-
|
107
|
-
# the signature is still valid (we have to set a weird verification time because the response
|
108
|
-
# was signed with an expired signature)
|
109
|
-
expect(response.validate_signature(fingerprint: 'afe71c28ef740bc87425be13a2263d37971da1f9')).to eq []
|
110
|
-
|
111
|
-
# the comment is ignored, but doesn't truncate the nameid
|
112
|
-
expect(response.assertions.first.subject.name_id.id).to eq 'testuser@example.com'
|
113
|
-
end
|
114
|
-
|
115
|
-
it "doesn't choke on missing Conditions" do
|
116
|
-
response = Response.parse(fixture("noconditions_response.xml"))
|
117
|
-
expect(response.assertions.first.conditions).to eq nil
|
118
|
-
end
|
119
|
-
|
120
|
-
describe "#validate" do
|
121
|
-
let (:idp_entity) do
|
122
|
-
idp_entity = Entity.new("issuer")
|
123
|
-
idp = IdentityProvider.new
|
124
|
-
idp.keys << KeyDescriptor.new(fixture("certificate.pem"))
|
125
|
-
idp_entity.roles << idp
|
126
|
-
|
127
|
-
idp_entity
|
128
|
-
end
|
129
|
-
|
130
|
-
before do
|
131
|
-
sp.private_keys << OpenSSL::PKey::RSA.new(fixture("privatekey.key"))
|
132
|
-
end
|
133
|
-
|
134
|
-
it "succeeds" do
|
135
|
-
response = Response.parse(fixture("response_signed.xml"))
|
136
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
137
|
-
expect(response.errors).to eq []
|
138
|
-
end
|
139
|
-
|
140
|
-
it "checks the issuer" do
|
141
|
-
response = Response.parse(fixture("response_signed.xml"))
|
142
|
-
idp_entity.entity_id = 'someoneelse'
|
143
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
144
|
-
expect(response.errors).to eq ["received unexpected message from 'issuer'; expected it to be from 'someoneelse'"]
|
145
|
-
end
|
146
|
-
|
147
|
-
it "complains about old message" do
|
148
|
-
response = Response.parse(fixture("response_signed.xml"))
|
149
|
-
sp_entity.valid_response?(response, idp_entity)
|
150
|
-
expect(response.errors.length).to eq 1
|
151
|
-
expect(response.errors.first).to match (/not_on_or_after .* is earlier than/)
|
152
|
-
end
|
153
|
-
|
154
|
-
it "complains about mismatched audience restriction" do
|
155
|
-
response = Response.parse(fixture("response_signed.xml"))
|
156
|
-
sp_entity.entity_id = 'someoneelse'
|
157
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
158
|
-
expect(response.errors).to eq ["audience someoneelse not in allowed list of http://siteadmin.instructure.com/saml2"]
|
159
|
-
end
|
160
|
-
|
161
|
-
it "complains about no signature" do
|
162
|
-
response = Response.parse(fixture("response_with_encrypted_assertion.xml"))
|
163
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
164
|
-
expect(response.errors).to eq ["neither response nor assertion were signed"]
|
165
|
-
end
|
166
|
-
|
167
|
-
it "complains if the signature has been tampered with" do
|
168
|
-
response = Response.parse(fixture("response_tampered_signature.xml"))
|
169
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
170
|
-
expect(response.errors).to eq ["signature is invalid"]
|
171
|
-
end
|
172
|
-
|
173
|
-
it "complains if the trusted certificate isn't what signed the response" do
|
174
|
-
idp_entity.identity_providers.first.keys.clear
|
175
|
-
idp_entity.identity_providers.first.fingerprints << "afe71c28ef740bc87425be13a2263d37971da1f9"
|
176
|
-
|
177
|
-
response = Response.parse(fixture("response_tampered_certificate.xml"))
|
178
|
-
sp_entity.valid_response?(response, idp_entity,
|
179
|
-
verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
180
|
-
expect(response.errors).to eq ["signature is invalid"]
|
181
|
-
end
|
182
|
-
|
183
|
-
it "complains when we don't have any trusted keys" do
|
184
|
-
response = Response.parse(fixture("response_signed.xml"))
|
185
|
-
idp_entity.identity_providers.first.keys.clear
|
186
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
187
|
-
expect(response.errors).to eq ["could not find certificate to validate message"]
|
188
|
-
end
|
189
|
-
|
190
|
-
it "complains about a valid signature we don't trust" do
|
191
|
-
response = Response.parse(fixture("response_signed.xml"))
|
192
|
-
idp_entity.identity_providers.first.keys.clear
|
193
|
-
idp_entity.identity_providers.first.keys << KeyDescriptor.new(fixture("othercertificate.pem"))
|
194
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
195
|
-
expect(response.errors.length).to eq 1
|
196
|
-
expect(response.errors.first).to eq("no trusted signing key found")
|
197
|
-
end
|
198
|
-
|
199
|
-
it "validates signature by fingerprint" do
|
200
|
-
response = Response.parse(fixture("response_signed.xml"))
|
201
|
-
idp_entity.identity_providers.first.keys.clear
|
202
|
-
idp_entity.identity_providers.first.fingerprints << "1c:37:7d:30:c1:83:18:ea:20:8b:dc:d5:35:b6:16:85:17:58:f7:c9"
|
203
|
-
|
204
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
205
|
-
expect(response.errors).to eq []
|
206
|
-
end
|
207
|
-
|
208
|
-
it "complains when we don't have any trusted fingerprints" do
|
209
|
-
response = Response.parse(fixture("response_signed.xml"))
|
210
|
-
idp_entity.identity_providers.first.keys.clear
|
211
|
-
idp_entity.identity_providers.first.fingerprints << "1c:37:7d:30:c1:83:18:ea:20:8b:dc:d5:35:b6:16:85:17:58:f7:ca"
|
212
|
-
|
213
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
214
|
-
expect(response.errors).to eq ["no trusted signing key found"]
|
215
|
-
end
|
216
|
-
|
217
|
-
it "protects against xml signature wrapping attacks targeting nameid" do
|
218
|
-
response = Response.parse(fixture("xml_signature_wrapping_attack_response_nameid.xml"))
|
219
|
-
idp_entity.identity_providers.first.keys.clear
|
220
|
-
idp_entity.identity_providers.first.fingerprints << "afe71c28ef740bc87425be13a2263d37971da1f9"
|
221
|
-
|
222
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2012-08-03T20:07:16Z'))
|
223
|
-
expect(response.errors.length).to eq 1
|
224
|
-
expect(response.errors.first.to_s).to eq "5:0: ERROR: Element '{urn:oasis:names:tc:SAML:2.0:assertion}Assertion': This element is not expected. Expected is one of ( {http://www.w3.org/2000/09/xmldsig#}Signature, {urn:oasis:names:tc:SAML:2.0:protocol}Extensions, {urn:oasis:names:tc:SAML:2.0:protocol}Status )."
|
225
|
-
end
|
226
|
-
|
227
|
-
it "protects against xml signature wrapping attacks targeting attributes" do
|
228
|
-
response = Response.parse(fixture("xml_signature_wrapping_attack_response_attributes.xml"))
|
229
|
-
idp_entity.identity_providers.first.keys.clear
|
230
|
-
idp_entity.identity_providers.first.fingerprints << "afe71c28ef740bc87425be13a2263d37971da1f9"
|
231
|
-
|
232
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2012-08-03T20:07:16Z'))
|
233
|
-
expect(response.errors.length).to eq 1
|
234
|
-
expect(response.errors.first.to_s).to eq "30:0: ERROR: Element '{urn:oasis:names:tc:SAML:2.0:assertion}Subject': This element is not expected."
|
235
|
-
end
|
236
|
-
|
237
|
-
it "protects against xml signature wrapping attacks with duplicate IDs" do
|
238
|
-
response = Response.parse(fixture("xml_signature_wrapping_attack_duplicate_ids.xml"))
|
239
|
-
idp_entity.identity_providers.first.keys.clear
|
240
|
-
idp_entity.identity_providers.first.fingerprints << "7292914fc5bffa6f3fe1e43fd47c205395fecfa2"
|
241
|
-
|
242
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2014-02-01T13:48:10.831Z'))
|
243
|
-
expect(response.errors.length).to eq 1
|
244
|
-
expect(response.errors.first.to_s).to eq "1:0: ERROR: Element '{urn:oasis:names:tc:SAML:2.0:assertion}Assertion', attribute 'ID': 'pfx77d6c794-8295-f1c4-298e-c25ecae8046d' is not a valid value of the atomic type 'xs:ID'."
|
245
|
-
end
|
246
|
-
|
247
|
-
it "protects against additional mis-signed assertions" do
|
248
|
-
response = Response.parse(fixture("xml_missigned_assertion.xml"))
|
249
|
-
idp_entity.identity_providers.first.keys.clear
|
250
|
-
idp_entity.identity_providers.first.fingerprints << "c38e789fcfbbd4727bd8ff7fc365b44fc3596bda"
|
251
|
-
|
252
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-27T19:12:52Z'))
|
253
|
-
expect(response.errors.map(&:to_s)).to eq ["2:0: ERROR: Element '{http://www.w3.org/2000/09/xmldsig#}Signature': This element is not expected.",
|
254
|
-
"43:0: ERROR: Element '{http://www.w3.org/2000/09/xmldsig#}Signature': This element is not expected."]
|
255
|
-
end
|
256
|
-
|
257
|
-
it "doesn't break the signature by decrypting elements first" do
|
258
|
-
response = Response.parse(fixture("response_with_signed_assertion_and_encrypted_subject.xml"))
|
259
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2015-02-12T22:51:30Z'))
|
260
|
-
expect(response.errors).to eq []
|
261
|
-
expect(response.assertions.first.subject.name_id.id).to eq 'jacob'
|
262
|
-
end
|
263
|
-
|
264
|
-
it "allows signatures that don't include KeyInfo, if we have a full cert" do
|
265
|
-
response = Response.parse(fixture("response_without_keyinfo.xml"))
|
266
|
-
sp_entity.entity_id = 'http://unimelb-dev.instructure.com/saml2'
|
267
|
-
idp_entity.entity_id = 'https://authidm3tst.unimelb.edu.au:443/oam/fed'
|
268
|
-
idp_entity.identity_providers.first.keys.clear
|
269
|
-
idp_entity.identity_providers.first.keys << KeyDescriptor.new(<<-CERTIFICATE)
|
270
|
-
MIIB/jCCAWegAwIBAgIBCjANBgkqhkiG9w0BAQQFADAkMSIwIAYDVQQDExlhZGRlcjEuaXRzLnVuaW1lbGIuZWR1LmF1MB4XDTE3MDUyMjA2MzQzOFoXDTI3MDUyMDA2MzQzOFowJDEiMCAGA1UEAxMZYWRkZXIxLml0cy51bmltZWxiLmVkdS5hdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAjJgoa5bPS+Jukq2vNaMwZ39L3IhAg6oOytz+bgOhmF+o5zYARbFqC67faa/rMSkfQwYIpp/MdsC8XHtHeR6HCJjbuPkH/EooHiREOClTI0EKZvI2Xv/DqexxEegRPxXiwPUEPozGGT1yWtSwkQTvRvA9tMpZl3yLg1LhDOP6s6MCAwEAAaNAMD4wDAYDVR0TAQH/BAIwADAPBgNVHQ8BAf8EBQMDB9gAMB0GA1UdDgQWBBRukBh7J1okLMIfSRpzF5opuj0LizANBgkqhkiG9w0BAQQFAAOBgQB0zySVaypIGRksTwpmjaQhMvNrYWGvj74Rs1iuqOdsEQkpgk5dQKRFiAFEr+6b7WN4k+IAH5S++l1R0bUG6k9HFSn7uy7AD+qZcdoUm9a39brtH2kefs0D3bQfrwkqggAtWKwqfU4r7nAcdtVE+CT3cny5QU2/mJav9W9bzFPMXQ==
|
271
|
-
CERTIFICATE
|
272
|
-
|
273
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: Time.parse('2019-04-16T00:56:03Z'))
|
274
|
-
expect(response.errors).to eq []
|
275
|
-
expect(response.assertions.first.subject.name_id.id).to eq 'testuserint.sso@staff.oimtest.unimelb.edu.au'
|
276
|
-
end
|
277
|
-
|
278
|
-
it "finds signatures the sign the assertion, not inside the assertion" do
|
279
|
-
response = Response.parse(fixture("response_assertion_signed_reffed_from_response.xml"))
|
280
|
-
sp_entity.entity_id = 'http://wscc.instructure.com/saml2'
|
281
|
-
idp_entity.entity_id = 'https://my.wscc.edu/idp'
|
282
|
-
idp_entity.identity_providers.first.keys.clear
|
283
|
-
idp_entity.identity_providers.first.fingerprints << "c4f473274116a3cbc295c3abf77c7ed1ade9b904"
|
284
|
-
|
285
|
-
sp_entity.valid_response?(response, idp_entity, verification_time: response.issue_instant)
|
286
|
-
expect(response.errors).to eq []
|
287
|
-
expect(response.assertions.first.subject.name_id.id).to eq 'narnold@wscc.edu'
|
288
|
-
expect(response).not_to be_signed
|
289
|
-
expect(response.assertions.first).to be_signed
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
293
|
-
end
|
@@ -1,76 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../spec_helper'
|
4
|
-
|
5
|
-
module SAML2
|
6
|
-
describe ServiceProvider do
|
7
|
-
it "should serialize valid xml" do
|
8
|
-
entity = Entity.new
|
9
|
-
entity.entity_id = 'http://sso.canvaslms.com/SAML2'
|
10
|
-
entity.organization = Organization.new('Canvas', 'Canvas by Instructure', 'https://www.canvaslms.com/')
|
11
|
-
contact = Contact.new(Contact::Type::TECHNICAL)
|
12
|
-
contact.company = 'Instructure'
|
13
|
-
contact.email_addresses << 'mailto:ops@instructure.com'
|
14
|
-
entity.contacts << contact
|
15
|
-
|
16
|
-
sp = ServiceProvider.new
|
17
|
-
sp.single_logout_services << Endpoint.new('https://sso.canvaslms.com/SAML2/Logout',
|
18
|
-
Bindings::HTTPRedirect::URN)
|
19
|
-
sp.assertion_consumer_services << Endpoint::Indexed.new('https://sso.canvaslms.com/SAML2/Login1')
|
20
|
-
sp.assertion_consumer_services << Endpoint::Indexed.new('https://sso.canvaslms.com/SAML2/Login2')
|
21
|
-
sp.keys << KeyDescriptor.new('somedata', KeyDescriptor::Type::ENCRYPTION, [KeyDescriptor::EncryptionMethod.new])
|
22
|
-
sp.keys << KeyDescriptor.new('somedata', KeyDescriptor::Type::SIGNING)
|
23
|
-
acs = AttributeConsumingService.new
|
24
|
-
acs.name[:en] = 'service'
|
25
|
-
acs.requested_attributes << RequestedAttribute.create('uid')
|
26
|
-
sp.attribute_consuming_services << acs
|
27
|
-
|
28
|
-
entity.roles << sp
|
29
|
-
expect(Schemas.metadata.validate(Nokogiri::XML(entity.to_s))).to eq []
|
30
|
-
end
|
31
|
-
|
32
|
-
describe "valid metadata" do
|
33
|
-
let(:entity) { Entity.parse(fixture('service_provider.xml')) }
|
34
|
-
let(:sp) { entity.roles.first }
|
35
|
-
|
36
|
-
it "should create the assertion_consumer_services array" do
|
37
|
-
expect(sp.assertion_consumer_services.length).to eq 4
|
38
|
-
expect(sp.assertion_consumer_services.map(&:index)).to eq [0, 1, 2, 3]
|
39
|
-
expect(sp.assertion_consumer_services.first.location).to eq 'https://siteadmin.instructure.com/saml_consume'
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should find the signing certificate" do
|
43
|
-
expect(sp.signing_keys.first.x509).to match(/MIIE8TCCA9mgAwIBAgIJAITusxON60cKMA0GCSqGSIb3DQEBBQUAMIGrMQswCQYD/)
|
44
|
-
end
|
45
|
-
|
46
|
-
it "should load the organization" do
|
47
|
-
expect(entity.organization.display_name.to_s).to eq 'Canvas'
|
48
|
-
end
|
49
|
-
|
50
|
-
it "should load contacts" do
|
51
|
-
expect(entity.contacts.length).to eq 1
|
52
|
-
expect(entity.contacts.first.type).to eq Contact::Type::TECHNICAL
|
53
|
-
expect(entity.contacts.first.surname).to eq 'Administrator'
|
54
|
-
end
|
55
|
-
|
56
|
-
it "loads attribute_consuming_services" do
|
57
|
-
expect(sp.attribute_consuming_services.length).to eq 1
|
58
|
-
acs = sp.attribute_consuming_services.first
|
59
|
-
expect(acs.index).to eq 0
|
60
|
-
expect(acs.name.to_s).to eq 'service'
|
61
|
-
expect(acs.requested_attributes.length).to eq 1
|
62
|
-
expect(acs.requested_attributes.first.name).to eq 'urn:oid:2.5.4.42'
|
63
|
-
end
|
64
|
-
|
65
|
-
it "loads the key info" do
|
66
|
-
expect(sp.keys.first.encryption_methods.first.algorithm).to eq KeyDescriptor::EncryptionMethod::Algorithm::AES128_CBC
|
67
|
-
expect(sp.keys.first.encryption_methods.first.key_size).to eq 128
|
68
|
-
end
|
69
|
-
|
70
|
-
it "loads service provider attributes" do
|
71
|
-
expect(sp.authn_requests_signed?).to be_truthy
|
72
|
-
expect(sp.want_assertions_signed?).to be_truthy
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
data/spec/lib/signable_spec.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative '../spec_helper'
|
4
|
-
|
5
|
-
module SAML2
|
6
|
-
describe Signable do
|
7
|
-
describe '#valid_signature?' do
|
8
|
-
it 'can work with an explicit key from metadata' do
|
9
|
-
response = Response.parse(fixture("response_with_rsa_key_value.xml"))
|
10
|
-
key = response.assertions.first.signing_key.key
|
11
|
-
expect(response.assertions.first.valid_signature?(key: [key])).to eq true
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|