diff --git a/crates/handlers/src/graphql/model/site_config.rs b/crates/handlers/src/graphql/model/site_config.rs index f22b6dbf0..f94bdc57d 100644 --- a/crates/handlers/src/graphql/model/site_config.rs +++ b/crates/handlers/src/graphql/model/site_config.rs @@ -14,15 +14,19 @@ #![allow(clippy::str_to_string)] // ComplexObject macro uses &str.to_string() -use async_graphql::{ComplexObject, SimpleObject, ID}; +use async_graphql::{ComplexObject, Enum, SimpleObject, ID}; use url::Url; pub const SITE_CONFIG_ID: &str = "site_config"; +pub const CAPTCHA_CONFIG_ID: &str = "captcha_config"; #[derive(SimpleObject)] #[graphql(complex)] #[allow(clippy::struct_excessive_bools)] pub struct SiteConfig { + /// The configuration of CAPTCHA provider. + captcha_config: Option, + /// The server name of the homeserver. server_name: String, @@ -47,12 +51,33 @@ pub struct SiteConfig { /// Whether passwords are enabled and users can change their own passwords. password_change_allowed: bool, + /// Whether passwords are enabled and users can register using a password. + password_registration_enabled: bool, + /// Minimum password complexity, from 0 to 4, in terms of a zxcvbn score. /// The exact scorer (including dictionaries and other data tables) /// in use is . minimum_password_complexity: u8, } +#[derive(SimpleObject)] +#[graphql(complex)] +pub struct CaptchaConfig { + /// Which Captcha service is being used + pub service: CaptchaService, + + /// The site key used by the instance + pub site_key: String, +} + +/// Which Captcha service is being used +#[derive(Enum, Debug, Clone, Copy, PartialEq, Eq)] +pub enum CaptchaService { + RecaptchaV2, + CloudflareTurnstile, + HCaptcha, +} + #[ComplexObject] impl SiteConfig { /// The ID of the site configuration. @@ -66,6 +91,7 @@ impl SiteConfig { /// [`mas_data_model:::SiteConfig`]. pub fn new(data_model: &mas_data_model::SiteConfig) -> Self { Self { + captcha_config: data_model.captcha.as_ref().map(CaptchaConfig::new), server_name: data_model.server_name.clone(), policy_uri: data_model.policy_uri.clone(), tos_uri: data_model.tos_uri.clone(), @@ -74,7 +100,32 @@ impl SiteConfig { display_name_change_allowed: data_model.displayname_change_allowed, password_login_enabled: data_model.password_login_enabled, password_change_allowed: data_model.password_change_allowed, + password_registration_enabled: data_model.password_registration_enabled, minimum_password_complexity: data_model.minimum_password_complexity, } } } + +#[ComplexObject] +impl CaptchaConfig { + pub async fn id(&self) -> ID { + CAPTCHA_CONFIG_ID.into() + } +} + +impl CaptchaConfig { + /// Create a new [`CaptchaConfig`] from the data model + /// [`mas_data_model:::CaptchaConfig`]. + pub fn new(data_model: &mas_data_model::CaptchaConfig) -> Self { + Self { + service: match data_model.service { + mas_data_model::CaptchaService::RecaptchaV2 => CaptchaService::RecaptchaV2, + mas_data_model::CaptchaService::CloudflareTurnstile => { + CaptchaService::CloudflareTurnstile + } + mas_data_model::CaptchaService::HCaptcha => CaptchaService::HCaptcha, + }, + site_key: data_model.site_key.clone(), + } + } +} diff --git a/frontend/schema.graphql b/frontend/schema.graphql index ee1f0dc5e..46a14a2ba 100644 --- a/frontend/schema.graphql +++ b/frontend/schema.graphql @@ -300,6 +300,27 @@ type BrowserSessionEdge { cursor: String! } +type CaptchaConfig { + """ + Which Captcha service is being used + """ + service: CaptchaService! + """ + The site key used by the instance + """ + siteKey: String! + id: ID! +} + +""" +Which Captcha service is being used +""" +enum CaptchaService { + RECAPTCHA_V2 + CLOUDFLARE_TURNSTILE + H_CAPTCHA +} + """ A compat session represents a client session which used the legacy Matrix login API. @@ -1414,6 +1435,10 @@ enum SetPrimaryEmailStatus { } type SiteConfig implements Node { + """ + The configuration of CAPTCHA provider. + """ + captchaConfig: CaptchaConfig """ The server name of the homeserver. """ @@ -1447,6 +1472,10 @@ type SiteConfig implements Node { """ passwordChangeAllowed: Boolean! """ + Whether passwords are enabled and users can register using a password. + """ + passwordRegistrationEnabled: Boolean! + """ Minimum password complexity, from 0 to 4, in terms of a zxcvbn score. The exact scorer (including dictionaries and other data tables) in use is . diff --git a/frontend/src/gql/graphql.ts b/frontend/src/gql/graphql.ts index 01b7d805d..9d2bb85da 100644 --- a/frontend/src/gql/graphql.ts +++ b/frontend/src/gql/graphql.ts @@ -212,6 +212,22 @@ export type BrowserSessionEdge = { node: BrowserSession; }; +export type CaptchaConfig = { + __typename?: 'CaptchaConfig'; + id: Scalars['ID']['output']; + /** Which Captcha service is being used */ + service: CaptchaService; + /** The site key used by the instance */ + siteKey: Scalars['String']['output']; +}; + +/** Which Captcha service is being used */ +export enum CaptchaService { + CloudflareTurnstile = 'CLOUDFLARE_TURNSTILE', + HCaptcha = 'H_CAPTCHA', + RecaptchaV2 = 'RECAPTCHA_V2' +} + /** * A compat session represents a client session which used the legacy Matrix * login API. @@ -1063,6 +1079,8 @@ export enum SetPrimaryEmailStatus { export type SiteConfig = Node & { __typename?: 'SiteConfig'; + /** The configuration of CAPTCHA provider. */ + captchaConfig?: Maybe; /** Whether users can change their display name. */ displayNameChangeAllowed: Scalars['Boolean']['output']; /** Whether users can change their email. */ @@ -1081,6 +1099,8 @@ export type SiteConfig = Node & { passwordChangeAllowed: Scalars['Boolean']['output']; /** Whether passwords are enabled for login. */ passwordLoginEnabled: Scalars['Boolean']['output']; + /** Whether passwords are enabled and users can register using a password. */ + passwordRegistrationEnabled: Scalars['Boolean']['output']; /** The URL to the privacy policy. */ policyUri?: Maybe; /** The server name of the homeserver. */ diff --git a/frontend/src/gql/schema.ts b/frontend/src/gql/schema.ts index 11c364f52..db64b98c2 100644 --- a/frontend/src/gql/schema.ts +++ b/frontend/src/gql/schema.ts @@ -529,6 +529,46 @@ export default { ], "interfaces": [] }, + { + "kind": "OBJECT", + "name": "CaptchaConfig", + "fields": [ + { + "name": "id", + "type": { + "kind": "NON_NULL", + "ofType": { + "kind": "SCALAR", + "name": "Any" + } + }, + "args": [] + }, + { + "name": "service", + "type": { + "kind": "NON_NULL", + "ofType": { + "kind": "SCALAR", + "name": "Any" + } + }, + "args": [] + }, + { + "name": "siteKey", + "type": { + "kind": "NON_NULL", + "ofType": { + "kind": "SCALAR", + "name": "Any" + } + }, + "args": [] + } + ], + "interfaces": [] + }, { "kind": "OBJECT", "name": "CompatSession", @@ -2570,6 +2610,15 @@ export default { "kind": "OBJECT", "name": "SiteConfig", "fields": [ + { + "name": "captchaConfig", + "type": { + "kind": "OBJECT", + "name": "CaptchaConfig", + "ofType": null + }, + "args": [] + }, { "name": "displayNameChangeAllowed", "type": { @@ -2644,6 +2693,17 @@ export default { }, "args": [] }, + { + "name": "passwordRegistrationEnabled", + "type": { + "kind": "NON_NULL", + "ofType": { + "kind": "SCALAR", + "name": "Any" + } + }, + "args": [] + }, { "name": "policyUri", "type": {