use axum::{extract::State, http::HeaderMap, Json};
use std::sync::Arc;
use crate::callback::AuthCallback;
use crate::errors::AppError;
use crate::models::{
AuthorizeRequest, AuthorizeResponse, GetPermissionsRequest, GetPermissionsResponse,
};
use crate::services::{AuthorizationService, EmailService, PolicyContext, PolicyService};
use crate::utils::authenticate;
use crate::AppState;
pub async fn authorize<C: AuthCallback, E: EmailService>(
State(state): State<Arc<AppState<C, E>>>,
headers: HeaderMap,
Json(req): Json<AuthorizeRequest>,
) -> Result<Json<AuthorizeResponse>, AppError> {
let auth = authenticate(&state, &headers).await?;
let context = build_policy_context(req.resource, req.environment);
let policy_service = PolicyService::new(
state.policy_repo.clone(),
state.user_repo.clone(),
state.org_repo.clone(),
state.membership_repo.clone(),
);
let result = policy_service
.evaluate(auth.user_id, req.org_id, &req.permission, context)
.await?;
Ok(Json(AuthorizeResponse {
allowed: result.allowed,
reason: result.reason,
permissions: None,
matched_policy_id: result.matched_policy_id,
matched_policy_name: result.matched_policy_name,
used_rbac_fallback: Some(result.used_rbac_fallback),
}))
}
fn build_policy_context(
resource: Option<std::collections::HashMap<String, serde_json::Value>>,
environment: Option<std::collections::HashMap<String, serde_json::Value>>,
) -> Option<PolicyContext> {
if resource.is_none() && environment.is_none() {
return None;
}
let mut context = PolicyContext::new();
if let Some(resource_map) = resource {
for (key, value) in resource_map {
context.resource.insert(key, value);
}
}
if let Some(environment_map) = environment {
for (key, value) in environment_map {
context.environment.insert(key, value);
}
}
Some(context)
}
pub async fn get_permissions<C: AuthCallback, E: EmailService>(
State(state): State<Arc<AppState<C, E>>>,
headers: HeaderMap,
Json(req): Json<GetPermissionsRequest>,
) -> Result<Json<GetPermissionsResponse>, AppError> {
let auth = authenticate(&state, &headers).await?;
let auth_service = AuthorizationService::new(
state.user_repo.clone(),
state.org_repo.clone(),
state.membership_repo.clone(),
);
let permissions = auth_service
.get_user_permissions(auth.user_id, req.org_id)
.await?;
let membership = state
.membership_repo
.find_by_user_and_org(auth.user_id, req.org_id)
.await?
.ok_or_else(|| AppError::Forbidden("Not a member of this organization".into()))?;
Ok(Json(GetPermissionsResponse {
permissions: permissions.iter().map(|p| p.as_str().to_string()).collect(),
role: Some(membership.role.as_str().to_string()),
}))
}