Skip to content
This repository was archived by the owner on Jul 6, 2024. It is now read-only.

Commit 1025be1

Browse files
committed
refactor: Further improve request interface and session request policies
Request interface has been further improved so that there needs to be no distinction between request with body and without body in the implementation. Add Session Requests Policies to provide special behavior when executing requests on session.
1 parent c3d88d4 commit 1025be1

File tree

15 files changed

+482
-191
lines changed

15 files changed

+482
-191
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ secrecy = "0.8"
2424
anyhow = "1.0"
2525
bytes = "1.4"
2626
log = "0.4"
27+
parking_lot = "0.12"
2728
ureq = {version="2.6", optional=true}
2829

2930

examples/user_id.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use proton_api_rs::{http, ping_async};
2-
use proton_api_rs::{Session, SessionType};
1+
use proton_api_rs::SessionType;
2+
use proton_api_rs::{http, ping_async, DefaultSession};
33
pub use tokio;
44
use tokio::io::{AsyncBufReadExt, AsyncWriteExt};
55

@@ -16,7 +16,7 @@ async fn main() {
1616

1717
ping_async(&client).await.unwrap();
1818

19-
let session = match Session::login_async(&client, &user_email, &user_password)
19+
let session = match DefaultSession::login_async(&client, &user_email, &user_password)
2020
.await
2121
.unwrap()
2222
{

examples/user_id_sync.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use proton_api_rs::clientv2::{ping, Session, SessionType};
2-
use proton_api_rs::http;
1+
use proton_api_rs::clientv2::{ping, SessionType};
2+
use proton_api_rs::{http, DefaultSession};
33
use std::io::{BufRead, Write};
44

55
fn main() {
@@ -16,7 +16,7 @@ fn main() {
1616

1717
ping(&client).unwrap();
1818

19-
let session = match Session::login(&client, &user_email, &user_password).unwrap() {
19+
let session = match DefaultSession::login(&client, &user_email, &user_password).unwrap() {
2020
SessionType::Authenticated(s) => s,
2121
SessionType::AwaitingTotp(mut t) => {
2222
let mut line_reader = std::io::BufReader::new(std::io::stdin());

src/clientv2/client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::http;
2-
use crate::http::RequestNoBody;
2+
use crate::http::Request;
33
use crate::requests::Ping;
44

55
pub fn ping<T: http::ClientSync>(client: &T) -> Result<(), http::Error> {

src/clientv2/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
mod client;
22
mod session;
3+
mod session_policies;
34
mod totp;
45

56
pub use client::*;
67
pub use session::*;
8+
pub use session_policies::*;
79
pub use totp::*;

src/clientv2/session.rs

Lines changed: 77 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use crate::clientv2::TotpSession;
22
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};
54
use crate::requests::{
65
AuthInfoRequest, AuthInfoResponse, AuthRefreshRequest, AuthRequest, AuthResponse,
76
GetEventRequest, GetLatestEventRequest, LogoutRequest, TFAStatus, UserAuth, UserInfoRequest,
87
};
8+
use crate::{http, AutoAuthRefreshRequestPolicy};
99
use go_srp::SRPAuth;
10-
use secrecy::ExposeSecret;
10+
use secrecy::Secret;
11+
use std::future::Future;
12+
use std::pin::Pin;
1113

1214
#[derive(Debug, thiserror::Error)]
1315
pub enum LoginError {
@@ -25,40 +27,61 @@ pub enum LoginError {
2527
SRPProof(String),
2628
}
2729

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+
2860
#[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>),
3264
}
3365

66+
/// Authenticated Session from which one can access data/functionality restricted to authenticated
67+
/// users.
3468
#[derive(Debug)]
35-
pub struct Session {
36-
user: UserAuth,
69+
pub struct Session<P: SessionRequestPolicy> {
70+
pub(super) policy: P,
3771
}
3872

39-
impl Session {
73+
impl<P: SessionRequestPolicy> Session<P> {
4074
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+
}
5378
}
54-
}
5579

56-
impl Session {
5780
pub fn login<T: http::ClientSync>(
5881
client: &T,
5982
username: &str,
6083
password: &str,
61-
) -> Result<SessionType, LoginError> {
84+
) -> Result<SessionType<P>, LoginError> {
6285
let auth_info_response =
6386
AuthInfoRequest { username }.execute_sync::<T>(client, &DefaultRequestFactory {})?;
6487

@@ -79,7 +102,7 @@ impl Session {
79102
client: &T,
80103
username: &str,
81104
password: &str,
82-
) -> Result<SessionType, LoginError> {
105+
) -> Result<SessionType<P>, LoginError> {
83106
let auth_info_response = AuthInfoRequest { username }
84107
.execute_async::<T>(client, &DefaultRequestFactory {})
85108
.await?;
@@ -111,7 +134,6 @@ impl Session {
111134
}
112135

113136
pub fn refresh<T: http::ClientSync>(
114-
&self,
115137
client: &T,
116138
user_uid: &UserUid,
117139
token: &str,
@@ -123,55 +145,71 @@ impl Session {
123145
}
124146

125147
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 {})?;
127149
Ok(user.user)
128150
}
129151

130152
pub async fn get_user_async<T: http::ClientAsync>(
131153
&self,
132154
client: &T,
133155
) -> 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?;
135160
Ok(user.user)
136161
}
137162

138163
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)
140165
}
141166

142167
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
144171
}
145172

146173
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 {})?;
148175
Ok(r.event_id)
149176
}
150177

151178
pub async fn get_latest_event_async<T: http::ClientAsync>(
152179
&self,
153180
client: &T,
154181
) -> 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?;
156186
Ok(r.event_id)
157187
}
158188

159189
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))
161191
}
162192

163193
pub async fn get_event_async<T: http::ClientAsync>(
164194
&self,
165195
client: &T,
166196
id: &EventId,
167197
) -> 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()
169205
}
170206
}
171207

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()
175213
}
176214
}
177215

@@ -191,10 +229,10 @@ fn generate_session_proof(
191229
.map_err(LoginError::ServerProof)
192230
}
193231

194-
fn validate_server_proof(
232+
fn validate_server_proof<P: SessionRequestPolicy>(
195233
proof: &SRPAuth,
196234
auth_response: &AuthResponse,
197-
) -> Result<SessionType, LoginError> {
235+
) -> Result<SessionType<P>, LoginError> {
198236
if proof.expected_server_proof != auth_response.server_proof {
199237
return Err(LoginError::ServerProof(
200238
"Server Proof does not match".to_string(),

0 commit comments

Comments
 (0)