saml_idp 0.7.2 → 1.0.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 (59) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +1 -1
  3. data/README.md +71 -55
  4. data/lib/saml_idp/assertion_builder.rb +28 -3
  5. data/lib/saml_idp/configurator.rb +9 -3
  6. data/lib/saml_idp/controller.rb +27 -16
  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 +31 -1
  10. data/lib/saml_idp/metadata_builder.rb +25 -9
  11. data/lib/saml_idp/persisted_metadata.rb +4 -0
  12. data/lib/saml_idp/request.rb +103 -13
  13. data/lib/saml_idp/response_builder.rb +26 -6
  14. data/lib/saml_idp/saml_response.rb +62 -28
  15. data/lib/saml_idp/service_provider.rb +16 -6
  16. data/lib/saml_idp/signable.rb +1 -2
  17. data/lib/saml_idp/signature_builder.rb +2 -1
  18. data/lib/saml_idp/signed_info_builder.rb +2 -2
  19. data/lib/saml_idp/version.rb +1 -1
  20. data/lib/saml_idp/xml_security.rb +20 -15
  21. data/lib/saml_idp.rb +4 -3
  22. data/saml_idp.gemspec +46 -42
  23. data/spec/acceptance/idp_controller_spec.rb +5 -4
  24. data/spec/lib/saml_idp/algorithmable_spec.rb +6 -6
  25. data/spec/lib/saml_idp/assertion_builder_spec.rb +151 -8
  26. data/spec/lib/saml_idp/attribute_decorator_spec.rb +8 -8
  27. data/spec/lib/saml_idp/configurator_spec.rb +45 -7
  28. data/spec/lib/saml_idp/controller_spec.rb +86 -25
  29. data/spec/lib/saml_idp/encryptor_spec.rb +4 -4
  30. data/spec/lib/saml_idp/fingerprint_spec.rb +14 -0
  31. data/spec/lib/saml_idp/incoming_metadata_spec.rb +134 -0
  32. data/spec/lib/saml_idp/metadata_builder_spec.rb +30 -17
  33. data/spec/lib/saml_idp/name_id_formatter_spec.rb +3 -3
  34. data/spec/lib/saml_idp/request_spec.rb +153 -64
  35. data/spec/lib/saml_idp/response_builder_spec.rb +5 -3
  36. data/spec/lib/saml_idp/saml_response_spec.rb +146 -12
  37. data/spec/lib/saml_idp/service_provider_spec.rb +2 -2
  38. data/spec/lib/saml_idp/signable_spec.rb +1 -1
  39. data/spec/lib/saml_idp/signature_builder_spec.rb +2 -2
  40. data/spec/lib/saml_idp/signed_info_builder_spec.rb +3 -3
  41. data/spec/rails_app/app/controllers/saml_controller.rb +1 -1
  42. data/spec/rails_app/app/controllers/saml_idp_controller.rb +55 -3
  43. data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +3 -4
  44. data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
  45. data/spec/rails_app/config/application.rb +1 -6
  46. data/spec/rails_app/config/boot.rb +1 -1
  47. data/spec/rails_app/config/environments/development.rb +2 -5
  48. data/spec/rails_app/config/environments/production.rb +1 -0
  49. data/spec/rails_app/config/environments/test.rb +1 -0
  50. data/spec/spec_helper.rb +23 -1
  51. data/spec/support/certificates/sp_cert_req.csr +12 -0
  52. data/spec/support/certificates/sp_private_key.pem +16 -0
  53. data/spec/support/certificates/sp_x509_cert.crt +18 -0
  54. data/spec/support/saml_request_macros.rb +107 -5
  55. data/spec/support/security_helpers.rb +12 -2
  56. data/spec/xml_security_spec.rb +19 -15
  57. metadata +146 -80
  58. data/app/controllers/saml_idp/idp_controller.rb +0 -59
  59. data/spec/lib/saml_idp/.assertion_builder_spec.rb.swp +0 -0
@@ -24,6 +24,10 @@ module SamlIdp
24
24
  key_transport: 'rsa-oaep-mgf1p',
25
25
  }
26
26
  end
27
+ let(:signed_response_opts) { true }
28
+ let(:unsigned_response_opts) { false }
29
+ let(:signed_assertion_opts) { true }
30
+ let(:compress_opts) { false }
27
31
  let(:subject_encrypted) { described_class.new(reference_id,
28
32
  response_id,
29
33
  issuer_uri,
@@ -35,7 +39,12 @@ module SamlIdp
35
39
  authn_context_classref,
36
40
  expiry,
37
41
  encryption_opts,
38
- session_expiry
42
+ session_expiry,
43
+ nil,
44
+ nil,
45
+ unsigned_response_opts,
46
+ signed_assertion_opts,
47
+ compress_opts
39
48
  )
40
49
  }
41
50
 
@@ -50,7 +59,12 @@ module SamlIdp
50
59
  authn_context_classref,
51
60
  expiry,
52
61
  nil,
53
- session_expiry
62
+ session_expiry,
63
+ nil,
64
+ nil,
65
+ signed_response_opts,
66
+ signed_assertion_opts,
67
+ compress_opts
54
68
  )
55
69
  }
56
70
 
@@ -63,23 +77,143 @@ module SamlIdp
63
77
  end
64
78
 
65
79
  it "has a valid build" do
66
- subject.build.should be_present
80
+ expect(subject.build).to be_present
81
+ end
82
+
83
+ context "encrypted" do
84
+ it "builds encrypted" do
85
+ expect(subject_encrypted.build).to_not match(audience_uri)
86
+ encoded_xml = subject_encrypted.build
87
+ resp_settings = saml_settings(saml_acs_url)
88
+ resp_settings.private_key = Default::SECRET_KEY
89
+ resp_settings.issuer = audience_uri
90
+ saml_resp = OneLogin::RubySaml::Response.new(encoded_xml, settings: resp_settings)
91
+ saml_resp.soft = false
92
+ expect(saml_resp.is_valid?).to eq(true)
93
+ end
94
+ end
95
+
96
+ context "signed response" do
97
+ let(:resp_settings) do
98
+ resp_settings = saml_settings(saml_acs_url)
99
+ resp_settings.private_key = Default::SECRET_KEY
100
+ resp_settings.issuer = audience_uri
101
+ resp_settings
102
+ end
103
+
104
+ it "will build signed valid response" do
105
+ expect { subject.build }.not_to raise_error
106
+ signed_encoded_xml = subject.build
107
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
108
+ expect(
109
+ Nokogiri::XML(saml_resp.response).at_xpath(
110
+ "//p:Response//ds:Signature",
111
+ {
112
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
113
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
114
+ }
115
+ )).to be_present
116
+ expect(saml_resp.send(:validate_signature)).to eq(true)
117
+ expect(saml_resp.is_valid?).to eq(true)
118
+ end
119
+
120
+ context "when signed_assertion_opts is true" do
121
+ it "builds a signed assertion" do
122
+ expect { subject.build }.not_to raise_error
123
+ signed_encoded_xml = subject.build
124
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
125
+ expect(
126
+ Nokogiri::XML(saml_resp.response).at_xpath(
127
+ "//p:Response//a:Assertion//ds:Signature",
128
+ {
129
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
130
+ "a" => "urn:oasis:names:tc:SAML:2.0:assertion",
131
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
132
+ }
133
+ )).to be_present
134
+ end
135
+ end
136
+
137
+ context "when signed_assertion_opts is false" do
138
+ let(:signed_assertion_opts) { false }
139
+
140
+ it "builds a raw assertion" do
141
+ expect { subject.build }.not_to raise_error
142
+ signed_encoded_xml = subject.build
143
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
144
+ expect(
145
+ Nokogiri::XML(saml_resp.response).at_xpath(
146
+ "//p:Response//a:Assertion",
147
+ {
148
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
149
+ "a" => "urn:oasis:names:tc:SAML:2.0:assertion"
150
+ }
151
+ )).to be_present
152
+
153
+ expect(
154
+ Nokogiri::XML(saml_resp.response).at_xpath(
155
+ "//p:Response//Assertion//ds:Signature",
156
+ {
157
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
158
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
159
+ }
160
+ )).to_not be_present
161
+ end
162
+ end
163
+
164
+ context "when compress opts is true" do
165
+ let(:compress_opts) { true }
166
+ it "will build a compressed valid response" do
167
+ expect { subject.build }.not_to raise_error
168
+ compressed_signed_encoded_xml = subject.build
169
+ saml_resp = OneLogin::RubySaml::Response.new(compressed_signed_encoded_xml, settings: resp_settings)
170
+ expect(saml_resp.send(:validate_signature)).to eq(true)
171
+ expect(saml_resp.is_valid?).to eq(true)
172
+ end
173
+ end
67
174
  end
68
175
 
69
- it "builds encrypted" do
70
- subject_encrypted.build.should_not match(audience_uri)
71
- encoded_xml = subject_encrypted.build
176
+ it "will build signed valid response" do
177
+ expect { subject.build }.not_to raise_error
178
+ signed_encoded_xml = subject.build
72
179
  resp_settings = saml_settings(saml_acs_url)
73
180
  resp_settings.private_key = Default::SECRET_KEY
74
181
  resp_settings.issuer = audience_uri
75
- saml_resp = OneLogin::RubySaml::Response.new(encoded_xml, settings: resp_settings)
76
- saml_resp.soft = false
77
- saml_resp.is_valid?.should == true
182
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
183
+ expect(
184
+ Nokogiri::XML(saml_resp.response).at_xpath(
185
+ "//p:Response//ds:Signature",
186
+ {
187
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
188
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
189
+ }
190
+ )).to be_present
191
+ expect(saml_resp.send(:validate_signature)).to eq(true)
192
+ expect(saml_resp.is_valid?).to eq(true)
193
+ end
194
+
195
+ it "will pass reference_id as SessionIndex" do
196
+ expect { subject.build }.not_to raise_error
197
+ signed_encoded_xml = subject.build
198
+ resp_settings = saml_settings(saml_acs_url)
199
+ resp_settings.private_key = Default::SECRET_KEY
200
+ resp_settings.issuer = audience_uri
201
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
202
+
203
+ expect(
204
+ Nokogiri::XML(saml_resp.response).at_xpath(
205
+ "//saml:AuthnStatement/@SessionIndex",
206
+ {
207
+ "samlp" => "urn:oasis:names:tc:SAML:2.0:protocol",
208
+ "saml" => "urn:oasis:names:tc:SAML:2.0:assertion"
209
+ }
210
+ ).value
211
+ ).to eq("_#{reference_id}")
78
212
  end
79
213
 
80
214
  it "sets session expiration" do
81
215
  saml_resp = OneLogin::RubySaml::Response.new(subject.build)
82
- saml_resp.session_expires_at.should == Time.local(1990, "jan", 2).iso8601
216
+ expect(saml_resp.session_expires_at).to eq Time.local(1990, "jan", 2).iso8601
83
217
  end
84
218
 
85
219
  context "session expiration is set to 0" do
@@ -89,14 +223,14 @@ module SamlIdp
89
223
  resp_settings = saml_settings(saml_acs_url)
90
224
  resp_settings.issuer = audience_uri
91
225
  saml_resp = OneLogin::RubySaml::Response.new(subject.build, settings: resp_settings)
92
- saml_resp.is_valid?.should == true
226
+ expect(saml_resp.is_valid?).to eq(true)
93
227
  end
94
228
 
95
229
  it "doesn't set a session expiration" do
96
230
  resp_settings = saml_settings(saml_acs_url)
97
231
  resp_settings.issuer = audience_uri
98
232
  saml_resp = OneLogin::RubySaml::Response.new(subject.build, settings: resp_settings)
99
- saml_resp.session_expires_at.should be_nil
233
+ expect(saml_resp.session_expires_at).to be_nil
100
234
  end
101
235
  end
102
236
  end
@@ -14,11 +14,11 @@ module SamlIdp
14
14
  let(:metadata_url) { "http://localhost:3000/metadata" }
15
15
 
16
16
  it "has a valid fingerprint" do
17
- subject.fingerprint.should == fingerprint
17
+ expect(subject.fingerprint).to eq(fingerprint)
18
18
  end
19
19
 
20
20
  it "has a valid metadata_url" do
21
- subject.metadata_url.should == metadata_url
21
+ expect(subject.metadata_url).to eq(metadata_url)
22
22
  end
23
23
 
24
24
  it { should be_valid }
@@ -70,7 +70,7 @@ module SamlIdp
70
70
  end
71
71
 
72
72
  it "has a valid signed" do
73
- subject.signed.should match all_regex
73
+ expect(subject.signed).to match all_regex
74
74
  end
75
75
 
76
76
  end
@@ -9,11 +9,11 @@ module SamlIdp
9
9
  ) }
10
10
 
11
11
  before do
12
- Time.stub now: Time.parse("Jul 31 2013")
12
+ allow(Time).to receive(:now).and_return Time.parse("Jul 31 2013")
13
13
  end
14
14
 
15
15
  it "builds a legit raw XML file" do
16
- subject.raw.should == "<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha256\"/><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha256\"/><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>jvEbD/rsiPKmoXy7Lhm+FGn88NPGlap4EcPZ2fvjBnk03YESs87FXAIiZZEzN5xq4sBZksUmZe2bV3rrr9sxQNgQawmrrvr66ot7cJiv0ETFArr6kQIZaR5g/V0M4ydxvrfefp6cQVI0hXvmxi830pq0tISiO4J7tyBNX/kvhZk=</ds:SignatureValue><KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><ds:X509Data><ds:X509Certificate>MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MQwwCgYDVQQKDANQSVQxCTAHBgNVBAsMADEYMBYGA1UEAwwPbGF3cmVuY2VwaXQuY29tMSUwIwYJKoZIhvcNAQkBDBZsYXdyZW5jZS5waXRAZ21haWwuY29tMB4XDTEyMDQyODAyMjIyOFoXDTMyMDQyMzAyMjIyOFowgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuBywPNlC1FopGLYfF96SotiK8Nj6/nW084O4omRMifzy7x955RLEy673q2aiJNB3LvE6Xvkt9cGtxtNoOXw1g2UvHKpldQbr6bOEjLNeDNW7j0ob+JrRvAUOK9CRgdyw5MC6lwqVQQ5C1DnaT/2fSBFjasBFTR24dEpfTy8HfKECAwEAAaOCASUwggEhMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgUgMB0GA1UdDgQWBBQNBGmmt3ytKpcJaBaYNbnyU2xkazATBgNVHSUEDDAKBggrBgEFBQcDATAdBglghkgBhvhCAQ0EEBYOVGVzdCBYNTA5IGNlcnQwgbMGA1UdIwSBqzCBqIAUDQRpprd8rSqXCWgWmDW58lNsZGuhgYykgYkwgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbYIBATANBgkqhkiG9w0BAQsFAAOBgQAEcVUPBX7uZmzqZJfy+tUPOT5ImNQj8VE2lerhnFjnGPHmHIqhpzgnwHQujJfs/a309Wm5qwcCaC1eO5cWjcG0x3OjdllsgYDatl5GAumtBx8J3NhWRqNUgitCIkQlxHIwUfgQaCushYgDDL5YbIQa++egCgpIZ+T0Dj5oRew//A==</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>"
16
+ expect(subject.raw).to eq("<ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/><ds:SignatureMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#rsa-sha256\"/><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha256\"/><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo><ds:SignatureValue>jvEbD/rsiPKmoXy7Lhm+FGn88NPGlap4EcPZ2fvjBnk03YESs87FXAIiZZEzN5xq4sBZksUmZe2bV3rrr9sxQNgQawmrrvr66ot7cJiv0ETFArr6kQIZaR5g/V0M4ydxvrfefp6cQVI0hXvmxi830pq0tISiO4J7tyBNX/kvhZk=</ds:SignatureValue><KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><ds:X509Data><ds:X509Certificate>MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UEBxMGU3lkbmV5MQwwCgYDVQQKDANQSVQxCTAHBgNVBAsMADEYMBYGA1UEAwwPbGF3cmVuY2VwaXQuY29tMSUwIwYJKoZIhvcNAQkBDBZsYXdyZW5jZS5waXRAZ21haWwuY29tMB4XDTEyMDQyODAyMjIyOFoXDTMyMDQyMzAyMjIyOFowgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuBywPNlC1FopGLYfF96SotiK8Nj6/nW084O4omRMifzy7x955RLEy673q2aiJNB3LvE6Xvkt9cGtxtNoOXw1g2UvHKpldQbr6bOEjLNeDNW7j0ob+JrRvAUOK9CRgdyw5MC6lwqVQQ5C1DnaT/2fSBFjasBFTR24dEpfTy8HfKECAwEAAaOCASUwggEhMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgUgMB0GA1UdDgQWBBQNBGmmt3ytKpcJaBaYNbnyU2xkazATBgNVHSUEDDAKBggrBgEFBQcDATAdBglghkgBhvhCAQ0EEBYOVGVzdCBYNTA5IGNlcnQwgbMGA1UdIwSBqzCBqIAUDQRpprd8rSqXCWgWmDW58lNsZGuhgYykgYkwgYYxCzAJBgNVBAYTAkFVMQwwCgYDVQQIEwNOU1cxDzANBgNVBAcTBlN5ZG5leTEMMAoGA1UECgwDUElUMQkwBwYDVQQLDAAxGDAWBgNVBAMMD2xhd3JlbmNlcGl0LmNvbTElMCMGCSqGSIb3DQEJAQwWbGF3cmVuY2UucGl0QGdtYWlsLmNvbYIBATANBgkqhkiG9w0BAQsFAAOBgQAEcVUPBX7uZmzqZJfy+tUPOT5ImNQj8VE2lerhnFjnGPHmHIqhpzgnwHQujJfs/a309Wm5qwcCaC1eO5cWjcG0x3OjdllsgYDatl5GAumtBx8J3NhWRqNUgitCIkQlxHIwUfgQaCushYgDDL5YbIQa++egCgpIZ+T0Dj5oRew//A==</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>")
17
17
  end
18
18
  end
19
19
  end
@@ -11,15 +11,15 @@ module SamlIdp
11
11
  ) }
12
12
 
13
13
  before do
14
- Time.stub now: Time.parse("Jul 31 2013")
14
+ allow(Time).to receive(:now).and_return Time.parse("Jul 31 2013")
15
15
  end
16
16
 
17
17
  it "builds a legit raw XML file" do
18
- subject.raw.should == "<ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"></ds:SignatureMethod><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"></ds:Transform><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo>"
18
+ expect(subject.raw).to eq("<ds:SignedInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"></ds:SignatureMethod><ds:Reference URI=\"#_abc\"><ds:Transforms><ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"></ds:Transform><ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"></ds:DigestMethod><ds:DigestValue>em8csGAWynywpe8S4nN64o56/4DosXi2XWMY6RJ6YfA=</ds:DigestValue></ds:Reference></ds:SignedInfo>")
19
19
  end
20
20
 
21
21
  it "builds a legit digest of the XML file" do
22
- subject.signed.should == "hKLeWLRgatHcV6N5Fc8aKveqNp6Y/J4m2WSYp0awGFtsCTa/2nab32wI3du+3kuuIy59EDKeUhHVxEfyhoHUo6xTZuO2N7XcTpSonuZ/CB3WjozC2Q/9elss3z1rOC3154v5pW4puirLPRoG+Pwi8SmptxNRHczr6NvmfYmmGfo="
22
+ expect(subject.signed).to eq("hKLeWLRgatHcV6N5Fc8aKveqNp6Y/J4m2WSYp0awGFtsCTa/2nab32wI3du+3kuuIy59EDKeUhHVxEfyhoHUo6xTZuO2N7XcTpSonuZ/CB3WjozC2Q/9elss3z1rOC3154v5pW4puirLPRoG+Pwi8SmptxNRHczr6NvmfYmmGfo=")
23
23
  end
24
24
  end
25
25
  end
@@ -2,7 +2,7 @@ class SamlController < ApplicationController
2
2
 
3
3
  def consume
4
4
  response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
5
- render :text => response.name_id
5
+ render :plain => response.name_id
6
6
  end
7
7
 
8
8
  end
@@ -1,9 +1,61 @@
1
- class SamlIdpController < SamlIdp::IdpController
1
+ class SamlIdpController < ApplicationController
2
+ include SamlIdp::Controller
3
+
4
+ if Rails::VERSION::MAJOR >= 4
5
+ before_action :add_view_path, only: [:new, :create, :logout]
6
+ before_action :validate_saml_request, only: [:new, :create, :logout]
7
+ else
8
+ before_filter :add_view_path, only: [:new, :create, :logout]
9
+ before_filter :validate_saml_request, only: [:new, :create, :logout]
10
+ end
11
+
12
+ def new
13
+ render template: "saml_idp/idp/new"
14
+ end
15
+
16
+ def show
17
+ render xml: SamlIdp.metadata.signed
18
+ end
19
+
20
+ def create
21
+ unless params[:email].blank? && params[:password].blank?
22
+ person = idp_authenticate(params[:email], params[:password])
23
+ if person.nil?
24
+ @saml_idp_fail_msg = "Incorrect email or password."
25
+ else
26
+ @saml_response = idp_make_saml_response(person)
27
+ render :template => "saml_idp/idp/saml_post", :layout => false
28
+ return
29
+ end
30
+ end
31
+ render :template => "saml_idp/idp/new"
32
+ end
33
+
34
+ def logout
35
+ idp_logout
36
+ @saml_response = idp_make_saml_response(nil)
37
+ render :template => "saml_idp/idp/saml_post", :layout => false
38
+ end
39
+
40
+ def idp_logout
41
+ raise NotImplementedError
42
+ end
43
+ private :idp_logout
44
+
2
45
  def idp_authenticate(email, password)
3
46
  { :email => email }
4
47
  end
48
+ protected :idp_authenticate
5
49
 
6
- def idp_make_saml_response(user)
7
- encode_response(user[:email])
50
+ def idp_make_saml_response(person)
51
+ encode_response(person[:email])
8
52
  end
53
+ protected :idp_make_saml_response
54
+
55
+ private
56
+
57
+ def add_view_path
58
+ prepend_view_path("app/views")
59
+ end
60
+
9
61
  end
@@ -1,22 +1,21 @@
1
1
  <% if @saml_idp_fail_msg %>
2
2
  <div id="saml_idp_fail_msg" class="flash error"><%= @saml_idp_fail_msg %></div>
3
3
  <% end %>
4
-
5
4
  <%= form_tag do %>
6
5
  <%= hidden_field_tag("SAMLRequest", params[:SAMLRequest]) %>
7
6
  <%= hidden_field_tag("RelayState", params[:RelayState]) %>
7
+ <%= hidden_field_tag("SigAlg", params[:SigAlg]) %>
8
+ <%= hidden_field_tag("Signature", params[:Signature]) %>
8
9
 
9
10
  <p>
10
11
  <%= label_tag :email %>
11
12
  <%= email_field_tag :email, params[:email], :autocapitalize => "off", :autocorrect => "off", :autofocus => "autofocus", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
12
13
  </p>
13
-
14
14
  <p>
15
15
  <%= label_tag :password %>
16
16
  <%= password_field_tag :password, params[:password], :autocapitalize => "off", :autocorrect => "off", :spellcheck => "false", :size => 30, :class => "email_pwd txt" %>
17
17
  </p>
18
-
19
18
  <p>
20
19
  <%= submit_tag "Sign in", :class => "button big blueish" %>
21
20
  </p>
22
- <% end %>
21
+ <% end %>
@@ -11,4 +11,4 @@
11
11
  <%= submit_tag "Submit" %>
12
12
  <% end %>
13
13
  </body>
14
- </html>
14
+ </html>
@@ -18,6 +18,7 @@ module RailsApp
18
18
 
19
19
  # Custom directories with classes and modules you want to be autoloadable.
20
20
  # config.autoload_paths += %W(#{config.root}/extras)
21
+ config.autoload_paths += %w[app/controllers]
21
22
 
22
23
  # Only load the plugins named here, in the order given (default is alphabetical).
23
24
  # :all can be used as a placeholder for all plugins not explicitly named.
@@ -50,11 +51,5 @@ module RailsApp
50
51
  # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
51
52
  # parameters by using an attr_accessible or attr_protected declaration.
52
53
  # config.active_record.whitelist_attributes = true
53
-
54
- # Enable the asset pipeline
55
- config.assets.enabled = true
56
-
57
- # Version of your assets, change this if you want to expire all your assets
58
- config.assets.version = '1.0'
59
54
  end
60
55
  end
@@ -3,4 +3,4 @@ require 'rubygems'
3
3
  # Set up gems listed in the Gemfile.
4
4
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5
5
 
6
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
6
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
@@ -4,6 +4,7 @@ RailsApp::Application.configure do
4
4
  # In the development environment your application's code is reloaded on
5
5
  # every request. This slows down response time but is perfect for development
6
6
  # since you don't have to restart the web server when you make code changes.
7
+ config.eager_load = false if config.respond_to?(:eager_load)
7
8
  config.cache_classes = false
8
9
 
9
10
  # Log error messages when you accidentally call methods on nil.
@@ -29,9 +30,5 @@ RailsApp::Application.configure do
29
30
  # with SQLite, MySQL, and PostgreSQL)
30
31
  #config.active_record.auto_explain_threshold_in_seconds = 0.5
31
32
 
32
- # Do not compress assets
33
- config.assets.compress = false
34
-
35
- # Expands the lines which load the assets
36
- config.assets.debug = true
33
+ config.hosts << "foo.example.com" if config.respond_to?(:hosts)
37
34
  end
@@ -2,6 +2,7 @@ RailsApp::Application.configure do
2
2
  # Settings specified here will take precedence over those in config/application.rb
3
3
 
4
4
  # Code is not reloaded between requests
5
+ config.eager_load = true if config.respond_to?(:eager_load)
5
6
  config.cache_classes = true
6
7
 
7
8
  # Full error reports are disabled and caching is turned on
@@ -5,6 +5,7 @@ RailsApp::Application.configure do
5
5
  # test suite. You never need to work with it otherwise. Remember that
6
6
  # your test database is "scratch space" for the test suite and is wiped
7
7
  # and recreated between test runs. Don't rely on the data there!
8
+ config.eager_load = true if config.respond_to?(:eager_load)
8
9
  config.cache_classes = true
9
10
 
10
11
  # Configure static asset server for tests with Cache-Control for performance
data/spec/spec_helper.rb CHANGED
@@ -43,6 +43,28 @@ RSpec.configure do |config|
43
43
  }
44
44
  end
45
45
  end
46
+
47
+ # To reset to default config
48
+ config.after do
49
+ SamlIdp.instance_variable_set(:@config, nil)
50
+ SamlIdp.configure do |c|
51
+ c.attributes = {
52
+ emailAddress: {
53
+ name: "email-address",
54
+ getter: ->(p) { "foo@example.com" }
55
+ }
56
+ }
57
+
58
+ c.name_id.formats = {
59
+ "1.1" => {
60
+ email_address: ->(p) { "foo@example.com" }
61
+ }
62
+ }
63
+ end
64
+ end
46
65
  end
47
66
 
48
- Capybara.default_host = "https://app.example.com"
67
+ SamlIdp::Default::SERVICE_PROVIDER[:metadata_url] = 'https://example.com/meta'
68
+ SamlIdp::Default::SERVICE_PROVIDER[:response_hosts] = ['foo.example.com']
69
+ SamlIdp::Default::SERVICE_PROVIDER[:assertion_consumer_logout_service_url] = 'https://foo.example.com/saml/logout'
70
+ Capybara.default_host = "https://foo.example.com"
@@ -0,0 +1,12 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIIByTCCATICAQAwgYgxCzAJBgNVBAYTAmpwMQ4wDAYDVQQIDAVUb2t5bzELMAkG
3
+ A1UECgwCR1MxIDAeBgNVBAMMF2h0dHBzOi8vZm9vLmV4YW1wbGUuY29tMQwwCgYD
4
+ VQQHDANGb28xDDAKBgNVBAsMA0JvbzEeMBwGCSqGSIb3DQEJARYPZm9vQGV4YW1w
5
+ bGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8DVj2mVLQV7AjT+cn
6
+ Lv3kDnQFvAo3RdUeGGhplsYFacYByzNRD/jeguu1ahrvznDyZN8p3yB7OPbmt0r0
7
+ aGr+yYzPh6brgkf5u6FMtWTj94vLQuT/uyQGuzdBkiLb5mAWRMtm43oHXDK0v25J
8
+ tsG1PJnntkXfBDpFP1eWLO+jZwIDAQABoAAwDQYJKoZIhvcNAQENBQADgYEAd/J6
9
+ 5zjrMhgjxuaMuWCiNN7IS4F9SKy+gEmhkpNVCpChbpggruaEIoERjDP/TkZn2dgL
10
+ VUeHTZB92t+wWfQbHNvEfbzqlV3XkuHkxewCwofnIV/k+8zG1Al5ELSKHehItxig
11
+ rnTuBrFYsd2j4HEVqLzm4NyCfL+xzn/D4U2ec50=
12
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,16 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALwNWPaZUtBXsCNP
3
+ 5ycu/eQOdAW8CjdF1R4YaGmWxgVpxgHLM1EP+N6C67VqGu/OcPJk3ynfIHs49ua3
4
+ SvRoav7JjM+HpuuCR/m7oUy1ZOP3i8tC5P+7JAa7N0GSItvmYBZEy2bjegdcMrS/
5
+ bkm2wbU8mee2Rd8EOkU/V5Ys76NnAgMBAAECgYEArwclVHCkebIECPnnxbqhKNCj
6
+ AGtifsuKbrZ9CDoDGSq31xeQLdTV6BSm2nVlmOnmilWEuG4qx0Xf2CGlrBI78kmv
7
+ vHCfFdaGnTxbmYnD0HN0u4RK2trsxWO+rEkJk14JE2eVD6ZRPrq1UOSMgGPrQSMb
8
+ SuwAHUu/j94eL8BXuhECQQD3jTlo3Y4VPWttP6XPNqKDP+jRYJs5G0Bch//S9Qy7
9
+ QzmU9/yAUk0BEOyqYcLxinjJhoq6bR2fiIibn+77z3jtAkEAwnhLwkGYOb7Nt3V6
10
+ dQLKx1BP9dnYH7qG/sCmAs7GHPv4LGluaz4zsh2pdEDF/Xar4gwTzUpxYo8FpkCH
11
+ rf4nIwJAVfWnGr/cR4nVVNFGHUcGdXbqvFHEdLb+yWK8NZ+79Qap5w2Zk2GAtb8P
12
+ vzZFQCRqPuhGIegj4jLB5PBLRwtLHQJBAJiWyWL4ExikRUhBTr/HXBL+Sm9u6i0j
13
+ L89unBQx6LNPZhB6/Z/6Y5fLvG2ycWgLGJ06usLnOYaLEHS9x3hXpp8CQQCdtQHw
14
+ xeLBPhRDpfWWbSmFr+bFxyD/4iQHTHToIs3kaecn6OJ4rczIFpGm2Bm7f4X7F3H3
15
+ DDy4jZ0R6iDqCcQD
16
+ -----END PRIVATE KEY-----
@@ -0,0 +1,18 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC2DCCAkGgAwIBAgIBADANBgkqhkiG9w0BAQ0FADCBiDELMAkGA1UEBhMCanAx
3
+ DjAMBgNVBAgMBVRva3lvMQswCQYDVQQKDAJHUzEgMB4GA1UEAwwXaHR0cHM6Ly9m
4
+ b28uZXhhbXBsZS5jb20xDDAKBgNVBAcMA0ZvbzEMMAoGA1UECwwDQm9vMR4wHAYJ
5
+ KoZIhvcNAQkBFg9mb29AZXhhbXBsZS5jb20wHhcNMjAwMTIzMDYyMzI5WhcNNDcw
6
+ NjA5MDYyMzI5WjCBiDELMAkGA1UEBhMCanAxDjAMBgNVBAgMBVRva3lvMQswCQYD
7
+ VQQKDAJHUzEgMB4GA1UEAwwXaHR0cHM6Ly9mb28uZXhhbXBsZS5jb20xDDAKBgNV
8
+ BAcMA0ZvbzEMMAoGA1UECwwDQm9vMR4wHAYJKoZIhvcNAQkBFg9mb29AZXhhbXBs
9
+ ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALwNWPaZUtBXsCNP5ycu
10
+ /eQOdAW8CjdF1R4YaGmWxgVpxgHLM1EP+N6C67VqGu/OcPJk3ynfIHs49ua3SvRo
11
+ av7JjM+HpuuCR/m7oUy1ZOP3i8tC5P+7JAa7N0GSItvmYBZEy2bjegdcMrS/bkm2
12
+ wbU8mee2Rd8EOkU/V5Ys76NnAgMBAAGjUDBOMB0GA1UdDgQWBBQMtOtrh2VS/mh4
13
+ awGbKA37vVnw+zAfBgNVHSMEGDAWgBQMtOtrh2VS/mh4awGbKA37vVnw+zAMBgNV
14
+ HRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4GBAHjTTm4Hyx1rfzygknc6q1dYwpEv
15
+ /3AsPiTnF4AfH/5kGIIXNzwg0ADsziFMJYRRR9eMu97CHQbr8gHt99P8uaen6cmJ
16
+ 4VCwJLP2N8gZrycssimA3M83DWRRVZbxZhpuUWNajtYIxwyUbB7eRSJgz3Tc0opF
17
+ 933YwucWuFzKSqn3
18
+ -----END CERTIFICATE-----