softwear-lib 1.4.3 → 1.5.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 +4 -4
- data/lib/softwear/auth/belongs_to_user.rb +32 -0
- data/lib/softwear/auth/controller.rb +39 -0
- data/lib/softwear/auth/helper.rb +27 -0
- data/lib/softwear/auth/model.rb +408 -0
- data/lib/softwear/lib.rb +5 -0
- data/lib/softwear/lib/authentication.rb +103 -0
- data/lib/softwear/lib/controller_authentication.rb +103 -0
- data/lib/softwear/lib/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07abbcd1086c6337273c1b6536c2f3ada2c817ca
|
4
|
+
data.tar.gz: afbd2b01e668364fd40d18e5f047a2297a824083
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44716bcb3a8898364f4092328fc8c59217eb834768678204395dc469316d1b0b28ce27437f6df7ec650b7d15ecf5075ac004f4697d312611b2b77d16f97bee7f
|
7
|
+
data.tar.gz: 4d54bb27f4ece39a7132503fa59759eca15fc39c337d8d1f16f513cad1367d7bf5a10f5a6cd631432af9b612fb122c1e3c02886c2c0074a94f35b31a74da6bad
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Auth
|
3
|
+
module BelongsToUser
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def belongs_to_user_called(name, options = {})
|
8
|
+
foreign_key = "#{name}_id"
|
9
|
+
|
10
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
11
|
+
def #{name}
|
12
|
+
@#{name} ||= User.find(#{name}_id)
|
13
|
+
end
|
14
|
+
|
15
|
+
def #{name}=(new)
|
16
|
+
self.#{foreign_key} = new.id
|
17
|
+
@#{name} = new
|
18
|
+
end
|
19
|
+
RUBY
|
20
|
+
end
|
21
|
+
|
22
|
+
def belongs_to_user
|
23
|
+
belongs_to_user_called(:user)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
included do
|
28
|
+
extend ClassMethods
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Auth
|
3
|
+
class Controller < ApplicationController
|
4
|
+
skip_before_filter :authenticate_user!, only: [:set_session_token, :clear_query_cache]
|
5
|
+
|
6
|
+
# ====================
|
7
|
+
# Comes from an img tag on softwear-hub to let an authorized app know that
|
8
|
+
# a user has signed in.
|
9
|
+
# ====================
|
10
|
+
def set_session_token
|
11
|
+
encrypted_token = params[:token]
|
12
|
+
redirect_to Figaro.env.softwear_hub_url and return if encrypted_token.blank?
|
13
|
+
|
14
|
+
Rails.logger.info "RECEIVED ENCRYPTED TOKEN: #{encrypted_token}"
|
15
|
+
|
16
|
+
decipher = OpenSSL::Cipher::AES.new(256, :CBC)
|
17
|
+
decipher.decrypt
|
18
|
+
decipher.key = Figaro.env.token_cipher_key
|
19
|
+
decipher.iv = Figaro.env.token_cipher_iv
|
20
|
+
|
21
|
+
session[:user_token] = decipher.update(Base64.urlsafe_decode64(encrypted_token)) + decipher.final
|
22
|
+
|
23
|
+
render inline: 'Done'
|
24
|
+
end
|
25
|
+
|
26
|
+
# ====================
|
27
|
+
# Comes from an img tag on softwear-hub when there has been a change to user
|
28
|
+
# attributes or roles and the cache should be cleared.
|
29
|
+
# ====================
|
30
|
+
def clear_query_cache
|
31
|
+
Softwear::Auth::Model.descendants.each do |user|
|
32
|
+
user.query_cache.clear
|
33
|
+
end
|
34
|
+
|
35
|
+
render inline: 'Done'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Auth
|
3
|
+
module Helper
|
4
|
+
def profile_picture_of(user = nil, options = {})
|
5
|
+
options[:class] ||= ''
|
6
|
+
options[:class] += ' media-object img-circle profile-pic'
|
7
|
+
options[:alt] ||= "#{user.try(:full_name) || '(Unknown)'}'s Avatar"
|
8
|
+
options[:title] ||= user.try(:full_name) || 'Someone'
|
9
|
+
|
10
|
+
image_tag user.try(:profile_picture_url) || 'avatar/masarie.jpg', options
|
11
|
+
end
|
12
|
+
|
13
|
+
def auth_server_error_banner
|
14
|
+
return unless Softwear::Auth::Model.auth_server_down?
|
15
|
+
|
16
|
+
content_tag :div, class: 'alert alert-danger' do
|
17
|
+
content_tag :strong do
|
18
|
+
(Softwear::Auth::Model.auth_server_went_down_at || Time.now).strftime(
|
19
|
+
"WARNING: The authentication server is unreachable as of %I:%M%p. "\
|
20
|
+
"Some site features might not function properly."
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,408 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Auth
|
3
|
+
class Model
|
4
|
+
include ActiveModel::Model
|
5
|
+
include ActiveModel::Conversion
|
6
|
+
|
7
|
+
class AccessDeniedError < StandardError
|
8
|
+
end
|
9
|
+
class InvalidCommandError < StandardError
|
10
|
+
end
|
11
|
+
class AuthServerError < StandardError
|
12
|
+
end
|
13
|
+
class AuthServerDown < StandardError
|
14
|
+
end
|
15
|
+
|
16
|
+
# ============================= CLASS METHODS ======================
|
17
|
+
class << self
|
18
|
+
attr_writer :query_cache
|
19
|
+
attr_accessor :total_query_cache
|
20
|
+
attr_writer :query_cache_expiry
|
21
|
+
alias_method :expire_query_cache_every, :query_cache_expiry=
|
22
|
+
attr_accessor :auth_server_went_down_at
|
23
|
+
attr_accessor :sent_auth_server_down_email
|
24
|
+
attr_accessor :time_before_down_email
|
25
|
+
alias_method :email_when_down_after, :time_before_down_email=
|
26
|
+
|
27
|
+
# ====================
|
28
|
+
# Returns true if the authentication server was unreachable for the previous query.
|
29
|
+
# ====================
|
30
|
+
def auth_server_down?
|
31
|
+
!!auth_server_went_down_at
|
32
|
+
end
|
33
|
+
|
34
|
+
# ====================
|
35
|
+
# The query cache takes message keys (such as "get 12") with response values straight from
|
36
|
+
# the server. So yes, this will cache error responses.
|
37
|
+
# You can clear this with <User Class>.query_cache.clear or <User Class>.query_cache = nil
|
38
|
+
# ====================
|
39
|
+
def query_cache
|
40
|
+
@query_cache ||= ThreadSafe::Cache.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def query_cache_expiry
|
44
|
+
@query_cache_expiry || Figaro.env.query_cache_expiry.try(:to_f) || 1.hour
|
45
|
+
end
|
46
|
+
|
47
|
+
# ===================
|
48
|
+
# Override this in your subclasses! The mailer should have auth_server_down(time) and
|
49
|
+
# auth_server_up(time)
|
50
|
+
# ===================
|
51
|
+
def auth_server_down_mailer
|
52
|
+
# override me
|
53
|
+
end
|
54
|
+
|
55
|
+
# ======================================
|
56
|
+
def primary_key
|
57
|
+
:id
|
58
|
+
end
|
59
|
+
|
60
|
+
def base_class
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def relation_delegate_class(*)
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def unscoped
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def new(*args)
|
73
|
+
if args.size == 3
|
74
|
+
assoc_class = args[2].owner.class.name
|
75
|
+
assoc_name = args[2].reflection.name
|
76
|
+
raise "Unsupported user association: #{assoc_class}##{assoc_name}. If this is a belongs_to "\
|
77
|
+
"association, you may have #{assoc_class} include Softwear::Auth::BelongsToUser and call "\
|
78
|
+
"`belongs_to_user_called :#{assoc_name}' instead of the traditional rails method."
|
79
|
+
else
|
80
|
+
super
|
81
|
+
end
|
82
|
+
end
|
83
|
+
# ======================================
|
84
|
+
|
85
|
+
# ====================
|
86
|
+
# Not a fully featured has_many - must specify foreign_key if the association doesn't match
|
87
|
+
# the model name.
|
88
|
+
# ====================
|
89
|
+
def has_many(assoc, options = {})
|
90
|
+
assoc = assoc.to_s
|
91
|
+
|
92
|
+
class_name = options[:class_name] || assoc.singularize.camelize
|
93
|
+
foreign_key = options[:foreign_key] || 'user_id'
|
94
|
+
|
95
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
96
|
+
def #{assoc}
|
97
|
+
#{class_name}.where(#{foreign_key}: id)
|
98
|
+
end
|
99
|
+
RUBY
|
100
|
+
end
|
101
|
+
|
102
|
+
def arel_table
|
103
|
+
@arel_table ||= Arel::Table.new(model_name.plural, self)
|
104
|
+
end
|
105
|
+
|
106
|
+
# ====================
|
107
|
+
# This is only used to record how long it takes to perform queries for development.
|
108
|
+
# ====================
|
109
|
+
def record(before, after, type, body)
|
110
|
+
ms = (after - before) * 1000
|
111
|
+
# The garbage in this string gives us the bold and color
|
112
|
+
Rails.logger.info " \033[1m\033[33m#{type} (#{'%.1f' % ms}ms)\033[0m #{body}"
|
113
|
+
end
|
114
|
+
|
115
|
+
# ====================
|
116
|
+
# Host of the auth server, from 'auth_server_endpoint' env variable.
|
117
|
+
# Defaults to localhost.
|
118
|
+
# ====================
|
119
|
+
def auth_server_host
|
120
|
+
endpoint = Figaro.env.auth_server_endpoint
|
121
|
+
if endpoint.blank?
|
122
|
+
'localhost'
|
123
|
+
elsif endpoint.include?(':')
|
124
|
+
endpoint.split(':').first
|
125
|
+
else
|
126
|
+
endpoint
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# ====================
|
131
|
+
# Port of the auth server, from 'auth_server_endpoint' env variable.
|
132
|
+
# Defaults to 2900.
|
133
|
+
# ====================
|
134
|
+
def auth_server_port
|
135
|
+
endpoint = Figaro.env.auth_server_endpoint
|
136
|
+
if endpoint.try(:include?, ':')
|
137
|
+
endpoint.split(':').last
|
138
|
+
else
|
139
|
+
2900
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def default_socket
|
144
|
+
@default_socket ||= TCPSocket.open(auth_server_host, auth_server_port)
|
145
|
+
end
|
146
|
+
|
147
|
+
# ====================
|
148
|
+
# Bare minimum query function - sends a message and returns the response, and
|
149
|
+
# handles a broken socket. #query and #force_query call this function.
|
150
|
+
# ====================
|
151
|
+
def raw_query(message)
|
152
|
+
begin
|
153
|
+
default_socket.puts message
|
154
|
+
|
155
|
+
rescue Errno::EPIPE => e
|
156
|
+
@default_socket = TCPSocket.open(auth_server_host, auth_server_port)
|
157
|
+
@default_socket.puts message
|
158
|
+
end
|
159
|
+
|
160
|
+
return default_socket.gets.chomp
|
161
|
+
|
162
|
+
rescue Errno::ECONNREFUSED => e
|
163
|
+
raise AuthServerDown, "Unable to connect to the authentication server."
|
164
|
+
end
|
165
|
+
|
166
|
+
# ====================
|
167
|
+
# Expires the query cache, setting a new expiration time as well as merging
|
168
|
+
# with the previous query cache, in case of an auth server outage.
|
169
|
+
# ====================
|
170
|
+
def expire_query_cache
|
171
|
+
before = Time.now
|
172
|
+
if total_query_cache
|
173
|
+
query_cache.each_pair do |key, value|
|
174
|
+
total_query_cache[key] = value
|
175
|
+
end
|
176
|
+
else
|
177
|
+
self.total_query_cache = query_cache.clone
|
178
|
+
end
|
179
|
+
|
180
|
+
query_cache.clear
|
181
|
+
query_cache['_expire_at'] = (query_cache_expiry || 1.hour).from_now
|
182
|
+
after = Time.now
|
183
|
+
|
184
|
+
record(before, after, "Authentication Expire Cache", "")
|
185
|
+
end
|
186
|
+
|
187
|
+
# ====================
|
188
|
+
# Queries the authentication server only if there isn't a cached response.
|
189
|
+
# Also keeps track of whether or not the server is reachable, and sends emails
|
190
|
+
# when the server goes down and back up.
|
191
|
+
# ====================
|
192
|
+
def query(message)
|
193
|
+
before = Time.now
|
194
|
+
|
195
|
+
expire_at = query_cache['_expire_at']
|
196
|
+
expire_query_cache if expire_at.blank? || Time.now > expire_at
|
197
|
+
|
198
|
+
if cached_response = query_cache[message]
|
199
|
+
response = cached_response
|
200
|
+
action = "Authentication Cache"
|
201
|
+
else
|
202
|
+
begin
|
203
|
+
response = raw_query(message)
|
204
|
+
action = "Authentication Query"
|
205
|
+
query_cache[message] = response
|
206
|
+
|
207
|
+
if auth_server_went_down_at
|
208
|
+
self.auth_server_went_down_at = nil
|
209
|
+
|
210
|
+
if sent_auth_server_down_email
|
211
|
+
self.sent_auth_server_down_email = false
|
212
|
+
if (mailer = auth_server_down_mailer) && mailer.respond_to?(:auth_server_up)
|
213
|
+
mailer.auth_server_up(Time.now).deliver_now
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
rescue AuthServerError => e
|
219
|
+
raise unless total_query_cache
|
220
|
+
|
221
|
+
old_response = total_query_cache[message]
|
222
|
+
if old_response
|
223
|
+
response = old_response
|
224
|
+
action = "Authentication Cache (due to error)"
|
225
|
+
Rails.logger.error "AUTHENTICATION: The authentication server encountered an error. "\
|
226
|
+
"You should probably check the auth server's logs. "\
|
227
|
+
"A cached response was used."
|
228
|
+
else
|
229
|
+
raise
|
230
|
+
end
|
231
|
+
|
232
|
+
rescue AuthServerDown => e
|
233
|
+
if auth_server_went_down_at.nil?
|
234
|
+
self.auth_server_went_down_at = Time.now
|
235
|
+
expire_query_cache
|
236
|
+
|
237
|
+
elsif auth_server_went_down_at > (time_before_down_email || 5.minutes).ago
|
238
|
+
unless sent_auth_server_down_email
|
239
|
+
self.sent_auth_server_down_email = true
|
240
|
+
|
241
|
+
if (mailer = auth_server_down_mailer) && mailer.respond_to?(:auth_server_down)
|
242
|
+
mailer.auth_server_down(auth_server_went_down_at).deliver_now
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
old_response = total_query_cache[message]
|
248
|
+
if old_response
|
249
|
+
response = old_response
|
250
|
+
action = "Authentication Cache (server down)"
|
251
|
+
else
|
252
|
+
raise AuthServerDown, "An uncached query was attempted, and the authentication server is down."
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
after = Time.now
|
257
|
+
|
258
|
+
record(before, after, action, message)
|
259
|
+
response
|
260
|
+
end
|
261
|
+
|
262
|
+
# ====================
|
263
|
+
# Runs a query through the server without error or cache checking.
|
264
|
+
# ====================
|
265
|
+
def force_query(message)
|
266
|
+
before = Time.now
|
267
|
+
response = raw_query(message)
|
268
|
+
after = Time.now
|
269
|
+
|
270
|
+
record(before, after, "Authentication Query (forced)", message)
|
271
|
+
response
|
272
|
+
end
|
273
|
+
|
274
|
+
# ====================
|
275
|
+
# Expects a response string returned from #query and raises an error for the
|
276
|
+
# following cases:
|
277
|
+
#
|
278
|
+
# - Access denied (AccessDeniedError)
|
279
|
+
# - Invalid command (bad query message) (InvalidCommandError)
|
280
|
+
# - Error on auth server's side (AuthServerError)
|
281
|
+
# ====================
|
282
|
+
def validate_response(response_string)
|
283
|
+
case response_string
|
284
|
+
when 'denied' then raise AccessDeniedError, "Denied"
|
285
|
+
when 'invalid' then raise InvalidCommandError, "Invalid command"
|
286
|
+
when 'sorry'
|
287
|
+
expire_query_cache
|
288
|
+
raise AuthServerError, "Authentication server encountered an error"
|
289
|
+
else
|
290
|
+
response_string
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# ====================
|
295
|
+
# Finds a user with the given ID
|
296
|
+
# ====================
|
297
|
+
def find(target_id)
|
298
|
+
json = validate_response query "get #{target_id}"
|
299
|
+
|
300
|
+
if json == 'nosuchuser'
|
301
|
+
nil
|
302
|
+
else
|
303
|
+
object = new(JSON.parse(json))
|
304
|
+
object.instance_variable_set(:@persisted, true)
|
305
|
+
object
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# ====================
|
310
|
+
# Returns an array of all registered users
|
311
|
+
# ====================
|
312
|
+
def all
|
313
|
+
json = validate_response query "all"
|
314
|
+
|
315
|
+
objects = JSON.parse(json).map(&method(:new))
|
316
|
+
objects.each { |u| u.instance_variable_set(:@persisted, true) }
|
317
|
+
objects
|
318
|
+
end
|
319
|
+
|
320
|
+
# ====================
|
321
|
+
# Given a valid signin token:
|
322
|
+
# Returns the authenticated user for the given token
|
323
|
+
# Given an invalid signin token:
|
324
|
+
# Returns false
|
325
|
+
# ====================
|
326
|
+
def auth(token)
|
327
|
+
response = validate_response query "auth #{Figaro.env.hub_app_name} #{token}"
|
328
|
+
|
329
|
+
return false unless response =~ /^yes .+$/
|
330
|
+
|
331
|
+
_yes, json = response.split(' ', 2)
|
332
|
+
object = new(JSON.parse(json))
|
333
|
+
object.instance_variable_set(:@persisted, true)
|
334
|
+
object
|
335
|
+
end
|
336
|
+
|
337
|
+
# ====================
|
338
|
+
# Overridable logger method used when recording query benchmarks
|
339
|
+
# ====================
|
340
|
+
def logger
|
341
|
+
Rails.logger
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# ============================= INSTANCE METHODS ======================
|
346
|
+
|
347
|
+
REMOTE_ATTRIBUTES = [
|
348
|
+
:id, :email, :first_name, :last_name,
|
349
|
+
:profile_picture_url
|
350
|
+
]
|
351
|
+
REMOTE_ATTRIBUTES.each(&method(:attr_accessor))
|
352
|
+
|
353
|
+
attr_reader :persisted
|
354
|
+
alias_method :persisted?, :persisted
|
355
|
+
|
356
|
+
# ====================
|
357
|
+
# Various class methods accessible on instances
|
358
|
+
def query(*a)
|
359
|
+
self.class.query(*a)
|
360
|
+
end
|
361
|
+
def raw_query(*a)
|
362
|
+
self.class.raw_query(*a)
|
363
|
+
end
|
364
|
+
def force_query(*a)
|
365
|
+
self.class.force_query(*a)
|
366
|
+
end
|
367
|
+
def logger
|
368
|
+
self.class.logger
|
369
|
+
end
|
370
|
+
# ====================
|
371
|
+
|
372
|
+
def initialize(attributes = {})
|
373
|
+
update_attributes(attributes)
|
374
|
+
end
|
375
|
+
|
376
|
+
def update_attributes(attributes={})
|
377
|
+
return if attributes.blank?
|
378
|
+
attributes = attributes.with_indifferent_access
|
379
|
+
|
380
|
+
REMOTE_ATTRIBUTES.each do |attr|
|
381
|
+
instance_variable_set("@#{attr}", attributes[attr])
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
def to_json
|
386
|
+
{
|
387
|
+
id: @id,
|
388
|
+
email: @email,
|
389
|
+
first_name: @first_name,
|
390
|
+
last_name: @last_name
|
391
|
+
}
|
392
|
+
.to_json
|
393
|
+
end
|
394
|
+
|
395
|
+
def reload
|
396
|
+
json = validate_response query "get #{id}"
|
397
|
+
|
398
|
+
update_attributes(JSON.parse(json))
|
399
|
+
@persisted = true
|
400
|
+
self
|
401
|
+
end
|
402
|
+
|
403
|
+
def full_name
|
404
|
+
"#{@first_name} #{@last_name}"
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
data/lib/softwear/lib.rb
CHANGED
@@ -2,6 +2,11 @@ require "softwear/lib/version"
|
|
2
2
|
require "softwear/lib/spec"
|
3
3
|
require "softwear/lib/api_controller"
|
4
4
|
|
5
|
+
require "softwear/lib/controller_authentication"
|
6
|
+
require "softwear/auth/helper"
|
7
|
+
require "softwear/auth/model"
|
8
|
+
require "softwear/auth/belongs_to_user"
|
9
|
+
|
5
10
|
module Softwear
|
6
11
|
module Lib
|
7
12
|
GEMFILE_OPENER = "# === BEGIN SOFTWEAR LIB GEMS === #"
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Lib
|
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
|
+
raise "Please define a user model that extends Softwear::Auth::Model."
|
27
|
+
end
|
28
|
+
Softwear::Auth::Model.descendants.first
|
29
|
+
end
|
30
|
+
|
31
|
+
# ====================
|
32
|
+
# Action called when a NotSignedInError is raised.
|
33
|
+
# ====================
|
34
|
+
def user_not_signed_in
|
35
|
+
redirect_to Figaro.env.softwear_hub_url + "/users/sign_in?#{{return_to: request.original_url}.to_param}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# ====================
|
39
|
+
# Action called when a NotSignedInError is raised.
|
40
|
+
# ====================
|
41
|
+
def auth_server_down(error)
|
42
|
+
respond_to do |format|
|
43
|
+
format.html do
|
44
|
+
render inline: \
|
45
|
+
"<h1>#{error.message}</h1><div>Not all site functions will work until the problem is resolved. "\
|
46
|
+
"<a href='javascripr' onclick='history.go(-1);return false;' class='btn btn-default'>Go back.</a></div>"
|
47
|
+
end
|
48
|
+
|
49
|
+
format.js do
|
50
|
+
render inline: "alert(\"#{error.message.gsub('"', '\"')}\");"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# ====================
|
56
|
+
# Drop this into a before_filter to require a user be signed in on every request -
|
57
|
+
# just like in Devise.
|
58
|
+
# ====================
|
59
|
+
def authenticate_user!
|
60
|
+
token = session[:user_token]
|
61
|
+
|
62
|
+
if token.blank?
|
63
|
+
raise NotSignedInError, "No token"
|
64
|
+
end
|
65
|
+
|
66
|
+
if user = user_class.auth(token)
|
67
|
+
@current_user = user
|
68
|
+
else
|
69
|
+
session[:user_token] = nil
|
70
|
+
raise NotSignedInError, "Invalid token"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def current_user
|
75
|
+
@current_user
|
76
|
+
end
|
77
|
+
|
78
|
+
def user_signed_in?
|
79
|
+
!@current_user.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
# -- url uelpers --
|
83
|
+
|
84
|
+
def destroy_user_session_path
|
85
|
+
Figaro.env.softwear_hub_url + "/users/sign_out"
|
86
|
+
end
|
87
|
+
|
88
|
+
def user_path(user)
|
89
|
+
user_id = user.is_a?(user_class) ? user.id : user
|
90
|
+
Figaro.env.softwear_hub_url + "/users/#{user_id}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def edit_user_path(user)
|
94
|
+
user_id = user.is_a?(user_class) ? user.id : user
|
95
|
+
Figaro.env.softwear_hub_url + "/users/#{user_id}/edit"
|
96
|
+
end
|
97
|
+
|
98
|
+
def users_path
|
99
|
+
Figaro.env.softwear_hub_url + "/users"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Softwear
|
2
|
+
module Lib
|
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
|
+
raise "Please define a user model that extends Softwear::Auth::Model."
|
27
|
+
end
|
28
|
+
Softwear::Auth::Model.descendants.first
|
29
|
+
end
|
30
|
+
|
31
|
+
# ====================
|
32
|
+
# Action called when a NotSignedInError is raised.
|
33
|
+
# ====================
|
34
|
+
def user_not_signed_in
|
35
|
+
redirect_to Figaro.env.softwear_hub_url + "/users/sign_in?#{{return_to: request.original_url}.to_param}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# ====================
|
39
|
+
# Action called when a NotSignedInError is raised.
|
40
|
+
# ====================
|
41
|
+
def auth_server_down(error)
|
42
|
+
respond_to do |format|
|
43
|
+
format.html do
|
44
|
+
render inline: \
|
45
|
+
"<h1>#{error.message}</h1><div>Not all site functions will work until the problem is resolved. "\
|
46
|
+
"<a href='javascripr' onclick='history.go(-1);return false;' class='btn btn-default'>Go back.</a></div>"
|
47
|
+
end
|
48
|
+
|
49
|
+
format.js do
|
50
|
+
render inline: "alert(\"#{error.message.gsub('"', '\"')}\");"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# ====================
|
56
|
+
# Drop this into a before_filter to require a user be signed in on every request -
|
57
|
+
# just like in Devise.
|
58
|
+
# ====================
|
59
|
+
def authenticate_user!
|
60
|
+
token = session[:user_token]
|
61
|
+
|
62
|
+
if token.blank?
|
63
|
+
raise NotSignedInError, "No token"
|
64
|
+
end
|
65
|
+
|
66
|
+
if user = user_class.auth(token)
|
67
|
+
@current_user = user
|
68
|
+
else
|
69
|
+
session[:user_token] = nil
|
70
|
+
raise NotSignedInError, "Invalid token"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def current_user
|
75
|
+
@current_user
|
76
|
+
end
|
77
|
+
|
78
|
+
def user_signed_in?
|
79
|
+
!@current_user.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
# -- url uelpers --
|
83
|
+
|
84
|
+
def destroy_user_session_path
|
85
|
+
Figaro.env.softwear_hub_url + "/users/sign_out"
|
86
|
+
end
|
87
|
+
|
88
|
+
def user_path(user)
|
89
|
+
user_id = user.is_a?(user_class) ? user.id : user
|
90
|
+
Figaro.env.softwear_hub_url + "/users/#{user_id}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def edit_user_path(user)
|
94
|
+
user_id = user.is_a?(user_class) ? user.id : user
|
95
|
+
Figaro.env.softwear_hub_url + "/users/#{user_id}/edit"
|
96
|
+
end
|
97
|
+
|
98
|
+
def users_path
|
99
|
+
Figaro.env.softwear_hub_url + "/users"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/softwear/lib/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: softwear-lib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nigel Baillie
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -83,8 +83,14 @@ files:
|
|
83
83
|
- Rakefile
|
84
84
|
- bin/softwear
|
85
85
|
- bin/softwear-deploy
|
86
|
+
- lib/softwear/auth/belongs_to_user.rb
|
87
|
+
- lib/softwear/auth/controller.rb
|
88
|
+
- lib/softwear/auth/helper.rb
|
89
|
+
- lib/softwear/auth/model.rb
|
86
90
|
- lib/softwear/lib.rb
|
87
91
|
- lib/softwear/lib/api_controller.rb
|
92
|
+
- lib/softwear/lib/authentication.rb
|
93
|
+
- lib/softwear/lib/controller_authentication.rb
|
88
94
|
- lib/softwear/lib/spec.rb
|
89
95
|
- lib/softwear/lib/version.rb
|
90
96
|
- softwear-lib.gemspec
|