devise_saml_authenticatable 1.3.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +0 -2
- data/.travis.yml +37 -22
- data/Gemfile +2 -10
- data/README.md +127 -44
- data/app/controllers/devise/saml_sessions_controller.rb +38 -7
- data/devise_saml_authenticatable.gemspec +2 -1
- data/lib/devise_saml_authenticatable.rb +70 -0
- data/lib/devise_saml_authenticatable/default_attribute_map_resolver.rb +26 -0
- data/lib/devise_saml_authenticatable/default_idp_entity_id_reader.rb +10 -2
- data/lib/devise_saml_authenticatable/exception.rb +1 -1
- data/lib/devise_saml_authenticatable/model.rb +20 -32
- data/lib/devise_saml_authenticatable/routes.rb +17 -6
- data/lib/devise_saml_authenticatable/saml_mapped_attributes.rb +38 -0
- data/lib/devise_saml_authenticatable/saml_response.rb +16 -0
- data/lib/devise_saml_authenticatable/strategy.rb +10 -2
- data/lib/devise_saml_authenticatable/version.rb +1 -1
- data/spec/controllers/devise/saml_sessions_controller_spec.rb +118 -11
- data/spec/devise_saml_authenticatable/default_attribute_map_resolver_spec.rb +58 -0
- data/spec/devise_saml_authenticatable/default_idp_entity_id_reader_spec.rb +34 -4
- data/spec/devise_saml_authenticatable/model_spec.rb +199 -5
- data/spec/devise_saml_authenticatable/saml_mapped_attributes_spec.rb +50 -0
- data/spec/devise_saml_authenticatable/strategy_spec.rb +18 -0
- data/spec/features/saml_authentication_spec.rb +45 -21
- data/spec/rails_helper.rb +6 -2
- data/spec/routes/routes_spec.rb +102 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/Gemfile.rails4 +24 -6
- data/spec/support/Gemfile.rails5 +25 -0
- data/spec/support/Gemfile.rails5.1 +25 -0
- data/spec/support/Gemfile.rails5.2 +25 -0
- data/spec/support/attribute-map.yml +12 -0
- data/spec/support/attribute_map_resolver.rb.erb +14 -0
- data/spec/support/idp_settings_adapter.rb.erb +5 -5
- data/spec/support/idp_template.rb +8 -1
- data/spec/support/rails_app.rb +110 -16
- data/spec/support/saml_idp_controller.rb.erb +22 -10
- data/spec/support/sp_template.rb +52 -21
- metadata +26 -10
- data/spec/support/Gemfile.ruby-saml-1.3 +0 -23
data/spec/rails_helper.rb
CHANGED
@@ -3,12 +3,16 @@ ENV["RAILS_ENV"] ||= 'test'
|
|
3
3
|
require 'spec_helper'
|
4
4
|
|
5
5
|
create_app('sp', 'USE_SUBJECT_TO_AUTHENTICATE' => "false")
|
6
|
-
require
|
6
|
+
require "#{working_directory}/sp/config/environment"
|
7
7
|
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("#{working_directory}/sp/db/migrate/")
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
1
3
|
RSpec.configure do |config|
|
2
4
|
config.run_all_when_everything_filtered = true
|
3
5
|
config.filter_run :focus
|
@@ -28,8 +30,13 @@ RSpec.configure do |config|
|
|
28
30
|
Devise.saml_session_index_key = @original_saml_session_index_key
|
29
31
|
Devise.idp_settings_adapter = nil
|
30
32
|
end
|
33
|
+
|
34
|
+
config.after :suite do
|
35
|
+
FileUtils.rm_rf($working_directory) if $working_directory
|
36
|
+
end
|
31
37
|
end
|
32
38
|
|
33
39
|
require 'support/rails_app'
|
34
40
|
|
41
|
+
require "action_controller" # https://github.com/heartcombo/responders/pull/95
|
35
42
|
require 'devise_saml_authenticatable'
|
data/spec/support/Gemfile.rails4
CHANGED
@@ -4,20 +4,38 @@ 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
|
-
gem 'rspec-rails'
|
11
|
-
gem 'sqlite3'
|
9
|
+
gem 'rspec-rails', '~> 3.9'
|
10
|
+
gem 'sqlite3', '~> 1.3.6'
|
12
11
|
gem 'capybara'
|
13
12
|
gem 'poltergeist'
|
14
13
|
|
15
14
|
# Lock down versions of gems for older versions of Ruby
|
16
|
-
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.
|
17
|
-
gem '
|
18
|
-
|
15
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
16
|
+
gem 'rake', '~> 12.2'
|
17
|
+
else
|
18
|
+
gem 'rake'
|
19
19
|
end
|
20
|
+
|
20
21
|
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
21
22
|
gem 'devise', '~> 3.5'
|
23
|
+
gem 'minitest', '~> 5.11.0'
|
24
|
+
gem 'nokogiri', '~> 1.6.8'
|
25
|
+
gem 'public_suffix', '~> 2.0.5'
|
26
|
+
end
|
27
|
+
|
28
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
29
|
+
gem 'responders', '~> 1.0'
|
30
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
31
|
+
gem 'responders', '~> 2.0'
|
32
|
+
end
|
33
|
+
|
34
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.2")
|
35
|
+
gem 'byebug', '~> 9.0'
|
36
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.3")
|
37
|
+
gem 'byebug', '~> 10.0'
|
38
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
39
|
+
gem 'byebug', '~> 11.0.0'
|
22
40
|
end
|
23
41
|
end
|
@@ -0,0 +1,25 @@
|
|
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.0.0'
|
10
|
+
gem 'rspec-rails', '~> 3.9'
|
11
|
+
gem 'sqlite3', '~> 1.3.6'
|
12
|
+
gem 'capybara'
|
13
|
+
gem 'poltergeist'
|
14
|
+
|
15
|
+
# Lock down versions of gems for older versions of Ruby
|
16
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
17
|
+
gem 'responders', '~> 2.4'
|
18
|
+
end
|
19
|
+
|
20
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.3")
|
21
|
+
gem 'byebug', '~> 10.0'
|
22
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
23
|
+
gem 'byebug', '~> 11.0.0'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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', '~> 3.9'
|
11
|
+
gem 'sqlite3', '~> 1.3.6'
|
12
|
+
gem 'capybara'
|
13
|
+
gem 'poltergeist'
|
14
|
+
|
15
|
+
# Lock down versions of gems for older versions of Ruby
|
16
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
17
|
+
gem 'responders', '~> 2.4'
|
18
|
+
end
|
19
|
+
|
20
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.3")
|
21
|
+
gem 'byebug', '~> 10.0'
|
22
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
23
|
+
gem 'byebug', '~> 11.0.0'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
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.2'
|
10
|
+
gem 'rspec-rails', '~> 3.9'
|
11
|
+
gem 'sqlite3', '~> 1.3.6'
|
12
|
+
gem 'capybara'
|
13
|
+
gem 'poltergeist'
|
14
|
+
|
15
|
+
# Lock down versions of gems for older versions of Ruby
|
16
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
17
|
+
gem 'responders', '~> 2.4'
|
18
|
+
end
|
19
|
+
|
20
|
+
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.3")
|
21
|
+
gem 'byebug', '~> 10.0'
|
22
|
+
elsif Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.4")
|
23
|
+
gem 'byebug', '~> 11.0.0'
|
24
|
+
end
|
25
|
+
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"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class AttributeMapResolver < DeviseSamlAuthenticatable::DefaultAttributeMapResolver
|
2
|
+
def attribute_map
|
3
|
+
issuer = saml_response.issuers.first
|
4
|
+
Rails.logger.info("[#{self.class.name}] issuer=#{issuer.inspect}")
|
5
|
+
if issuer == "http://localhost:8009/saml/auth"
|
6
|
+
{
|
7
|
+
"myemailaddress" => "email",
|
8
|
+
"myname" => "name",
|
9
|
+
}
|
10
|
+
else
|
11
|
+
{}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -2,15 +2,15 @@ class IdpSettingsAdapter
|
|
2
2
|
def self.settings(idp_entity_id)
|
3
3
|
if idp_entity_id == "http://localhost:8020/saml/metadata"
|
4
4
|
{
|
5
|
-
assertion_consumer_service_url: "
|
5
|
+
assertion_consumer_service_url: "http://localhost:8020/users/saml/auth",
|
6
6
|
assertion_consumer_service_binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
|
7
|
-
name_identifier_format: "urn:oasis:names:tc:SAML:
|
7
|
+
name_identifier_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
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://
|
12
|
-
idp_sso_target_url: "http://
|
13
|
-
|
11
|
+
idp_slo_target_url: "http://localhost:8010/saml/logout",
|
12
|
+
idp_sso_target_url: "http://localhost:8010/saml/auth",
|
13
|
+
idp_cert_fingerprint: "9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D"
|
14
14
|
}
|
15
15
|
else
|
16
16
|
{}
|
@@ -1,9 +1,15 @@
|
|
1
1
|
# Set up a SAML IdP
|
2
2
|
|
3
|
+
@email_address_attribute_key = ENV.fetch("EMAIL_ADDRESS_ATTRIBUTE_KEY", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")
|
4
|
+
@name_attribute_key = ENV.fetch("NAME_ATTRIBUTE_KEY", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name")
|
3
5
|
@include_subject_in_attributes = ENV.fetch('INCLUDE_SUBJECT_IN_ATTRIBUTES')
|
4
6
|
@valid_destination = ENV.fetch('VALID_DESTINATION', "true")
|
5
7
|
|
6
|
-
|
8
|
+
if Rails::VERSION::MAJOR < 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR < 2)
|
9
|
+
gsub_file 'config/secrets.yml', /secret_key_base:.*$/, 'secret_key_base: "34814fd41f91c493b89aa01ac73c44d241a31245b5bc5542fa4b7317525e1dcfa60ba947b3d085e4e229456fdee0d8af6aac6a63cf750d807ea6fe5d853dff4a"'
|
10
|
+
end
|
11
|
+
|
12
|
+
gem 'ruby-saml-idp', '~> 0.3.3'
|
7
13
|
gem 'thin'
|
8
14
|
|
9
15
|
insert_into_file('Gemfile', after: /\z/) {
|
@@ -11,6 +17,7 @@ insert_into_file('Gemfile', after: /\z/) {
|
|
11
17
|
# Lock down versions of gems for older versions of Ruby
|
12
18
|
if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.1")
|
13
19
|
gem 'devise', '~> 3.5'
|
20
|
+
gem 'nokogiri', '~> 1.6.8'
|
14
21
|
end
|
15
22
|
GEMFILE
|
16
23
|
}
|
data/spec/support/rails_app.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require "open3"
|
2
|
+
require "socket"
|
3
|
+
require "tempfile"
|
4
|
+
require "timeout"
|
5
|
+
|
6
|
+
APP_READY_TIMEOUT ||= 30
|
2
7
|
|
3
8
|
def sh!(cmd)
|
4
9
|
unless system(cmd)
|
@@ -7,38 +12,127 @@ def sh!(cmd)
|
|
7
12
|
end
|
8
13
|
|
9
14
|
def app_ready?(pid, port)
|
10
|
-
Process.getpgid(pid) &&
|
11
|
-
|
15
|
+
Process.getpgid(pid) && port_open?(port)
|
16
|
+
rescue Errno::ESRCH
|
17
|
+
false
|
12
18
|
end
|
13
19
|
|
14
20
|
def create_app(name, env = {})
|
15
|
-
|
16
|
-
rails_new_options
|
17
|
-
|
18
|
-
|
19
|
-
|
21
|
+
puts "[#{name}] Creating Rails app"
|
22
|
+
rails_new_options = %w[-T -J -S --skip-spring --skip-listen --skip-bootsnap]
|
23
|
+
rails_new_options << "-O" if name == "idp"
|
24
|
+
with_clean_env do
|
25
|
+
Dir.chdir(working_directory) do
|
26
|
+
FileUtils.rm_rf(name)
|
27
|
+
puts("rails _#{Rails.version}_ new #{name} #{rails_new_options.join(" ")} -m #{File.expand_path("../#{name}_template.rb", __FILE__)}")
|
28
|
+
system(env, "rails", "_#{Rails.version}_", "new", name, *rails_new_options, "-m", File.expand_path("../#{name}_template.rb", __FILE__))
|
29
|
+
end
|
20
30
|
end
|
21
31
|
end
|
22
32
|
|
23
33
|
def start_app(name, port, options = {})
|
34
|
+
puts "[#{name}] Starting Rails app"
|
24
35
|
pid = nil
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
app_bundle_install(name)
|
37
|
+
|
38
|
+
with_clean_env do
|
39
|
+
Dir.chdir(app_dir(name)) do
|
40
|
+
pid = Process.spawn(app_env(name), "bundle exec rails server -p #{port} -e production", chdir: app_dir(name), out: "log/#{name}.log", err: "log/#{name}.err.log")
|
41
|
+
begin
|
42
|
+
Timeout.timeout(APP_READY_TIMEOUT) do
|
43
|
+
sleep 1 until app_ready?(pid, port)
|
44
|
+
end
|
45
|
+
if app_ready?(pid, port)
|
46
|
+
puts "[#{name}] Launched #{name} on port #{port} (pid #{pid})..."
|
47
|
+
else
|
48
|
+
raise "#{name} failed after starting"
|
49
|
+
end
|
50
|
+
rescue Timeout::Error
|
32
51
|
raise "#{name} failed to start"
|
33
52
|
end
|
34
53
|
end
|
35
54
|
end
|
36
55
|
pid
|
56
|
+
rescue RuntimeError => e
|
57
|
+
warn "=== #{name}"
|
58
|
+
Dir.chdir(app_dir(name)) do
|
59
|
+
warn File.read("log/#{name}.log") if File.exist?("log/#{name}.log")
|
60
|
+
warn File.read("log/#{name}.err.log") if File.exist?("log/#{name}.err.log")
|
61
|
+
end
|
62
|
+
raise e
|
37
63
|
end
|
38
64
|
|
39
|
-
def stop_app(pid)
|
65
|
+
def stop_app(name, pid)
|
40
66
|
if pid
|
41
67
|
Process.kill(:INT, pid)
|
42
68
|
Process.wait(pid)
|
43
69
|
end
|
70
|
+
Dir.chdir(app_dir(name)) do
|
71
|
+
if File.exist?("log/#{name}.log")
|
72
|
+
puts "=== [#{name}] stdout"
|
73
|
+
puts File.read("log/#{name}.log")
|
74
|
+
end
|
75
|
+
if File.exist?("log/#{name}.err.log")
|
76
|
+
warn "=== [#{name}] stderr"
|
77
|
+
warn File.read("log/#{name}.err.log")
|
78
|
+
end
|
79
|
+
if File.exist?("log/production.log")
|
80
|
+
puts "=== [#{name}] Rails logs"
|
81
|
+
puts File.read("log/production.log")
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def port_open?(port)
|
87
|
+
Timeout::timeout(1) do
|
88
|
+
begin
|
89
|
+
s = TCPSocket.new('localhost', port)
|
90
|
+
s.close
|
91
|
+
return true
|
92
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EADDRNOTAVAIL
|
93
|
+
# try 127.0.0.1
|
94
|
+
end
|
95
|
+
begin
|
96
|
+
s = TCPSocket.new('127.0.0.1', port)
|
97
|
+
s.close
|
98
|
+
return true
|
99
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
100
|
+
return false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
rescue Timeout::Error
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
107
|
+
def app_bundle_install(name)
|
108
|
+
with_clean_env do
|
109
|
+
Open3.popen3(app_env(name), "bundle install", chdir: app_dir(name)) do |stdin, stdout, stderr, thread|
|
110
|
+
stdin.close
|
111
|
+
exit_status = thread.value
|
112
|
+
|
113
|
+
puts stdout.read
|
114
|
+
warn stderr.read
|
115
|
+
raise "bundle install failed" unless exit_status.success?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def app_dir(name)
|
121
|
+
File.join(working_directory, name)
|
122
|
+
end
|
123
|
+
|
124
|
+
def app_env(name)
|
125
|
+
{"BUNDLE_GEMFILE" => File.join(app_dir(name), "Gemfile"), "RAILS_ENV" => "production"}
|
126
|
+
end
|
127
|
+
|
128
|
+
def working_directory
|
129
|
+
$working_directory ||= Dir.mktmpdir("dsa_test")
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_clean_env(&blk)
|
133
|
+
if Bundler.respond_to?(:with_original_env)
|
134
|
+
Bundler.with_original_env(&blk)
|
135
|
+
else
|
136
|
+
Bundler.with_clean_env(&blk)
|
137
|
+
end
|
44
138
|
end
|