saml_idp 0.7.2 → 0.11.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 (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