@@ -11,11 +11,17 @@ use std::borrow::Cow;
11
11
use std:: sync:: Arc ;
12
12
13
13
pub enum PageContext < W : std:: io:: Write > {
14
+ /// Indicates that we should stay in the header context
14
15
Header ( HeaderContext < W > ) ,
16
+
17
+ /// Indicates that we should start rendering the body
15
18
Body {
16
19
http_response : HttpResponseBuilder ,
17
20
renderer : RenderContext < W > ,
18
21
} ,
22
+
23
+ /// The headers have been set, but no response body should be sent
24
+ Close ( HeaderContext < W > ) ,
19
25
}
20
26
21
27
/// Handles the first SQL statements, before the headers have been sent to
@@ -42,7 +48,9 @@ impl<W: std::io::Write> HeaderContext<W> {
42
48
match get_object_str ( & data, "component" ) {
43
49
Some ( "status_code" ) => self . status_code ( & data) . map ( PageContext :: Header ) ,
44
50
Some ( "http_header" ) => self . add_http_header ( & data) . map ( PageContext :: Header ) ,
51
+ Some ( "redirect" ) => self . redirect ( & data) . map ( PageContext :: Close ) ,
45
52
Some ( "cookie" ) => self . add_cookie ( & data) . map ( PageContext :: Header ) ,
53
+ Some ( "authentication" ) => self . authentication ( & data) ,
46
54
_ => self . start_body ( data) . await ,
47
55
}
48
56
}
@@ -123,6 +131,40 @@ impl<W: std::io::Write> HeaderContext<W> {
123
131
Ok ( self )
124
132
}
125
133
134
+ fn redirect ( mut self , data : & JsonValue ) -> anyhow:: Result < Self > {
135
+ self . response . status ( StatusCode :: FOUND ) ;
136
+ self . has_status = true ;
137
+ let link = get_object_str ( data, "link" )
138
+ . with_context ( || "The redirect component requires a 'link' property" ) ?;
139
+ self . response . insert_header ( ( header:: LOCATION , link) ) ;
140
+ Ok ( self )
141
+ }
142
+
143
+ fn authentication ( mut self , data : & JsonValue ) -> anyhow:: Result < PageContext < W > > {
144
+ use argon2:: Argon2 ;
145
+ use password_hash:: PasswordHash ;
146
+ let link = get_object_str ( data, "link" )
147
+ . with_context ( || "The authentication component requires a 'link' property" ) ?;
148
+ let password_hash = get_object_str ( data, "password_hash" ) ;
149
+ let password = get_object_str ( data, "password" ) ;
150
+ if let ( Some ( password) , Some ( password_hash) ) = ( password, password_hash) {
151
+ match PasswordHash :: new ( password_hash)
152
+ . map_err ( |e| {
153
+ anyhow:: anyhow!( "invalid value for the password_hash property: {}" , e)
154
+ } ) ?
155
+ . verify_password ( & [ & Argon2 :: default ( ) ] , password)
156
+ {
157
+ Ok ( ( ) ) => return Ok ( PageContext :: Header ( self ) ) ,
158
+ Err ( e) => log:: info!( "User authentication failed: {}" , e) ,
159
+ }
160
+ }
161
+ // The authentication failed
162
+ self . response . status ( StatusCode :: FOUND ) ;
163
+ self . response . insert_header ( ( header:: LOCATION , link) ) ;
164
+ self . has_status = true ;
165
+ Ok ( PageContext :: Close ( self ) )
166
+ }
167
+
126
168
async fn start_body ( self , data : JsonValue ) -> anyhow:: Result < PageContext < W > > {
127
169
let renderer = RenderContext :: new ( self . app_state , self . writer , data)
128
170
. await
0 commit comments