redact-store 1.2.16

Provides a common interface on top of storage backings
use crate::routes::error::{
    BadRequestRejection, CryptoErrorRejection, NotFoundRejection, X509ErrorRejection,
};
use redact_crypto::{CryptoError, IndexedStorer, Type};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio_rustls::rustls::Certificate;
use warp::{Filter, Rejection, Reply};

#[derive(Serialize, Deserialize)]
struct GetQueryParams {
    skip: Option<u64>,
    page_size: Option<i64>,
}

#[derive(Serialize)]
struct GetCollectionResponse<T: Serialize> {
    results: Vec<T>,
}

pub fn get<T: IndexedStorer>(
    storer: Arc<T>,
) -> impl Filter<Extract = (impl Reply,), Error = Rejection> + Clone {
    warp::path!(String)
        .map(|data_path| data_path)
        .and(
            warp::query::<GetQueryParams>().and_then(|query: GetQueryParams| async move {
                if let Some(page_size) = query.page_size {
                    if page_size > 100 {
                        Err(warp::reject::custom(BadRequestRejection))
                    } else {
                        Ok(query)
                    }
                } else {
                    Ok(query)
                }
            }),
        )
        .and(warp::ext::get::<Certificate>())
        .and(warp::any().map(move || storer.clone()))
        .and_then(
            move |data_path: String,
            query: GetQueryParams,
            client_cert: Certificate,
            storer: Arc<T>| async move {
                let x509_result = x509_parser::parse_x509_certificate(&client_cert.0);
                let _ = match x509_result {
                    Ok((_, cert)) => Ok(cert),
                    Err(e) => Err(warp::reject::custom(X509ErrorRejection(e))),
                }?;

                if let Some(skip) = query.skip {
                    let page_size = if let Some(page_size) = query.page_size {
                        page_size
                    } else {
                        10
                    };

                    match storer.list::<Type>(&data_path, skip, page_size).await {
                        Ok(results) => Ok::<_, Rejection>(warp::reply::with_status(
                            warp::reply::json(&GetCollectionResponse { results }),
                            warp::http::StatusCode::OK,
                        )),
                        Err(e) => {
                            if let CryptoError::NotFound { .. } = e {
                                Err(warp::reject::custom(NotFoundRejection))
                            } else {
                                log::error!("An error occurred while retrieving the entries list at path {}: {}", data_path, e);
                                Err(warp::reject::custom(CryptoErrorRejection(e)))
                            }
                        }
                    }
                } else {
                    match storer.get::<Type>(&data_path).await {
                        Ok(data) => {
                            let dereferenced_data = data.dereference().await;

                            match dereferenced_data {
                                Ok(data) => Ok::<_, Rejection>(warp::reply::with_status(
                                    warp::reply::json(&data),
                                    warp::http::StatusCode::OK,
                                )),
                                Err(e) => {
                                    if let CryptoError::NotFound { .. } = e {
                                        Err(warp::reject::custom(NotFoundRejection))
                                    } else {
                                        log::error!("An error occurred while dereferencing the entry at path {}: {}", data_path, e);
                                        Err(warp::reject::custom(CryptoErrorRejection(e)))
                                    }
                                }
                            }
                        }
                        Err(e) => {
                            if let CryptoError::NotFound { .. } = e {
                                Err(warp::reject::custom(NotFoundRejection))
                            } else {
                                log::error!("An error occurred while retrieving the entry at path {}: {}", data_path, e);
                                Err(warp::reject::custom(CryptoErrorRejection(e)))
                            }
                        }
                    }
                }
            },
        )
}