use serde::{Deserialize, Serialize};
use super::entry::AclEntry;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateAclBody {
pub entry: AclEntry,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateAclResponseBody {
pub entry: AclEntry,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateAclResultBody {
pub did: String,
pub role: String,
pub label: Option<String>,
pub allowed_contexts: Vec<String>,
pub created_at: u64,
pub created_by: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expires_at: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub step_up_approver: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub step_up_require: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub approve_all_contexts: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub approve_contexts: Vec<String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scoped_expiring_grant_keeps_its_scope_and_expiry() {
let json = serde_json::json!({
"entry": {
"subject": "did:key:z6MkSubject",
"role": "admin",
"scopes": ["ctx-a", "ctx-b"],
"expiresAt": "2027-01-15T08:00:00Z",
"stepUp": { "approver": "did:key:z6MkApprover", "require": "delegated" }
}
});
let body: CreateAclBody = serde_json::from_value(json).unwrap();
assert_eq!(body.entry.scopes, vec!["ctx-a", "ctx-b"]);
assert!(body.entry.expires_at.is_some());
assert_eq!(
body.entry.step_up_approver().as_deref(),
Some("did:key:z6MkApprover")
);
assert_eq!(body.entry.step_up_require().as_deref(), Some("delegated"));
}
#[test]
fn unknown_member_is_rejected() {
let json = serde_json::json!({
"entry": { "subject": "did:key:zA", "role": "admin", "scope": ["ctx-a"] }
});
assert!(serde_json::from_value::<CreateAclBody>(json).is_err());
}
#[test]
fn pre_fold_flat_body_is_rejected() {
let json = serde_json::json!({
"did": "did:key:zA", "role": "admin", "allowedContexts": ["ctx-a"]
});
assert!(serde_json::from_value::<CreateAclBody>(json).is_err());
}
}