Skip to main content

_synta/certificate/
ace88.rs

1//! Python bindings for the RFC 7773 Authentication Context certificate
2//! extension (ACE-88) builder.
3//!
4//! Exposes [`PyAuthenticationContextsBuilder`] for constructing DER-encoded
5//! ``AuthenticationContexts`` structures (OID ``1.2.752.201.5.1``).
6
7use pyo3::prelude::*;
8use pyo3::types::PyBytes;
9
10// ── PyAuthenticationContextsBuilder ──────────────────────────────────────────
11
12/// Python-facing wrapper for
13/// [`synta_certificate::AuthenticationContextsBuilder`].
14///
15/// Builds a DER-encoded ``AuthenticationContexts`` SEQUENCE OF (RFC 7773 /
16/// ACE-88).
17///
18/// Each entry is an ``AuthenticationContext`` with a mandatory
19/// ``contextType`` UTF8String and an optional ``contextInfo`` UTF8String.
20/// At least one entry must be added before calling :meth:`build`.
21///
22/// The resulting DER goes inside the OCTET STRING wrapper of an X.509
23/// ``Extension`` SEQUENCE for OID ``1.2.752.201.5.1``
24/// (``id-ce-authContext``).
25///
26/// Example::
27///
28///     import synta
29///
30///     extn_der = (
31///         synta.AuthenticationContextsBuilder()
32///         .add("urn:id:skatteverket:2:1.0", None)
33///         .add(
34///             "urn:id:skatteverket:1:1.0",
35///             "https://www.skatteverket.se/ac/context",
36///         )
37///         .build()
38///     )
39#[pyclass(name = "AuthenticationContextsBuilder")]
40pub struct PyAuthenticationContextsBuilder {
41    inner: synta_certificate::AuthenticationContextsBuilder,
42    built: bool,
43}
44
45#[pymethods]
46impl PyAuthenticationContextsBuilder {
47    /// Create a new, empty ``AuthenticationContextsBuilder``.
48    #[new]
49    fn new() -> Self {
50        Self {
51            inner: synta_certificate::AuthenticationContextsBuilder::new(),
52            built: false,
53        }
54    }
55
56    /// Add an ``AuthenticationContext`` entry.
57    ///
58    /// :param context_type: URI string identifying the authentication context,
59    ///     e.g. ``"urn:id:skatteverket:2:1.0"``.
60    /// :param context_info: optional URL pointing to a document describing the
61    ///     context (e.g. ``"https://www.skatteverket.se/ac/context.xml"``), or
62    ///     ``None``.
63    /// :raises ValueError: if DER encoding fails (deferred to :meth:`build`).
64    #[pyo3(signature = (context_type, context_info=None))]
65    fn add<'py>(
66        slf: Bound<'py, Self>,
67        context_type: &str,
68        context_info: Option<&str>,
69    ) -> Bound<'py, Self> {
70        {
71            let mut guard = slf.borrow_mut();
72            let old = std::mem::replace(
73                &mut guard.inner,
74                synta_certificate::AuthenticationContextsBuilder::new(),
75            );
76            guard.inner = old.add(context_type, context_info);
77        }
78        slf
79    }
80
81    /// Build the DER-encoded ``AuthenticationContexts`` SEQUENCE OF.
82    ///
83    /// :returns: DER bytes of the ``AuthenticationContexts``.
84    /// :raises ValueError: if no entries were added or DER encoding fails.
85    fn build<'py>(&mut self, py: Python<'py>) -> PyResult<Bound<'py, PyBytes>> {
86        if self.built {
87            return Err(pyo3::exceptions::PyValueError::new_err(
88                "build() has already been called; create a new builder",
89            ));
90        }
91        self.built = true;
92        let inner = std::mem::replace(
93            &mut self.inner,
94            synta_certificate::AuthenticationContextsBuilder::new(),
95        );
96        let der = inner
97            .build()
98            .map_err(pyo3::exceptions::PyValueError::new_err)?;
99        Ok(PyBytes::new(py, &der))
100    }
101
102    fn __repr__(&self) -> String {
103        "AuthenticationContextsBuilder()".to_string()
104    }
105}
106
107// ── register ──────────────────────────────────────────────────────────────────
108
109/// Register ACE-88 builder classes into the given module.
110pub(super) fn register_ace88_classes(m: &Bound<'_, PyModule>) -> PyResult<()> {
111    m.add_class::<PyAuthenticationContextsBuilder>()?;
112    Ok(())
113}