gloo 3.6.2 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
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