saml_idp 0.7.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +41 -13
  4. data/lib/saml_idp/configurator.rb +5 -1
  5. data/lib/saml_idp/controller.rb +9 -5
  6. data/lib/saml_idp/incoming_metadata.rb +22 -1
  7. data/lib/saml_idp/metadata_builder.rb +23 -8
  8. data/lib/saml_idp/persisted_metadata.rb +4 -0
  9. data/lib/saml_idp/request.rb +22 -3
  10. data/lib/saml_idp/response_builder.rb +19 -5
  11. data/lib/saml_idp/saml_response.rb +15 -3
  12. data/lib/saml_idp/service_provider.rb +15 -6
  13. data/lib/saml_idp/signable.rb +1 -2
  14. data/lib/saml_idp/version.rb +1 -1
  15. data/lib/saml_idp/xml_security.rb +1 -1
  16. data/saml_idp.gemspec +25 -23
  17. data/spec/acceptance/idp_controller_spec.rb +5 -4
  18. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  19. data/spec/lib/saml_idp/assertion_builder_spec.rb +8 -8
  20. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  21. data/spec/lib/saml_idp/configurator_spec.rb +8 -7
  22. data/spec/lib/saml_idp/controller_spec.rb +47 -20
  23. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  24. data/spec/lib/saml_idp/incoming_metadata_spec.rb +60 -0
  25. data/spec/lib/saml_idp/metadata_builder_spec.rb +30 -17
  26. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  27. data/spec/lib/saml_idp/request_spec.rb +22 -22
  28. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  29. data/spec/lib/saml_idp/saml_response_spec.rb +31 -8
  30. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  31. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  32. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  33. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  34. data/spec/rails_app/app/controllers/saml_controller.rb +5 -1
  35. data/spec/rails_app/config/application.rb +0 -6
  36. data/spec/rails_app/config/environments/development.rb +1 -6
  37. data/spec/rails_app/config/environments/production.rb +1 -0
  38. data/spec/rails_app/config/environments/test.rb +1 -0
  39. data/spec/spec_helper.rb +22 -0
  40. data/spec/support/certificates/sp_cert_req.csr +12 -0
  41. data/spec/support/certificates/sp_private_key.pem +16 -0
  42. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  43. data/spec/support/saml_request_macros.rb +64 -4
  44. data/spec/support/security_helpers.rb +10 -0
  45. data/spec/xml_security_spec.rb +12 -12
  46. metadata +89 -52
  47. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
@@ -17,6 +17,7 @@ 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
20
21
 
21
22
  def initialize(reference_id,
22
23
  response_id,
@@ -29,7 +30,8 @@ module SamlIdp
29
30
  authn_context_classref,
30
31
  expiry=60*60,
31
32
  encryption_opts=nil,
32
- session_expiry=0
33
+ session_expiry=0,
34
+ signed_message_opts
33
35
  )
34
36
  self.reference_id = reference_id
35
37
  self.response_id = response_id
@@ -45,10 +47,11 @@ module SamlIdp
45
47
  self.expiry = expiry
46
48
  self.encryption_opts = encryption_opts
47
49
  self.session_expiry = session_expiry
50
+ self.signed_message_opts = signed_message_opts
48
51
  end
49
52
 
50
53
  def build
51
- @built ||= response_builder.encoded
54
+ @built ||= encoded_message
52
55
  end
53
56
 
54
57
  def signed_assertion
@@ -60,8 +63,17 @@ module SamlIdp
60
63
  end
61
64
  private :signed_assertion
62
65
 
66
+ def encoded_message
67
+ if signed_message_opts
68
+ response_builder.encoded(signed_message: true)
69
+ else
70
+ response_builder.encoded(signed_message: false)
71
+ end
72
+ end
73
+ private :encoded_message
74
+
63
75
  def response_builder
64
- ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion)
76
+ ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion, algorithm)
65
77
  end
66
78
  private :response_builder
67
79
 
@@ -13,6 +13,7 @@ module SamlIdp
13
13
  attribute :validate_signature
14
14
  attribute :acs_url
15
15
  attribute :assertion_consumer_logout_service_url
16
+ attribute :response_hosts
16
17
 
17
18
  delegate :config, to: :SamlIdp
18
19
 
@@ -21,18 +22,13 @@ module SamlIdp
21
22
  end
22
23
 
23
24
  def valid_signature?(doc, require_signature = false)
24
- if require_signature || should_validate_signature?
25
+ if require_signature || attributes[:validate_signature]
25
26
  doc.valid_signature?(fingerprint)
26
27
  else
27
28
  true
28
29
  end
29
30
  end
30
31
 
31
- def should_validate_signature?
32
- attributes[:validate_signature] ||
33
- current_metadata.respond_to?(:sign_assertions?) && current_metadata.sign_assertions?
34
- end
35
-
36
32
  def refresh_metadata
37
33
  fresh = fresh_incoming_metadata
38
34
  if valid_signature?(fresh.document)
@@ -46,6 +42,19 @@ module SamlIdp
46
42
  @current_metadata ||= get_current_or_build
47
43
  end
48
44
 
45
+ def acceptable_response_hosts
46
+ hosts = Array(self.response_hosts)
47
+ hosts.push(metadata_url_host) if metadata_url_host
48
+
49
+ hosts
50
+ end
51
+
52
+ def metadata_url_host
53
+ if metadata_url.present?
54
+ URI(metadata_url).host
55
+ end
56
+ end
57
+
49
58
  def get_current_or_build
50
59
  persisted = metadata_getter[identifier, self]
51
60
  if persisted.is_a? Hash
@@ -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.7.2'
3
+ VERSION = '0.11.0'
4
4
  end
@@ -108,7 +108,7 @@ module SamlIdp
108
108
  canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
109
109
  canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
110
110
 
111
- digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
111
+ digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod", {'ds' => DSIG}))
112
112
 
113
113
  hash = digest_algorithm.digest(canon_hashed_element)
114
114
  digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
@@ -7,28 +7,29 @@ Gem::Specification.new do |s|
7
7
  s.version = SamlIdp::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Jon Phenow"]
10
- s.email = %q{jon.phenow@sportngin.com}
11
- s.homepage = %q{http://github.com/sportngin/saml_idp}
12
- s.summary = %q{SAML Indentity Provider in ruby}
13
- s.description = %q{SAML IdP (Identity Provider) library in ruby}
10
+ s.email = 'jon.phenow@sportngin.com'
11
+ s.homepage = 'https://github.com/saml-idp/saml_idp'
12
+ s.summary = 'SAML Indentity Provider for Ruby'
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.glob("app/**/*") + Dir.glob("lib/**/*") + [
16
- "LICENSE",
17
- "README.md",
18
- "Gemfile",
19
- "saml_idp.gemspec"
20
- ]
15
+ s.files = Dir['app/**/*', 'lib/**/*', 'LICENSE', 'README.md', 'Gemfile', 'saml_idp.gemspec']
21
16
  s.required_ruby_version = '>= 2.2'
22
- s.license = "LICENSE"
17
+ s.license = 'MIT'
23
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
20
  s.require_paths = ["lib"]
26
- s.rdoc_options = ["--charset=UTF-8"]
21
+ s.rdoc_options = ['--charset=UTF-8']
22
+ s.metadata = {
23
+ 'homepage_uri' => 'https://github.com/saml-idp/saml_idp',
24
+ 'source_code_uri' => 'https://github.com/saml-idp/saml_idp',
25
+ 'bug_tracker_uri' => 'https://github.com/saml-idp/saml_idp/issues',
26
+ 'documentation_uri' => "http://rdoc.info/gems/saml_idp/#{SamlIdp::VERSION}"
27
+ }
27
28
 
28
29
  s.post_install_message = <<-INST
29
30
  If you're just recently updating saml_idp - please be aware we've changed the default
30
31
  certificate. See the PR and a description of why we've done this here:
31
- https://github.com/sportngin/saml_idp/pull/29
32
+ https://github.com/saml-idp/saml_idp/pull/29
32
33
 
33
34
  If you just need to see the certificate `bundle open saml_idp` and go to
34
35
  `lib/saml_idp/default.rb`
@@ -43,17 +44,18 @@ section of the README.
43
44
  INST
44
45
 
45
46
  s.add_dependency('activesupport', '>= 3.2')
46
- s.add_dependency('uuid', '~> 2.3')
47
- s.add_dependency('builder', '~> 3.0')
47
+ s.add_dependency('builder', '>= 3.0')
48
48
  s.add_dependency('nokogiri', '>= 1.6.2')
49
49
 
50
- s.add_development_dependency('rake', '~> 10.4.2')
51
- s.add_development_dependency('simplecov', '~> 0.12')
52
- s.add_development_dependency('rspec', '~> 2.5')
53
- s.add_development_dependency('ruby-saml', '~> 1.3')
54
- s.add_development_dependency('rails', '~> 3.2')
55
- s.add_development_dependency('capybara', '~> 2.11.0')
56
- s.add_development_dependency('timecop', '~> 0.8')
50
+ s.add_development_dependency('rake')
51
+ s.add_development_dependency('simplecov')
52
+ s.add_development_dependency('rspec', '>= 3.7.0')
53
+ s.add_development_dependency('ruby-saml', '>= 1.7.2')
54
+ s.add_development_dependency('rails', '>= 3.2')
55
+ s.add_development_dependency('activeresource', '>= 3.2')
56
+ s.add_development_dependency('capybara', '>= 2.16')
57
+ s.add_development_dependency('timecop', '>= 0.8')
57
58
  s.add_development_dependency('xmlenc', '>= 0.6.4')
59
+ s.add_development_dependency('appraisal')
60
+ s.add_development_dependency('byebug')
58
61
  end
59
-
@@ -4,11 +4,12 @@ feature 'IdpController' do
4
4
  scenario 'Login via default signup page' do
5
5
  saml_request = make_saml_request("http://foo.example.com/saml/consume")
6
6
  visit "/saml/auth?SAMLRequest=#{CGI.escape(saml_request)}"
7
- fill_in 'Email', :with => "foo@example.com"
8
- fill_in 'Password', :with => "okidoki"
7
+ expect(status_code).to eq(200)
8
+ fill_in 'email', :with => "foo@example.com"
9
+ fill_in 'password', :with => "okidoki"
9
10
  click_button 'Sign in'
10
11
  click_button 'Submit' # simulating onload
11
- current_url.should == 'http://foo.example.com/saml/consume'
12
- page.should have_content "foo@example.com"
12
+ expect(current_url).to eq('http://foo.example.com/saml/consume')
13
+ expect(page).to have_content "foo@example.com"
13
14
  end
14
15
  end
@@ -9,11 +9,11 @@ module SamlIdp
9
9
  end
10
10
 
11
11
  it "finds algorithm class" do
12
- algorithm.should == OpenSSL::Digest::SHA256
12
+ expect(algorithm).to eq(OpenSSL::Digest::SHA256)
13
13
  end
14
14
 
15
15
  it "finds the name" do
16
- algorithm_name.should == "sha256"
16
+ expect(algorithm_name).to eq("sha256")
17
17
  end
18
18
  end
19
19
 
@@ -23,11 +23,11 @@ module SamlIdp
23
23
  end
24
24
 
25
25
  it "finds algorithm class" do
26
- algorithm.should == OpenSSL::Digest::SHA512
26
+ expect(algorithm).to eq(OpenSSL::Digest::SHA512)
27
27
  end
28
28
 
29
29
  it "finds the name" do
30
- algorithm_name.should == "sha512"
30
+ expect(algorithm_name).to eq("sha512")
31
31
  end
32
32
  end
33
33
 
@@ -37,11 +37,11 @@ module SamlIdp
37
37
  end
38
38
 
39
39
  it "finds algorithm class" do
40
- algorithm.should == OpenSSL::Digest::SHA1
40
+ expect(algorithm).to eq(OpenSSL::Digest::SHA1)
41
41
  end
42
42
 
43
43
  it "finds the name" do
44
- algorithm_name.should == "sha1"
44
+ expect(algorithm_name).to eq("sha1")
45
45
  end
46
46
  end
47
47
  end
@@ -36,14 +36,14 @@ module SamlIdp
36
36
 
37
37
  it "builds a legit raw XML file" do
38
38
  Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
39
- subject.raw.should == "<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 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=\"email-address\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>"
39
+ expect(subject.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 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=\"email-address\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>")
40
40
  end
41
41
  end
42
42
  end
43
43
 
44
44
  it "builds a legit raw XML file" do
45
45
  Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
46
- subject.raw.should == "<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=\"email-address\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>"
46
+ expect(subject.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=\"email-address\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>")
47
47
  end
48
48
  end
49
49
 
@@ -55,12 +55,12 @@ module SamlIdp
55
55
  email_address: ->(p) { "foo@example.com" }
56
56
  }
57
57
  }
58
- SamlIdp.stub(config: config)
58
+ allow(SamlIdp).to receive(:config).and_return(config)
59
59
  end
60
60
 
61
61
  it "doesn't include attribute statement" do
62
62
  Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
63
- subject.raw.should == "<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></Assertion>"
63
+ expect(subject.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></Assertion>")
64
64
  end
65
65
  end
66
66
  end
@@ -81,7 +81,7 @@ module SamlIdp
81
81
  expiry
82
82
  )
83
83
  Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
84
- builder.raw.should == "<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=\"emailAddress\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>"
84
+ 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=\"emailAddress\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:uri\" FriendlyName=\"emailAddress\"><AttributeValue>foo@example.com</AttributeValue></Attribute></AttributeStatement></Assertion>")
85
85
  end
86
86
  end
87
87
  end
@@ -100,14 +100,14 @@ module SamlIdp
100
100
  encryption_opts
101
101
  )
102
102
  encrypted_xml = builder.encrypt
103
- encrypted_xml.should_not match(audience_uri)
103
+ expect(encrypted_xml).to_not match(audience_uri)
104
104
  end
105
105
 
106
106
  describe "with custom session_expiry configuration" do
107
107
  let(:config) { SamlIdp::Configurator.new }
108
108
  before do
109
109
  config.session_expiry = 8
110
- SamlIdp.stub(config: config)
110
+ allow(SamlIdp).to receive(:config).and_return(config)
111
111
  end
112
112
 
113
113
  it "sets default session_expiry from config" do
@@ -123,7 +123,7 @@ module SamlIdp
123
123
  expiry,
124
124
  encryption_opts
125
125
  )
126
- builder.session_expiry.should == 8
126
+ expect(builder.session_expiry).to eq(8)
127
127
  end
128
128
  end
129
129
  end
@@ -12,19 +12,19 @@ module SamlIdp
12
12
  let(:values) { nil }
13
13
 
14
14
  it "has a valid name" do
15
- subject.name.should be_nil
15
+ expect(subject.name).to be_nil
16
16
  end
17
17
 
18
18
  it "has a valid friendly_name" do
19
- subject.friendly_name.should be_nil
19
+ expect(subject.friendly_name).to be_nil
20
20
  end
21
21
 
22
22
  it "has a valid name_format" do
23
- subject.name_format.should == Saml::XML::Namespaces::Formats::Attr::URI
23
+ expect(subject.name_format).to eq(Saml::XML::Namespaces::Formats::Attr::URI)
24
24
  end
25
25
 
26
26
  it "has a valid values" do
27
- subject.values.should == []
27
+ expect(subject.values).to eq []
28
28
  end
29
29
 
30
30
  describe "with values set" do
@@ -34,19 +34,19 @@ module SamlIdp
34
34
  let(:values) { :val }
35
35
 
36
36
  it "has a valid name" do
37
- subject.name.should == name
37
+ expect(subject.name).to eq(name)
38
38
  end
39
39
 
40
40
  it "has a valid friendly_name" do
41
- subject.friendly_name.should == friendly_name
41
+ expect(subject.friendly_name).to eq(friendly_name)
42
42
  end
43
43
 
44
44
  it "has a valid name_format" do
45
- subject.name_format.should == name_format
45
+ expect(subject.name_format).to eq(name_format)
46
46
  end
47
47
 
48
48
  it "has a valid values" do
49
- subject.values.should == [values]
49
+ expect(subject.values).to eq [values]
50
50
  end
51
51
  end
52
52
  end
@@ -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 }
@@ -18,32 +19,32 @@ module SamlIdp
18
19
  it { should respond_to :session_expiry }
19
20
 
20
21
  it "has a valid x509_certificate" do
21
- subject.x509_certificate.should == Default::X509_CERTIFICATE
22
+ expect(subject.x509_certificate).to eq(Default::X509_CERTIFICATE)
22
23
  end
23
24
 
24
25
  it "has a valid secret_key" do
25
- subject.secret_key.should == Default::SECRET_KEY
26
+ expect(subject.secret_key).to eq(Default::SECRET_KEY)
26
27
  end
27
28
 
28
29
  it "has a valid algorithm" do
29
- subject.algorithm.should == :sha1
30
+ expect(subject.algorithm).to eq(:sha1)
30
31
  end
31
32
 
32
33
  it "has a valid reference_id_generator" do
33
- subject.reference_id_generator.should respond_to :call
34
+ expect(subject.reference_id_generator).to respond_to :call
34
35
  end
35
36
 
36
37
 
37
38
  it "can call service provider finder" do
38
- subject.service_provider.finder.should respond_to :call
39
+ expect(subject.service_provider.finder).to respond_to :call
39
40
  end
40
41
 
41
42
  it "can call service provider metadata persister" do
42
- subject.service_provider.metadata_persister.should respond_to :call
43
+ expect(subject.service_provider.metadata_persister).to respond_to :call
43
44
  end
44
45
 
45
46
  it 'has a valid session_expiry' do
46
- subject.session_expiry.should == 0
47
+ expect(subject.session_expiry).to eq(0)
47
48
  end
48
49
  end
49
50
  end
@@ -7,6 +7,9 @@ describe SamlIdp::Controller do
7
7
  def render(*)
8
8
  end
9
9
 
10
+ def head(*)
11
+ end
12
+
10
13
  def params
11
14
  @params ||= {}
12
15
  end
@@ -14,8 +17,32 @@ describe SamlIdp::Controller do
14
17
  it "should find the SAML ACS URL" do
15
18
  requested_saml_acs_url = "https://example.com/saml/consume"
16
19
  params[:SAMLRequest] = make_saml_request(requested_saml_acs_url)
17
- validate_saml_request
18
- saml_acs_url.should == requested_saml_acs_url
20
+ expect(validate_saml_request).to eq(true)
21
+ expect(saml_acs_url).to eq(requested_saml_acs_url)
22
+ end
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
19
46
  end
20
47
 
21
48
  context "SAML Responses" do
@@ -32,36 +59,36 @@ describe SamlIdp::Controller do
32
59
  it "should create a SAML Response" do
33
60
  saml_response = encode_response(principal, { audience_uri: 'http://example.com/issuer', issuer_uri: 'http://example.com', acs_url: 'https://foo.example.com/saml/consume' })
34
61
  response = OneLogin::RubySaml::Response.new(saml_response)
35
- response.name_id.should == "foo@example.com"
36
- response.issuers.first.should == "http://example.com"
62
+ expect(response.name_id).to eq("foo@example.com")
63
+ expect(response.issuers.first).to eq("http://example.com")
37
64
  response.settings = saml_settings
38
- response.is_valid?.should be_truthy
65
+ expect(response.is_valid?).to be_truthy
39
66
  end
40
67
  end
41
68
 
42
69
  context "solicited Response" do
43
70
  before(:each) do
44
71
  params[:SAMLRequest] = make_saml_request
45
- validate_saml_request
72
+ expect(validate_saml_request).to eq(true)
46
73
  end
47
74
 
48
75
  it "should create a SAML Response" do
49
76
  saml_response = encode_response(principal)
50
77
  response = OneLogin::RubySaml::Response.new(saml_response)
51
- response.name_id.should == "foo@example.com"
52
- response.issuers.first.should == "http://example.com"
78
+ expect(response.name_id).to eq("foo@example.com")
79
+ expect(response.issuers.first).to eq("http://example.com")
53
80
  response.settings = saml_settings
54
- response.is_valid?.should be_truthy
81
+ expect(response.is_valid?).to be_truthy
55
82
  end
56
83
 
57
84
  it "should create a SAML Logout Response" do
58
85
  params[:SAMLRequest] = make_saml_logout_request
59
- validate_saml_request
86
+ expect(validate_saml_request).to eq(true)
60
87
  expect(saml_request.logout_request?).to eq true
61
88
  saml_response = encode_response(principal)
62
89
  response = OneLogin::RubySaml::Logoutresponse.new(saml_response, saml_settings)
63
- response.validate.should == true
64
- response.issuer.should == "http://example.com"
90
+ expect(response.validate).to eq(true)
91
+ expect(response.issuer).to eq("http://example.com")
65
92
  end
66
93
 
67
94
 
@@ -70,10 +97,10 @@ describe SamlIdp::Controller do
70
97
  self.algorithm = algorithm_name
71
98
  saml_response = encode_response(principal)
72
99
  response = OneLogin::RubySaml::Response.new(saml_response)
73
- response.name_id.should == "foo@example.com"
74
- response.issuers.first.should == "http://example.com"
100
+ expect(response.name_id).to eq("foo@example.com")
101
+ expect(response.issuers.first).to eq("http://example.com")
75
102
  response.settings = saml_settings
76
- response.is_valid?.should be_truthy
103
+ expect(response.is_valid?).to be_truthy
77
104
  end
78
105
 
79
106
  it "should encrypt SAML Response assertion" do
@@ -82,11 +109,11 @@ describe SamlIdp::Controller do
82
109
  resp_settings = saml_settings
83
110
  resp_settings.private_key = SamlIdp::Default::SECRET_KEY
84
111
  response = OneLogin::RubySaml::Response.new(saml_response, settings: resp_settings)
85
- response.document.to_s.should_not match("foo@example.com")
86
- response.decrypted_document.to_s.should match("foo@example.com")
87
- response.name_id.should == "foo@example.com"
88
- response.issuers.first.should == "http://example.com"
89
- response.is_valid?.should be_truthy
112
+ expect(response.document.to_s).to_not match("foo@example.com")
113
+ expect(response.decrypted_document.to_s).to match("foo@example.com")
114
+ expect(response.name_id).to eq("foo@example.com")
115
+ expect(response.issuers.first).to eq("http://example.com")
116
+ expect(response.is_valid?).to be_truthy
90
117
  end
91
118
  end
92
119
  end