1
1
use crate :: templates:: SplitTemplate ;
2
- use crate :: webserver:: http:: LayoutContext ;
2
+ use crate :: webserver:: http:: RequestContext ;
3
3
use crate :: webserver:: ErrorWithStatus ;
4
4
use crate :: AppState ;
5
5
use actix_web:: cookie:: time:: format_description:: well_known:: Rfc3339 ;
@@ -15,9 +15,9 @@ use serde_json::{json, Value};
15
15
use std:: borrow:: Cow ;
16
16
use std:: sync:: Arc ;
17
17
18
- pub enum PageContext < ' a , W : std:: io:: Write > {
18
+ pub enum PageContext < W : std:: io:: Write > {
19
19
/// Indicates that we should stay in the header context
20
- Header ( HeaderContext < ' a , W > ) ,
20
+ Header ( HeaderContext < W > ) ,
21
21
22
22
/// Indicates that we should start rendering the body
23
23
Body {
@@ -30,27 +30,28 @@ pub enum PageContext<'a, W: std::io::Write> {
30
30
}
31
31
32
32
/// Handles the first SQL statements, before the headers have been sent to
33
- pub struct HeaderContext < ' a , W : std:: io:: Write > {
33
+ pub struct HeaderContext < W : std:: io:: Write > {
34
34
app_state : Arc < AppState > ,
35
- layout_context : & ' a LayoutContext ,
35
+ request_context : RequestContext ,
36
36
pub writer : W ,
37
37
response : HttpResponseBuilder ,
38
38
has_status : bool ,
39
39
}
40
40
41
- impl < ' a , W : std:: io:: Write > HeaderContext < ' a , W > {
42
- pub fn new ( app_state : Arc < AppState > , layout_context : & ' a LayoutContext , writer : W ) -> Self {
41
+ impl < ' a , W : std:: io:: Write > HeaderContext < W > {
42
+ pub fn new ( app_state : Arc < AppState > , request_context : RequestContext , writer : W ) -> Self {
43
43
let mut response = HttpResponseBuilder :: new ( StatusCode :: OK ) ;
44
44
response. content_type ( "text/html; charset=utf-8" ) ;
45
+ response. insert_header ( & request_context. content_security_policy ) ;
45
46
Self {
46
47
app_state,
47
- layout_context ,
48
+ request_context ,
48
49
writer,
49
50
response,
50
51
has_status : false ,
51
52
}
52
53
}
53
- pub async fn handle_row ( self , data : JsonValue ) -> anyhow:: Result < PageContext < ' a , W > > {
54
+ pub async fn handle_row ( self , data : JsonValue ) -> anyhow:: Result < PageContext < W > > {
54
55
log:: debug!( "Handling header row: {data}" ) ;
55
56
match get_object_str ( & data, "component" ) {
56
57
Some ( "status_code" ) => self . status_code ( & data) . map ( PageContext :: Header ) ,
@@ -63,7 +64,7 @@ impl<'a, W: std::io::Write> HeaderContext<'a, W> {
63
64
}
64
65
}
65
66
66
- pub async fn handle_error ( self , err : anyhow:: Error ) -> anyhow:: Result < PageContext < ' a , W > > {
67
+ pub async fn handle_error ( self , err : anyhow:: Error ) -> anyhow:: Result < PageContext < W > > {
67
68
if self . app_state . config . environment . is_prod ( ) {
68
69
return Err ( err) ;
69
70
}
@@ -198,7 +199,7 @@ impl<'a, W: std::io::Write> HeaderContext<'a, W> {
198
199
Ok ( self . response . body ( json_response) )
199
200
}
200
201
201
- async fn authentication ( mut self , mut data : JsonValue ) -> anyhow:: Result < PageContext < ' a , W > > {
202
+ async fn authentication ( mut self , mut data : JsonValue ) -> anyhow:: Result < PageContext < W > > {
202
203
let password_hash = take_object_str ( & mut data, "password_hash" ) ;
203
204
let password = take_object_str ( & mut data, "password" ) ;
204
205
if let ( Some ( password) , Some ( password_hash) ) = ( password, password_hash) {
@@ -228,8 +229,8 @@ impl<'a, W: std::io::Write> HeaderContext<'a, W> {
228
229
Ok ( PageContext :: Close ( http_response) )
229
230
}
230
231
231
- async fn start_body ( self , data : JsonValue ) -> anyhow:: Result < PageContext < ' a , W > > {
232
- let renderer = RenderContext :: new ( self . app_state , self . layout_context , self . writer , data)
232
+ async fn start_body ( self , data : JsonValue ) -> anyhow:: Result < PageContext < W > > {
233
+ let renderer = RenderContext :: new ( self . app_state , self . request_context , self . writer , data)
233
234
. await
234
235
. with_context ( || "Failed to create a render context from the header context." ) ?;
235
236
let http_response = self . response ;
@@ -287,6 +288,7 @@ pub struct RenderContext<W: std::io::Write> {
287
288
current_component : Option < SplitTemplateRenderer > ,
288
289
shell_renderer : SplitTemplateRenderer ,
289
290
current_statement : usize ,
291
+ request_context : RequestContext ,
290
292
}
291
293
292
294
const DEFAULT_COMPONENT : & str = "table" ;
@@ -296,7 +298,7 @@ const FRAGMENT_SHELL_COMPONENT: &str = "shell-empty";
296
298
impl < W : std:: io:: Write > RenderContext < W > {
297
299
pub async fn new (
298
300
app_state : Arc < AppState > ,
299
- layout_context : & LayoutContext ,
301
+ request_context : RequestContext ,
300
302
mut writer : W ,
301
303
initial_row : JsonValue ,
302
304
) -> anyhow:: Result < RenderContext < W > > {
@@ -309,7 +311,7 @@ impl<W: std::io::Write> RenderContext<W> {
309
311
. and_then ( |c| get_object_str ( c, "component" ) )
310
312
. is_some_and ( Self :: is_shell_component)
311
313
{
312
- let default_shell = if layout_context . is_embedded {
314
+ let default_shell = if request_context . is_embedded {
313
315
FRAGMENT_SHELL_COMPONENT
314
316
} else {
315
317
PAGE_SHELL_COMPONENT
@@ -329,6 +331,7 @@ impl<W: std::io::Write> RenderContext<W> {
329
331
get_object_str ( & shell_row, "component" ) . expect ( "shell should exist" ) ,
330
332
Arc :: clone ( & app_state) ,
331
333
0 ,
334
+ request_context. content_security_policy . nonce ,
332
335
)
333
336
. await
334
337
. with_context ( || "The shell component should always exist" ) ?;
@@ -341,6 +344,7 @@ impl<W: std::io::Write> RenderContext<W> {
341
344
current_component : None ,
342
345
shell_renderer,
343
346
current_statement : 1 ,
347
+ request_context,
344
348
} ;
345
349
346
350
for row in rows_iter {
@@ -461,6 +465,7 @@ impl<W: std::io::Write> RenderContext<W> {
461
465
component : & str ,
462
466
app_state : Arc < AppState > ,
463
467
component_index : usize ,
468
+ nonce : u64 ,
464
469
) -> anyhow:: Result < SplitTemplateRenderer > {
465
470
let split_template = app_state
466
471
. all_templates
@@ -470,6 +475,7 @@ impl<W: std::io::Write> RenderContext<W> {
470
475
split_template,
471
476
app_state,
472
477
component_index,
478
+ nonce,
473
479
) )
474
480
}
475
481
@@ -486,6 +492,7 @@ impl<W: std::io::Write> RenderContext<W> {
486
492
component,
487
493
Arc :: clone ( & self . app_state ) ,
488
494
current_component_index + 1 ,
495
+ self . request_context . content_security_policy . nonce ,
489
496
)
490
497
. await ?;
491
498
Ok ( self . current_component . replace ( new_component) )
@@ -543,13 +550,15 @@ pub struct SplitTemplateRenderer {
543
550
app_state : Arc < AppState > ,
544
551
row_index : usize ,
545
552
component_index : usize ,
553
+ nonce : JsonValue ,
546
554
}
547
555
548
556
impl SplitTemplateRenderer {
549
557
fn new (
550
558
split_template : Arc < SplitTemplate > ,
551
559
app_state : Arc < AppState > ,
552
560
component_index : usize ,
561
+ nonce : u64 ,
553
562
) -> Self {
554
563
Self {
555
564
split_template,
@@ -558,6 +567,7 @@ impl SplitTemplateRenderer {
558
567
row_index : 0 ,
559
568
ctx : Context :: null ( ) ,
560
569
component_index,
570
+ nonce : nonce. into ( ) ,
561
571
}
562
572
}
563
573
fn name ( & self ) -> & str {
@@ -581,13 +591,15 @@ impl SplitTemplateRenderer {
581
591
. unwrap_or_default( ) ,
582
592
) ;
583
593
let mut render_context = handlebars:: RenderContext :: new ( None ) ;
584
- render_context
594
+ let blk = render_context
585
595
. block_mut ( )
586
- . expect ( "context created without block" )
587
- . set_local_var (
588
- "component_index" ,
589
- JsonValue :: Number ( self . component_index . into ( ) ) ,
590
- ) ;
596
+ . expect ( "context created without block" ) ;
597
+ blk. set_local_var (
598
+ "component_index" ,
599
+ JsonValue :: Number ( self . component_index . into ( ) ) ,
600
+ ) ;
601
+ blk. set_local_var ( "csp_nonce" , self . nonce . clone ( ) ) ;
602
+
591
603
* self . ctx . data_mut ( ) = data;
592
604
let mut output = HandlebarWriterOutput ( writer) ;
593
605
self . split_template . before_list . render (
@@ -618,6 +630,7 @@ impl SplitTemplateRenderer {
618
630
let mut blk = BlockContext :: new ( ) ;
619
631
blk. set_base_value ( data) ;
620
632
blk. set_local_var ( "row_index" , JsonValue :: Number ( self . row_index . into ( ) ) ) ;
633
+ blk. set_local_var ( "csp_nonce" , self . nonce . clone ( ) ) ;
621
634
render_context. push_block ( blk) ;
622
635
let mut output = HandlebarWriterOutput ( writer) ;
623
636
self . split_template . list_content . render (
@@ -646,6 +659,7 @@ impl SplitTemplateRenderer {
646
659
if let Some ( mut local_vars) = self . local_vars . take ( ) {
647
660
let mut render_context = handlebars:: RenderContext :: new ( None ) ;
648
661
local_vars. put ( "row_index" , self . row_index . into ( ) ) ;
662
+ local_vars. put ( "csp_nonce" , self . nonce . clone ( ) ) ;
649
663
log:: trace!( "Rendering the after_list template with the following local variables: {local_vars:?}" ) ;
650
664
* render_context
651
665
. block_mut ( )
@@ -681,7 +695,7 @@ mod tests {
681
695
let mut output = Vec :: new ( ) ;
682
696
let config = app_config:: tests:: test_config ( ) ;
683
697
let app_state = Arc :: new ( AppState :: init ( & config) . await . unwrap ( ) ) ;
684
- let mut rdr = SplitTemplateRenderer :: new ( Arc :: new ( split) , app_state, 0 ) ;
698
+ let mut rdr = SplitTemplateRenderer :: new ( Arc :: new ( split) , app_state, 0 , 0 ) ;
685
699
rdr. render_start ( & mut output, json ! ( { "name" : "SQL" } ) ) ?;
686
700
rdr. render_item ( & mut output, json ! ( { "x" : 1 } ) ) ?;
687
701
rdr. render_item ( & mut output, json ! ( { "x" : 2 } ) ) ?;
@@ -702,7 +716,7 @@ mod tests {
702
716
let mut output = Vec :: new ( ) ;
703
717
let config = app_config:: tests:: test_config ( ) ;
704
718
let app_state = Arc :: new ( AppState :: init ( & config) . await . unwrap ( ) ) ;
705
- let mut rdr = SplitTemplateRenderer :: new ( Arc :: new ( split) , app_state, 0 ) ;
719
+ let mut rdr = SplitTemplateRenderer :: new ( Arc :: new ( split) , app_state, 0 , 0 ) ;
706
720
rdr. render_start ( & mut output, json ! ( null) ) ?;
707
721
rdr. render_item ( & mut output, json ! ( { "x" : 1 } ) ) ?;
708
722
rdr. render_item ( & mut output, json ! ( { "x" : 2 } ) ) ?;
0 commit comments