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
use crate::error::{ApiError, ApiErrorKind};
use crate::extractors::{registration_path_args::RegistrationPathArgs, routers::RouterType};
use actix_web::{dev::Payload, web, FromRequest, HttpRequest};
use futures::{future::LocalBoxFuture, FutureExt};
use lazy_static::lazy_static;
use regex::Regex;
use uuid::Uuid;

lazy_static! {
    static ref VALID_TOKEN: Regex = Regex::new(r"^[^ ]{8,}$").unwrap();
}

/// Extracts the router data from the request body and validates the token
/// against the given router's token schema (taken from request path params).
#[derive(serde::Deserialize)]
pub struct RouterDataInput {
    pub token: String,
    #[serde(rename = "channelID")]
    pub channel_id: Option<Uuid>,
    pub key: Option<String>,
    pub aps: Option<String>,
}

impl FromRequest for RouterDataInput {
    type Error = ApiError;
    type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
        let req = req.clone();
        let mut payload = payload.take();

        async move {
            let path_args = RegistrationPathArgs::extract(&req).into_inner()?;
            let data: web::Json<Self> = web::Json::from_request(&req, &mut payload)
                .await
                .map_err(ApiErrorKind::PayloadError)?;

            // Validate the token according to each router's token schema
            let is_valid = match path_args.router_type {
                RouterType::WebPush => true,
                RouterType::FCM | RouterType::GCM | RouterType::APNS => {
                    VALID_TOKEN.is_match(&data.token)
                }
                #[cfg(feature = "stub")]
                RouterType::STUB => data.token.as_str() == "success",
            };

            if !is_valid {
                return Err(ApiErrorKind::InvalidRouterToken.into());
            }

            Ok(data.into_inner())
        }
        .boxed_local()
    }
}