From 5db4d4869ce603049d1571f4ffc58b096076ac40 Mon Sep 17 00:00:00 2001 From: antifallobst Date: Sun, 31 Mar 2024 13:47:36 +0200 Subject: [PATCH] feat(server >> api): implemented an endpoint that gives the client information about their authenticated account --- server/Cargo.toml | 1 + server/src/api/endpoints/account/mod.rs | 28 +++++++++++++++++++++++-- server/src/api/mod.rs | 1 + server/src/backend/mod.rs | 2 +- server/src/backend/permissions.rs | 20 ++++++++++++++++++ server/src/backend/user.rs | 21 ++++++++++++++++++- 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/server/Cargo.toml b/server/Cargo.toml index 3c7e86e..644650a 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -19,6 +19,7 @@ env_logger = "0.11.3" log = "0.4.21" rand = "0.8.5" serde = { version = "1.0.197", features = ["default"] } +strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.58" uuid = { version = "1.7.0", features = ["v4"] } tokio = { version = "1.36.0", features = ["sync"] } diff --git a/server/src/api/endpoints/account/mod.rs b/server/src/api/endpoints/account/mod.rs index b9ddb71..5c3aeba 100644 --- a/server/src/api/endpoints/account/mod.rs +++ b/server/src/api/endpoints/account/mod.rs @@ -1,7 +1,8 @@ pub mod invite; -use crate::backend::Backend; -use actix_web::{post, web, HttpResponse, Responder}; +use crate::backend::{permissions::Permission, Backend}; +use actix_web::{get, post, web, HttpResponse, Responder}; +use actix_web_httpauth::extractors::bearer::BearerAuth; use serde::{Deserialize, Serialize}; use std::str::FromStr; use uuid::Uuid; @@ -67,3 +68,26 @@ pub async fn auth(backend: web::Data, body: web::Json) -> }, } } + +#[derive(Debug, Serialize)] +struct InfoResponse { + userid: String, + permissions: Vec, +} + +#[get("/account/info")] +pub async fn info(backend: web::Data, 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, + }), + }, + } +} diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 02bd6bc..5b9506b 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -11,6 +11,7 @@ pub async fn start(config: &Config, backend: Backend) -> Result<()> { .app_data(data.clone()) .service(endpoints::account::register) .service(endpoints::account::auth) + .service(endpoints::account::info) .service(endpoints::account::invite::new) .service(endpoints::relay::create) .service(endpoints::relay::relay_id::user::user_id::public_key) diff --git a/server/src/backend/mod.rs b/server/src/backend/mod.rs index 0b51918..4b45475 100644 --- a/server/src/backend/mod.rs +++ b/server/src/backend/mod.rs @@ -1,6 +1,6 @@ mod db_structures; pub mod error; -mod permissions; +pub mod permissions; mod relay; mod tokens; mod user; diff --git a/server/src/backend/permissions.rs b/server/src/backend/permissions.rs index 73f3b87..b7cf4f1 100644 --- a/server/src/backend/permissions.rs +++ b/server/src/backend/permissions.rs @@ -1,3 +1,7 @@ +use serde::Serialize; +use strum::{EnumIter, IntoEnumIterator}; + +#[derive(EnumIter, Debug, Serialize)] pub enum Permission { GenerateInviteTokens, PromoteUsers, @@ -11,3 +15,19 @@ impl Permission { } } } + +pub struct Permissions(pub u16); + +impl Into> for Permissions { + fn into(self) -> Vec { + Permission::iter() + .filter_map(|permission| { + if (self.0 & permission.as_u16()) > 0 { + Some(permission) + } else { + None + } + }) + .collect() + } +} diff --git a/server/src/backend/user.rs b/server/src/backend/user.rs index e1b5bb2..4e5fbfe 100644 --- a/server/src/backend/user.rs +++ b/server/src/backend/user.rs @@ -1,5 +1,8 @@ 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, }; use anyhow::{bail, Result}; @@ -35,6 +38,10 @@ impl User { (self.permissions & permission.as_u16()) > 0 } + pub fn permissions(&self) -> Vec { + Permissions(self.permissions).into() + } + pub fn verify_password(&self, password: &str) -> Result { let hash = argon2::PasswordHash::new(self.password.as_str()).map_err(|_| { anyhow::Error::msg(format!( @@ -173,4 +180,16 @@ impl Backend { Ok(Ok(token)) } + + pub async fn account_info( + &self, + requester: impl IntoUser, + ) -> Result), 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()))) + } }