saml_idp 0.9.0 → 0.14.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -45
  3. data/lib/saml_idp.rb +2 -1
  4. data/lib/saml_idp/assertion_builder.rb +28 -3
  5. data/lib/saml_idp/configurator.rb +4 -1
  6. data/lib/saml_idp/controller.rb +11 -9
  7. data/lib/saml_idp/encryptor.rb +0 -1
  8. data/lib/saml_idp/fingerprint.rb +19 -0
  9. data/lib/saml_idp/incoming_metadata.rb +13 -0
  10. data/lib/saml_idp/metadata_builder.rb +23 -8
  11. data/lib/saml_idp/persisted_metadata.rb +4 -0
  12. data/lib/saml_idp/request.rb +9 -3
  13. data/lib/saml_idp/response_builder.rb +19 -5
  14. data/lib/saml_idp/saml_response.rb +37 -16
  15. data/lib/saml_idp/service_provider.rb +1 -6
  16. data/lib/saml_idp/signable.rb +1 -2
  17. data/lib/saml_idp/version.rb +1 -1
  18. data/saml_idp.gemspec +8 -8
  19. data/spec/lib/saml_idp/assertion_builder_spec.rb +73 -0
  20. data/spec/lib/saml_idp/configurator_spec.rb +1 -0
  21. data/spec/lib/saml_idp/controller_spec.rb +24 -0
  22. data/spec/lib/saml_idp/fingerprint_spec.rb +14 -0
  23. data/spec/lib/saml_idp/incoming_metadata_spec.rb +15 -1
  24. data/spec/lib/saml_idp/metadata_builder_spec.rb +23 -0
  25. data/spec/lib/saml_idp/response_builder_spec.rb +3 -1
  26. data/spec/lib/saml_idp/saml_response_spec.rb +25 -2
  27. data/spec/rails_app/app/controllers/saml_controller.rb +1 -5
  28. data/spec/rails_app/app/controllers/saml_idp_controller.rb +47 -8
  29. data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +1 -5
  30. data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
  31. data/spec/rails_app/config/environments/development.rb +2 -0
  32. data/spec/spec_helper.rb +20 -1
  33. data/spec/support/certificates/sp_cert_req.csr +12 -0
  34. data/spec/support/certificates/sp_private_key.pem +16 -0
  35. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  36. data/spec/support/saml_request_macros.rb +62 -3
  37. data/spec/support/security_helpers.rb +10 -0
  38. metadata +51 -28
  39. data/app/controllers/saml_idp/idp_controller.rb +0 -59
@@ -106,6 +106,7 @@ module SamlIdp
106
106
  end
107
107
 
108
108
  if !service_provider.acceptable_response_hosts.include?(response_host)
109
+ log "#{service_provider.acceptable_response_hosts} compare to #{response_host}"
109
110
  log "No acceptable AssertionConsumerServiceURL, either configure them via config.service_provider.response_hosts or match to your metadata_url host"
110
111
  return false
111
112
  end
@@ -114,9 +115,14 @@ module SamlIdp
114
115
  end
115
116
 
116
117
  def valid_signature?
117
- # Force signatures for logout requests because there is no other
118
- # protection against a cross-site DoS.
119
- service_provider.valid_signature?(document, logout_request?)
118
+ # Force signatures for logout requests because there is no other protection against a cross-site DoS.
119
+ # Validate signature when metadata specify AuthnRequest should be signed
120
+ metadata = service_provider.current_metadata
121
+ if logout_request? || authn_request? && metadata.respond_to?(:sign_authn_request?) && metadata.sign_authn_request?
122
+ document.valid_signature?(service_provider.fingerprint)
123
+ else
124
+ true
125
+ end
120
126
  end
121
127
 
122
128
  def service_provider?
@@ -1,32 +1,45 @@
1
1
  require 'builder'
2
+ require 'saml_idp/algorithmable'
3
+ require 'saml_idp/signable'
2
4
  module SamlIdp
3
5
  class ResponseBuilder
6
+ include Algorithmable
7
+ include Signable
4
8
  attr_accessor :response_id
5
9
  attr_accessor :issuer_uri
6
10
  attr_accessor :saml_acs_url
7
11
  attr_accessor :saml_request_id
8
12
  attr_accessor :assertion_and_signature
13
+ attr_accessor :raw_algorithm
9
14
 
10
- def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature)
15
+ alias_method :reference_id, :response_id
16
+
17
+ def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature, raw_algorithm)
11
18
  self.response_id = response_id
12
19
  self.issuer_uri = issuer_uri
13
20
  self.saml_acs_url = saml_acs_url
14
21
  self.saml_request_id = saml_request_id
15
22
  self.assertion_and_signature = assertion_and_signature
23
+ self.raw_algorithm = raw_algorithm
16
24
  end
17
25
 
18
- def encoded
19
- @encoded ||= encode
26
+ def encoded(signed_message: false)
27
+ @encoded ||= signed_message ? encode_signed_message : encode_raw_message
20
28
  end
21
29
 
22
30
  def raw
23
31
  build
24
32
  end
25
33
 
26
- def encode
34
+ def encode_raw_message
27
35
  Base64.strict_encode64(raw)
28
36
  end
29
- private :encode
37
+ private :encode_raw_message
38
+
39
+ def encode_signed_message
40
+ Base64.strict_encode64(signed)
41
+ end
42
+ private :encode_signed_message
30
43
 
31
44
  def build
32
45
  resp_options = {}
@@ -41,6 +54,7 @@ module SamlIdp
41
54
  builder = Builder::XmlMarkup.new
42
55
  builder.tag! "samlp:Response", resp_options do |response|
43
56
  response.Issuer issuer_uri, xmlns: Saml::XML::Namespaces::ASSERTION
57
+ sign response
44
58
  response.tag! "samlp:Status" do |status|
45
59
  status.tag! "samlp:StatusCode", Value: Saml::XML::Namespaces::Statuses::SUCCESS
46
60
  end
@@ -17,20 +17,27 @@ module SamlIdp
17
17
  attr_accessor :expiry
18
18
  attr_accessor :encryption_opts
19
19
  attr_accessor :session_expiry
20
+ attr_accessor :signed_message_opts
21
+ attr_accessor :name_id_formats_opts
22
+ attr_accessor :asserted_attributes_opts
20
23
 
21
- def initialize(reference_id,
22
- response_id,
23
- issuer_uri,
24
- principal,
25
- audience_uri,
26
- saml_request_id,
27
- saml_acs_url,
28
- algorithm,
29
- authn_context_classref,
30
- expiry=60*60,
31
- encryption_opts=nil,
32
- session_expiry=0
33
- )
24
+ def initialize(
25
+ reference_id,
26
+ response_id,
27
+ issuer_uri,
28
+ principal,
29
+ audience_uri,
30
+ saml_request_id,
31
+ saml_acs_url,
32
+ algorithm,
33
+ authn_context_classref,
34
+ expiry=60*60,
35
+ encryption_opts=nil,
36
+ session_expiry=0,
37
+ signed_message_opts=false,
38
+ name_id_formats_opts = nil,
39
+ asserted_attributes_opts = nil
40
+ )
34
41
  self.reference_id = reference_id
35
42
  self.response_id = response_id
36
43
  self.issuer_uri = issuer_uri
@@ -45,10 +52,13 @@ module SamlIdp
45
52
  self.expiry = expiry
46
53
  self.encryption_opts = encryption_opts
47
54
  self.session_expiry = session_expiry
55
+ self.signed_message_opts = signed_message_opts
56
+ self.name_id_formats_opts = name_id_formats_opts
57
+ self.asserted_attributes_opts = asserted_attributes_opts
48
58
  end
49
59
 
50
60
  def build
51
- @built ||= response_builder.encoded
61
+ @built ||= encoded_message
52
62
  end
53
63
 
54
64
  def signed_assertion
@@ -60,8 +70,17 @@ module SamlIdp
60
70
  end
61
71
  private :signed_assertion
62
72
 
73
+ def encoded_message
74
+ if signed_message_opts
75
+ response_builder.encoded(signed_message: true)
76
+ else
77
+ response_builder.encoded(signed_message: false)
78
+ end
79
+ end
80
+ private :encoded_message
81
+
63
82
  def response_builder
64
- ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion)
83
+ ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion, algorithm)
65
84
  end
66
85
  private :response_builder
67
86
 
@@ -76,7 +95,9 @@ module SamlIdp
76
95
  authn_context_classref,
77
96
  expiry,
78
97
  encryption_opts,
79
- session_expiry
98
+ session_expiry,
99
+ name_id_formats_opts,
100
+ asserted_attributes_opts
80
101
  end
81
102
  private :assertion_builder
82
103
  end
@@ -22,18 +22,13 @@ module SamlIdp
22
22
  end
23
23
 
24
24
  def valid_signature?(doc, require_signature = false)
25
- if require_signature || should_validate_signature?
25
+ if require_signature || attributes[:validate_signature]
26
26
  doc.valid_signature?(fingerprint)
27
27
  else
28
28
  true
29
29
  end
30
30
  end
31
31
 
32
- def should_validate_signature?
33
- attributes[:validate_signature] ||
34
- current_metadata.respond_to?(:sign_assertions?) && current_metadata.sign_assertions?
35
- end
36
-
37
32
  def refresh_metadata
38
33
  fresh = fresh_incoming_metadata
39
34
  if valid_signature?(fresh.document)
@@ -108,8 +108,7 @@ module SamlIdp
108
108
  canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
109
109
  canon_hashed_element = noko_raw.canonicalize(canon_algorithm, inclusive_namespaces)
110
110
  digest_algorithm = get_algorithm
111
-
112
- hash = digest_algorithm.digest(canon_hashed_element)
111
+ hash = digest_algorithm.digest(canon_hashed_element)
113
112
  Base64.strict_encode64(hash).gsub(/\n/, '')
114
113
  end
115
114
  private :digest
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module SamlIdp
3
- VERSION = '0.9.0'
3
+ VERSION = '0.14.0'
4
4
  end
data/saml_idp.gemspec CHANGED
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
12
12
  s.summary = 'SAML Indentity Provider for Ruby'
13
13
  s.description = 'SAML IdP (Identity Provider) Library for Ruby'
14
14
  s.date = Time.now.utc.strftime("%Y-%m-%d")
15
- s.files = Dir['app/**/*', 'lib/**/*', 'LICENSE', 'README.md', 'Gemfile', 'saml_idp.gemspec']
16
- s.required_ruby_version = '>= 2.2'
15
+ s.files = Dir['lib/**/*', 'LICENSE', 'README.md', 'Gemfile', 'saml_idp.gemspec']
16
+ s.required_ruby_version = '>= 2.5'
17
17
  s.license = 'MIT'
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
@@ -43,20 +43,20 @@ Encrypted Assertions require the xmlenc gem. See the example in the Controller
43
43
  section of the README.
44
44
  INST
45
45
 
46
- s.add_dependency('activesupport', '>= 3.2')
47
- s.add_dependency('uuid', '>= 2.3')
46
+ s.add_dependency('activesupport', '>= 5.2')
48
47
  s.add_dependency('builder', '>= 3.0')
49
48
  s.add_dependency('nokogiri', '>= 1.6.2')
49
+ s.add_dependency('xmlenc', '>= 0.7.1')
50
+ s.add_dependency('rexml')
50
51
 
51
52
  s.add_development_dependency('rake')
52
53
  s.add_development_dependency('simplecov')
53
54
  s.add_development_dependency('rspec', '>= 3.7.0')
54
55
  s.add_development_dependency('ruby-saml', '>= 1.7.2')
55
- s.add_development_dependency('rails', '>= 3.2')
56
- s.add_development_dependency('activeresource', '>= 3.2')
56
+ s.add_development_dependency('rails', '>= 5.2')
57
+ s.add_development_dependency('activeresource', '>= 5.1')
57
58
  s.add_development_dependency('capybara', '>= 2.16')
58
59
  s.add_development_dependency('timecop', '>= 0.8')
59
- s.add_development_dependency('xmlenc', '>= 0.6.4')
60
60
  s.add_development_dependency('appraisal')
61
+ s.add_development_dependency('byebug')
61
62
  end
62
-
@@ -19,6 +19,9 @@ module SamlIdp
19
19
  key_transport: 'rsa-oaep-mgf1p',
20
20
  }
21
21
  end
22
+ let(:session_expiry) { nil }
23
+ let(:name_id_formats_opt) { nil }
24
+ let(:asserted_attributes_opt) { nil }
22
25
  subject { described_class.new(
23
26
  reference_id,
24
27
  issuer_uri,
@@ -103,6 +106,76 @@ module SamlIdp
103
106
  expect(encrypted_xml).to_not match(audience_uri)
104
107
  end
105
108
 
109
+ describe "with name_id_formats_opt" do
110
+ let(:name_id_formats_opt) {
111
+ {
112
+ persistent: -> (principal) {
113
+ principal.unique_identifier
114
+ }
115
+ }
116
+ }
117
+ it "delegates name_id_formats to opts" do
118
+ UserWithUniqueId = Struct.new(:unique_identifier, :email, :asserted_attributes)
119
+ principal = UserWithUniqueId.new('unique_identifier_123456', 'foo@example.com', { emailAddress: { getter: :email } })
120
+ builder = described_class.new(
121
+ reference_id,
122
+ issuer_uri,
123
+ principal,
124
+ audience_uri,
125
+ saml_request_id,
126
+ saml_acs_url,
127
+ algorithm,
128
+ authn_context_classref,
129
+ expiry,
130
+ encryption_opts,
131
+ session_expiry,
132
+ name_id_formats_opt,
133
+ asserted_attributes_opt
134
+ )
135
+ Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
136
+ expect(builder.raw).to eq("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2010-06-01T13:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><Subject><NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\">unique_identifier_123456</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2010-06-01T13:03:00Z\" Recipient=\"http://saml.acs.url\"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore=\"2010-06-01T12:59:55Z\" NotOnOrAfter=\"2010-06-01T16:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AuthnStatement AuthnInstant=\"2010-06-01T13:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement><AttributeStatement><Attribute Name=\"emailAddress\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>")
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "with asserted_attributes_opt" do
142
+ let(:asserted_attributes_opt) {
143
+ {
144
+ 'GivenName' => {
145
+ getter: :first_name
146
+ },
147
+ 'SurName' => {
148
+ getter: -> (principal) {
149
+ principal.last_name
150
+ }
151
+ }
152
+ }
153
+ }
154
+
155
+ it "delegates asserted_attributes to opts" do
156
+ UserWithName = Struct.new(:email, :first_name, :last_name)
157
+ principal = UserWithName.new('foo@example.com', 'George', 'Washington')
158
+ builder = described_class.new(
159
+ reference_id,
160
+ issuer_uri,
161
+ principal,
162
+ audience_uri,
163
+ saml_request_id,
164
+ saml_acs_url,
165
+ algorithm,
166
+ authn_context_classref,
167
+ expiry,
168
+ encryption_opts,
169
+ session_expiry,
170
+ name_id_formats_opt,
171
+ asserted_attributes_opt
172
+ )
173
+ Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
174
+ expect(builder.raw).to eq("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2010-06-01T13:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">foo@example.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2010-06-01T13:03:00Z\" Recipient=\"http://saml.acs.url\"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore=\"2010-06-01T12:59:55Z\" NotOnOrAfter=\"2010-06-01T16:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AuthnStatement AuthnInstant=\"2010-06-01T13:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</AuthnContextClassRef></AuthnContext></AuthnStatement><AttributeStatement><Attribute Name=\"GivenName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"GivenName\"><AttributeValue>George</AttributeValue></Attribute><Attribute Name=\"SurName\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"SurName\"><AttributeValue>Washington</AttributeValue></Attribute></AttributeStatement></Assertion>")
175
+ end
176
+ end
177
+ end
178
+
106
179
  describe "with custom session_expiry configuration" do
107
180
  let(:config) { SamlIdp::Configurator.new }
108
181
  before do
@@ -9,6 +9,7 @@ module SamlIdp
9
9
  it { should respond_to :base_saml_location }
10
10
  it { should respond_to :reference_id_generator }
11
11
  it { should respond_to :attribute_service_location }
12
+ it { should respond_to :single_service_redirect_location }
12
13
  it { should respond_to :single_service_post_location }
13
14
  it { should respond_to :single_logout_service_post_location }
14
15
  it { should respond_to :single_logout_service_redirect_location }
@@ -21,6 +21,30 @@ describe SamlIdp::Controller do
21
21
  expect(saml_acs_url).to eq(requested_saml_acs_url)
22
22
  end
23
23
 
24
+ context "When SP metadata required to validate auth request signature" do
25
+ before do
26
+ idp_configure("https://foo.example.com/saml/consume", true)
27
+ params[:SAMLRequest] = make_saml_request("https://foo.example.com/saml/consume", true)
28
+ end
29
+
30
+ it 'SP metadata sign_authn_request attribute should be true' do
31
+ # Signed auth request will be true in the metadata
32
+ expect(SamlIdp.config.service_provider.persisted_metadata_getter.call(nil,nil)[:sign_authn_request]).to eq(true)
33
+ end
34
+
35
+ it 'should call xml signature validation method' do
36
+ signed_doc = SamlIdp::XMLSecurity::SignedDocument.new(params[:SAMLRequest])
37
+ allow(signed_doc).to receive(:validate).and_return(true)
38
+ allow(SamlIdp::XMLSecurity::SignedDocument).to receive(:new).and_return(signed_doc)
39
+ validate_saml_request
40
+ expect(signed_doc).to have_received(:validate).once
41
+ end
42
+
43
+ it 'should successfully validate signature' do
44
+ expect(validate_saml_request).to eq(true)
45
+ end
46
+ end
47
+
24
48
  context "SAML Responses" do
25
49
  let(:principal) { double email_address: "foo@example.com" }
26
50
  let (:encryption_opts) do
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ module SamlIdp
4
+ describe Fingerprint do
5
+ describe "certificate_digest" do
6
+ let(:cert) { sp_x509_cert }
7
+ let(:fingerprint) { "a2:cb:f6:6b:bc:2a:33:b9:4f:f3:c3:7e:26:a4:21:cd:41:83:ef:26:88:fa:ba:71:37:40:07:3e:d5:76:04:b7" }
8
+
9
+ it "returns the fingerprint string" do
10
+ expect(Fingerprint.certificate_digest(cert, :sha256)).to eq(fingerprint)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,7 +3,7 @@ module SamlIdp
3
3
 
4
4
  metadata_1 = <<-eos
5
5
  <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
6
- <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="true" WantAssertionsSigned="false">
6
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol" AuthnRequestsSigned="false" WantAssertionsSigned="false">
7
7
  </md:SPSSODescriptor>
8
8
  </md:EntityDescriptor>
9
9
  eos
@@ -22,10 +22,18 @@ module SamlIdp
22
22
  </md:EntityDescriptor>
23
23
  eos
24
24
 
25
+ metadata_4 = <<-eos
26
+ <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="test" entityID="https://test-saml.com/saml">
27
+ <md:SPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
28
+ </md:SPSSODescriptor>
29
+ </md:EntityDescriptor>
30
+ eos
31
+
25
32
  describe IncomingMetadata do
26
33
  it 'should properly set sign_assertions to false' do
27
34
  metadata = SamlIdp::IncomingMetadata.new(metadata_1)
28
35
  expect(metadata.sign_assertions).to eq(false)
36
+ expect(metadata.sign_authn_request).to eq(false)
29
37
  end
30
38
 
31
39
  it 'should properly set entity_id as https://test-saml.com/saml' do
@@ -36,11 +44,17 @@ module SamlIdp
36
44
  it 'should properly set sign_assertions to true' do
37
45
  metadata = SamlIdp::IncomingMetadata.new(metadata_2)
38
46
  expect(metadata.sign_assertions).to eq(true)
47
+ expect(metadata.sign_authn_request).to eq(true)
39
48
  end
40
49
 
41
50
  it 'should properly set sign_assertions to false when WantAssertionsSigned is not included' do
42
51
  metadata = SamlIdp::IncomingMetadata.new(metadata_3)
43
52
  expect(metadata.sign_assertions).to eq(false)
44
53
  end
54
+
55
+ it 'should properly set sign_authn_request to false when AuthnRequestsSigned is not included' do
56
+ metadata = SamlIdp::IncomingMetadata.new(metadata_4)
57
+ expect(metadata.sign_authn_request).to eq(false)
58
+ end
45
59
  end
46
60
  end
@@ -11,7 +11,30 @@ module SamlIdp
11
11
 
12
12
  it "includes logout element" do
13
13
  subject.configurator.single_logout_service_post_location = 'https://example.com/saml/logout'
14
+ subject.configurator.single_logout_service_redirect_location = 'https://example.com/saml/logout'
14
15
  expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/logout"/>')
16
+ expect(subject.fresh).to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/logout"/>')
17
+ end
18
+
19
+ it 'will not includes empty logout endpoint' do
20
+ subject.configurator.single_logout_service_post_location = ''
21
+ subject.configurator.single_logout_service_redirect_location = nil
22
+ expect(subject.fresh).not_to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"')
23
+ expect(subject.fresh).not_to match('<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"')
24
+ end
25
+
26
+ it 'will includes sso element' do
27
+ subject.configurator.single_service_post_location = 'https://example.com/saml/sso'
28
+ subject.configurator.single_service_redirect_location = 'https://example.com/saml/sso'
29
+ expect(subject.fresh).to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://example.com/saml/sso"/>')
30
+ expect(subject.fresh).to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://example.com/saml/sso"/>')
31
+ end
32
+
33
+ it 'will not includes empty sso element' do
34
+ subject.configurator.single_service_post_location = ''
35
+ subject.configurator.single_service_redirect_location = nil
36
+ expect(subject.fresh).not_to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"')
37
+ expect(subject.fresh).not_to match('<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"')
15
38
  end
16
39
 
17
40
  context "technical contact" do