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
|
#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
|
# A static registration token that new users will have to provide when
|
||||||
# creating an account. If unset and `allow_registration` is true,
|
# creating an account. This token does not supersede tokens from other sources, such as the `!admin token`
|
||||||
# you must set
|
# command or the `registration_token_file` configuration option.
|
||||||
# `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.
|
|
||||||
#
|
#
|
||||||
# example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
# example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||||
#
|
#
|
||||||
#registration_token =
|
#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
|
# The public site key for reCaptcha. If this is provided, reCaptcha
|
||||||
# becomes required during registration. If both captcha *and*
|
# becomes required during registration. If both captcha *and*
|
||||||
# registration token are enabled, both will be required during
|
# registration token are enabled, both will be required during
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ pub fn check(config: &Config) -> Result {
|
||||||
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()
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Open registration is enabled via setting \
|
"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,
|
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
|
/// A static registration token that new users will have to provide when
|
||||||
/// creating an account. If unset and `allow_registration` is true,
|
/// creating an account. This token does not supersede tokens from other
|
||||||
/// you must set
|
/// sources, such as the `!admin token` command or the
|
||||||
/// `yes_i_am_very_very_sure_i_want_an_open_registration_server_prone_to_abuse`
|
/// `registration_token_file` configuration option.
|
||||||
/// 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.
|
|
||||||
///
|
///
|
||||||
/// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
/// example: "o&^uCtes4HPf0Vu@F20jQeeWE7"
|
||||||
///
|
///
|
||||||
/// display: sensitive
|
/// display: sensitive
|
||||||
pub registration_token: Option<String>,
|
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
|
/// The public site key for reCaptcha. If this is provided, reCaptcha
|
||||||
/// becomes required during registration. If both captcha *and*
|
/// becomes required during registration. If both captcha *and*
|
||||||
/// registration token are enabled, both will be required during
|
/// 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.
|
/// Get the registration token set in the config file, if it exists.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_config_file_token(&self) -> Option<ValidToken> {
|
pub fn get_config_file_token(&self) -> Option<ValidToken> {
|
||||||
self.registration_token.clone().map(|token| ValidToken {
|
self.registration_token
|
||||||
token,
|
.clone()
|
||||||
source: ValidTokenSource::ConfigFile,
|
.map(|token| ValidToken { token, source: ValidTokenSource::Config })
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ mod data;
|
||||||
|
|
||||||
use std::{future::ready, pin::Pin, sync::Arc};
|
use std::{future::ready, pin::Pin, sync::Arc};
|
||||||
|
|
||||||
use conduwuit::{Err, Result, utils};
|
use conduwuit::{Err, Result, err, utils};
|
||||||
use data::Data;
|
use data::Data;
|
||||||
pub use data::{DatabaseTokenInfo, TokenExpires};
|
pub use data::{DatabaseTokenInfo, TokenExpires};
|
||||||
use futures::{
|
use futures::{
|
||||||
|
|
@ -18,6 +18,9 @@ const RANDOM_TOKEN_LENGTH: usize = 16;
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
db: Data,
|
db: Data,
|
||||||
services: Services,
|
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 {
|
struct Services {
|
||||||
|
|
@ -45,34 +48,54 @@ impl PartialEq<str> for ValidToken {
|
||||||
/// The source of a valid database token.
|
/// The source of a valid database token.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ValidTokenSource {
|
pub enum ValidTokenSource {
|
||||||
/// The static token set in the homeserver's config file, which is
|
/// The static token set in the homeserver's config file.
|
||||||
/// always valid.
|
Config,
|
||||||
ConfigFile,
|
|
||||||
/// A database token which has been checked to be valid.
|
/// A database token which has been checked to be valid.
|
||||||
Database(DatabaseTokenInfo),
|
Database(DatabaseTokenInfo),
|
||||||
/// The single-use token which may be used to create the homeserver's first
|
/// The single-use token which may be used to create the homeserver's first
|
||||||
/// account.
|
/// account.
|
||||||
FirstAccount,
|
FirstAccount,
|
||||||
|
/// A registration token read from the registration token file set in the
|
||||||
|
/// config.
|
||||||
|
TokenFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ValidTokenSource {
|
impl std::fmt::Display for ValidTokenSource {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
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::Database(info) => info.fmt(f),
|
||||||
| Self::FirstAccount => write!(f, "Initial setup token."),
|
| Self::FirstAccount => write!(f, "Initial setup token."),
|
||||||
|
| Self::TokenFile => write!(f, "Static token set in the registration token file."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Service for Service {
|
impl crate::Service for Service {
|
||||||
fn build(args: crate::Args<'_>) -> Result<Arc<Self>> {
|
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 {
|
Ok(Arc::new(Self {
|
||||||
db: Data::new(args.db),
|
db: Data::new(args.db),
|
||||||
services: Services {
|
services: Services {
|
||||||
config: args.depend::<config::Service>("config"),
|
config: args.depend::<config::Service>("config"),
|
||||||
firstrun: args.depend::<firstrun::Service>("firstrun"),
|
firstrun: args.depend::<firstrun::Service>("firstrun"),
|
||||||
},
|
},
|
||||||
|
registration_tokens_from_file,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,12 +120,23 @@ impl Service {
|
||||||
(token, info)
|
(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.
|
/// database.
|
||||||
fn iterate_static_tokens(&self) -> impl Iterator<Item = ValidToken> {
|
fn iterate_static_tokens(&self) -> impl Iterator<Item = ValidToken> {
|
||||||
// This does not include the first-account token, because it's special:
|
// This does not include the first-account token, because it has special
|
||||||
// no other registration tokens are valid when it is set.
|
// behavior: no other registration tokens are valid when it is set.
|
||||||
self.services.config.get_config_file_token().into_iter()
|
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.
|
/// Validate a registration token.
|
||||||
|
|
@ -158,7 +192,7 @@ impl Service {
|
||||||
/// revoked.
|
/// revoked.
|
||||||
pub fn revoke_token(&self, ValidToken { token, source }: ValidToken) -> Result {
|
pub fn revoke_token(&self, ValidToken { token, source }: ValidToken) -> Result {
|
||||||
match source {
|
match source {
|
||||||
| ValidTokenSource::ConfigFile => {
|
| ValidTokenSource::Config => {
|
||||||
Err!(
|
Err!(
|
||||||
"The token set in the config file cannot be revoked. Edit the config file \
|
"The token set in the config file cannot be revoked. Edit the config file \
|
||||||
to change it."
|
to change it."
|
||||||
|
|
@ -171,6 +205,12 @@ impl Service {
|
||||||
| ValidTokenSource::FirstAccount => {
|
| ValidTokenSource::FirstAccount => {
|
||||||
Err!("The initial setup token cannot be revoked.")
|
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
Add a link
Reference in a new issue