use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct RbacConfig {
#[serde(default)]
pub enabled: bool,
#[serde(
default = "default_fallback_role",
rename = "defaultRole",
alias = "default_role"
)]
pub default_role: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub jwt: Option<JwtConfig>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub tokens: HashMap<String, TokenAssignment>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub roles: HashMap<String, RolePolicyConfig>,
}
fn default_fallback_role() -> String {
"anonymous".to_string()
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct JwtConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub issuer: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub audience: Option<String>,
#[serde(
default,
rename = "jwksUrl",
alias = "jwks_url",
skip_serializing_if = "Option::is_none"
)]
pub jwks_url: Option<String>,
#[serde(
default,
rename = "roleClaim",
alias = "role_claim",
skip_serializing_if = "Option::is_none"
)]
pub role_claim: Option<String>,
#[serde(
default,
rename = "tenantClaim",
alias = "tenant_claim",
skip_serializing_if = "Option::is_none"
)]
pub tenant_claim: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub secret: Option<String>,
#[serde(
default,
rename = "secretEnv",
alias = "secret_env",
skip_serializing_if = "Option::is_none"
)]
pub secret_env: Option<String>,
}
impl JwtConfig {
pub fn resolve_secret(&self) -> Option<String> {
if let Some(ref env_name) = self.secret_env {
if let Ok(val) = std::env::var(env_name) {
if !val.trim().is_empty() {
return Some(val);
}
}
}
self.secret.clone()
}
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct TokenAssignment {
pub role: String,
#[serde(
default,
rename = "tenantId",
alias = "tenant_id",
skip_serializing_if = "Option::is_none"
)]
pub tenant_id: Option<String>,
#[serde(
default,
rename = "actorId",
alias = "actor_id",
skip_serializing_if = "Option::is_none"
)]
pub actor_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Default)]
pub struct RolePolicyConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub allow: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub deny: Vec<String>,
#[serde(
default,
rename = "requireApproval",
alias = "require_approval",
skip_serializing_if = "Vec::is_empty"
)]
pub require_approval: Vec<String>,
#[serde(
default,
rename = "redactKeys",
alias = "redact_keys",
skip_serializing_if = "Vec::is_empty"
)]
pub redact_keys: Vec<String>,
}
#[derive(Clone, Debug, PartialEq)]
pub struct TenantContext {
pub tenant_id: String,
pub role: String,
pub actor_id: Option<String>,
pub grant_id: Option<String>,
pub effective_policy: crate::daemon::Policy,
}
impl TenantContext {
pub fn default_for_policy(default_role: &str, base_policy: &crate::daemon::Policy) -> Self {
Self {
tenant_id: "default".to_string(),
role: default_role.to_string(),
actor_id: None,
grant_id: None,
effective_policy: base_policy.clone(),
}
}
}