devise_saml_authenticatable 1.4.1 → 1.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -2
  3. data/.travis.yml +29 -20
  4. data/Gemfile +2 -2
  5. data/README.md +104 -35
  6. data/app/controllers/devise/saml_sessions_controller.rb +34 -7
  7. data/lib/devise_saml_authenticatable.rb +26 -1
  8. data/lib/devise_saml_authenticatable/default_attribute_map_resolver.rb +26 -0
  9. data/lib/devise_saml_authenticatable/exception.rb +1 -1
  10. data/lib/devise_saml_authenticatable/model.rb +14 -13
  11. data/lib/devise_saml_authenticatable/routes.rb +17 -6
  12. data/lib/devise_saml_authenticatable/saml_config.rb +18 -2
  13. data/lib/devise_saml_authenticatable/saml_mapped_attributes.rb +15 -2
  14. data/lib/devise_saml_authenticatable/strategy.rb +1 -1
  15. data/lib/devise_saml_authenticatable/version.rb +1 -1
  16. data/spec/controllers/devise/saml_sessions_controller_spec.rb +69 -11
  17. data/spec/devise_saml_authenticatable/default_attribute_map_resolver_spec.rb +58 -0
  18. data/spec/devise_saml_authenticatable/model_spec.rb +55 -7
  19. data/spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb +50 -0
  20. data/spec/features/saml_authentication_spec.rb +46 -38
  21. data/spec/rails_helper.rb +6 -2
  22. data/spec/routes/routes_spec.rb +102 -0
  23. data/spec/spec_helper.rb +7 -0
  24. data/spec/support/Gemfile.rails4 +20 -10
  25. data/spec/support/Gemfile.rails5 +13 -2
  26. data/spec/support/Gemfile.rails5.1 +25 -0
  27. data/spec/support/Gemfile.rails5.2 +25 -0
  28. data/spec/support/attribute-map.yml +12 -0
  29. data/spec/support/attribute_map_resolver.rb.erb +14 -0
  30. data/spec/support/idp_settings_adapter.rb.erb +5 -5
  31. data/spec/support/idp_template.rb +6 -2
  32. data/spec/support/rails_app.rb +75 -17
  33. data/spec/support/saml_idp_controller.rb.erb +13 -6
  34. data/spec/support/sp_template.rb +46 -22
  35. metadata +21 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 377131d065bd2670e5830eff8c0cdf75fa221d69
4
- data.tar.gz: 9304fbf24d72a198ea36290fe45f25a63be62790
2
+ SHA256:
3
+ metadata.gz: f648472eaaf23e5e668e51f84fadff2354879045f0ec798a383dd8a2e2ee135a
4
+ data.tar.gz: 443a5e883595f8baa2297e2ca173e8f97ae0abf3502538ce1d0f4e0e1c84081e
5
5
  SHA512:
6
- metadata.gz: 0d4d5fb0973e73ee61052e9ba5f2a34cc5bf721659e32d0b760db46d355648cd1d825e7510278e73f7597aa4fb52c87187270441131bf409baf1308583ccfeb7
7
- data.tar.gz: 0749346b7c5d0db06780fe811763eb62ffebb49f0f0d0c5d6de45c69fd04baafb3fd3c8957c95628c403044bfc26b2ee27460db28817c05d3ca5802d58b3842d
6
+ metadata.gz: 4765fe0a60d2a8ffd97d30d5b0d2fdb55d70a56bd9cb91f760ef7862a0d9ec80570da5d4758a784ac552232e1e5893fdd1accead2ff677893b0eb4a3500dcb9c
7
+ data.tar.gz: fcd0e6f70b75fdb9b12b3c1954899989e26f8ddb874c6222ab59ca460387a9672ab7767c13855bc4c3cbabd14fdcdb3ddabb2478412dbb452a17194c20ff4c32
data/.gitignore CHANGED
@@ -13,6 +13,4 @@ lib/bundler/man
13
13
  pkg
14
14
  rdoc
15
15
  spec/reports
16
- spec/support/idp/
17
- spec/support/sp/
18
16
  tmp
@@ -1,43 +1,52 @@
1
1
  language: ruby
2
2
  rvm:
3
- - "1.9.3"
4
3
  - "2.0.0"
5
4
  - "2.1.10"
6
- - "2.2.9"
7
- - "2.3.6"
8
- - "2.4.3"
9
- - "2.5.0"
5
+ - "2.2.10"
6
+ - "2.3.8"
7
+ - "2.4.10"
8
+ - "2.5.8"
9
+ - "2.6.6"
10
+ - "2.7.1"
10
11
  gemfile:
11
12
  - Gemfile
13
+ - spec/support/Gemfile.rails5.2
14
+ - spec/support/Gemfile.rails5.1
12
15
  - spec/support/Gemfile.rails5
13
16
  - spec/support/Gemfile.rails4
14
17
  matrix:
15
18
  allow_failures:
16
- - rvm: "1.9.3"
17
- gemfile: Gemfile
18
- - rvm: "1.9.3"
19
- gemfile: spec/support/Gemfile.rails5
20
19
  - rvm: "2.0.0"
21
20
  gemfile: Gemfile
22
21
  - rvm: "2.0.0"
23
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
24
27
  - rvm: "2.1.10"
25
28
  gemfile: Gemfile
26
29
  - rvm: "2.1.10"
27
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
28
47
 
29
48
  before_install:
30
- # update bundler to avoid https://github.com/travis-ci/travis-ci/issues/5239
31
- - gem install bundler
49
+ - command -v bundle || gem install bundler -v '~> 1.17.3'
32
50
 
33
51
  script:
34
52
  - bundle exec rake
35
-
36
- notifications:
37
- hipchat:
38
- rooms:
39
- secure: cuDak5a6fBeg+sp61COqxQfzdcFEsjwCqtwvCISso0RNh5SR8v+uVYKcA8rlK+GE1l9uR7tLRHeHF3ZmzvFSOat07NvpScvjZXi+OSpWlc6rwQ6Pl6bBP6gu6sREiKVe0eT/uGrvJloyWKZaXIhiiBzQ+ZERx/ssGA9WMmNkhlwy1OgGnPNurNNHZLBjEZn1V6kdyxiXx6QPASNpjNEgN1G8dUh3qzcWUGVQGNZSJk65A6ie1MveNyecTjDhw+ADBU8nS28Ja4y6ohRm4FzofSgespYrvfygIZ5rYF0HPMj5FW1ZDWtM5355ojCk8RLT+ZkuhssCn1OJk7ogaOVjnYcOFRxEfpu3eIbjtMmUz3j4umatFqbgas+6SXMVIPkr5HUoTrP8HNFssIpcEBOnPwAF8QCpx+daHc0r2cc8lGuXhtJfpW0P2F0dmwJNiQ7//nz2y2xs84x4Gb7MV9tEDYp0FqEClMuFBkPNizBljarm04PkiLSrqvR52aMDfQz7YAX2oXAvFjPzI1GC0K8x7xX8TuHT9yuHy7fI+rUSNivZYLKO+IEZqPPDdJpXISUbVwanZoNvmQYk5PZV21MfDSGwQrz8eO/uFiAblj18yIlNbAfb2hdZDVYsm4EvWxELJtfaTxgrj6M3Y3m/KbCbCoDp+2jE307M2rxL0Gum2gk=
40
- template:
41
- - '%{repository}<a href="%{build_url}">#%{build_number}</a> (%{branch} - <a href="%{compare_url}">%{commit}</a> : %{author}): %{message}'
42
- format: html
43
- on_pull_requests: true
data/Gemfile CHANGED
@@ -6,9 +6,9 @@ gemspec
6
6
  group :test do
7
7
  gem 'rake'
8
8
  gem 'rspec', '~> 3.0'
9
- gem 'rails', '~> 5.1'
9
+ gem 'rails', '~> 6.0'
10
10
  gem 'rspec-rails'
11
- gem 'sqlite3'
11
+ gem 'sqlite3', '~> 1.4.0'
12
12
  gem 'capybara'
13
13
  gem 'poltergeist'
14
14
  end
data/README.md CHANGED
@@ -6,19 +6,20 @@ It uses [ruby-saml][] to handle all SAML-related stuff.
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
9
+ Add this gem to your application's Gemfile:
10
10
 
11
- gem 'devise_saml_authenticatable'
11
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
12
+ gem "devise_saml_authenticatable", github: "apokalipto/devise_saml_authenticatable"
12
13
 
13
14
  And then execute:
14
15
 
15
16
  $ bundle
16
17
 
17
- Or install it yourself as:
18
+ ## Usage
18
19
 
19
- $ gem install devise_saml_authenticatable
20
+ Follow the [normal devise installation process](https://github.com/plataformatec/devise/tree/master#getting-started). The controller filters and helpers are unchanged from normal devise usage.
20
21
 
21
- ## Usage
22
+ ### Configuring Models
22
23
 
23
24
  In `app/models/<YOUR_MODEL>.rb` set the `:saml_authenticatable` strategy.
24
25
 
@@ -32,6 +33,37 @@ In the example the model is `user.rb`:
32
33
  end
33
34
  ```
34
35
 
36
+ ### Configuring routes
37
+
38
+ In `config/routes.rb` add `devise_for` to set up helper methods and routes:
39
+
40
+ ```ruby
41
+ devise_for :users
42
+ ```
43
+
44
+ The named routes can be customized in the initializer config file.
45
+
46
+ ### Configuring the IdP
47
+
48
+ An extra step in SAML SSO setup is adding your application to your identity provider. The required setup is specific to each IdP, but we have some examples in [our wiki](https://github.com/apokalipto/devise_saml_authenticatable/wiki). You'll need to tell your IdP how to send requests and responses to your application.
49
+
50
+ - Creating a new session: `/users/saml/auth`
51
+ - IdPs may call this the "consumer," "recipient," "destination," or even "single sign-on." This is where they send a SAML response for an authenticated user.
52
+ - Metadata: `/users/saml/metadata`
53
+ - IdPs may call this the "audience."
54
+ - Single Logout: `/users/saml/idp_sign_out`
55
+ - if desired, you can ask the IdP to send a Logout request to this endpoint to sign the user out of your application when they sign out of the IdP itself.
56
+
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
+
59
+ - Issuer (`idp_entity_id`)
60
+ - SSO endpoint (`idp_sso_target_url`)
61
+ - SLO endpoint (`idp_slo_target_url`)
62
+ - Certificate fingerprint (`idp_cert_fingerprint`) and algorithm (`idp_cert_fingerprint_algorithm`)
63
+ - Or the certificate itself (`idp_cert`)
64
+
65
+ ### Configuring handling of IdP requests and responses
66
+
35
67
  In `config/initializers/devise.rb`:
36
68
 
37
69
  ```ruby
@@ -57,18 +89,28 @@ In `config/initializers/devise.rb`:
57
89
  # If you don't set it then email will be extracted from SAML assertation attributes.
58
90
  config.saml_use_subject = true
59
91
 
60
- # You can support multiple IdPs by setting this value to a class that implements a #settings method which takes
61
- # an IdP entity id as an argument and returns a hash of idp settings for the corresponding IdP.
62
- 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"
63
95
 
64
96
  # You provide you own method to find the idp_entity_id in a SAML message in the case of multiple IdPs
65
- # by setting this to a custom reader class, or use the default.
66
- # 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"
67
99
 
68
100
  # You can set a handler object that takes the response for a failed SAML request and the strategy,
69
101
  # and implements a #handle method. This method can then redirect the user, return error messages, etc.
70
102
  # config.saml_failed_callback = nil
71
103
 
104
+ # You can customize the named routes generated in case of named route collisions with
105
+ # other Devise modules or libraries. Set the saml_route_helper_prefix to a string that will
106
+ # be appended to the named route.
107
+ # If saml_route_helper_prefix = 'saml' then the new_user_session route becomes new_saml_user_session
108
+ # config.saml_route_helper_prefix = 'saml'
109
+
110
+ # You can add allowance for clock drift between the sp and idp.
111
+ # This is a time in seconds.
112
+ # config.allowed_clock_drift_in_seconds = 0
113
+
72
114
  # Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/onelogin/ruby-saml).
73
115
  config.saml_configure do |settings|
74
116
  # assertion_consumer_service_url is required starting with ruby-saml 1.4.3: https://github.com/onelogin/ruby-saml#updating-from-142-to-143
@@ -79,29 +121,32 @@ In `config/initializers/devise.rb`:
79
121
  settings.authn_context = ""
80
122
  settings.idp_slo_target_url = "http://localhost/simplesaml/www/saml2/idp/SingleLogoutService.php"
81
123
  settings.idp_sso_target_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
82
- settings.idp_cert = <<-CERT.chomp
83
- -----BEGIN CERTIFICATE-----
84
- 1111111111111111111111111111111111111111111111111111111111111111
85
- 1111111111111111111111111111111111111111111111111111111111111111
86
- 1111111111111111111111111111111111111111111111111111111111111111
87
- 1111111111111111111111111111111111111111111111111111111111111111
88
- 1111111111111111111111111111111111111111111111111111111111111111
89
- 1111111111111_______IDP_CERTIFICATE________111111111111111111111
90
- 1111111111111111111111111111111111111111111111111111111111111111
91
- 1111111111111111111111111111111111111111111111111111111111111111
92
- 1111111111111111111111111111111111111111111111111111111111111111
93
- 1111111111111111111111111111111111111111111111111111111111111111
94
- 1111111111111111111111111111111111111111111111111111111111111111
95
- 1111111111111111111111111111111111111111111111111111111111111111
96
- 1111111111111111111111111111111111111111111111111111111111111111
97
- 111111111111111111
98
- -----END CERTIFICATE-----
99
- CERT
124
+ 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
+ settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
100
126
  end
101
127
  end
102
128
  ```
103
129
 
104
- In the config directory, create a YAML file (`attribute-map.yml`) that maps SAML attributes with your model's fields:
130
+ #### Attributes
131
+
132
+ There are two ways to map SAML attributes to User attributes:
133
+
134
+ - [initializer](#attribute-map-initializer)
135
+ - [config file](#attribute-map-config-file)
136
+
137
+ The attribute mappings are very dependent on the way the IdP encodes the attributes.
138
+ In these examples the attributes are given in URN style.
139
+ Other IdPs might provide them as OID's, or by other means.
140
+
141
+ You are now ready to test it against an IdP.
142
+
143
+ When the user visits `/users/saml/sign_in` they will be redirected to the login page of the IdP.
144
+
145
+ Upon successful login the user is redirected to the Devise `user_root_path`.
146
+
147
+ ##### Attribute map config file
148
+
149
+ Create a YAML file (`config/attribute-map.yml`) that maps SAML attributes with your model's fields:
105
150
 
106
151
  ```yaml
107
152
  # attribute-map.yml
@@ -112,15 +157,39 @@ In the config directory, create a YAML file (`attribute-map.yml`) that maps SAML
112
157
  "urn:mace:dir:attribute-def:givenName": "name"
113
158
  ```
114
159
 
115
- The attribute mappings are very dependent on the way the IdP encodes the attributes.
116
- In this example the attributes are given in URN style.
117
- Other IdPs might provide them as OID's, or by other means.
160
+ ##### Attribute map initializer
118
161
 
119
- You are now ready to test it against an IdP.
162
+ In `config/initializers/devise.rb` (see above), add an attribute map resolver.
163
+ The resolver gets the [SAML response from the IdP](https://github.com/onelogin/ruby-saml/blob/master/lib/onelogin/ruby-saml/response.rb) so it can decide which attribute map to load.
164
+ If you only have one IdP, you can use the config file above, or just return a single hash.
120
165
 
121
- When the user visits `/users/saml/sign_in` they will be redirected to the login page of the IdP.
166
+ ```ruby
167
+ # config/initializers/devise.rb
168
+ Devise.setup do |config|
169
+ ...
170
+ # ==> Configuration for :saml_authenticatable
122
171
 
123
- Upon successful login the user is redirected to the Devise `user_root_path`.
172
+ config.saml_attribute_map_resolver = "MyAttributeMapResolver"
173
+ end
174
+ ```
175
+
176
+ ```ruby
177
+ # app/lib/my_attribute_map_resolver
178
+ class MyAttributeMapResolver < DeviseSamlAuthenticatable::DefaultAttributeMapResolver
179
+ def attribute_map
180
+ issuer = saml_response.issuers.first
181
+ case issuer
182
+ when "idp_entity_id"
183
+ {
184
+ "urn:mace:dir:attribute-def:uid" => "user_name",
185
+ "urn:mace:dir:attribute-def:email" => "email",
186
+ "urn:mace:dir:attribute-def:name" => "last_name",
187
+ "urn:mace:dir:attribute-def:givenName" => "name",
188
+ }
189
+ end
190
+ end
191
+ end
192
+ ```
124
193
 
125
194
  ## Supporting Multiple IdPs
126
195
 
@@ -5,8 +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
9
  else
9
10
  skip_before_action :verify_authenticity_token, raise: false
11
+ prepend_before_action :store_info_for_sp_initiated_logout, only: :destroy
10
12
  end
11
13
 
12
14
  def new
@@ -18,8 +20,9 @@ class Devise::SamlSessionsController < Devise::SessionsController
18
20
  end
19
21
 
20
22
  def metadata
23
+ idp_entity_id = params[:idp_entity_id]
21
24
  meta = OneLogin::RubySaml::Metadata.new
22
- render :xml => meta.generate(saml_config)
25
+ render :xml => meta.generate(saml_config(idp_entity_id))
23
26
  end
24
27
 
25
28
  def idp_sign_out
@@ -30,16 +33,16 @@ class Devise::SamlSessionsController < Devise::SessionsController
30
33
 
31
34
  redirect_to generate_idp_logout_response(saml_config, logout_request.id)
32
35
  elsif params[:SAMLResponse]
33
- #Currently Devise handles the session invalidation when the request is made.
34
- #To support a true SP initiated logout response, the request ID would have to be tracked and session invalidated
35
- #based on that.
36
+ # Currently Devise handles the session invalidation when the request is made.
37
+ # To support a true SP initiated logout response, the request ID would have to be tracked and session invalidated
38
+ # based on that.
36
39
  if Devise.saml_sign_out_success_url
37
40
  redirect_to Devise.saml_sign_out_success_url
38
41
  else
39
42
  redirect_to action: :new
40
43
  end
41
44
  else
42
- head :invalid_request
45
+ head 500
43
46
  end
44
47
  end
45
48
 
@@ -51,14 +54,38 @@ class Devise::SamlSessionsController < Devise::SessionsController
51
54
  end
52
55
  end
53
56
 
57
+ # For non transient name ID, save info to identify user for logout purpose
58
+ # before that user's session got destroyed. These info are used in the
59
+ # `after_sign_out_path_for` method below.
60
+ def store_info_for_sp_initiated_logout
61
+ return if Devise.saml_config.name_identifier_format == "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
62
+ @name_identifier_value_for_sp_initiated_logout = Devise.saml_name_identifier_retriever.call(current_user)
63
+ @sessionindex_for_sp_initiated_logout = current_user.public_send(Devise.saml_session_index_key) if Devise.saml_session_index_key
64
+ end
65
+
54
66
  # Override devise to send user to IdP logout for SLO
55
67
  def after_sign_out_path_for(_)
56
68
  idp_entity_id = get_idp_entity_id(params)
57
69
  request = OneLogin::RubySaml::Logoutrequest.new
58
- request.create(saml_config(idp_entity_id))
70
+ saml_settings = saml_config(idp_entity_id).dup
71
+
72
+ # Add attributes to saml_settings which will later be used to create the SP
73
+ # initiated logout request
74
+ unless Devise.saml_config.name_identifier_format == 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient'
75
+ saml_settings.name_identifier_value = @name_identifier_value_for_sp_initiated_logout
76
+ saml_settings.sessionindex = @sessionindex_for_sp_initiated_logout
77
+ end
78
+
79
+ request.create(saml_settings)
59
80
  end
60
81
 
61
82
  def generate_idp_logout_response(saml_config, logout_request_id)
62
- OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil)
83
+
84
+ params = {}
85
+ if relay_state
86
+ params[:RelayState] = relay_state
87
+ end
88
+
89
+ OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil, params)
63
90
  end
64
91
  end
@@ -5,6 +5,7 @@ require "devise_saml_authenticatable/exception"
5
5
  require "devise_saml_authenticatable/logger"
6
6
  require "devise_saml_authenticatable/routes"
7
7
  require "devise_saml_authenticatable/saml_config"
8
+ require "devise_saml_authenticatable/default_attribute_map_resolver"
8
9
  require "devise_saml_authenticatable/default_idp_entity_id_reader"
9
10
 
10
11
  begin
@@ -19,6 +20,10 @@ end
19
20
 
20
21
  # Get saml information from config/saml.yml now
21
22
  module Devise
23
+ # Allow route customization to avoid collision
24
+ mattr_accessor :saml_route_helper_prefix
25
+ @@saml_route_helper_prefix
26
+
22
27
  # Allow logging
23
28
  mattr_accessor :saml_logger
24
29
  @@saml_logger = true
@@ -51,7 +56,7 @@ module Devise
51
56
 
52
57
  # Reader that can parse entity id from a SAMLMessage
53
58
  mattr_accessor :idp_entity_id_reader
54
- @@idp_entity_id_reader ||= ::DeviseSamlAuthenticatable::DefaultIdpEntityIdReader
59
+ @@idp_entity_id_reader ||= "::DeviseSamlAuthenticatable::DefaultIdpEntityIdReader"
55
60
 
56
61
  # Implements a #handle method that takes the response and strategy as an argument
57
62
  mattr_accessor :saml_failed_callback
@@ -62,11 +67,23 @@ module Devise
62
67
  mattr_accessor :saml_relay_state
63
68
  @@saml_relay_state
64
69
 
70
+ # Instead of storing the attribute_map in attribute-map.yml, store it in the database, or set it programatically
71
+ mattr_accessor :saml_attribute_map_resolver
72
+ @@saml_attribute_map_resolver ||= "::DeviseSamlAuthenticatable::DefaultAttributeMapResolver"
73
+
65
74
  # Implements a #validate method that takes the retrieved resource and response right after retrieval,
66
75
  # and returns true if it's valid. False will cause authentication to fail.
76
+ # Only one of saml_resource_validator and saml_resource_validator_hook may be used.
67
77
  mattr_accessor :saml_resource_validator
68
78
  @@saml_resource_validator
69
79
 
80
+ # Proc that determines whether a technically correct SAML response is valid per some custom logic.
81
+ # Receives the user object (or nil, if no match was found), decorated saml_response and
82
+ # auth_value, inspects the combination for acceptability of login (or create+login, if enabled),
83
+ # and returns true if it's valid. False will cause authentication to fail.
84
+ mattr_accessor :saml_resource_validator_hook
85
+ @@saml_resource_validator_hook
86
+
70
87
  # Custom value for ruby-saml allowed_clock_drift
71
88
  mattr_accessor :allowed_clock_drift_in_seconds
72
89
  @@allowed_clock_drift_in_seconds
@@ -112,6 +129,14 @@ module Devise
112
129
  # See saml_default_resource_locator above for an example.
113
130
  mattr_accessor :saml_resource_locator
114
131
  @@saml_resource_locator = @@saml_default_resource_locator
132
+
133
+ # Proc that is called to resolve the name identifier to use in a LogoutRequest for the current user.
134
+ # Receives the logged-in user.
135
+ # Is expected to return the identifier the IdP understands for this user, e.g. email address or username.
136
+ mattr_accessor :saml_name_identifier_retriever
137
+ @@saml_name_identifier_retriever = Proc.new do |current_user|
138
+ current_user.public_send(Devise.saml_default_user_key)
139
+ end
115
140
  end
116
141
 
117
142
  # Add saml_authenticatable strategy to defaults.