From 349c1a24a4769d439ceaa6b0dcbc78002d74a19f Mon Sep 17 00:00:00 2001 From: xeovalyte Date: Fri, 14 Feb 2025 15:59:11 +0100 Subject: [PATCH] Optimized permissions --- server/src/auth.rs | 11 ++-------- server/src/model/user.rs | 32 ++++++++++++++++++++++++++++ server/src/routes.rs | 2 +- server/src/routes/auth.rs | 6 +++--- server/src/routes/member.rs | 2 +- server/src/routes/member/migrate.rs | 18 ++++++++++------ server/src/routes/user.rs | 33 ++++++++++++++++++++++++----- 7 files changed, 78 insertions(+), 26 deletions(-) diff --git a/server/src/auth.rs b/server/src/auth.rs index 818c921..78362e7 100644 --- a/server/src/auth.rs +++ b/server/src/auth.rs @@ -18,10 +18,7 @@ use crate::{ mod error; -pub async fn get_user_from_header( - pool: &PgPool, - headers: &HeaderMap, -) -> Result<(Roles, User), AuthError> { +pub async fn get_user_from_header(pool: &PgPool, headers: &HeaderMap) -> Result { let bearer_value = headers.get(header::AUTHORIZATION); let bearer_value = bearer_value .ok_or(AuthError::InvalidToken)? @@ -44,11 +41,7 @@ pub async fn get_user_from_header( Err(_) => return Err(AuthError::InvalidToken), }; - let roles = UserMember::get_roles(pool, &db_user.user_id) - .await - .unwrap_or(Roles::MEMBER); - - Ok((roles, db_user.into())) + Ok(db_user.into()) } pub fn get_token_from_bearer(bearer: &str) -> Result { diff --git a/server/src/model/user.rs b/server/src/model/user.rs index 86c407f..ff2f317 100644 --- a/server/src/model/user.rs +++ b/server/src/model/user.rs @@ -8,10 +8,12 @@ pub struct User { pub admin: bool, } +use crate::auth::AuthError; use crate::database::model::User as DbUser; use crate::database::model::UserMember as DbUserMember; use crate::util::convert_vec; +use super::member::Roles; use super::Member; impl From for User { fn from(db_user: DbUser) -> Self { @@ -40,4 +42,34 @@ impl User { Ok(convert_vec(related_members)) } + + pub async fn authorize( + &self, + pool: &PgPool, + required_roles: Option, + requested_user_id: Option, + ) -> Result<(), AuthError> { + if let Some(user_id) = requested_user_id { + let user_uuid = uuid::Uuid::parse_str(&user_id).map_err(|_| AuthError::NoPermssions)?; + + if self.id != user_uuid { + return Err(AuthError::NoPermssions); + } + + return Ok(()); + } + + if let Some(roles) = required_roles { + let user_roles = DbUserMember::get_roles(pool, &self.id) + .await + .unwrap_or(Roles::MEMBER); + if !user_roles.contains(roles) { + return Err(AuthError::NoPermssions); + } + + return Ok(()); + } + + Ok(()) + } } diff --git a/server/src/routes.rs b/server/src/routes.rs index 740af81..6f0f452 100644 --- a/server/src/routes.rs +++ b/server/src/routes.rs @@ -18,7 +18,7 @@ async fn root( State(state): State, headers: HeaderMap, ) -> Result, crate::Error> { - let (_roles, user) = get_user_from_header(&state.pool, &headers).await?; + let user = get_user_from_header(&state.pool, &headers).await?; Ok(Json(user)) } diff --git a/server/src/routes/auth.rs b/server/src/routes/auth.rs index be4ccb0..cf825dd 100644 --- a/server/src/routes/auth.rs +++ b/server/src/routes/auth.rs @@ -26,7 +26,7 @@ pub struct LoginRequest { password: String, } -pub async fn login<'a>( +pub async fn login( State(state): State, Json(login_request): Json, ) -> Result { @@ -99,7 +99,7 @@ pub async fn change_password( headers: HeaderMap, Json(request): Json, ) -> Result<(), crate::Error> { - let (_, user) = get_user_from_header(&state.pool, &headers).await?; + let user = get_user_from_header(&state.pool, &headers).await?; // Verify that password is correct let db_user: DbUser = user.into(); @@ -138,7 +138,7 @@ pub async fn change_email( headers: HeaderMap, Json(request): Json, ) -> Result<(), crate::Error> { - let (_, user) = get_user_from_header(&state.pool, &headers).await?; + let user = get_user_from_header(&state.pool, &headers).await?; // Verify that password is correct let db_user: DbUser = user.into(); diff --git a/server/src/routes/member.rs b/server/src/routes/member.rs index 86a1225..dd61273 100644 --- a/server/src/routes/member.rs +++ b/server/src/routes/member.rs @@ -20,7 +20,7 @@ pub async fn get_current_members( State(state): State, headers: HeaderMap, ) -> Result>, crate::Error> { - let (_roles, user) = get_user_from_header(&state.pool, &headers).await?; + let user = get_user_from_header(&state.pool, &headers).await?; let members = user.members(&state.pool).await?; diff --git a/server/src/routes/member/migrate.rs b/server/src/routes/member/migrate.rs index 9c95d9e..58a8686 100644 --- a/server/src/routes/member/migrate.rs +++ b/server/src/routes/member/migrate.rs @@ -4,7 +4,7 @@ use axum::{extract::State, http::HeaderMap, Json}; use sqlx::PgPool; use crate::{ - auth::{get_user_from_header, AuthError}, + auth::get_user_from_header, database::model::Member as DbMember, model::{ member::{Groups, Name, Roles}, @@ -14,16 +14,15 @@ use crate::{ AppState, }; -pub async fn migrate_request<'a>( +pub async fn migrate_request( State(state): State, headers: HeaderMap, body: String, ) -> Result, crate::Error> { - let (roles, _user) = get_user_from_header(&state.pool, &headers).await?; + let user = get_user_from_header(&state.pool, &headers).await?; - if !roles.contains(Roles::ADMIN) { - return Err(AuthError::NoPermssions.into()); - } + user.authorize(&state.pool, Some(Roles::ADMIN), None) + .await?; tracing::info!("Migration is requested"); @@ -46,10 +45,15 @@ pub async fn migrate_request<'a>( Ok(Json(MigrationResponse::from((count, members_diff)))) } -pub async fn migrate_confirm<'a>( +pub async fn migrate_confirm( State(state): State, + headers: HeaderMap, body: String, ) -> Result<(), crate::Error> { + let user = get_user_from_header(&state.pool, &headers).await?; + user.authorize(&state.pool, Some(Roles::ADMIN), None) + .await?; + tracing::info!("Migration is confirmed"); let count = match body.trim().parse::() { diff --git a/server/src/routes/user.rs b/server/src/routes/user.rs index 1943a4d..5cb7ceb 100644 --- a/server/src/routes/user.rs +++ b/server/src/routes/user.rs @@ -1,15 +1,38 @@ -use axum::{extract::State, http::HeaderMap, routing::get, Json, Router}; +use axum::{ + extract::{Path, State}, + http::HeaderMap, + routing::{get, post}, + Json, Router, +}; -use crate::{auth::get_user_from_header, model::User, AppState}; +use crate::{ + auth::get_user_from_header, + model::{member::Roles, User}, + AppState, +}; pub fn routes() -> Router { - Router::new().route("/user", get(get_current_user)) + Router::new() + .route("/user", get(get_current_user)) + .route("/user/{user_id}/members", post(members_insert)) } -pub async fn get_current_user<'a>( +pub async fn get_current_user( State(state): State, headers: HeaderMap, ) -> Result, crate::Error> { - let (_roles, user) = get_user_from_header(&state.pool, &headers).await?; + let user = get_user_from_header(&state.pool, &headers).await?; Ok(Json(user)) } + +pub async fn members_insert( + State(state): State, + Path(user_id): Path, + headers: HeaderMap, +) -> Result<(), crate::Error> { + let user = get_user_from_header(&state.pool, &headers).await?; + user.authorize(&state.pool, Some(Roles::ADMIN), Some(user_id)) + .await?; + + Ok(()) +}