gloo 3.7.0 → 3.9.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: 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