gloo 3.7.0 → 3.9.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: c1dfa24d10974fad30c699b9935d765525ceda2eef22c642be9f30fd8f717ce7
4
- data.tar.gz: c631fafd6862b14c03994767ad520cc95380868ea9a1c60a68f0da67399d9fab
3
+ metadata.gz: afc950a2a2470041f3a3d990480cf26bee1005e6f4e00bcc95feb4e38d8de394
4
+ data.tar.gz: fe2cf5549734e2779ed1ed1f90bc5974fcae7c0b8245b22626a9828d2f54c54e
5
5
  SHA512:
6
- metadata.gz: a020b85943d3fa84e2940dc37a4bf61d541a89e55de775b0a22fd001b7e57de84a852748d0084e92bf1490e5f561761b62cbea9c94cbfa57e422632bf77134c5
7
- data.tar.gz: 04ca71cc0e8c3c483d31155799876e336d372d2bee8f1a923f429c3aee0c2902528af4ebe289d3a6e5709274205ca7d5feaaf164e1f8187c48ce6a5c4315a847
6
+ metadata.gz: 7eed261a94581ca0a1e7f5c9a61657bd8e39f1455d55f4754bc4530641fe33ef359d7055f191dd204c505b31e501291803f5ef5822672640a280845c5c27b0d1
7
+ data.tar.gz: 643bd6cc65823bf8d1a8d1666e0b5b51abf5c44d0a8f2b25b51c901a1f18c3efda08264c5e686fe34efb0063a7e9e56b7040c50befa353e17369f0c04f32b186
data/lib/VERSION CHANGED
@@ -1 +1 @@
1
- 3.7.0
1
+ 3.9.0
data/lib/VERSION_NOTES CHANGED
@@ -1,3 +1,18 @@
1
+ 3.9.0 - 2025.02.21
2
+ - Fixes alias in page parameters
3
+ - Adds comparison operators
4
+ - Renames some verb shortcuts for compatibility
5
+ - Adds Group-By option in for each child loop
6
+ - Adds starts_with?, ends_with?, contains?, and format_for_html messages to string
7
+
8
+
9
+
10
+ 3.8.0 - 2025.01.23
11
+ - Fixes issue with sessions
12
+ - Adds authenticity token tag and supporting session id and validation
13
+
14
+
15
+
1
16
  3.7.0 - 2025.01.09
2
17
  - Adds File message to get SHA256 hash
3
18
  - Asset Fingerprinting
data/lib/gloo/app/log.rb CHANGED
@@ -169,7 +169,7 @@ module Gloo
169
169
  # Also write to the console unless quiet.
170
170
  #
171
171
  def error( msg, ex = nil, engine = nil )
172
- engine&.heap&.error&.set_to msg
172
+ engine&.heap&.error&.set_to( msg ) if engine
173
173
  @logger.error msg
174
174
  @error.error msg
175
175
  if ex
data/lib/gloo/core/op.rb CHANGED
@@ -13,19 +13,39 @@ module Gloo
13
13
  # Is the token an operator?
14
14
  #
15
15
  def self.op?( token )
16
- return [ '+', '-', '*', '/' ].include?( token.strip )
16
+ return [
17
+ Gloo::Expr::OpPlus::SYMBOL,
18
+ Gloo::Expr::OpMinus::SYMBOL,
19
+ Gloo::Expr::OpMult::SYMBOL,
20
+ Gloo::Expr::OpDiv::SYMBOL,
21
+ Gloo::Expr::OpEq::SYMBOL,
22
+ Gloo::Expr::OpEq::ALT_SYMBOL,
23
+ Gloo::Expr::OpIneq::SYMBOL,
24
+ Gloo::Expr::OpGt::SYMBOL,
25
+ Gloo::Expr::OpLt::SYMBOL,
26
+ Gloo::Expr::OpGteq::SYMBOL,
27
+ Gloo::Expr::OpLteq::SYMBOL
28
+ ].include?( token.strip )
17
29
  end
18
30
 
19
31
  #
20
32
  # Create the operator for the given token.
21
33
  #
22
34
  def self.create_op( token )
23
- return Gloo::Expr::OpMinus.new if token == '-'
24
- return Gloo::Expr::OpMult.new if token == '*'
25
- return Gloo::Expr::OpDiv.new if token == '/'
26
- return Gloo::Expr::OpPlus.new if token == '+'
27
-
28
- return default_op
35
+ case token
36
+ when Gloo::Expr::OpPlus::SYMBOL then Gloo::Expr::OpPlus.new
37
+ when Gloo::Expr::OpMinus::SYMBOL then Gloo::Expr::OpMinus.new
38
+ when Gloo::Expr::OpMult::SYMBOL then Gloo::Expr::OpMult.new
39
+ when Gloo::Expr::OpDiv::SYMBOL then Gloo::Expr::OpDiv.new
40
+ when Gloo::Expr::OpEq::SYMBOL then return Gloo::Expr::OpEq.new
41
+ when Gloo::Expr::OpEq::ALT_SYMBOL then return Gloo::Expr::OpEq.new
42
+ when Gloo::Expr::OpIneq::SYMBOL then return Gloo::Expr::OpIneq.new
43
+ when Gloo::Expr::OpGt::SYMBOL then return Gloo::Expr::OpGt.new
44
+ when Gloo::Expr::OpLt::SYMBOL then return Gloo::Expr::OpLt.new
45
+ when Gloo::Expr::OpGteq::SYMBOL then return Gloo::Expr::OpGteq.new
46
+ when Gloo::Expr::OpLteq::SYMBOL then return Gloo::Expr::OpLteq.new
47
+ else return default_op
48
+ end
29
49
  end
30
50
 
31
51
  #
@@ -8,6 +8,8 @@ module Gloo
8
8
  module Expr
9
9
  class OpDiv < Gloo::Core::Op
10
10
 
11
+ SYMBOL = '/'.freeze
12
+
11
13
  #
12
14
  # Perform the operation and return the result.
13
15
  #
@@ -0,0 +1,29 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Equality operator.
5
+ #
6
+
7
+ module Gloo
8
+ module Expr
9
+ class OpEq < Gloo::Core::Op
10
+
11
+ SYMBOL = '='.freeze
12
+ ALT_SYMBOL = '=='.freeze
13
+
14
+ #
15
+ # Perform the operation and return the result.
16
+ #
17
+ def perform( left, right )
18
+ return left == right.to_s if left.is_a? String
19
+
20
+ return left == right.to_i if left.is_a? Integer
21
+
22
+ return left == right.to_f if left.is_a? Numeric
23
+
24
+ return false
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Greater than operator.
5
+ #
6
+
7
+ module Gloo
8
+ module Expr
9
+ class OpGt < Gloo::Core::Op
10
+
11
+ SYMBOL = '>'.freeze
12
+
13
+ #
14
+ # Perform the operation and return the result.
15
+ #
16
+ def perform( left, right )
17
+ return left > right.to_i if left.is_a? Integer
18
+
19
+ return left > right.to_f if left.is_a? Numeric
20
+
21
+ return false
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Greater than or equal operator.
5
+ #
6
+
7
+ module Gloo
8
+ module Expr
9
+ class OpGteq < Gloo::Core::Op
10
+
11
+ SYMBOL = '>='.freeze
12
+
13
+ #
14
+ # Perform the operation and return the result.
15
+ #
16
+ def perform( left, right )
17
+ return left >= right.to_i if left.is_a? Integer
18
+
19
+ return left >= right.to_f if left.is_a? Numeric
20
+
21
+ return false
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Inequality operator.
5
+ #
6
+
7
+ module Gloo
8
+ module Expr
9
+ class OpIneq < Gloo::Core::Op
10
+
11
+ SYMBOL = '!='.freeze
12
+
13
+ #
14
+ # Perform the operation and return the result.
15
+ #
16
+ def perform( left, right )
17
+ return left != right.to_s if left.is_a? String
18
+
19
+ return left != right.to_i if left.is_a? Integer
20
+
21
+ return left != right.to_f if left.is_a? Numeric
22
+
23
+ return false
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Less than operator.
5
+ #
6
+
7
+ module Gloo
8
+ module Expr
9
+ class OpLt < Gloo::Core::Op
10
+
11
+ SYMBOL = '<'.freeze
12
+
13
+ #
14
+ # Perform the operation and return the result.
15
+ #
16
+ def perform( left, right )
17
+ return left < right.to_i if left.is_a? Integer
18
+
19
+ return left < right.to_f if left.is_a? Numeric
20
+
21
+ return false
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2025 Eric Crane. All rights reserved.
3
+ #
4
+ # Less than or equal operator.
5
+ #
6
+
7
+ module Gloo
8
+ module Expr
9
+ class OpLteq < Gloo::Core::Op
10
+
11
+ SYMBOL = '<='.freeze
12
+
13
+ #
14
+ # Perform the operation and return the result.
15
+ #
16
+ def perform( left, right )
17
+ return left <= right.to_i if left.is_a? Integer
18
+
19
+ return left <= right.to_f if left.is_a? Numeric
20
+
21
+ return false
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -8,6 +8,8 @@ module Gloo
8
8
  module Expr
9
9
  class OpMinus < Gloo::Core::Op
10
10
 
11
+ SYMBOL = '-'.freeze
12
+
11
13
  #
12
14
  # Perform the operation and return the result.
13
15
  #
@@ -8,6 +8,8 @@ module Gloo
8
8
  module Expr
9
9
  class OpMult < Gloo::Core::Op
10
10
 
11
+ SYMBOL = '*'.freeze
12
+
11
13
  #
12
14
  # Perform the operation and return the result.
13
15
  #
@@ -8,6 +8,8 @@ module Gloo
8
8
  module Expr
9
9
  class OpPlus < Gloo::Core::Op
10
10
 
11
+ SYMBOL = '+'.freeze
12
+
11
13
  #
12
14
  # Perform the operation and return the result.
13
15
  #
@@ -12,6 +12,7 @@ module Gloo
12
12
 
13
13
  KEYWORD = 'string'.freeze
14
14
  KEYWORD_SHORT = 'str'.freeze
15
+ MISSING_PARAM_MSG = 'Missing parameter!'.freeze
15
16
 
16
17
  #
17
18
  # The name of the object type.
@@ -42,10 +43,95 @@ module Gloo
42
43
  # Get a list of message names that this object receives.
43
44
  #
44
45
  def self.messages
45
- return super + %w[up down size encode64 decode64 escape unescape
46
+ return super + %w[up down size starts_with? ends_with? contains?
47
+ format_for_html encode64 decode64 escape unescape
46
48
  gen_alphanumeric gen_uuid gen_hex gen_base64]
47
49
  end
48
50
 
51
+ #
52
+ # Convert whitespace to HTML friendly spaces.
53
+ #
54
+ def msg_format_for_html
55
+ text = self.value
56
+ out = ""
57
+ return out unless text
58
+
59
+ # indentation
60
+ text.each_line do |line|
61
+ i = 0
62
+ while line[i] == ' '
63
+ i += 1
64
+ out << "&nbsp;"
65
+ end
66
+
67
+ i = 0
68
+ while line[i] == "\t"
69
+ i += 1
70
+ out << "&nbsp;&nbsp;&nbsp;&nbsp;"
71
+ end
72
+ out << line
73
+ end
74
+
75
+ self.value = out.gsub( "\n", "<br/>" )
76
+ end
77
+
78
+ #
79
+ # Does the string start with the given string?
80
+ #
81
+ def msg_starts_with?
82
+ if @params&.token_count&.positive?
83
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
84
+ data = expr.evaluate
85
+
86
+ result = self.value.start_with?( data )
87
+ @engine.heap.it.set_to result
88
+ return result
89
+ else
90
+ # Error
91
+ @engine.log.error MISSING_PARAM_MSG
92
+ @engine.heap.it.set_to false
93
+ return false
94
+ end
95
+ end
96
+
97
+ #
98
+ # Does the string end with the given string?
99
+ #
100
+ def msg_ends_with?
101
+ if @params&.token_count&.positive?
102
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
103
+ data = expr.evaluate
104
+
105
+ result = value.end_with?( data )
106
+ @engine.heap.it.set_to result
107
+ return result
108
+ else
109
+ # Error
110
+ @engine.log.error MISSING_PARAM_MSG
111
+ @engine.heap.it.set_to false
112
+ return false
113
+ end
114
+ end
115
+
116
+ #
117
+ # Does the string contain the given string?
118
+ #
119
+ def msg_contains?
120
+ if @params&.token_count&.positive?
121
+ expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
122
+ data = expr.evaluate
123
+
124
+ result = value.include?( data )
125
+ @engine.heap.it.set_to result
126
+ return result
127
+ else
128
+ # Error
129
+ @engine.log.error MISSING_PARAM_MSG
130
+ @engine.heap.it.set_to false
131
+ return false
132
+ end
133
+ end
134
+
49
135
  #
50
136
  # Get the size of the string.
51
137
  #
@@ -10,6 +10,11 @@ module Gloo
10
10
 
11
11
  CHILD = 'child'.freeze
12
12
  IN = 'IN'.freeze
13
+
14
+ GROUP_BY = 'group_by'.freeze
15
+ ON_GROUP_START = 'on_group_start'.freeze
16
+ ON_GROUP_END = 'on_group_end'.freeze
17
+
13
18
 
14
19
  # ---------------------------------------------------------------------
15
20
  # Create Iterator
@@ -34,6 +39,61 @@ module Gloo
34
39
  return false
35
40
  end
36
41
 
42
+
43
+ # ---------------------------------------------------------------------
44
+ # Group By
45
+ # ---------------------------------------------------------------------
46
+
47
+ #
48
+ # If the iterator has a group by,
49
+ # then we need to group by that value.
50
+ # Otherwise, we just loop for each child.
51
+ #
52
+ def has_group_by?
53
+ child = @iterator_obj.find_child GROUP_BY
54
+ return false unless child
55
+
56
+ return true
57
+ end
58
+
59
+ #
60
+ # Get the child that is the group by.
61
+ # Return nil if there is no group by.
62
+ #
63
+ def group_by_value obj_can
64
+ return nil unless obj_can
65
+
66
+ child = @iterator_obj.find_child GROUP_BY
67
+ return nil unless child
68
+
69
+ group_by_child_name = child.value
70
+
71
+ obj = obj_can.find_child( group_by_child_name )
72
+ return nil unless obj
73
+
74
+ return obj.value
75
+ end
76
+
77
+ #
78
+ # Run the on group start script.
79
+ #
80
+ def run_on_group_start
81
+ o = @iterator_obj.find_child ON_GROUP_START
82
+ return unless o
83
+
84
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
85
+ end
86
+
87
+ #
88
+ # Run the on group end script.
89
+ #
90
+ def run_on_group_end
91
+ o = @iterator_obj.find_child ON_GROUP_END
92
+ return unless o
93
+
94
+ Gloo::Exec::Dispatch.message( @engine, 'run', o )
95
+ end
96
+
37
97
 
38
98
  # ---------------------------------------------------------------------
39
99
  # Iterate
@@ -46,11 +106,38 @@ module Gloo
46
106
  o = @iterator_obj.find_child IN
47
107
  return unless o
48
108
 
109
+ # Set up for optional groups.
110
+ group_mode = false
111
+ if has_group_by?
112
+ group_mode = true
113
+ last_group = nil
114
+ first_time = true
115
+ end
116
+
49
117
  o = Gloo::Objs::Alias.resolve_alias( @engine, o )
50
118
  o.children.each do |child|
119
+ if group_mode
120
+ group_value = group_by_value( child )
121
+ # puts "last group = #{last_group}, group_value = #{group_value}"
122
+ if last_group != group_value
123
+ last_group = group_value
124
+ # puts "New group: #{group_value}"
125
+ if first_time
126
+ first_time = false
127
+ else
128
+ run_on_group_end
129
+ end
130
+ run_on_group_start
131
+ end
132
+ end
133
+
51
134
  set_child child
52
135
  @iterator_obj.run_do
53
136
  end
137
+
138
+ if group_mode && !first_time
139
+ run_on_group_end
140
+ end
54
141
  end
55
142
 
56
143
  #
@@ -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
@@ -137,6 +137,8 @@ module Gloo
137
137
 
138
138
  h = {}
139
139
  params_can.children.each do |o|
140
+ # resolve aliases
141
+ o = Gloo::Objs::Alias.resolve_alias( @engine, o )
140
142
  h[ o.name ] = o.value
141
143
  end
142
144
 
@@ -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
@@ -407,6 +408,7 @@ module Gloo
407
408
  def self.messages
408
409
  return super + [ 'start', 'stop',
409
410
  'list_routes', 'list_assets',
411
+ 'add_session_to_response', 'clear_session_data',
410
412
  'list_asset_img', 'list_asset_css', 'list_asset_js' ]
411
413
  end
412
414
 
@@ -494,6 +496,22 @@ module Gloo
494
496
  end
495
497
  end
496
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
+
497
515
 
498
516
  # ---------------------------------------------------------------------
499
517
  # Start and Stop Events
@@ -533,7 +551,7 @@ module Gloo
533
551
  @engine.log.info "Stopping web server…"
534
552
 
535
553
  # Last chance to clear out session data.
536
- clear_session_data
554
+ reset_session_data
537
555
 
538
556
  @web_server.stop
539
557
  @web_server = nil
@@ -570,13 +588,18 @@ module Gloo
570
588
 
571
589
  #
572
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.
573
593
  #
574
- 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
575
598
  o = find_child ON_REQUEST
576
599
  return unless o
577
600
  o = Gloo::Objs::Alias.resolve_alias( @engine, o )
578
601
 
579
- Gloo::Exec::Dispatch.message( @engine, 'run', o )
602
+ Gloo::Exec::Dispatch.message( @engine, 'run', o, CURRENT_PAGE => current_page )
580
603
  end
581
604
 
582
605
  #
@@ -595,6 +618,10 @@ module Gloo
595
618
  # This is done before the on_request event is fired.
596
619
  #
597
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
+
598
625
  data = find_child RESQUEST_DATA
599
626
  return unless data
600
627
  data = Gloo::Objs::Alias.resolve_alias( @engine, data )
@@ -9,7 +9,7 @@ module Gloo
9
9
  class Load < Gloo::Core::Verb
10
10
 
11
11
  KEYWORD = 'load'.freeze
12
- KEYWORD_SHORT = '<'.freeze
12
+ KEYWORD_SHORT = 'ld'.freeze
13
13
  MISSING_EXPR_ERR = 'Missing Expression!'.freeze
14
14
 
15
15
  #
@@ -9,7 +9,7 @@ module Gloo
9
9
  class Save < Gloo::Core::Verb
10
10
 
11
11
  KEYWORD = 'save'.freeze
12
- KEYWORD_SHORT = '>'.freeze
12
+ KEYWORD_SHORT = 'sv'.freeze
13
13
 
14
14
  #
15
15
  # Run the verb.
@@ -9,7 +9,7 @@ module Gloo
9
9
  class Show < Gloo::Core::Verb
10
10
 
11
11
  KEYWORD = 'show'.freeze
12
- KEYWORD_SHORT = '='.freeze
12
+ KEYWORD_SHORT = 'print'.freeze
13
13
 
14
14
  #
15
15
  # Run the verb.
@@ -72,6 +72,14 @@ module Gloo
72
72
  return "<link rel='stylesheet' media='all' href='#{published_name}' />"
73
73
  end
74
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
+
75
83
 
76
84
  # ---------------------------------------------------------------------
77
85
  # Obj Helper Functions
@@ -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
 
@@ -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
@@ -73,8 +73,8 @@ module Gloo
73
73
  end
74
74
 
75
75
  str += "<tr class='#{styles[ ROW ]}'>"
76
- str += "<th style='#{styles[ HEAD_CELL ]}'>#{head[ :title ]}</th>"
77
- str += "<td style='#{styles[ CELL ]}'>#{cell_value}</td>"
76
+ str += "<th class='#{styles[ HEAD_CELL ]}'>#{head[ :title ]}</th>"
77
+ str += "<td class='#{styles[ CELL ]}'>#{cell_value}</td>"
78
78
  str += "</tr>"
79
79
  end
80
80
 
@@ -113,7 +113,7 @@ module Gloo
113
113
  else
114
114
  cell_value = cell
115
115
  end
116
- str += "<td style='#{styles[ CELL ]}'>#{cell_value}</td>"
116
+ str += "<td class='#{styles[ CELL ]}'>#{cell_value}</td>"
117
117
  end
118
118
  str += "</tr>"
119
119
  end
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.7.0
4
+ version: 3.9.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: 2025-01-09 00:00:00.000000000 Z
11
+ date: 2025-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -419,6 +419,12 @@ files:
419
419
  - lib/gloo/expr/l_integer.rb
420
420
  - lib/gloo/expr/l_string.rb
421
421
  - lib/gloo/expr/op_div.rb
422
+ - lib/gloo/expr/op_eq.rb
423
+ - lib/gloo/expr/op_gt.rb
424
+ - lib/gloo/expr/op_gteq.rb
425
+ - lib/gloo/expr/op_ineq.rb
426
+ - lib/gloo/expr/op_lt.rb
427
+ - lib/gloo/expr/op_lteq.rb
422
428
  - lib/gloo/expr/op_minus.rb
423
429
  - lib/gloo/expr/op_mult.rb
424
430
  - lib/gloo/expr/op_plus.rb
@@ -462,6 +468,7 @@ files:
462
468
  - lib/gloo/objs/ror/erb.rb
463
469
  - lib/gloo/objs/ror/eval.rb
464
470
  - lib/gloo/objs/security/cipher.rb
471
+ - lib/gloo/objs/security/csrf_token.rb
465
472
  - lib/gloo/objs/security/password.rb
466
473
  - lib/gloo/objs/system/file_handle.rb
467
474
  - lib/gloo/objs/system/ssh_exec.rb