Skip to content

Commit fa1d3d8

Browse files
committed
Reorganize auth
1 parent 60dbce1 commit fa1d3d8

File tree

2 files changed

+126
-118
lines changed

2 files changed

+126
-118
lines changed

src/modules/auth.rs

Lines changed: 2 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,11 @@ use super::bakalari::Client;
22
use reqwest::Response;
33
use std::borrow::Cow;
44
use std::sync::Arc;
5-
use std::time::{Duration, Instant};
65
use thiserror::Error;
7-
use tokio::sync::mpsc;
8-
use tokio::sync::oneshot;
96

10-
/// Struct to hold token that expires after certain time
11-
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
12-
pub struct TempToken {
13-
token: String,
14-
expiration: Instant,
15-
}
16-
17-
/// Lifetime of [`TempToken`] in seconds
18-
const TOKEN_LIFETIME: u64 = 60 * 5;
19-
20-
impl TempToken {
21-
/// Create token with expiration [`TOKEN_LIFETIME`]
22-
fn new(token: String) -> Self {
23-
Self {
24-
token,
25-
expiration: Instant::now() + Duration::from_secs(TOKEN_LIFETIME),
26-
}
27-
}
28-
29-
/// Whether token is expired
30-
fn expired(&self) -> bool {
31-
Instant::now() > self.expiration
32-
}
33-
34-
/// Get reference to token if it is not expired
35-
fn get(&self) -> Option<&str> {
36-
if self.expired() {
37-
return None;
38-
}
39-
Some(&self.token)
40-
}
41-
}
42-
43-
type TokenRequest = (Arc<Client>, oneshot::Sender<LoginResult<String>>);
7+
pub use credentials::Credentials;
448

45-
/// Struct that hold the credentials and token
46-
#[derive(Debug)]
47-
pub struct Credentials {
48-
sender: mpsc::Sender<TokenRequest>,
49-
}
9+
mod credentials;
5010

5111
/// Authentication error
5212
#[derive(Debug, Error)]
@@ -64,82 +24,6 @@ pub enum LoginError {
6424

6525
pub type LoginResult<T> = Result<T, LoginError>;
6626

67-
impl Credentials {
68-
/// Create new credentials from username and password
69-
///
70-
/// # Errors
71-
/// If login fails
72-
pub async fn new((username, password): (String, String), client: &Client) -> LoginResult<Self> {
73-
let token = TempToken::new(Self::login((&username, &password), client).await?);
74-
75-
let (sender, mut receiver) = mpsc::channel::<TokenRequest>(10);
76-
77-
tokio::spawn(async move {
78-
let mut store = token;
79-
80-
while let Some((client, sender)) = receiver.recv().await {
81-
let token = if let Some(token) = store.get() {
82-
Ok(token.to_owned())
83-
} else {
84-
let token = Self::login((&username, &password), &client).await;
85-
token.map(|token| {
86-
store = TempToken::new(token);
87-
store.token.clone()
88-
})
89-
};
90-
sender.send(token).unwrap();
91-
}
92-
});
93-
94-
Ok(Self { sender })
95-
}
96-
97-
/// Get token, and renew in case it expired
98-
///
99-
/// # Errors
100-
/// If renew fails
101-
///
102-
/// # Panics
103-
/// Panics if token expires somehow (shouldn't)
104-
pub async fn get_token(&self, client: Arc<Client>) -> LoginResult<String> {
105-
let (tx, rx) = oneshot::channel();
106-
self.sender
107-
.send((client, tx))
108-
.await
109-
.expect("failed to send token request");
110-
rx.await.unwrap()
111-
}
112-
113-
// Issue new token from api
114-
pub async fn login((username, password): (&str, &str), client: &Client) -> LoginResult<String> {
115-
let res = client
116-
.reqwest_client()
117-
.post(client.url().join("Login").unwrap())
118-
.body(format!("username={username}&password={password}"))
119-
.header("Content-Type", "application/x-www-form-urlencoded")
120-
.send()
121-
.await?;
122-
123-
if res.status().as_u16() != 302 {
124-
return Err(LoginError::Login(res));
125-
}
126-
127-
let v = res
128-
.headers()
129-
.get_all("Set-Cookie")
130-
.iter()
131-
.filter_map(|h| h.to_str().ok())
132-
.filter_map(|h| h.split_once(';'))
133-
.map(|h| h.0)
134-
.filter_map(|h| h.split_once('='))
135-
.find(|h| h.0 == "BakaAuth")
136-
.map(|h| h.1)
137-
.ok_or(LoginError::CookieParse)?;
138-
139-
Ok(v.to_owned())
140-
}
141-
}
142-
14327
/// Authentication types
14428
#[derive(Debug)]
14529
pub enum Auth {

src/modules/auth/credentials.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use super::{LoginError, LoginResult};
2+
use crate::modules::bakalari::Client;
3+
use std::{
4+
sync::Arc,
5+
time::{Duration, Instant},
6+
};
7+
use tokio::sync::{mpsc, oneshot};
8+
9+
/// Struct to hold token that expires after certain time
10+
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
11+
pub struct TempToken {
12+
token: String,
13+
expiration: Instant,
14+
}
15+
16+
/// Lifetime of [`TempToken`] in seconds
17+
const TOKEN_LIFETIME: u64 = 60 * 5;
18+
19+
impl TempToken {
20+
/// Create token with expiration [`TOKEN_LIFETIME`]
21+
fn new(token: String) -> Self {
22+
Self {
23+
token,
24+
expiration: Instant::now() + Duration::from_secs(TOKEN_LIFETIME),
25+
}
26+
}
27+
28+
/// Whether token is expired
29+
fn expired(&self) -> bool {
30+
Instant::now() > self.expiration
31+
}
32+
33+
/// Get reference to token if it is not expired
34+
fn get(&self) -> Option<&str> {
35+
if self.expired() {
36+
return None;
37+
}
38+
Some(&self.token)
39+
}
40+
}
41+
42+
type TokenRequest = (Arc<Client>, oneshot::Sender<LoginResult<String>>);
43+
44+
/// Struct that hold the credentials and token
45+
#[derive(Debug)]
46+
pub struct Credentials {
47+
sender: mpsc::Sender<TokenRequest>,
48+
}
49+
50+
impl Credentials {
51+
/// Create new credentials from username and password
52+
///
53+
/// # Errors
54+
/// If login fails
55+
pub async fn new((username, password): (String, String), client: &Client) -> LoginResult<Self> {
56+
let token = TempToken::new(Self::login((&username, &password), client).await?);
57+
58+
let (sender, mut receiver) = mpsc::channel::<TokenRequest>(10);
59+
60+
tokio::spawn(async move {
61+
let mut store = token;
62+
63+
while let Some((client, sender)) = receiver.recv().await {
64+
let token = if let Some(token) = store.get() {
65+
Ok(token.to_owned())
66+
} else {
67+
let token = Self::login((&username, &password), &client).await;
68+
token.map(|token| {
69+
store = TempToken::new(token);
70+
store.token.clone()
71+
})
72+
};
73+
sender.send(token).unwrap();
74+
}
75+
});
76+
77+
Ok(Self { sender })
78+
}
79+
80+
/// Get token, and renew in case it expired
81+
///
82+
/// # Errors
83+
/// If renew fails
84+
///
85+
/// # Panics
86+
/// Panics if token expires somehow (shouldn't)
87+
pub async fn get_token(&self, client: Arc<Client>) -> LoginResult<String> {
88+
let (tx, rx) = oneshot::channel();
89+
self.sender
90+
.send((client, tx))
91+
.await
92+
.expect("failed to send token request");
93+
rx.await.unwrap()
94+
}
95+
96+
// Issue new token from api
97+
pub async fn login((username, password): (&str, &str), client: &Client) -> LoginResult<String> {
98+
let res = client
99+
.reqwest_client()
100+
.post(client.url().join("Login").unwrap())
101+
.body(format!("username={username}&password={password}"))
102+
.header("Content-Type", "application/x-www-form-urlencoded")
103+
.send()
104+
.await?;
105+
106+
if res.status().as_u16() != 302 {
107+
return Err(LoginError::Login(res));
108+
}
109+
110+
let v = res
111+
.headers()
112+
.get_all("Set-Cookie")
113+
.iter()
114+
.filter_map(|h| h.to_str().ok())
115+
.filter_map(|h| h.split_once(';'))
116+
.map(|h| h.0)
117+
.filter_map(|h| h.split_once('='))
118+
.find(|h| h.0 == "BakaAuth")
119+
.map(|h| h.1)
120+
.ok_or(LoginError::CookieParse)?;
121+
122+
Ok(v.to_owned())
123+
}
124+
}

0 commit comments

Comments
 (0)