feat(client >> session): implemented session authentiaction with credentials
This commit is contained in:
parent
1906d66026
commit
c88da7fddb
|
@ -1,5 +1,8 @@
|
||||||
#[derive(Debug, thiserror::Error, Clone)]
|
#[derive(Debug, thiserror::Error, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
#[error("Failed to authenticate with the given password")]
|
||||||
|
WrongPassword,
|
||||||
|
|
||||||
#[error("Failed to handle a server response: {0}")]
|
#[error("Failed to handle a server response: {0}")]
|
||||||
BadResponse(String),
|
BadResponse(String),
|
||||||
|
|
||||||
|
@ -14,4 +17,7 @@ pub enum Error {
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
|
|
||||||
|
#[error("The user was not found.")]
|
||||||
|
UserNotFound,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ pub use session::Session;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn authorized_session() {
|
async fn authorized_session() {
|
||||||
|
@ -15,7 +17,19 @@ mod tests {
|
||||||
.expect("The environment variable ICRC_TEST_AUTH needs to be set!");
|
.expect("The environment variable ICRC_TEST_AUTH needs to be set!");
|
||||||
|
|
||||||
let session = Session::new(&host, Some(token)).await.unwrap();
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,22 @@ pub mod api {
|
||||||
pub mod account {
|
pub mod account {
|
||||||
use super::*;
|
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 {
|
pub mod info {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
pub(self) mod data;
|
pub(self) mod data;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
|
use reqwest::Response;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
struct AuthorizedSession {
|
struct AuthorizedSession {
|
||||||
token: String,
|
pub(self) token: String,
|
||||||
userid: Uuid,
|
pub(self) userid: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Session is a connection to an ICRC Server.
|
/// 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,
|
/// but an authorized session uses Bearer Authentication to get access to resources,
|
||||||
/// which are not publicly accessible.
|
/// which are not publicly accessible.
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
host: String,
|
pub(self) host: String,
|
||||||
client: reqwest::Client,
|
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 {
|
impl Session {
|
||||||
|
@ -48,36 +84,10 @@ impl Session {
|
||||||
let request = self
|
let request = self
|
||||||
.client
|
.client
|
||||||
.get(format!("{host}/account/info", host = self.host))
|
.get(format!("{host}/account/info", host = self.host))
|
||||||
.bearer_auth(&token);
|
.bearer_auth(&token)
|
||||||
|
.send();
|
||||||
|
|
||||||
let response = match request.send().await {
|
let response = parse_response::<data::api::account::info::Response>(request.await).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()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.auth = Some(AuthorizedSession {
|
self.auth = Some(AuthorizedSession {
|
||||||
token,
|
token,
|
||||||
|
@ -87,4 +97,30 @@ impl Session {
|
||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue