feat(client >> session): implemented session authentiaction with credentials

This commit is contained in:
antifallobst 2024-03-31 17:55:53 +02:00
parent 1906d66026
commit c88da7fddb
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
4 changed files with 107 additions and 35 deletions

View File

@ -1,5 +1,8 @@
#[derive(Debug, thiserror::Error, Clone)]
pub enum Error {
#[error("Failed to authenticate with the given password")]
WrongPassword,
#[error("Failed to handle a server response: {0}")]
BadResponse(String),
@ -14,4 +17,7 @@ pub enum Error {
#[error("{0}")]
Unknown(String),
#[error("The user was not found.")]
UserNotFound,
}

View File

@ -6,6 +6,8 @@ pub use session::Session;
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
use uuid::Uuid;
#[tokio::test]
async fn authorized_session() {
@ -15,7 +17,19 @@ mod tests {
.expect("The environment variable ICRC_TEST_AUTH needs to be set!");
let session = Session::new(&host, Some(token)).await.unwrap();
}
// assert_eq!(result, 4);
#[tokio::test]
async fn authenticate() {
let host = std::env::var("ICRC_TEST_HOST")
.expect("The environment variable ICRC_TEST_HOST needs to be set!");
let userid = std::env::var("ICRC_TEST_USER")
.expect("The environment variable ICRC_TEST_USER needs to be set!");
let mut session = Session::new(&host, None).await.unwrap();
session
.auth_with_credentials(Uuid::from_str(&userid).unwrap(), "test".to_string())
.await
.unwrap()
}
}

View File

@ -12,6 +12,22 @@ pub mod api {
pub mod account {
use super::*;
pub mod auth {
use super::*;
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct Request {
pub userid: String,
pub password: String,
}
#[derive(Debug, Deserialize)]
pub struct Response {
pub token: String,
}
}
pub mod info {
use super::*;

View File

@ -1,12 +1,14 @@
pub(self) mod data;
use crate::error::Error;
use reqwest::Response;
use serde::de::DeserializeOwned;
use std::str::FromStr;
use uuid::Uuid;
struct AuthorizedSession {
token: String,
userid: Uuid,
pub(self) token: String,
pub(self) userid: Uuid,
}
/// A Session is a connection to an ICRC Server.
@ -15,10 +17,44 @@ struct AuthorizedSession {
/// but an authorized session uses Bearer Authentication to get access to resources,
/// which are not publicly accessible.
pub struct Session {
host: String,
client: reqwest::Client,
pub(self) host: String,
pub(self) client: reqwest::Client,
auth: Option<AuthorizedSession>,
pub(self) auth: Option<AuthorizedSession>,
}
pub(self) async fn parse_response<T: DeserializeOwned>(
response: Result<Response, reqwest::Error>,
) -> Result<T, Error> {
match response {
Ok(resp) => {
if resp.status().is_success() {
resp.json::<T>()
.await
.map_err(|e| Error::BadResponse(e.to_string()))
} else {
return Err(
match resp
.json::<data::api::error::Body>()
.await
.map_err(|e| Error::BadResponse(e.to_string()))?
.error
{
data::api::error::Error::InvalidToken => Error::InvalidToken,
data::api::error::Error::TokenExpired => Error::TokenExpired,
e => Error::BadResponse(format!("Unknown Error/{e} in this context")),
},
);
}
}
Err(e) => {
if e.is_connect() || e.is_timeout() {
Err(Error::ConnectionError)
} else {
Err(Error::Unknown(e.to_string()))
}
}
}
}
impl Session {
@ -48,36 +84,10 @@ impl Session {
let request = self
.client
.get(format!("{host}/account/info", host = self.host))
.bearer_auth(&token);
.bearer_auth(&token)
.send();
let response = match request.send().await {
Ok(resp) => {
if resp.status().is_success() {
resp.json::<data::api::account::info::Response>()
.await
.map_err(|e| Error::BadResponse(e.to_string()))?
} else {
return Err(
match resp
.json::<data::api::error::Body>()
.await
.map_err(|e| Error::BadResponse(e.to_string()))?
.error
{
data::api::error::Error::InvalidToken => Error::InvalidToken,
data::api::error::Error::TokenExpired => Error::TokenExpired,
e => Error::BadResponse(format!("Unknown Error/{e} in this context")),
},
);
}
}
Err(e) => {
if e.is_connect() || e.is_timeout() {
return Err(Error::ConnectionError);
}
return Err(Error::Unknown(e.to_string()));
}
};
let response = parse_response::<data::api::account::info::Response>(request.await).await?;
self.auth = Some(AuthorizedSession {
token,
@ -87,4 +97,30 @@ impl Session {
Ok(())
}
/// Tries to authorize the session. If the credentials are not accepted by the server, this
/// leads to a [crate::error::Error::AuthenticationFailure] or [crate::error::Error::UserNotFound] error.
pub async fn auth_with_credentials(
&mut self,
userid: Uuid,
password: String,
) -> Result<(), Error> {
let request = self
.client
.post(format!("{host}/account/auth", host = self.host))
.json(&data::api::account::auth::Request {
userid: userid.to_string(),
password,
})
.send();
let response = parse_response::<data::api::account::auth::Response>(request.await).await?;
self.auth = Some(AuthorizedSession {
token: response.token,
userid,
});
Ok(())
}
}