barycenter/src/authz/errors.rs

87 lines
2.8 KiB
Rust
Raw Normal View History

use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use axum::Json;
use miette::Diagnostic;
use serde_json::json;
use thiserror::Error;
#[derive(Debug, Error, Diagnostic)]
pub enum AuthzError {
#[error("Failed to load policy file `{path}`")]
#[diagnostic(
code(barycenter::authz::policy_load),
help("Check that the file exists and contains valid KDL syntax")
)]
PolicyLoadError {
path: String,
#[source]
source: std::io::Error,
},
#[error("Invalid policy: {0}")]
#[diagnostic(
code(barycenter::authz::invalid_policy),
help("Each policy file must contain valid `resource`, `role`, `rule`, or `grant` KDL nodes")
)]
InvalidPolicy(String),
#[error("Invalid grant: {0}")]
#[diagnostic(
code(barycenter::authz::invalid_grant),
help("Grant syntax: grant \"relation\" on=\"type/id\" to=\"subject_type/id\" (optionally to=\"type/id#relation\")")
)]
InvalidGrant(String),
#[error("Invalid condition expression: {0}")]
#[diagnostic(
code(barycenter::authz::invalid_condition),
help("Supported operators: ==, !=, >, <, >=, <=, &&, ||, !, in. Paths use dot notation (e.g. request.ip)")
)]
InvalidCondition(String),
#[error("Undefined resource type `{0}`")]
#[diagnostic(
code(barycenter::authz::undefined_resource),
help("Define the resource type with: resource \"<name>\" {{ relations {{ ... }} permissions {{ ... }} }}")
)]
UndefinedResourceType(String),
#[error("Undefined role `{0}`")]
#[diagnostic(
code(barycenter::authz::undefined_role),
help("Define the role with: role \"<name>\" {{ permissions {{ ... }} }}")
)]
UndefinedRole(String),
#[error("Cyclic role inheritance detected: {0}")]
#[diagnostic(
code(barycenter::authz::cyclic_roles),
help("Check the `includes` lists in your role definitions for circular references")
)]
CyclicRoleInheritance(String),
#[error("KDL parse error: {0}")]
#[diagnostic(
code(barycenter::authz::kdl_parse),
help("Check your KDL file syntax — see https://kdl.dev for the specification")
)]
KdlParse(String),
#[error("I/O error: {0}")]
#[diagnostic(code(barycenter::authz::io))]
Io(#[from] std::io::Error),
}
impl IntoResponse for AuthzError {
fn into_response(self) -> Response {
let (status, message) = match &self {
AuthzError::InvalidPolicy(_)
| AuthzError::InvalidGrant(_)
| AuthzError::InvalidCondition(_) => (StatusCode::BAD_REQUEST, self.to_string()),
_ => (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()),
};
let body = json!({ "error": message });
(status, Json(body)).into_response()
}
}