use crate::container::XADES;
use crate::properties::{Error, PropertyResult, Reading};
use crate::xml::{Access, AccessError, Name, Snapshot, Xml};
use structured_asn1::memory::{OwnedVec, Work};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Claimed,
AttributeCertificate,
OtherCertificate,
Assertion,
}
#[derive(Debug, Clone, Copy)]
pub struct Entry<N> {
pub node: N,
pub group: N,
pub kind: Kind,
}
#[derive(Debug)]
pub struct Roles<N, D, G> {
snapshot: Snapshot<D, G>,
signature: N,
node: N,
entries: OwnedVec<Entry<N>>,
}
impl<N: Copy, D: Copy, G: Copy> Roles<N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn node(&self) -> N {
self.node
}
#[must_use]
pub fn entries(&self) -> &[Entry<N>] {
&self.entries
}
}
pub type Catalogue<X> = Roles<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
pub fn read<X: Xml, W: Work>(
access: &Access<'_, X, W>,
signed: &Reading<X>,
) -> PropertyResult<Option<Catalogue<X>>, X> {
if access.snapshot() != signed.snapshot() {
return Err(Error::Access(AccessError::Changed {
node: signed.parent(),
}));
}
access
.query(signed.parent(), |_, _, _| Ok(()))
.map_err(Error::Access)?;
let Some(node) = signed.role() else {
return Ok(None);
};
read_node(access, signed.snapshot(), signed.signature(), node).map(Some)
}
pub(crate) fn locate<X: Xml, W: Work>(
access: &Access<'_, X, W>,
layout: &crate::container::Layout<X>,
) -> PropertyResult<Option<X::Id>, X> {
let (parent, _) = crate::properties::sections(access, layout)?;
let Some(parent) = parent else {
return Ok(None);
};
let mut selected = None;
for child in access.children(parent) {
let node = child.map_err(Error::Child)?;
if access
.has_name(
node,
Name {
namespace: XADES,
local: "SignerRoleV2",
},
)
.map_err(Error::Access)?
{
if selected.is_some() {
return Err(Error::Duplicate { node });
}
selected = Some(node);
}
}
Ok(selected)
}
pub(crate) fn read_node<X: Xml, W: Work>(
access: &Access<'_, X, W>,
snapshot: Snapshot<X::Document, X::Generation>,
signature: X::Id,
node: X::Id,
) -> PropertyResult<Catalogue<X>, X> {
if snapshot != access.snapshot() {
return Err(Error::Access(AccessError::Changed { node }));
}
let mut entries = access
.query(node, |_, _, work| Ok(work.vector()))
.map_err(Error::Access)?;
let mut previous = None;
for child in access.children(node) {
let group = child.map_err(Error::Child)?;
let rank = rank(access, group)?;
if previous == Some(rank) {
return Err(Error::Duplicate { node: group });
}
if previous.is_some_and(|value| value > rank) {
return Err(Error::Order { node: group });
}
previous = Some(rank);
let mut populated = false;
for child in access.children(group) {
let child = child.map_err(Error::Child)?;
let (payload, kind) = match rank {
0 => {
expect(access, child, "ClaimedRole")?;
(child, Kind::Claimed)
}
1 => {
expect(access, child, "CertifiedRole")?;
credential(access, child)?
}
_ => {
expect(access, child, "SignedAssertion")?;
(child, Kind::Assertion)
}
};
entries
.try_push(Entry {
node: payload,
group,
kind,
})
.map_err(|cause| Error::Resource { node: child, cause })?;
populated = true;
}
if !populated {
return Err(Error::Missing {
parent: group,
local: "role value",
});
}
}
Ok(Roles {
snapshot,
signature,
node,
entries: entries.into_owned(),
})
}
fn rank<X: Xml, W: Work>(access: &Access<'_, X, W>, node: X::Id) -> PropertyResult<usize, X> {
for (rank, local) in ["ClaimedRoles", "CertifiedRolesV2", "SignedAssertions"]
.into_iter()
.enumerate()
{
if access
.has_name(
node,
Name {
namespace: XADES,
local,
},
)
.map_err(Error::Access)?
{
return Ok(rank);
}
}
Err(Error::Unexpected { node })
}
fn expect<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
local: &'static str,
) -> PropertyResult<(), X> {
if !access
.has_name(
node,
Name {
namespace: XADES,
local,
},
)
.map_err(Error::Access)?
{
return Err(Error::Unexpected { node });
}
Ok(())
}
fn credential<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
) -> PropertyResult<(X::Id, Kind), X> {
let mut children = access.children(node);
let value = children
.next()
.transpose()
.map_err(Error::Child)?
.ok_or(Error::Missing {
parent: node,
local: "attribute certificate",
})?;
let kind = if access
.has_name(
value,
Name {
namespace: XADES,
local: "X509AttributeCertificate",
},
)
.map_err(Error::Access)?
{
Kind::AttributeCertificate
} else {
expect(access, value, "OtherAttributeCertificate")?;
Kind::OtherCertificate
};
if let Some(extra) = children.next() {
return Err(Error::Unexpected {
node: extra.map_err(Error::Child)?,
});
}
Ok((value, kind))
}