mas_templates/context/
captcha.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use std::sync::Arc;
8
9use minijinja::{
10    Value,
11    value::{Enumerator, Object},
12};
13use serde::Serialize;
14
15use crate::TemplateContext;
16
17#[derive(Debug)]
18struct CaptchaConfig(mas_data_model::CaptchaConfig);
19
20impl Object for CaptchaConfig {
21    fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
22        match key.as_str() {
23            Some("service") => Some(match &self.0.service {
24                mas_data_model::CaptchaService::RecaptchaV2 => "recaptcha_v2".into(),
25                mas_data_model::CaptchaService::CloudflareTurnstile => {
26                    "cloudflare_turnstile".into()
27                }
28                mas_data_model::CaptchaService::HCaptcha => "hcaptcha".into(),
29            }),
30            Some("site_key") => Some(self.0.site_key.clone().into()),
31            _ => None,
32        }
33    }
34
35    fn enumerate(self: &Arc<Self>) -> Enumerator {
36        Enumerator::Str(&["service", "site_key"])
37    }
38}
39
40/// Context with an optional CAPTCHA configuration in it
41#[derive(Serialize)]
42pub struct WithCaptcha<T> {
43    captcha: Option<Value>,
44
45    #[serde(flatten)]
46    inner: T,
47}
48
49impl<T> WithCaptcha<T> {
50    #[must_use]
51    pub(crate) fn new(captcha: Option<mas_data_model::CaptchaConfig>, inner: T) -> Self {
52        Self {
53            captcha: captcha.map(|captcha| Value::from_object(CaptchaConfig(captcha))),
54            inner,
55        }
56    }
57}
58
59impl<T: TemplateContext> TemplateContext for WithCaptcha<T> {
60    fn sample(
61        now: chrono::DateTime<chrono::prelude::Utc>,
62        rng: &mut impl rand::prelude::Rng,
63    ) -> Vec<Self>
64    where
65        Self: Sized,
66    {
67        let inner = T::sample(now, rng);
68        inner
69            .into_iter()
70            .map(|inner| Self::new(None, inner))
71            .collect()
72    }
73}