hubssolib 3.7.1 → 3.8.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/CHANGELOG.md +7 -1
- data/Gemfile.lock +163 -3
- data/README.md +21 -0
- data/hubssolib.gemspec +2 -1
- data/lib/hub_sso_lib.rb +218 -87
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 32438e5c41d655367f4bb0e8385b9d972645f2649871a09ab7b09dc5d617fe9e
|
4
|
+
data.tar.gz: a999f01f88325690644675acc1b06b33b7b275374e980c6c3f2fa346c3875d93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 881e57fa9c2e42ed8465b2faabf98cabdacf2dcea5df20d3c617836e50ff74d2255bbe1b8c11ca39b0bb3d57f2927e229ca1a390de11d0fcff9b8e4b32851ae6
|
7
|
+
data.tar.gz: 15aa2e97054fe9a8a86e45d901bdfa36bff0d0a6473ba88d279ec0bf622ce8413e6bc1a7b0427bac1f0e2bc0c2b994d9a3550d48897a8efec7cf2acf0c74abfa
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 3.8.0, 03-Apr-2025
|
2
|
+
|
3
|
+
* Adds helper `HubSsoLib::Core#hubssolib_flash_markup` to compliment `HubSsoLib::Core#hubssolib_flash_data` and help make Flash presentation simpler and more consistent across integrated applications.
|
4
|
+
* Deprecates core methods `hubssolib_store_location` (which is now a no-op) and `hubssolib_redirect_back_or_default` (which now always redirects to the default location). **These methods will be removed in Hub 4**. Ephemeral return-to location storage has only worked when sessions are _on_, which once upon a time was always - logged in or not - but changed in Hub 3.4.0, to avoid very large numbers of anonymous sessions building up into a very RAM-hungry pools in the DRb server. This has in hindsight amounted to a breaking change, and I should've bumped to Hub 4 per SemVer at that time, but it was overlooked; apologies. At least in general all it really usually means is that, instead of redirecting back to some calling application after login, the user is left in their Hub account view.
|
5
|
+
* Hub's UI element added to navigation bars by using `HubSsoLib::Core#hubssolib_account_link` plus permissions and must-log-in checking in the `HubSsoLib::Core#hubssolib_beforehand` before-action callback already dealt with and continue to deal with redirect-to-origin automatically, so often, applications did't need to store a location and then manually redirect to Hub anyway. If you do need this for some reason, though, the new workflow is create a link or redirection to the URL returned by `HubSsoLib::Core#hubssolib_returnable_account_url`. This simply encodes a return-to address in the URL where needed, removing any requirement for cross-application session storage. The method's documentation describes its uses but, for the majority of use cases, you'll probably only want to be presenting a link for the case where a user must log _in_. In that case, call `hubssolib_returnable_account_url(logged_in: false)`.
|
6
|
+
|
1
7
|
## 3.7.0 and 3.7.1, 28-Mar-2025
|
2
8
|
|
3
9
|
* Login indicator cookie wasn't updated on session timeout, so when the page loaded for pages that do *not* require authorisation, the warning flash about being timed out would show, but the indicator would still show a "logged in" state until the next page fetch.
|
@@ -85,7 +91,7 @@ In Hub v1 and v2, login indication was done via an image that was served by the
|
|
85
91
|
|
86
92
|
```html
|
87
93
|
<a class="img" href="<%= ENV['HUB_PATH_PREFIX'] %>/account/login_conditional">
|
88
|
-
<img src="<%= ENV['HUB_PATH_PREFIX'] %>/account/login_indication" alt="Account" height="22" width="90"
|
94
|
+
<img src="<%= ENV['HUB_PATH_PREFIX'] %>/account/login_indication" alt="Account" height="22" width="90">
|
89
95
|
</a>
|
90
96
|
```
|
91
97
|
|
data/Gemfile.lock
CHANGED
@@ -1,14 +1,91 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
hubssolib (3.
|
4
|
+
hubssolib (3.8.0)
|
5
5
|
base64 (~> 0.2)
|
6
6
|
drb (~> 2.2)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
actioncable (8.0.2)
|
12
|
+
actionpack (= 8.0.2)
|
13
|
+
activesupport (= 8.0.2)
|
14
|
+
nio4r (~> 2.0)
|
15
|
+
websocket-driver (>= 0.6.1)
|
16
|
+
zeitwerk (~> 2.6)
|
17
|
+
actionmailbox (8.0.2)
|
18
|
+
actionpack (= 8.0.2)
|
19
|
+
activejob (= 8.0.2)
|
20
|
+
activerecord (= 8.0.2)
|
21
|
+
activestorage (= 8.0.2)
|
22
|
+
activesupport (= 8.0.2)
|
23
|
+
mail (>= 2.8.0)
|
24
|
+
actionmailer (8.0.2)
|
25
|
+
actionpack (= 8.0.2)
|
26
|
+
actionview (= 8.0.2)
|
27
|
+
activejob (= 8.0.2)
|
28
|
+
activesupport (= 8.0.2)
|
29
|
+
mail (>= 2.8.0)
|
30
|
+
rails-dom-testing (~> 2.2)
|
31
|
+
actionpack (8.0.2)
|
32
|
+
actionview (= 8.0.2)
|
33
|
+
activesupport (= 8.0.2)
|
34
|
+
nokogiri (>= 1.8.5)
|
35
|
+
rack (>= 2.2.4)
|
36
|
+
rack-session (>= 1.0.1)
|
37
|
+
rack-test (>= 0.6.3)
|
38
|
+
rails-dom-testing (~> 2.2)
|
39
|
+
rails-html-sanitizer (~> 1.6)
|
40
|
+
useragent (~> 0.16)
|
41
|
+
actiontext (8.0.2)
|
42
|
+
actionpack (= 8.0.2)
|
43
|
+
activerecord (= 8.0.2)
|
44
|
+
activestorage (= 8.0.2)
|
45
|
+
activesupport (= 8.0.2)
|
46
|
+
globalid (>= 0.6.0)
|
47
|
+
nokogiri (>= 1.8.5)
|
48
|
+
actionview (8.0.2)
|
49
|
+
activesupport (= 8.0.2)
|
50
|
+
builder (~> 3.1)
|
51
|
+
erubi (~> 1.11)
|
52
|
+
rails-dom-testing (~> 2.2)
|
53
|
+
rails-html-sanitizer (~> 1.6)
|
54
|
+
activejob (8.0.2)
|
55
|
+
activesupport (= 8.0.2)
|
56
|
+
globalid (>= 0.3.6)
|
57
|
+
activemodel (8.0.2)
|
58
|
+
activesupport (= 8.0.2)
|
59
|
+
activerecord (8.0.2)
|
60
|
+
activemodel (= 8.0.2)
|
61
|
+
activesupport (= 8.0.2)
|
62
|
+
timeout (>= 0.4.0)
|
63
|
+
activestorage (8.0.2)
|
64
|
+
actionpack (= 8.0.2)
|
65
|
+
activejob (= 8.0.2)
|
66
|
+
activerecord (= 8.0.2)
|
67
|
+
activesupport (= 8.0.2)
|
68
|
+
marcel (~> 1.0)
|
69
|
+
activesupport (8.0.2)
|
70
|
+
base64
|
71
|
+
benchmark (>= 0.3)
|
72
|
+
bigdecimal
|
73
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
74
|
+
connection_pool (>= 2.2.5)
|
75
|
+
drb
|
76
|
+
i18n (>= 1.6, < 2)
|
77
|
+
logger (>= 1.4.2)
|
78
|
+
minitest (>= 5.1)
|
79
|
+
securerandom (>= 0.3)
|
80
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
81
|
+
uri (>= 0.13.1)
|
11
82
|
base64 (0.2.0)
|
83
|
+
benchmark (0.4.0)
|
84
|
+
bigdecimal (3.1.9)
|
85
|
+
builder (3.3.0)
|
86
|
+
concurrent-ruby (1.3.5)
|
87
|
+
connection_pool (2.5.0)
|
88
|
+
crass (1.0.6)
|
12
89
|
date (3.4.1)
|
13
90
|
debug (1.10.0)
|
14
91
|
irb (~> 1.10)
|
@@ -18,18 +95,88 @@ GEM
|
|
18
95
|
doggo (1.4.0)
|
19
96
|
rspec-core (~> 3.13)
|
20
97
|
drb (2.2.1)
|
98
|
+
erubi (1.13.1)
|
99
|
+
globalid (1.2.1)
|
100
|
+
activesupport (>= 6.1)
|
101
|
+
i18n (1.14.7)
|
102
|
+
concurrent-ruby (~> 1.0)
|
21
103
|
io-console (0.8.0)
|
22
|
-
irb (1.15.
|
104
|
+
irb (1.15.2)
|
23
105
|
pp (>= 0.6.0)
|
24
106
|
rdoc (>= 4.0.0)
|
25
107
|
reline (>= 0.4.2)
|
108
|
+
logger (1.7.0)
|
109
|
+
loofah (2.24.0)
|
110
|
+
crass (~> 1.0.2)
|
111
|
+
nokogiri (>= 1.12.0)
|
112
|
+
mail (2.8.1)
|
113
|
+
mini_mime (>= 0.1.1)
|
114
|
+
net-imap
|
115
|
+
net-pop
|
116
|
+
net-smtp
|
117
|
+
marcel (1.0.4)
|
118
|
+
mini_mime (1.1.5)
|
119
|
+
mini_portile2 (2.8.8)
|
120
|
+
minitest (5.25.5)
|
121
|
+
net-imap (0.5.6)
|
122
|
+
date
|
123
|
+
net-protocol
|
124
|
+
net-pop (0.1.2)
|
125
|
+
net-protocol
|
126
|
+
net-protocol (0.2.2)
|
127
|
+
timeout
|
128
|
+
net-smtp (0.5.1)
|
129
|
+
net-protocol
|
130
|
+
nio4r (2.7.4)
|
131
|
+
nokogiri (1.18.7)
|
132
|
+
mini_portile2 (~> 2.8.2)
|
133
|
+
racc (~> 1.4)
|
26
134
|
pp (0.6.2)
|
27
135
|
prettyprint
|
28
136
|
prettyprint (0.2.0)
|
29
137
|
psych (5.2.3)
|
30
138
|
date
|
31
139
|
stringio
|
32
|
-
|
140
|
+
racc (1.8.1)
|
141
|
+
rack (3.1.12)
|
142
|
+
rack-session (2.1.0)
|
143
|
+
base64 (>= 0.1.0)
|
144
|
+
rack (>= 3.0.0)
|
145
|
+
rack-test (2.2.0)
|
146
|
+
rack (>= 1.3)
|
147
|
+
rackup (2.2.1)
|
148
|
+
rack (>= 3)
|
149
|
+
rails (8.0.2)
|
150
|
+
actioncable (= 8.0.2)
|
151
|
+
actionmailbox (= 8.0.2)
|
152
|
+
actionmailer (= 8.0.2)
|
153
|
+
actionpack (= 8.0.2)
|
154
|
+
actiontext (= 8.0.2)
|
155
|
+
actionview (= 8.0.2)
|
156
|
+
activejob (= 8.0.2)
|
157
|
+
activemodel (= 8.0.2)
|
158
|
+
activerecord (= 8.0.2)
|
159
|
+
activestorage (= 8.0.2)
|
160
|
+
activesupport (= 8.0.2)
|
161
|
+
bundler (>= 1.15.0)
|
162
|
+
railties (= 8.0.2)
|
163
|
+
rails-dom-testing (2.2.0)
|
164
|
+
activesupport (>= 5.0.0)
|
165
|
+
minitest
|
166
|
+
nokogiri (>= 1.6)
|
167
|
+
rails-html-sanitizer (1.6.2)
|
168
|
+
loofah (~> 2.21)
|
169
|
+
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
170
|
+
railties (8.0.2)
|
171
|
+
actionpack (= 8.0.2)
|
172
|
+
activesupport (= 8.0.2)
|
173
|
+
irb (~> 1.13)
|
174
|
+
rackup (>= 1.0.0)
|
175
|
+
rake (>= 12.2)
|
176
|
+
thor (~> 1.0, >= 1.2.2)
|
177
|
+
zeitwerk (~> 2.6)
|
178
|
+
rake (13.2.1)
|
179
|
+
rdoc (6.13.1)
|
33
180
|
psych (>= 4.0.0)
|
34
181
|
reline (0.6.0)
|
35
182
|
io-console (~> 0.5)
|
@@ -46,6 +193,7 @@ GEM
|
|
46
193
|
diff-lcs (>= 1.2.0, < 2.0)
|
47
194
|
rspec-support (~> 3.13.0)
|
48
195
|
rspec-support (3.13.2)
|
196
|
+
securerandom (0.4.1)
|
49
197
|
simplecov (0.22.0)
|
50
198
|
docile (~> 1.1)
|
51
199
|
simplecov-html (~> 0.11)
|
@@ -53,6 +201,17 @@ GEM
|
|
53
201
|
simplecov-html (0.13.1)
|
54
202
|
simplecov_json_formatter (0.1.4)
|
55
203
|
stringio (3.1.6)
|
204
|
+
thor (1.3.2)
|
205
|
+
timeout (0.4.3)
|
206
|
+
tzinfo (2.0.6)
|
207
|
+
concurrent-ruby (~> 1.0)
|
208
|
+
uri (1.0.3)
|
209
|
+
useragent (0.16.11)
|
210
|
+
websocket-driver (0.7.7)
|
211
|
+
base64
|
212
|
+
websocket-extensions (>= 0.1.0)
|
213
|
+
websocket-extensions (0.1.5)
|
214
|
+
zeitwerk (2.7.2)
|
56
215
|
|
57
216
|
PLATFORMS
|
58
217
|
ruby
|
@@ -61,6 +220,7 @@ DEPENDENCIES
|
|
61
220
|
debug (~> 1.1)
|
62
221
|
doggo (~> 1.4)
|
63
222
|
hubssolib!
|
223
|
+
rails (~> 8.0)
|
64
224
|
rspec (~> 3.13)
|
65
225
|
rspec-mocks (~> 3.13)
|
66
226
|
simplecov (~> 0.22)
|
data/README.md
CHANGED
@@ -449,4 +449,25 @@ end
|
|
449
449
|
* Your previously submitted action scheme and URL are also given in case you need them because you've decided to implement one callback handler for different kinds of untrusted action.
|
450
450
|
* The `action_payload` is where all your important data, meaningful only to you, resides and this must contain everything else, other than the above parameters, required to complete the action previously sent for review, _while making sure things are attributed to the correct user_.
|
451
451
|
|
452
|
+
Use `head ...` to indicate non-200 responses. If you don't want the originating user to get notified of a successful run, then on-success, also reply with `head :ok` or `head 200` (according to style preference). If you want to ask Hub to notify the user, then on-success instead do this:
|
453
|
+
|
454
|
+
```ruby
|
455
|
+
render json: { mail_item_url: ..., mail_subject: ... }, status: :ok
|
456
|
+
```
|
457
|
+
|
458
|
+
The payload items shown above are both mandatory:
|
459
|
+
|
460
|
+
* `mail_item_url` is the full URL of the entity that you just successfully edited, updated, or some other sensible URL to take the user (this will be included verbatim in the e-mail)
|
461
|
+
* `mail_subject` is a subject line of your choosing - for example, `"Your new forum post is now visible"`. Hub might add a prefix such as `[ORGNAME] ...`
|
462
|
+
|
463
|
+
If either item is missing or blank, or if for any reason Hub finds itself unable to associated the action with a user record on Hub's side, then no e-mail message will be sent.
|
464
|
+
|
465
|
+
**IMPORTANT:** The Hub application's database migration at the time you updated to 3.7.0 will have set existing users to trusted for historic data, but new users are untrusted. If you introduce trust integration to your site's other apps after this, you might want to enter the console to update any new users added since likewise; inside `app/hub`, issue:
|
466
|
+
|
467
|
+
```
|
468
|
+
$ bundle exec rails c
|
469
|
+
> User.update_all(trusted: true)
|
470
|
+
> exit
|
471
|
+
```
|
472
|
+
|
452
473
|
The trust mechanism involves a fair amount of effort on the integrating app's side but it can be very useful if you have a site where, despite your best efforts, sometimes spam/bot accounts manage to get inside and try to flood the system with spam. It's just one of many different potential protection and mitigation mechanisms that your site might choose to employ.
|
data/hubssolib.gemspec
CHANGED
@@ -4,7 +4,7 @@ spec = Gem::Specification.new do |s|
|
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.name = 'hubssolib'
|
6
6
|
|
7
|
-
s.version = '3.
|
7
|
+
s.version = '3.8.0'
|
8
8
|
s.author = 'Andrew Hodgkinson and others'
|
9
9
|
s.email = 'ahodgkin@rowing.org.uk'
|
10
10
|
s.homepage = 'http://pond.org.uk/'
|
@@ -28,6 +28,7 @@ spec = Gem::Specification.new do |s|
|
|
28
28
|
s.add_dependency 'base64', '~> 0.2'
|
29
29
|
|
30
30
|
s.add_development_dependency 'debug', '~> 1.1'
|
31
|
+
s.add_development_dependency 'rails', '~> 8.0'
|
31
32
|
s.add_development_dependency 'simplecov', '~> 0.22'
|
32
33
|
s.add_development_dependency 'doggo', '~> 1.4'
|
33
34
|
s.add_development_dependency 'rspec', '~> 3.13'
|
data/lib/hub_sso_lib.rb
CHANGED
@@ -69,7 +69,7 @@ module HubSsoLib
|
|
69
69
|
|
70
70
|
# Location of Hub application root.
|
71
71
|
#
|
72
|
-
HUB_PATH_PREFIX = ENV['HUB_PATH_PREFIX'] || ''
|
72
|
+
HUB_PATH_PREFIX = (ENV['HUB_PATH_PREFIX'] || '').sub(/(\/)+$/, '')
|
73
73
|
|
74
74
|
# Time limit, *in seconds*, for the account inactivity timeout. If a user
|
75
75
|
# performs no Hub actions during this time they will be automatically logged
|
@@ -394,24 +394,6 @@ module HubSsoLib
|
|
394
394
|
attr_accessor :user_password_reset_code_expires_at
|
395
395
|
attr_accessor :user_trusted
|
396
396
|
|
397
|
-
def initialize
|
398
|
-
@user_salt = nil
|
399
|
-
@user_roles = nil
|
400
|
-
@user_updated_at = nil
|
401
|
-
@user_activated_at = nil
|
402
|
-
@user_real_name = nil
|
403
|
-
@user_crypted_password = nil
|
404
|
-
@user_remember_token_expires_at = nil
|
405
|
-
@user_activation_code = nil
|
406
|
-
@user_member_id = nil
|
407
|
-
@user_id = nil
|
408
|
-
@user_password_reset_code = nil
|
409
|
-
@user_remember_token = nil
|
410
|
-
@user_email = nil
|
411
|
-
@user_created_at = nil
|
412
|
-
@user_password_reset_code_expires_at = nil
|
413
|
-
@user_trusted = nil
|
414
|
-
end
|
415
397
|
end # User class
|
416
398
|
|
417
399
|
#######################################################################
|
@@ -437,7 +419,7 @@ module HubSsoLib
|
|
437
419
|
include DRb::DRbUndumped
|
438
420
|
|
439
421
|
attr_accessor :session_last_used
|
440
|
-
|
422
|
+
attr_reader :session_return_to # DEPRECATED
|
441
423
|
attr_accessor :session_flash
|
442
424
|
attr_accessor :session_user
|
443
425
|
attr_accessor :session_rotated_key
|
@@ -613,7 +595,9 @@ module HubSsoLib
|
|
613
595
|
keys = if count > HUB_SESSION_ENUMERATION_KEY_MAX
|
614
596
|
nil
|
615
597
|
else
|
616
|
-
|
598
|
+
HUB_MUTEX.synchronize do
|
599
|
+
@hub_sessions.keys # (Hash#keys returns a new array)
|
600
|
+
end
|
617
601
|
end
|
618
602
|
|
619
603
|
return { count: count, keys: keys }
|
@@ -628,7 +612,7 @@ module HubSsoLib
|
|
628
612
|
# found for that key.
|
629
613
|
#
|
630
614
|
def retrieve_session_by_key(key)
|
631
|
-
@hub_sessions[key]
|
615
|
+
HUB_MUTEX.synchronize { @hub_sessions[key] }
|
632
616
|
end
|
633
617
|
|
634
618
|
# WARNING: Comparatively slow.
|
@@ -655,6 +639,10 @@ module HubSsoLib
|
|
655
639
|
# session data. Does nothing if the key is not found.
|
656
640
|
#
|
657
641
|
def destroy_session_by_key(key)
|
642
|
+
unless @hub_be_quiet
|
643
|
+
puts "Session factory: Deleting session with key #{key}"
|
644
|
+
end
|
645
|
+
|
658
646
|
HUB_MUTEX.synchronize do
|
659
647
|
@hub_sessions.delete(key)
|
660
648
|
end
|
@@ -674,6 +662,10 @@ module HubSsoLib
|
|
674
662
|
# SESSIONS IN THE HASH and must therefore lock on mutex for the duration.
|
675
663
|
#
|
676
664
|
def destroy_sessions_by_user_id(user_id)
|
665
|
+
unless @hub_be_quiet
|
666
|
+
puts "Session factory: Deleting all session records for user ID #{user_id.inspect}"
|
667
|
+
end
|
668
|
+
|
677
669
|
HUB_MUTEX.synchronize do
|
678
670
|
@hub_sessions.reject! do | key, session |
|
679
671
|
session&.session_user&.user_id == user_id
|
@@ -685,6 +677,30 @@ module HubSsoLib
|
|
685
677
|
raise
|
686
678
|
end
|
687
679
|
|
680
|
+
# WARNING: Comparatively slow.
|
681
|
+
#
|
682
|
+
# Call only if you MUST update details of a session user inside all Hub
|
683
|
+
# sessions. Pass the user ID and HubSsoLib::User details that are to be
|
684
|
+
# stored for all sessions owned by that user ID.
|
685
|
+
#
|
686
|
+
def update_sessions_by_user_id(user_id, user)
|
687
|
+
unless @hub_be_quiet
|
688
|
+
puts "Session factory: Updating all session records for user ID #{user_id.inspect}"
|
689
|
+
end
|
690
|
+
|
691
|
+
HUB_MUTEX.synchronize do
|
692
|
+
@hub_sessions.each do | key, session |
|
693
|
+
if session&.session_user&.user_id == user_id
|
694
|
+
session.session_user = user
|
695
|
+
end
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
rescue => e
|
700
|
+
Sentry.capture_exception(e) if defined?(Sentry) && Sentry.respond_to?(:capture_exception)
|
701
|
+
raise
|
702
|
+
end
|
703
|
+
|
688
704
|
# WARNING: Slow.
|
689
705
|
#
|
690
706
|
# This is a housekeeping task which checks sessions against Hub expiry and,
|
@@ -964,34 +980,97 @@ module HubSsoLib
|
|
964
980
|
end
|
965
981
|
end
|
966
982
|
|
967
|
-
#
|
968
|
-
#
|
969
|
-
#
|
970
|
-
#
|
983
|
+
# Return a URL that leads to a Hub "log in" page if the user is not logged
|
984
|
+
# in currently, else an "account" page for logged-in users.
|
985
|
+
#
|
986
|
+
# +universal+:: Use +true+ if concerned that a "Back" browser button action
|
987
|
+
# might cause a page to appear that's got a cached link which
|
988
|
+
# may no longer reflect the current state. This jumps to Hub
|
989
|
+
# and redirects to the "log in" or "account" destinations
|
990
|
+
# depending on current state. If using this, bear in mind
|
991
|
+
# that the link text must be ambiguous, because the eventual
|
992
|
+
# destination isn't known.
|
993
|
+
#
|
994
|
+
# It's often better to just use #hubssolib_account_link to
|
995
|
+
# create markup for this in a navigation area.
|
996
|
+
#
|
997
|
+
# The default is +false+, which means that a bespoke link
|
998
|
+
# for the exact current instantaneous log in state is
|
999
|
+
# generated; the +logged_in+ parameter applies (see below).
|
1000
|
+
#
|
1001
|
+
# +logged_in+:: If +universal+ is +false+, then this parameter defaults to
|
1002
|
+
# the current user logged-in state, but can be overridden
|
1003
|
+
# with +true+ (act as if someone is logged in) or +false+
|
1004
|
+
# (act as if someone is not logged in).
|
1005
|
+
#
|
1006
|
+
# +return_to+:: Overrides the use of <tt>request.original_url</tt> to give
|
1007
|
+
# an alternative return-after-logging-in URL. Will be of no
|
1008
|
+
# use for users already logged in. This is rarely used, but
|
1009
|
+
# might be helpful if you (say) want them to return to the
|
1010
|
+
# current page, but with a specific fragment added ("#foo").
|
1011
|
+
# Specify as a String, Symbol or URI, at your preference.
|
1012
|
+
#
|
1013
|
+
# Note that the universal links, or a not-logged-in state link will include
|
1014
|
+
# a query string which takes the value of <tt>request.original_url</tt> or,
|
1015
|
+
# if not defined, tries <tt>request.referrer</tt> (else is absent). This is
|
1016
|
+
# a URL used for the redirection after successful login. The logged-in
|
1017
|
+
# state link does not require this addition so omits it for brevity.
|
1018
|
+
#
|
1019
|
+
def hubssolib_returnable_account_url(
|
1020
|
+
universal: false,
|
1021
|
+
logged_in: self.hubssolib_logged_in?,
|
1022
|
+
return_to: nil
|
1023
|
+
)
|
1024
|
+
account_url = if universal
|
1025
|
+
"#{HUB_PATH_PREFIX}/account/login_conditional"
|
1026
|
+
else
|
1027
|
+
"#{HUB_PATH_PREFIX}/#{'account/login' unless logged_in}"
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
# Attach a return-to URL always for the universal case which needs to
|
1031
|
+
# work regardless of login state; else we only need it when logged out,
|
1032
|
+
# so that we can return to the originating location after logging in.
|
1033
|
+
# There's no functional difference - just a simpler URL that omits what
|
1034
|
+
# would otherwise be an unused query string parameter.
|
1035
|
+
#
|
1036
|
+
if universal or not logged_in
|
1037
|
+
if return_to.nil?
|
1038
|
+
request_obj = self.try(:request)
|
1039
|
+
return_to = request_obj.try(:original_url) || request_obj.try(:referrer)
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
if return_to.present?
|
1043
|
+
account_url << "?return_to_url=#{CGI.escape(return_to.to_s)}"
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
return account_url
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
# Returns markup for a link that leads to Hub's login or logout link, using
|
1051
|
+
# pure HTML + CSS for styling. A universal "conditional login" link is used
|
1052
|
+
# since page data may be old due to e.g. a "back" button being used, after
|
1053
|
+
# the user either logged in or out elsewhere. This possibility is also why
|
1054
|
+
# JavaScript is used, updating the button styling the correct login state
|
1055
|
+
# if needed. This requires the "pageshow" event to be supported. NOSCRIPT
|
1056
|
+
# browsers use the a no-cache image-based fallback, which is much less
|
1057
|
+
# efficient, but works.
|
971
1058
|
#
|
972
|
-
#
|
973
|
-
#
|
974
|
-
#
|
975
|
-
# no-cache image fallback, which is much less efficient, but works.
|
1059
|
+
# Although the JavaScript-powered option could use the non-conditional link
|
1060
|
+
# for log in/out and swap those, it's simpler just to use generate the same
|
1061
|
+
# link for all states - script-capable or otherwise, logged in or out,
|
976
1062
|
#
|
977
1063
|
def hubssolib_account_link
|
978
|
-
|
979
|
-
|
1064
|
+
logged_in_url = self.hubssolib_returnable_account_url(universal: false, logged_in: true)
|
1065
|
+
logged_out_url = self.hubssolib_returnable_account_url(universal: false, logged_in: false)
|
1066
|
+
universal_url = self.hubssolib_returnable_account_url(universal: true)
|
1067
|
+
|
980
1068
|
noscript_img_src = "#{HUB_PATH_PREFIX}/account/login_indication.png"
|
981
1069
|
noscript_img_tag = helpers.image_tag(noscript_img_src, size: '90x22', border: '0', alt: 'Log in or out')
|
982
1070
|
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
if return_to_url.present?
|
987
|
-
return_query = URI.encode_www_form({ return_to_url: return_to_url.to_s })
|
988
|
-
ui_href << "?#{return_query}"
|
989
|
-
end
|
990
|
-
end
|
991
|
-
|
992
|
-
logged_in_link = helpers.link_to('Account', ui_href, id: 'hubssolib_logged_in_link')
|
993
|
-
logged_out_link = helpers.link_to('Log in', ui_href, id: 'hubssolib_logged_out_link')
|
994
|
-
noscript_link = helpers.link_to(noscript_img_tag, ui_href, id: 'hubssolib_login_noscript')
|
1071
|
+
logged_in_link = helpers.link_to('Account', logged_in_url, id: 'hubssolib_logged_in_link')
|
1072
|
+
logged_out_link = helpers.link_to('Log in', logged_out_url, id: 'hubssolib_logged_out_link')
|
1073
|
+
noscript_link = helpers.link_to(noscript_img_tag, universal_url, id: 'hubssolib_login_noscript')
|
995
1074
|
|
996
1075
|
# Yes, it's ugly, but yes, it works and it's a lot better for the server
|
997
1076
|
# to avoid the repeated image fetches. It probably works out as overall
|
@@ -1217,6 +1296,18 @@ module HubSsoLib
|
|
1217
1296
|
hubssolib_factory().destroy_sessions_by_user_id(hub_user_id) unless hub_user_id.nil?
|
1218
1297
|
end
|
1219
1298
|
|
1299
|
+
# WARNING: Comparatively slow.
|
1300
|
+
#
|
1301
|
+
# If a Hub user record changes, make sure their session records reflect
|
1302
|
+
# the updated demographics according to the HubSsoLib::User provided.
|
1303
|
+
#
|
1304
|
+
# For information about performance limitations, see
|
1305
|
+
# HubSsoLib::SessionFactory#destroy_sessions_by_user_id.
|
1306
|
+
#
|
1307
|
+
def hubssolib_update_user_sessions(hub_user_id, hub_user)
|
1308
|
+
hubssolib_factory().update_sessions_by_user_id(hub_user_id, hub_user) unless hub_user_id.nil?
|
1309
|
+
end
|
1310
|
+
|
1220
1311
|
# If an application needs to know about changes of a user e-mail address
|
1221
1312
|
# or display name (e.g. because of sync to a local relational store of
|
1222
1313
|
# users related to other application-managed resources, with therefore a
|
@@ -1327,7 +1418,6 @@ module HubSsoLib
|
|
1327
1418
|
cookies.delete(HUB_LOGIN_INDICATOR_COOKIE, domain: :all, path: '/')
|
1328
1419
|
|
1329
1420
|
if login_is_required
|
1330
|
-
hubssolib_store_location()
|
1331
1421
|
return hubssolib_must_login()
|
1332
1422
|
else
|
1333
1423
|
return true
|
@@ -1354,14 +1444,10 @@ module HubSsoLib
|
|
1354
1444
|
# if OK, else indicate that access is denied.
|
1355
1445
|
|
1356
1446
|
if (hubssolib_session_expired?)
|
1357
|
-
hubssolib_store_location()
|
1358
1447
|
hubssolib_log_out()
|
1359
1448
|
hubssolib_set_flash(:attention, 'Sorry, your session timed out; you need to log in again to continue.')
|
1360
1449
|
|
1361
|
-
|
1362
|
-
# ...except for the Hub, rather than the current application (whatever
|
1363
|
-
# it may be).
|
1364
|
-
redirect_to HUB_PATH_PREFIX + '/account/login'
|
1450
|
+
redirect_to hubssolib_returnable_account_url(logged_in: false)
|
1365
1451
|
else
|
1366
1452
|
hubssolib_set_last_used(Time.now.utc)
|
1367
1453
|
return hubssolib_authorized? ? true : hubssolib_access_denied()
|
@@ -1410,28 +1496,17 @@ module HubSsoLib
|
|
1410
1496
|
end
|
1411
1497
|
end
|
1412
1498
|
|
1413
|
-
#
|
1414
|
-
# optional supplied specific URI.
|
1415
|
-
#
|
1416
|
-
# We can return to this location by calling #redirect_back_or_default.
|
1499
|
+
# Deprecated. Don't use this. See #hubssolib_returnable_account_url.
|
1417
1500
|
#
|
1418
1501
|
def hubssolib_store_location(uri_str = request.url)
|
1419
|
-
|
1420
|
-
uri_str = hubssolib_promote_uri_to_ssl(uri_str, request.host) unless request.ssl?
|
1421
|
-
hubssolib_set_return_to(uri_str)
|
1422
|
-
else
|
1423
|
-
hubssolib_set_return_to(nil)
|
1424
|
-
end
|
1502
|
+
Rails.logger.warn('hubssolib_store_location: DEPRECATED (no-op)') rescue nil
|
1425
1503
|
end
|
1426
1504
|
|
1427
|
-
#
|
1428
|
-
# to the passed default.
|
1505
|
+
# Deprecated. Don't use this. See #hubssolib_returnable_account_url.
|
1429
1506
|
#
|
1430
1507
|
def hubssolib_redirect_back_or_default(default)
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
redirect_to(url || default)
|
1508
|
+
Rails.logger.warn('hubssolib_redirect_back_or_default: DEPRECATED (always redirects to default)') rescue nil
|
1509
|
+
redirect_to(default)
|
1435
1510
|
end
|
1436
1511
|
|
1437
1512
|
# Take a URI and pass an optional host parameter. Decomposes the URI,
|
@@ -1440,9 +1515,10 @@ module HubSsoLib
|
|
1440
1515
|
# as a flat string.
|
1441
1516
|
#
|
1442
1517
|
def hubssolib_promote_uri_to_ssl(uri_str, host = nil)
|
1443
|
-
uri
|
1444
|
-
uri.host
|
1518
|
+
uri = URI.parse(uri_str)
|
1519
|
+
uri.host = host if host
|
1445
1520
|
uri.scheme = hub_bypass_ssl? ? 'http' : 'https'
|
1521
|
+
|
1446
1522
|
return uri.to_s
|
1447
1523
|
end
|
1448
1524
|
|
@@ -1454,8 +1530,7 @@ module HubSsoLib
|
|
1454
1530
|
if request.ssl? || hub_bypass_ssl?
|
1455
1531
|
return true
|
1456
1532
|
else
|
1457
|
-
|
1458
|
-
redirect_to( hubssolib_promote_uri_to_ssl( request.request_uri, request.host ) )
|
1533
|
+
redirect_to(hubssolib_promote_uri_to_ssl(request.original_url))
|
1459
1534
|
return false
|
1460
1535
|
end
|
1461
1536
|
end
|
@@ -1465,21 +1540,32 @@ module HubSsoLib
|
|
1465
1540
|
# dropped. However, we also want this to work without being logged in, so
|
1466
1541
|
# in that case it uses the normal flash as a backup when *writing*.
|
1467
1542
|
#
|
1543
|
+
# This method returns the Hub flash if logged in, else +nil+.
|
1544
|
+
#
|
1468
1545
|
def hubssolib_get_flash
|
1469
1546
|
session = self.hubssolib_get_session()
|
1470
|
-
session&.session_flash
|
1547
|
+
session&.session_flash
|
1471
1548
|
end
|
1472
1549
|
|
1550
|
+
# Set Flash information under the given symbol with the given text message,
|
1551
|
+
# using the Hub cross-application flash store if logged in, else the
|
1552
|
+
# this-application local session flash store otherwise.
|
1553
|
+
#
|
1473
1554
|
def hubssolib_set_flash(symbol, message)
|
1474
1555
|
session = self.hubssolib_get_session()
|
1475
|
-
|
1476
|
-
f
|
1556
|
+
|
1557
|
+
f = hubssolib_get_flash()
|
1558
|
+
f ||= self.flash if self.respond_to?(:flash)
|
1559
|
+
f ||= {}
|
1477
1560
|
|
1478
1561
|
f[symbol] = message
|
1479
1562
|
|
1480
1563
|
session.session_flash = f unless session.nil?
|
1481
1564
|
end
|
1482
1565
|
|
1566
|
+
# Clears the *hub* flash for logged-in users, but not the local application
|
1567
|
+
# session flash.
|
1568
|
+
#
|
1483
1569
|
def hubssolib_clear_flash
|
1484
1570
|
session = self.hubssolib_get_session()
|
1485
1571
|
session.session_flash = {} unless session.nil?
|
@@ -1495,6 +1581,10 @@ module HubSsoLib
|
|
1495
1581
|
# values. This allows both the Hub and standard flashes to have values
|
1496
1582
|
# inside them under the same key. All keys are strings.
|
1497
1583
|
#
|
1584
|
+
# You may well prefer to use #hubssolib_flash_markup to obtain something
|
1585
|
+
# that can be written straight into a view, unless it doesn't meet your
|
1586
|
+
# markup requirements.
|
1587
|
+
#
|
1498
1588
|
def hubssolib_flash_data
|
1499
1589
|
|
1500
1590
|
# These known key values are used to guarantee an order in the output
|
@@ -1510,14 +1600,14 @@ module HubSsoLib
|
|
1510
1600
|
# Get an array of keys for the Hub flash with the ordered key items
|
1511
1601
|
# first and store data from that flash; same again for standard.
|
1512
1602
|
|
1513
|
-
hash = hubssolib_get_flash()
|
1603
|
+
hash = hubssolib_get_flash() || {}
|
1514
1604
|
keys = ordered_keys | hash.keys
|
1515
1605
|
|
1516
1606
|
keys.each do | key |
|
1517
1607
|
compiled_data['hub'][key] = hash[key] if hash.key?(key)
|
1518
1608
|
end
|
1519
1609
|
|
1520
|
-
if
|
1610
|
+
if self.respond_to?( :flash )
|
1521
1611
|
hash = flash.to_h()
|
1522
1612
|
keys = ordered_keys | hash.keys
|
1523
1613
|
|
@@ -1527,11 +1617,60 @@ module HubSsoLib
|
|
1527
1617
|
end
|
1528
1618
|
|
1529
1619
|
hubssolib_clear_flash()
|
1530
|
-
flash.
|
1620
|
+
flash.clear()
|
1531
1621
|
|
1532
1622
|
return compiled_data
|
1533
1623
|
end
|
1534
1624
|
|
1625
|
+
# A companion to #hubssolib_flash_data which returns standardised Flash
|
1626
|
+
# markup for your view. THIS MUST ONLY BE USED IN A VIEW / HELPER, or at
|
1627
|
+
# least somewhere that ActionView::Helpers::TagHelper#tag is available.
|
1628
|
+
#
|
1629
|
+
# * An outer DIV with class "flash" wraps the content.
|
1630
|
+
#
|
1631
|
+
# * Within that, +H2+ tags wrap each message in the flash data. These
|
1632
|
+
# tags also have class "flash", along with an additional tag which is
|
1633
|
+
# equal to the key under which the message was found - so if adding a
|
1634
|
+
# flash message under, say, <tt>:alert</tt>, the rendered result would
|
1635
|
+
# be <tt><h2 class="flash alert">...</h2></tt>.
|
1636
|
+
#
|
1637
|
+
# * As a special case, a key with the suffix <tt>_html_safe</tt> is taken
|
1638
|
+
# to contain HTML-safe strings and potential markup, so you could do
|
1639
|
+
# things like add emphasis, other classes or styles to the data written
|
1640
|
+
# inside the +H2+ tag. Be very careful to make sure any non-markup data
|
1641
|
+
# is properly escaped first. The key-related class in the arising +H2+
|
1642
|
+
# tag _excludes_ the suffix, so e.g. <tt>:notice_html_safe</tt> will
|
1643
|
+
# lead to class +notice+.
|
1644
|
+
#
|
1645
|
+
# Hub session-sourced and Rails local app session-sourced flash data is
|
1646
|
+
# treated the same, with Hub-sourced data listed first. The other is
|
1647
|
+
# otherwise undefined (it's rare for there to be more than one thing in
|
1648
|
+
# the flash at any given time, though).
|
1649
|
+
#
|
1650
|
+
def hubssolib_flash_markup
|
1651
|
+
data = hubssolib_flash_data()
|
1652
|
+
proc = Proc.new do | key, value |
|
1653
|
+
key = key.to_s
|
1654
|
+
|
1655
|
+
if key.end_with?( '_html_safe' )
|
1656
|
+
value = value.html_safe()
|
1657
|
+
key = key.chomp( '_html_safe' )
|
1658
|
+
end
|
1659
|
+
|
1660
|
+
helpers.tag.h2(value, class: "flash #{ key }")
|
1661
|
+
end
|
1662
|
+
|
1663
|
+
return helpers.tag.div(class: 'flash') do
|
1664
|
+
data['hub'].each do | key, value |
|
1665
|
+
helpers.concat(proc.call(key, value))
|
1666
|
+
end
|
1667
|
+
|
1668
|
+
data['standard'].each do | key, value |
|
1669
|
+
helpers.concat(proc.call(key, value))
|
1670
|
+
end
|
1671
|
+
end
|
1672
|
+
end
|
1673
|
+
|
1535
1674
|
# Retrieve the message of an exception stored as an object in the given
|
1536
1675
|
# string.
|
1537
1676
|
#
|
@@ -1548,8 +1687,10 @@ module HubSsoLib
|
|
1548
1687
|
|
1549
1688
|
:hubssolib_current_user,
|
1550
1689
|
:hubssolib_unique_name,
|
1690
|
+
:hubssolib_returnable_account_url,
|
1551
1691
|
:hubssolib_account_link,
|
1552
1692
|
:hubssolib_flash_data,
|
1693
|
+
:hubssolib_flash_markup,
|
1553
1694
|
|
1554
1695
|
:hubssolib_logged_in?,
|
1555
1696
|
:hubssolib_authorized?,
|
@@ -1703,7 +1844,7 @@ module HubSsoLib
|
|
1703
1844
|
#
|
1704
1845
|
if hubssolib_ensure_https
|
1705
1846
|
hubssolib_set_flash(:alert, 'You must log in before you can continue.')
|
1706
|
-
redirect_to
|
1847
|
+
redirect_to hubssolib_returnable_account_url(logged_in: false)
|
1707
1848
|
end
|
1708
1849
|
|
1709
1850
|
return false
|
@@ -1795,16 +1936,6 @@ module HubSsoLib
|
|
1795
1936
|
session = self.hubssolib_get_session()
|
1796
1937
|
session.session_last_used = time unless session.nil?
|
1797
1938
|
end
|
1798
|
-
|
1799
|
-
def hubssolib_get_return_to
|
1800
|
-
self.hubssolib_get_session()&.session_return_to
|
1801
|
-
end
|
1802
|
-
|
1803
|
-
def hubssolib_set_return_to(uri)
|
1804
|
-
session = self.hubssolib_get_session()
|
1805
|
-
session.session_return_to = uri unless session.nil?
|
1806
|
-
end
|
1807
|
-
|
1808
1939
|
end # Core module
|
1809
1940
|
end # HubSsoLib module
|
1810
1941
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hubssolib
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Hodgkinson and others
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-04-04 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: drb
|
@@ -51,6 +51,20 @@ dependencies:
|
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '1.1'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rails
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '8.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '8.0'
|
54
68
|
- !ruby/object:Gem::Dependency
|
55
69
|
name: simplecov
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|