devise_saml_authenticatable 1.4.0 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -2
  3. data/.travis.yml +27 -24
  4. data/Gemfile +2 -2
  5. data/README.md +99 -30
  6. data/app/controllers/devise/saml_sessions_controller.rb +34 -7
  7. data/devise_saml_authenticatable.gemspec +1 -1
  8. data/lib/devise_saml_authenticatable.rb +25 -0
  9. data/lib/devise_saml_authenticatable/default_attribute_map_resolver.rb +26 -0
  10. data/lib/devise_saml_authenticatable/exception.rb +1 -1
  11. data/lib/devise_saml_authenticatable/model.rb +10 -17
  12. data/lib/devise_saml_authenticatable/routes.rb +17 -6
  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 +45 -37
  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 +45 -21
  35. metadata +23 -12
  36. data/spec/support/Gemfile.ruby-saml-1.3 +0 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 9a26db022c02d24566654e1d965c374a0e3b3380
4
- data.tar.gz: 1341712900741f3c7326ddb7f099468bbc1d1066
2
+ SHA256:
3
+ metadata.gz: c0d1e1f1df121795583d9fc25cab40edac01bb61f8adbdb4fac7c0c15bfe4b0c
4
+ data.tar.gz: 0d7770994b0c119da178a3b19ced6d52a88364d561279c5bd759ce5580fb5fe5
5
5
  SHA512:
6
- metadata.gz: 9ee3d6d7075cccdb05b87c8cea6e7f9feab58d3f2a0ae4fc1cbc9d6e679b82d1c59e675195c520867903808070cc8e9fb49ff99028b7816f039e029f0d455a6a
7
- data.tar.gz: 9ea8211cda495a8347e5e2672214e5fc8448e232ca1d961645dde9f22b18eaf9bd6bcf8e6876dea110cbe2292a8d01fdc303039629cbe781f3901a15ab73efc8
6
+ metadata.gz: d849dc5dff4bd09d0dd034449ed3e68494f0f1c82c8e31c708b7d6e0d3a366a2fd119fb24704d0e5a946b6fc206dbe072fadbf7218489abffdbe1b3644d41419
7
+ data.tar.gz: e64b35109ed35b61e804fa5e06d3b3c299210cf75804f706e299147d02a7ddb929246c1a7fdb65aee12f5ceed5348267d3c6e4657544288e3a45741474d5e30d
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,49 +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.7"
7
- - "2.3.4"
8
- - "2.4.1"
5
+ - "2.2.10"
6
+ - "2.3.8"
7
+ - "2.4.10"
8
+ - "2.5.8"
9
+ - "2.6.6"
10
+ - "2.7.1"
9
11
  gemfile:
10
12
  - Gemfile
13
+ - spec/support/Gemfile.rails5.2
14
+ - spec/support/Gemfile.rails5.1
11
15
  - spec/support/Gemfile.rails5
12
16
  - spec/support/Gemfile.rails4
13
- - spec/support/Gemfile.ruby-saml-1.3
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
- - rvm: "1.9.3"
21
- gemfile: spec/support/Gemfile.ruby-saml-1.3
22
19
  - rvm: "2.0.0"
23
20
  gemfile: Gemfile
24
21
  - rvm: "2.0.0"
25
22
  gemfile: spec/support/Gemfile.rails5
26
23
  - rvm: "2.0.0"
27
- gemfile: spec/support/Gemfile.ruby-saml-1.3
24
+ gemfile: spec/support/Gemfile.rails5.1
25
+ - rvm: "2.0.0"
26
+ gemfile: spec/support/Gemfile.rails5.2
28
27
  - rvm: "2.1.10"
29
28
  gemfile: Gemfile
30
29
  - rvm: "2.1.10"
31
30
  gemfile: spec/support/Gemfile.rails5
32
31
  - rvm: "2.1.10"
33
- gemfile: spec/support/Gemfile.ruby-saml-1.3
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
34
47
 
35
48
  before_install:
36
- # update bundler to avoid https://github.com/travis-ci/travis-ci/issues/5239
37
- - gem install bundler
49
+ - command -v bundle || gem install bundler -v '~> 1.17.3'
38
50
 
39
51
  script:
40
52
  - bundle exec rake
41
-
42
- notifications:
43
- hipchat:
44
- rooms:
45
- secure: cuDak5a6fBeg+sp61COqxQfzdcFEsjwCqtwvCISso0RNh5SR8v+uVYKcA8rlK+GE1l9uR7tLRHeHF3ZmzvFSOat07NvpScvjZXi+OSpWlc6rwQ6Pl6bBP6gu6sREiKVe0eT/uGrvJloyWKZaXIhiiBzQ+ZERx/ssGA9WMmNkhlwy1OgGnPNurNNHZLBjEZn1V6kdyxiXx6QPASNpjNEgN1G8dUh3qzcWUGVQGNZSJk65A6ie1MveNyecTjDhw+ADBU8nS28Ja4y6ohRm4FzofSgespYrvfygIZ5rYF0HPMj5FW1ZDWtM5355ojCk8RLT+ZkuhssCn1OJk7ogaOVjnYcOFRxEfpu3eIbjtMmUz3j4umatFqbgas+6SXMVIPkr5HUoTrP8HNFssIpcEBOnPwAF8QCpx+daHc0r2cc8lGuXhtJfpW0P2F0dmwJNiQ7//nz2y2xs84x4Gb7MV9tEDYp0FqEClMuFBkPNizBljarm04PkiLSrqvR52aMDfQz7YAX2oXAvFjPzI1GC0K8x7xX8TuHT9yuHy7fI+rUSNivZYLKO+IEZqPPDdJpXISUbVwanZoNvmQYk5PZV21MfDSGwQrz8eO/uFiAblj18yIlNbAfb2hdZDVYsm4EvWxELJtfaTxgrj6M3Y3m/KbCbCoDp+2jE307M2rxL0Gum2gk=
46
- template:
47
- - '%{repository}<a href="%{build_url}">#%{build_number}</a> (%{branch} - <a href="%{compare_url}">%{commit}</a> : %{author}): %{message}'
48
- format: html
49
- 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
@@ -69,6 +101,16 @@ In `config/initializers/devise.rb`:
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
@@ -18,5 +18,5 @@ Gem::Specification.new do |gem|
18
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
19
 
20
20
  gem.add_dependency("devise","> 2.0.0")
21
- gem.add_dependency("ruby-saml","~> 1.3")
21
+ gem.add_dependency("ruby-saml","~> 1.7")
22
22
  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
@@ -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.
@@ -0,0 +1,26 @@
1
+ module DeviseSamlAuthenticatable
2
+ class DefaultAttributeMapResolver
3
+ def initialize(saml_response)
4
+ @saml_response = saml_response
5
+ end
6
+
7
+ def attribute_map
8
+ return {} unless File.exist?(attribute_map_path)
9
+
10
+ attribute_map = YAML.load(File.read(attribute_map_path))
11
+ if attribute_map.key?(Rails.env)
12
+ attribute_map[Rails.env]
13
+ else
14
+ attribute_map
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :saml_response
21
+
22
+ def attribute_map_path
23
+ Rails.root.join("config", "attribute-map.yml")
24
+ end
25
+ end
26
+ end