devise_saml_authenticatable 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 69aceb045d8de7ed6acd66fd027fdabfe8da4f89
4
+ data.tar.gz: def4131da232ae9818379b35750b686a77e61d78
5
+ SHA512:
6
+ metadata.gz: 5a8c183ecbe7dcda723e6fa459056de365390de80baf3c587719ebdf40df07741e89de14355c5b0c623755371ee0473c6be729b1dd5d044d00da7474d87a4b12
7
+ data.tar.gz: 5b3b63caf790992b2263e6ccceec1570df0af4e207c42e8d89c5eeb1726740195dd28ae4e90e2823dbf1f6d47b880fa71de338ec75f35fd550012791a3df8406
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .idea/
5
6
  .yardoc
6
7
  Gemfile.lock
7
8
  InstalledFiles
@@ -12,6 +13,6 @@ lib/bundler/man
12
13
  pkg
13
14
  rdoc
14
15
  spec/reports
15
- test/tmp
16
- test/version_tmp
16
+ spec/support/idp/
17
+ spec/support/sp/
17
18
  tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ - "2.1.0"
6
+ - "2.2.0"
7
+
8
+ script:
9
+ - xvfb-run bundle exec rake
10
+
11
+ notifications:
12
+ hipchat:
13
+ rooms:
14
+ secure: eWahLFbtvZaJgulhDdnmbIxtSnl2lGR4zHqcJ6d7ExG0HzfxzCknHkau4YfqhANRa3+e66F4rCNoU3+db1EKE5jij7+oIOgRHAmVwLi9WRswzb7NQfWV0/PyUjdF6T7Ac/sfQRkFIDTUL2npYMr2zTQnjDcYrT7e0IkRxNA3VXWogzhqnC4sXKLr8j9zLLw5BdFfELcJD021Rpd7O1EmAt+hahxaJCipyXV2l/kDANR24jnlXgaQn8HJwgRXsBARAQe1QgApnrmbqnGnWc4T1GbI7kExsKGCUbHxYBOp+99m25T6MK66xnvRltwKaUeMXuUyWRZtmAV3LlbACRMX3n5qkDqg8P2OPNvWWua84hkRmUNUska7YrSLX7aCWvGfA6Vr1INnsueLl5sYjHhHZnaS8Wl0iC9ltxBUY4ikg1QlHSghXFQvMC5UAROJ2x7BHGV3kDRTc52ZLdy5BDuJrf/OFEhpuTp+HQALmEKkcJKFUdBn8Cu20wF0pF8o5XvYnSozuqytJKP+b/3+U0X4SEYqbkYCi/PcSnXTtSTIl0+7uxmEBCwPKpXRkkCfVsLqIVUkyVj1d1qIxG8Zbikn5NxK6USV5+Zl4KTDFUVD4r4T8fXCV09+8q0t/uyVjCDv3g1vGRcHvxQyDiLrs7q4DL3GNQY3trrEQ6WneA8nsLw=
15
+ template:
16
+ - '%{repository}<a href="%{build_url}">#%{build_number}</a> (%{branch} - <a href="%{compare_url}">%{commit}</a> : %{author}): %{message}'
17
+ format: html
18
+ on_pull_requests: false
data/Gemfile CHANGED
@@ -2,3 +2,13 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in devise_saml_authenticatable.gemspec
4
4
  gemspec
5
+
6
+ group :test do
7
+ gem 'rake'
8
+ gem 'rspec', '~> 3.0'
9
+ gem 'rails'
10
+ gem 'rspec-rails'
11
+ gem 'sqlite3'
12
+ gem 'capybara-webkit'
13
+ end
14
+
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
+ [![Build Status](https://travis-ci.org/apokalipto/devise_saml_authenticatable.svg?branch=master)](https://travis-ci.org/apokalipto/devise_saml_authenticatable)
1
2
  # DeviseSamlAuthenticatable
2
3
 
3
- Devise Saml Authenticatable is a Single-Sign-On authentication strategy for devise that relies on SAML. It uses [ruby-saml](https://github.com/onelogin/ruby-saml) to handle all SAML related stuff.
4
+ Devise Saml Authenticatable is a Single-Sign-On authentication strategy for devise that relies on SAML.
5
+ It uses [ruby-saml][] to handle all SAML related stuff.
4
6
 
5
7
  ## Installation
6
8
 
@@ -18,7 +20,8 @@ Or install it yourself as:
18
20
 
19
21
  ## Usage
20
22
 
21
- In app/models/<YOUR_MODEL>.rb set the :saml_authenticatable strategy. In the example the model is user.rb
23
+ In `app/models/<YOUR_MODEL>.rb` set the `:saml_authenticatable` strategy.
24
+ In the example the model is `user.rb`:
22
25
 
23
26
  ```ruby
24
27
  class User < ActiveRecord::Base
@@ -33,51 +36,52 @@ In config/initializers/devise.rb
33
36
  ```ruby
34
37
  Devise.setup do |config|
35
38
  ...
36
- # ==> DeviseSamlAuthenticatable Configuration
39
+ # ==> Configuration for :saml_authenticatable
37
40
 
38
41
  # Create user if the user does not exist. (Default is false)
39
42
  config.saml_create_user = true
40
43
 
41
- # Set the default user key (default is email). The user will be looked up by this key. Make sure that the Authentication Response includes
42
- # the attribute
44
+ # Set the default user key. The user will be looked up by this key. Make
45
+ # sure that the Authentication Response includes the attribute.
43
46
  config.saml_default_user_key = :email
47
+
48
+ # You can set this value to use Subject or SAML assertation as info to which email will be compared
49
+ # If you don't set it then email will be extracted from SAML assertation attributes
50
+ config.saml_use_subject = true
51
+
52
+ # Configure with your SAML settings (see [ruby-saml][] for more information).
53
+ config.saml_configure do |settings|
54
+ settings.assertion_consumer_service_url = "http://localhost:3000/users/saml/auth"
55
+ settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
56
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
57
+ settings.issuer = "http://localhost:3000"
58
+ settings.authn_context = ""
59
+ settings.idp_sso_target_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
60
+ settings.idp_cert = <<-CERT.chomp
61
+ -----BEGIN CERTIFICATE-----
62
+ 1111111111111111111111111111111111111111111111111111111111111111
63
+ 1111111111111111111111111111111111111111111111111111111111111111
64
+ 1111111111111111111111111111111111111111111111111111111111111111
65
+ 1111111111111111111111111111111111111111111111111111111111111111
66
+ 1111111111111111111111111111111111111111111111111111111111111111
67
+ 1111111111111_______IDP_CERTIFICATE________111111111111111111111
68
+ 1111111111111111111111111111111111111111111111111111111111111111
69
+ 1111111111111111111111111111111111111111111111111111111111111111
70
+ 1111111111111111111111111111111111111111111111111111111111111111
71
+ 1111111111111111111111111111111111111111111111111111111111111111
72
+ 1111111111111111111111111111111111111111111111111111111111111111
73
+ 1111111111111111111111111111111111111111111111111111111111111111
74
+ 1111111111111111111111111111111111111111111111111111111111111111
75
+ 111111111111111111
76
+ -----END CERTIFICATE-----
77
+ CERT
78
+ end
44
79
  end
45
80
  ```
46
81
 
47
- In config directory, create a YAML file (idp.yml) with your SAML settings (see ruby-saml for more information). For example
48
-
49
- ```ruby
50
- # idp.yaml
51
- development:
52
- idp_metadata: ""
53
- idp_metadata_ttl: ""
54
- assertion_consumer_service_url: "http://localhost:3000/users/sign_in"
55
- assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
56
- name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
57
- issuer: "http://localhost:3000"
58
- authn_context: ""
59
- idp_sso_target_url: "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
60
- idp_cert: |-
61
- -----BEGIN CERTIFICATE-----
62
- 1111111111111111111111111111111111111111111111111111111111111111
63
- 1111111111111111111111111111111111111111111111111111111111111111
64
- 1111111111111111111111111111111111111111111111111111111111111111
65
- 1111111111111111111111111111111111111111111111111111111111111111
66
- 1111111111111111111111111111111111111111111111111111111111111111
67
- 1111111111111_______IDP_CERTIFICATE________111111111111111111111
68
- 1111111111111111111111111111111111111111111111111111111111111111
69
- 1111111111111111111111111111111111111111111111111111111111111111
70
- 1111111111111111111111111111111111111111111111111111111111111111
71
- 1111111111111111111111111111111111111111111111111111111111111111
72
- 1111111111111111111111111111111111111111111111111111111111111111
73
- 1111111111111111111111111111111111111111111111111111111111111111
74
- 1111111111111111111111111111111111111111111111111111111111111111
75
- 111111111111111111
76
- -----END CERTIFICATE-----
77
- ```
78
- In config directory create a YAML file (attribute-map.yml) that maps SAML attributes with your model's fields
82
+ In config directory create a YAML file (`attribute-map.yml`) that maps SAML attributes with your model's fields:
79
83
 
80
- ```ruby
84
+ ```yaml
81
85
  # attribute-map.yml
82
86
 
83
87
  "urn:mace:dir:attribute-def:uid": "user_name"
@@ -86,18 +90,19 @@ In config directory create a YAML file (attribute-map.yml) that maps SAML attrib
86
90
  "urn:mace:dir:attribute-def:givenName": "name"
87
91
  ```
88
92
 
89
- The attribute mappings are very dependent on the way the IdP encodes the attributes. In this example the attributes are given in URN style. Other IdPs might provide them as OID's or other means.
93
+ The attribute mappings are very dependent on the way the IdP encodes the attributes.
94
+ In this example the attributes are given in URN style.
95
+ Other IdPs might provide them as OID's or other means.
90
96
 
91
- You are now ready to test it against an IdP. When the user goes to /users/saml/sign_in he will be redirected to the login page of the IdP. Upon successful login the user is redirected to devise user_root_path.
97
+ You are now ready to test it against an IdP.
98
+ When the user goes to `/users/saml/sign_in` he will be redirected to the login page of the IdP.
99
+ Upon successful login the user is redirected to devise `user_root_path`.
92
100
 
93
101
  ## Identity Provider
94
102
 
95
103
  If you don't have an identity provider an you would like to test the authentication against your app there are some options:
96
104
 
97
- 1. Use [ruby-saml-idp](https://github.com/lawrencepit/ruby-saml-idp).
98
-
99
- You can add your own logic to your IdP, or you can also set it as a dummy IdP that always sends a valid authentication response to your app.
100
-
105
+ 1. Use [ruby-saml-idp](https://github.com/lawrencepit/ruby-saml-idp). You can add your own logic to your IdP, or you can also set it as a dummy IdP that always sends a valid authentication response to your app.
101
106
  2. Use an online service that can act as an IdP. Onelogin, Salesforce and some others provide you with this functionality
102
107
  3. Install your own IdP.
103
108
 
@@ -107,13 +112,21 @@ There are numerous IdPs that support SAML 2.0, there are propietary (like Micros
107
112
 
108
113
  ## Limitations
109
114
 
110
- 1. At the moment there is no support for Single Logout (we're working on that)
115
+ 1. At the moment there is no support for Single Logout
111
116
  2. The Authentication Requests (from your app to the IdP) are not signed and encrypted
112
117
 
118
+ ## Thanks
119
+
120
+ 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.
121
+
122
+ Thanks to all other contributors that have also helped us make this software better.
113
123
  ## Contributing
114
124
 
115
125
  1. Fork it
116
126
  2. Create your feature branch (`git checkout -b my-new-feature`)
117
127
  3. Commit your changes (`git commit -am 'Added some feature'`)
118
- 4. Push to the branch (`git push origin my-new-feature`)
119
- 5. Create new Pull Request
128
+ 4. Run the tests (`bundle exec rspec`)
129
+ 5. Push to the branch (`git push origin my-new-feature`)
130
+ 6. Create new Pull Request
131
+
132
+ [ruby-saml]: https://github.com/onelogin/ruby-saml
data/Rakefile CHANGED
@@ -1,2 +1,8 @@
1
1
  #!/usr/bin/env rake
2
2
  require "bundler/gem_tasks"
3
+
4
+ require "rspec/core/rake_task"
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ desc "Default: run tests"
8
+ task :default => :spec
@@ -4,6 +4,8 @@ class Devise::SamlSessionsController < Devise::SessionsController
4
4
  include DeviseSamlAuthenticatable::SamlConfig
5
5
  unloadable if Rails::VERSION::MAJOR < 4
6
6
  before_filter :get_saml_config
7
+ skip_before_filter :verify_authenticity_token
8
+
7
9
  def new
8
10
  request = OneLogin::RubySaml::Authrequest.new
9
11
  action = request.create(@saml_config)
@@ -14,7 +14,8 @@ Gem::Specification.new do |gem|
14
14
  gem.name = "devise_saml_authenticatable"
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = DeviseSamlAuthenticatable::VERSION
17
-
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+
18
19
  gem.add_dependency("devise","> 2.0.0")
19
20
  gem.add_dependency("ruby-saml",">= 0.8.2")
20
21
  end
@@ -20,16 +20,22 @@ module Devise
20
20
  # Allow logging
21
21
  mattr_accessor :saml_logger
22
22
  @@saml_logger = true
23
-
23
+
24
24
  # Add valid users to database
25
25
  mattr_accessor :saml_create_user
26
26
  @@saml_create_user = false
27
-
28
- mattr_accessor :saml_config
29
- @@saml_config = "#{Rails.root}/config/saml.yml"
30
-
27
+
31
28
  mattr_accessor :saml_default_user_key
32
29
  @@saml_default_user_key
30
+
31
+ mattr_accessor :saml_use_subject
32
+ @@saml_use_subject
33
+
34
+ mattr_accessor :saml_config
35
+ @@saml_config = OneLogin::RubySaml::Settings.new
36
+ def self.saml_configure
37
+ yield saml_config
38
+ end
33
39
  end
34
40
 
35
41
  # Add saml_authenticatable strategy to defaults.
@@ -4,7 +4,7 @@ module Devise
4
4
  module Models
5
5
  module SamlAuthenticatable
6
6
  extend ActiveSupport::Concern
7
-
7
+
8
8
  # Need to determine why these need to be included
9
9
  included do
10
10
  attr_reader :password, :current_password
@@ -26,37 +26,45 @@ module Devise
26
26
 
27
27
  module ClassMethods
28
28
  include DeviseSamlAuthenticatable::SamlConfig
29
- def authenticate_with_saml(attributes)
29
+ def authenticate_with_saml(saml_response)
30
30
  key = Devise.saml_default_user_key
31
- inv_attr = attribute_map.invert
32
- auth_value = attributes[inv_attr[key.to_s]]
33
- auth_value.try(:downcase!) if Devise.case_insensitive_keys.include?(key)
31
+ attributes = saml_response.attributes
32
+ if (Devise.saml_use_subject)
33
+ auth_value = saml_response.name_id
34
+ else
35
+ inv_attr = attribute_map.invert
36
+ auth_value = attributes[inv_attr[key.to_s]]
37
+ auth_value.try(:downcase!) if Devise.case_insensitive_keys.include?(key)
38
+ end
34
39
  resource = where(key => auth_value).first
35
40
  if (resource.nil? && !Devise.saml_create_user)
36
- logger.info("User(#{attributes[inv_attr[key.to_s]]}) not found. Not configured to create the user.")
41
+ logger.info("User(#{auth_value}) not found. Not configured to create the user.")
37
42
  return nil
38
43
  end
39
44
 
40
- if (resource.nil? && Devise.saml_create_user)
41
- logger.info("Creating user(#{attributes[inv_attr[key.to_s]]}).")
42
- resource = new
45
+ if (resource.nil? && Devise.saml_create_user)
46
+ logger.info("Creating user(#{auth_value}).")
47
+ resource = new
43
48
  set_user_saml_attributes(resource,attributes)
49
+ if (Devise.saml_use_subject)
50
+ resource.send "#{key}=", auth_value
51
+ end
44
52
  resource.save!
45
53
  end
46
54
 
47
55
  resource
48
- end
56
+ end
49
57
 
50
58
  def find_for_shibb_authentication(conditions)
51
59
  find_for_authentication(conditions)
52
60
  end
53
-
61
+
54
62
  def attribute_map
55
63
  @attribute_map ||= YAML.load(File.read("#{Rails.root}/config/attribute-map.yml"))
56
64
  end
57
65
 
58
66
  private
59
-
67
+
60
68
  def set_user_saml_attributes(user,attributes)
61
69
  attribute_map.each do |k,v|
62
70
  Rails.logger.info "Setting: #{v}, #{attributes[k]}"
@@ -2,7 +2,13 @@ require 'ruby-saml'
2
2
  module DeviseSamlAuthenticatable
3
3
  module SamlConfig
4
4
  def get_saml_config
5
- @saml_config = OneLogin::RubySaml::Settings.new(YAML.load(File.read("#{Rails.root}/config/idp.yml"))[Rails.env])
5
+ idp_config_path = "#{Rails.root}/config/idp.yml"
6
+ # Support 0.0.x-style configuration via a YAML file
7
+ if File.exists?(idp_config_path)
8
+ Devise.saml_config = OneLogin::RubySaml::Settings.new(YAML.load(File.read(idp_config_path))[Rails.env])
9
+ end
10
+
11
+ @saml_config = Devise.saml_config
6
12
  end
7
13
  end
8
14
  end
@@ -8,22 +8,22 @@ module Devise
8
8
  end
9
9
  def authenticate!
10
10
  @response = OneLogin::RubySaml::Response.new(params[:SAMLResponse])
11
- @response.settings = get_saml_config
12
- resource = mapping.to.authenticate_with_saml(@response.attributes)
11
+ @response.settings = get_saml_config
12
+ resource = mapping.to.authenticate_with_saml(@response)
13
13
  if @response.is_valid?
14
14
  success!(resource)
15
15
  else
16
16
  fail!(:invalid)
17
17
  end
18
18
  end
19
-
19
+
20
20
  # This method should turn off storage whenever CSRF cannot be verified.
21
21
  # Any known way on how to let the IdP send the CSRF token along with the SAMLResponse ?
22
22
  # Please let me know!
23
23
  def store?
24
24
  true
25
25
  end
26
-
26
+
27
27
  end
28
28
  end
29
29
  end
@@ -1,3 +1,3 @@
1
1
  module DeviseSamlAuthenticatable
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,30 @@
1
+ require 'rails_helper'
2
+
3
+ class Devise::SessionsController < ActionController::Base
4
+
5
+ end
6
+
7
+ require_relative '../../../app/controllers/devise/saml_sessions_controller'
8
+
9
+
10
+ describe Devise::SamlSessionsController, type: :controller do
11
+ let(:saml_config) { Devise.saml_config }
12
+
13
+ describe '#new' do
14
+ it 'redirects to the SAML Auth Request endpoint' do
15
+ get :new
16
+ expect(response).to redirect_to(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
17
+ end
18
+ end
19
+
20
+ describe '#metadata' do
21
+ it "generates metadata" do
22
+ get :metadata
23
+
24
+ # Remove ID that can vary across requests
25
+ expected_metadata = OneLogin::RubySaml::Metadata.new.generate(saml_config)
26
+ metadata_pattern = Regexp.escape(expected_metadata).gsub(/ ID='[^']+'/, " ID='[\\w-]+'")
27
+ expect(response.body).to match(Regexp.new(metadata_pattern))
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe Devise::Models::SamlAuthenticatable do
4
+ class Model
5
+ include Devise::Models::SamlAuthenticatable
6
+ attr_accessor :email, :name, :saved
7
+ def save!
8
+ self.saved = true
9
+ end
10
+
11
+ # Fake out ActiveRecord and Devise API to satisfy verifiable mocks
12
+ class << self
13
+ def where(*args); end
14
+ def logger; end
15
+ end
16
+ end
17
+
18
+ before do
19
+ logger = double(:logger).as_null_object
20
+ allow(Model).to receive(:logger).and_return(logger)
21
+ allow(Rails).to receive(:logger).and_return(logger)
22
+ end
23
+
24
+ before do
25
+ allow(Devise).to receive(:saml_default_user_key).and_return(:email)
26
+ allow(Devise).to receive(:saml_create_user).and_return(false)
27
+ allow(Devise).to receive(:saml_use_subject).and_return(false)
28
+ end
29
+
30
+ before do
31
+ allow(Rails).to receive(:root).and_return("/railsroot")
32
+ allow(File).to receive(:read).with("/railsroot/config/attribute-map.yml").and_return(<<-ATTRIBUTEMAP)
33
+ ---
34
+ "saml-email-format": email
35
+ "saml-name-format": name
36
+ ATTRIBUTEMAP
37
+ end
38
+
39
+ let(:response) { double(:response, attributes: attributes, name_id: name_id) }
40
+ let(:attributes) {
41
+ OneLogin::RubySaml::Attributes.new(
42
+ 'saml-email-format' => ['user@example.com'],
43
+ 'saml-name-format' => ['A User'],
44
+ )
45
+ }
46
+ let(:name_id) { nil }
47
+
48
+ it "looks up the user by the configured default user key" do
49
+ user = double(:user)
50
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
51
+ expect(Model.authenticate_with_saml(response)).to eq(user)
52
+ end
53
+
54
+ it "returns nil if it cannot find a user" do
55
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
56
+ expect(Model.authenticate_with_saml(response)).to be_nil
57
+ end
58
+
59
+ context "when configured to use the subject" do
60
+ let(:attributes) { OneLogin::RubySaml::Attributes.new('saml-name-format' => ['A User']) }
61
+ let(:name_id) { 'user@example.com' }
62
+
63
+ before do
64
+ allow(Devise).to receive(:saml_use_subject).and_return(true)
65
+ end
66
+
67
+ it "looks up the user by the configured default user key" do
68
+ user = double(:user)
69
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
70
+ expect(Model.authenticate_with_saml(response)).to eq(user)
71
+ end
72
+
73
+ it "returns nil if it cannot find a user" do
74
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
75
+ expect(Model.authenticate_with_saml(response)).to be_nil
76
+ end
77
+
78
+ context "when configured to create a user and the user is not found" do
79
+ before do
80
+ allow(Devise).to receive(:saml_create_user).and_return(true)
81
+ end
82
+
83
+ it "creates and returns a new user with the name identifier and given attributes" do
84
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
85
+ model = Model.authenticate_with_saml(response)
86
+ expect(model.email).to eq('user@example.com')
87
+ expect(model.name).to eq('A User')
88
+ expect(model.saved).to be(true)
89
+ end
90
+ end
91
+ end
92
+
93
+ context "when configured to create a user and the user is not found" do
94
+ before do
95
+ allow(Devise).to receive(:saml_create_user).and_return(true)
96
+ end
97
+
98
+ it "creates and returns a new user with the given attributes" do
99
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([])
100
+ model = Model.authenticate_with_saml(response)
101
+ expect(model.email).to eq('user@example.com')
102
+ expect(model.name).to eq('A User')
103
+ expect(model.saved).to be(true)
104
+ end
105
+ end
106
+
107
+ context "when configured with a case-insensitive key" do
108
+ before do
109
+ allow(Devise).to receive(:case_insensitive_keys).and_return([:email])
110
+ end
111
+
112
+ it "looks up the user with a downcased value" do
113
+ user = double(:user)
114
+ expect(Model).to receive(:where).with(email: 'user@example.com').and_return([user])
115
+ expect(Model.authenticate_with_saml(response)).to eq(user)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+
3
+ describe DeviseSamlAuthenticatable::SamlConfig do
4
+ subject(:saml_config) { controller.get_saml_config }
5
+ let(:controller) { Class.new { include DeviseSamlAuthenticatable::SamlConfig }.new }
6
+
7
+ # Replace global config since this test changes it
8
+ before do
9
+ @original_saml_config = Devise.saml_config
10
+ end
11
+ after do
12
+ Devise.saml_config = @original_saml_config
13
+ end
14
+
15
+ context "when config/idp.yml does not exist" do
16
+ before do
17
+ allow(Rails).to receive(:root).and_return("/railsroot")
18
+ allow(File).to receive(:exists?).with("/railsroot/config/idp.yml").and_return(false)
19
+ end
20
+
21
+ it "is the global devise SAML config" do
22
+ Devise.saml_configure do |settings|
23
+ settings.assertion_consumer_logout_service_binding = 'test'
24
+ end
25
+ expect(saml_config).to be(Devise.saml_config)
26
+ expect(saml_config.assertion_consumer_logout_service_binding).to eq('test')
27
+ end
28
+ end
29
+
30
+ context "when config/idp.yml exists" do
31
+ before do
32
+ allow(Rails).to receive(:env).and_return("environment")
33
+ allow(Rails).to receive(:root).and_return("/railsroot")
34
+ allow(File).to receive(:exists?).with("/railsroot/config/idp.yml").and_return(true)
35
+ allow(File).to receive(:read).with("/railsroot/config/idp.yml").and_return(<<-IDP)
36
+ ---
37
+ environment:
38
+ assertion_consumer_logout_service_binding: assertion_consumer_logout_service_binding
39
+ assertion_consumer_logout_service_url: assertion_consumer_logout_service_url
40
+ assertion_consumer_service_binding: assertion_consumer_service_binding
41
+ assertion_consumer_service_url: assertion_consumer_service_url
42
+ attributes_index: attributes_index
43
+ authn_context: authn_context
44
+ authn_context_comparison: authn_context_comparison
45
+ authn_context_decl_ref: authn_context_decl_ref
46
+ certificate: certificate
47
+ compress_request: compress_request
48
+ compress_response: compress_response
49
+ double_quote_xml_attribute_values: double_quote_xml_attribute_values
50
+ force_authn: force_authn
51
+ idp_cert: idp_cert
52
+ idp_cert_fingerprint: idp_cert_fingerprint
53
+ idp_cert_fingerprint_algorithm: idp_cert_fingerprint_algorithm
54
+ idp_entity_id: idp_entity_id
55
+ idp_slo_target_url: idp_slo_target_url
56
+ idp_sso_target_url: idp_sso_target_url
57
+ issuer: issuer
58
+ name_identifier_format: name_identifier_format
59
+ name_identifier_value: name_identifier_value
60
+ passive: passive
61
+ private_key: private_key
62
+ protocol_binding: protocol_binding
63
+ security: security
64
+ sessionindex: sessionindex
65
+ sp_name_qualifier: sp_name_qualifier
66
+ IDP
67
+ end
68
+
69
+ it "stores the configured IdP settings" do
70
+ expect(saml_config.assertion_consumer_logout_service_binding).to eq('assertion_consumer_logout_service_binding')
71
+ expect(saml_config.assertion_consumer_logout_service_url).to eq('assertion_consumer_logout_service_url')
72
+ expect(saml_config.assertion_consumer_service_binding).to eq('assertion_consumer_service_binding')
73
+ expect(saml_config.assertion_consumer_service_url).to eq('assertion_consumer_service_url')
74
+ expect(saml_config.attributes_index).to eq('attributes_index')
75
+ expect(saml_config.authn_context).to eq('authn_context')
76
+ expect(saml_config.authn_context_comparison).to eq('authn_context_comparison')
77
+ expect(saml_config.authn_context_decl_ref).to eq('authn_context_decl_ref')
78
+ expect(saml_config.certificate).to eq('certificate')
79
+ expect(saml_config.compress_request).to eq('compress_request')
80
+ expect(saml_config.compress_response).to eq('compress_response')
81
+ expect(saml_config.double_quote_xml_attribute_values).to eq('double_quote_xml_attribute_values')
82
+ expect(saml_config.force_authn).to eq('force_authn')
83
+ expect(saml_config.idp_cert).to eq('idp_cert')
84
+ expect(saml_config.idp_cert_fingerprint).to eq('idp_cert_fingerprint')
85
+ expect(saml_config.idp_cert_fingerprint_algorithm).to eq('idp_cert_fingerprint_algorithm')
86
+ expect(saml_config.idp_entity_id).to eq('idp_entity_id')
87
+ expect(saml_config.idp_slo_target_url).to eq('idp_slo_target_url')
88
+ expect(saml_config.idp_sso_target_url).to eq('idp_sso_target_url')
89
+ expect(saml_config.issuer).to eq('issuer')
90
+ expect(saml_config.name_identifier_format).to eq('name_identifier_format')
91
+ expect(saml_config.name_identifier_value).to eq('name_identifier_value')
92
+ expect(saml_config.passive).to eq('passive')
93
+ expect(saml_config.private_key).to eq('private_key')
94
+ expect(saml_config.protocol_binding).to eq('protocol_binding')
95
+ expect(saml_config.security).to eq('security')
96
+ expect(saml_config.sessionindex).to eq('sessionindex')
97
+ expect(saml_config.sp_name_qualifier).to eq('sp_name_qualifier')
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe Devise::Strategies::SamlAuthenticatable do
4
+ subject(:strategy) { described_class.new(env, :user) }
5
+ let(:env) { {} }
6
+
7
+ let(:response) { double(:response, :settings= => nil, is_valid?: true) }
8
+ before do
9
+ allow(OneLogin::RubySaml::Response).to receive(:new).and_return(response)
10
+ end
11
+
12
+ let(:saml_config) { OneLogin::RubySaml::Settings.new }
13
+ before do
14
+ allow(strategy).to receive(:get_saml_config).and_return(saml_config)
15
+ end
16
+
17
+ let(:mapping) { double(:mapping, to: user_class) }
18
+ let(:user_class) { double(:user_class, authenticate_with_saml: user) }
19
+ let(:user) { double(:user) }
20
+ before do
21
+ allow(strategy).to receive(:mapping).and_return(mapping)
22
+ end
23
+
24
+ let(:params) { {} }
25
+ before do
26
+ allow(strategy).to receive(:params).and_return(params)
27
+ end
28
+
29
+ context "with a SAMLResponse parameter" do
30
+ let(:params) { {SAMLResponse: ""} }
31
+
32
+ it "is valid" do
33
+ expect(strategy).to be_valid
34
+ end
35
+
36
+ it "authenticates with the response" do
37
+ expect(OneLogin::RubySaml::Response).to receive(:new).with(params[:SAMLResponse])
38
+ expect(response).to receive(:settings=).with(saml_config)
39
+ expect(user_class).to receive(:authenticate_with_saml).with(response)
40
+
41
+ expect(strategy).to receive(:success!).with(user)
42
+ strategy.authenticate!
43
+ end
44
+
45
+ context "and the SAML response is not valid" do
46
+ before do
47
+ allow(response).to receive(:is_valid?).and_return(false)
48
+ end
49
+
50
+ it "fails to authenticate" do
51
+ expect(strategy).to receive(:fail!).with(:invalid)
52
+ strategy.authenticate!
53
+ end
54
+ end
55
+ end
56
+
57
+ it "is not valid without a SAMLResponse parameter" do
58
+ expect(strategy).not_to be_valid
59
+ end
60
+
61
+ it "is permanent" do
62
+ expect(strategy).to be_store
63
+ end
64
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'capybara/rspec'
5
+ require 'capybara/webkit'
6
+ Capybara.default_driver = :webkit
7
+
8
+ describe "SAML Authentication", type: :feature do
9
+ let(:idp_port) { 8009 }
10
+ let(:sp_port) { 8020 }
11
+
12
+ shared_examples_for "it authenticates and creates users" do
13
+ it "authenticates an existing user on a SP via an IdP" do
14
+ create_user("you@example.com")
15
+
16
+ visit 'http://localhost:8020/'
17
+ expect(current_url).to match(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
18
+ fill_in "Email", with: "you@example.com"
19
+ fill_in "Password", with: "asdf"
20
+ click_on "Sign in"
21
+ expect(page).to have_content("you@example.com")
22
+ expect(current_url).to eq("http://localhost:8020/")
23
+ end
24
+
25
+ it "creates a user on the SP from the IdP attributes" do
26
+ visit 'http://localhost:8020/'
27
+ expect(current_url).to match(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
28
+ fill_in "Email", with: "you@example.com"
29
+ fill_in "Password", with: "asdf"
30
+ click_on "Sign in"
31
+ expect(page).to have_content("you@example.com")
32
+ expect(page).to have_content("A User")
33
+ expect(current_url).to eq("http://localhost:8020/")
34
+ end
35
+ end
36
+
37
+ context "when the attributes are used to authenticate" do
38
+ before(:each) do
39
+ create_app('idp', %w(y))
40
+ create_app('sp', %w(n))
41
+ @idp_pid = start_app('idp', idp_port)
42
+ @sp_pid = start_app('sp', sp_port)
43
+ end
44
+ after(:each) do
45
+ stop_app(@idp_pid)
46
+ stop_app(@sp_pid)
47
+ end
48
+
49
+ it_behaves_like "it authenticates and creates users"
50
+ end
51
+
52
+ context "when the subject is used to authenticate" do
53
+ before(:each) do
54
+ create_app('idp', %w(n))
55
+ create_app('sp', %w(y))
56
+ @idp_pid = start_app('idp', idp_port)
57
+ @sp_pid = start_app('sp', sp_port)
58
+ end
59
+ after(:each) do
60
+ stop_app(@idp_pid)
61
+ stop_app(@sp_pid)
62
+ end
63
+
64
+ it_behaves_like "it authenticates and creates users"
65
+ end
66
+
67
+ def create_user(email)
68
+ response = Net::HTTP.post_form(URI('http://localhost:8020/users'), email: email)
69
+ expect(response.code).to eq('201')
70
+ end
71
+ end
@@ -0,0 +1,15 @@
1
+ ENV["RAILS_ENV"] ||= 'test'
2
+
3
+ require 'spec_helper'
4
+
5
+ create_app('sp', %w(n))
6
+ require 'support/sp/config/environment'
7
+ require 'rspec/rails'
8
+
9
+ ActiveRecord::Migration.verbose = false
10
+ ActiveRecord::Base.logger = Logger.new(nil)
11
+ ActiveRecord::Migrator.migrate(File.expand_path("../support/sp/db/migrate/", __FILE__))
12
+
13
+ RSpec.configure do |config|
14
+ config.use_transactional_fixtures = true
15
+ end
@@ -0,0 +1,22 @@
1
+ RSpec.configure do |config|
2
+ config.run_all_when_everything_filtered = true
3
+ config.filter_run :focus
4
+ config.order = 'random'
5
+
6
+ config.expect_with :rspec do |expectations|
7
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
8
+ end
9
+
10
+ # rspec-mocks config goes here. You can use an alternate test double
11
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
12
+ config.mock_with :rspec do |mocks|
13
+ # Prevents you from mocking or stubbing a method that does not exist on
14
+ # a real object. This is generally recommended, and will default to
15
+ # `true` in RSpec 4.
16
+ mocks.verify_partial_doubles = true
17
+ end
18
+ end
19
+
20
+ require 'support/rails_app'
21
+
22
+ require 'devise_saml_authenticatable'
@@ -0,0 +1,10 @@
1
+ # Set up a SAML IdP
2
+
3
+ @include_subject_in_attributes = ask("Include the subject in the attributes?", limit: %w(y n)) == "y"
4
+
5
+ gem 'ruby-saml-idp'
6
+
7
+ route "get '/saml/auth' => 'saml_idp#new'"
8
+ route "post '/saml/auth' => 'saml_idp#create'"
9
+
10
+ template File.expand_path('../saml_idp_controller.rb.erb', __FILE__), 'app/controllers/saml_idp_controller.rb'
@@ -0,0 +1,57 @@
1
+ require 'open3'
2
+
3
+ def sh!(cmd)
4
+ unless system(cmd)
5
+ raise "[#{cmd}] failed with exit code #{$?.exitstatus}"
6
+ end
7
+ end
8
+
9
+ def app_ready?(pid, port)
10
+ Process.getpgid(pid) &&
11
+ system("lsof -i:#{port}", out: '/dev/null')
12
+ end
13
+
14
+ def create_app(name, answers = [])
15
+ rails_new_options = %w(-T -J -S --skip-spring)
16
+ rails_new_options << "-O" if name == 'idp'
17
+ Bundler.with_clean_env do
18
+ Dir.chdir(File.expand_path('../../support', __FILE__)) do
19
+ FileUtils.rm_rf(name)
20
+ Open3.popen3("rails", "new", name, *rails_new_options, "-m", "#{name}_template.rb") do |stdin, stdout, stderr, wait_thread|
21
+ while answers.any?
22
+ question = stdout.gets
23
+ answer = answers.shift
24
+ stdin.puts answer
25
+ $stdout.puts "#{question} #{answer}"
26
+ end
27
+ wait_thread.join
28
+
29
+ $stdout.puts stdout.read
30
+ $stderr.puts stderr.read
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def start_app(name, port, options = {})
37
+ pid = nil
38
+ Bundler.with_clean_env do
39
+ Dir.chdir(File.expand_path("../../support/#{name}", __FILE__)) do
40
+ pid = Process.spawn("bundle exec rails server -p #{port}")
41
+ sleep 1 until app_ready?(pid, port)
42
+ if app_ready?(pid, port)
43
+ puts "Launched #{name} on port #{port} (pid #{pid})..."
44
+ else
45
+ raise "#{name} failed to start"
46
+ end
47
+ end
48
+ end
49
+ pid
50
+ end
51
+
52
+ def stop_app(pid)
53
+ if pid
54
+ Process.kill(:INT, pid)
55
+ Process.wait(pid)
56
+ end
57
+ end
@@ -0,0 +1,54 @@
1
+ class SamlIdpController < SamlIdp::IdpController
2
+ def idp_authenticate(email, password)
3
+ true
4
+ end
5
+
6
+ def idp_make_saml_response(user)
7
+ attributes = {
8
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" => "A User",
9
+ }
10
+ if include_subject_in_attributes
11
+ attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"] = "you@example.com"
12
+ end
13
+ encode_SAMLResponse("you@example.com", attributes: attributes)
14
+ end
15
+
16
+ private
17
+
18
+ def encode_SAMLResponse(nameID, opts = {})
19
+ now = Time.now.utc
20
+ response_id, reference_id = UUID.generate, UUID.generate
21
+ audience_uri = opts[:audience_uri] || saml_acs_url[/^(.*?\/\/.*?\/)/, 1]
22
+ issuer_uri = opts[:issuer_uri] || (defined?(request) && request.url) || "http://example.com"
23
+
24
+ attributes = opts.fetch(:attributes, {})
25
+ if attributes.any?
26
+ attribute_items = attributes.map { |format, value|
27
+ %[<Attribute Name="#{format}"><AttributeValue>#{value}</AttributeValue></Attribute>]
28
+ }
29
+ attribute_statement = %[<AttributeStatement>#{attribute_items.join}</AttributeStatement>]
30
+ else
31
+ attribute_statement = ""
32
+ end
33
+
34
+ assertion = %[<Assertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion" ID="_#{reference_id}" IssueInstant="#{now.iso8601}" Version="2.0"><Issuer>#{issuer_uri}</Issuer><Subject><NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">#{nameID}</NameID><SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><SubjectConfirmationData InResponseTo="#{@saml_request_id}" NotOnOrAfter="#{(now+3*60).iso8601}" Recipient="#{@saml_acs_url}"></SubjectConfirmationData></SubjectConfirmation></Subject><Conditions NotBefore="#{(now-5).iso8601}" NotOnOrAfter="#{(now+60*60).iso8601}"><AudienceRestriction><Audience>#{audience_uri}</Audience></AudienceRestriction></Conditions>#{attribute_statement}<AuthnStatement AuthnInstant="#{now.iso8601}" SessionIndex="_#{reference_id}"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatement></Assertion>]
35
+
36
+ digest_value = Base64.encode64(algorithm.digest(assertion)).gsub(/\n/, '')
37
+
38
+ signed_info = %[<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:CanonicalizationMethod><ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-#{algorithm_name}"></ds:SignatureMethod><ds:Reference URI="#_#{reference_id}"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig##{algorithm_name}"></ds:DigestMethod><ds:DigestValue>#{digest_value}</ds:DigestValue></ds:Reference></ds:SignedInfo>]
39
+
40
+ signature_value = sign(signed_info).gsub(/\n/, '')
41
+
42
+ signature = %[<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">#{signed_info}<ds:SignatureValue>#{signature_value}</ds:SignatureValue><KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"><ds:X509Data><ds:X509Certificate>#{self.x509_certificate}</ds:X509Certificate></ds:X509Data></KeyInfo></ds:Signature>]
43
+
44
+ assertion_and_signature = assertion.sub(/Issuer\>\<Subject/, "Issuer>#{signature}<Subject")
45
+
46
+ xml = %[<samlp:Response ID="_#{response_id}" Version="2.0" IssueInstant="#{now.iso8601}" Destination="#{@saml_acs_url}" Consent="urn:oasis:names:tc:SAML:2.0:consent:unspecified" InResponseTo="#{@saml_request_id}" xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"><Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">#{issuer_uri}</Issuer><samlp:Status><samlp:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" /></samlp:Status>#{assertion_and_signature}</samlp:Response>]
47
+
48
+ Base64.encode64(xml)
49
+ end
50
+
51
+ def include_subject_in_attributes
52
+ <%= @include_subject_in_attributes %>
53
+ end
54
+ end
@@ -0,0 +1,57 @@
1
+ # Set up a SAML Service Provider
2
+
3
+ use_subject_to_authenticate = ask("Use subject to authenticate?", limit: %w(y n)) == "y"
4
+
5
+ gem 'devise_saml_authenticatable', path: '../../..'
6
+
7
+ create_file 'config/attribute-map.yml', <<-ATTRIBUTES
8
+ ---
9
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": email
10
+ "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": name
11
+ ATTRIBUTES
12
+
13
+ after_bundle do
14
+ generate :controller, 'home', 'index'
15
+ insert_into_file('app/controllers/home_controller.rb', after: "class HomeController < ApplicationController\n") {
16
+ <<-AUTHENTICATE
17
+ before_action :authenticate_user!
18
+ AUTHENTICATE
19
+ }
20
+ insert_into_file('app/views/home/index.html.erb', after: /\z/) {
21
+ "<%= current_user.email %> <%= current_user.name %>"
22
+ }
23
+ route "root to: 'home#index'"
24
+
25
+ # Configure for our SAML IdP
26
+ generate 'devise:install'
27
+ gsub_file 'config/initializers/devise.rb', /^end$/, <<-CONFIG
28
+ config.saml_default_user_key = :email
29
+
30
+ config.saml_use_subject = #{use_subject_to_authenticate}
31
+ config.saml_create_user = true
32
+
33
+ config.saml_configure do |settings|
34
+ settings.assertion_consumer_service_url = "http://localhost:8020/users/saml/auth"
35
+ settings.issuer = "http://localhost:8020/saml/metadata"
36
+ settings.idp_sso_target_url = "http://localhost:8009/saml/auth"
37
+ settings.idp_cert_fingerprint = "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
38
+ end
39
+ end
40
+ CONFIG
41
+
42
+ generate :devise, "user", "email:string", "name:string"
43
+ gsub_file 'app/models/user.rb', /database_authenticatable.*\n.*/, 'saml_authenticatable'
44
+ route "resources :users"
45
+ create_file('app/controllers/users_controller.rb', <<-USERS)
46
+ class UsersController < ApplicationController
47
+ skip_before_filter :verify_authenticity_token
48
+ def create
49
+ User.create!(email: params[:email])
50
+ render nothing: true, status: 201
51
+ end
52
+ end
53
+ USERS
54
+
55
+ rake "db:create"
56
+ rake "db:migrate"
57
+ end
metadata CHANGED
@@ -1,46 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise_saml_authenticatable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
- prerelease:
4
+ version: 0.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Josef Sauter
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2015-02-02 00:00:00.000000000 Z
11
+ date: 2015-06-13 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: devise
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>'
17
+ - - ">"
20
18
  - !ruby/object:Gem::Version
21
19
  version: 2.0.0
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>'
24
+ - - ">"
28
25
  - !ruby/object:Gem::Version
29
26
  version: 2.0.0
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: ruby-saml
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: 0.8.2
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: 0.8.2
46
41
  description: SAML Authentication for devise
@@ -50,7 +45,9 @@ executables: []
50
45
  extensions: []
51
46
  extra_rdoc_files: []
52
47
  files:
53
- - .gitignore
48
+ - ".gitignore"
49
+ - ".rspec"
50
+ - ".travis.yml"
54
51
  - Gemfile
55
52
  - LICENSE
56
53
  - README.md
@@ -66,29 +63,50 @@ files:
66
63
  - lib/devise_saml_authenticatable/strategy.rb
67
64
  - lib/devise_saml_authenticatable/version.rb
68
65
  - rails/init.rb
66
+ - spec/controllers/devise/saml_sessions_controller_spec.rb
67
+ - spec/devise_saml_authenticatable/model_spec.rb
68
+ - spec/devise_saml_authenticatable/saml_config_spec.rb
69
+ - spec/devise_saml_authenticatable/strategy_spec.rb
70
+ - spec/features/saml_authentication_spec.rb
71
+ - spec/rails_helper.rb
72
+ - spec/spec_helper.rb
73
+ - spec/support/idp_template.rb
74
+ - spec/support/rails_app.rb
75
+ - spec/support/saml_idp_controller.rb.erb
76
+ - spec/support/sp_template.rb
69
77
  homepage: ''
70
78
  licenses: []
79
+ metadata: {}
71
80
  post_install_message:
72
81
  rdoc_options: []
73
82
  require_paths:
74
83
  - lib
75
84
  required_ruby_version: !ruby/object:Gem::Requirement
76
- none: false
77
85
  requirements:
78
- - - ! '>='
86
+ - - ">="
79
87
  - !ruby/object:Gem::Version
80
88
  version: '0'
81
89
  required_rubygems_version: !ruby/object:Gem::Requirement
82
- none: false
83
90
  requirements:
84
- - - ! '>='
91
+ - - ">="
85
92
  - !ruby/object:Gem::Version
86
93
  version: '0'
87
94
  requirements: []
88
95
  rubyforge_project:
89
- rubygems_version: 1.8.24
96
+ rubygems_version: 2.4.6
90
97
  signing_key:
91
- specification_version: 3
98
+ specification_version: 4
92
99
  summary: SAML Authentication for devise
93
- test_files: []
100
+ test_files:
101
+ - spec/controllers/devise/saml_sessions_controller_spec.rb
102
+ - spec/devise_saml_authenticatable/model_spec.rb
103
+ - spec/devise_saml_authenticatable/saml_config_spec.rb
104
+ - spec/devise_saml_authenticatable/strategy_spec.rb
105
+ - spec/features/saml_authentication_spec.rb
106
+ - spec/rails_helper.rb
107
+ - spec/spec_helper.rb
108
+ - spec/support/idp_template.rb
109
+ - spec/support/rails_app.rb
110
+ - spec/support/saml_idp_controller.rb.erb
111
+ - spec/support/sp_template.rb
94
112
  has_rdoc: