devise_saml_authenticatable 1.6.0 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f372d3d801220ab4ce4a7e83501f0e5d7a9cc2590eb64aa1c91a0a5ad053d7d
4
- data.tar.gz: bcdfc0370f0926a542164ea577030e9ff17666adb9e0d5ae8367a605e4966866
3
+ metadata.gz: 23fdb33308c8d98c67e3fe7d5654bfcdc7afe40c2276822a3936785ed29e15ea
4
+ data.tar.gz: 11569096a198fb51b129d82eaab03a870c3c8983b7dfe88aa0bcc53f1f4fa2ce
5
5
  SHA512:
6
- metadata.gz: d9bd026db0bb199aaa9b72d1c03fccef49a905dc7270739b48e682623036f945b74f16c99f1d457086df47ff03f8e71aee5c4a412d01cd9f9c80b4f84e79d58a
7
- data.tar.gz: c2f32e5297c0e9a4aadce6069282d8c17e91d95550a777e2f5755745effc0fda5eaa958e4656d5776e2a507251d230fdaba3d944189874d0166d3f2cefbe153c
6
+ metadata.gz: 605f76c64fa08cb1ec9f26224af74ccec6c8d85b507899e6f61f1c65e8ada6b7672b6b9398d78fcfbd075d8c7754e824e0a8ed822001b124a6658e862b72203c
7
+ data.tar.gz: 0f8bb1f715288790cffb1b20214d6c0da71c546aaa09e626183b86d1274c5d6e3aae90dea8af6c4d572c6f2131360c948203a9a14706ed7414c685833eac173d
@@ -0,0 +1,76 @@
1
+ name: ci
2
+ on:
3
+ push:
4
+ branches:
5
+ - master
6
+ pull_request:
7
+ branches:
8
+ - master
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ ruby:
15
+ - "2.7"
16
+ - "2.6"
17
+ - "2.5"
18
+ - "2.4"
19
+ - "2.3"
20
+ gemfile:
21
+ - Gemfile
22
+ - spec/support/Gemfile.rails6
23
+ - spec/support/Gemfile.rails5.2
24
+ - spec/support/Gemfile.rails5.1
25
+ - spec/support/Gemfile.rails5
26
+ bundler:
27
+ - "2"
28
+ exclude:
29
+ - ruby: "2.3"
30
+ gemfile: Gemfile
31
+ bundler: "2"
32
+ - ruby: "2.3"
33
+ gemfile: spec/support/Gemfile.rails6
34
+ bundler: "2"
35
+ - ruby: "2.4"
36
+ gemfile: Gemfile
37
+ bundler: "2"
38
+ - ruby: "2.4"
39
+ gemfile: spec/support/Gemfile.rails6
40
+ bundler: "2"
41
+ include:
42
+ - ruby: "2.5"
43
+ gemfile: spec/support/Gemfile.rails4
44
+ bundler: "1"
45
+ - ruby: "2.4"
46
+ gemfile: spec/support/Gemfile.rails4
47
+ bundler: "1"
48
+ - ruby: "2.3"
49
+ gemfile: spec/support/Gemfile.rails4
50
+ bundler: "1"
51
+ - ruby: "2.2"
52
+ gemfile: spec/support/Gemfile.rails5.1
53
+ bundler: "1"
54
+ - ruby: "2.2"
55
+ gemfile: spec/support/Gemfile.rails5
56
+ bundler: "1"
57
+ - ruby: "2.2"
58
+ gemfile: spec/support/Gemfile.rails4
59
+ bundler: "1"
60
+ - ruby: "2.1"
61
+ gemfile: spec/support/Gemfile.rails4
62
+ bundler: "1"
63
+ - ruby: "2.0"
64
+ gemfile: spec/support/Gemfile.rails4
65
+ bundler: "1"
66
+ runs-on: ubuntu-latest
67
+ env:
68
+ BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
69
+ steps:
70
+ - uses: actions/checkout@v2
71
+ - uses: ruby/setup-ruby@v1
72
+ with:
73
+ bundler: ${{ matrix.bundler }}
74
+ ruby-version: ${{ matrix.ruby }}
75
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
76
+ - run: bundle exec rake
data/README.md CHANGED
@@ -57,8 +57,8 @@ An extra step in SAML SSO setup is adding your application to your identity prov
57
57
  Your IdP should give you some information you need to configure in [ruby-saml](https://github.com/onelogin/ruby-saml), as in the next section:
58
58
 
59
59
  - Issuer (`idp_entity_id`)
60
- - SSO endpoint (`idp_sso_target_url`)
61
- - SLO endpoint (`idp_slo_target_url`)
60
+ - SSO endpoint (`idp_sso_service_url`)
61
+ - SLO endpoint (`idp_slo_service_url`)
62
62
  - Certificate fingerprint (`idp_cert_fingerprint`) and algorithm (`idp_cert_fingerprint_algorithm`)
63
63
  - Or the certificate itself (`idp_cert`)
64
64
 
@@ -89,13 +89,13 @@ In `config/initializers/devise.rb`:
89
89
  # If you don't set it then email will be extracted from SAML assertation attributes.
90
90
  config.saml_use_subject = true
91
91
 
92
- # You can support multiple IdPs by setting this value to a class that implements a #settings method which takes
93
- # an IdP entity id as an argument and returns a hash of idp settings for the corresponding IdP.
94
- config.idp_settings_adapter = nil
92
+ # You can support multiple IdPs by setting this value to the name of a class that implements a ::settings method
93
+ # which takes an IdP entity id as an argument and returns a hash of idp settings for the corresponding IdP.
94
+ # config.idp_settings_adapter = "MyIdPSettingsAdapter"
95
95
 
96
96
  # You provide you own method to find the idp_entity_id in a SAML message in the case of multiple IdPs
97
- # by setting this to a custom reader class, or use the default.
98
- # config.idp_entity_id_reader = DeviseSamlAuthenticatable::DefaultIdpEntityIdReader
97
+ # by setting this to the name of a custom reader class, or use the default.
98
+ # config.idp_entity_id_reader = "DeviseSamlAuthenticatable::DefaultIdpEntityIdReader"
99
99
 
100
100
  # You can set a handler object that takes the response for a failed SAML request and the strategy,
101
101
  # and implements a #handle method. This method can then redirect the user, return error messages, etc.
@@ -111,6 +111,10 @@ In `config/initializers/devise.rb`:
111
111
  # This is a time in seconds.
112
112
  # config.allowed_clock_drift_in_seconds = 0
113
113
 
114
+ # In SAML responses, validate that the identity provider has included an InResponseTo
115
+ # header that matches the ID of the SAML request. (Default is false)
116
+ # config.saml_validate_in_response_to = false
117
+
114
118
  # Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/onelogin/ruby-saml).
115
119
  config.saml_configure do |settings|
116
120
  # assertion_consumer_service_url is required starting with ruby-saml 1.4.3: https://github.com/onelogin/ruby-saml#updating-from-142-to-143
@@ -119,8 +123,8 @@ In `config/initializers/devise.rb`:
119
123
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
120
124
  settings.issuer = "http://localhost:3000/saml/metadata"
121
125
  settings.authn_context = ""
122
- settings.idp_slo_target_url = "http://localhost/simplesaml/www/saml2/idp/SingleLogoutService.php"
123
- settings.idp_sso_target_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
126
+ settings.idp_slo_service_url = "http://localhost/simplesaml/www/saml2/idp/SingleLogoutService.php"
127
+ settings.idp_sso_service_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
124
128
  settings.idp_cert_fingerprint = "00:A1:2B:3C:44:55:6F:A7:88:CC:DD:EE:22:33:44:55:D6:77:8F:99"
125
129
  settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
126
130
  end
@@ -169,7 +173,7 @@ If you only have one IdP, you can use the config file above, or just return a si
169
173
  ...
170
174
  # ==> Configuration for :saml_authenticatable
171
175
 
172
- config.saml_attribute_map_resolver = MyAttributeMapResolver
176
+ config.saml_attribute_map_resolver = "MyAttributeMapResolver"
173
177
  end
174
178
  ```
175
179
 
@@ -207,8 +211,8 @@ class IdPSettingsAdapter
207
211
  issuer: "http://localhost:3000/saml/metadata",
208
212
  idp_entity_id: "http://www.example_idp_entity_id.com",
209
213
  authn_context: "",
210
- idp_slo_target_url: "http://example_idp_slo_target_url.com",
211
- idp_sso_target_url: "http://example_idp_sso_target_url.com",
214
+ idp_slo_service_url: "http://example_idp_slo_service_url.com",
215
+ idp_sso_service_url: "http://example_idp_sso_service_url.com",
212
216
  idp_cert: "example_idp_cert"
213
217
  }
214
218
  when "http://www.another_idp_entity_id.biz"
@@ -219,8 +223,8 @@ class IdPSettingsAdapter
219
223
  issuer: "http://localhost:3000/saml/metadata",
220
224
  idp_entity_id: "http://www.another_idp_entity_id.biz",
221
225
  authn_context: "",
222
- idp_slo_target_url: "http://another_idp_slo_target_url.com",
223
- idp_sso_target_url: "http://another_idp_sso_target_url.com",
226
+ idp_slo_service_url: "http://another_idp_slo_service_url.com",
227
+ idp_sso_service_url: "http://another_idp_sso_service_url.com",
224
228
  idp_cert: "another_idp_cert"
225
229
  }
226
230
  else
@@ -5,10 +5,10 @@ class Devise::SamlSessionsController < Devise::SessionsController
5
5
  unloadable if Rails::VERSION::MAJOR < 4
6
6
  if Rails::VERSION::MAJOR < 5
7
7
  skip_before_filter :verify_authenticity_token
8
- prepend_before_filter :store_info_for_sp_initiated_logout, only: :destroy
8
+ prepend_before_filter :verify_signed_out_user, :store_info_for_sp_initiated_logout, only: :destroy
9
9
  else
10
10
  skip_before_action :verify_authenticity_token, raise: false
11
- prepend_before_action :store_info_for_sp_initiated_logout, only: :destroy
11
+ prepend_before_action :verify_signed_out_user, :store_info_for_sp_initiated_logout, only: :destroy
12
12
  end
13
13
 
14
14
  def new
@@ -16,12 +16,16 @@ class Devise::SamlSessionsController < Devise::SessionsController
16
16
  request = OneLogin::RubySaml::Authrequest.new
17
17
  auth_params = { RelayState: relay_state } if relay_state
18
18
  action = request.create(saml_config(idp_entity_id), auth_params || {})
19
+ if request.respond_to?(:request_id)
20
+ session[:saml_transaction_id] = request.request_id
21
+ end
19
22
  redirect_to action
20
23
  end
21
24
 
22
25
  def metadata
26
+ idp_entity_id = params[:idp_entity_id]
23
27
  meta = OneLogin::RubySaml::Metadata.new
24
- render :xml => meta.generate(saml_config)
28
+ render :xml => meta.generate(saml_config(idp_entity_id))
25
29
  end
26
30
 
27
31
  def idp_sign_out
@@ -78,6 +82,18 @@ class Devise::SamlSessionsController < Devise::SessionsController
78
82
  request.create(saml_settings)
79
83
  end
80
84
 
85
+ # Overried devise: if user is signed out, not create the SP initiated logout request,
86
+ # redirect to saml_sign_out_success_url,
87
+ # or devise's after_sign_out_path_for
88
+ def verify_signed_out_user
89
+ if all_signed_out?
90
+ set_flash_message! :notice, :already_signed_out
91
+
92
+ redirect_to Devise.saml_sign_out_success_url.presence ||
93
+ Devise::SessionsController.new.after_sign_out_path_for(resource_name)
94
+ end
95
+ end
96
+
81
97
  def generate_idp_logout_response(saml_config, logout_request_id)
82
98
 
83
99
  params = {}
@@ -33,7 +33,7 @@ module Devise
33
33
  key = Devise.saml_default_user_key
34
34
  decorated_response = ::SamlAuthenticatable::SamlResponse.new(
35
35
  saml_response,
36
- Devise.saml_attribute_map_resolver.new(saml_response).attribute_map,
36
+ attribute_map(saml_response),
37
37
  )
38
38
  if Devise.saml_use_subject
39
39
  auth_value = saml_response.name_id
@@ -80,6 +80,18 @@ module Devise
80
80
  def find_for_shibb_authentication(conditions)
81
81
  find_for_authentication(conditions)
82
82
  end
83
+
84
+ def attribute_map(saml_response = nil)
85
+ attribute_map_resolver.new(saml_response).attribute_map
86
+ end
87
+
88
+ def attribute_map_resolver
89
+ if Devise.saml_attribute_map_resolver.respond_to?(:new)
90
+ Devise.saml_attribute_map_resolver
91
+ else
92
+ Devise.saml_attribute_map_resolver.constantize
93
+ end
94
+ end
83
95
  end
84
96
  end
85
97
  end
@@ -22,7 +22,7 @@ module DeviseSamlAuthenticatable
22
22
  def adapter_based_config(idp_entity_id)
23
23
  config = Marshal.load(Marshal.dump(Devise.saml_config))
24
24
 
25
- Devise.idp_settings_adapter.settings(idp_entity_id).each do |k,v|
25
+ idp_settings_adapter.settings(idp_entity_id).each do |k,v|
26
26
  acc = "#{k.to_s}=".to_sym
27
27
 
28
28
  if config.respond_to? acc
@@ -33,7 +33,23 @@ module DeviseSamlAuthenticatable
33
33
  end
34
34
 
35
35
  def get_idp_entity_id(params)
36
- Devise.idp_entity_id_reader.entity_id(params)
36
+ idp_entity_id_reader.entity_id(params)
37
+ end
38
+
39
+ def idp_entity_id_reader
40
+ if Devise.idp_entity_id_reader.respond_to?(:entity_id)
41
+ Devise.idp_entity_id_reader
42
+ else
43
+ @idp_entity_id_reader ||= Devise.idp_entity_id_reader.constantize
44
+ end
45
+ end
46
+
47
+ def idp_settings_adapter
48
+ if Devise.idp_settings_adapter.respond_to?(:settings)
49
+ Devise.idp_settings_adapter
50
+ else
51
+ @idp_settings_adapter ||= Devise.idp_settings_adapter.constantize
52
+ end
37
53
  end
38
54
  end
39
55
  end
@@ -8,8 +8,7 @@ module Devise
8
8
  if params[:SAMLResponse]
9
9
  OneLogin::RubySaml::Response.new(
10
10
  params[:SAMLResponse],
11
- settings: saml_config(get_idp_entity_id(params)),
12
- allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
11
+ response_options,
13
12
  )
14
13
  else
15
14
  false
@@ -36,8 +35,7 @@ module Devise
36
35
  def parse_saml_response
37
36
  @response = OneLogin::RubySaml::Response.new(
38
37
  params[:SAMLResponse],
39
- settings: saml_config(get_idp_entity_id(params)),
40
- allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
38
+ response_options,
41
39
  )
42
40
  unless @response.is_valid?
43
41
  failed_auth("Auth errors: #{@response.errors.join(', ')}")
@@ -57,6 +55,18 @@ module Devise
57
55
  Devise.saml_failed_callback.new.handle(@response, self) if Devise.saml_failed_callback
58
56
  end
59
57
 
58
+ def response_options
59
+ options = {
60
+ settings: saml_config(get_idp_entity_id(params)),
61
+ allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
62
+ }
63
+
64
+ if Devise.saml_validate_in_response_to
65
+ options[:matches_request_id] = request.session[:saml_transaction_id] || "ID_MISSING"
66
+ end
67
+
68
+ options
69
+ end
60
70
  end
61
71
  end
62
72
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseSamlAuthenticatable
2
- VERSION = "1.6.0"
2
+ VERSION = "1.7.0"
3
3
  end
@@ -56,7 +56,7 @@ module Devise
56
56
 
57
57
  # Reader that can parse entity id from a SAMLMessage
58
58
  mattr_accessor :idp_entity_id_reader
59
- @@idp_entity_id_reader ||= ::DeviseSamlAuthenticatable::DefaultIdpEntityIdReader
59
+ @@idp_entity_id_reader ||= "::DeviseSamlAuthenticatable::DefaultIdpEntityIdReader"
60
60
 
61
61
  # Implements a #handle method that takes the response and strategy as an argument
62
62
  mattr_accessor :saml_failed_callback
@@ -67,9 +67,13 @@ module Devise
67
67
  mattr_accessor :saml_relay_state
68
68
  @@saml_relay_state
69
69
 
70
+ # Validate that the InResponseTo header in SAML responses matches the ID of the request.
71
+ mattr_accessor :saml_validate_in_response_to
72
+ @@saml_validate_in_response_to = false
73
+
70
74
  # Instead of storing the attribute_map in attribute-map.yml, store it in the database, or set it programatically
71
75
  mattr_accessor :saml_attribute_map_resolver
72
- @@saml_attribute_map_resolver ||= ::DeviseSamlAuthenticatable::DefaultAttributeMapResolver
76
+ @@saml_attribute_map_resolver ||= "::DeviseSamlAuthenticatable::DefaultAttributeMapResolver"
73
77
 
74
78
  # Implements a #validate method that takes the retrieved resource and response right after retrieval,
75
79
  # and returns true if it's valid. False will cause authentication to fail.
@@ -1,4 +1,5 @@
1
1
  require 'rails_helper'
2
+ require 'support/ruby_saml_support'
2
3
 
3
4
  # The important parts from devise
4
5
  class DeviseController < ApplicationController
@@ -8,38 +9,55 @@ class DeviseController < ApplicationController
8
9
  User
9
10
  end
10
11
 
12
+ def resource_name
13
+ "users"
14
+ end
15
+
11
16
  def require_no_authentication
12
17
  end
18
+
19
+ def set_flash_message!(key, kind, options = {})
20
+ flash[key] = I18n.t("devise.sessions.#{kind}")
21
+ end
13
22
  end
23
+
14
24
  class Devise::SessionsController < DeviseController
15
25
  def destroy
16
26
  sign_out
17
27
  redirect_to after_sign_out_path_for(:user)
18
28
  end
19
-
20
- def verify_signed_out_user
21
- # no-op for these tests
22
- end
23
29
  end
24
30
 
25
31
  require_relative '../../../app/controllers/devise/saml_sessions_controller'
26
32
 
27
33
  describe Devise::SamlSessionsController, type: :controller do
34
+ include RubySamlSupport
35
+
28
36
  let(:idp_providers_adapter) { spy("Stub IDPSettings Adaptor") }
29
37
 
30
38
  before do
31
39
  @request.env["devise.mapping"] = Devise.mappings[:user]
32
- allow(idp_providers_adapter).to receive(:settings).and_return({
40
+ settings = {
33
41
  assertion_consumer_service_url: "acs_url",
34
42
  assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
35
43
  name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
36
44
  issuer: "sp_issuer",
37
45
  idp_entity_id: "http://www.example.com",
38
46
  authn_context: "",
39
- idp_slo_target_url: "http://idp_slo_url",
40
- idp_sso_target_url: "http://idp_sso_url",
41
47
  idp_cert: "idp_cert"
48
+ }
49
+ with_ruby_saml_1_12_or_greater(proc {
50
+ settings.merge!(
51
+ idp_slo_service_url: "http://idp_slo_url",
52
+ idp_sso_service_url: "http://idp_sso_url",
53
+ )
54
+ }, else_do: proc {
55
+ settings.merge!(
56
+ idp_slo_target_url: "http://idp_slo_url",
57
+ idp_sso_target_url: "http://idp_sso_url",
58
+ )
42
59
  })
60
+ allow(idp_providers_adapter).to receive(:settings).and_return(settings)
43
61
  end
44
62
 
45
63
  before do
@@ -72,6 +90,13 @@ describe Devise::SamlSessionsController, type: :controller do
72
90
  do_get
73
91
  expect(response).to redirect_to(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
74
92
  end
93
+
94
+ it "stores saml_transaction_id in the session" do
95
+ do_get
96
+ if OneLogin::RubySaml::Authrequest.public_instance_methods.include?(:request_id)
97
+ expect(session[:saml_transaction_id]).to be_present
98
+ end
99
+ end
75
100
  end
76
101
 
77
102
  context "with a specified idp" do
@@ -84,6 +109,13 @@ describe Devise::SamlSessionsController, type: :controller do
84
109
  expect(response).to redirect_to(%r(\Ahttp://idp_sso_url\?SAMLRequest=))
85
110
  end
86
111
 
112
+ it "stores saml_transaction_id in the session" do
113
+ do_get
114
+ if OneLogin::RubySaml::Authrequest.public_instance_methods.include?(:request_id)
115
+ expect(session[:saml_transaction_id]).to be_present
116
+ end
117
+ end
118
+
87
119
  it "uses the DefaultIdpEntityIdReader" do
88
120
  expect(DeviseSamlAuthenticatable::DefaultIdpEntityIdReader).to receive(:entity_id)
89
121
  do_get
@@ -172,80 +204,136 @@ describe Devise::SamlSessionsController, type: :controller do
172
204
  end
173
205
 
174
206
  describe '#destroy' do
175
- before do
176
- allow(controller).to receive(:sign_out)
177
- end
207
+ subject { delete :destroy }
178
208
 
179
- context "when using the default saml config" do
180
- it "signs out and redirects to the IdP" do
181
- delete :destroy
182
- expect(controller).to have_received(:sign_out)
183
- expect(response).to redirect_to(%r(\Ahttp://localhost:8009/saml/logout\?SAMLRequest=))
209
+ context "when user is signed out" do
210
+ before do
211
+ class Devise::SessionsController < DeviseController
212
+ def all_signed_out?
213
+ true
214
+ end
215
+ end
184
216
  end
185
- end
186
217
 
187
- context "when configured to use a non-transient name identifier" do
188
- before do
189
- allow(Devise.saml_config).to receive(:name_identifier_format).and_return("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent")
218
+ shared_examples "not create SP initiated logout request" do
219
+ it do
220
+ expect(OneLogin::RubySaml::Logoutrequest).not_to receive(:new)
221
+ subject
222
+ end
223
+ end
224
+
225
+ context "when Devise.saml_sign_out_success_url is set" do
226
+ before do
227
+ allow(Devise).to receive(:saml_sign_out_success_url).and_return("http://localhost:8009/logged_out")
228
+ end
229
+
230
+ it "redirect to saml_sign_out_success_url" do
231
+ is_expected.to redirect_to "http://localhost:8009/logged_out"
232
+ expect(flash[:notice]).to eq I18n.t("devise.sessions.already_signed_out")
233
+ end
234
+
235
+ it_behaves_like "not create SP initiated logout request"
190
236
  end
191
237
 
192
- it "includes a LogoutRequest with the name identifier and session index", :aggregate_failures do
193
- controller.current_user = Struct.new(:email, :session_index).new("user@example.com", "sessionindex")
238
+ context "when Devise.saml_sign_out_success_url is not set" do
239
+ before do
240
+ class Devise::SessionsController < DeviseController
241
+ def after_sign_out_path_for(_)
242
+ "http://localhost:8009/logged_out"
243
+ end
244
+ end
245
+ end
194
246
 
195
- actual_settings = nil
196
- expect_any_instance_of(OneLogin::RubySaml::Logoutrequest).to receive(:create) do |_, settings|
197
- actual_settings = settings
198
- "http://localhost:8009/saml/logout"
247
+ it "redirect to devise's after sign out path" do
248
+ is_expected.to redirect_to "http://localhost:8009/logged_out"
249
+ expect(flash[:notice]).to eq I18n.t("devise.sessions.already_signed_out")
199
250
  end
200
251
 
201
- delete :destroy
202
- expect(actual_settings.name_identifier_value).to eq("user@example.com")
203
- expect(actual_settings.sessionindex).to eq("sessionindex")
252
+ it_behaves_like "not create SP initiated logout request"
204
253
  end
205
254
  end
206
255
 
207
- context "with a specified idp" do
256
+ context "when user is not signed out" do
208
257
  before do
209
- Devise.idp_settings_adapter = idp_providers_adapter
258
+ class Devise::SessionsController < DeviseController
259
+ def all_signed_out?
260
+ false
261
+ end
262
+ end
263
+ allow(controller).to receive(:sign_out)
210
264
  end
211
265
 
212
- it "redirects to the associated IdP SSO target url" do
213
- expect(DeviseSamlAuthenticatable::DefaultIdpEntityIdReader).to receive(:entity_id)
214
- delete :destroy
215
- expect(controller).to have_received(:sign_out)
216
- expect(response).to redirect_to(%r(\Ahttp://idp_slo_url\?SAMLRequest=))
266
+ context "when using the default saml config" do
267
+ it "signs out and redirects to the IdP" do
268
+ delete :destroy
269
+ expect(controller).to have_received(:sign_out)
270
+ expect(response).to redirect_to(%r(\Ahttp://localhost:8009/saml/logout\?SAMLRequest=))
271
+ end
217
272
  end
218
273
 
219
- context "with a specified idp entity id reader" do
220
- class OurIdpEntityIdReader
221
- def self.entity_id(params)
222
- params[:entity_id]
223
- end
274
+ context "when configured to use a non-transient name identifier" do
275
+ before do
276
+ allow(Devise.saml_config).to receive(:name_identifier_format).and_return("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent")
224
277
  end
225
278
 
226
- subject(:do_delete) {
227
- if Rails::VERSION::MAJOR > 4
228
- delete :destroy, params: {entity_id: "http://www.example.com"}
229
- else
230
- delete :destroy, entity_id: "http://www.example.com"
279
+ it "includes a LogoutRequest with the name identifier and session index", :aggregate_failures do
280
+ controller.current_user = Struct.new(:email, :session_index).new("user@example.com", "sessionindex")
281
+
282
+ actual_settings = nil
283
+ expect_any_instance_of(OneLogin::RubySaml::Logoutrequest).to receive(:create) do |_, settings|
284
+ actual_settings = settings
285
+ "http://localhost:8009/saml/logout"
231
286
  end
232
- }
233
287
 
234
- before do
235
- @default_reader = Devise.idp_entity_id_reader
236
- Devise.idp_entity_id_reader = OurIdpEntityIdReader # which will have some different behavior
288
+ delete :destroy
289
+ expect(actual_settings.name_identifier_value).to eq("user@example.com")
290
+ expect(actual_settings.sessionindex).to eq("sessionindex")
237
291
  end
292
+ end
238
293
 
239
- after do
240
- Devise.idp_entity_id_reader = @default_reader
294
+ context "with a specified idp" do
295
+ before do
296
+ Devise.idp_settings_adapter = idp_providers_adapter
241
297
  end
242
298
 
243
- it "redirects to the associated IdP SLO target url" do
244
- do_delete
299
+ it "redirects to the associated IdP SSO target url" do
300
+ expect(DeviseSamlAuthenticatable::DefaultIdpEntityIdReader).to receive(:entity_id)
301
+ delete :destroy
245
302
  expect(controller).to have_received(:sign_out)
246
- expect(idp_providers_adapter).to have_received(:settings).with("http://www.example.com")
247
303
  expect(response).to redirect_to(%r(\Ahttp://idp_slo_url\?SAMLRequest=))
248
304
  end
305
+
306
+ context "with a specified idp entity id reader" do
307
+ class OurIdpEntityIdReader
308
+ def self.entity_id(params)
309
+ params[:entity_id]
310
+ end
311
+ end
312
+
313
+ subject(:do_delete) {
314
+ if Rails::VERSION::MAJOR > 4
315
+ delete :destroy, params: {entity_id: "http://www.example.com"}
316
+ else
317
+ delete :destroy, entity_id: "http://www.example.com"
318
+ end
319
+ }
320
+
321
+ before do
322
+ @default_reader = Devise.idp_entity_id_reader
323
+ Devise.idp_entity_id_reader = OurIdpEntityIdReader # which will have some different behavior
324
+ end
325
+
326
+ after do
327
+ Devise.idp_entity_id_reader = @default_reader
328
+ end
329
+
330
+ it "redirects to the associated IdP SLO target url" do
331
+ do_delete
332
+ expect(controller).to have_received(:sign_out)
333
+ expect(idp_providers_adapter).to have_received(:settings).with("http://www.example.com")
334
+ expect(response).to redirect_to(%r(\Ahttp://idp_slo_url\?SAMLRequest=))
335
+ end
336
+ end
249
337
  end
250
338
  end
251
339
  end
@@ -372,4 +372,10 @@ describe Devise::Models::SamlAuthenticatable do
372
372
  allow(Devise).to receive(:saml_resource_locator).and_return(block)
373
373
  end
374
374
  end
375
+
376
+ describe "::attribute_map" do
377
+ it "returns the attribute map" do
378
+ expect(Model.attribute_map).to eq(attributemap)
379
+ end
380
+ end
375
381
  end
@@ -1,6 +1,9 @@
1
1
  require 'spec_helper'
2
+ require 'support/ruby_saml_support'
2
3
 
3
4
  describe DeviseSamlAuthenticatable::SamlConfig do
5
+ include RubySamlSupport
6
+
4
7
  let(:saml_config) { controller.saml_config }
5
8
  let(:controller) { Class.new { include DeviseSamlAuthenticatable::SamlConfig }.new }
6
9
 
@@ -26,32 +29,54 @@ describe DeviseSamlAuthenticatable::SamlConfig do
26
29
  let(:saml_config) { controller.saml_config(idp_entity_id) }
27
30
  let(:idp_providers_adapter) {
28
31
  Class.new {
32
+ extend RubySamlSupport
33
+
29
34
  def self.settings(idp_entity_id)
30
35
  #some hash of stuff (by doing a fetch, in our case, but could also be a giant hash keyed by idp_entity_id)
31
36
  if idp_entity_id == "http://www.example.com"
32
- {
37
+ base = {
33
38
  assertion_consumer_service_url: "acs_url",
34
39
  assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
35
40
  name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
36
41
  issuer: "sp_issuer",
37
42
  idp_entity_id: "http://www.example.com",
38
43
  authn_context: "",
39
- idp_slo_target_url: "idp_slo_url",
40
- idp_sso_target_url: "idp_sso_url",
41
44
  idp_cert: "idp_cert"
42
45
  }
46
+ with_ruby_saml_1_12_or_greater(proc {
47
+ base.merge!(
48
+ idp_slo_service_url: "idp_slo_url",
49
+ idp_sso_service_url: "idp_sso_url",
50
+ )
51
+ }, else_do: proc {
52
+ base.merge!(
53
+ idp_slo_target_url: "idp_slo_url",
54
+ idp_sso_target_url: "idp_sso_url",
55
+ )
56
+ })
57
+ base
43
58
  elsif idp_entity_id == "http://www.example.com_other"
44
- {
59
+ base = {
45
60
  assertion_consumer_service_url: "acs_url_other",
46
61
  assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST_other",
47
62
  name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress_other",
48
63
  issuer: "sp_issuer_other",
49
64
  idp_entity_id: "http://www.example.com_other",
50
65
  authn_context: "_other",
51
- idp_slo_target_url: "idp_slo_url_other",
52
- idp_sso_target_url: "idp_sso_url_other",
53
66
  idp_cert: "idp_cert_other"
54
67
  }
68
+ with_ruby_saml_1_12_or_greater(proc {
69
+ base.merge!(
70
+ idp_slo_service_url: "idp_slo_url_other",
71
+ idp_sso_service_url: "idp_sso_url_other",
72
+ )
73
+ }, else_do: proc {
74
+ base.merge!(
75
+ idp_slo_target_url: "idp_slo_url_other",
76
+ idp_sso_target_url: "idp_sso_url_other",
77
+ )
78
+ })
79
+ base
55
80
  else
56
81
  {}
57
82
  end
@@ -63,7 +88,11 @@ describe DeviseSamlAuthenticatable::SamlConfig do
63
88
  let(:idp_entity_id) { "http://www.example.com" }
64
89
  it "uses the settings from the adapter for that idp" do
65
90
  expect(saml_config.idp_entity_id).to eq (idp_entity_id)
66
- expect(saml_config.idp_sso_target_url).to eq ("idp_sso_url")
91
+ with_ruby_saml_1_12_or_greater(proc {
92
+ expect(saml_config.idp_sso_service_url).to eq('idp_sso_url')
93
+ }, else_do: proc {
94
+ expect(saml_config.idp_sso_target_url).to eq('idp_sso_url')
95
+ })
67
96
  expect(saml_config.class).to eq OneLogin::RubySaml::Settings
68
97
  end
69
98
  end
@@ -72,7 +101,11 @@ describe DeviseSamlAuthenticatable::SamlConfig do
72
101
  let(:idp_entity_id) { "http://www.example.com_other" }
73
102
  it "returns the other idp settings" do
74
103
  expect(saml_config.idp_entity_id).to eq (idp_entity_id)
75
- expect(saml_config.idp_sso_target_url).to eq ("idp_sso_url_other")
104
+ with_ruby_saml_1_12_or_greater(proc {
105
+ expect(saml_config.idp_sso_service_url).to eq('idp_sso_url_other')
106
+ }, else_do: proc {
107
+ expect(saml_config.idp_sso_target_url).to eq('idp_sso_url_other')
108
+ })
76
109
  expect(saml_config.class).to eq OneLogin::RubySaml::Settings
77
110
  end
78
111
  end
@@ -80,11 +113,8 @@ describe DeviseSamlAuthenticatable::SamlConfig do
80
113
  end
81
114
 
82
115
  context "when config/idp.yml exists" do
83
- before do
84
- allow(Rails).to receive(:env).and_return("environment")
85
- allow(Rails).to receive(:root).and_return("/railsroot")
86
- allow(File).to receive(:exists?).with("/railsroot/config/idp.yml").and_return(true)
87
- allow(File).to receive(:read).with("/railsroot/config/idp.yml").and_return(<<-IDP)
116
+ let(:idp_yaml) {
117
+ yaml = <<-IDP
88
118
  ---
89
119
  environment:
90
120
  assertion_consumer_logout_service_binding: assertion_consumer_logout_service_binding
@@ -104,8 +134,6 @@ environment:
104
134
  idp_cert_fingerprint: idp_cert_fingerprint
105
135
  idp_cert_fingerprint_algorithm: idp_cert_fingerprint_algorithm
106
136
  idp_entity_id: idp_entity_id
107
- idp_slo_target_url: idp_slo_target_url
108
- idp_sso_target_url: idp_sso_target_url
109
137
  issuer: issuer
110
138
  name_identifier_format: name_identifier_format
111
139
  name_identifier_value: name_identifier_value
@@ -116,6 +144,20 @@ environment:
116
144
  sessionindex: sessionindex
117
145
  sp_name_qualifier: sp_name_qualifier
118
146
  IDP
147
+ with_ruby_saml_1_12_or_greater(proc { yaml << <<SERVICE_URLS }, else_do: proc { yaml << <<TARGET_URLS })
148
+ idp_slo_service_url: idp_slo_service_url
149
+ idp_sso_service_url: idp_sso_service_url
150
+ SERVICE_URLS
151
+ idp_slo_target_url: idp_slo_service_url
152
+ idp_sso_target_url: idp_sso_service_url
153
+ TARGET_URLS
154
+ yaml
155
+ }
156
+ before do
157
+ allow(Rails).to receive(:env).and_return("environment")
158
+ allow(Rails).to receive(:root).and_return("/railsroot")
159
+ allow(File).to receive(:exists?).with("/railsroot/config/idp.yml").and_return(true)
160
+ allow(File).to receive(:read).with("/railsroot/config/idp.yml").and_return(idp_yaml)
119
161
  end
120
162
 
121
163
  it "uses that file's contents" do
@@ -136,8 +178,13 @@ environment:
136
178
  expect(saml_config.idp_cert_fingerprint).to eq('idp_cert_fingerprint')
137
179
  expect(saml_config.idp_cert_fingerprint_algorithm).to eq('idp_cert_fingerprint_algorithm')
138
180
  expect(saml_config.idp_entity_id).to eq('idp_entity_id')
139
- expect(saml_config.idp_slo_target_url).to eq('idp_slo_target_url')
140
- expect(saml_config.idp_sso_target_url).to eq('idp_sso_target_url')
181
+ with_ruby_saml_1_12_or_greater(proc {
182
+ expect(saml_config.idp_slo_service_url).to eq('idp_slo_service_url')
183
+ expect(saml_config.idp_sso_service_url).to eq('idp_sso_service_url')
184
+ }, else_do: proc {
185
+ expect(saml_config.idp_slo_target_url).to eq('idp_slo_service_url')
186
+ expect(saml_config.idp_sso_target_url).to eq('idp_sso_service_url')
187
+ })
141
188
  expect(saml_config.issuer).to eq('issuer')
142
189
  expect(saml_config.name_identifier_format).to eq('name_identifier_format')
143
190
  expect(saml_config.name_identifier_value).to eq('name_identifier_value')
@@ -1,6 +1,9 @@
1
1
  require 'rails_helper'
2
+ require 'support/ruby_saml_support'
2
3
 
3
4
  describe Devise::Strategies::SamlAuthenticatable do
5
+ include RubySamlSupport
6
+
4
7
  subject(:strategy) { described_class.new(env, :user) }
5
8
  let(:env) { {} }
6
9
  let(:errors) { ["Test1", "Test2"] }
@@ -16,7 +19,7 @@ describe Devise::Strategies::SamlAuthenticatable do
16
19
  let(:user) { double(:user) }
17
20
  before do
18
21
  allow(strategy).to receive(:mapping).and_return(mapping)
19
- allow(user).to receive(:after_saml_authentication)
22
+ allow(user).to(receive(:after_saml_authentication)) if user
20
23
  end
21
24
 
22
25
  let(:params) { {} }
@@ -54,17 +57,27 @@ describe Devise::Strategies::SamlAuthenticatable do
54
57
  let(:idp_providers_adapter) {
55
58
  Class.new {
56
59
  def self.settings(idp_entity_id)
57
- {
60
+ base = {
58
61
  assertion_consumer_service_url: "acs_url",
59
62
  assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
60
63
  name_identifier_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
61
64
  issuer: "sp_issuer",
62
65
  idp_entity_id: "http://www.example.com",
63
66
  authn_context: "",
64
- idp_slo_target_url: "idp_slo_url",
65
- idp_sso_target_url: "http://idp_sso_url",
66
67
  idp_cert: "idp_cert"
67
68
  }
69
+ with_ruby_saml_1_12_or_greater(proc {
70
+ base.merge!(
71
+ idp_slo_service_url: "idp_slo_url",
72
+ idp_sso_service_url: "http://idp_sso_url",
73
+ )
74
+ }, else_do: proc {
75
+ base.merge!(
76
+ idp_slo_target_url: "idp_slo_url",
77
+ idp_sso_target_url: "http://idp_sso_url",
78
+ )
79
+ })
80
+ base
68
81
  end
69
82
  }
70
83
  }
@@ -93,8 +106,10 @@ describe Devise::Strategies::SamlAuthenticatable do
93
106
  let(:user) { nil }
94
107
 
95
108
  it "fails to authenticate" do
96
- expect(strategy).to receive(:fail!).with(:invalid)
97
109
  strategy.authenticate!
110
+ expect(strategy).to be_halted
111
+ expect(strategy.message).to be(:invalid)
112
+ expect(strategy.result).to be(:failure)
98
113
  end
99
114
 
100
115
  it 'logs the error' do
@@ -152,6 +167,40 @@ describe Devise::Strategies::SamlAuthenticatable do
152
167
  strategy.authenticate!
153
168
  end
154
169
  end
170
+
171
+ context "when saml_validate_in_response_to is opted-in to" do
172
+ let(:transaction_id) { "abc123" }
173
+
174
+ before do
175
+ allow(Devise).to receive(:saml_validate_in_response_to).and_return(true)
176
+ allow_any_instance_of(ActionDispatch::Request).to receive(:session).and_return(session)
177
+ end
178
+
179
+ context "when the session has a saml_transaction_id" do
180
+ let(:session) { { saml_transaction_id: transaction_id }}
181
+
182
+ it "is valid with the matches_request_id parameter" do
183
+ expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], hash_including(matches_request_id: transaction_id))
184
+ expect(strategy).to be_valid
185
+ end
186
+
187
+ it "authenticates with the matches_request_id parameter" do
188
+ expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], hash_including(matches_request_id: transaction_id))
189
+
190
+ expect(strategy).to receive(:success!).with(user)
191
+ strategy.authenticate!
192
+ end
193
+ end
194
+
195
+ context "when the session is missing a saml_transaction_id" do
196
+ let(:session) { { } }
197
+
198
+ it "uses 'ID_MISSING' for matches_request_id so validation will fail" do
199
+ expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse], hash_including(matches_request_id: "ID_MISSING"))
200
+ strategy.authenticate!
201
+ end
202
+ end
203
+ end
155
204
  end
156
205
 
157
206
  it "is not valid without a SAMLResponse parameter" do
@@ -141,7 +141,7 @@ describe "SAML Authentication", type: :feature do
141
141
  context "when the idp_settings_adapter key is set" do
142
142
  before(:each) do
143
143
  create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "false")
144
- create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true", 'IDP_SETTINGS_ADAPTER' => "IdpSettingsAdapter", 'IDP_ENTITY_ID_READER' => "OurEntityIdReader")
144
+ create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true", 'IDP_SETTINGS_ADAPTER' => '"IdpSettingsAdapter"', 'IDP_ENTITY_ID_READER' => '"OurEntityIdReader"')
145
145
 
146
146
  # use a different port for this entity ID; configured in spec/support/idp_settings_adapter.rb.erb
147
147
  @idp_pid = start_app('idp', 8010)
@@ -204,7 +204,7 @@ describe "SAML Authentication", type: :feature do
204
204
  )
205
205
  create_app(
206
206
  "sp",
207
- "ATTRIBUTE_MAP_RESOLVER" => "AttributeMapResolver",
207
+ "ATTRIBUTE_MAP_RESOLVER" => '"AttributeMapResolver"',
208
208
  "USE_SUBJECT_TO_AUTHENTICATE" => "true",
209
209
  )
210
210
  @idp_pid = start_app("idp", idp_port)
@@ -6,7 +6,7 @@ gemspec path: '../..'
6
6
  group :test do
7
7
  gem 'rake'
8
8
  gem 'rspec', '~> 3.0'
9
- gem 'rails', '~> 5.2'
9
+ gem 'rails', '~> 5.2.0'
10
10
  gem 'rspec-rails', '~> 3.9'
11
11
  gem 'sqlite3', '~> 1.3.6'
12
12
  gem 'capybara'
@@ -0,0 +1,14 @@
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', '~> 6.0.0'
10
+ gem 'rspec-rails', '~> 5.0'
11
+ gem 'sqlite3', '~> 1.4.0'
12
+ gem 'capybara'
13
+ gem 'poltergeist'
14
+ end
@@ -1,17 +1,27 @@
1
1
  class IdpSettingsAdapter
2
2
  def self.settings(idp_entity_id)
3
3
  if idp_entity_id == "http://localhost:8020/saml/metadata"
4
- {
5
- assertion_consumer_service_url: "http://localhost:8020/users/saml/auth",
6
- assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
7
- name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
8
- issuer: "sp_issuer",
9
- idp_entity_id: "http://localhost:8020/saml/metadata",
10
- authn_context: "",
4
+ base = {
5
+ assertion_consumer_service_url: "http://localhost:8020/users/saml/auth",
6
+ assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
7
+ name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
8
+ issuer: "sp_issuer",
9
+ idp_entity_id: "http://localhost:8020/saml/metadata",
10
+ authn_context: "",
11
+ idp_cert_fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
12
+ }
13
+ if Gem::Version.new(OneLogin::RubySaml::VERSION) >= Gem::Version.new("1.12.0")
14
+ base.merge!(
15
+ idp_slo_service_url: "http://localhost:8010/saml/logout",
16
+ idp_sso_service_url: "http://localhost:8010/saml/auth",
17
+ )
18
+ else
19
+ base.merge!(
11
20
  idp_slo_target_url: "http://localhost:8010/saml/logout",
12
21
  idp_sso_target_url: "http://localhost:8010/saml/auth",
13
- idp_cert_fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
14
- }
22
+ )
23
+ end
24
+ base
15
25
  else
16
26
  {}
17
27
  end
@@ -21,12 +21,11 @@ def create_app(name, env = {})
21
21
  puts "[#{name}] Creating Rails app"
22
22
  rails_new_options = %w[-T -J -S --skip-spring --skip-listen --skip-bootsnap]
23
23
  rails_new_options << "-O" if name == "idp"
24
- with_clean_env do
25
- Dir.chdir(working_directory) do
26
- FileUtils.rm_rf(name)
27
- puts("rails _#{Rails.version}_ new #{name} #{rails_new_options.join(" ")} -m #{File.expand_path("../#{name}_template.rb", __FILE__)}")
28
- system(env, "rails", "_#{Rails.version}_", "new", name, *rails_new_options, "-m", File.expand_path("../#{name}_template.rb", __FILE__))
29
- end
24
+ env.merge!("RUBY_SAML_VERSION" => OneLogin::RubySaml::VERSION)
25
+ Dir.chdir(working_directory) do
26
+ FileUtils.rm_rf(name)
27
+ puts("[#{working_directory}] rails _#{Rails.version}_ new #{name} #{rails_new_options.join(" ")} -m #{File.expand_path("../#{name}_template.rb", __FILE__)}")
28
+ system(env, "rails", "_#{Rails.version}_", "new", name, *rails_new_options, "-m", File.expand_path("../#{name}_template.rb", __FILE__))
30
29
  end
31
30
  end
32
31
 
@@ -0,0 +1,10 @@
1
+ module RubySamlSupport
2
+ VERSION_1_12 = Gem::Version.new("1.12.0")
3
+ def with_ruby_saml_1_12_or_greater(body, args = {else_do: nil})
4
+ if Gem::Version.new(OneLogin::RubySaml::VERSION) >= VERSION_1_12
5
+ body.call
6
+ else
7
+ args[:else_do].call
8
+ end
9
+ end
10
+ end
@@ -6,15 +6,16 @@ attribute_map_resolver = ENV.fetch("ATTRIBUTE_MAP_RESOLVER", "nil")
6
6
  saml_session_index_key = ENV.fetch('SAML_SESSION_INDEX_KEY', ":session_index")
7
7
  use_subject_to_authenticate = ENV.fetch('USE_SUBJECT_TO_AUTHENTICATE')
8
8
  idp_settings_adapter = ENV.fetch('IDP_SETTINGS_ADAPTER', "nil")
9
- idp_entity_id_reader = ENV.fetch('IDP_ENTITY_ID_READER', "DeviseSamlAuthenticatable::DefaultIdpEntityIdReader")
9
+ idp_entity_id_reader = ENV.fetch('IDP_ENTITY_ID_READER', '"DeviseSamlAuthenticatable::DefaultIdpEntityIdReader"')
10
10
  saml_failed_callback = ENV.fetch('SAML_FAILED_CALLBACK', "nil")
11
+ ruby_saml_version = ENV.fetch("RUBY_SAML_VERSION")
11
12
 
12
13
  if Rails::VERSION::MAJOR < 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR < 2)
13
14
  gsub_file 'config/secrets.yml', /secret_key_base:.*$/, 'secret_key_base: "8b5889df1fcf03f76c7d66da02d8776bcc85b06bed7d9c592f076d9c8a5455ee6d4beae45986c3c030b40208db5e612f2a6ef8283036a352e3fae83c5eda36be"'
14
15
  end
15
16
 
16
17
  gem 'devise_saml_authenticatable', path: File.expand_path("../../..", __FILE__)
17
- gem 'ruby-saml', OneLogin::RubySaml::VERSION
18
+ gem 'ruby-saml', ruby_saml_version
18
19
  gem 'thin'
19
20
 
20
21
  insert_into_file('Gemfile', after: /\z/) {
@@ -92,13 +93,24 @@ after_bundle do
92
93
  config.saml_configure do |settings|
93
94
  settings.assertion_consumer_service_url = "http://localhost:8020/users/saml/auth"
94
95
  settings.issuer = "http://localhost:8020/saml/metadata"
95
- settings.idp_slo_target_url = "http://localhost:8009/saml/logout"
96
- settings.idp_sso_target_url = "http://localhost:8009/saml/auth"
97
96
  settings.idp_cert_fingerprint = "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
98
97
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
99
98
  end
100
99
  end
101
100
  CONFIG
101
+ if Gem::Version.new(ruby_saml_version) >= Gem::Version.new("1.12.0")
102
+ gsub_file 'config/initializers/devise.rb', /^ config\.saml_configure do \|settings\|$/, <<CONFIG
103
+ config.saml_configure do |settings|
104
+ settings.idp_slo_service_url = "http://localhost:8009/saml/logout"
105
+ settings.idp_sso_service_url = "http://localhost:8009/saml/auth"
106
+ CONFIG
107
+ else
108
+ gsub_file 'config/initializers/devise.rb', /^ config\.saml_configure do \|settings\|$/, <<CONFIG
109
+ config.saml_configure do |settings|
110
+ settings.idp_slo_target_url = "http://localhost:8009/saml/logout"
111
+ settings.idp_sso_target_url = "http://localhost:8009/saml/auth"
112
+ CONFIG
113
+ end
102
114
 
103
115
  generate :controller, 'home', 'index'
104
116
  insert_into_file('app/controllers/home_controller.rb', after: "class HomeController < ApplicationController\n") {
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.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josef Sauter
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-13 00:00:00.000000000 Z
11
+ date: 2021-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: devise
@@ -45,9 +45,9 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".github/workflows/ci.yml"
48
49
  - ".gitignore"
49
50
  - ".rspec"
50
- - ".travis.yml"
51
51
  - Gemfile
52
52
  - LICENSE
53
53
  - README.md
@@ -82,12 +82,14 @@ files:
82
82
  - spec/support/Gemfile.rails5
83
83
  - spec/support/Gemfile.rails5.1
84
84
  - spec/support/Gemfile.rails5.2
85
+ - spec/support/Gemfile.rails6
85
86
  - spec/support/attribute-map.yml
86
87
  - spec/support/attribute_map_resolver.rb.erb
87
88
  - spec/support/idp_settings_adapter.rb.erb
88
89
  - spec/support/idp_template.rb
89
90
  - spec/support/rails_app.rb
90
91
  - spec/support/response_encrypted_nameid.xml.base64
92
+ - spec/support/ruby_saml_support.rb
91
93
  - spec/support/saml_idp-saml_slo_post.html.erb
92
94
  - spec/support/saml_idp_controller.rb.erb
93
95
  - spec/support/sp_template.rb
@@ -95,7 +97,7 @@ homepage: ''
95
97
  licenses:
96
98
  - MIT
97
99
  metadata: {}
98
- post_install_message:
100
+ post_install_message:
99
101
  rdoc_options: []
100
102
  require_paths:
101
103
  - lib
@@ -110,8 +112,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
112
  - !ruby/object:Gem::Version
111
113
  version: '0'
112
114
  requirements: []
113
- rubygems_version: 3.0.6
114
- signing_key:
115
+ rubygems_version: 3.1.4
116
+ signing_key:
115
117
  specification_version: 4
116
118
  summary: SAML Authentication for devise
117
119
  test_files:
@@ -130,12 +132,14 @@ test_files:
130
132
  - spec/support/Gemfile.rails5
131
133
  - spec/support/Gemfile.rails5.1
132
134
  - spec/support/Gemfile.rails5.2
135
+ - spec/support/Gemfile.rails6
133
136
  - spec/support/attribute-map.yml
134
137
  - spec/support/attribute_map_resolver.rb.erb
135
138
  - spec/support/idp_settings_adapter.rb.erb
136
139
  - spec/support/idp_template.rb
137
140
  - spec/support/rails_app.rb
138
141
  - spec/support/response_encrypted_nameid.xml.base64
142
+ - spec/support/ruby_saml_support.rb
139
143
  - spec/support/saml_idp-saml_slo_post.html.erb
140
144
  - spec/support/saml_idp_controller.rb.erb
141
145
  - spec/support/sp_template.rb
data/.travis.yml DELETED
@@ -1,52 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - "2.0.0"
4
- - "2.1.10"
5
- - "2.2.10"
6
- - "2.3.8"
7
- - "2.4.10"
8
- - "2.5.8"
9
- - "2.6.6"
10
- - "2.7.1"
11
- gemfile:
12
- - Gemfile
13
- - spec/support/Gemfile.rails5.2
14
- - spec/support/Gemfile.rails5.1
15
- - spec/support/Gemfile.rails5
16
- - spec/support/Gemfile.rails4
17
- matrix:
18
- allow_failures:
19
- - rvm: "2.0.0"
20
- gemfile: Gemfile
21
- - rvm: "2.0.0"
22
- gemfile: spec/support/Gemfile.rails5
23
- - rvm: "2.0.0"
24
- gemfile: spec/support/Gemfile.rails5.1
25
- - rvm: "2.0.0"
26
- gemfile: spec/support/Gemfile.rails5.2
27
- - rvm: "2.1.10"
28
- gemfile: Gemfile
29
- - rvm: "2.1.10"
30
- gemfile: spec/support/Gemfile.rails5
31
- - rvm: "2.1.10"
32
- gemfile: spec/support/Gemfile.rails5.1
33
- - rvm: "2.1.10"
34
- gemfile: spec/support/Gemfile.rails5.2
35
- - rvm: "2.2.10"
36
- gemfile: Gemfile
37
- - rvm: "2.2.10"
38
- gemfile: spec/support/Gemfile.rails5.2
39
- - rvm: "2.3.8"
40
- gemfile: Gemfile
41
- - rvm: "2.4.10"
42
- gemfile: Gemfile
43
- - rvm: "2.6.6"
44
- gemfile: spec/support/Gemfile.rails4
45
- - rvm: "2.7.1"
46
- gemfile: spec/support/Gemfile.rails4
47
-
48
- before_install:
49
- - command -v bundle || gem install bundler -v '~> 1.17.3'
50
-
51
- script:
52
- - bundle exec rake