rusty_vault 0.2.1

RustyVault is a powerful identity-based secrets management software, providing features such as cryptographic key management, encryption as a service, public key cryptography, certificates management, identity credentials management and so forth. RustyVault's RESTful API is designed to be fully compatible with Hashicorp Vault.
Documentation
use std::{collections::HashMap, sync::Arc};

use openssl::x509::X509;
use serde_json::json;

use super::{PkiBackend, PkiBackendInner};
use crate::{
    context::Context,
    errors::RvError,
    logical::{Backend, Field, FieldType, Operation, Path, PathOperation, Request, Response},
    new_fields, new_fields_internal, new_path, new_path_internal,
    storage::StorageEntry,
    utils::cert::CertBundle,
};

impl PkiBackend {
    pub fn fetch_ca_path(&self) -> Path {
        let pki_backend_ref = Arc::clone(&self.inner);

        let path = new_path!({
            pattern: "ca(/pem)?",
            operations: [
                {op: Operation::Read, handler: pki_backend_ref.read_path_fetch_ca}
            ],
            help: r#"
This allows certificates to be fetched. If using the fetch/ prefix any non-revoked certificate can be fetched.
Using "ca" or "crl" as the value fetches the appropriate information in DER encoding. Add "/pem" to either to get PEM encoding.
                "#
        });

        path
    }

    pub fn fetch_crl_path(&self) -> Path {
        let pki_backend_ref = Arc::clone(&self.inner);

        let path = new_path!({
            pattern: "crl(/pem)?",
            operations: [
                {op: Operation::Read, handler: pki_backend_ref.read_path_fetch_crl}
            ],
            help: r#"
This allows certificates to be fetched. If using the fetch/ prefix any non-revoked certificate can be fetched.
Using "ca" or "crl" as the value fetches the appropriate information in DER encoding. Add "/pem" to either to get PEM encoding.
                "#
        });

        path
    }

    pub fn fetch_cert_path(&self) -> Path {
        let pki_backend_ref = Arc::clone(&self.inner);

        let path = new_path!({
            pattern: r"cert/(?P<serial>[0-9A-Fa-f-:]+)",
            fields: {
                "serial": {
                    field_type: FieldType::Str,
                    description: "Certificate serial number, in colon- or hyphen-separated octal"
                }
            },
            operations: [
                {op: Operation::Read, handler: pki_backend_ref.read_path_fetch_cert}
            ],
            help: r#"
This allows certificates to be fetched. If using the fetch/ prefix any non-revoked certificate can be fetched.
Using "ca" or "crl" as the value fetches the appropriate information in DER encoding. Add "/pem" to either to get PEM encoding.
                "#
        });

        path
    }

    pub fn fetch_cert_crl_path(&self) -> Path {
        let pki_backend_ref = Arc::clone(&self.inner);

        let path = new_path!({
            pattern: "cert/crl",
            operations: [
                {op: Operation::Read, handler: pki_backend_ref.read_path_fetch_cert_crl}
            ],
            help: r#"
This allows certificates to be fetched. If using the fetch/ prefix any non-revoked certificate can be fetched.
Using "ca" or "crl" as the value fetches the appropriate information in DER encoding. Add "/pem" to either to get PEM encoding.
                "#
        });

        path
    }
}

impl PkiBackendInner {
    pub fn handle_fetch_cert_bundle(&self, cert_bundle: &CertBundle) -> Result<Option<Response>, RvError> {
        let ca_chain_pem: String = cert_bundle
            .ca_chain
            .iter()
            .rev()
            .map(|x509| x509.to_pem().unwrap())
            .map(|pem| String::from_utf8_lossy(&pem).to_string())
            .collect::<Vec<String>>()
            .join("");
        let resp_data = json!({
            "ca_chain": ca_chain_pem,
            "certificate": String::from_utf8_lossy(&cert_bundle.certificate.to_pem()?),
            "serial_number": cert_bundle.serial_number.clone(),
        })
        .as_object()
        .unwrap()
        .clone();

        Ok(Some(Response::data_response(Some(resp_data))))
    }

    pub fn read_path_fetch_ca(&self, _backend: &dyn Backend, req: &mut Request) -> Result<Option<Response>, RvError> {
        let ca_bundle = self.fetch_ca_bundle(req)?;
        self.handle_fetch_cert_bundle(&ca_bundle)
    }

    pub fn read_path_fetch_crl(&self, _backend: &dyn Backend, _req: &mut Request) -> Result<Option<Response>, RvError> {
        Ok(None)
    }

    pub fn read_path_fetch_cert(&self, _backend: &dyn Backend, req: &mut Request) -> Result<Option<Response>, RvError> {
        let serial_number_value = req.get_data("serial")?;
        let serial_number = serial_number_value.as_str().ok_or(RvError::ErrRequestFieldInvalid)?;
        let serial_number_hex = serial_number.replace(":", "-").to_lowercase();
        let cert = self.fetch_cert(req, &serial_number_hex)?;
        let ca_bundle = self.fetch_ca_bundle(req)?;

        let mut ca_chain_pem: String = ca_bundle
            .ca_chain
            .iter()
            .rev()
            .map(|x509| x509.to_pem().unwrap())
            .map(|pem| String::from_utf8_lossy(&pem).to_string())
            .collect::<Vec<String>>()
            .join("");

        ca_chain_pem = ca_chain_pem + &String::from_utf8_lossy(&ca_bundle.certificate.to_pem().unwrap());

        let resp_data = json!({
            "ca_chain": ca_chain_pem,
            "certificate": String::from_utf8_lossy(&cert.to_pem()?),
            "serial_number": serial_number,
        })
        .as_object()
        .unwrap()
        .clone();

        Ok(Some(Response::data_response(Some(resp_data))))
    }

    pub fn read_path_fetch_cert_crl(
        &self,
        _backend: &dyn Backend,
        _req: &mut Request,
    ) -> Result<Option<Response>, RvError> {
        Ok(None)
    }

    pub fn fetch_cert(&self, req: &Request, serial_number: &str) -> Result<X509, RvError> {
        let entry = req.storage_get(format!("certs/{}", serial_number).as_str())?;
        if entry.is_none() {
            return Err(RvError::ErrPkiCertNotFound);
        }

        let cert: X509 = X509::from_der(entry.unwrap().value.as_slice())?;
        Ok(cert)
    }

    pub fn store_cert(&self, req: &Request, serial_number: &str, cert: &X509) -> Result<(), RvError> {
        let value = cert.to_der()?;
        let entry = StorageEntry { key: format!("certs/{}", serial_number), value };
        req.storage_put(&entry)?;
        Ok(())
    }

    pub fn delete_cert(&self, req: &Request, serial_number: &str) -> Result<(), RvError> {
        req.storage_delete(format!("certs/{}", serial_number).as_str())?;
        Ok(())
    }
}