devise_saml_authenticatable 1.3.2 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -2
  3. data/.travis.yml +29 -22
  4. data/Gemfile +2 -2
  5. data/README.md +105 -32
  6. data/app/controllers/devise/saml_sessions_controller.rb +35 -7
  7. data/devise_saml_authenticatable.gemspec +2 -1
  8. data/lib/devise_saml_authenticatable.rb +27 -2
  9. data/lib/devise_saml_authenticatable/default_attribute_map_resolver.rb +26 -0
  10. data/lib/devise_saml_authenticatable/default_idp_entity_id_reader.rb +2 -0
  11. data/lib/devise_saml_authenticatable/exception.rb +1 -1
  12. data/lib/devise_saml_authenticatable/model.rb +16 -18
  13. data/lib/devise_saml_authenticatable/routes.rb +17 -6
  14. data/lib/devise_saml_authenticatable/saml_mapped_attributes.rb +15 -2
  15. data/lib/devise_saml_authenticatable/strategy.rb +1 -0
  16. data/lib/devise_saml_authenticatable/version.rb +1 -1
  17. data/spec/controllers/devise/saml_sessions_controller_spec.rb +118 -11
  18. data/spec/devise_saml_authenticatable/default_attribute_map_resolver_spec.rb +58 -0
  19. data/spec/devise_saml_authenticatable/model_spec.rb +68 -7
  20. data/spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb +50 -0
  21. data/spec/features/saml_authentication_spec.rb +45 -21
  22. data/spec/rails_helper.rb +6 -2
  23. data/spec/routes/routes_spec.rb +102 -0
  24. data/spec/spec_helper.rb +7 -0
  25. data/spec/support/Gemfile.rails4 +23 -6
  26. data/spec/support/Gemfile.rails5 +13 -2
  27. data/spec/support/Gemfile.rails5.1 +25 -0
  28. data/spec/support/Gemfile.rails5.2 +25 -0
  29. data/spec/support/attribute-map.yml +12 -0
  30. data/spec/support/attribute_map_resolver.rb.erb +14 -0
  31. data/spec/support/idp_settings_adapter.rb.erb +5 -5
  32. data/spec/support/idp_template.rb +6 -2
  33. data/spec/support/rails_app.rb +75 -17
  34. data/spec/support/saml_idp_controller.rb.erb +13 -6
  35. data/spec/support/sp_template.rb +45 -21
  36. metadata +25 -13
  37. data/spec/support/Gemfile.ruby-saml-1.3 +0 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 269c0505d92b49c3b9a79d1b8aece87533018b10
4
- data.tar.gz: 259f7e012808aabe3ab06e854a01deab8959843a
2
+ SHA256:
3
+ metadata.gz: d3ca38cfe51cdf71fc8a264de868fc285bfa413307aa4798e097fbf88e85020d
4
+ data.tar.gz: 2371ef1371e29547b4eb9681066fa43f0b6d39236b7df8676236bf13678e2d01
5
5
  SHA512:
6
- metadata.gz: 83a89cbe087accbcf239e047027188a35793c9cdcb3198fd207edeb95652bb6e8957ef8e0aeaa9b919f01cbb91677bf4c5ade49fae0a85b86cf95edc4b09592b
7
- data.tar.gz: 230f5490186a0853cd63f401b390afa29129b8f103de04cbb64a734274edd88de9467030784122cccdc743fbd5eb7babf75ad3635be5b51d2a5af467d4e17b1f
6
+ metadata.gz: 872bfb214a00504735d9183d169e4119ebb9c6ae3129de729be642f39c4675b57040bb1c9d46899e73d534aa82d7ac9291c425109c10eaeb290831480e7976ab
7
+ data.tar.gz: 3269ef293937aef443afa9a325acc0addf027c3ad7421865a9ebe1554d7215461767ffe9e34cd4cc2bf8edbae831e7e342dd3c47245d57209d8ddd820a128212
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,45 +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
47
+
48
+ before_install:
49
+ - command -v bundle || gem install bundler -v '~> 1.17.3'
34
50
 
35
51
  script:
36
52
  - bundle exec rake
37
-
38
- notifications:
39
- hipchat:
40
- rooms:
41
- secure: cuDak5a6fBeg+sp61COqxQfzdcFEsjwCqtwvCISso0RNh5SR8v+uVYKcA8rlK+GE1l9uR7tLRHeHF3ZmzvFSOat07NvpScvjZXi+OSpWlc6rwQ6Pl6bBP6gu6sREiKVe0eT/uGrvJloyWKZaXIhiiBzQ+ZERx/ssGA9WMmNkhlwy1OgGnPNurNNHZLBjEZn1V6kdyxiXx6QPASNpjNEgN1G8dUh3qzcWUGVQGNZSJk65A6ie1MveNyecTjDhw+ADBU8nS28Ja4y6ohRm4FzofSgespYrvfygIZ5rYF0HPMj5FW1ZDWtM5355ojCk8RLT+ZkuhssCn1OJk7ogaOVjnYcOFRxEfpu3eIbjtMmUz3j4umatFqbgas+6SXMVIPkr5HUoTrP8HNFssIpcEBOnPwAF8QCpx+daHc0r2cc8lGuXhtJfpW0P2F0dmwJNiQ7//nz2y2xs84x4Gb7MV9tEDYp0FqEClMuFBkPNizBljarm04PkiLSrqvR52aMDfQz7YAX2oXAvFjPzI1GC0K8x7xX8TuHT9yuHy7fI+rUSNivZYLKO+IEZqPPDdJpXISUbVwanZoNvmQYk5PZV21MfDSGwQrz8eO/uFiAblj18yIlNbAfb2hdZDVYsm4EvWxELJtfaTxgrj6M3Y3m/KbCbCoDp+2jE307M2rxL0Gum2gk=
42
- template:
43
- - '%{repository}<a href="%{build_url}">#%{build_number}</a> (%{branch} - <a href="%{compare_url}">%{commit}</a> : %{author}): %{message}'
44
- format: html
45
- 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,8 +101,19 @@ 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
 
72
- # Configure with your SAML settings (see [ruby-saml][] for more information).
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
+
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|
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
74
117
  settings.assertion_consumer_service_url = "http://localhost:3000/users/saml/auth"
75
118
  settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
76
119
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
@@ -78,29 +121,32 @@ In `config/initializers/devise.rb`:
78
121
  settings.authn_context = ""
79
122
  settings.idp_slo_target_url = "http://localhost/simplesaml/www/saml2/idp/SingleLogoutService.php"
80
123
  settings.idp_sso_target_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
81
- settings.idp_cert = <<-CERT.chomp
82
- -----BEGIN CERTIFICATE-----
83
- 1111111111111111111111111111111111111111111111111111111111111111
84
- 1111111111111111111111111111111111111111111111111111111111111111
85
- 1111111111111111111111111111111111111111111111111111111111111111
86
- 1111111111111111111111111111111111111111111111111111111111111111
87
- 1111111111111111111111111111111111111111111111111111111111111111
88
- 1111111111111_______IDP_CERTIFICATE________111111111111111111111
89
- 1111111111111111111111111111111111111111111111111111111111111111
90
- 1111111111111111111111111111111111111111111111111111111111111111
91
- 1111111111111111111111111111111111111111111111111111111111111111
92
- 1111111111111111111111111111111111111111111111111111111111111111
93
- 1111111111111111111111111111111111111111111111111111111111111111
94
- 1111111111111111111111111111111111111111111111111111111111111111
95
- 1111111111111111111111111111111111111111111111111111111111111111
96
- 111111111111111111
97
- -----END CERTIFICATE-----
98
- 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"
99
126
  end
100
127
  end
101
128
  ```
102
129
 
103
- 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:
104
150
 
105
151
  ```yaml
106
152
  # attribute-map.yml
@@ -111,15 +157,39 @@ In the config directory, create a YAML file (`attribute-map.yml`) that maps SAML
111
157
  "urn:mace:dir:attribute-def:givenName": "name"
112
158
  ```
113
159
 
114
- The attribute mappings are very dependent on the way the IdP encodes the attributes.
115
- In this example the attributes are given in URN style.
116
- Other IdPs might provide them as OID's, or by other means.
160
+ ##### Attribute map initializer
117
161
 
118
- 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.
119
165
 
120
- 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
121
171
 
122
- 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
+ ```
123
193
 
124
194
  ## Supporting Multiple IdPs
125
195
 
@@ -159,6 +229,7 @@ class IdPSettingsAdapter
159
229
  end
160
230
  end
161
231
  ```
232
+ Settings specified in the adapter will override settings in `config/initializers/devise.rb`. This is useful for establishing common settings or defaults across all IdPs.
162
233
 
163
234
  Detecting the entity ID passed to the `settings` method is done by `config.idp_entity_id_reader`.
164
235
 
@@ -192,10 +263,12 @@ Logout requests from the IDP are supported by the `idp_sign_out` endpoint. Dire
192
263
 
193
264
  `saml_session_index_key` must be configured to support this feature.
194
265
 
195
- ## Signing and Encrypting Authentication Requests
266
+ ## Signing and Encrypting Authentication Requests and Assertions
196
267
 
197
268
  ruby-saml 1.0.0 supports signature and decrypt. The only requirement is to set the public certificate and the private key. For more information, see [the ruby-saml documentation](https://github.com/onelogin/ruby-saml#signing).
198
269
 
270
+ If you have multiple IdPs, the certificate and private key must be in the shared settings in `config/initializers/devise.rb`.
271
+
199
272
  ## Thanks
200
273
 
201
274
  The continued maintenance of this gem could not have been possible without the hard work of [Adam Stegman](https://github.com/adamstegman) and [Mitch Lindsay](https://github.com/mitch-lindsay). Thank you guys for keeping this project alive.
@@ -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,13 +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(_)
68
+ idp_entity_id = get_idp_entity_id(params)
56
69
  request = OneLogin::RubySaml::Logoutrequest.new
57
- request.create(saml_config)
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)
58
80
  end
59
81
 
60
82
  def generate_idp_logout_response(saml_config, logout_request_id)
61
- 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)
62
90
  end
63
91
  end
@@ -7,6 +7,7 @@ Gem::Specification.new do |gem|
7
7
  gem.description = %q{SAML Authentication for devise}
8
8
  gem.summary = %q{SAML Authentication for devise }
9
9
  gem.homepage = ""
10
+ gem.license = "MIT"
10
11
 
11
12
  gem.files = `git ls-files`.split($\)
12
13
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -17,5 +18,5 @@ Gem::Specification.new do |gem|
17
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
19
 
19
20
  gem.add_dependency("devise","> 2.0.0")
20
- gem.add_dependency("ruby-saml","~> 1.3")
21
+ gem.add_dependency("ruby-saml","~> 1.7")
21
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
@@ -94,7 +111,7 @@ module Devise
94
111
  end
95
112
 
96
113
  # Proc that is called if Devise.saml_update_user and/or Devise.saml_create_user are true.
97
- # Recieves the user object, saml_response and auth_value, and defines how the object's values are
114
+ # Receives the user object, saml_response and auth_value, and defines how the object's values are
98
115
  # updated with regards to the SAML response. See saml_default_update_resource_hook for an example.
99
116
  mattr_accessor :saml_update_resource_hook
100
117
  @@saml_update_resource_hook = @@saml_default_update_resource_hook
@@ -107,11 +124,19 @@ module Devise
107
124
  end
108
125
 
109
126
  # Proc that is called to resolve the saml_response and auth_value into the correct user object.
110
- # Recieves a copy of the ActiveRecord::Model, saml_response and auth_value. Is expected to return
127
+ # Receives a copy of the ActiveRecord::Model, saml_response and auth_value. Is expected to return
111
128
  # one instance of the provided model that is the matched account, or nil if none exists.
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.