use crate::serve::rbac::{AuthContext, Permission, Role};
use axum::{Extension, Json};
use serde::Serialize;
#[derive(Debug, Serialize)]
pub struct WhoAmI {
pub principal: String,
pub role: Role,
pub permissions: Vec<Permission>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tenant: Option<String>,
}
impl WhoAmI {
pub fn of(actor: &AuthContext) -> Self {
Self {
principal: actor.principal.clone(),
role: actor.role,
permissions: actor.role.permissions(),
tenant: actor.tenant.clone(),
}
}
}
pub async fn whoami(Extension(actor): Extension<AuthContext>) -> Json<WhoAmI> {
Json(WhoAmI::of(&actor))
}
#[cfg(test)]
mod tests {
use super::*;
fn actor(role: Role) -> AuthContext {
AuthContext {
principal: "alice".into(),
role,
source_ip: None,
tenant: None,
}
}
#[tokio::test]
async fn reports_the_callers_role_and_exactly_its_permissions() {
let Json(me) = whoami(Extension(actor(Role::Operator))).await;
assert_eq!(me.principal, "alice");
assert_eq!(me.role, Role::Operator);
assert!(me.permissions.contains(&Permission::RunWrite));
assert!(!me.permissions.contains(&Permission::TemplateAdmin));
let json = serde_json::to_value(WhoAmI::of(&actor(Role::Viewer))).unwrap();
assert_eq!(json["role"], "viewer");
assert!(
json["permissions"]
.as_array()
.unwrap()
.contains(&serde_json::json!("template_read"))
);
}
}