use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::error::IamError;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Subject {
#[serde(rename = "type")]
pub kind: String,
pub id: String,
}
impl Subject {
pub fn new(kind: impl Into<String>, id: impl Into<String>) -> Self {
Self {
kind: kind.into(),
id: id.into(),
}
}
pub fn user(id: impl Into<String>) -> Self {
Self::new("user", id)
}
pub fn service_account(id: impl Into<String>) -> Self {
Self::new("service_account", id)
}
pub fn group(id: impl Into<String>) -> Self {
Self::new("group", id)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Resource {
#[serde(rename = "type")]
pub kind: String,
pub id: String,
}
impl Resource {
pub fn new(kind: impl Into<String>, id: impl Into<String>) -> Self {
Self {
kind: kind.into(),
id: id.into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DecisionQuery {
pub subject: Subject,
pub permission: String,
pub organization: Option<String>,
pub application: Option<String>,
pub resource: Option<String>,
pub context: Value,
pub current_aal: String,
pub explain: bool,
}
impl Default for DecisionQuery {
fn default() -> Self {
Self {
subject: Subject::new(String::new(), String::new()),
permission: String::new(),
organization: None,
application: None,
resource: None,
context: Value::Object(serde_json::Map::new()),
current_aal: "aal1".to_string(),
explain: false,
}
}
}
impl DecisionQuery {
pub fn new(subject: Subject, permission: impl Into<String>) -> Self {
Self {
subject,
permission: permission.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Decision {
pub allowed: bool,
pub decision_id: String,
pub policy_version: i64,
pub requires_step_up: bool,
pub required_aal: Option<String>,
pub explanation: Vec<String>,
}
impl Decision {
pub fn deny(reason: impl Into<String>) -> Self {
Self {
allowed: false,
explanation: vec![reason.into()],
..Self::default()
}
}
pub(crate) fn from_value(value: &Value) -> Result<Self, IamError> {
let obj = value
.as_object()
.ok_or_else(|| IamError::Malformed("response body is not a JSON object".to_string()))?;
let explanation = obj
.get("explanation")
.and_then(Value::as_array)
.map(|items| {
items
.iter()
.filter_map(Value::as_str)
.map(ToString::to_string)
.collect()
})
.unwrap_or_default();
Ok(Self {
allowed: obj.get("allowed") == Some(&Value::Bool(true)),
decision_id: obj
.get("decision_id")
.and_then(Value::as_str)
.unwrap_or_default()
.to_string(),
policy_version: obj
.get("policy_version")
.and_then(Value::as_i64)
.unwrap_or(0),
requires_step_up: obj.get("requires_step_up") == Some(&Value::Bool(true)),
required_aal: obj
.get("required_aal")
.and_then(Value::as_str)
.map(ToString::to_string),
explanation,
})
}
#[must_use]
pub fn granted(&self) -> bool {
self.allowed && !self.requires_step_up
}
#[must_use]
pub fn is_allowed(&self) -> bool {
self.granted()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub iss: String,
#[serde(default)]
pub aud: Value,
pub exp: i64,
#[serde(default)]
pub nbf: Option<i64>,
#[serde(default)]
pub iat: Option<i64>,
#[serde(flatten)]
pub extra: serde_json::Map<String, Value>,
}