gloo 3.6.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5821f97e00507549b9270307c7965649e57f9b2db2ddca0c443fa5705fe4e50
4
- data.tar.gz: 31fc695bb6f0e367cc8a034a35c6548eb958db3946f874b80bde1748c9691c6f
3
+ metadata.gz: 0c8d357cda5602195adcfddc50c58705d34670b6ac01848eb8a1d66243571356
4
+ data.tar.gz: 86687c8e18f4843dc9bca97111e140c26750479fce8619341a902284dde5a573
5
5
  SHA512:
6
- metadata.gz: 6ca5dcd7554a5f640a8ec16512de56bfe76638de1a87f7267fafd57b727cb3a3044d77ad0f099cb4d390c90ec8b0dd6e30b3ce7eb3875731c2082fb6ba626f43
7
- data.tar.gz: 99acc770c29d36ed41e117f5b0ca4ceb20dcefa6bb6115d16116f890502dcdf8cae49f92cbb1ce4724261939be602edab8e516519a3dfddc9c6a5e3aa5f48c99
6
+ metadata.gz: baa186fd096e79a6152b7c287e23048a4b2fa67af5aa5db1c4e860918f42477b75e52fabbae8309506adfa32a77f5616bc2c4f4b22b6972142cb3977c9c965c9
7
+ data.tar.gz: 8467cf3db96cd931cad4dc06910a4933c65284b25675a3f884e17d5639cb8b3d9b50ff8bd1c171d329f318317390e715a9a5af62dceed681509fe336d28c3439
data/lib/VERSION CHANGED
@@ -1 +1 @@
1
- 3.6.2
1
+ 3.8.0
data/lib/VERSION_NOTES CHANGED
@@ -1,3 +1,16 @@
1
+ 3.8.0 - 2025.01.23
2
+ - Fixes issue with sessions
3
+ - Adds authenticity token tag and supporting session id and validation
4
+
5
+
6
+
7
+ 3.7.0 - 2025.01.09
8
+ - Adds File message to get SHA256 hash
9
+ - Asset Fingerprinting
10
+ - Adds message to web app to show assets (in the terminal)
11
+ - Adds asset helper tags
12
+
13
+
1
14
  3.6.2 - 2024.12.19
2
15
  - Bug fix for URI open
3
16
 
@@ -12,6 +12,7 @@ module Gloo
12
12
  class Platform
13
13
 
14
14
  DEFAULT_TMP_FILE = 'tmp.txt'.freeze
15
+ RETURN = "\n".freeze
15
16
 
16
17
  attr_reader :prompt, :table
17
18
 
@@ -0,0 +1,72 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Helper class to generate and verify a csrf token.
5
+ #
6
+ require 'securerandom'
7
+ require 'base64'
8
+ require 'active_support/security_utils'
9
+
10
+ module Gloo
11
+ module Objs
12
+ class CsrfToken
13
+
14
+ TOKEN_LENGTH = 32
15
+ AUTHENTICITY_TOKEN = 'authenticity_token'.freeze
16
+
17
+ #
18
+ # Generate a random token
19
+ #
20
+ def self.generate_csrf_token
21
+ SecureRandom.base64( TOKEN_LENGTH )
22
+ end
23
+
24
+ #
25
+ # Generate a masked token.
26
+ #
27
+ def self.mask_token( base_token )
28
+ one_time_pad = SecureRandom.random_bytes( base_token.bytesize )
29
+ masked_token = one_time_pad.bytes.zip( base_token.bytes ).map { |a, b| a ^ b }.pack('C*')
30
+ return Base64.urlsafe_encode64( one_time_pad + masked_token ) # Encode the result
31
+ end
32
+
33
+ #
34
+ # Unmask a masked token.
35
+ #
36
+ def self.unmask_token( masked_token )
37
+ decoded = Base64.urlsafe_decode64( masked_token )
38
+ one_time_pad, masked_token = decoded[0...decoded.length / 2], decoded[decoded.length / 2..]
39
+ return one_time_pad.bytes.zip( masked_token.bytes ).map { |a, b| (a ^ b).chr }.join
40
+ end
41
+
42
+ #
43
+ # Compare two tokens.
44
+ # Use ActiveSupport::SecurityUtils.secure_compare to avoid timing attacks.
45
+ #
46
+ def self.compare_tokens( token1, token2 )
47
+ return ActiveSupport::SecurityUtils.secure_compare( token1, token2 )
48
+ end
49
+
50
+ #
51
+ # Return a hidden field with the masked csrf token.
52
+ #
53
+ def self.get_csrf_token_hidden_field( base_token )
54
+ form_token = mask_token( base_token )
55
+
56
+ return "<input type='hidden' name='#{AUTHENTICITY_TOKEN}' value='#{form_token}' />"
57
+ end
58
+
59
+ #
60
+ # Validate a masked csrf token that came from a form submit.
61
+ #
62
+ def self.valid_csrf_token?( base_token, masked_token )
63
+ return false unless base_token && masked_token
64
+
65
+ unmasked_token = unmask_token( masked_token )
66
+
67
+ return compare_tokens( base_token, unmasked_token )
68
+ end
69
+
70
+ end
71
+ end
72
+ end
@@ -11,6 +11,10 @@ module Gloo
11
11
  KEYWORD = 'file'.freeze
12
12
  KEYWORD_SHORT = 'dir'.freeze
13
13
 
14
+ FILE_NAME_ERR = 'file and path name expected'.freeze
15
+ FILE_MISSING_ERR = 'file not found'.freeze
16
+
17
+
14
18
  #
15
19
  # The name of the object type.
16
20
  #
@@ -33,7 +37,7 @@ module Gloo
33
37
  # Get a list of message names that this object receives.
34
38
  #
35
39
  def self.messages
36
- basic = %w[read write get_name get_ext get_parent]
40
+ basic = %w[read write get_name get_ext get_parent get_sha256]
37
41
  checks = %w[exists? is_file? is_dir?]
38
42
  search = %w[find_match]
39
43
  show = %w[show page open]
@@ -73,7 +77,7 @@ module Gloo
73
77
  # Read the contents of the file into the object.
74
78
  #
75
79
  def msg_read
76
- return unless value && File.file?( value )
80
+ return unless check_file_exists?
77
81
 
78
82
  data = File.read( value )
79
83
  if @params&.token_count&.positive?
@@ -165,6 +169,44 @@ module Gloo
165
169
  end
166
170
  end
167
171
 
172
+ #
173
+ # Get the SHA256 hash of the file contents.
174
+ #
175
+ def msg_get_sha256
176
+ return unless check_file_exists?
177
+
178
+ file_hash = FileHandle.hash_for_file( value )
179
+ @engine.heap.it.set_to file_hash
180
+ end
181
+
182
+ #
183
+ # Get the SHA256 hash of the file contents.
184
+ #
185
+ def self.hash_for_file( file_path )
186
+ require 'digest'
187
+ file_data = File.read( file_path )
188
+ file_hash = Digest::SHA256.hexdigest( file_data )
189
+ return file_hash
190
+ end
191
+
192
+ #
193
+ # Check to see if the file exists.
194
+ # Show error if not.
195
+ #
196
+ def check_file_exists?
197
+ if value.blank?
198
+ @engine.log.error FILE_NAME_ERR
199
+ return false
200
+ end
201
+
202
+ unless File.exist?( value )
203
+ @engine.log.error FILE_MISSING_ERR
204
+ return false
205
+ end
206
+
207
+ return true
208
+ end
209
+
168
210
  end
169
211
  end
170
212
  end
@@ -154,8 +154,6 @@ module Gloo
154
154
  cmd_with_param = "#{cmd} \"#{url}\""
155
155
 
156
156
  if OS.mac?
157
- engine.log.warn 'Opening URL.' if engine
158
-
159
157
  `#{cmd_with_param}`
160
158
  else
161
159
  # This does not work in Linux or in WSL on Windows:
@@ -51,7 +51,8 @@ module Gloo
51
51
  ELAPSED = 'elapsed'.freeze
52
52
  DB = 'db'.freeze
53
53
  PAGE = 'page'.freeze
54
-
54
+ CURRENT_PAGE = 'current_page'.freeze
55
+
55
56
  # Container with pages in the web app.
56
57
  PAGES = 'pages'.freeze
57
58
 
@@ -303,7 +304,7 @@ module Gloo
303
304
  # Important to do this after the response is sent
304
305
  # to avoid holding on to data that is no longer needed.
305
306
  #
306
- def clear_session_data
307
+ def reset_session_data
307
308
  session_container.children.each do |session_var|
308
309
  session_var.value = ''
309
310
  end
@@ -405,7 +406,10 @@ module Gloo
405
406
  # Get a list of message names that this object receives.
406
407
  #
407
408
  def self.messages
408
- return super + [ 'start', 'stop', 'routes' ]
409
+ return super + [ 'start', 'stop',
410
+ 'list_routes', 'list_assets',
411
+ 'add_session_to_response', 'clear_session_data',
412
+ 'list_asset_img', 'list_asset_css', 'list_asset_js' ]
409
413
  end
410
414
 
411
415
  #
@@ -434,8 +438,9 @@ module Gloo
434
438
 
435
439
  #
436
440
  # Helper message to show all routes in the running server.
441
+ # A Debugging tool.
437
442
  #
438
- def msg_routes
443
+ def msg_list_routes
439
444
  if @router
440
445
  @router.show_routes
441
446
  else
@@ -443,6 +448,70 @@ module Gloo
443
448
  end
444
449
  end
445
450
 
451
+ #
452
+ # Helper message to show all assets in the running server.
453
+ # A Debugging tool.
454
+ #
455
+ def msg_list_assets
456
+ if @router
457
+ Gloo::WebSvr::AssetInfo.list_all( @engine )
458
+ else
459
+ @engine.err SERVER_NOT_RUNNING
460
+ end
461
+ end
462
+
463
+ #
464
+ # List all asset images in the running server.
465
+ # A Debugging tool.
466
+ #
467
+ def msg_list_asset_img
468
+ if @router
469
+ @asset.list_image_assets
470
+ else
471
+ @engine.err SERVER_NOT_RUNNING
472
+ end
473
+ end
474
+
475
+ #
476
+ # List all asset css in the running server.
477
+ # A Debugging tool.
478
+ #
479
+ def msg_list_asset_css
480
+ if @router
481
+ @asset.list_css_assets
482
+ else
483
+ @engine.err SERVER_NOT_RUNNING
484
+ end
485
+ end
486
+
487
+ #
488
+ # List all asset javascript in the running server.
489
+ # A Debugging tool.
490
+ #
491
+ def msg_list_asset_js
492
+ if @router
493
+ @asset.list_js_assets
494
+ else
495
+ @engine.err SERVER_NOT_RUNNING
496
+ end
497
+ end
498
+
499
+ #
500
+ # Add the session data to the response.
501
+ # This will be done for the current (next) request only.
502
+ #
503
+ def msg_add_session_to_response
504
+ @session.add_session_to_response if @session
505
+ end
506
+
507
+ #
508
+ # Clear out the session data, and remove it from the response.
509
+ #
510
+ def msg_clear_session_data
511
+ reset_session_data
512
+ @session.clear_session_data if @session
513
+ end
514
+
446
515
 
447
516
  # ---------------------------------------------------------------------
448
517
  # Start and Stop Events
@@ -482,7 +551,7 @@ module Gloo
482
551
  @engine.log.info "Stopping web server…"
483
552
 
484
553
  # Last chance to clear out session data.
485
- clear_session_data
554
+ reset_session_data
486
555
 
487
556
  @web_server.stop
488
557
  @web_server = nil
@@ -519,13 +588,18 @@ module Gloo
519
588
 
520
589
  #
521
590
  # Run the on request script if there is one.
591
+ # Set thee current page object so the app knows
592
+ # which page is being requested.
522
593
  #
523
- def run_on_request
594
+ def run_on_request current_page
595
+ for_page = find_child CURRENT_PAGE
596
+ alias_value = current_page.pn
597
+ for_page.set_value( alias_value ) if for_page
524
598
  o = find_child ON_REQUEST
525
599
  return unless o
526
600
  o = Gloo::Objs::Alias.resolve_alias( @engine, o )
527
601
 
528
- Gloo::Exec::Dispatch.message( @engine, 'run', o )
602
+ Gloo::Exec::Dispatch.message( @engine, 'run', o, CURRENT_PAGE => current_page )
529
603
  end
530
604
 
531
605
  #
@@ -544,6 +618,10 @@ module Gloo
544
618
  # This is done before the on_request event is fired.
545
619
  #
546
620
  def set_request_data( request )
621
+ # Clear out the redirect if there is one since this is the start of
622
+ # a new request.
623
+ @redirect = nil
624
+
547
625
  data = find_child RESQUEST_DATA
548
626
  return unless data
549
627
  data = Gloo::Objs::Alias.resolve_alias( @engine, data )
@@ -116,6 +116,38 @@ module Gloo
116
116
  return Gloo::WebSvr::Response.new( @engine, code, type, data )
117
117
  end
118
118
 
119
+ #
120
+ # Check if the given name is an asset.
121
+ #
122
+ def is_asset? name
123
+ return name == ASSET_FOLDER
124
+ end
125
+
126
+
127
+ # ---------------------------------------------------------------------
128
+ # Asset with Fingerprints
129
+ # ---------------------------------------------------------------------
130
+
131
+ #
132
+ # Register an asset with the web server.
133
+ # Adds fingerprint to the file names for later access.
134
+ #
135
+ # full_path is the FILE from which we build the SHA256 hash
136
+ # pn is the path and name within the assets directory
137
+ # name is the simple file name (icon.png)
138
+ #
139
+ def register_asset name, pn, full_path
140
+ asset_pn = "/asset/#{pn}"
141
+ return AssetInfo.new( @engine, full_path, name, asset_pn ).register
142
+ end
143
+
144
+ #
145
+ # Get the published name for the given asset name.
146
+ #
147
+ def published_name asset_name
148
+ return AssetInfo.find_published_name_for( asset_name )
149
+ end
150
+
119
151
 
120
152
  # ---------------------------------------------------------------------
121
153
  # Dynamic Add Assets
@@ -170,7 +202,8 @@ module Gloo
170
202
 
171
203
  add_files_in_folder( full_path, child, pn )
172
204
  else
173
- add_file_obj( container, name, pn )
205
+ info = register_asset( name, pn, full_path )
206
+ add_file_obj( container, name, pn, info )
174
207
  end
175
208
  end
176
209
  end
@@ -227,7 +260,7 @@ module Gloo
227
260
  #
228
261
  # Add a file object (page route) to the given container.
229
262
  #
230
- def add_file_obj( can, name, pn )
263
+ def add_file_obj( can, name, pn, info )
231
264
  name = name.gsub( '.', '_' )
232
265
  @log.debug "Adding route for file: #{name}"
233
266
 
@@ -236,6 +269,66 @@ module Gloo
236
269
  return if child
237
270
 
238
271
  @factory.create_file( name, pn, can )
272
+ # @factory.create_file( info.published_name, pn, can )
273
+ end
274
+
275
+
276
+ # ---------------------------------------------------------------------
277
+ # List Asset Helpers
278
+ # ---------------------------------------------------------------------
279
+
280
+ #
281
+ # List all image assets.
282
+ # This looks in the image container and lists the images found earlier.
283
+ # A Debugging tool.
284
+ #
285
+ def list_image_assets
286
+ data = []
287
+ @images.children.each do |o|
288
+ data << [ o.name, o.pn, o.value ]
289
+ end
290
+ headers = [ "Name", "PN", "Value" ]
291
+
292
+ puts Gloo::App::Platform::RETURN
293
+ title = "Image Assets with Routes"
294
+ @engine.platform.table.show headers, data, title
295
+ puts Gloo::App::Platform::RETURN
296
+ end
297
+
298
+ #
299
+ # List all js assets.
300
+ # This looks in the js container and lists the js files found earlier.
301
+ # A Debugging tool.
302
+ #
303
+ def list_js_assets
304
+ data = []
305
+ @javascript.children.each do |o|
306
+ data << [ o.name, o.pn, o.value ]
307
+ end
308
+ headers = [ "Name", "PN", "Value" ]
309
+
310
+ puts Gloo::App::Platform::RETURN
311
+ title = "JavaScript Assets with Routes"
312
+ @engine.platform.table.show headers, data, title
313
+ puts Gloo::App::Platform::RETURN
314
+ end
315
+
316
+ #
317
+ # List all css assets.
318
+ # This looks in the css container and lists the css files found earlier.
319
+ # A Debugging tool.
320
+ #
321
+ def list_css_assets
322
+ data = []
323
+ @stylesheets.children.each do |o|
324
+ data << [ o.name, o.pn, o.value ]
325
+ end
326
+ headers = [ "Name", "PN", "Value" ]
327
+
328
+ puts Gloo::App::Platform::RETURN
329
+ title = "Stylesheet Assets with Routes"
330
+ @engine.platform.table.show headers, data, title
331
+ puts Gloo::App::Platform::RETURN
239
332
  end
240
333
 
241
334
  end
@@ -0,0 +1,112 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Information about a single asset.
5
+ #
6
+ # Full Path is the full path to the file in the file system.
7
+ # Name is the name of the file with extension.
8
+ # PN is the path within assets and the name.
9
+ # ie: /asset/stylesheet/stylesheet.css
10
+ # Hash is the SHA256 hash of the file.
11
+ # Published Name is the name of the file that is published
12
+ # to the web server, and includes the hash.
13
+ #
14
+
15
+ module Gloo
16
+ module WebSvr
17
+ class AssetInfo
18
+
19
+ # Class Variables
20
+ @@index_by_published = {}
21
+ @@index_by_pn = {}
22
+
23
+ attr_reader :name, :pn, :published_name, :published_pn, :hash
24
+
25
+
26
+ # ---------------------------------------------------------------------
27
+ # Initialization
28
+ # ---------------------------------------------------------------------
29
+
30
+ #
31
+ # Set up an asset information object.
32
+ #
33
+ def initialize( engine, full_path, name, pn )
34
+ @engine = engine
35
+ @log = @engine.log
36
+
37
+ @full_path = full_path
38
+ @name = name
39
+ @pn = pn
40
+ end
41
+
42
+
43
+ # ---------------------------------------------------------------------
44
+ # Functions
45
+ # ---------------------------------------------------------------------
46
+
47
+ #
48
+ # Register the asset with indexes, inflating all needed data elements.
49
+ #
50
+ def register
51
+ @log.debug "*** REGISTERING ASSET: #{@name}"
52
+ @log.debug "*** #{@full_path} "
53
+ @log.debug "*** #PN: #{@pn} name: #{@name}"
54
+
55
+ @hash = Gloo::Objs::FileHandle.hash_for_file( @full_path )
56
+
57
+ # Build published name
58
+ ext = File.extname( @pn ) # Gets just the extension
59
+ n = @name[ 0..-ext.length - 1 ]
60
+ pn = @pn[ 0..-ext.length - 1 ]
61
+
62
+ @published_name = "#{n}-#{@hash}#{ext}"
63
+ @published_pn = "#{pn}-#{@hash}#{ext}"
64
+
65
+ @log.debug "*** Published Name: #{@published_name}"
66
+ @log.debug "*** Published Path: #{@published_pn}"
67
+
68
+ # Add to indexes
69
+ AssetInfo.index self
70
+ end
71
+
72
+ #
73
+ # Index the the given asset info record.
74
+ #
75
+ def self.index info
76
+ @@index_by_pn[ info.pn ] = info
77
+ @@index_by_published[ info.published_pn ] = info
78
+ end
79
+
80
+ #
81
+ # Find the asset info for the given published name.
82
+ #
83
+ def self.find_published_name_for pn
84
+ return @@index_by_pn[ pn ].published_pn
85
+ end
86
+
87
+ #
88
+ # Find the asset info for the given published name.
89
+ #
90
+ def self.find_info_for pn
91
+ return @@index_by_published[ pn ]
92
+ end
93
+
94
+ #
95
+ # List All assets.
96
+ #
97
+ def self.list_all engine
98
+ data = []
99
+ @@index_by_pn.each do |pn, info|
100
+ data << [ info.name, info.pn, info.published_pn ]
101
+ end
102
+ headers = [ "Name", "Asset Path", "Published" ]
103
+
104
+ puts Gloo::App::Platform::RETURN
105
+ title = "Assets in Running Web App"
106
+ engine.platform.table.show headers, data, title
107
+ puts Gloo::App::Platform::RETURN
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -29,6 +29,58 @@ module Gloo
29
29
  end
30
30
 
31
31
 
32
+ # ---------------------------------------------------------------------
33
+ # Tag Helpers
34
+ # ---------------------------------------------------------------------
35
+
36
+ #
37
+ # Render a favicon tag.
38
+ # By default the name is 'favicon.ico' and does not need to be provided
39
+ # if that is the correct file name.
40
+ #
41
+ def favicon_tag( name = 'favicon.ico' )
42
+ icon_path = "/#{Asset::ASSET_FOLDER}/#{Asset::IMAGE_FOLDER}/#{name}"
43
+ published_name = @engine.running_app.obj.asset.published_name( icon_path )
44
+ return "<link rel='shortcut icon' type='image/x-icon' href='#{published_name}' />"
45
+ end
46
+
47
+ #
48
+ # Render an image tag for the given image name.
49
+ # Include optional proterties as part of the tag.
50
+ #
51
+ def image_tag( img_name, properties = '' )
52
+ image_path = "/#{Asset::ASSET_FOLDER}/#{Asset::IMAGE_FOLDER}/#{img_name}"
53
+ published_name = @engine.running_app.obj.asset.published_name( image_path )
54
+ return "<image src='#{published_name}' #{properties} />"
55
+ end
56
+
57
+ #
58
+ # Render a script tag for the given script name.
59
+ #
60
+ def js_tag( name )
61
+ js_path = "/#{Asset::ASSET_FOLDER}/#{Asset::JAVASCRIPT_FOLDER}/#{name}"
62
+ published_name = @engine.running_app.obj.asset.published_name( js_path )
63
+ return "<script src='#{published_name}'></script>"
64
+ end
65
+
66
+ #
67
+ # Render a stylesheet tag for the given stylesheet name.
68
+ #
69
+ def css_tag( name )
70
+ css_path = "/#{Asset::ASSET_FOLDER}/#{Asset::STYLESHEET_FOLDER}/#{name}"
71
+ published_name = @engine.running_app.obj.asset.published_name( css_path )
72
+ return "<link rel='stylesheet' media='all' href='#{published_name}' />"
73
+ end
74
+
75
+ #
76
+ # Embed a hidden field with the autenticity token.
77
+ #
78
+ def autenticity_token_tag
79
+ session_id = @engine.running_app.obj&.session&.get_session_id
80
+ return Gloo::Objs::CsrfToken.get_csrf_token_hidden_field( session_id )
81
+ end
82
+
83
+
32
84
  # ---------------------------------------------------------------------
33
85
  # Obj Helper Functions
34
86
  # ---------------------------------------------------------------------
@@ -46,6 +46,9 @@ module Gloo
46
46
  request.request_params.log_id_keys
47
47
 
48
48
  if page
49
+ # Run the on_request script with the found page.
50
+ @server_obj.run_on_request( page )
51
+
49
52
  if page.is_a? Gloo::Objs::FileHandle
50
53
  result = handle_file page
51
54
  else
@@ -59,9 +59,15 @@ module Gloo
59
59
 
60
60
  # Run the on_request script if there is one.
61
61
  @handler.server_obj.set_request_data self
62
- @handler.server_obj.run_on_request
63
62
 
64
- result, page_obj = @handler.handle self
63
+ # Check authenticity token if it's given.
64
+ if @request_params.check_authenticity_token( @engine )
65
+ result, page_obj = @handler.handle self
66
+ else
67
+ # Render the error page.
68
+ result = @handler.server_error_result
69
+ end
70
+
65
71
  finish_timer
66
72
 
67
73
  # Run the on_response script if there is one.
@@ -56,6 +56,29 @@ module Gloo
56
56
  end
57
57
 
58
58
 
59
+ # ---------------------------------------------------------------------
60
+ # Authenticity Token checking
61
+ # ---------------------------------------------------------------------
62
+
63
+ #
64
+ # Check the authenticity token if it is present.
65
+ # Returns true if it is present and valid, and
66
+ # also if it is not present.
67
+ # Returns false if it is present but not valid.
68
+ #
69
+ def check_authenticity_token engine
70
+ auth_token = @query_params[ Gloo::Objs::CsrfToken::AUTHENTICITY_TOKEN ]
71
+ if auth_token
72
+ session_id = engine.running_app.obj&.session&.get_session_id
73
+ return false unless session_id
74
+
75
+ return Gloo::Objs::CsrfToken.valid_csrf_token?( session_id, auth_token )
76
+ end
77
+
78
+ return true
79
+ end
80
+
81
+
59
82
  # ---------------------------------------------------------------------
60
83
  # Helper functions
61
84
  # ---------------------------------------------------------------------
@@ -122,9 +122,12 @@ module Gloo
122
122
  headers[ 'Location' ] = @location
123
123
  end
124
124
 
125
- session = @engine&.running_app&.obj&.session
125
+ session = @engine&.running_app&.obj&.session
126
126
  headers = session.add_session_for_response( headers ) if session
127
127
 
128
+ # Clear out session data after the response is prepared.
129
+ @engine&.running_app&.obj&.reset_session_data
130
+
128
131
  return headers
129
132
  end
130
133
 
@@ -201,6 +201,13 @@ module Gloo
201
201
  # Create a list of path segments.
202
202
  #
203
203
  def detect_segments path
204
+ # For Assets, substitute the published name with fingerprint
205
+ # for the simple asset name (if it is found).
206
+ asset_info = Gloo::WebSvr::AssetInfo.find_info_for( path )
207
+ unless asset_info.blank?
208
+ path = asset_info.pn
209
+ end
210
+
204
211
  # Split the path into segments.
205
212
  @route_segments = path.split SEGMENT_DIVIDER
206
213
 
@@ -10,7 +10,6 @@ module Gloo
10
10
  class ShowRoutes
11
11
 
12
12
  SEGMENT_DIVIDER = '/'.freeze
13
- RETURN = "\n".freeze
14
13
 
15
14
 
16
15
  # ---------------------------------------------------------------------
@@ -76,10 +75,10 @@ module Gloo
76
75
  # Show the Routes title.
77
76
  #
78
77
  def show_table
79
- puts RETURN
78
+ puts Gloo::App::Platform::RETURN
80
79
  title = "Routes in Running Web App"
81
80
  @engine.platform.table.show headers, @found_routes, title
82
- puts RETURN
81
+ puts Gloo::App::Platform::RETURN
83
82
  end
84
83
 
85
84
  #
@@ -15,6 +15,7 @@ module Gloo
15
15
  class Session
16
16
 
17
17
  SESSION_CONTAINER = 'session'.freeze
18
+ SESSION_ID_NAME = 'session_id'.freeze
18
19
 
19
20
 
20
21
  # ---------------------------------------------------------------------
@@ -29,6 +30,8 @@ module Gloo
29
30
  @log = @engine.log
30
31
 
31
32
  @server_obj = server_obj
33
+ @include_in_response = false
34
+ @clearing_session = false
32
35
  end
33
36
 
34
37
 
@@ -52,8 +55,12 @@ module Gloo
52
55
  data = decode_decrypt( data )
53
56
  return unless data
54
57
 
58
+ @session_id = data[ SESSION_ID_NAME ]
59
+
55
60
  data.each do |key, value|
56
- @server_obj.set_session_var( key, value )
61
+ unless key == SESSION_ID_NAME
62
+ @server_obj.set_session_var( key, value )
63
+ end
57
64
  end
58
65
  end
59
66
  end
@@ -64,18 +71,62 @@ module Gloo
64
71
 
65
72
 
66
73
  # ---------------------------------------------------------------------
67
- # Get Session Data for Response
74
+ # Set Session Data for Response
68
75
  # ---------------------------------------------------------------------
69
76
 
77
+ #
78
+ # Temporarily set the flag to add the session data to the response.
79
+ # Once this is done, the flag will be cleared and it will not
80
+ # be added to the next request unless specifically set.
81
+ #
82
+ def add_session_to_response
83
+ @include_in_response = true
84
+ end
85
+
86
+ def init_session_id
87
+ @session_id = Gloo::Objs::CsrfToken.generate_csrf_token
88
+ return @session_id
89
+ end
90
+
91
+ #
92
+ # Initialize the session id and add it to the data.
93
+ # Use the current session ID if it is there.
94
+ #
95
+ def get_session_id
96
+ if @clearing_session
97
+ @clearing_session = false
98
+ return nil
99
+ end
100
+
101
+ init_session_id if @session_id.blank?
102
+
103
+ return @session_id
104
+ end
105
+
106
+ #
107
+ # Clear out the session Id.
108
+ # Set the flag to add the session data to the response.
109
+ #
110
+ def clear_session_data
111
+ @session_id = nil
112
+ @clearing_session = true
113
+ add_session_to_response
114
+ end
115
+
70
116
  #
71
117
  # If there is session data, encrypt and add it to the response.
72
118
  # Once done, clear out the session data.
73
119
  #
74
120
  def add_session_for_response( headers )
75
121
  # Are we using sessions?
76
- if @server_obj.use_session?
122
+ if @server_obj.use_session? && @include_in_response
123
+ # Reset the flag because we are adding to the session data now
124
+ @include_in_response = false
125
+
77
126
  # Build and add encrypted session data
78
127
  data = @server_obj.get_session_data
128
+ data[ SESSION_ID_NAME ] = get_session_id
129
+
79
130
  unless data.empty?
80
131
  data = encrypt_encode( data )
81
132
  session_hash = {
@@ -90,9 +141,6 @@ module Gloo
90
141
 
91
142
  Rack::Utils.set_cookie_header!( headers, session_name, session_hash )
92
143
  end
93
-
94
- # Clear out session data
95
- @server_obj.clear_session_data
96
144
  end
97
145
 
98
146
  return headers
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gloo
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.6.2
4
+ version: 3.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Crane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-19 00:00:00.000000000 Z
11
+ date: 2025-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -462,6 +462,7 @@ files:
462
462
  - lib/gloo/objs/ror/erb.rb
463
463
  - lib/gloo/objs/ror/eval.rb
464
464
  - lib/gloo/objs/security/cipher.rb
465
+ - lib/gloo/objs/security/csrf_token.rb
465
466
  - lib/gloo/objs/security/password.rb
466
467
  - lib/gloo/objs/system/file_handle.rb
467
468
  - lib/gloo/objs/system/ssh_exec.rb
@@ -514,6 +515,7 @@ files:
514
515
  - lib/gloo/verbs/version.rb
515
516
  - lib/gloo/verbs/wait.rb
516
517
  - lib/gloo/web_svr/asset.rb
518
+ - lib/gloo/web_svr/asset_info.rb
517
519
  - lib/gloo/web_svr/config.rb
518
520
  - lib/gloo/web_svr/embedded_renderer.rb
519
521
  - lib/gloo/web_svr/handler.rb