softwear 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/softwear/application.js +13 -0
- data/app/assets/javascripts/softwear/error_utils.js.coffee +82 -0
- data/app/assets/javascripts/softwear/modals.js.coffee +171 -0
- data/app/assets/stylesheets/softwear/application.css +33 -0
- data/app/controllers/softwear/application_controller.rb +4 -0
- data/app/controllers/softwear/error_reports_controller.rb +37 -0
- data/app/helpers/softwear/application_helper.rb +4 -0
- data/app/helpers/softwear/emails_helper.rb +9 -0
- data/app/mailers/error_report_mailer.rb +53 -0
- data/app/views/error_report_mailer/send_report.html.erb +30 -0
- data/app/views/layouts/softwear/application.html.erb +14 -0
- data/app/views/softwear/errors/_error.html.erb +35 -0
- data/app/views/softwear/errors/internal_server_error.html.erb +6 -0
- data/app/views/softwear/errors/internal_server_error.js.erb +10 -0
- data/bin/rails +12 -0
- data/config/routes.rb +3 -0
- data/lib/softwear/auth/belongs_to_user.rb +43 -0
- data/lib/softwear/auth/controller.rb +43 -0
- data/lib/softwear/auth/helper.rb +35 -0
- data/lib/softwear/auth/model.rb +16 -0
- data/lib/softwear/auth/spec.rb +62 -0
- data/lib/softwear/auth/standard_model.rb +498 -0
- data/lib/softwear/auth/stubbed_model.rb +130 -0
- data/lib/softwear/auth/token_authentication.rb +72 -0
- data/lib/softwear/engine.rb +5 -0
- data/lib/softwear/error_catcher.rb +65 -0
- data/lib/softwear/library/api_controller.rb +169 -0
- data/lib/softwear/library/capistrano.rb +94 -0
- data/lib/softwear/library/controller_authentication.rb +145 -0
- data/lib/softwear/library/enqueue.rb +87 -0
- data/lib/softwear/library/spec.rb +127 -0
- data/lib/softwear/library/tcp_server.rb +107 -0
- data/lib/softwear/version.rb +3 -0
- data/lib/softwear.rb +107 -0
- metadata +172 -0
@@ -0,0 +1,130 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Auth
|
3
|
+
# =======================================================================
|
4
|
+
# In development, unless ENV['AUTH_SERVER'] is set, Softwear::Auth::Model is
|
5
|
+
# set to this StubbedModel. The StubbedModel does not contact the auth_server,
|
6
|
+
# and instead retrieves user info from config/users.yml, which assumes the
|
7
|
+
# following format:
|
8
|
+
=begin
|
9
|
+
---
|
10
|
+
signed_in: nigel@annarbortees.com
|
11
|
+
|
12
|
+
users:
|
13
|
+
admin@softwearcrm.com:
|
14
|
+
first_name: Admin
|
15
|
+
last_name: User
|
16
|
+
roles:
|
17
|
+
- sales_manager
|
18
|
+
|
19
|
+
nigel@annarbortees.com:
|
20
|
+
first_name: Nigel
|
21
|
+
last_name: Baillie
|
22
|
+
roles:
|
23
|
+
- developer
|
24
|
+
- admin
|
25
|
+
|
26
|
+
ricky@annarbortees.com:
|
27
|
+
first_name: Ricky
|
28
|
+
last_name: Winowiecki
|
29
|
+
roles:
|
30
|
+
- developer
|
31
|
+
- administrator
|
32
|
+
=end
|
33
|
+
# Changes in this .yml file will be reflected live, without restarting your
|
34
|
+
# server.
|
35
|
+
# =======================================================================
|
36
|
+
class StubbedModel < Softwear::Auth::StandardModel
|
37
|
+
class << self
|
38
|
+
def raw_query(m, *args)
|
39
|
+
return 'yes' if m =~ /^token/
|
40
|
+
raise %[Cannot perform auth server queries on stubbed auth model. (tried to send "#{m.split(/\s+/).first} ..." query)]
|
41
|
+
end
|
42
|
+
|
43
|
+
def users_yml_file
|
44
|
+
Rails.root.join('config', 'users.yml').to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
def users_yml
|
48
|
+
if @users_yml
|
49
|
+
yml_mtime = File.mtime(users_yml_file)
|
50
|
+
|
51
|
+
if @users_yml_modified.nil? || yml_mtime > @users_yml_modified
|
52
|
+
@users_yml_modified = yml_mtime
|
53
|
+
@users_yml = nil
|
54
|
+
end
|
55
|
+
else
|
56
|
+
@users_yml_modified = File.mtime(users_yml_file)
|
57
|
+
end
|
58
|
+
|
59
|
+
if @users_yml.nil?
|
60
|
+
@users_yml = YAML.load(IO.read(users_yml_file)).with_indifferent_access
|
61
|
+
@users_yml[:users].to_a.each_with_index do |entry, i|
|
62
|
+
entry[1][:id] ||= i + 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
@users_yml
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Transforms
|
70
|
+
# ['email@email.com', { 'attr1' => 'val1', 'attr2' => 'val2' }]
|
71
|
+
# From the yml format
|
72
|
+
#
|
73
|
+
# Into
|
74
|
+
# { 'email' => 'email@email.com', 'attr1' => 'val1', 'attr2' => 'val2' }
|
75
|
+
#
|
76
|
+
def yml_entry(entry, id_if_default = nil)
|
77
|
+
attributes = {}.with_indifferent_access
|
78
|
+
|
79
|
+
if entry.nil?
|
80
|
+
entry = ['usernotfound@example.com', { first_name: 'Unknown', last_name: 'User', id: id_if_default || -1 }]
|
81
|
+
end
|
82
|
+
|
83
|
+
attributes[:email] = entry[0]
|
84
|
+
attributes.merge!(entry[1])
|
85
|
+
if attributes[:profile_picture]
|
86
|
+
attributes[:profile_picture_url] ||= "file://#{attributes[:profile_picture]}"
|
87
|
+
end
|
88
|
+
|
89
|
+
new(attributes).tap { |u| u.instance_variable_set(:@persisted, true) }
|
90
|
+
end
|
91
|
+
|
92
|
+
def find(target_id)
|
93
|
+
return nil if target_id.nil?
|
94
|
+
yml_entry users_yml[:users].to_a[target_id.to_i - 1], target_id.to_i
|
95
|
+
end
|
96
|
+
|
97
|
+
def all
|
98
|
+
users_yml[:users].to_a.map(&method(:yml_entry))
|
99
|
+
end
|
100
|
+
|
101
|
+
def of_role(*roles)
|
102
|
+
roles = Array(roles).map(&:to_s)
|
103
|
+
all.select { |u| !u.roles.nil? && roles.any? { |r| u.roles.include?(r) } }
|
104
|
+
end
|
105
|
+
|
106
|
+
def auth(_token, _appname = nil)
|
107
|
+
signed_in = users_yml[:signed_in]
|
108
|
+
|
109
|
+
yml_entry [signed_in, users_yml[:users][signed_in]] unless signed_in.blank?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def yml_entry(*args)
|
114
|
+
self.class.yml_entry(*args)
|
115
|
+
end
|
116
|
+
def users_yml(*args)
|
117
|
+
self.class.users_yml(*args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def reload
|
121
|
+
update_attributes yml_entry users_yml[:users].to_a[id - 1]
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def valid_password?
|
126
|
+
true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Auth
|
3
|
+
module TokenAuthentication
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
cattr_accessor :user_class
|
8
|
+
cattr_accessor :token_auth_options
|
9
|
+
end
|
10
|
+
|
11
|
+
def token_authenticate_user!
|
12
|
+
user_class = self.class.user_class || base_class.user_class || User
|
13
|
+
options = (self.class.token_auth_options || base_class.token_auth_options || {}).with_indifferent_access
|
14
|
+
params_options = (options[:params] || {}).with_indifferent_access
|
15
|
+
headers_options = (options[:headers] || {}).with_indifferent_access
|
16
|
+
|
17
|
+
email_param = params_options[:email] || 'user_email'
|
18
|
+
token_param = params_options[:authentication_token] || 'user_token'
|
19
|
+
email_header = headers_options[:email] || 'X-User-Email'
|
20
|
+
token_header = headers_options[:authentication_token] || 'X-User-Token'
|
21
|
+
|
22
|
+
email = params[email_param] || request.headers[email_header]
|
23
|
+
token = params[token_param] || request.headers[token_header]
|
24
|
+
|
25
|
+
return render_unauthorized if email.blank? || token.blank?
|
26
|
+
|
27
|
+
case user_class.query "token #{Figaro.env.hub_app_name} #{email} #{token}"
|
28
|
+
when 'no' then render_unauthorized
|
29
|
+
when 'invaild' then render_unauthorized
|
30
|
+
when 'sorry' then render_internal_server_error
|
31
|
+
when 'yes' then true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def http_headers
|
38
|
+
Hash[
|
39
|
+
request.headers.each
|
40
|
+
.select { |h| h[0] =~ /^HTTP/ }
|
41
|
+
.map { |h| [h[0].gsub(/^HTTP_/, ''), h[1]] }
|
42
|
+
]
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_unauthorized
|
46
|
+
Rails.logger.error "#{self.class.name} Token authentication unauthorized request.\n"\
|
47
|
+
"Params: #{JSON.pretty_generate(params)}\n"\
|
48
|
+
"Headers: #{JSON.pretty_generate(http_headers)}"
|
49
|
+
|
50
|
+
respond_to do |format|
|
51
|
+
format.json do
|
52
|
+
render status: :unauthorized,
|
53
|
+
json: { error: "Invalid or missing credentials" }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def render_internal_server_error
|
59
|
+
Rails.logger.error "#{self.class.name} Token authentication request resulted in error.\n"\
|
60
|
+
"Params: #{JSON.pretty_generate(params)}\n"\
|
61
|
+
"Headers: #{JSON.pretty_generate(http_headers)}"
|
62
|
+
|
63
|
+
respond_to do |format|
|
64
|
+
format.json do
|
65
|
+
render status: :internal_server_error,
|
66
|
+
json: { error: "Authentication server broke" }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Softwear
|
2
|
+
module ErrorCatcher
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
rescue_from Exception, StandardError, with: :error_report_form unless Rails.env.test?
|
7
|
+
end
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def iterm_annotation(content)
|
12
|
+
puts "=======================================================\033]1337;AddAnnotation=#{content}\a"
|
13
|
+
end
|
14
|
+
|
15
|
+
def error_report_form(error)
|
16
|
+
time_of_error = Time.now.strftime("%x %I:%M %p")
|
17
|
+
|
18
|
+
iterm_annotation("Begin #{error.class.name} (#{time_of_error})")
|
19
|
+
Rails.logger.error "**** #{error.class.name}: #{error.message} ****\n\n"\
|
20
|
+
"\t#{error.backtrace.join("\n\t")}"
|
21
|
+
iterm_annotation("End #{error.class.name} (#{time_of_error})")
|
22
|
+
|
23
|
+
@error = error
|
24
|
+
@additional_info = gather_additional_info
|
25
|
+
|
26
|
+
begin
|
27
|
+
respond_to do |format|
|
28
|
+
format.html { render 'softwear/errors/internal_server_error', layout: layout_for_error, status: 500 }
|
29
|
+
format.js { render 'softwear/errors/internal_server_error', layout: layout_for_error, status: 500 }
|
30
|
+
format.json { render json: '{}', status: 500 }
|
31
|
+
end
|
32
|
+
rescue AbstractController::DoubleRenderError => e
|
33
|
+
Rails.logger.error "DOUBLE RENDER ERROR IN CONTROLLER ERROR CATCHER!!! #{e.message}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def filter_params(params)
|
38
|
+
new_hash = {}
|
39
|
+
params.each do |key, value|
|
40
|
+
new_value = value
|
41
|
+
|
42
|
+
case key.to_s
|
43
|
+
when /cc_number/ then new_value = "<FILTERED>"
|
44
|
+
when /cc_cvc/ then new_value = "<FILTERED>"
|
45
|
+
when /password/ then new_value = "<FILTERED>"
|
46
|
+
end
|
47
|
+
|
48
|
+
new_hash[key] = new_value
|
49
|
+
end
|
50
|
+
new_hash
|
51
|
+
end
|
52
|
+
|
53
|
+
def gather_additional_info
|
54
|
+
JSON.pretty_generate(filter_params(params)) + "|||" +
|
55
|
+
instance_variables
|
56
|
+
.reject { |v| /^@_/ =~ v.to_s || %i(@view_renderer @output_buffer @view_flow @error).include?(v) }
|
57
|
+
.map { |v| "#{v}: #{instance_variable_get(v).inspect}" }
|
58
|
+
.join("|||")
|
59
|
+
end
|
60
|
+
|
61
|
+
def layout_for_error
|
62
|
+
current_user ? 'application' : 'no_overlay'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'inherited_resources'
|
2
|
+
|
3
|
+
module Softwear
|
4
|
+
module Library
|
5
|
+
class ApiController < ActionController::Base
|
6
|
+
include ::InheritedResources::Actions
|
7
|
+
include ::InheritedResources::BaseHelpers
|
8
|
+
extend ::InheritedResources::ClassMethods
|
9
|
+
extend ::InheritedResources::UrlHelpers
|
10
|
+
|
11
|
+
skip_before_filter :authenticate_user!
|
12
|
+
skip_before_filter :verify_authenticity_token
|
13
|
+
|
14
|
+
before_filter :allow_origin
|
15
|
+
|
16
|
+
respond_to :json
|
17
|
+
self.responder = InheritedResources::Responder
|
18
|
+
|
19
|
+
self.class_attribute :resource_class, :instance_writer => false unless self.respond_to? :resource_class
|
20
|
+
self.class_attribute :parents_symbols, :resources_configuration, :instance_writer => false
|
21
|
+
|
22
|
+
def self.base_class
|
23
|
+
Softwear::Library::ApiController
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.token_authenticate(user_class, options = {})
|
27
|
+
include Softwear::Auth::TokenAuthentication
|
28
|
+
self.user_class = user_class
|
29
|
+
self.token_auth_options = options
|
30
|
+
prepend_before_filter :token_authenticate_user!
|
31
|
+
end
|
32
|
+
|
33
|
+
def index(&block)
|
34
|
+
yield if block_given?
|
35
|
+
|
36
|
+
key_values = (permitted_attributes + [:id]).uniq.map do |a|
|
37
|
+
[a, params[a]] if params[a]
|
38
|
+
end
|
39
|
+
.compact
|
40
|
+
|
41
|
+
self.records ||= resource_class
|
42
|
+
self.records = records.where(Hash[key_values])
|
43
|
+
|
44
|
+
respond_to do |format|
|
45
|
+
format.json(&render_json(records))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def show
|
50
|
+
super do |format|
|
51
|
+
format.json(&render_json)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def update
|
56
|
+
self.record = resource_class.find(params[:id])
|
57
|
+
|
58
|
+
permitted_attributes.each do |a|
|
59
|
+
begin
|
60
|
+
record.send("#{a}=", params[a]) if params[a]
|
61
|
+
rescue ActiveRecord::AssociationTypeMismatch
|
62
|
+
# If you try to send "job" as an attribute to order, we're assuming it's
|
63
|
+
# not intentional. Send "job_attributes" or update the job model separately
|
64
|
+
# to actually update the job.
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
if record.save
|
69
|
+
respond_to { |format| format.json(&render_json) }
|
70
|
+
else
|
71
|
+
respond_to { |format| format.json(&render_errors) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def create
|
76
|
+
create! do |success, failure|
|
77
|
+
success.json do
|
78
|
+
headers['Location'] = resource_url(record.id)
|
79
|
+
render_json(status: 201).call
|
80
|
+
end
|
81
|
+
failure.json(&render_errors)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def options
|
86
|
+
head(:ok) if request.request_method == 'OPTIONS'
|
87
|
+
end
|
88
|
+
|
89
|
+
protected
|
90
|
+
|
91
|
+
def base_class
|
92
|
+
self.class.base_class
|
93
|
+
end
|
94
|
+
|
95
|
+
def render_json(options = {})
|
96
|
+
proc do
|
97
|
+
if options.is_a?(Hash)
|
98
|
+
records = nil
|
99
|
+
else
|
100
|
+
records = options
|
101
|
+
options = {}
|
102
|
+
end
|
103
|
+
rendering = {
|
104
|
+
json: (records || record),
|
105
|
+
methods: (['id'] + permitted_attributes).uniq,
|
106
|
+
include: includes
|
107
|
+
}
|
108
|
+
.merge(options)
|
109
|
+
|
110
|
+
render rendering
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def render_errors(options = {})
|
115
|
+
proc do
|
116
|
+
Rails.logger.error "API #{record.class.name} ERROR: #{record.errors.full_messages}"
|
117
|
+
|
118
|
+
rendering = {
|
119
|
+
json: { errors: record.errors },
|
120
|
+
status: :unprocessable_entity
|
121
|
+
}
|
122
|
+
.merge(options)
|
123
|
+
|
124
|
+
render rendering
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.model_name
|
129
|
+
name.gsub('Api::', '').gsub('Controller', '').singularize
|
130
|
+
end
|
131
|
+
|
132
|
+
# Override this to specify the :include option of rendering json.
|
133
|
+
def includes
|
134
|
+
{}
|
135
|
+
end
|
136
|
+
|
137
|
+
def records
|
138
|
+
instance_variable_get("@#{resource_class.model_name.collection}")
|
139
|
+
end
|
140
|
+
|
141
|
+
def records=(r)
|
142
|
+
instance_variable_set("@#{resource_class.model_name.collection}", r)
|
143
|
+
end
|
144
|
+
|
145
|
+
def record
|
146
|
+
instance_variable_get("@#{resource_class.model_name.element}")
|
147
|
+
end
|
148
|
+
|
149
|
+
def record=(r)
|
150
|
+
instance_variable_set("@#{resource_class.model_name.element}", r)
|
151
|
+
end
|
152
|
+
|
153
|
+
def allow_origin
|
154
|
+
headers['Access-Control-Allow-Origin'] = '*'
|
155
|
+
headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, PATCH, DELETE'
|
156
|
+
headers['Access-Control-Allow-Headers'] = 'X-Requested-With'
|
157
|
+
headers['Access-Control-Allow-Credentials'] = 'true'
|
158
|
+
end
|
159
|
+
|
160
|
+
def permitted_attributes
|
161
|
+
resource_class.column_names + ['state_event']
|
162
|
+
end
|
163
|
+
|
164
|
+
def permitted_params
|
165
|
+
{ resource_class.model_name.element => permitted_attributes }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Library
|
3
|
+
def self.capistrano(context)
|
4
|
+
context.instance_eval do
|
5
|
+
Rake::Task["deploy:compile_assets"].clear
|
6
|
+
|
7
|
+
ruby = fetch(:rvm_ruby_string) || fetch(:rvm_ruby_version)
|
8
|
+
|
9
|
+
namespace :deploy do
|
10
|
+
|
11
|
+
desc 'Assure softwear-lib is up to date before deploying'
|
12
|
+
task :update_softwear_lib do
|
13
|
+
on roles(:app), in: :sequence do
|
14
|
+
execute "~/.rvm/bin/rvm #{ruby} do gem install --no-ri --no-rdoc softwear-lib"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Signal the running rails app to restart'
|
19
|
+
task :restart do
|
20
|
+
on roles([:web, :app]), in: :sequence, wait: 5 do
|
21
|
+
execute :mkdir, '-p', "#{release_path}/tmp"
|
22
|
+
execute :touch, release_path.join('tmp/restart.txt')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
after :publishing, :restart_sidekiq_manager do
|
27
|
+
on roles(:sidekiq) do
|
28
|
+
execute 'sudo restart sidekiq-manager'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# This is more problematic than helpful
|
33
|
+
=begin
|
34
|
+
if !$LOAD_PATH.grep(/sunspot_solr/).empty? || fetch(:no_reindex)
|
35
|
+
before :publishing, :reindex_solr do
|
36
|
+
on roles(:db) do
|
37
|
+
with rails_env: fetch(:rails_env) || fetch(:stage) do
|
38
|
+
within(release_path) do
|
39
|
+
execute :rake, 'sunspot:solr:reindex'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
=end
|
46
|
+
|
47
|
+
# This is no longer necessary
|
48
|
+
# before :updating, :update_softwear_lib
|
49
|
+
after :publishing, :restart
|
50
|
+
|
51
|
+
desc 'Compile assets'
|
52
|
+
task :compile_assets => [:set_rails_env] do
|
53
|
+
invoke 'deploy:assets:precompile_local'
|
54
|
+
invoke 'deploy:assets:backup_manifest'
|
55
|
+
end
|
56
|
+
|
57
|
+
namespace :assets do
|
58
|
+
|
59
|
+
desc "Precompile assets locally and then rsync to web servers"
|
60
|
+
task :precompile_local do
|
61
|
+
# compile assets locally
|
62
|
+
run_locally do
|
63
|
+
execute "RAILS_ENV=#{fetch(:stage)} bundle exec rake assets:precompile"
|
64
|
+
end
|
65
|
+
|
66
|
+
# rsync to each server
|
67
|
+
assets_dir = "public/#{fetch(:assets_prefix) || 'assets'}"
|
68
|
+
local_dir = "./#{assets_dir}/"
|
69
|
+
on roles( fetch(:assets_roles, [:web]) ) do
|
70
|
+
# this needs to be done outside run_locally in order for host to exist
|
71
|
+
remote_dir = "#{host.user}@#{host.hostname}:#{release_path}/#{assets_dir}"
|
72
|
+
|
73
|
+
execute "mkdir -p #{release_path}/#{assets_dir}"
|
74
|
+
run_locally { execute "rsync -av --delete #{local_dir} #{remote_dir}" }
|
75
|
+
|
76
|
+
if fetch(:asset_sync)
|
77
|
+
with rails_env: fetch(:rails_env) || fetch(:stage) do
|
78
|
+
within(release_path) do
|
79
|
+
with_rvm(fetch(:task_ruby_version)) { execute :rake, 'assets:sync' }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# clean up
|
86
|
+
run_locally { execute "rm -rf #{local_dir}" }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Library
|
3
|
+
module ControllerAuthentication
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class NotSignedInError < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
included do
|
10
|
+
rescue_from NotSignedInError, with: :user_not_signed_in
|
11
|
+
rescue_from Softwear::Auth::Model::AuthServerDown, with: :auth_server_down
|
12
|
+
|
13
|
+
helper_method :current_user
|
14
|
+
helper_method :user_signed_in?
|
15
|
+
|
16
|
+
helper_method :destroy_user_session_path
|
17
|
+
helper_method :users_path
|
18
|
+
helper_method :user_path
|
19
|
+
helper_method :edit_user_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def user_class
|
23
|
+
if Softwear::Auth::Model.descendants.size > 1
|
24
|
+
raise "More than one descendent of Softwear::Auth::Model is not supported."
|
25
|
+
elsif Softwear::Auth::Model.descendants.size == 0
|
26
|
+
# Assume there is a "User" model
|
27
|
+
begin
|
28
|
+
User.inspect
|
29
|
+
return User if User.ancestors.include?(Softwear::Auth::Model)
|
30
|
+
rescue NameError => _
|
31
|
+
end
|
32
|
+
raise "Please define a user model that extends Softwear::Auth::Model."
|
33
|
+
end
|
34
|
+
Softwear::Auth::Model.descendants.first
|
35
|
+
end
|
36
|
+
|
37
|
+
# ====================
|
38
|
+
# Action called when a NotSignedInError is raised.
|
39
|
+
# ====================
|
40
|
+
def user_not_signed_in
|
41
|
+
if Softwear::Auth::Model::STUBBED
|
42
|
+
self.user_token = "dummy-token"
|
43
|
+
authenticate_user!
|
44
|
+
else
|
45
|
+
redirect_to softwear_hub_url + "/users/sign_in?#{{return_to: request.original_url}.to_param}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# ====================
|
50
|
+
# Action called when a NotSignedInError is raised.
|
51
|
+
# ====================
|
52
|
+
def auth_server_down(error)
|
53
|
+
respond_to do |format|
|
54
|
+
format.html do
|
55
|
+
render inline: \
|
56
|
+
"<div class='panel panel-danger'>"\
|
57
|
+
"<div class='panel-heading'>"\
|
58
|
+
"<h3 class='panel-title'>#{error.message}</h3>"\
|
59
|
+
"</div>"\
|
60
|
+
"<div class='panel-body'>"\
|
61
|
+
"Not all site functions will work until the problem is resolved. "\
|
62
|
+
"<a href='javascript' onclick='history.go(-1);return false;' class='btn btn-default'>Go back.</a>"\
|
63
|
+
"</div>"\
|
64
|
+
"</div>"
|
65
|
+
end
|
66
|
+
|
67
|
+
format.js do
|
68
|
+
render inline: "alert(\"#{error.message.gsub('"', '\"')}\");"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# ====================
|
74
|
+
# Drop this into a before_filter to require a user be signed in on every request -
|
75
|
+
# just like in Devise.
|
76
|
+
# ====================
|
77
|
+
def authenticate_user!
|
78
|
+
if user_token.blank?
|
79
|
+
raise NotSignedInError, "No token"
|
80
|
+
end
|
81
|
+
|
82
|
+
if user = user_class.auth(user_token)
|
83
|
+
@current_user = user
|
84
|
+
else
|
85
|
+
self.user_token = nil
|
86
|
+
raise NotSignedInError, "Invalid token"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def current_user
|
91
|
+
return @current_user if @current_user
|
92
|
+
|
93
|
+
if @current_user.nil? && !user_token.blank?
|
94
|
+
@current_user = user_class.auth(user_token)
|
95
|
+
else
|
96
|
+
nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def user_signed_in?
|
101
|
+
!!current_user
|
102
|
+
end
|
103
|
+
|
104
|
+
# -- url uelpers --
|
105
|
+
|
106
|
+
def softwear_hub_url
|
107
|
+
if Rails.env.production?
|
108
|
+
Figaro.env.softwear_hub_url || (raise "Please set softwear_hub_url in application.yml")
|
109
|
+
elsif Rails.env.test?
|
110
|
+
'http://hub.example.com'
|
111
|
+
else
|
112
|
+
Figaro.env.softwear_hub_url || 'http://localhost:2995'
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def destroy_user_session_path
|
117
|
+
softwear_hub_url + "/users/sign_out"
|
118
|
+
end
|
119
|
+
|
120
|
+
def user_path(user)
|
121
|
+
user_id = user.is_a?(user_class) ? user.id : user
|
122
|
+
softwear_hub_url + "/users/#{user_id}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def edit_user_path(user)
|
126
|
+
user_id = user.is_a?(user_class) ? user.id : user
|
127
|
+
softwear_hub_url + "/users/#{user_id}/edit"
|
128
|
+
end
|
129
|
+
|
130
|
+
def users_path
|
131
|
+
softwear_hub_url + "/users"
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def user_token
|
137
|
+
session[:user_token]
|
138
|
+
end
|
139
|
+
|
140
|
+
def user_token=(new_token)
|
141
|
+
session[:user_token] = new_token
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|