redact-client 1.5.9

Receives request for private data and decrypts it to display securely in browser
pub mod data;

use crate::{
    relayer::Relayer,
    render::Renderer,
    routes::error::{
        IframeTokensDoNotMatchRejection, NoPathTokenProvided, SessionTokenNotFoundRejection,
    },
    token::TokenGenerator,
};
use redact_crypto::Storer;
use std::sync::Arc;
use warp::{filters::BoxedFilter, path::Peek, Filter, Rejection, Reply};
use warp_sessions::{
    CookieOptions, SameSiteCookieOption, Session, SessionStore, SessionWithStore, WithSession,
};

pub fn data<H: Storer, R: Renderer + Clone + Send + 'static, T: TokenGenerator, Q: Relayer>(
    storer: Arc<H>,
    render_engine: R,
    token_generator: T,
    relayer: Q,
) -> impl Filter<Extract = (Box<dyn Reply>, String, Option<String>, Option<String>), Error = Rejection>
       + Clone {
    warp::path!("data" / ..).and(
        data::get(
            storer.clone(),
            render_engine.clone(),
            token_generator.clone(),
        )
        .or(data::post(render_engine, token_generator, storer, relayer))
        .unify(),
    )
}

pub fn session<T, S: SessionStore>(
    session_store: S,
) -> impl Fn(T) -> BoxedFilter<(WithSession<Box<dyn Reply>>,)>
where
    T: Filter<
            Extract = (Box<dyn Reply>, String, Option<String>, Option<String>),
            Error = Rejection,
        > + Clone
        + Send
        + Sync
        + 'static,
{
    move |filter: T| {
        warp::any()
            .and(warp::path::peek().map(|peek: Peek| {
                if let Some(token) = peek.segments().last() {
                    Some(token.to_owned())
                } else {
                    None
                }
            }))
            .and(warp_sessions::request::with_session(
                session_store.clone(),
                Some(CookieOptions {
                    cookie_name: "sid",
                    cookie_value: None,
                    max_age: Some(60),
                    domain: None,
                    path: None,
                    secure: false,
                    http_only: true,
                    same_site: Some(SameSiteCookieOption::None),
                }),
            ))
            .and_then(
                move |token: Option<String>, session_with_store: SessionWithStore<S>| async move {
                    let token = match token {
                        Some(t) => t,
                        None => return Err(warp::reject::custom(NoPathTokenProvided)),
                    };
                    if let Some(session_token) = session_with_store.session.get::<String>("token") {
                        if session_token != token {
                            Err(warp::reject::custom(IframeTokensDoNotMatchRejection))
                        } else {
                            Ok(session_with_store)
                        }
                    } else {
                        Err(warp::reject::custom(SessionTokenNotFoundRejection))
                    }
                },
            )
            .and(filter)
            .and_then(
                |mut session_with_store: SessionWithStore<S>,
                 reply: Box<dyn Reply>,
                 old_path: String,
                 new_path: Option<String>,
                 token: Option<String>| async move {
                    session_with_store.cookie_options.path = Some(old_path);
                    session_with_store.session.destroy();

                    match (new_path, token) {
                        (Some(new_path), Some(token)) => {
                            let mut new_session = SessionWithStore::<S> {
                                session: Session::new(),
                                session_store: session_with_store.session_store.clone(),
                                cookie_options: CookieOptions {
                                    cookie_name: "sid",
                                    cookie_value: None,
                                    max_age: Some(60),
                                    domain: None,
                                    path: Some(new_path),
                                    secure: false,
                                    http_only: true,
                                    same_site: Some(SameSiteCookieOption::None),
                                },
                            };

                            new_session
                                .session
                                .insert("token", token)
                                .map_err(|_| warp::reject())?;
                            Ok::<_, Rejection>((
                                Box::new(
                                    warp_sessions::reply::with_session(reply, session_with_store)
                                        .await?,
                                ) as Box<dyn Reply>,
                                new_session,
                            ))
                        }
                        _ => Ok::<_, Rejection>((
                            Box::new(reply) as Box<dyn Reply>,
                            session_with_store,
                        )),
                    }
                },
            )
            .untuple_one()
            .and_then(warp_sessions::reply::with_session)
            .boxed()
    }
}