1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
pub use facebook_fb_login_deauth_callback;

use std::{collections::HashMap, convert::Infallible, error, sync::Arc};

use facebook_fb_login_deauth_callback::{
    get::PASS_BACK_STATUS_CODE,
    post::{pass_back_with_signed_request, PassBackCallbackFn, SIGNED_REQUEST_FORM_KEY},
};
use warp::{
    http::{Response, StatusCode},
    hyper::Body,
    Filter,
};

pub trait Context: Send + Sync + Clone {
    fn get_app_secret(&self, app_id: u64) -> Result<String, Box<dyn error::Error + Send + Sync>>;
}

pub fn handle<C: Context>(
    path_prefix: String,
    ctx: C,
    callback: PassBackCallbackFn<'static, C>,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    get_filter(path_prefix.clone(), ctx.clone()).or(post_filter(
        path_prefix,
        ctx,
        Arc::new(callback),
    ))
}

fn get_filter<C: Context>(
    path_prefix: String,
    ctx: C,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path(path_prefix)
        .and(warp::path::param::<u64>())
        .and(warp::path::end())
        .and(warp::get())
        .and_then(move |app_id| {
            let ctx = ctx.clone();

            async move {
                let part: Result<Result<Response<Body>, warp::http::Error>, Infallible> = {
                    match ctx.get_app_secret(app_id) {
                        Ok(_) => Ok(Response::builder()
                            .status(PASS_BACK_STATUS_CODE)
                            .body("".into())),
                        Err(err) => Ok(Response::builder()
                            .status(StatusCode::INTERNAL_SERVER_ERROR)
                            .body(err.to_string().into())),
                    }
                };
                part
            }
        })
}

fn post_filter<C: Context>(
    path_prefix: String,
    ctx: C,
    callback: Arc<PassBackCallbackFn<'static, C>>,
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
    warp::path(path_prefix)
        .and(warp::path::param::<u64>())
        .and(warp::path::end())
        .and(warp::post())
        .and(warp::body::content_length_limit(1024 * 32).and(warp::body::form()))
        .and_then(move |app_id: u64, request_form: HashMap<String, String>| {
            let ctx = ctx.clone();
            let callback = callback.clone();

            async move {
                let part: Result<Result<Response<Body>, warp::http::Error>, Infallible> = {
                    match request_form.get(SIGNED_REQUEST_FORM_KEY) {
                        Some(signed_request) => match ctx.get_app_secret(app_id) {
                            Ok(app_secret) => {
                                let res = pass_back_with_signed_request(
                                    signed_request,
                                    &app_secret,
                                    ctx,
                                    callback,
                                )
                                .await;

                                Ok(Response::builder()
                                    .status(res.status_code)
                                    .body(res.body.into()))
                            }
                            Err(err) => Ok(Response::builder()
                                .status(StatusCode::INTERNAL_SERVER_ERROR)
                                .body(err.to_string().into())),
                        },
                        None => Ok(Response::builder()
                            .status(StatusCode::BAD_REQUEST)
                            .body("form invalid".into())),
                    }
                };
                part
            }
        })
}