devise_saml_authenticatable 1.4.1 → 1.5.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 +4 -4
- data/.travis.yml +15 -5
- data/README.md +43 -18
- data/lib/devise_saml_authenticatable.rb +12 -0
- data/lib/devise_saml_authenticatable/model.rb +6 -2
- data/lib/devise_saml_authenticatable/routes.rb +17 -6
- data/lib/devise_saml_authenticatable/saml_mapped_attributes.rb +15 -2
- data/lib/devise_saml_authenticatable/version.rb +1 -1
- data/spec/devise_saml_authenticatable/model_spec.rb +42 -5
- data/spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb +50 -0
- data/spec/features/saml_authentication_spec.rb +3 -2
- data/spec/rails_helper.rb +5 -1
- data/spec/routes/routes_spec.rb +102 -0
- data/spec/support/Gemfile.rails5.1 +14 -0
- data/spec/support/attribute-map.yml +12 -0
- data/spec/support/idp_template.rb +3 -1
- data/spec/support/rails_app.rb +1 -1
- data/spec/support/sp_template.rb +3 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 388799cd1bf9c14ad21b7a042d63957d965ec080
|
4
|
+
data.tar.gz: b5e153ee444879be849e6ddda43e2762a3905092
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c304efa473b057a46895b96db3d6d076ebdaa332de7c7d6a3480b86a3ae3e33d17123b5f565d229c5412a9496cc9c549e34960aa119f8a1c18180eea87eb05e
|
7
|
+
data.tar.gz: 40a2dd033fd20cccc2803dc587309be272005a5d84c8e80a0325b038079fcd2deb9b6aad78f8afb30c2462f9952efe1e49ffa100b9187480c5234a6565aaf566
|
data/.travis.yml
CHANGED
@@ -3,12 +3,14 @@ rvm:
|
|
3
3
|
- "1.9.3"
|
4
4
|
- "2.0.0"
|
5
5
|
- "2.1.10"
|
6
|
-
- "2.2.
|
7
|
-
- "2.3.
|
8
|
-
- "2.4.
|
9
|
-
- "2.5.
|
6
|
+
- "2.2.10"
|
7
|
+
- "2.3.8"
|
8
|
+
- "2.4.5"
|
9
|
+
- "2.5.3"
|
10
|
+
- "2.6.0"
|
10
11
|
gemfile:
|
11
12
|
- Gemfile
|
13
|
+
- spec/support/Gemfile.rails5.1
|
12
14
|
- spec/support/Gemfile.rails5
|
13
15
|
- spec/support/Gemfile.rails4
|
14
16
|
matrix:
|
@@ -17,18 +19,26 @@ matrix:
|
|
17
19
|
gemfile: Gemfile
|
18
20
|
- rvm: "1.9.3"
|
19
21
|
gemfile: spec/support/Gemfile.rails5
|
22
|
+
- rvm: "1.9.3"
|
23
|
+
gemfile: spec/support/Gemfile.rails5.1
|
20
24
|
- rvm: "2.0.0"
|
21
25
|
gemfile: Gemfile
|
22
26
|
- rvm: "2.0.0"
|
23
27
|
gemfile: spec/support/Gemfile.rails5
|
28
|
+
- rvm: "2.0.0"
|
29
|
+
gemfile: spec/support/Gemfile.rails5.1
|
24
30
|
- rvm: "2.1.10"
|
25
31
|
gemfile: Gemfile
|
26
32
|
- rvm: "2.1.10"
|
27
33
|
gemfile: spec/support/Gemfile.rails5
|
34
|
+
- rvm: "2.1.10"
|
35
|
+
gemfile: spec/support/Gemfile.rails5.1
|
36
|
+
- rvm: "2.6.0"
|
37
|
+
gemfile: spec/support/Gemfile.rails4
|
28
38
|
|
29
39
|
before_install:
|
30
40
|
# update bundler to avoid https://github.com/travis-ci/travis-ci/issues/5239
|
31
|
-
- gem install bundler
|
41
|
+
- command -v bundle || gem install bundler -v '~> 1.17.3'
|
32
42
|
|
33
43
|
script:
|
34
44
|
- bundle exec rake
|
data/README.md
CHANGED
@@ -20,6 +20,10 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
+
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.
|
24
|
+
|
25
|
+
### Configuring Models
|
26
|
+
|
23
27
|
In `app/models/<YOUR_MODEL>.rb` set the `:saml_authenticatable` strategy.
|
24
28
|
|
25
29
|
In the example the model is `user.rb`:
|
@@ -32,6 +36,37 @@ In the example the model is `user.rb`:
|
|
32
36
|
end
|
33
37
|
```
|
34
38
|
|
39
|
+
### Configuring routes
|
40
|
+
|
41
|
+
In `config/routes.rb` add `devise_for` to set up helper methods and routes:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
devise_for :users
|
45
|
+
```
|
46
|
+
|
47
|
+
The named routes can be customized in the initializer config file.
|
48
|
+
|
49
|
+
### Configuring the IdP
|
50
|
+
|
51
|
+
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.
|
52
|
+
|
53
|
+
- Creating a new session: `/users/saml/auth`
|
54
|
+
- 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.
|
55
|
+
- Metadata: `/users/saml/metadata`
|
56
|
+
- IdPs may call this the "audience."
|
57
|
+
- Single Logout: `/users/saml/idp_sign_out`
|
58
|
+
- 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.
|
59
|
+
|
60
|
+
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:
|
61
|
+
|
62
|
+
- Issuer (`idp_entity_id`)
|
63
|
+
- SSO endpoint (`idp_sso_target_url`)
|
64
|
+
- SLO endpoint (`idp_slo_target_url`)
|
65
|
+
- Certificate fingerprint (`idp_cert_fingerprint`) and algorithm (`idp_cert_fingerprint_algorithm`)
|
66
|
+
- Or the certificate itself (`idp_cert`)
|
67
|
+
|
68
|
+
### Configuring handling of IdP requests and responses
|
69
|
+
|
35
70
|
In `config/initializers/devise.rb`:
|
36
71
|
|
37
72
|
```ruby
|
@@ -69,6 +104,12 @@ In `config/initializers/devise.rb`:
|
|
69
104
|
# and implements a #handle method. This method can then redirect the user, return error messages, etc.
|
70
105
|
# config.saml_failed_callback = nil
|
71
106
|
|
107
|
+
# You can customize the named routes generated in case of named route collisions with
|
108
|
+
# other Devise modules or libraries. Set the saml_route_helper_prefix to a string that will
|
109
|
+
# be appended to the named route.
|
110
|
+
# If saml_route_helper_prefix = 'saml' then the new_user_session route becomes new_saml_user_session
|
111
|
+
# config.saml_route_helper_prefix = 'saml'
|
112
|
+
|
72
113
|
# Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/onelogin/ruby-saml).
|
73
114
|
config.saml_configure do |settings|
|
74
115
|
# 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,24 +120,8 @@ In `config/initializers/devise.rb`:
|
|
79
120
|
settings.authn_context = ""
|
80
121
|
settings.idp_slo_target_url = "http://localhost/simplesaml/www/saml2/idp/SingleLogoutService.php"
|
81
122
|
settings.idp_sso_target_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
|
82
|
-
settings.
|
83
|
-
|
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
|
123
|
+
settings.idp_cert_fingerprint = "00:A1:2B:3C:44:55:6F:A7:88:CC:DD:EE:22:33:44:55:D6:77:8F:99"
|
124
|
+
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
|
100
125
|
end
|
101
126
|
end
|
102
127
|
```
|
@@ -19,6 +19,10 @@ end
|
|
19
19
|
|
20
20
|
# Get saml information from config/saml.yml now
|
21
21
|
module Devise
|
22
|
+
# Allow route customization to avoid collision
|
23
|
+
mattr_accessor :saml_route_helper_prefix
|
24
|
+
@@saml_route_helper_prefix
|
25
|
+
|
22
26
|
# Allow logging
|
23
27
|
mattr_accessor :saml_logger
|
24
28
|
@@saml_logger = true
|
@@ -64,9 +68,17 @@ module Devise
|
|
64
68
|
|
65
69
|
# Implements a #validate method that takes the retrieved resource and response right after retrieval,
|
66
70
|
# and returns true if it's valid. False will cause authentication to fail.
|
71
|
+
# Only one of saml_resource_validator and saml_resource_validator_hook may be used.
|
67
72
|
mattr_accessor :saml_resource_validator
|
68
73
|
@@saml_resource_validator
|
69
74
|
|
75
|
+
# Proc that determines whether a technically correct SAML response is valid per some custom logic.
|
76
|
+
# Receives the user object (or nil, if no match was found), decorated saml_response and
|
77
|
+
# auth_value, inspects the combination for acceptability of login (or create+login, if enabled),
|
78
|
+
# and returns true if it's valid. False will cause authentication to fail.
|
79
|
+
mattr_accessor :saml_resource_validator_hook
|
80
|
+
@@saml_resource_validator_hook
|
81
|
+
|
70
82
|
# Custom value for ruby-saml allowed_clock_drift
|
71
83
|
mattr_accessor :allowed_clock_drift_in_seconds
|
72
84
|
@@allowed_clock_drift_in_seconds
|
@@ -44,8 +44,12 @@ module Devise
|
|
44
44
|
|
45
45
|
resource = Devise.saml_resource_locator.call(self, decorated_response, auth_value)
|
46
46
|
|
47
|
-
if Devise.saml_resource_validator
|
48
|
-
|
47
|
+
raise "Only one validator configuration can be used at a time" if Devise.saml_resource_validator && Devise.saml_resource_validator_hook
|
48
|
+
if Devise.saml_resource_validator || Devise.saml_resource_validator_hook
|
49
|
+
valid = if Devise.saml_resource_validator then Devise.saml_resource_validator.new.validate(resource, saml_response)
|
50
|
+
else Devise.saml_resource_validator_hook.call(resource, decorated_response, auth_value)
|
51
|
+
end
|
52
|
+
if !valid
|
49
53
|
logger.info("User(#{auth_value}) did not pass custom validation.")
|
50
54
|
return nil
|
51
55
|
end
|
@@ -1,12 +1,23 @@
|
|
1
1
|
ActionDispatch::Routing::Mapper.class_eval do
|
2
2
|
protected
|
3
3
|
def devise_saml_authenticatable(mapping, controllers)
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
if ::Devise.saml_route_helper_prefix
|
5
|
+
prefix = ::Devise.saml_route_helper_prefix
|
6
|
+
resource :session, only: [], controller: controllers[:saml_sessions], path: '' do
|
7
|
+
get :new, path: 'saml/sign_in', as: "new_#{prefix}"
|
8
|
+
post :create, path: 'saml/auth', as: prefix
|
9
|
+
match :destroy, path: mapping.path_names[:sign_out], as: "destroy_#{prefix}", via: mapping.sign_out_via
|
10
|
+
get :metadata, path: 'saml/metadata'
|
11
|
+
match :idp_sign_out, path: 'saml/idp_sign_out', as: "idp_destroy_#{prefix}", via: [:get, :post]
|
12
|
+
end
|
13
|
+
else
|
14
|
+
resource :session, only: [], controller: controllers[:saml_sessions], path: '' do
|
15
|
+
get :new, path: 'saml/sign_in', as: 'new'
|
16
|
+
post :create, path: 'saml/auth'
|
17
|
+
match :destroy, path: mapping.path_names[:sign_out], as: 'destroy', via: mapping.sign_out_via
|
18
|
+
get :metadata, path: 'saml/metadata'
|
19
|
+
match :idp_sign_out, path: 'saml/idp_sign_out', via: [:get, :post]
|
20
|
+
end
|
10
21
|
end
|
11
22
|
end
|
12
23
|
end
|
@@ -3,7 +3,6 @@ module SamlAuthenticatable
|
|
3
3
|
def initialize(attributes, attribute_map)
|
4
4
|
@attributes = attributes
|
5
5
|
@attribute_map = attribute_map
|
6
|
-
@inverted_attribute_map = @attribute_map.invert
|
7
6
|
end
|
8
7
|
|
9
8
|
def saml_attribute_keys
|
@@ -15,7 +14,21 @@ module SamlAuthenticatable
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def value_by_resource_key(key)
|
18
|
-
|
17
|
+
str_key = String(key)
|
18
|
+
|
19
|
+
# Find all of the SAML attributes that map to the resource key
|
20
|
+
attribute_map_for_key = @attribute_map.select { |_, resource_key| String(resource_key) == str_key }
|
21
|
+
|
22
|
+
saml_value = nil
|
23
|
+
|
24
|
+
# Find the first non-nil value
|
25
|
+
attribute_map_for_key.each_key do |saml_key|
|
26
|
+
saml_value = value_by_saml_attribute_key(saml_key)
|
27
|
+
|
28
|
+
break unless saml_value.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
saml_value
|
19
32
|
end
|
20
33
|
|
21
34
|
def value_by_saml_attribute_key(key)
|
@@ -39,13 +39,15 @@ describe Devise::Models::SamlAuthenticatable do
|
|
39
39
|
|
40
40
|
before do
|
41
41
|
allow(Rails).to receive(:root).and_return("/railsroot")
|
42
|
-
allow(File).to receive(:read).with("/railsroot/config/attribute-map.yml").and_return(
|
42
|
+
allow(File).to receive(:read).with("/railsroot/config/attribute-map.yml").and_return(attributemap)
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:attributemap) {<<-ATTRIBUTEMAP
|
43
46
|
---
|
44
47
|
"saml-email-format": email
|
45
48
|
"saml-name-format": name
|
46
49
|
ATTRIBUTEMAP
|
47
|
-
|
48
|
-
|
50
|
+
}
|
49
51
|
let(:response) { double(:response, attributes: attributes, name_id: name_id) }
|
50
52
|
let(:attributes) {
|
51
53
|
OneLogin::RubySaml::Attributes.new(
|
@@ -179,8 +181,8 @@ describe Devise::Models::SamlAuthenticatable do
|
|
179
181
|
end
|
180
182
|
end
|
181
183
|
|
182
|
-
context "when configured with a resource validator" do
|
183
|
-
let(:validator_class) { double("
|
184
|
+
context "when configured with a resource validator class" do
|
185
|
+
let(:validator_class) { double("validator") }
|
184
186
|
let(:validator) { double("validator") }
|
185
187
|
let(:user) { Model.new(new_record: false) }
|
186
188
|
|
@@ -212,6 +214,41 @@ describe Devise::Models::SamlAuthenticatable do
|
|
212
214
|
end
|
213
215
|
end
|
214
216
|
|
217
|
+
|
218
|
+
context "when configured with a resource validator hook" do
|
219
|
+
let(:validator_hook) { double("validator_hook") }
|
220
|
+
let(:decorated_response) { ::SamlAuthenticatable::SamlResponse.new(response, YAML.load(attributemap)) }
|
221
|
+
let(:user) { Model.new(new_record: false) }
|
222
|
+
|
223
|
+
before do
|
224
|
+
allow(Devise).to receive(:saml_resource_validator_hook).and_return(validator_hook)
|
225
|
+
allow(::SamlAuthenticatable::SamlResponse).to receive(:new).with(response, YAML.load(attributemap)).and_return(decorated_response)
|
226
|
+
end
|
227
|
+
|
228
|
+
context "and sent a valid value" do
|
229
|
+
before do
|
230
|
+
expect(validator_hook).to receive(:call).with(user, decorated_response, 'user@example.com').and_return(true)
|
231
|
+
end
|
232
|
+
|
233
|
+
it "returns the user" do
|
234
|
+
expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
|
235
|
+
expect(Model.authenticate_with_saml(response, nil)).to eq(user)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
context "and sent an invalid value" do
|
240
|
+
before do
|
241
|
+
expect(validator_hook).to receive(:call).with(user, decorated_response, 'user@example.com').and_return(false)
|
242
|
+
end
|
243
|
+
|
244
|
+
it "returns nil" do
|
245
|
+
expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
|
246
|
+
expect(Model.authenticate_with_saml(response, nil)).to be_nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
|
215
252
|
context "when configured to use a custom update hook" do
|
216
253
|
it "can replicate the default behaviour in a custom hook" do
|
217
254
|
configure_hook do |user, saml_response|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'devise_saml_authenticatable/saml_mapped_attributes'
|
3
|
+
|
4
|
+
describe SamlAuthenticatable::SamlMappedAttributes do
|
5
|
+
let(:instance) { described_class.new(saml_attributes, attribute_map) }
|
6
|
+
let(:attribute_map_file) { File.join(File.dirname(__FILE__), '../support/attribute-map.yml') }
|
7
|
+
let(:attribute_map) { YAML.load(File.read(attribute_map_file)) }
|
8
|
+
let(:saml_attributes) do
|
9
|
+
{
|
10
|
+
"first_name" => ["John"],
|
11
|
+
"last_name"=>["Smith"],
|
12
|
+
"email"=>["john.smith@example.com"]
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#value_by_resource_key" do
|
17
|
+
RSpec.shared_examples "correctly maps the value of the resource key" do |saml_key, resource_key, expected_value|
|
18
|
+
subject(:perform) { instance.value_by_resource_key(resource_key) }
|
19
|
+
|
20
|
+
it "correctly maps the resource key, #{resource_key}, to the value of the '#{saml_key}' SAML key" do
|
21
|
+
saml_attributes[saml_key] = saml_attributes.delete(resource_key)
|
22
|
+
expect(perform).to eq(expected_value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "first_name" do
|
27
|
+
saml_keys = ['urn:mace:dir:attribute-def:first_name', 'first_name', 'firstName', 'firstname']
|
28
|
+
|
29
|
+
saml_keys.each do |saml_key|
|
30
|
+
include_examples 'correctly maps the value of the resource key', saml_key, 'first_name', ['John']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'last_name' do
|
35
|
+
saml_keys = ['urn:mace:dir:attribute-def:last_name', 'last_name', 'lastName', 'lastname']
|
36
|
+
|
37
|
+
saml_keys.each do |saml_key|
|
38
|
+
include_examples 'correctly maps the value of the resource key', saml_key, 'last_name', ['Smith']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'email' do
|
43
|
+
saml_keys = ['urn:mace:dir:attribute-def:email', 'email_address', 'emailAddress', 'email']
|
44
|
+
|
45
|
+
saml_keys.each do |saml_key|
|
46
|
+
include_examples 'correctly maps the value of the resource key', saml_key, 'email', ['john.smith@example.com']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -5,6 +5,7 @@ require 'uri'
|
|
5
5
|
require 'capybara/rspec'
|
6
6
|
require 'capybara/poltergeist'
|
7
7
|
Capybara.default_driver = :poltergeist
|
8
|
+
Capybara.server = :webrick
|
8
9
|
|
9
10
|
describe "SAML Authentication", type: :feature do
|
10
11
|
let(:idp_port) { 8009 }
|
@@ -203,7 +204,7 @@ describe "SAML Authentication", type: :feature do
|
|
203
204
|
fill_in "Email", with: "you@example.com"
|
204
205
|
fill_in "Password", with: "asdf"
|
205
206
|
click_on "Sign in"
|
206
|
-
expect(page).to have_content("Example Domain This domain is established to be used for illustrative examples in documents. You may use this domain in examples without prior coordination or asking for permission.")
|
207
|
+
expect(page).to have_content(:all, "Example Domain This domain is established to be used for illustrative examples in documents. You may use this domain in examples without prior coordination or asking for permission.")
|
207
208
|
expect(current_url).to eq("http://www.example.com/")
|
208
209
|
end
|
209
210
|
end
|
@@ -220,7 +221,7 @@ describe "SAML Authentication", type: :feature do
|
|
220
221
|
fill_in "Email", with: "you@example.com"
|
221
222
|
fill_in "Password", with: "asdf"
|
222
223
|
click_on "Sign in"
|
223
|
-
Timeout.timeout(Capybara.
|
224
|
+
Timeout.timeout(Capybara.default_max_wait_time) do
|
224
225
|
loop do
|
225
226
|
sleep 0.1
|
226
227
|
break if current_url == "http://localhost:8020/"
|
data/spec/rails_helper.rb
CHANGED
@@ -8,7 +8,11 @@ require 'rspec/rails'
|
|
8
8
|
|
9
9
|
ActiveRecord::Migration.verbose = false
|
10
10
|
ActiveRecord::Base.logger = Logger.new(nil)
|
11
|
-
ActiveRecord::
|
11
|
+
if ActiveRecord::Base.connection.respond_to?(:migration_context)
|
12
|
+
ActiveRecord::Base.connection.migration_context.migrate
|
13
|
+
else
|
14
|
+
ActiveRecord::Migrator.migrate(File.expand_path("../support/sp/db/migrate/", __FILE__))
|
15
|
+
end
|
12
16
|
|
13
17
|
RSpec.configure do |config|
|
14
18
|
config.use_transactional_fixtures = true
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
describe 'SamlAuthenticatable Routes', type: :routing do
|
4
|
+
describe 'GET /users/saml/sign_in (login)' do
|
5
|
+
it 'routes to Devise::SamlSessionsController#new' do
|
6
|
+
expect(get: '/users/saml/sign_in').to route_to(controller: 'devise/saml_sessions', action: 'new')
|
7
|
+
expect(get: new_user_session_path).to route_to(controller: 'devise/saml_sessions', action: 'new')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'POST /users/saml/auth (session creation)' do
|
12
|
+
it 'routes to Devise::SamlSessionsController#create' do
|
13
|
+
expect(post: '/users/saml/auth').to route_to(controller: 'devise/saml_sessions', action: 'create')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'DELETE /users/sign_out (logout)' do
|
18
|
+
it 'routes to Devise::SamlSessionsController#destroy' do
|
19
|
+
expect(delete: '/users/sign_out').to route_to(controller: 'devise/saml_sessions', action: 'destroy')
|
20
|
+
expect(delete: destroy_user_session_path).to route_to(controller: 'devise/saml_sessions', action: 'destroy')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'GET /users/saml/metadata' do
|
25
|
+
it 'routes to Devise::SamlSessionsController#metadata' do
|
26
|
+
expect(get: '/users/saml/metadata').to route_to(controller: 'devise/saml_sessions', action: 'metadata')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'GET /users/saml/idp_sign_out (IdP-initiated logout)' do
|
31
|
+
it 'routes to Devise::SamlSessionsController#idp_sign_out' do
|
32
|
+
expect(get: '/users/saml/idp_sign_out').to route_to(controller: 'devise/saml_sessions', action: 'idp_sign_out')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'POST /users/saml/idp_sign_out (IdP-initiated logout)' do
|
37
|
+
it 'routes to Devise::SamlSessionsController#idp_sign_out' do
|
38
|
+
expect(post: '/users/saml/idp_sign_out').to route_to(controller: 'devise/saml_sessions', action: 'idp_sign_out')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when saml_route_helper_prefix is "sso"' do
|
43
|
+
before(:all) do
|
44
|
+
::Devise.saml_route_helper_prefix = 'sso'
|
45
|
+
|
46
|
+
# A very simple Rails engine
|
47
|
+
module SamlRouteHelperPrefixEngine
|
48
|
+
class Engine < ::Rails::Engine
|
49
|
+
isolate_namespace SamlRouteHelperPrefixEngine
|
50
|
+
end
|
51
|
+
|
52
|
+
Engine.routes.draw do
|
53
|
+
devise_for :users, module: :devise
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
after(:all) do
|
58
|
+
::Devise.saml_route_helper_prefix = nil
|
59
|
+
end
|
60
|
+
routes { SamlRouteHelperPrefixEngine::Engine.routes }
|
61
|
+
|
62
|
+
describe 'GET /users/saml/sign_in (login)' do
|
63
|
+
it 'routes to Devise::SamlSessionsController#new' do
|
64
|
+
expect(get: '/users/saml/sign_in').to route_to(controller: 'devise/saml_sessions', action: 'new')
|
65
|
+
expect(get: new_sso_user_session_path).to route_to(controller: 'devise/saml_sessions', action: 'new')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'POST /users/saml/auth (session creation)' do
|
70
|
+
it 'routes to Devise::SamlSessionsController#create' do
|
71
|
+
expect(post: '/users/saml/auth').to route_to(controller: 'devise/saml_sessions', action: 'create')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe 'DELETE /users/sign_out (logout)' do
|
76
|
+
it 'routes to Devise::SamlSessionsController#destroy' do
|
77
|
+
expect(delete: '/users/sign_out').to route_to(controller: 'devise/saml_sessions', action: 'destroy')
|
78
|
+
expect(delete: destroy_sso_user_session_path).to route_to(controller: 'devise/saml_sessions', action: 'destroy')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'GET /users/saml/metadata' do
|
83
|
+
it 'routes to Devise::SamlSessionsController#metadata' do
|
84
|
+
expect(get: '/users/saml/metadata').to route_to(controller: 'devise/saml_sessions', action: 'metadata')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe 'GET /users/saml/idp_sign_out (IdP-initiated logout)' do
|
89
|
+
it 'routes to Devise::SamlSessionsController#idp_sign_out' do
|
90
|
+
expect(get: '/users/saml/idp_sign_out').to route_to(controller: 'devise/saml_sessions', action: 'idp_sign_out')
|
91
|
+
expect(get: idp_destroy_sso_user_session_path).to route_to(controller: 'devise/saml_sessions', action: 'idp_sign_out')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe 'POST /users/saml/idp_sign_out (IdP-initiated logout)' do
|
96
|
+
it 'routes to Devise::SamlSessionsController#idp_sign_out' do
|
97
|
+
expect(post: '/users/saml/idp_sign_out').to route_to(controller: 'devise/saml_sessions', action: 'idp_sign_out')
|
98
|
+
expect(post: idp_destroy_sso_user_session_path).to route_to(controller: 'devise/saml_sessions', action: 'idp_sign_out')
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -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', '~> 5.1.0'
|
10
|
+
gem 'rspec-rails'
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'capybara'
|
13
|
+
gem 'poltergeist'
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
"urn:mace:dir:attribute-def:first_name": "first_name"
|
2
|
+
"first_name": "first_name"
|
3
|
+
"firstName": "first_name"
|
4
|
+
"firstname": "first_name"
|
5
|
+
"urn:mace:dir:attribute-def:last_name": "last_name"
|
6
|
+
"last_name": "last_name"
|
7
|
+
"lastName": "last_name"
|
8
|
+
"lastname": "last_name"
|
9
|
+
"urn:mace:dir:attribute-def:email": "email"
|
10
|
+
"email_address": "email"
|
11
|
+
"emailAddress": "email"
|
12
|
+
"email": "email"
|
@@ -3,7 +3,9 @@
|
|
3
3
|
@include_subject_in_attributes = ENV.fetch('INCLUDE_SUBJECT_IN_ATTRIBUTES')
|
4
4
|
@valid_destination = ENV.fetch('VALID_DESTINATION', "true")
|
5
5
|
|
6
|
-
|
6
|
+
if Rails::VERSION::MAJOR < 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR < 2)
|
7
|
+
gsub_file 'config/secrets.yml', /secret_key_base:.*$/, 'secret_key_base: "34814fd41f91c493b89aa01ac73c44d241a31245b5bc5542fa4b7317525e1dcfa60ba947b3d085e4e229456fdee0d8af6aac6a63cf750d807ea6fe5d853dff4a"'
|
8
|
+
end
|
7
9
|
|
8
10
|
gem 'ruby-saml-idp', git: "https://github.com/lawrencepit/ruby-saml-idp.git", ref: "ec715b252e849105c7a96df27b731c6e7f725a51"
|
9
11
|
gem 'thin'
|
data/spec/support/rails_app.rb
CHANGED
@@ -17,7 +17,7 @@ rescue Errno::ESRCH
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def create_app(name, env = {})
|
20
|
-
rails_new_options = %w(-T -J -S --skip-spring --skip-listen)
|
20
|
+
rails_new_options = %w(-T -J -S --skip-spring --skip-listen --skip-bootsnap)
|
21
21
|
rails_new_options << "-O" if name == 'idp'
|
22
22
|
Dir.chdir(File.expand_path('../../support', __FILE__)) do
|
23
23
|
FileUtils.rm_rf(name)
|
data/spec/support/sp_template.rb
CHANGED
@@ -8,7 +8,9 @@ idp_settings_adapter = ENV.fetch('IDP_SETTINGS_ADAPTER', "nil")
|
|
8
8
|
idp_entity_id_reader = ENV.fetch('IDP_ENTITY_ID_READER', "DeviseSamlAuthenticatable::DefaultIdpEntityIdReader")
|
9
9
|
saml_failed_callback = ENV.fetch('SAML_FAILED_CALLBACK', "nil")
|
10
10
|
|
11
|
-
|
11
|
+
if Rails::VERSION::MAJOR < 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR < 2)
|
12
|
+
gsub_file 'config/secrets.yml', /secret_key_base:.*$/, 'secret_key_base: "8b5889df1fcf03f76c7d66da02d8776bcc85b06bed7d9c592f076d9c8a5455ee6d4beae45986c3c030b40208db5e612f2a6ef8283036a352e3fae83c5eda36be"'
|
13
|
+
end
|
12
14
|
|
13
15
|
gem 'devise_saml_authenticatable', path: '../../..'
|
14
16
|
gem 'ruby-saml', OneLogin::RubySaml::VERSION
|
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.5.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: 2019-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: devise
|
@@ -70,12 +70,16 @@ files:
|
|
70
70
|
- spec/devise_saml_authenticatable/default_idp_entity_id_reader_spec.rb
|
71
71
|
- spec/devise_saml_authenticatable/model_spec.rb
|
72
72
|
- spec/devise_saml_authenticatable/saml_config_spec.rb
|
73
|
+
- spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb
|
73
74
|
- spec/devise_saml_authenticatable/strategy_spec.rb
|
74
75
|
- spec/features/saml_authentication_spec.rb
|
75
76
|
- spec/rails_helper.rb
|
77
|
+
- spec/routes/routes_spec.rb
|
76
78
|
- spec/spec_helper.rb
|
77
79
|
- spec/support/Gemfile.rails4
|
78
80
|
- spec/support/Gemfile.rails5
|
81
|
+
- spec/support/Gemfile.rails5.1
|
82
|
+
- spec/support/attribute-map.yml
|
79
83
|
- spec/support/idp_settings_adapter.rb.erb
|
80
84
|
- spec/support/idp_template.rb
|
81
85
|
- spec/support/rails_app.rb
|
@@ -112,12 +116,16 @@ test_files:
|
|
112
116
|
- spec/devise_saml_authenticatable/default_idp_entity_id_reader_spec.rb
|
113
117
|
- spec/devise_saml_authenticatable/model_spec.rb
|
114
118
|
- spec/devise_saml_authenticatable/saml_config_spec.rb
|
119
|
+
- spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb
|
115
120
|
- spec/devise_saml_authenticatable/strategy_spec.rb
|
116
121
|
- spec/features/saml_authentication_spec.rb
|
117
122
|
- spec/rails_helper.rb
|
123
|
+
- spec/routes/routes_spec.rb
|
118
124
|
- spec/spec_helper.rb
|
119
125
|
- spec/support/Gemfile.rails4
|
120
126
|
- spec/support/Gemfile.rails5
|
127
|
+
- spec/support/Gemfile.rails5.1
|
128
|
+
- spec/support/attribute-map.yml
|
121
129
|
- spec/support/idp_settings_adapter.rb.erb
|
122
130
|
- spec/support/idp_template.rb
|
123
131
|
- spec/support/rails_app.rb
|