1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-01 20:26:56 +03:00

Render reCAPTCHA challenge on the registration form

This commit is contained in:
Quentin Gliech
2024-05-10 17:17:19 +02:00
parent c422c29a60
commit a3beeb2398
18 changed files with 342 additions and 19 deletions

View File

@ -0,0 +1,80 @@
// Copyright 2024 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use schemars::JsonSchema;
use serde::{de::Error, Deserialize, Serialize};
use crate::ConfigurationSection;
/// Which service should be used for CAPTCHA protection
#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
pub enum CaptchaServiceKind {
/// Use Google's reCAPTCHA v2 API
#[serde(rename = "recaptcha_v2")]
RecaptchaV2,
}
/// Configuration section to setup CAPTCHA protection on a few operations
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, Default)]
pub struct CaptchaConfig {
/// Which service should be used for CAPTCHA protection
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<CaptchaServiceKind>,
/// The site key to use
#[serde(skip_serializing_if = "Option::is_none")]
pub site_key: Option<String>,
/// The secret key to use
#[serde(skip_serializing_if = "Option::is_none")]
pub secret_key: Option<String>,
}
impl CaptchaConfig {
/// Returns true if the configuration is the default one
pub(crate) fn is_default(&self) -> bool {
self.service.is_none() && self.site_key.is_none() && self.secret_key.is_none()
}
}
impl ConfigurationSection for CaptchaConfig {
const PATH: Option<&'static str> = Some("captcha");
fn validate(&self, figment: &figment::Figment) -> Result<(), figment::Error> {
let metadata = figment.find_metadata(Self::PATH.unwrap());
let error_on_field = |mut error: figment::error::Error, field: &'static str| {
error.metadata = metadata.cloned();
error.profile = Some(figment::Profile::Default);
error.path = vec![Self::PATH.unwrap().to_owned(), field.to_owned()];
error
};
let missing_field = |field: &'static str| {
error_on_field(figment::error::Error::missing_field(field), field)
};
if let Some(CaptchaServiceKind::RecaptchaV2) = self.service {
if self.site_key.is_none() {
return Err(missing_field("site_key"));
}
if self.secret_key.is_none() {
return Err(missing_field("secret_key"));
}
}
Ok(())
}
}

View File

@ -17,6 +17,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
mod branding;
mod captcha;
mod clients;
mod database;
mod email;
@ -32,6 +33,7 @@ mod upstream_oauth2;
pub use self::{
branding::BrandingConfig,
captcha::{CaptchaConfig, CaptchaServiceKind},
clients::{ClientAuthMethodConfig, ClientConfig, ClientsConfig},
database::DatabaseConfig,
email::{EmailConfig, EmailSmtpMode, EmailTransportKind},
@ -107,6 +109,10 @@ pub struct RootConfig {
#[serde(default, skip_serializing_if = "BrandingConfig::is_default")]
pub branding: BrandingConfig,
/// Configuration section to setup CAPTCHA protection on a few operations
#[serde(default, skip_serializing_if = "CaptchaConfig::is_default")]
pub captcha: CaptchaConfig,
/// Experimental configuration options
#[serde(default, skip_serializing_if = "ExperimentalConfig::is_default")]
pub experimental: ExperimentalConfig,
@ -126,6 +132,7 @@ impl ConfigurationSection for RootConfig {
self.policy.validate(figment)?;
self.upstream_oauth2.validate(figment)?;
self.branding.validate(figment)?;
self.captcha.validate(figment)?;
self.experimental.validate(figment)?;
Ok(())
@ -155,6 +162,7 @@ impl RootConfig {
policy: PolicyConfig::default(),
upstream_oauth2: UpstreamOAuth2Config::default(),
branding: BrandingConfig::default(),
captcha: CaptchaConfig::default(),
experimental: ExperimentalConfig::default(),
})
}
@ -175,6 +183,7 @@ impl RootConfig {
policy: PolicyConfig::default(),
upstream_oauth2: UpstreamOAuth2Config::default(),
branding: BrandingConfig::default(),
captcha: CaptchaConfig::default(),
experimental: ExperimentalConfig::default(),
}
}
@ -209,6 +218,9 @@ pub struct AppConfig {
#[serde(default)]
pub branding: BrandingConfig,
#[serde(default)]
pub captcha: CaptchaConfig,
#[serde(default)]
pub experimental: ExperimentalConfig,
}
@ -224,6 +236,7 @@ impl ConfigurationSection for AppConfig {
self.matrix.validate(figment)?;
self.policy.validate(figment)?;
self.branding.validate(figment)?;
self.captcha.validate(figment)?;
self.experimental.validate(figment)?;
Ok(())