devise_saml_authenticatable 1.2.1 → 1.2.2
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 +5 -13
- data/.rspec +1 -0
- data/.travis.yml +1 -1
- data/Gemfile +1 -2
- data/README.md +2 -2
- data/app/controllers/devise/saml_sessions_controller.rb +6 -8
- data/lib/devise_saml_authenticatable/model.rb +4 -2
- data/lib/devise_saml_authenticatable/saml_config.rb +3 -1
- data/lib/devise_saml_authenticatable/strategy.rb +3 -3
- data/lib/devise_saml_authenticatable/version.rb +1 -1
- data/spec/devise_saml_authenticatable/saml_config_spec.rb +1 -1
- data/spec/devise_saml_authenticatable/strategy_spec.rb +1 -1
- data/spec/features/saml_authentication_spec.rb +38 -5
- data/spec/rails_helper.rb +2 -2
- data/spec/support/idp_template.rb +2 -2
- data/spec/support/rails_app.rb +2 -13
- data/spec/support/sp_template.rb +3 -2
- metadata +11 -11
checksums.yaml
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
|
|
5
|
-
data.tar.gz: !binary |-
|
|
6
|
-
MDY1NTJiZTA4NjhlOTc2YmM2ZjlhYWMxNDcyMDNiMzUxMTBiNTBkNA==
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d2e501ba6f2facaaa55601864a66fbf5799f2b93
|
|
4
|
+
data.tar.gz: 644a41f1ef4dafdac806600b8a5646e3a00acbf6
|
|
7
5
|
SHA512:
|
|
8
|
-
metadata.gz:
|
|
9
|
-
|
|
10
|
-
NjMwMTg4YWVhMDQyM2NhNWVkYzNiY2JmYWViNzY5ZWJjN2FkMzk2ZGExNzlh
|
|
11
|
-
ZGMzNzU0NWU2MDZiZjM5MTMxNTNkZjA1MGUxZTgwOTljYTNhODE=
|
|
12
|
-
data.tar.gz: !binary |-
|
|
13
|
-
ZDYzODA0NzBlMWFmMjNhMjZmNzE4ZjBmMDllYzkyMzM3YWYxOGY4NTNiMTEy
|
|
14
|
-
ZTJhYjZmYTg4MDY5MDAyZmVmYzkzYWMzNjY0YzRkZGQxMzZlOWJkMzkwMzQx
|
|
15
|
-
NDYxMzAwMDU0OTE1ZTZlYWI5MTE3YzJlZjU1YzM4NjkxY2M3YmE=
|
|
6
|
+
metadata.gz: a4096feb553134033f04079e7c10a1ace113fc048f79dbf712ee0fe5b478ce6696ccd4434c5c05f4aecced1c55e79f13415027bfa613ca2937a216e8bb6949cd
|
|
7
|
+
data.tar.gz: 20c0f29acc30f4db788fa8ada4bfbff4df88421d48403f52b39cdb9876f1907be732a3641a0d9cdd7e3997f63809f71894769e8791014183a226333bedc2353e
|
data/.rspec
CHANGED
data/.travis.yml
CHANGED
|
@@ -11,7 +11,7 @@ script:
|
|
|
11
11
|
notifications:
|
|
12
12
|
hipchat:
|
|
13
13
|
rooms:
|
|
14
|
-
secure:
|
|
14
|
+
secure: cuDak5a6fBeg+sp61COqxQfzdcFEsjwCqtwvCISso0RNh5SR8v+uVYKcA8rlK+GE1l9uR7tLRHeHF3ZmzvFSOat07NvpScvjZXi+OSpWlc6rwQ6Pl6bBP6gu6sREiKVe0eT/uGrvJloyWKZaXIhiiBzQ+ZERx/ssGA9WMmNkhlwy1OgGnPNurNNHZLBjEZn1V6kdyxiXx6QPASNpjNEgN1G8dUh3qzcWUGVQGNZSJk65A6ie1MveNyecTjDhw+ADBU8nS28Ja4y6ohRm4FzofSgespYrvfygIZ5rYF0HPMj5FW1ZDWtM5355ojCk8RLT+ZkuhssCn1OJk7ogaOVjnYcOFRxEfpu3eIbjtMmUz3j4umatFqbgas+6SXMVIPkr5HUoTrP8HNFssIpcEBOnPwAF8QCpx+daHc0r2cc8lGuXhtJfpW0P2F0dmwJNiQ7//nz2y2xs84x4Gb7MV9tEDYp0FqEClMuFBkPNizBljarm04PkiLSrqvR52aMDfQz7YAX2oXAvFjPzI1GC0K8x7xX8TuHT9yuHy7fI+rUSNivZYLKO+IEZqPPDdJpXISUbVwanZoNvmQYk5PZV21MfDSGwQrz8eO/uFiAblj18yIlNbAfb2hdZDVYsm4EvWxELJtfaTxgrj6M3Y3m/KbCbCoDp+2jE307M2rxL0Gum2gk=
|
|
15
15
|
template:
|
|
16
16
|
- '%{repository}<a href="%{build_url}">#%{build_number}</a> (%{branch} - <a href="%{compare_url}">%{commit}</a> : %{author}): %{message}'
|
|
17
17
|
format: html
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -127,9 +127,9 @@ Logout support is included by immediately terminating the local session and then
|
|
|
127
127
|
Logout requests from the IDP are supported by the `idp_sign_out` end point. Directing logout requests to `users/saml/idp_sign_out` will logout the respective user by invalidating their current sessions.
|
|
128
128
|
`saml_session_index_key` must be configured to support this feature.
|
|
129
129
|
|
|
130
|
-
##
|
|
130
|
+
## Signing and Encrypting Authentication Requests
|
|
131
131
|
|
|
132
|
-
1.
|
|
132
|
+
ruby-saml 1.0.0 supports signature and decrypt. Teh only requirement is to place the public certificate and the private key. Please reffer to these features in the ruby-saml documentation [here](https://github.com/onelogin/ruby-saml#signing)
|
|
133
133
|
|
|
134
134
|
## Thanks
|
|
135
135
|
|
|
@@ -3,23 +3,22 @@ require "ruby-saml"
|
|
|
3
3
|
class Devise::SamlSessionsController < Devise::SessionsController
|
|
4
4
|
include DeviseSamlAuthenticatable::SamlConfig
|
|
5
5
|
unloadable if Rails::VERSION::MAJOR < 4
|
|
6
|
-
before_filter :get_saml_config
|
|
7
6
|
skip_before_filter :verify_authenticity_token
|
|
8
7
|
|
|
9
8
|
def new
|
|
10
9
|
request = OneLogin::RubySaml::Authrequest.new
|
|
11
|
-
action = request.create(
|
|
10
|
+
action = request.create(saml_config)
|
|
12
11
|
redirect_to action
|
|
13
12
|
end
|
|
14
|
-
|
|
13
|
+
|
|
15
14
|
def metadata
|
|
16
15
|
meta = OneLogin::RubySaml::Metadata.new
|
|
17
|
-
render :xml => meta.generate(
|
|
16
|
+
render :xml => meta.generate(saml_config)
|
|
18
17
|
end
|
|
19
18
|
|
|
20
19
|
def idp_sign_out
|
|
21
20
|
if params[:SAMLRequest] && Devise.saml_session_index_key
|
|
22
|
-
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings:
|
|
21
|
+
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: saml_config)
|
|
23
22
|
resource_class.reset_session_key_for(logout_request.name_id)
|
|
24
23
|
|
|
25
24
|
redirect_to generate_idp_logout_response(logout_request)
|
|
@@ -42,12 +41,11 @@ class Devise::SamlSessionsController < Devise::SessionsController
|
|
|
42
41
|
# Override devise to send user to IdP logout for SLO
|
|
43
42
|
def after_sign_out_path_for(_)
|
|
44
43
|
request = OneLogin::RubySaml::Logoutrequest.new
|
|
45
|
-
request.create(
|
|
44
|
+
request.create(saml_config)
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
def generate_idp_logout_response(logout_request)
|
|
49
48
|
logout_request_id = logout_request.id
|
|
50
|
-
OneLogin::RubySaml::SloLogoutresponse.new.create(
|
|
49
|
+
OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil)
|
|
51
50
|
end
|
|
52
51
|
end
|
|
53
|
-
|
|
@@ -25,13 +25,15 @@ module Devise
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def after_saml_authentication(session_index)
|
|
28
|
-
if self.respond_to?
|
|
28
|
+
if Devise.saml_session_index_key && self.respond_to?(Devise.saml_session_index_key)
|
|
29
29
|
self.update_attribute(Devise.saml_session_index_key, session_index)
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def authenticatable_salt
|
|
34
|
-
if
|
|
34
|
+
if Devise.saml_session_index_key &&
|
|
35
|
+
self.respond_to?(Devise.saml_session_index_key) &&
|
|
36
|
+
self.send(Devise.saml_session_index_key).present?
|
|
35
37
|
self.send(Devise.saml_session_index_key)
|
|
36
38
|
else
|
|
37
39
|
super
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
require 'ruby-saml'
|
|
2
2
|
module DeviseSamlAuthenticatable
|
|
3
3
|
module SamlConfig
|
|
4
|
-
def
|
|
4
|
+
def saml_config
|
|
5
|
+
return @saml_config if @saml_config
|
|
6
|
+
|
|
5
7
|
idp_config_path = "#{Rails.root}/config/idp.yml"
|
|
6
8
|
# Support 0.0.x-style configuration via a YAML file
|
|
7
9
|
if File.exists?(idp_config_path)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require 'devise/strategies/authenticatable'
|
|
1
|
+
require 'devise/strategies/authenticatable'
|
|
2
2
|
|
|
3
3
|
module Devise
|
|
4
4
|
module Strategies
|
|
@@ -6,7 +6,7 @@ module Devise
|
|
|
6
6
|
include DeviseSamlAuthenticatable::SamlConfig
|
|
7
7
|
def valid?
|
|
8
8
|
if params[:SAMLResponse]
|
|
9
|
-
response = OneLogin::RubySaml::Logoutresponse.new(params[:SAMLResponse],
|
|
9
|
+
response = OneLogin::RubySaml::Logoutresponse.new(params[:SAMLResponse], saml_config)
|
|
10
10
|
!(response.response.include? 'LogoutResponse')
|
|
11
11
|
else
|
|
12
12
|
false
|
|
@@ -14,7 +14,7 @@ module Devise
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def authenticate!
|
|
17
|
-
@response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings:
|
|
17
|
+
@response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], settings: saml_config)
|
|
18
18
|
resource = mapping.to.authenticate_with_saml(@response)
|
|
19
19
|
if @response.is_valid? && resource
|
|
20
20
|
resource.after_saml_authentication(@response.sessionindex)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe DeviseSamlAuthenticatable::SamlConfig do
|
|
4
|
-
subject(:saml_config) { controller.
|
|
4
|
+
subject(:saml_config) { controller.saml_config }
|
|
5
5
|
let(:controller) { Class.new { include DeviseSamlAuthenticatable::SamlConfig }.new }
|
|
6
6
|
|
|
7
7
|
# Replace global config since this test changes it
|
|
@@ -11,7 +11,7 @@ describe Devise::Strategies::SamlAuthenticatable do
|
|
|
11
11
|
|
|
12
12
|
let(:saml_config) { OneLogin::RubySaml::Settings.new }
|
|
13
13
|
before do
|
|
14
|
-
allow(strategy).to receive(:
|
|
14
|
+
allow(strategy).to receive(:saml_config).and_return(saml_config)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
let(:mapping) { double(:mapping, to: user_class) }
|
|
@@ -63,8 +63,10 @@ describe "SAML Authentication", type: :feature do
|
|
|
63
63
|
visit 'http://localhost:8020/'
|
|
64
64
|
expect(current_url).to match(%r(\Ahttp://localhost:8009/saml/auth\?SAMLRequest=))
|
|
65
65
|
end
|
|
66
|
+
end
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
shared_examples_for "it logs a user out via the IdP" do
|
|
69
|
+
it 'logs a user out of the SP via the IdP' do
|
|
68
70
|
sign_in
|
|
69
71
|
|
|
70
72
|
visit "http://localhost:#{idp_port}/saml/sp_sign_out"
|
|
@@ -76,8 +78,8 @@ describe "SAML Authentication", type: :feature do
|
|
|
76
78
|
|
|
77
79
|
context "when the attributes are used to authenticate" do
|
|
78
80
|
before(:each) do
|
|
79
|
-
create_app('idp',
|
|
80
|
-
create_app('sp',
|
|
81
|
+
create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "true")
|
|
82
|
+
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "false")
|
|
81
83
|
@idp_pid = start_app('idp', idp_port)
|
|
82
84
|
@sp_pid = start_app('sp', sp_port)
|
|
83
85
|
end
|
|
@@ -91,8 +93,39 @@ describe "SAML Authentication", type: :feature do
|
|
|
91
93
|
|
|
92
94
|
context "when the subject is used to authenticate" do
|
|
93
95
|
before(:each) do
|
|
94
|
-
create_app('idp',
|
|
95
|
-
create_app('sp',
|
|
96
|
+
create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "false")
|
|
97
|
+
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true")
|
|
98
|
+
@idp_pid = start_app('idp', idp_port)
|
|
99
|
+
@sp_pid = start_app('sp', sp_port)
|
|
100
|
+
end
|
|
101
|
+
after(:each) do
|
|
102
|
+
stop_app(@idp_pid)
|
|
103
|
+
stop_app(@sp_pid)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it_behaves_like "it authenticates and creates users"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context "when the session index key is set" do
|
|
110
|
+
before(:each) do
|
|
111
|
+
create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "false")
|
|
112
|
+
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true", 'SAML_SESSION_INDEX_KEY' => ":session_index")
|
|
113
|
+
@idp_pid = start_app('idp', idp_port)
|
|
114
|
+
@sp_pid = start_app('sp', sp_port)
|
|
115
|
+
end
|
|
116
|
+
after(:each) do
|
|
117
|
+
stop_app(@idp_pid)
|
|
118
|
+
stop_app(@sp_pid)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it_behaves_like "it authenticates and creates users"
|
|
122
|
+
it_behaves_like "it logs a user out via the IdP"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context "when the session index key is not set" do
|
|
126
|
+
before(:each) do
|
|
127
|
+
create_app('idp', 'INCLUDE_SUBJECT_IN_ATTRIBUTES' => "false")
|
|
128
|
+
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "true", 'SAML_SESSION_INDEX_KEY' => "nil")
|
|
96
129
|
@idp_pid = start_app('idp', idp_port)
|
|
97
130
|
@sp_pid = start_app('sp', sp_port)
|
|
98
131
|
end
|
data/spec/rails_helper.rb
CHANGED
|
@@ -2,7 +2,7 @@ ENV["RAILS_ENV"] ||= 'test'
|
|
|
2
2
|
|
|
3
3
|
require 'spec_helper'
|
|
4
4
|
|
|
5
|
-
create_app('sp',
|
|
5
|
+
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "false")
|
|
6
6
|
require 'support/sp/config/environment'
|
|
7
7
|
require 'rspec/rails'
|
|
8
8
|
|
|
@@ -17,4 +17,4 @@ end
|
|
|
17
17
|
Devise.setup do |config|
|
|
18
18
|
config.saml_default_user_key = :email
|
|
19
19
|
config.saml_session_index_key = :session_index
|
|
20
|
-
end
|
|
20
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Set up a SAML IdP
|
|
2
2
|
|
|
3
|
-
@include_subject_in_attributes =
|
|
3
|
+
@include_subject_in_attributes = ENV.fetch('INCLUDE_SUBJECT_IN_ATTRIBUTES')
|
|
4
4
|
|
|
5
5
|
gem 'ruby-saml-idp'
|
|
6
6
|
gem 'thin'
|
|
@@ -18,4 +18,4 @@ gsub_file 'config/application.rb', /end[\n\w]*end$/, <<-CONFIG
|
|
|
18
18
|
config.slo_sp_url = "http://localhost:8020/users/saml/idp_sign_out"
|
|
19
19
|
end
|
|
20
20
|
end
|
|
21
|
-
CONFIG
|
|
21
|
+
CONFIG
|
data/spec/support/rails_app.rb
CHANGED
|
@@ -11,24 +11,13 @@ def app_ready?(pid, port)
|
|
|
11
11
|
system("lsof -i:#{port}", out: '/dev/null')
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def create_app(name,
|
|
14
|
+
def create_app(name, env = {})
|
|
15
15
|
rails_new_options = %w(-T -J -S --skip-spring)
|
|
16
16
|
rails_new_options << "-O" if name == 'idp'
|
|
17
17
|
Bundler.with_clean_env do
|
|
18
18
|
Dir.chdir(File.expand_path('../../support', __FILE__)) do
|
|
19
19
|
FileUtils.rm_rf(name)
|
|
20
|
-
|
|
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
|
|
20
|
+
system(env, "rails", "new", name, *rails_new_options, "-m", "#{name}_template.rb")
|
|
32
21
|
end
|
|
33
22
|
end
|
|
34
23
|
end
|
data/spec/support/sp_template.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Set up a SAML Service Provider
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
saml_session_index_key = ENV.fetch('SAML_SESSION_INDEX_KEY', ":session_index")
|
|
4
|
+
use_subject_to_authenticate = ENV.fetch('USE_SUBJECT_TO_AUTHENTICATE')
|
|
4
5
|
|
|
5
6
|
gem 'devise_saml_authenticatable', path: '../../..'
|
|
6
7
|
gem 'thin'
|
|
@@ -32,7 +33,7 @@ after_bundle do
|
|
|
32
33
|
generate 'devise:install'
|
|
33
34
|
gsub_file 'config/initializers/devise.rb', /^end$/, <<-CONFIG
|
|
34
35
|
config.saml_default_user_key = :email
|
|
35
|
-
config.saml_session_index_key =
|
|
36
|
+
config.saml_session_index_key = #{saml_session_index_key}
|
|
36
37
|
|
|
37
38
|
config.saml_use_subject = #{use_subject_to_authenticate}
|
|
38
39
|
config.saml_create_user = true
|
metadata
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: devise_saml_authenticatable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.2
|
|
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: 2016-01-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: devise
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- -
|
|
17
|
+
- - ">"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: 2.0.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
|
-
- -
|
|
24
|
+
- - ">"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: 2.0.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: ruby-saml
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
|
-
- - ~>
|
|
31
|
+
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
33
|
version: '1.0'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
|
-
- - ~>
|
|
38
|
+
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
40
|
version: '1.0'
|
|
41
41
|
description: SAML Authentication for devise
|
|
@@ -45,9 +45,9 @@ executables: []
|
|
|
45
45
|
extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
|
47
47
|
files:
|
|
48
|
-
- .gitignore
|
|
49
|
-
- .rspec
|
|
50
|
-
- .travis.yml
|
|
48
|
+
- ".gitignore"
|
|
49
|
+
- ".rspec"
|
|
50
|
+
- ".travis.yml"
|
|
51
51
|
- Gemfile
|
|
52
52
|
- LICENSE
|
|
53
53
|
- README.md
|
|
@@ -84,12 +84,12 @@ require_paths:
|
|
|
84
84
|
- lib
|
|
85
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
86
|
requirements:
|
|
87
|
-
- -
|
|
87
|
+
- - ">="
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
89
|
version: '0'
|
|
90
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
91
|
requirements:
|
|
92
|
-
- -
|
|
92
|
+
- - ">="
|
|
93
93
|
- !ruby/object:Gem::Version
|
|
94
94
|
version: '0'
|
|
95
95
|
requirements: []
|