1
1
use crate :: clientv2:: TotpSession ;
2
2
use crate :: domain:: { Event , EventId , TwoFactorAuth , User , UserUid } ;
3
- use crate :: http;
4
- use crate :: http:: { DefaultRequestFactory , RequestFactory , RequestNoBody , RequestWithBody } ;
3
+ use crate :: http:: { DefaultRequestFactory , Request , RequestFactory } ;
5
4
use crate :: requests:: {
6
5
AuthInfoRequest , AuthInfoResponse , AuthRefreshRequest , AuthRequest , AuthResponse ,
7
6
GetEventRequest , GetLatestEventRequest , LogoutRequest , TFAStatus , UserAuth , UserInfoRequest ,
8
7
} ;
8
+ use crate :: { http, AutoAuthRefreshRequestPolicy } ;
9
9
use go_srp:: SRPAuth ;
10
- use secrecy:: ExposeSecret ;
10
+ use secrecy:: Secret ;
11
+ use std:: future:: Future ;
12
+ use std:: pin:: Pin ;
11
13
12
14
#[ derive( Debug , thiserror:: Error ) ]
13
15
pub enum LoginError {
@@ -25,40 +27,61 @@ pub enum LoginError {
25
27
SRPProof ( String ) ,
26
28
}
27
29
30
+ /// Trait to access the underlying user authentication data in a session policy.
31
+ pub trait UserAuthFromSessionRequestPolicy {
32
+ fn user_auth ( & self ) -> & UserAuth ;
33
+ fn user_auth_mut ( & mut self ) -> & mut UserAuth ;
34
+ }
35
+
36
+ /// Data which can be used to save a session and restore it later.
37
+ pub struct SessionRefreshData {
38
+ pub user_uid : Secret < UserUid > ,
39
+ pub token : Secret < String > ,
40
+ }
41
+
42
+ /// Session Request Policy can be used to add custom behavior to a session request, such as
43
+ /// retrying on network errors, respecting 429 codes, etc...
44
+ pub trait SessionRequestPolicy : From < UserAuth > + RequestFactory {
45
+ fn execute < C : http:: ClientSync , R : Request > (
46
+ & self ,
47
+ client : & C ,
48
+ request : R ,
49
+ ) -> http:: Result < R :: Output > ;
50
+
51
+ fn execute_async < ' a , C : http:: ClientAsync , R : Request + ' a > (
52
+ & ' a self ,
53
+ client : & ' a C ,
54
+ request : R ,
55
+ ) -> Pin < Box < dyn Future < Output = http:: Result < R :: Output > > + ' a > > ;
56
+
57
+ fn get_refresh_data ( & self ) -> SessionRefreshData ;
58
+ }
59
+
28
60
#[ derive( Debug ) ]
29
- pub enum SessionType {
30
- Authenticated ( Session ) ,
31
- AwaitingTotp ( TotpSession ) ,
61
+ pub enum SessionType < P : SessionRequestPolicy > {
62
+ Authenticated ( Session < P > ) ,
63
+ AwaitingTotp ( TotpSession < P > ) ,
32
64
}
33
65
66
+ /// Authenticated Session from which one can access data/functionality restricted to authenticated
67
+ /// users.
34
68
#[ derive( Debug ) ]
35
- pub struct Session {
36
- user : UserAuth ,
69
+ pub struct Session < P : SessionRequestPolicy > {
70
+ pub ( super ) policy : P ,
37
71
}
38
72
39
- impl Session {
73
+ impl < P : SessionRequestPolicy > Session < P > {
40
74
fn new ( user : UserAuth ) -> Self {
41
- Self { user }
42
- }
43
-
44
- fn apply_auth_token ( & self , request : http:: Request ) -> http:: Request {
45
- request
46
- . header ( http:: X_PM_UID_HEADER , & self . user . uid . expose_secret ( ) . 0 )
47
- . bearer_token ( self . user . access_token . expose_secret ( ) )
48
- }
49
-
50
- fn new_request ( & self , method : http:: Method , url : & str ) -> http:: Request {
51
- let request = http:: Request :: new ( method, url) ;
52
- self . apply_auth_token ( request)
75
+ Self {
76
+ policy : P :: from ( user) ,
77
+ }
53
78
}
54
- }
55
79
56
- impl Session {
57
80
pub fn login < T : http:: ClientSync > (
58
81
client : & T ,
59
82
username : & str ,
60
83
password : & str ,
61
- ) -> Result < SessionType , LoginError > {
84
+ ) -> Result < SessionType < P > , LoginError > {
62
85
let auth_info_response =
63
86
AuthInfoRequest { username } . execute_sync :: < T > ( client, & DefaultRequestFactory { } ) ?;
64
87
@@ -79,7 +102,7 @@ impl Session {
79
102
client : & T ,
80
103
username : & str ,
81
104
password : & str ,
82
- ) -> Result < SessionType , LoginError > {
105
+ ) -> Result < SessionType < P > , LoginError > {
83
106
let auth_info_response = AuthInfoRequest { username }
84
107
. execute_async :: < T > ( client, & DefaultRequestFactory { } )
85
108
. await ?;
@@ -111,7 +134,6 @@ impl Session {
111
134
}
112
135
113
136
pub fn refresh < T : http:: ClientSync > (
114
- & self ,
115
137
client : & T ,
116
138
user_uid : & UserUid ,
117
139
token : & str ,
@@ -123,55 +145,71 @@ impl Session {
123
145
}
124
146
125
147
pub fn get_user < T : http:: ClientSync > ( & self , client : & T ) -> Result < User , http:: Error > {
126
- let user = UserInfoRequest { } . execute_sync :: < T > ( client, self ) ?;
148
+ let user = self . policy . execute ( client, UserInfoRequest { } ) ?;
127
149
Ok ( user. user )
128
150
}
129
151
130
152
pub async fn get_user_async < T : http:: ClientAsync > (
131
153
& self ,
132
154
client : & T ,
133
155
) -> Result < User , http:: Error > {
134
- let user = UserInfoRequest { } . execute_async :: < T > ( client, self ) . await ?;
156
+ let user = self
157
+ . policy
158
+ . execute_async ( client, UserInfoRequest { } )
159
+ . await ?;
135
160
Ok ( user. user )
136
161
}
137
162
138
163
pub fn logout < T : http:: ClientSync > ( & self , client : & T ) -> Result < ( ) , http:: Error > {
139
- LogoutRequest { } . execute_sync :: < T > ( client, self )
164
+ LogoutRequest { } . execute_sync :: < T > ( client, & self . policy )
140
165
}
141
166
142
167
pub async fn logout_async < T : http:: ClientAsync > ( & self , client : & T ) -> Result < ( ) , http:: Error > {
143
- LogoutRequest { } . execute_async :: < T > ( client, self ) . await
168
+ LogoutRequest { }
169
+ . execute_async :: < T > ( client, & self . policy )
170
+ . await
144
171
}
145
172
146
173
pub fn get_latest_event < T : http:: ClientSync > ( & self , client : & T ) -> http:: Result < EventId > {
147
- let r = GetLatestEventRequest . execute_sync ( client, self ) ?;
174
+ let r = self . policy . execute ( client, GetLatestEventRequest { } ) ?;
148
175
Ok ( r. event_id )
149
176
}
150
177
151
178
pub async fn get_latest_event_async < T : http:: ClientAsync > (
152
179
& self ,
153
180
client : & T ,
154
181
) -> http:: Result < EventId > {
155
- let r = GetLatestEventRequest . execute_async ( client, self ) . await ?;
182
+ let r = self
183
+ . policy
184
+ . execute_async ( client, GetLatestEventRequest { } )
185
+ . await ?;
156
186
Ok ( r. event_id )
157
187
}
158
188
159
189
pub fn get_event < T : http:: ClientSync > ( & self , client : & T , id : & EventId ) -> http:: Result < Event > {
160
- GetEventRequest :: new ( id) . execute_sync ( client , self )
190
+ self . policy . execute ( client , GetEventRequest :: new ( id) )
161
191
}
162
192
163
193
pub async fn get_event_async < T : http:: ClientAsync > (
164
194
& self ,
165
195
client : & T ,
166
196
id : & EventId ,
167
197
) -> http:: Result < Event > {
168
- GetEventRequest :: new ( id) . execute_async ( client, self ) . await
198
+ self . policy
199
+ . execute_async ( client, GetEventRequest :: new ( id) )
200
+ . await
201
+ }
202
+
203
+ pub fn get_refresh_data ( & self ) -> SessionRefreshData {
204
+ self . policy . get_refresh_data ( )
169
205
}
170
206
}
171
207
172
- impl RequestFactory for Session {
173
- fn new_request ( & self , method : http:: Method , url : & str ) -> http:: Request {
174
- self . new_request ( method, url)
208
+ impl < T : SessionRequestPolicy + UserAuthFromSessionRequestPolicy >
209
+ Session < AutoAuthRefreshRequestPolicy < T > >
210
+ {
211
+ pub fn was_auth_refreshed ( & self ) -> bool {
212
+ self . policy . was_auth_refreshed ( )
175
213
}
176
214
}
177
215
@@ -191,10 +229,10 @@ fn generate_session_proof(
191
229
. map_err ( LoginError :: ServerProof )
192
230
}
193
231
194
- fn validate_server_proof (
232
+ fn validate_server_proof < P : SessionRequestPolicy > (
195
233
proof : & SRPAuth ,
196
234
auth_response : & AuthResponse ,
197
- ) -> Result < SessionType , LoginError > {
235
+ ) -> Result < SessionType < P > , LoginError > {
198
236
if proof. expected_server_proof != auth_response. server_proof {
199
237
return Err ( LoginError :: ServerProof (
200
238
"Server Proof does not match" . to_string ( ) ,
0 commit comments