mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-06-25 15:37:42 +00:00
132 lines
4.1 KiB
Rust
132 lines
4.1 KiB
Rust
|
|
//! Permission checking utilities for API endpoints.
|
||
|
|
//!
|
||
|
|
//! Provides reusable functions to check user permissions against the role system.
|
||
|
|
|
||
|
|
use axum::http::StatusCode;
|
||
|
|
use sqlx::PgPool;
|
||
|
|
use uuid::Uuid;
|
||
|
|
|
||
|
|
/// Check if a user has a specific permission, optionally within a community scope.
|
||
|
|
pub async fn user_has_permission(
|
||
|
|
pool: &PgPool,
|
||
|
|
user_id: Uuid,
|
||
|
|
permission: &str,
|
||
|
|
community_id: Option<Uuid>,
|
||
|
|
) -> Result<bool, (StatusCode, String)> {
|
||
|
|
let has_perm = sqlx::query_scalar!(
|
||
|
|
"SELECT user_has_permission($1, $2, $3)",
|
||
|
|
user_id,
|
||
|
|
permission,
|
||
|
|
community_id
|
||
|
|
)
|
||
|
|
.fetch_one(pool)
|
||
|
|
.await
|
||
|
|
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
||
|
|
.unwrap_or(false);
|
||
|
|
|
||
|
|
Ok(has_perm)
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Require a permission, returning Forbidden error if not granted.
|
||
|
|
pub async fn require_permission(
|
||
|
|
pool: &PgPool,
|
||
|
|
user_id: Uuid,
|
||
|
|
permission: &str,
|
||
|
|
community_id: Option<Uuid>,
|
||
|
|
) -> Result<(), (StatusCode, String)> {
|
||
|
|
if !user_has_permission(pool, user_id, permission, community_id).await? {
|
||
|
|
return Err((
|
||
|
|
StatusCode::FORBIDDEN,
|
||
|
|
format!("Permission '{}' required", permission),
|
||
|
|
));
|
||
|
|
}
|
||
|
|
Ok(())
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Require any of the given permissions.
|
||
|
|
pub async fn require_any_permission(
|
||
|
|
pool: &PgPool,
|
||
|
|
user_id: Uuid,
|
||
|
|
permissions: &[&str],
|
||
|
|
community_id: Option<Uuid>,
|
||
|
|
) -> Result<(), (StatusCode, String)> {
|
||
|
|
for perm in permissions {
|
||
|
|
if user_has_permission(pool, user_id, perm, community_id).await? {
|
||
|
|
return Ok(());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Err((
|
||
|
|
StatusCode::FORBIDDEN,
|
||
|
|
format!("One of these permissions required: {}", permissions.join(", ")),
|
||
|
|
))
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Check if user is a platform admin (has platform.admin permission).
|
||
|
|
#[allow(dead_code)]
|
||
|
|
pub async fn is_platform_admin(
|
||
|
|
pool: &PgPool,
|
||
|
|
user_id: Uuid,
|
||
|
|
) -> Result<bool, (StatusCode, String)> {
|
||
|
|
user_has_permission(pool, user_id, "platform.admin", None).await
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Require platform admin access.
|
||
|
|
#[allow(dead_code)]
|
||
|
|
pub async fn require_platform_admin(
|
||
|
|
pool: &PgPool,
|
||
|
|
user_id: Uuid,
|
||
|
|
) -> Result<(), (StatusCode, String)> {
|
||
|
|
require_permission(pool, user_id, "platform.admin", None).await
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Check if user is a community admin or moderator.
|
||
|
|
#[allow(dead_code)]
|
||
|
|
pub async fn is_community_staff(
|
||
|
|
pool: &PgPool,
|
||
|
|
user_id: Uuid,
|
||
|
|
community_id: Uuid,
|
||
|
|
) -> Result<bool, (StatusCode, String)> {
|
||
|
|
let is_admin = user_has_permission(pool, user_id, "community.admin", Some(community_id)).await?;
|
||
|
|
if is_admin {
|
||
|
|
return Ok(true);
|
||
|
|
}
|
||
|
|
user_has_permission(pool, user_id, "community.moderate", Some(community_id)).await
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Permission constants for common operations.
|
||
|
|
/// Used throughout API handlers for authorization.
|
||
|
|
pub mod perms {
|
||
|
|
// Platform-level
|
||
|
|
pub const PLATFORM_ADMIN: &str = "platform.admin";
|
||
|
|
pub const PLATFORM_SETTINGS: &str = "platform.settings";
|
||
|
|
pub const PLATFORM_PLUGINS: &str = "platform.plugins";
|
||
|
|
|
||
|
|
// Community-level
|
||
|
|
pub const COMMUNITY_CREATE: &str = "community.create";
|
||
|
|
pub const COMMUNITY_ADMIN: &str = "community.admin";
|
||
|
|
pub const COMMUNITY_SETTINGS: &str = "community.settings";
|
||
|
|
pub const COMMUNITY_MODERATE: &str = "community.moderate";
|
||
|
|
|
||
|
|
// Proposals
|
||
|
|
pub const PROPOSAL_CREATE: &str = "proposal.create";
|
||
|
|
pub const PROPOSAL_EDIT_OWN: &str = "proposal.edit_own";
|
||
|
|
pub const PROPOSAL_EDIT_ANY: &str = "proposal.edit_any";
|
||
|
|
pub const PROPOSAL_DELETE_OWN: &str = "proposal.delete_own";
|
||
|
|
pub const PROPOSAL_DELETE_ANY: &str = "proposal.delete_any";
|
||
|
|
pub const PROPOSAL_MANAGE_STATUS: &str = "proposal.manage_status";
|
||
|
|
|
||
|
|
// Voting
|
||
|
|
pub const VOTE_CAST: &str = "vote.cast";
|
||
|
|
pub const VOTE_VIEW_RESULTS: &str = "vote.view_results";
|
||
|
|
pub const VOTING_CONFIG: &str = "voting.configure";
|
||
|
|
|
||
|
|
// Moderation
|
||
|
|
pub const MOD_BAN_USERS: &str = "moderation.ban_users";
|
||
|
|
pub const MOD_REMOVE_CONTENT: &str = "moderation.remove_content";
|
||
|
|
pub const MOD_VIEW_REPORTS: &str = "moderation.view_reports";
|
||
|
|
|
||
|
|
// Users
|
||
|
|
pub const USER_MANAGE: &str = "users.manage";
|
||
|
|
pub const USER_INVITE: &str = "users.invite";
|
||
|
|
}
|