feat: Readded support for reading registration tokens from a file
Co-authored-by: Ginger <ginger@gingershaped.computer>
This commit is contained in:
parent
da561ab792
commit
5eb74bc1dd
5 changed files with 79 additions and 28 deletions
|
|
@ -476,18 +476,23 @@
|
|||
#yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse = false
|
||||
|
||||
# A static registration token that new users will have to provide when
|
||||
# creating an account. If unset and `allow_registration` is true,
|
||||
# you must set
|
||||
# `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
# to true to allow open registration without any conditions.
|
||||
#
|
||||
# If you do not want to set a static token, the `!admin token` commands
|
||||
# may also be used to manage registration tokens.
|
||||
# creating an account. This token does not supersede tokens from other sources, such as the `!admin token`
|
||||
# command or the `registration_token_file` configuration option.
|
||||
#
|
||||
# example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
#
|
||||
#registration_token =
|
||||
|
||||
# A path to a file containing static registration tokens, one per line.
|
||||
# Tokens in this file do not supersede tokens from other sources, such as the `!admin token`
|
||||
# command or the `registration_token` configuration option.
|
||||
#
|
||||
# The file will be read once, when Continuwuity starts. It is not currently reread
|
||||
# when the server configuration is reloaded. If the file cannot be read, Continuwuity
|
||||
# will fail to start.
|
||||
#
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ pub fn check(config: &Config) -> Result {
|
|||
if config.allow_registration
|
||||
&& config.yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse
|
||||
&& config.registration_token.is_none()
|
||||
&& config.registration_token_file.is_none()
|
||||
{
|
||||
warn!(
|
||||
"Open registration is enabled via setting \
|
||||
|
|
|
|||
|
|
@ -609,19 +609,25 @@ pub struct Config {
|
|||
pub yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse: bool,
|
||||
|
||||
/// A static registration token that new users will have to provide when
|
||||
/// creating an account. If unset and `allow_registration` is true,
|
||||
/// you must set
|
||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
||||
/// to true to allow open registration without any conditions.
|
||||
///
|
||||
/// If you do not want to set a static token, the `!admin token` commands
|
||||
/// may also be used to manage registration tokens.
|
||||
/// creating an account. This token does not supersede tokens from other
|
||||
/// sources, such as the `!admin token` command or the
|
||||
/// `registration_token_file` configuration option.
|
||||
///
|
||||
/// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||
///
|
||||
/// display: sensitive
|
||||
pub registration_token: Option<String>,
|
||||
|
||||
/// A path to a file containing static registration tokens, one per line.
|
||||
/// Tokens in this file do not supersede tokens from other sources, such as
|
||||
/// the `!admin token` command or the `registration_token` configuration
|
||||
/// option.
|
||||
///
|
||||
/// The file will be read once, when Continuwuity starts. It is not
|
||||
/// currently reread when the server configuration is reloaded. If the file
|
||||
/// cannot be read, Continuwuity will fail to start.
|
||||
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
|
||||
|
|
|
|||
|
|
@ -19,10 +19,9 @@ impl Service {
|
|||
/// Get the registration token set in the config file, if it exists.
|
||||
#[must_use]
|
||||
pub fn get_config_file_token(&self) -> Option<ValidToken> {
|
||||
self.registration_token.clone().map(|token| ValidToken {
|
||||
token,
|
||||
source: ValidTokenSource::ConfigFile,
|
||||
})
|
||||
self.registration_token
|
||||
.clone()
|
||||
.map(|token| ValidToken { token, source: ValidTokenSource::Config })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ mod data;
|
|||
|
||||
use std::{future::ready, pin::Pin, sync::Arc};
|
||||
|
||||
use conduwuit::{Err, Result, utils};
|
||||
use conduwuit::{Err, Result, err, utils};
|
||||
use data::Data;
|
||||
pub use data::{DatabaseTokenInfo, TokenExpires};
|
||||
use futures::{
|
||||
|
|
@ -18,6 +18,9 @@ const RANDOM_TOKEN_LENGTH: usize = 16;
|
|||
pub struct Service {
|
||||
db: Data,
|
||||
services: Services,
|
||||
/// The registration tokens which were read from the registration token
|
||||
/// file, if one is configured.
|
||||
registration_tokens_from_file: Vec<String>,
|
||||
}
|
||||
|
||||
struct Services {
|
||||
|
|
@ -45,34 +48,54 @@ impl PartialEq<str> for ValidToken {
|
|||
/// The source of a valid database token.
|
||||
#[derive(Debug)]
|
||||
pub enum ValidTokenSource {
|
||||
/// The static token set in the homeserver's config file, which is
|
||||
/// always valid.
|
||||
ConfigFile,
|
||||
/// The static token set in the homeserver's config file.
|
||||
Config,
|
||||
/// A database token which has been checked to be valid.
|
||||
Database(DatabaseTokenInfo),
|
||||
/// The single-use token which may be used to create the homeserver's first
|
||||
/// account.
|
||||
FirstAccount,
|
||||
/// A registration token read from the registration token file set in the
|
||||
/// config.
|
||||
TokenFile,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ValidTokenSource {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
| Self::ConfigFile => write!(f, "Token defined in config."),
|
||||
| Self::Config => write!(f, "Static token set in the server configuration."),
|
||||
| Self::Database(info) => info.fmt(f),
|
||||
| Self::FirstAccount => write!(f, "Initial setup token."),
|
||||
| Self::TokenFile => write!(f, "Static token set in the registration token file."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::Service for Service {
|
||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
||||
let registration_tokens_from_file = args.server.config.registration_token_file
|
||||
.clone()
|
||||
// If the token file option was set, read the path it points to
|
||||
.map(std::fs::read_to_string)
|
||||
.transpose()
|
||||
.map_err(|err| err!("Failed to read registration token file: {err}"))
|
||||
.map(|tokens| {
|
||||
if let Some(tokens) = tokens {
|
||||
// If the token file option was set, return the file's lines as tokens
|
||||
tokens.lines().map(ToOwned::to_owned).collect()
|
||||
} else {
|
||||
// Otherwise, if the option wasn't set, return no tokens
|
||||
vec![]
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Arc::new(Self {
|
||||
db: Data::new(args.db),
|
||||
services: Services {
|
||||
config: args.depend::<config::Service>("config"),
|
||||
firstrun: args.depend::<firstrun::Service>("firstrun"),
|
||||
},
|
||||
registration_tokens_from_file,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -97,12 +120,23 @@ impl Service {
|
|||
(token, info)
|
||||
}
|
||||
|
||||
/// Get all the "special" registration tokens that aren't defined in the
|
||||
/// Get all the static registration tokens that aren't defined in the
|
||||
/// database.
|
||||
fn iterate_static_tokens(&self) -> impl Iterator<Item = ValidToken> {
|
||||
// This does not include the first-account token, because it's special:
|
||||
// no other registration tokens are valid when it is set.
|
||||
self.services.config.get_config_file_token().into_iter()
|
||||
// This does not include the first-account token, because it has special
|
||||
// behavior: no other registration tokens are valid when it is set.
|
||||
self.services
|
||||
.config
|
||||
.get_config_file_token()
|
||||
.into_iter()
|
||||
.chain(
|
||||
self.registration_tokens_from_file
|
||||
.iter()
|
||||
.map(|token_string| ValidToken {
|
||||
token: token_string.clone(),
|
||||
source: ValidTokenSource::TokenFile,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Validate a registration token.
|
||||
|
|
@ -158,7 +192,7 @@ impl Service {
|
|||
/// revoked.
|
||||
pub fn revoke_token(&self, ValidToken { token, source }: ValidToken) -> Result {
|
||||
match source {
|
||||
| ValidTokenSource::ConfigFile => {
|
||||
| ValidTokenSource::Config => {
|
||||
Err!(
|
||||
"The token set in the config file cannot be revoked. Edit the config file \
|
||||
to change it."
|
||||
|
|
@ -171,6 +205,12 @@ impl Service {
|
|||
| ValidTokenSource::FirstAccount => {
|
||||
Err!("The initial setup token cannot be revoked.")
|
||||
},
|
||||
| ValidTokenSource::TokenFile => {
|
||||
Err!(
|
||||
"Tokens set in the registration token file cannot be revoked. Edit the \
|
||||
registration token file and restart Continuwuity to change them."
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue