mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-11 18:33:01 +02:00
Compare commits
9 commits
13b21b00a9
...
b71186d958
Author | SHA1 | Date | |
---|---|---|---|
|
b71186d958 |
||
|
c362499cef |
||
|
14774fa153 |
||
|
ff805d8ae1 |
||
|
f0994355d4 |
||
|
980774a275 |
||
|
e4a6abe15e |
||
|
df1cb10a8e |
||
|
651d07a609 |
7 changed files with 123 additions and 20 deletions
12
Cargo.lock
generated
12
Cargo.lock
generated
|
@ -1076,6 +1076,7 @@ dependencies = [
|
||||||
"loole",
|
"loole",
|
||||||
"lru-cache",
|
"lru-cache",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"recaptcha-verify",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"ruma",
|
"ruma",
|
||||||
|
@ -3751,6 +3752,17 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "recaptcha-verify"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e3be7b2e46e24637ac96b0c9f70070f188652018573f36f4e511dcad09738a"
|
||||||
|
dependencies = [
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.13"
|
version = "0.5.13"
|
||||||
|
|
|
@ -441,6 +441,26 @@
|
||||||
#
|
#
|
||||||
#registration_token_file =
|
#registration_token_file =
|
||||||
|
|
||||||
|
# The public site key for reCaptcha. If this is provided, reCaptcha
|
||||||
|
# becomes required during registration. If both captcha *and*
|
||||||
|
# registration token are enabled, both will be required during
|
||||||
|
# registration.
|
||||||
|
#
|
||||||
|
# IMPORTANT: "Verify the origin of reCAPTCHA solutions" **MUST** BE
|
||||||
|
# DISABLED IF YOU WANT THE CAPTCHA TO WORK IN 3RD PARTY CLIENTS, OR
|
||||||
|
# CLIENTS HOSTED ON DOMAINS OTHER THAN YOUR OWN!
|
||||||
|
#
|
||||||
|
# Registration must be enabled (`allow_registration` must be true) for
|
||||||
|
# this to have any effect.
|
||||||
|
#
|
||||||
|
#recaptcha_site_key =
|
||||||
|
|
||||||
|
# The private site key for reCaptcha.
|
||||||
|
# If this is omitted, captcha registration will not work,
|
||||||
|
# even if `recaptcha_site_key` is set.
|
||||||
|
#
|
||||||
|
#recaptcha_private_site_key =
|
||||||
|
|
||||||
# Controls whether encrypted rooms and events are allowed.
|
# Controls whether encrypted rooms and events are allowed.
|
||||||
#
|
#
|
||||||
#allow_encryption = true
|
#allow_encryption = true
|
||||||
|
|
|
@ -291,19 +291,34 @@ pub(crate) async fn register_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
// UIAA
|
// UIAA
|
||||||
let mut uiaainfo;
|
let mut uiaainfo = UiaaInfo {
|
||||||
let skip_auth = if services.globals.registration_token.is_some() {
|
flows: Vec::new(),
|
||||||
|
completed: Vec::new(),
|
||||||
|
params: Box::default(),
|
||||||
|
session: None,
|
||||||
|
auth_error: None,
|
||||||
|
};
|
||||||
|
let mut skip_auth = body.appservice_info.is_some();
|
||||||
|
if services.globals.registration_token.is_some() {
|
||||||
// Registration token required
|
// Registration token required
|
||||||
uiaainfo = UiaaInfo {
|
uiaainfo.flows.push(AuthFlow {
|
||||||
flows: vec![AuthFlow {
|
stages: vec![AuthType::RegistrationToken],
|
||||||
stages: vec![AuthType::RegistrationToken],
|
});
|
||||||
}],
|
}
|
||||||
completed: Vec::new(),
|
if services.config.recaptcha_private_site_key.is_some() {
|
||||||
params: Box::default(),
|
if let Some(pubkey) = &services.config.recaptcha_site_key {
|
||||||
session: None,
|
// ReCaptcha required
|
||||||
auth_error: None,
|
uiaainfo
|
||||||
};
|
.flows
|
||||||
body.appservice_info.is_some()
|
.push(AuthFlow { stages: vec![AuthType::ReCaptcha] });
|
||||||
|
uiaainfo.params = serde_json::value::to_raw_value(&serde_json::json!({
|
||||||
|
"m.login.recaptcha": {
|
||||||
|
"public_key": pubkey,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
.expect("Failed to serialize recaptcha params");
|
||||||
|
skip_auth = skip_auth || is_guest;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No registration token necessary, but clients must still go through the flow
|
// No registration token necessary, but clients must still go through the flow
|
||||||
uiaainfo = UiaaInfo {
|
uiaainfo = UiaaInfo {
|
||||||
|
@ -313,8 +328,8 @@ pub(crate) async fn register_route(
|
||||||
session: None,
|
session: None,
|
||||||
auth_error: None,
|
auth_error: None,
|
||||||
};
|
};
|
||||||
body.appservice_info.is_some() || is_guest
|
skip_auth = skip_auth || is_guest;
|
||||||
};
|
}
|
||||||
|
|
||||||
if !skip_auth {
|
if !skip_auth {
|
||||||
match &body.auth {
|
match &body.auth {
|
||||||
|
|
|
@ -180,19 +180,28 @@ pub fn check(config: &Config) -> Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.recaptcha_site_key.is_some() && config.recaptcha_private_site_key.is_none() {
|
||||||
|
return Err!(Config(
|
||||||
|
"recaptcha_private_site_key",
|
||||||
|
"reCAPTCHA private site key is required when reCAPTCHA site key is set."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if config.allow_registration
|
if config.allow_registration
|
||||||
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
&& !config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||||
&& config.registration_token.is_none()
|
&& config.registration_token.is_none()
|
||||||
&& config.registration_token_file.is_none()
|
&& config.registration_token_file.is_none()
|
||||||
|
&& config.recaptcha_site_key.is_none()
|
||||||
{
|
{
|
||||||
return Err!(Config(
|
return Err!(Config(
|
||||||
"registration_token",
|
"registration_token",
|
||||||
"!! You have `allow_registration` enabled without a token configured in your config \
|
"!! You have `allow_registration` enabled without a token or captcha configured \
|
||||||
which means you are allowing ANYONE to register on your conduwuit instance without \
|
which means you are allowing ANYONE to register on your continuwuity instance \
|
||||||
any 2nd-step (e.g. registration token). If this is not the intended behaviour, \
|
without any 2nd-step (e.g. registration token, captcha), which is FREQUENTLY \
|
||||||
please set a registration token. For security and safety reasons, conduwuit will \
|
abused by malicious actors. If this is not the intended behaviour, please set a \
|
||||||
shut down. If you are extra sure this is the desired behaviour you want, please \
|
registration token. For security and safety reasons, continuwuity will shut down. \
|
||||||
set the following config option to true:
|
If you are extra sure this is the desired behaviour you want, please set the \
|
||||||
|
following config option to true:
|
||||||
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
|
`yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -556,6 +556,24 @@ pub struct Config {
|
||||||
/// example: "/etc/continuwuity/.reg_token"
|
/// example: "/etc/continuwuity/.reg_token"
|
||||||
pub registration_token_file: Option<PathBuf>,
|
pub registration_token_file: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// The public site key for reCaptcha. If this is provided, reCaptcha
|
||||||
|
/// becomes required during registration. If both captcha *and*
|
||||||
|
/// registration token are enabled, both will be required during
|
||||||
|
/// registration.
|
||||||
|
///
|
||||||
|
/// IMPORTANT: "Verify the origin of reCAPTCHA solutions" **MUST** BE
|
||||||
|
/// DISABLED IF YOU WANT THE CAPTCHA TO WORK IN 3RD PARTY CLIENTS, OR
|
||||||
|
/// CLIENTS HOSTED ON DOMAINS OTHER THAN YOUR OWN!
|
||||||
|
///
|
||||||
|
/// Registration must be enabled (`allow_registration` must be true) for
|
||||||
|
/// this to have any effect.
|
||||||
|
pub recaptcha_site_key: Option<String>,
|
||||||
|
|
||||||
|
/// The private site key for reCaptcha.
|
||||||
|
/// If this is omitted, captcha registration will not work,
|
||||||
|
/// even if `recaptcha_site_key` is set.
|
||||||
|
pub recaptcha_private_site_key: Option<String>,
|
||||||
|
|
||||||
/// Controls whether encrypted rooms and events are allowed.
|
/// Controls whether encrypted rooms and events are allowed.
|
||||||
#[serde(default = "true_fn")]
|
#[serde(default = "true_fn")]
|
||||||
pub allow_encryption: bool,
|
pub allow_encryption: bool,
|
||||||
|
|
|
@ -111,6 +111,7 @@ webpage.workspace = true
|
||||||
webpage.optional = true
|
webpage.optional = true
|
||||||
blurhash.workspace = true
|
blurhash.workspace = true
|
||||||
blurhash.optional = true
|
blurhash.optional = true
|
||||||
|
recaptcha-verify = { version = "0.1.5", default-features = false }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
|
@ -177,6 +177,34 @@ pub async fn try_auth(
|
||||||
// Password was correct! Let's add it to `completed`
|
// Password was correct! Let's add it to `completed`
|
||||||
uiaainfo.completed.push(AuthType::Password);
|
uiaainfo.completed.push(AuthType::Password);
|
||||||
},
|
},
|
||||||
|
| AuthData::ReCaptcha(r) => {
|
||||||
|
if self.services.config.recaptcha_private_site_key.is_none() {
|
||||||
|
return Err!(Request(Forbidden("ReCaptcha is not configured.")));
|
||||||
|
}
|
||||||
|
match recaptcha_verify::verify(
|
||||||
|
self.services
|
||||||
|
.config
|
||||||
|
.recaptcha_private_site_key
|
||||||
|
.as_ref()
|
||||||
|
.unwrap(),
|
||||||
|
r.response.as_str(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
| Ok(()) => {
|
||||||
|
uiaainfo.completed.push(AuthType::ReCaptcha);
|
||||||
|
},
|
||||||
|
| Err(e) => {
|
||||||
|
error!("ReCaptcha verification failed: {e:?}");
|
||||||
|
uiaainfo.auth_error = Some(ruma::api::client::error::StandardErrorBody {
|
||||||
|
kind: ErrorKind::forbidden(),
|
||||||
|
message: "ReCaptcha verification failed.".to_owned(),
|
||||||
|
});
|
||||||
|
return Ok((false, uiaainfo));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
| AuthData::RegistrationToken(t) => {
|
| AuthData::RegistrationToken(t) => {
|
||||||
let tokens = self.read_tokens().await?;
|
let tokens = self.read_tokens().await?;
|
||||||
if tokens.contains(t.token.trim()) {
|
if tokens.contains(t.token.trim()) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue