devise_saml_authenticatable 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -0
- data/README.md +6 -2
- data/app/controllers/devise/saml_sessions_controller.rb +2 -1
- data/devise_saml_authenticatable.gemspec +1 -0
- data/lib/devise_saml_authenticatable.rb +2 -2
- data/lib/devise_saml_authenticatable/default_idp_entity_id_reader.rb +2 -0
- data/lib/devise_saml_authenticatable/strategy.rb +1 -0
- data/lib/devise_saml_authenticatable/version.rb +1 -1
- data/spec/controllers/devise/saml_sessions_controller_spec.rb +53 -4
- data/spec/features/saml_authentication_spec.rb +18 -2
- data/spec/support/Gemfile.rails4 +8 -1
- data/spec/support/Gemfile.ruby-saml-1.3 +1 -3
- data/spec/support/idp_settings_adapter.rb.erb +2 -2
- data/spec/support/sp_template.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a26db022c02d24566654e1d965c374a0e3b3380
|
4
|
+
data.tar.gz: 1341712900741f3c7326ddb7f099468bbc1d1066
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ee3d6d7075cccdb05b87c8cea6e7f9feab58d3f2a0ae4fc1cbc9d6e679b82d1c59e675195c520867903808070cc8e9fb49ff99028b7816f039e029f0d455a6a
|
7
|
+
data.tar.gz: 9ea8211cda495a8347e5e2672214e5fc8448e232ca1d961645dde9f22b18eaf9bd6bcf8e6876dea110cbe2292a8d01fdc303039629cbe781f3901a15ab73efc8
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -69,8 +69,9 @@ In `config/initializers/devise.rb`:
|
|
69
69
|
# and implements a #handle method. This method can then redirect the user, return error messages, etc.
|
70
70
|
# config.saml_failed_callback = nil
|
71
71
|
|
72
|
-
# Configure with your SAML settings (see
|
72
|
+
# Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/onelogin/ruby-saml).
|
73
73
|
config.saml_configure do |settings|
|
74
|
+
# 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
75
|
settings.assertion_consumer_service_url = "http://localhost:3000/users/saml/auth"
|
75
76
|
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
76
77
|
settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
|
@@ -159,6 +160,7 @@ class IdPSettingsAdapter
|
|
159
160
|
end
|
160
161
|
end
|
161
162
|
```
|
163
|
+
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
164
|
|
163
165
|
Detecting the entity ID passed to the `settings` method is done by `config.idp_entity_id_reader`.
|
164
166
|
|
@@ -192,10 +194,12 @@ Logout requests from the IDP are supported by the `idp_sign_out` endpoint. Dire
|
|
192
194
|
|
193
195
|
`saml_session_index_key` must be configured to support this feature.
|
194
196
|
|
195
|
-
## Signing and Encrypting Authentication Requests
|
197
|
+
## Signing and Encrypting Authentication Requests and Assertions
|
196
198
|
|
197
199
|
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
200
|
|
201
|
+
If you have multiple IdPs, the certificate and private key must be in the shared settings in `config/initializers/devise.rb`.
|
202
|
+
|
199
203
|
## Thanks
|
200
204
|
|
201
205
|
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.
|
@@ -53,8 +53,9 @@ class Devise::SamlSessionsController < Devise::SessionsController
|
|
53
53
|
|
54
54
|
# Override devise to send user to IdP logout for SLO
|
55
55
|
def after_sign_out_path_for(_)
|
56
|
+
idp_entity_id = get_idp_entity_id(params)
|
56
57
|
request = OneLogin::RubySaml::Logoutrequest.new
|
57
|
-
request.create(saml_config)
|
58
|
+
request.create(saml_config(idp_entity_id))
|
58
59
|
end
|
59
60
|
|
60
61
|
def generate_idp_logout_response(saml_config, logout_request_id)
|
@@ -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) }
|
@@ -94,7 +94,7 @@ module Devise
|
|
94
94
|
end
|
95
95
|
|
96
96
|
# Proc that is called if Devise.saml_update_user and/or Devise.saml_create_user are true.
|
97
|
-
#
|
97
|
+
# Receives the user object, saml_response and auth_value, and defines how the object's values are
|
98
98
|
# updated with regards to the SAML response. See saml_default_update_resource_hook for an example.
|
99
99
|
mattr_accessor :saml_update_resource_hook
|
100
100
|
@@saml_update_resource_hook = @@saml_default_update_resource_hook
|
@@ -107,7 +107,7 @@ module Devise
|
|
107
107
|
end
|
108
108
|
|
109
109
|
# Proc that is called to resolve the saml_response and auth_value into the correct user object.
|
110
|
-
#
|
110
|
+
# Receives a copy of the ActiveRecord::Model, saml_response and auth_value. Is expected to return
|
111
111
|
# one instance of the provided model that is the matched account, or nil if none exists.
|
112
112
|
# See saml_default_resource_locator above for an example.
|
113
113
|
mattr_accessor :saml_resource_locator
|
@@ -4,11 +4,13 @@ module DeviseSamlAuthenticatable
|
|
4
4
|
if params[:SAMLRequest]
|
5
5
|
OneLogin::RubySaml::SloLogoutrequest.new(
|
6
6
|
params[:SAMLRequest],
|
7
|
+
settings: Devise.saml_config,
|
7
8
|
allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
|
8
9
|
).issuer
|
9
10
|
elsif params[:SAMLResponse]
|
10
11
|
OneLogin::RubySaml::Response.new(
|
11
12
|
params[:SAMLResponse],
|
13
|
+
settings: Devise.saml_config,
|
12
14
|
allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
|
13
15
|
).issuers.first
|
14
16
|
end
|
@@ -66,6 +66,7 @@ describe Devise::SamlSessionsController, type: :controller do
|
|
66
66
|
it "uses the DefaultIdpEntityIdReader" do
|
67
67
|
expect(DeviseSamlAuthenticatable::DefaultIdpEntityIdReader).to receive(:entity_id)
|
68
68
|
do_get
|
69
|
+
expect(idp_providers_adapter).to have_received(:settings).with(nil)
|
69
70
|
end
|
70
71
|
|
71
72
|
context "with a relay_state lambda defined" do
|
@@ -104,6 +105,7 @@ describe Devise::SamlSessionsController, type: :controller do
|
|
104
105
|
|
105
106
|
it "redirects to the associated IdP SSO target url" do
|
106
107
|
do_get
|
108
|
+
expect(idp_providers_adapter).to have_received(:settings).with("http://www.example.com")
|
107
109
|
expect(response).to redirect_to(%r(\Ahttp://idp_sso_url\?SAMLRequest=))
|
108
110
|
end
|
109
111
|
end
|
@@ -147,10 +149,57 @@ describe Devise::SamlSessionsController, type: :controller do
|
|
147
149
|
end
|
148
150
|
|
149
151
|
describe '#destroy' do
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
152
|
+
context "when using the default saml config" do
|
153
|
+
it 'signs out and redirects to the IdP' do
|
154
|
+
expect(controller).to receive(:sign_out)
|
155
|
+
delete :destroy
|
156
|
+
expect(response).to redirect_to(%r(\Ahttp://localhost:8009/saml/logout\?SAMLRequest=))
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "with a specified idp" do
|
161
|
+
before do
|
162
|
+
Devise.idp_settings_adapter = idp_providers_adapter
|
163
|
+
end
|
164
|
+
|
165
|
+
it "redirects to the associated IdP SSO target url" do
|
166
|
+
expect(controller).to receive(:sign_out)
|
167
|
+
expect(DeviseSamlAuthenticatable::DefaultIdpEntityIdReader).to receive(:entity_id)
|
168
|
+
delete :destroy
|
169
|
+
expect(response).to redirect_to(%r(\Ahttp://idp_slo_url\?SAMLRequest=))
|
170
|
+
end
|
171
|
+
|
172
|
+
context "with a specified idp entity id reader" do
|
173
|
+
class OurIdpEntityIdReader
|
174
|
+
def self.entity_id(params)
|
175
|
+
params[:entity_id]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
subject(:do_delete) {
|
180
|
+
if Rails::VERSION::MAJOR > 4
|
181
|
+
delete :destroy, params: {entity_id: "http://www.example.com"}
|
182
|
+
else
|
183
|
+
delete :destroy, entity_id: "http://www.example.com"
|
184
|
+
end
|
185
|
+
}
|
186
|
+
|
187
|
+
before do
|
188
|
+
@default_reader = Devise.idp_entity_id_reader
|
189
|
+
Devise.idp_entity_id_reader = OurIdpEntityIdReader # which will have some different behavior
|
190
|
+
end
|
191
|
+
|
192
|
+
after do
|
193
|
+
Devise.idp_entity_id_reader = @default_reader
|
194
|
+
end
|
195
|
+
|
196
|
+
it "redirects to the associated IdP SLO target url" do
|
197
|
+
expect(controller).to receive(:sign_out)
|
198
|
+
do_delete
|
199
|
+
expect(idp_providers_adapter).to have_received(:settings).with("http://www.example.com")
|
200
|
+
expect(response).to redirect_to(%r(\Ahttp://idp_slo_url\?SAMLRequest=))
|
201
|
+
end
|
202
|
+
end
|
154
203
|
end
|
155
204
|
end
|
156
205
|
|
@@ -138,7 +138,6 @@ describe "SAML Authentication", type: :feature do
|
|
138
138
|
end
|
139
139
|
|
140
140
|
context "when the idp_settings_adapter key is set" do
|
141
|
-
|
142
141
|
before(:each) do
|
143
142
|
create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "false")
|
144
143
|
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true", 'IDP_SETTINGS_ADAPTER' => "IdpSettingsAdapter", 'IDP_ENTITY_ID_READER' => "OurEntityIdReader")
|
@@ -156,7 +155,24 @@ describe "SAML Authentication", type: :feature do
|
|
156
155
|
create_user("you@example.com")
|
157
156
|
|
158
157
|
visit 'http://localhost:8020/users/saml/sign_in/?entity_id=http%3A%2F%2Flocalhost%3A8020%2Fsaml%2Fmetadata'
|
159
|
-
expect(current_url).to match(%r(\Ahttp://www.example.com
|
158
|
+
expect(current_url).to match(%r(\Ahttp://www.example.com/sso\?SAMLRequest=))
|
159
|
+
end
|
160
|
+
|
161
|
+
it "logs a user out of the IdP via the SP" do
|
162
|
+
sign_in
|
163
|
+
|
164
|
+
# prove user is still signed in
|
165
|
+
visit 'http://localhost:8020/'
|
166
|
+
expect(page).to have_content("you@example.com")
|
167
|
+
expect(current_url).to eq("http://localhost:8020/")
|
168
|
+
|
169
|
+
click_on "Log out"
|
170
|
+
#confirm the logout response redirected to the SP which in turn attempted to sign th e
|
171
|
+
expect(current_url).to match(%r(\Ahttp://www.example.com/slo\?SAMLRequest=))
|
172
|
+
|
173
|
+
# prove user is now signed out
|
174
|
+
visit 'http://localhost:8020/users/saml/sign_in/?entity_id=http%3A%2F%2Flocalhost%3A8020%2Fsaml%2Fmetadata'
|
175
|
+
expect(current_url).to match(%r(\Ahttp://www.example.com/sso\?SAMLRequest=))
|
160
176
|
end
|
161
177
|
end
|
162
178
|
|
data/spec/support/Gemfile.rails4
CHANGED
@@ -4,7 +4,6 @@ source 'https://rubygems.org'
|
|
4
4
|
gemspec path: '../..'
|
5
5
|
|
6
6
|
group :test do
|
7
|
-
gem 'rake'
|
8
7
|
gem 'rspec', '~> 3.0'
|
9
8
|
gem 'rails', '~> 4.0'
|
10
9
|
gem 'rspec-rails'
|
@@ -16,7 +15,15 @@ group :test do
|
|
16
15
|
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
|
17
16
|
gem 'addressable', '~> 2.4.0'
|
18
17
|
gem 'mime-types', '~> 2.99'
|
18
|
+
gem 'public_suffix', '~> 1.4.6'
|
19
|
+
gem 'rake', '~> 12.2.0'
|
20
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
21
|
+
gem 'public_suffix', '~> 2.0.5'
|
22
|
+
gem 'rake'
|
23
|
+
else
|
24
|
+
gem 'rake'
|
19
25
|
end
|
26
|
+
|
20
27
|
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
21
28
|
gem 'devise', '~> 3.5'
|
22
29
|
gem 'nokogiri', '~> 1.6.8'
|
@@ -14,11 +14,9 @@ group :test do
|
|
14
14
|
gem 'poltergeist'
|
15
15
|
|
16
16
|
# Lock down versions of gems for older versions of Ruby
|
17
|
-
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0")
|
18
|
-
gem 'mime-types', '~> 2.99'
|
19
|
-
end
|
20
17
|
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
21
18
|
gem 'devise', '~> 3.5'
|
19
|
+
gem 'public_suffix', '~> 2.0.5'
|
22
20
|
gem 'nokogiri', '~> 1.6.8'
|
23
21
|
end
|
24
22
|
end
|
@@ -8,8 +8,8 @@ class IdpSettingsAdapter
|
|
8
8
|
issuer: "sp_issuer",
|
9
9
|
idp_entity_id: "http://localhost:8020/saml/metadata",
|
10
10
|
authn_context: "",
|
11
|
-
idp_slo_target_url: "http://www.example.com",
|
12
|
-
idp_sso_target_url: "http://www.example.com",
|
11
|
+
idp_slo_target_url: "http://www.example.com/slo",
|
12
|
+
idp_sso_target_url: "http://www.example.com/sso",
|
13
13
|
idp_cert: "idp_cert"
|
14
14
|
}
|
15
15
|
else
|
data/spec/support/sp_template.rb
CHANGED
@@ -68,7 +68,7 @@ after_bundle do
|
|
68
68
|
insert_into_file('app/views/home/index.html.erb', after: /\z/) {
|
69
69
|
<<-HOME
|
70
70
|
<%= current_user.email %> <%= current_user.name %>
|
71
|
-
<%= form_tag destroy_user_session_path, method: :delete do %>
|
71
|
+
<%= form_tag destroy_user_session_path(entity_id: "http://localhost:8020/saml/metadata"), method: :delete do %>
|
72
72
|
<%= submit_tag "Log out" %>
|
73
73
|
<% end %>
|
74
74
|
HOME
|
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.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josef Sauter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: devise
|
@@ -85,7 +85,8 @@ files:
|
|
85
85
|
- spec/support/saml_idp_controller.rb.erb
|
86
86
|
- spec/support/sp_template.rb
|
87
87
|
homepage: ''
|
88
|
-
licenses:
|
88
|
+
licenses:
|
89
|
+
- MIT
|
89
90
|
metadata: {}
|
90
91
|
post_install_message:
|
91
92
|
rdoc_options: []
|