feat(server >> api): implemented an endpoint that gives the client information about their authenticated account

This commit is contained in:
antifallobst 2024-03-31 13:47:36 +02:00
parent 8f8e2c5665
commit 5db4d4869c
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
6 changed files with 69 additions and 4 deletions

View File

@ -19,6 +19,7 @@ env_logger = "0.11.3"
log = "0.4.21" log = "0.4.21"
rand = "0.8.5" rand = "0.8.5"
serde = { version = "1.0.197", features = ["default"] } serde = { version = "1.0.197", features = ["default"] }
strum = { version = "0.26.2", features = ["derive"] }
thiserror = "1.0.58" thiserror = "1.0.58"
uuid = { version = "1.7.0", features = ["v4"] } uuid = { version = "1.7.0", features = ["v4"] }
tokio = { version = "1.36.0", features = ["sync"] } tokio = { version = "1.36.0", features = ["sync"] }

View File

@ -1,7 +1,8 @@
pub mod invite; pub mod invite;
use crate::backend::Backend; use crate::backend::{permissions::Permission, Backend};
use actix_web::{post, web, HttpResponse, Responder}; use actix_web::{get, post, web, HttpResponse, Responder};
use actix_web_httpauth::extractors::bearer::BearerAuth;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
use uuid::Uuid; use uuid::Uuid;
@ -67,3 +68,26 @@ pub async fn auth(backend: web::Data<Backend>, body: web::Json<AuthRequest>) ->
}, },
} }
} }
#[derive(Debug, Serialize)]
struct InfoResponse {
userid: String,
permissions: Vec<Permission>,
}
#[get("/account/info")]
pub async fn info(backend: web::Data<Backend>, bearer_auth: BearerAuth) -> impl Responder {
match backend.account_info(bearer_auth.token()).await {
Err(e) => {
log::error!("{e}");
HttpResponse::InternalServerError().finish()
}
Ok(res) => match res {
Err(e) => e.into(),
Ok((userid, permissions)) => HttpResponse::Ok().json(InfoResponse {
userid: userid.to_string(),
permissions,
}),
},
}
}

View File

@ -11,6 +11,7 @@ pub async fn start(config: &Config, backend: Backend) -> Result<()> {
.app_data(data.clone()) .app_data(data.clone())
.service(endpoints::account::register) .service(endpoints::account::register)
.service(endpoints::account::auth) .service(endpoints::account::auth)
.service(endpoints::account::info)
.service(endpoints::account::invite::new) .service(endpoints::account::invite::new)
.service(endpoints::relay::create) .service(endpoints::relay::create)
.service(endpoints::relay::relay_id::user::user_id::public_key) .service(endpoints::relay::relay_id::user::user_id::public_key)

View File

@ -1,6 +1,6 @@
mod db_structures; mod db_structures;
pub mod error; pub mod error;
mod permissions; pub mod permissions;
mod relay; mod relay;
mod tokens; mod tokens;
mod user; mod user;

View File

@ -1,3 +1,7 @@
use serde::Serialize;
use strum::{EnumIter, IntoEnumIterator};
#[derive(EnumIter, Debug, Serialize)]
pub enum Permission { pub enum Permission {
GenerateInviteTokens, GenerateInviteTokens,
PromoteUsers, PromoteUsers,
@ -11,3 +15,19 @@ impl Permission {
} }
} }
} }
pub struct Permissions(pub u16);
impl Into<Vec<Permission>> for Permissions {
fn into(self) -> Vec<Permission> {
Permission::iter()
.filter_map(|permission| {
if (self.0 & permission.as_u16()) > 0 {
Some(permission)
} else {
None
}
})
.collect()
}
}

View File

@ -1,5 +1,8 @@
use crate::backend::{ use crate::backend::{
db_structures::UsersRow, error::Error, permissions::Permission, tokens::resolve_auth_token, db_structures::UsersRow,
error::Error,
permissions::{Permission, Permissions},
tokens::resolve_auth_token,
Backend, Backend,
}; };
use anyhow::{bail, Result}; use anyhow::{bail, Result};
@ -35,6 +38,10 @@ impl User {
(self.permissions & permission.as_u16()) > 0 (self.permissions & permission.as_u16()) > 0
} }
pub fn permissions(&self) -> Vec<Permission> {
Permissions(self.permissions).into()
}
pub fn verify_password(&self, password: &str) -> Result<bool> { pub fn verify_password(&self, password: &str) -> Result<bool> {
let hash = argon2::PasswordHash::new(self.password.as_str()).map_err(|_| { let hash = argon2::PasswordHash::new(self.password.as_str()).map_err(|_| {
anyhow::Error::msg(format!( anyhow::Error::msg(format!(
@ -173,4 +180,16 @@ impl Backend {
Ok(Ok(token)) Ok(Ok(token))
} }
pub async fn account_info(
&self,
requester: impl IntoUser,
) -> Result<Result<(Uuid, Vec<Permission>), Error>> {
let requester = match requester.into_user(&self.pool).await? {
Err(e) => return Ok(Err(e)),
Ok(user) => user,
};
Ok(Ok((requester.uuid, requester.permissions())))
}
} }