saml_idp 0.0.1

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 (122) hide show
  1. checksums.yaml +15 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE +22 -0
  4. data/README.md +197 -0
  5. data/app/controllers/saml_idp/idp_controller.rb +42 -0
  6. data/app/views/saml_idp/idp/new.html.erb +21 -0
  7. data/app/views/saml_idp/idp/saml_post.html.erb +13 -0
  8. data/lib/saml_idp.rb +92 -0
  9. data/lib/saml_idp/algorithmable.rb +19 -0
  10. data/lib/saml_idp/assertion_builder.rb +144 -0
  11. data/lib/saml_idp/attribute_decorator.rb +27 -0
  12. data/lib/saml_idp/attributeable.rb +24 -0
  13. data/lib/saml_idp/configurator.rb +45 -0
  14. data/lib/saml_idp/controller.rb +71 -0
  15. data/lib/saml_idp/default.rb +28 -0
  16. data/lib/saml_idp/engine.rb +5 -0
  17. data/lib/saml_idp/hashable.rb +26 -0
  18. data/lib/saml_idp/incoming_metadata.rb +144 -0
  19. data/lib/saml_idp/metadata_builder.rb +158 -0
  20. data/lib/saml_idp/name_id_formatter.rb +68 -0
  21. data/lib/saml_idp/persisted_metadata.rb +10 -0
  22. data/lib/saml_idp/request.rb +79 -0
  23. data/lib/saml_idp/response_builder.rb +60 -0
  24. data/lib/saml_idp/saml_response.rb +63 -0
  25. data/lib/saml_idp/service_provider.rb +68 -0
  26. data/lib/saml_idp/signable.rb +131 -0
  27. data/lib/saml_idp/signature_builder.rb +42 -0
  28. data/lib/saml_idp/signed_info_builder.rb +51 -0
  29. data/lib/saml_idp/version.rb +4 -0
  30. data/lib/saml_idp/xml_security.rb +168 -0
  31. data/saml_idp.gemspec +38 -0
  32. data/spec/acceptance/acceptance_helper.rb +9 -0
  33. data/spec/acceptance/idp_controller_spec.rb +16 -0
  34. data/spec/lib/saml_idp/algorithmable_spec.rb +48 -0
  35. data/spec/lib/saml_idp/assertion_builder_spec.rb +27 -0
  36. data/spec/lib/saml_idp/attribute_decorator_spec.rb +31 -0
  37. data/spec/lib/saml_idp/configurator_spec.rb +30 -0
  38. data/spec/lib/saml_idp/controller_spec.rb +51 -0
  39. data/spec/lib/saml_idp/metadata_builder_spec.rb +9 -0
  40. data/spec/lib/saml_idp/name_id_formatter_spec.rb +39 -0
  41. data/spec/lib/saml_idp/request_spec.rb +14 -0
  42. data/spec/lib/saml_idp/response_builder_spec.rb +32 -0
  43. data/spec/lib/saml_idp/saml_response_spec.rb +27 -0
  44. data/spec/lib/saml_idp/service_provider_spec.rb +21 -0
  45. data/spec/lib/saml_idp/signable_spec.rb +74 -0
  46. data/spec/lib/saml_idp/signature_builder_spec.rb +19 -0
  47. data/spec/lib/saml_idp/signed_info_builder_spec.rb +25 -0
  48. data/spec/rails_app/.gitignore +15 -0
  49. data/spec/rails_app/README.rdoc +261 -0
  50. data/spec/rails_app/Rakefile +7 -0
  51. data/spec/rails_app/app/assets/images/rails.png +0 -0
  52. data/spec/rails_app/app/assets/javascripts/application.js +15 -0
  53. data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
  54. data/spec/rails_app/app/controllers/application_controller.rb +3 -0
  55. data/spec/rails_app/app/controllers/saml_controller.rb +8 -0
  56. data/spec/rails_app/app/controllers/saml_idp_controller.rb +11 -0
  57. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  58. data/spec/rails_app/app/mailers/.gitkeep +0 -0
  59. data/spec/rails_app/app/models/.gitkeep +0 -0
  60. data/spec/rails_app/app/views/layouts/application.html.erb +14 -0
  61. data/spec/rails_app/config.ru +4 -0
  62. data/spec/rails_app/config/application.rb +60 -0
  63. data/spec/rails_app/config/boot.rb +6 -0
  64. data/spec/rails_app/config/database.yml +25 -0
  65. data/spec/rails_app/config/environment.rb +5 -0
  66. data/spec/rails_app/config/environments/development.rb +37 -0
  67. data/spec/rails_app/config/environments/production.rb +67 -0
  68. data/spec/rails_app/config/environments/test.rb +37 -0
  69. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  70. data/spec/rails_app/config/initializers/inflections.rb +15 -0
  71. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  72. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  73. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  74. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  75. data/spec/rails_app/config/locales/en.yml +5 -0
  76. data/spec/rails_app/config/routes.rb +6 -0
  77. data/spec/rails_app/db/seeds.rb +7 -0
  78. data/spec/rails_app/doc/README_FOR_APP +2 -0
  79. data/spec/rails_app/lib/assets/.gitkeep +0 -0
  80. data/spec/rails_app/lib/tasks/.gitkeep +0 -0
  81. data/spec/rails_app/log/.gitkeep +0 -0
  82. data/spec/rails_app/public/404.html +26 -0
  83. data/spec/rails_app/public/422.html +26 -0
  84. data/spec/rails_app/public/500.html +25 -0
  85. data/spec/rails_app/public/favicon.ico +0 -0
  86. data/spec/rails_app/public/index.html +241 -0
  87. data/spec/rails_app/public/robots.txt +5 -0
  88. data/spec/rails_app/script/rails +6 -0
  89. data/spec/rails_app/test/fixtures/.gitkeep +0 -0
  90. data/spec/rails_app/test/functional/.gitkeep +0 -0
  91. data/spec/rails_app/test/integration/.gitkeep +0 -0
  92. data/spec/rails_app/test/performance/browsing_test.rb +12 -0
  93. data/spec/rails_app/test/test_helper.rb +13 -0
  94. data/spec/rails_app/test/unit/.gitkeep +0 -0
  95. data/spec/rails_app/vendor/assets/javascripts/.gitkeep +0 -0
  96. data/spec/rails_app/vendor/assets/stylesheets/.gitkeep +0 -0
  97. data/spec/rails_app/vendor/plugins/.gitkeep +0 -0
  98. data/spec/spec_helper.rb +49 -0
  99. data/spec/support/certificates/certificate1 +12 -0
  100. data/spec/support/certificates/r1_certificate2_base64 +1 -0
  101. data/spec/support/responses/adfs_response_sha1.xml +46 -0
  102. data/spec/support/responses/adfs_response_sha256.xml +46 -0
  103. data/spec/support/responses/adfs_response_sha384.xml +46 -0
  104. data/spec/support/responses/adfs_response_sha512.xml +46 -0
  105. data/spec/support/responses/logoutresponse_fixtures.rb +67 -0
  106. data/spec/support/responses/no_signature_ns.xml +48 -0
  107. data/spec/support/responses/open_saml_response.xml +56 -0
  108. data/spec/support/responses/r1_response6.xml.base64 +1 -0
  109. data/spec/support/responses/response1.xml.base64 +1 -0
  110. data/spec/support/responses/response2.xml.base64 +79 -0
  111. data/spec/support/responses/response3.xml.base64 +66 -0
  112. data/spec/support/responses/response4.xml.base64 +93 -0
  113. data/spec/support/responses/response5.xml.base64 +102 -0
  114. data/spec/support/responses/response_with_ampersands.xml +139 -0
  115. data/spec/support/responses/response_with_ampersands.xml.base64 +93 -0
  116. data/spec/support/responses/simple_saml_php.xml +71 -0
  117. data/spec/support/responses/starfield_response.xml.base64 +1 -0
  118. data/spec/support/responses/wrapped_response_2.xml.base64 +150 -0
  119. data/spec/support/saml_request_macros.rb +19 -0
  120. data/spec/support/security_helpers.rb +61 -0
  121. data/spec/xml_security_spec.rb +136 -0
  122. metadata +407 -0
@@ -0,0 +1,42 @@
1
+ require 'builder'
2
+ module SamlIdp
3
+ class SignatureBuilder
4
+ attr_accessor :signed_info_builder
5
+
6
+ def initialize(signed_info_builder)
7
+ self.signed_info_builder = signed_info_builder
8
+ end
9
+
10
+ def raw
11
+ builder = Builder::XmlMarkup.new
12
+ builder.tag! "ds:Signature", "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#" do |signature|
13
+ signature << signed_info
14
+ signature.tag! "ds:SignatureValue", signature_value
15
+ signature.KeyInfo xmlns: "http://www.w3.org/2000/09/xmldsig#" do |key_info|
16
+ key_info.tag! "ds:X509Data" do |x509|
17
+ x509.tag! "ds:X509Certificate", x509_certificate
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ def x509_certificate
24
+ SamlIdp.config.x509_certificate
25
+ .to_s
26
+ .gsub(/-----BEGIN CERTIFICATE-----/,"")
27
+ .gsub(/-----END CERTIFICATE-----/,"")
28
+ .gsub(/\n/, "")
29
+ end
30
+ private :x509_certificate
31
+
32
+ def signed_info
33
+ signed_info_builder.raw
34
+ end
35
+ private :signed_info
36
+
37
+ def signature_value
38
+ signed_info_builder.signed
39
+ end
40
+ private :signature_value
41
+ end
42
+ end
@@ -0,0 +1,51 @@
1
+ require 'builder'
2
+ module SamlIdp
3
+ class SignedInfoBuilder
4
+ include Algorithmable
5
+ attr_accessor :reference_id
6
+ attr_accessor :digest_value
7
+ attr_accessor :raw_algorithm
8
+
9
+ def initialize(reference_id, digest_value, raw_algorithm)
10
+ self.reference_id = reference_id
11
+ self.digest_value = digest_value
12
+ self.raw_algorithm = raw_algorithm
13
+ end
14
+
15
+ def raw
16
+ builder = Builder::XmlMarkup.new
17
+ builder.tag! "ds:SignedInfo", "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#" do |signed_info|
18
+ signed_info.tag!("ds:CanonicalizationMethod", Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#") {}
19
+ signed_info.tag!("ds:SignatureMethod", Algorithm: "http://www.w3.org/2000/09/xmldsig#rsa-#{algorithm_name}") {}
20
+ signed_info.tag! "ds:Reference", URI: reference_string do |reference|
21
+ reference.tag! "ds:Transforms" do |transforms|
22
+ transforms.tag!("ds:Transform", Algorithm: "http://www.w3.org/2000/09/xmldsig#enveloped-signature") {}
23
+ transforms.tag!("ds:Transform", Algorithm: "http://www.w3.org/2001/10/xml-exc-c14n#") {}
24
+ end
25
+ reference.tag!("ds:DigestMethod", Algorithm: "http://www.w3.org/2000/09/xmldsig##{algorithm_name}") {}
26
+ reference.tag! "ds:DigestValue", digest_value
27
+ end
28
+ end
29
+ end
30
+
31
+ def signed
32
+ encoded.gsub(/\n/, "")
33
+ end
34
+
35
+ def secret_key
36
+ SamlIdp.config.secret_key
37
+ end
38
+ private :secret_key
39
+
40
+ def encoded
41
+ key = OpenSSL::PKey::RSA.new(secret_key)
42
+ Base64.encode64(key.sign(algorithm.new, raw))
43
+ end
44
+ private :encoded
45
+
46
+ def reference_string
47
+ "#_#{reference_id}"
48
+ end
49
+ private :reference_string
50
+ end
51
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+ module SamlIdp
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,168 @@
1
+ # The contents of this file are subject to the terms
2
+ # of the Common Development and Distribution License
3
+ # (the License). You may not use this file except in
4
+ # compliance with the License.
5
+ #
6
+ # You can obtain a copy of the License at
7
+ # https://opensso.dev.java.net/public/CDDLv1.0.html or
8
+ # opensso/legal/CDDLv1.0.txt
9
+ # See the License for the specific language governing
10
+ # permission and limitations under the License.
11
+ #
12
+ # When distributing Covered Code, include this CDDL
13
+ # Header Notice in each file and include the License file
14
+ # at opensso/legal/CDDLv1.0.txt.
15
+ # If applicable, add the following below the CDDL Header,
16
+ # with the fields enclosed by brackets [] replaced by
17
+ # your own identifying information:
18
+ # "Portions Copyrighted [year] [name of copyright owner]"
19
+ #
20
+ # $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
21
+ #
22
+ # Copyright 2007 Sun Microsystems Inc. All Rights Reserved
23
+ # Portions Copyrighted 2007 Todd W Saxton.
24
+
25
+ require "rexml/document"
26
+ require "rexml/xpath"
27
+ require "openssl"
28
+ require 'nokogiri'
29
+ require "digest/sha1"
30
+ require "digest/sha2"
31
+
32
+ module SamlIdp
33
+ module XMLSecurity
34
+ class SignedDocument < REXML::Document
35
+ ValidationError = Class.new(StandardError)
36
+ C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
37
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
38
+
39
+ attr_accessor :signed_element_id
40
+
41
+ def initialize(response)
42
+ super(response)
43
+ extract_signed_element_id
44
+ end
45
+
46
+ def validate(idp_cert_fingerprint, soft = true)
47
+ # get cert from response
48
+ cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
49
+ raise ValidationError.new("Certificate element missing in response (ds:X509Certificate)") unless cert_element
50
+ base64_cert = cert_element.text
51
+ cert_text = Base64.decode64(base64_cert)
52
+ cert = OpenSSL::X509::Certificate.new(cert_text)
53
+
54
+ # check cert matches registered idp cert
55
+ fingerprint = Digest::SHA1.hexdigest(cert.to_der)
56
+
57
+ if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
58
+ return soft ? false : (raise ValidationError.new("Fingerprint mismatch"))
59
+ end
60
+
61
+ validate_doc(base64_cert, soft)
62
+ end
63
+
64
+ def validate_doc(base64_cert, soft = true)
65
+ # validate references
66
+
67
+ # check for inclusive namespaces
68
+ inclusive_namespaces = extract_inclusive_namespaces
69
+
70
+ document = Nokogiri.parse(self.to_s)
71
+
72
+ # create a working copy so we don't modify the original
73
+ @working_copy ||= REXML::Document.new(self.to_s).root
74
+
75
+ # store and remove signature node
76
+ @sig_element ||= begin
77
+ element = REXML::XPath.first(@working_copy, "//ds:Signature", {"ds"=>DSIG})
78
+ element.remove
79
+ end
80
+
81
+
82
+ # verify signature
83
+ signed_info_element = REXML::XPath.first(@sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
84
+ noko_sig_element = document.at_xpath('//ds:Signature', 'ds' => DSIG)
85
+ noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
86
+ canon_algorithm = canon_algorithm REXML::XPath.first(@sig_element, '//ds:CanonicalizationMethod', 'ds' => DSIG)
87
+ canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
88
+ noko_sig_element.remove
89
+
90
+ # check digests
91
+ REXML::XPath.each(@sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
92
+ uri = ref.attributes.get_attribute("URI").value
93
+
94
+ hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
95
+ canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod', 'ds' => DSIG)
96
+ canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces)
97
+
98
+ digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
99
+
100
+ hash = digest_algorithm.digest(canon_hashed_element)
101
+ digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
102
+
103
+ unless digests_match?(hash, digest_value)
104
+ return soft ? false : (raise ValidationError.new("Digest mismatch"))
105
+ end
106
+ end
107
+
108
+ base64_signature = REXML::XPath.first(@sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
109
+ signature = Base64.decode64(base64_signature)
110
+
111
+ # get certificate object
112
+ cert_text = Base64.decode64(base64_cert)
113
+ cert = OpenSSL::X509::Certificate.new(cert_text)
114
+
115
+ # signature method
116
+ signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
117
+
118
+ unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
119
+ return soft ? false : (raise ValidationError.new("Key validation error"))
120
+ end
121
+
122
+ return true
123
+ end
124
+
125
+ private
126
+
127
+ def digests_match?(hash, digest_value)
128
+ hash == digest_value
129
+ end
130
+
131
+ def extract_signed_element_id
132
+ reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
133
+ self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
134
+ end
135
+
136
+ def canon_algorithm(element)
137
+ algorithm = element.attribute('Algorithm').value if element
138
+ case algorithm
139
+ when "http://www.w3.org/2001/10/xml-exc-c14n#" then Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
140
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
141
+ when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
142
+ else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
143
+ end
144
+ end
145
+
146
+ def algorithm(element)
147
+ algorithm = element.attribute("Algorithm").value if element
148
+ algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
149
+ case algorithm
150
+ when 256 then OpenSSL::Digest::SHA256
151
+ when 384 then OpenSSL::Digest::SHA384
152
+ when 512 then OpenSSL::Digest::SHA512
153
+ else
154
+ OpenSSL::Digest::SHA1
155
+ end
156
+ end
157
+
158
+ def extract_inclusive_namespaces
159
+ if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
160
+ prefix_list = element.attributes.get_attribute("PrefixList").value
161
+ prefix_list.split(" ")
162
+ else
163
+ []
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "saml_idp/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{saml_idp}
7
+ s.version = SamlIdp::VERSION
8
+ s.platform = Gem::Platform::RUBY
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}
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
+ ]
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+ s.rdoc_options = ["--charset=UTF-8"]
25
+ s.add_dependency('activesupport')
26
+ s.add_dependency('uuid')
27
+ s.add_dependency('builder')
28
+ s.add_dependency('httparty')
29
+
30
+ s.add_development_dependency "rake"
31
+ s.add_development_dependency "simplecov"
32
+ s.add_development_dependency "rspec"
33
+ s.add_development_dependency "ruby-saml"
34
+ s.add_development_dependency("rails", "~> 3.2")
35
+ s.add_development_dependency("capybara")
36
+ s.add_development_dependency("timecop")
37
+ end
38
+
@@ -0,0 +1,9 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
2
+ require 'capybara/rspec'
3
+
4
+ # Put your acceptance spec helpers inside /spec/acceptance/support
5
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
6
+
7
+ RSpec.configure do |config|
8
+ config.include Rails.application.routes.url_helpers, :type => :request
9
+ end
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/acceptance_helper')
2
+
3
+ feature 'IdpController' do
4
+
5
+ scenario 'Login via default signup page' do
6
+ saml_request = make_saml_request("http://foo.example.com/saml/consume")
7
+ visit "/saml/auth?SAMLRequest=#{CGI.escape(saml_request)}"
8
+ fill_in 'Email', :with => "foo@example.com"
9
+ fill_in 'Password', :with => "okidoki"
10
+ click_button 'Sign in'
11
+ click_button 'Submit' # simulating onload
12
+ current_url.should == 'http://foo.example.com/saml/consume'
13
+ page.should have_content "foo@example.com"
14
+ end
15
+
16
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe "Algorithmable" do
4
+ include Algorithmable
5
+
6
+ describe "named raw algorithm" do
7
+ def raw_algorithm
8
+ :sha256
9
+ end
10
+
11
+ it "finds algorithm class" do
12
+ algorithm.should == OpenSSL::Digest::SHA256
13
+ end
14
+
15
+ it "finds the name" do
16
+ algorithm_name.should == "sha256"
17
+ end
18
+ end
19
+
20
+ describe "class raw algorithm" do
21
+ def raw_algorithm
22
+ OpenSSL::Digest::SHA512
23
+ end
24
+
25
+ it "finds algorithm class" do
26
+ algorithm.should == OpenSSL::Digest::SHA512
27
+ end
28
+
29
+ it "finds the name" do
30
+ algorithm_name.should == "sha512"
31
+ end
32
+ end
33
+
34
+ describe "nonexistent raw algorithm" do
35
+ def raw_algorithm
36
+ :sha1024
37
+ end
38
+
39
+ it "finds algorithm class" do
40
+ algorithm.should == OpenSSL::Digest::SHA1
41
+ end
42
+
43
+ it "finds the name" do
44
+ algorithm_name.should == "sha1"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe AssertionBuilder do
4
+ let(:reference_id) { "abc" }
5
+ let(:issuer_uri) { "http://sportngin.com" }
6
+ let(:name_id) { "jon.phenow@sportngin.com" }
7
+ let(:audience_uri) { "http://example.com" }
8
+ let(:saml_request_id) { "123" }
9
+ let(:saml_acs_url) { "http://saml.acs.url" }
10
+ let(:algorithm) { :sha256 }
11
+ subject { described_class.new(
12
+ reference_id,
13
+ issuer_uri,
14
+ name_id,
15
+ audience_uri,
16
+ saml_request_id,
17
+ saml_acs_url,
18
+ algorithm
19
+ ) }
20
+
21
+ it "builds a legit raw XML file" do
22
+ Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do
23
+ 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-01T14:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><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><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>"
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ module SamlIdp
3
+ describe AttributeDecorator do
4
+ subject { described_class.new name: name,
5
+ friendly_name: friendly_name,
6
+ name_format: name_format,
7
+ values: values
8
+ }
9
+ let(:name) { nil }
10
+ let(:friendly_name) { nil }
11
+ let(:name_format) { nil }
12
+ let(:values) { nil }
13
+
14
+ its(:name) { should be_nil }
15
+ its(:friendly_name) { should be_nil }
16
+ its(:name_format) { should == Saml::XML::Namespaces::Formats::Attr::URI }
17
+ its(:values) { should == [] }
18
+
19
+ describe "with values set" do
20
+ let(:name) { "test" }
21
+ let(:friendly_name) { "test too" }
22
+ let(:name_format) { "some format" }
23
+ let(:values) { :val }
24
+
25
+ its(:name) { should == name }
26
+ its(:friendly_name) { should == friendly_name }
27
+ its(:name_format) { should == name_format }
28
+ its(:values) { should == [values] }
29
+ end
30
+ end
31
+ end