softwear 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|