devise_saml_authenticatable 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 811f4c9c0306a1c5ba28080ac773ba88022b79cc
4
- data.tar.gz: f2571561eb9c3ba05832e65d1b8ab15d3c9c408c
3
+ metadata.gz: eeabad37a5473557499db0b8de604afb7740608d
4
+ data.tar.gz: 952108992254613cdf27f72aedbf6e2a772f95a3
5
5
  SHA512:
6
- metadata.gz: c21a2ac40153fcb51c843fcdfa081ba2caab761c94a43cf1ebe297d1260021fc54409fe6cdac9cc3c6340db077ba3c8d2cb15e2f83a9af8886cff90ac128cb58
7
- data.tar.gz: dc0c2d6f2174e0f5bef4a6b4590ed90558741a8a86af2754bb5c1094d039c29cd9da726f804bbd78a1693023ac4f50ad81d095956ad728272144d4977bb25eec
6
+ metadata.gz: 720d7b21c40596b762a85c5c0df2f9dc3d1af7fdce86a7c8562428b866e79eab6a768e070c648fd4c657415bca175102d4eaa0dff6866c7cc8b14025d1bf5101
7
+ data.tar.gz: c3368984264f54a99a37ce9e4b546b7bbd2460a834bc57300dfaf242232619bad50f51bb66adc4d2e1e41d287ce4f3f7f85923697835a06bf14f73968a31f1ed
data/.gitignore CHANGED
@@ -4,7 +4,7 @@
4
4
  .config
5
5
  .idea/
6
6
  .yardoc
7
- Gemfile.lock
7
+ Gemfile*.lock
8
8
  InstalledFiles
9
9
  _yardoc
10
10
  coverage
@@ -2,11 +2,30 @@ language: ruby
2
2
  rvm:
3
3
  - "1.9.3"
4
4
  - "2.0.0"
5
- - "2.1.0"
6
- - "2.2.0"
5
+ - "2.1.9"
6
+ - "2.2.5"
7
+ - "2.3.1"
8
+ gemfile:
9
+ - Gemfile
10
+ - spec/support/Gemfile.rails4
11
+ - spec/support/Gemfile.ruby-saml-1.3
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: "1.9.3"
15
+ gemfile: Gemfile
16
+ - rvm: "1.9.3"
17
+ gemfile: spec/support/Gemfile.ruby-saml-1.3
18
+ - rvm: "2.0.0"
19
+ gemfile: Gemfile
20
+ - rvm: "2.0.0"
21
+ gemfile: spec/support/Gemfile.ruby-saml-1.3
22
+ - rvm: "2.1.9"
23
+ gemfile: Gemfile
24
+ - rvm: "2.1.9"
25
+ gemfile: spec/support/Gemfile.ruby-saml-1.3
7
26
 
8
27
  script:
9
- - xvfb-run bundle exec rake
28
+ - bundle exec rake
10
29
 
11
30
  notifications:
12
31
  hipchat:
@@ -15,4 +34,4 @@ notifications:
15
34
  template:
16
35
  - '%{repository}<a href="%{build_url}">#%{build_number}</a> (%{branch} - <a href="%{compare_url}">%{commit}</a> : %{author}): %{message}'
17
36
  format: html
18
- on_pull_requests: false
37
+ on_pull_requests: true
data/Gemfile CHANGED
@@ -6,10 +6,11 @@ gemspec
6
6
  group :test do
7
7
  gem 'rake'
8
8
  gem 'rspec', '~> 3.0'
9
- gem 'rails', '~> 4.0'
9
+ gem 'rails', '~> 5.0'
10
10
  gem 'rspec-rails'
11
11
  gem 'sqlite3'
12
- gem 'capybara-webkit'
12
+ gem 'capybara'
13
+ gem 'poltergeist'
13
14
 
14
15
  # Lock down versions of gems for older versions of Ruby
15
16
  if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
data/README.md CHANGED
@@ -160,6 +160,7 @@ end
160
160
  Detecting the entity ID passed to the `settings` method is done by `config.idp_entity_id_reader`.
161
161
  By default this will find the `Issuer` in the SAML request.
162
162
  You can support more use cases by writing your own and implementing the `.entity_id` method.
163
+ If you use encrypted assertions, your entity ID reader will need to understand how to decrypt the response from each of the possible IdPs.
163
164
 
164
165
  ## Identity Provider
165
166
 
@@ -3,12 +3,13 @@ require "ruby-saml"
3
3
  class Devise::SamlSessionsController < Devise::SessionsController
4
4
  include DeviseSamlAuthenticatable::SamlConfig
5
5
  unloadable if Rails::VERSION::MAJOR < 4
6
- skip_before_filter :verify_authenticity_token
6
+ skip_before_filter :verify_authenticity_token, raise: false
7
7
 
8
8
  def new
9
9
  idp_entity_id = get_idp_entity_id(params)
10
10
  request = OneLogin::RubySaml::Authrequest.new
11
- action = request.create(saml_config(idp_entity_id))
11
+ auth_params = { RelayState: relay_state } if relay_state
12
+ action = request.create(saml_config(idp_entity_id), auth_params || {})
12
13
  redirect_to action
13
14
  end
14
15
 
@@ -40,6 +41,12 @@ class Devise::SamlSessionsController < Devise::SessionsController
40
41
 
41
42
  protected
42
43
 
44
+ def relay_state
45
+ @relay_state ||= if Devise.saml_relay_state.present?
46
+ Devise.saml_relay_state.call(request)
47
+ end
48
+ end
49
+
43
50
  # Override devise to send user to IdP logout for SLO
44
51
  def after_sign_out_path_for(_)
45
52
  request = OneLogin::RubySaml::Logoutrequest.new
@@ -57,6 +57,11 @@ module Devise
57
57
  mattr_accessor :saml_failed_callback
58
58
  @@saml_failed_callback
59
59
 
60
+ # lambda that generates the RelayState param for the SAML AuthRequest, takes request
61
+ # from SamlSessionsController#new action as an argument
62
+ mattr_accessor :saml_relay_state
63
+ @@saml_relay_state
64
+
60
65
  mattr_accessor :saml_config
61
66
  @@saml_config = OneLogin::RubySaml::Settings.new
62
67
  def self.saml_configure
@@ -28,7 +28,7 @@ module Devise
28
28
  end
29
29
 
30
30
  module ClassMethods
31
- def authenticate_with_saml(saml_response)
31
+ def authenticate_with_saml(saml_response, relay_state)
32
32
  key = Devise.saml_default_user_key
33
33
  attributes = saml_response.attributes
34
34
  if (Devise.saml_use_subject)
@@ -36,8 +36,9 @@ module Devise
36
36
  else
37
37
  inv_attr = attribute_map.invert
38
38
  auth_value = attributes[inv_attr[key.to_s]]
39
- auth_value.try(:downcase!) if Devise.case_insensitive_keys.include?(key)
40
39
  end
40
+ auth_value.try(:downcase!) if Devise.case_insensitive_keys.include?(key)
41
+
41
42
  resource = where(key => auth_value).first
42
43
 
43
44
  if resource.nil?
@@ -13,14 +13,11 @@ module Devise
13
13
  end
14
14
 
15
15
  def authenticate!
16
- @response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_config(get_idp_entity_id(params)))
17
- resource = mapping.to.authenticate_with_saml(@response)
18
- if @response.is_valid? && resource
19
- resource.after_saml_authentication(@response.sessionindex)
20
- success!(resource)
21
- else
22
- fail!(:invalid)
23
- Devise.saml_failed_callback.new.handle(@response, self) if Devise.saml_failed_callback
16
+ parse_saml_response
17
+ retrieve_resource unless self.halted?
18
+ unless self.halted?
19
+ @resource.after_saml_authentication(@response.sessionindex)
20
+ success!(@resource)
24
21
  end
25
22
  end
26
23
 
@@ -28,7 +25,28 @@ module Devise
28
25
  # Any known way on how to let the IdP send the CSRF token along with the SAMLResponse ?
29
26
  # Please let me know!
30
27
  def store?
31
- true
28
+ !mapping.to.skip_session_storage.include?(:saml_auth)
29
+ end
30
+
31
+ private
32
+ def parse_saml_response
33
+ @response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_config(get_idp_entity_id(params)))
34
+ unless @response.is_valid?
35
+ failed_auth("Auth errors: #{@response.errors.join(', ')}")
36
+ end
37
+ end
38
+
39
+ def retrieve_resource
40
+ @resource = mapping.to.authenticate_with_saml(@response, params[:RelayState])
41
+ if @resource.nil?
42
+ failed_auth("Resource could not be found")
43
+ end
44
+ end
45
+
46
+ def failed_auth(msg)
47
+ DeviseSamlAuthenticatable::Logger.send(msg)
48
+ fail!(:invalid)
49
+ Devise.saml_failed_callback.new.handle(@response, self) if Devise.saml_failed_callback
32
50
  end
33
51
 
34
52
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseSamlAuthenticatable
2
- VERSION = "1.3.0"
2
+ VERSION = "1.3.1"
3
3
  end
@@ -38,9 +38,17 @@ describe Devise::SamlSessionsController, type: :controller do
38
38
  describe '#new' do
39
39
  let(:saml_response) { File.read(File.join(File.dirname(__FILE__), '../../support', 'response_encrypted_nameid.xml.base64')) }
40
40
 
41
+ subject(:do_get) {
42
+ if Rails::VERSION::MAJOR > 4
43
+ get :new, params: {"SAMLResponse" => saml_response}
44
+ else
45
+ get :new, "SAMLResponse" => saml_response
46
+ end
47
+ }
48
+
41
49
  context "when using the default saml config" do
42
50
  it "redirects to the IdP SSO target url" do
43
- get :new, "SAMLResponse" => saml_response
51
+ do_get
44
52
  expect(response).to redirect_to(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
45
53
  end
46
54
  end
@@ -51,13 +59,23 @@ describe Devise::SamlSessionsController, type: :controller do
51
59
  end
52
60
 
53
61
  it "redirects to the associated IdP SSO target url" do
54
- get :new, "SAMLResponse" => saml_response
62
+ do_get
55
63
  expect(response).to redirect_to(%r(\Ahttp://idp_sso_url\?SAMLRequest=))
56
64
  end
57
65
 
58
66
  it "uses the DefaultIdpEntityIdReader" do
59
67
  expect(DeviseSamlAuthenticatable::DefaultIdpEntityIdReader).to receive(:entity_id)
60
- get :new, "SAMLResponse" => saml_response
68
+ do_get
69
+ end
70
+
71
+ context "with a relay_state lambda defined" do
72
+ let(:relay_state) { ->(request) { "123" } }
73
+
74
+ it "includes the RelayState param in the request to the IdP" do
75
+ expect(Devise).to receive(:saml_relay_state).at_least(:once).and_return(relay_state)
76
+ do_get
77
+ expect(response).to redirect_to(%r(\Ahttp://idp_sso_url\?SAMLRequest=.*&RelayState=123))
78
+ end
61
79
  end
62
80
 
63
81
  context "with a specified idp entity id reader" do
@@ -67,6 +85,14 @@ describe Devise::SamlSessionsController, type: :controller do
67
85
  end
68
86
  end
69
87
 
88
+ subject(:do_get) {
89
+ if Rails::VERSION::MAJOR > 4
90
+ get :new, params: {entity_id: "http://www.example.com"}
91
+ else
92
+ get :new, entity_id: "http://www.example.com"
93
+ end
94
+ }
95
+
70
96
  before do
71
97
  @default_reader = Devise.idp_entity_id_reader
72
98
  Devise.idp_entity_id_reader = OurIdpEntityIdReader # which will have some different behavior
@@ -77,7 +103,7 @@ describe Devise::SamlSessionsController, type: :controller do
77
103
  end
78
104
 
79
105
  it "redirects to the associated IdP SSO target url" do
80
- get :new, entity_id: "http://www.example.com"
106
+ do_get
81
107
  expect(response).to redirect_to(%r(\Ahttp://idp_sso_url\?SAMLRequest=))
82
108
  end
83
109
  end
@@ -129,76 +155,98 @@ describe Devise::SamlSessionsController, type: :controller do
129
155
  end
130
156
 
131
157
  describe '#idp_sign_out' do
132
- let(:name_id) { '12312312' }
133
- let(:saml_request) { double(:slo_logoutrequest, {
134
- id: 42,
135
- name_id: name_id,
136
- issuer: "http://www.example.com"
137
- }) }
138
158
  let(:saml_response) { double(:slo_logoutresponse) }
139
159
  let(:response_url) { 'http://localhost/logout_response' }
140
-
141
160
  before do
142
- allow(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).and_return(saml_request)
143
161
  allow(OneLogin::RubySaml::SloLogoutresponse).to receive(:new).and_return(saml_response)
144
162
  allow(saml_response).to receive(:create).and_return(response_url)
145
163
  end
146
164
 
147
- it 'returns invalid request if SAMLRequest is not passed' do
148
- expect(User).not_to receive(:reset_session_key_for).with(name_id)
165
+ it 'returns invalid request if SAMLRequest or SAMLResponse is not passed' do
166
+ expect(User).not_to receive(:reset_session_key_for)
149
167
  post :idp_sign_out
150
168
  expect(response.status).to eq 500
151
169
  end
152
170
 
153
- it 'accepts a LogoutResponse and redirects sign_in' do
154
- post :idp_sign_out, SAMLResponse: 'stubbed_response'
155
- expect(response.status).to eq 302
156
- expect(response).to redirect_to '/users/saml/sign_in'
157
- end
171
+ context "when receiving a logout response from the IdP after redirecting an SP logout request" do
172
+ subject(:do_post) {
173
+ if Rails::VERSION::MAJOR > 4
174
+ post :idp_sign_out, params: {SAMLResponse: "stubbed_response"}
175
+ else
176
+ post :idp_sign_out, SAMLResponse: "stubbed_response"
177
+ end
178
+ }
158
179
 
159
- context "with a specified idp" do
160
- let(:idp_entity_id) { "http://www.example.com" }
161
- before do
162
- Devise.idp_settings_adapter = idp_providers_adapter
180
+ it 'accepts a LogoutResponse and redirects sign_in' do
181
+ do_post
182
+ expect(response.status).to eq 302
183
+ expect(response).to redirect_to '/users/saml/sign_in'
163
184
  end
164
185
 
165
- it "accepts a LogoutResponse for the associated slo_target_url and redirects to sign_in" do
166
- post :idp_sign_out, SAMLRequest: "stubbed_logout_request"
167
- expect(response.status).to eq 302
168
- expect(idp_providers_adapter).to have_received(:settings).with(idp_entity_id)
169
- expect(response).to redirect_to "http://localhost/logout_response"
186
+ context 'when saml_sign_out_success_url is configured' do
187
+ let(:test_url) { '/test/url' }
188
+ before do
189
+ Devise.saml_sign_out_success_url = test_url
190
+ end
191
+
192
+ it 'accepts a LogoutResponse and returns success' do
193
+ do_post
194
+ expect(response.status).to eq 302
195
+ expect(response).to redirect_to test_url
196
+ end
170
197
  end
171
198
  end
172
199
 
173
- context 'when saml_sign_out_success_url is configured' do
174
- let(:test_url) { '/test/url' }
200
+ context "when receiving an IdP logout request" do
201
+ subject(:do_post) {
202
+ if Rails::VERSION::MAJOR > 4
203
+ post :idp_sign_out, params: {SAMLRequest: "stubbed_logout_request"}
204
+ else
205
+ post :idp_sign_out, SAMLRequest: "stubbed_logout_request"
206
+ end
207
+ }
208
+
209
+ let(:saml_request) { double(:slo_logoutrequest, {
210
+ id: 42,
211
+ name_id: name_id,
212
+ issuer: "http://www.example.com"
213
+ }) }
214
+ let(:name_id) { '12312312' }
175
215
  before do
176
- Devise.saml_sign_out_success_url = test_url
216
+ allow(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).and_return(saml_request)
177
217
  end
178
218
 
179
- it 'accepts a LogoutResponse and returns success' do
180
- post :idp_sign_out, SAMLResponse: 'stubbed_response'
181
- expect(response.status).to eq 302
182
- expect(response).to redirect_to test_url
219
+ it 'direct the resource to reset the session key' do
220
+ expect(User).to receive(:reset_session_key_for).with(name_id)
221
+ do_post
222
+ expect(response).to redirect_to response_url
183
223
  end
184
- end
185
224
 
186
- context 'when saml_session_index_key is not configured' do
187
- before do
188
- Devise.saml_session_index_key = nil
189
- end
225
+ context "with a specified idp" do
226
+ let(:idp_entity_id) { "http://www.example.com" }
227
+ before do
228
+ Devise.idp_settings_adapter = idp_providers_adapter
229
+ end
190
230
 
191
- it 'returns invalid request' do
192
- expect(User).not_to receive(:reset_session_key_for).with(name_id)
193
- post :idp_sign_out, SAMLRequest: 'stubbed_request'
194
- expect(response.status).to eq 500
231
+ it "accepts a LogoutResponse for the associated slo_target_url and redirects to sign_in" do
232
+ do_post
233
+ expect(response.status).to eq 302
234
+ expect(idp_providers_adapter).to have_received(:settings).with(idp_entity_id)
235
+ expect(response).to redirect_to "http://localhost/logout_response"
236
+ end
195
237
  end
196
- end
197
238
 
198
- it 'direct the resource to reset the session key' do
199
- expect(User).to receive(:reset_session_key_for).with(name_id)
200
- post :idp_sign_out, SAMLRequest: 'stubbed_request'
201
- expect(response).to redirect_to response_url
239
+ context 'when saml_session_index_key is not configured' do
240
+ before do
241
+ Devise.saml_session_index_key = nil
242
+ end
243
+
244
+ it 'returns invalid request' do
245
+ expect(User).not_to receive(:reset_session_key_for).with(name_id)
246
+ do_post
247
+ expect(response.status).to eq 500
248
+ end
249
+ end
202
250
  end
203
251
  end
204
252
  end
@@ -58,12 +58,12 @@ describe Devise::Models::SamlAuthenticatable do
58
58
  it "looks up the user by the configured default user key" do
59
59
  user = Model.new(new_record: false)
60
60
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
61
- expect(Model.authenticate_with_saml(response)).to eq(user)
61
+ expect(Model.authenticate_with_saml(response, nil)).to eq(user)
62
62
  end
63
63
 
64
64
  it "returns nil if it cannot find a user" do
65
65
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
66
- expect(Model.authenticate_with_saml(response)).to be_nil
66
+ expect(Model.authenticate_with_saml(response, nil)).to be_nil
67
67
  end
68
68
 
69
69
  context "when configured to use the subject" do
@@ -77,12 +77,12 @@ describe Devise::Models::SamlAuthenticatable do
77
77
  it "looks up the user by the configured default user key" do
78
78
  user = Model.new(new_record: false)
79
79
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
80
- expect(Model.authenticate_with_saml(response)).to eq(user)
80
+ expect(Model.authenticate_with_saml(response, nil)).to eq(user)
81
81
  end
82
82
 
83
83
  it "returns nil if it cannot find a user" do
84
84
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
85
- expect(Model.authenticate_with_saml(response)).to be_nil
85
+ expect(Model.authenticate_with_saml(response, nil)).to be_nil
86
86
  end
87
87
 
88
88
  context "when configured to create a user and the user is not found" do
@@ -92,7 +92,7 @@ describe Devise::Models::SamlAuthenticatable do
92
92
 
93
93
  it "creates and returns a new user with the name identifier and given attributes" do
94
94
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
95
- model = Model.authenticate_with_saml(response)
95
+ model = Model.authenticate_with_saml(response, nil)
96
96
  expect(model.email).to eq('user@example.com')
97
97
  expect(model.name).to eq('A User')
98
98
  expect(model.saved).to be(true)
@@ -107,7 +107,7 @@ describe Devise::Models::SamlAuthenticatable do
107
107
  it "creates and returns a new user with the name identifier and given attributes" do
108
108
  user = Model.new(email: "old_mail@mail.com", name: "old name", new_record: false)
109
109
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
110
- model = Model.authenticate_with_saml(response)
110
+ model = Model.authenticate_with_saml(response, nil)
111
111
  expect(model.email).to eq('user@example.com')
112
112
  expect(model.name).to eq('A User')
113
113
  expect(model.saved).to be(true)
@@ -122,7 +122,7 @@ describe Devise::Models::SamlAuthenticatable do
122
122
 
123
123
  it "creates and returns a new user with the given attributes" do
124
124
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
125
- model = Model.authenticate_with_saml(response)
125
+ model = Model.authenticate_with_saml(response, nil)
126
126
  expect(model.email).to eq('user@example.com')
127
127
  expect(model.name).to eq('A User')
128
128
  expect(model.saved).to be(true)
@@ -136,29 +136,46 @@ describe Devise::Models::SamlAuthenticatable do
136
136
 
137
137
  it "returns nil if the user is not found" do
138
138
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
139
- expect(Model.authenticate_with_saml(response)).to be_nil
139
+ expect(Model.authenticate_with_saml(response, nil)).to be_nil
140
140
  end
141
141
 
142
142
  it "updates the attributes if the user is found" do
143
143
  user = Model.new(email: "old_mail@mail.com", name: "old name", new_record: false)
144
144
  expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
145
- model = Model.authenticate_with_saml(response)
145
+ model = Model.authenticate_with_saml(response, nil)
146
146
  expect(model.email).to eq('user@example.com')
147
147
  expect(model.name).to eq('A User')
148
148
  expect(model.saved).to be(true)
149
149
  end
150
150
  end
151
151
 
152
-
153
152
  context "when configured with a case-insensitive key" do
154
- before do
155
- allow(Devise).to receive(:case_insensitive_keys).and_return([:email])
153
+ shared_examples "correct downcasing" do
154
+ before do
155
+ allow(Devise).to receive(:case_insensitive_keys).and_return([:email])
156
+ end
157
+
158
+ it "looks up the user with a downcased value" do
159
+ user = Model.new(new_record: false)
160
+ expect(Model).to receive(:where).with(email: 'upper@example.com').and_return([user])
161
+ expect(Model.authenticate_with_saml(response, nil)).to eq(user)
162
+ end
156
163
  end
157
164
 
158
- it "looks up the user with a downcased value" do
159
- user = Model.new(new_record: false)
160
- expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
161
- expect(Model.authenticate_with_saml(response)).to eq(user)
165
+ context "when configured to use the subject" do
166
+ let(:name_id) { 'UPPER@example.com' }
167
+
168
+ before do
169
+ allow(Devise).to receive(:saml_use_subject).and_return(true)
170
+ end
171
+
172
+ include_examples "correct downcasing"
173
+ end
174
+
175
+ context "when using default user key" do
176
+ let(:attributes) { OneLogin::RubySaml::Attributes.new('saml-email-format' => ['UPPER@example.com']) }
177
+
178
+ include_examples "correct downcasing"
162
179
  end
163
180
  end
164
181
  end
@@ -3,15 +3,16 @@ require 'rails_helper'
3
3
  describe Devise::Strategies::SamlAuthenticatable do
4
4
  subject(:strategy) { described_class.new(env, :user) }
5
5
  let(:env) { {} }
6
+ let(:errors) { ["Test1", "Test2"] }
6
7
 
7
- let(:response) { double(:response, issuers: [idp_entity_id], :settings= => nil, is_valid?: true, sessionindex: '123123123') }
8
+ let(:response) { double(:response, issuers: [idp_entity_id], :settings= => nil, is_valid?: true, sessionindex: '123123123', errors: errors) }
8
9
  let(:idp_entity_id) { "https://test/saml/metadata/123123" }
9
10
  before do
10
11
  allow(OneLogin::RubySaml::Response).to receive(:new).and_return(response)
11
12
  end
12
13
 
13
14
  let(:mapping) { double(:mapping, to: user_class) }
14
- let(:user_class) { double(:user_class, authenticate_with_saml: user) }
15
+ let(:user_class) { double(:user_class, authenticate_with_saml: user, skip_session_storage: []) }
15
16
  let(:user) { double(:user) }
16
17
  before do
17
18
  allow(strategy).to receive(:mapping).and_return(mapping)
@@ -32,13 +33,23 @@ describe Devise::Strategies::SamlAuthenticatable do
32
33
 
33
34
  it "authenticates with the response" do
34
35
  expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], anything)
35
- expect(user_class).to receive(:authenticate_with_saml).with(response)
36
+ expect(user_class).to receive(:authenticate_with_saml).with(response, nil)
36
37
  expect(user).to receive(:after_saml_authentication).with(response.sessionindex)
37
38
 
38
39
  expect(strategy).to receive(:success!).with(user)
39
40
  strategy.authenticate!
40
41
  end
41
42
 
43
+ context "and a RelayState parameter" do
44
+ let(:params) { super().merge(RelayState: "foo") }
45
+ it "authenticates with the response" do
46
+ expect(user_class).to receive(:authenticate_with_saml).with(response, params[:RelayState])
47
+
48
+ expect(strategy).to receive(:success!).with(user)
49
+ strategy.authenticate!
50
+ end
51
+ end
52
+
42
53
  context "when saml config uses an idp_adapter" do
43
54
  let(:idp_providers_adapter) {
44
55
  Class.new {
@@ -70,7 +81,7 @@ describe Devise::Strategies::SamlAuthenticatable do
70
81
  it "authenticates with the response for the corresponding idp" do
71
82
  expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], anything)
72
83
  expect(idp_providers_adapter).to receive(:settings).with(idp_entity_id)
73
- expect(user_class).to receive(:authenticate_with_saml).with(response)
84
+ expect(user_class).to receive(:authenticate_with_saml).with(response, params[:RelayState])
74
85
  expect(user).to receive(:after_saml_authentication).with(response.sessionindex)
75
86
 
76
87
  expect(strategy).to receive(:success!).with(user)
@@ -85,6 +96,11 @@ describe Devise::Strategies::SamlAuthenticatable do
85
96
  expect(strategy).to receive(:fail!).with(:invalid)
86
97
  strategy.authenticate!
87
98
  end
99
+
100
+ it 'logs the error' do
101
+ expect(DeviseSamlAuthenticatable::Logger).to receive(:send).with('Resource could not be found')
102
+ strategy.authenticate!
103
+ end
88
104
  end
89
105
 
90
106
  context "and the SAML response is not valid" do
@@ -103,6 +119,11 @@ describe Devise::Strategies::SamlAuthenticatable do
103
119
  Devise.saml_failed_callback = @saml_failed_login
104
120
  end
105
121
 
122
+ it 'logs the error' do
123
+ expect(DeviseSamlAuthenticatable::Logger).to receive(:send).with('Auth errors: Test1, Test2')
124
+ strategy.authenticate!
125
+ end
126
+
106
127
  it "fails to authenticate" do
107
128
  expect(strategy).to receive(:fail!).with(:invalid)
108
129
  strategy.authenticate!
@@ -122,4 +143,14 @@ describe Devise::Strategies::SamlAuthenticatable do
122
143
  it "is permanent" do
123
144
  expect(strategy).to be_store
124
145
  end
146
+
147
+ context "when the user should not be stored in the session" do
148
+ before do
149
+ allow(user_class).to receive(:skip_session_storage).and_return([:saml_auth])
150
+ end
151
+
152
+ it "is not stored" do
153
+ expect(strategy).not_to be_store
154
+ end
155
+ end
125
156
  end
@@ -3,8 +3,8 @@ require 'net/http'
3
3
  require 'timeout'
4
4
  require 'uri'
5
5
  require 'capybara/rspec'
6
- require 'capybara/webkit'
7
- Capybara.default_driver = :webkit
6
+ require 'capybara/poltergeist'
7
+ Capybara.default_driver = :poltergeist
8
8
 
9
9
  describe "SAML Authentication", type: :feature do
10
10
  let(:idp_port) { 8009 }
@@ -0,0 +1,23 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise_saml_authenticatable.gemspec
4
+ gemspec path: '../..'
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec', '~> 3.0'
9
+ gem 'rails', '~> 4.0'
10
+ gem 'rspec-rails'
11
+ gem 'sqlite3'
12
+ gem 'capybara'
13
+ gem 'poltergeist'
14
+
15
+ # Lock down versions of gems for older versions of Ruby
16
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
17
+ gem 'addressable', '~> 2.4.0'
18
+ gem 'mime-types', '~> 2.99'
19
+ end
20
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
21
+ gem 'devise', '~> 3.5'
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in devise_saml_authenticatable.gemspec
4
+ gemspec path: '../..'
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec', '~> 3.0'
9
+ gem 'rails', '~> 5.0'
10
+ gem 'rspec-rails'
11
+ gem 'ruby-saml', '~> 1.3.0'
12
+ gem 'sqlite3'
13
+ gem 'capybara'
14
+ gem 'poltergeist'
15
+
16
+ # Lock down versions of gems for older versions of Ruby
17
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
18
+ gem 'mime-types', '~> 2.99'
19
+ end
20
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
21
+ gem 'devise', '~> 3.5'
22
+ end
23
+ end
@@ -9,9 +9,6 @@ gem 'thin'
9
9
  insert_into_file('Gemfile', after: /\z/) {
10
10
  <<-GEMFILE
11
11
  # Lock down versions of gems for older versions of Ruby
12
- if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
13
- gem 'mime-types', '~> 2.99'
14
- end
15
12
  if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
16
13
  gem 'devise', '~> 3.5'
17
14
  end
@@ -12,13 +12,11 @@ def app_ready?(pid, port)
12
12
  end
13
13
 
14
14
  def create_app(name, env = {})
15
- rails_new_options = %w(-T -J -S --skip-spring)
15
+ rails_new_options = %w(-T -J -S --skip-spring --skip-listen)
16
16
  rails_new_options << "-O" if name == 'idp'
17
- Bundler.with_clean_env do
18
- Dir.chdir(File.expand_path('../../support', __FILE__)) do
19
- FileUtils.rm_rf(name)
20
- system(env, "rails", "new", name, *rails_new_options, "-m", "#{name}_template.rb")
21
- end
17
+ Dir.chdir(File.expand_path('../../support', __FILE__)) do
18
+ FileUtils.rm_rf(name)
19
+ system(env, "rails", "new", name, *rails_new_options, "-m", "#{name}_template.rb")
22
20
  end
23
21
  end
24
22
 
@@ -1,5 +1,7 @@
1
1
  # Set up a SAML Service Provider
2
2
 
3
+ require "onelogin/ruby-saml/version"
4
+
3
5
  saml_session_index_key = ENV.fetch('SAML_SESSION_INDEX_KEY', ":session_index")
4
6
  use_subject_to_authenticate = ENV.fetch('USE_SUBJECT_TO_AUTHENTICATE')
5
7
  idp_settings_adapter = ENV.fetch('IDP_SETTINGS_ADAPTER', "nil")
@@ -7,14 +9,12 @@ idp_entity_id_reader = ENV.fetch('IDP_ENTITY_ID_READER', "DeviseSamlAuthenticata
7
9
  saml_failed_callback = ENV.fetch('SAML_FAILED_CALLBACK', "nil")
8
10
 
9
11
  gem 'devise_saml_authenticatable', path: '../../..'
12
+ gem 'ruby-saml', OneLogin::RubySaml::VERSION
10
13
  gem 'thin'
11
14
 
12
15
  insert_into_file('Gemfile', after: /\z/) {
13
16
  <<-GEMFILE
14
17
  # Lock down versions of gems for older versions of Ruby
15
- if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
16
- gem 'mime-types', '~> 2.99'
17
- end
18
18
  if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
19
19
  gem 'devise', '~> 3.5'
20
20
  end
@@ -100,7 +100,7 @@ end
100
100
  route "resources :users, only: [:create]"
101
101
  create_file('app/controllers/users_controller.rb', <<-USERS)
102
102
  class UsersController < ApplicationController
103
- skip_before_filter :verify_authenticity_token
103
+ skip_before_action :verify_authenticity_token
104
104
  def create
105
105
  User.create!(email: params[:email])
106
106
  render nothing: true, status: 201
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_saml_authenticatable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josef Sauter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-07-07 00:00:00.000000000 Z
11
+ date: 2016-11-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: devise
@@ -72,6 +72,8 @@ files:
72
72
  - spec/features/saml_authentication_spec.rb
73
73
  - spec/rails_helper.rb
74
74
  - spec/spec_helper.rb
75
+ - spec/support/Gemfile.rails4
76
+ - spec/support/Gemfile.ruby-saml-1.3
75
77
  - spec/support/idp_settings_adapter.rb.erb
76
78
  - spec/support/idp_template.rb
77
79
  - spec/support/rails_app.rb
@@ -111,6 +113,8 @@ test_files:
111
113
  - spec/features/saml_authentication_spec.rb
112
114
  - spec/rails_helper.rb
113
115
  - spec/spec_helper.rb
116
+ - spec/support/Gemfile.rails4
117
+ - spec/support/Gemfile.ruby-saml-1.3
114
118
  - spec/support/idp_settings_adapter.rb.erb
115
119
  - spec/support/idp_template.rb
116
120
  - spec/support/rails_app.rb