Started perms middleware UNFINISHED

This commit is contained in:
2025-12-02 00:42:56 +01:00
parent fb7f3c4aa6
commit 89b01a3921
5 changed files with 107 additions and 10 deletions

View File

@@ -1,4 +1,70 @@
use axum::http::Method;
use std::collections::HashMap;
use crate::domain::user_prems::UserActions;
#[derive(Debug, Hash, Clone, PartialEq, Eq)]
pub struct RouteKey {
pub method: Method,
pub path: String,
}
#[derive(Debug)] #[derive(Debug)]
pub struct AppCfg { pub struct AppCfg {
pub db_path: String, pub db_path: String,
pub route_perms: HashMap<RouteKey, Vec<UserActions>>,
}
impl AppCfg {
pub fn new(db_path: String) -> Self {
Self {
db_path,
route_perms: HashMap::new(),
}
}
pub fn insert_route_perms(
&mut self,
method: Method,
path: impl Into<String>,
perms: Vec<UserActions>,
) {
let key = RouteKey {
method,
path: path.into(),
};
self.route_perms.insert(key, perms);
}
pub fn get_route_perms(&self, method: &Method, path: &str) -> Option<&Vec<UserActions>> {
let key = RouteKey {
method: method.clone(),
path: path.to_string(),
};
self.route_perms.get(&key)
}
pub fn route_allows(&self, method: &Method, path: &str, user_perms: &[UserActions]) -> bool {
if user_perms.contains(&UserActions::Root) {
return true;
}
let key = RouteKey {
method: method.clone(),
path: path.to_string(),
};
let required = match self.route_perms.get(&key) {
Some(perms) => perms,
None => return true, // no perms required → allow
};
if required.contains(&UserActions::Root) {
return false;
}
required.iter().all(|p| user_perms.contains(p))
}
} }

View File

@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use sqlx::prelude::FromRow; use sqlx::prelude::FromRow;
use uuid::Uuid; use uuid::Uuid;
#[derive(Debug)] #[derive(Debug, Eq, PartialEq)]
pub enum UserActions { pub enum UserActions {
Root, Root,
ManageUsers, ManageUsers,

View File

@@ -1,8 +1,10 @@
use std::sync::Arc; use std::{collections::HashMap, sync::Arc};
use anyhow::{Ok, Result}; use anyhow::{Ok, Result};
use axum::http::Method;
use rustymine_daemon::{ use rustymine_daemon::{
config::AppCfg, config::AppCfg,
domain::user_prems::UserActions,
router, router,
state::{AppState, check_root}, state::{AppState, check_root},
}; };
@@ -56,11 +58,15 @@ async fn main() -> Result<()> {
); );
let db_path: String = "postgres://rustymine:minecraft@localhost:5432/rustymine_dev".to_string(); let db_path: String = "postgres://rustymine:minecraft@localhost:5432/rustymine_dev".to_string();
let config = AppCfg { let mut config = AppCfg {
db_path: db_path.clone(), db_path: db_path.clone(),
route_perms: HashMap::new(),
}; };
let state = Arc::new(AppState::new(&config).await); config.insert_route_perms(Method::GET, "/api/login", vec![UserActions::Login]);
config.insert_route_perms(Method::GET, "/api/users", vec![UserActions::Login]);
let state = Arc::new(AppState::new(config).await);
check_root(state.clone()).await; check_root(state.clone()).await;
let app_result = router::init_router(state.clone()).await; let app_result = router::init_router(state.clone()).await;

View File

@@ -1,8 +1,9 @@
use crate::{core::user_routines, prelude::*}; use crate::{core::user_routines, domain::user::InternalUser, prelude::*};
use std::sync::Arc; use std::sync::Arc;
use axum::{ use axum::{
extract::{Request, State}, Extension,
extract::{MatchedPath, Request, State},
http::{self, Method, StatusCode, header::AUTHORIZATION}, http::{self, Method, StatusCode, header::AUTHORIZATION},
middleware::{Next, from_fn_with_state}, middleware::{Next, from_fn_with_state},
response::Response, response::Response,
@@ -69,6 +70,23 @@ pub async fn auth(
Ok(next.run(req).await) Ok(next.run(req).await)
} }
pub async fn perms(
State(state): State<Arc<AppState>>,
Extension(user): Extension<InternalUser>,
mut req: Request,
next: Next,
) -> Result<Response, StatusCode> {
let method: Method = req.method().clone();
let path = req
.extensions()
.get::<MatchedPath>()
.map(|p| p.as_str().to_string())
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(next.run(req).await)
}
pub fn cors() -> CorsLayer { pub fn cors() -> CorsLayer {
debug!("build cors layer"); debug!("build cors layer");
CorsLayer::new() CorsLayer::new()

View File

@@ -1,8 +1,11 @@
use std::{process::exit, sync::Arc}; use std::{collections::HashMap, process::exit, sync::Arc};
use crate::{ use crate::{
core, core,
domain::user::{InternalNewUser, NewUser}, domain::{
user::{InternalNewUser, NewUser},
user_prems::UserActions,
},
prelude::*, prelude::*,
}; };
@@ -12,10 +15,11 @@ use crate::{config::AppCfg, infra::db};
pub struct AppState { pub struct AppState {
pub db_pool: PgPool, pub db_pool: PgPool,
pub config: AppCfg,
} }
impl AppState { impl AppState {
pub async fn new(config: &AppCfg) -> Self { pub async fn new(config: AppCfg) -> Self {
debug!("init app state"); debug!("init app state");
debug!("establish database connection"); debug!("establish database connection");
let db_pool = db::connect(&config.db_path) let db_pool = db::connect(&config.db_path)
@@ -36,7 +40,10 @@ impl AppState {
.unwrap(); .unwrap();
info!("database ready after connect and migrate"); info!("database ready after connect and migrate");
Self { db_pool: db_pool } Self {
db_pool: db_pool,
config: config,
}
} }
} }