de-global services() from api

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-07-16 08:05:25 +00:00
parent 463f1a1287
commit 8b6018d77d
61 changed files with 1485 additions and 1320 deletions

View file

@ -1,5 +1,6 @@
use std::fmt::Write;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduit::debug_info;
use register::RegistrationKind;
@ -22,7 +23,6 @@ use tracing::{error, info, warn};
use super::{join_room_by_id_helper, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
use crate::{
service::user_is_local,
services,
utils::{self},
Error, Result, Ruma,
};
@ -42,20 +42,21 @@ const RANDOM_USER_ID_LENGTH: usize = 10;
/// invalid when trying to register
#[tracing::instrument(skip_all, fields(%client), name = "register_available")]
pub(crate) async fn get_register_available_route(
InsecureClientIp(client): InsecureClientIp, body: Ruma<get_username_availability::v3::Request>,
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<get_username_availability::v3::Request>,
) -> Result<get_username_availability::v3::Response> {
// Validate user id
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services().globals.server_name())
let user_id = UserId::parse_with_server_name(body.username.to_lowercase(), services.globals.server_name())
.ok()
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
// Check if username is creative enough
if services().users.exists(&user_id)? {
if services.users.exists(&user_id)? {
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
}
if services()
if services
.globals
.forbidden_usernames()
.is_match(user_id.localpart())
@ -91,9 +92,9 @@ pub(crate) async fn get_register_available_route(
#[allow(clippy::doc_markdown)]
#[tracing::instrument(skip_all, fields(%client), name = "register")]
pub(crate) async fn register_route(
InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp, body: Ruma<register::v3::Request>,
) -> Result<register::v3::Response> {
if !services().globals.allow_registration() && body.appservice_info.is_none() {
if !services.globals.allow_registration() && body.appservice_info.is_none() {
info!(
"Registration disabled and request not from known appservice, rejecting registration attempt for username \
{:?}",
@ -105,8 +106,8 @@ pub(crate) async fn register_route(
let is_guest = body.kind == RegistrationKind::Guest;
if is_guest
&& (!services().globals.allow_guest_registration()
|| (services().globals.allow_registration() && services().globals.config.registration_token.is_some()))
&& (!services.globals.allow_guest_registration()
|| (services.globals.allow_registration() && services.globals.config.registration_token.is_some()))
{
info!(
"Guest registration disabled / registration enabled with token configured, rejecting guest registration \
@ -121,7 +122,7 @@ pub(crate) async fn register_route(
// forbid guests from registering if there is not a real admin user yet. give
// generic user error.
if is_guest && services().users.count()? < 2 {
if is_guest && services.users.count()? < 2 {
warn!(
"Guest account attempted to register before a real admin user has been registered, rejecting \
registration. Guest's initial device name: {:?}",
@ -133,16 +134,16 @@ pub(crate) async fn register_route(
let user_id = match (&body.username, is_guest) {
(Some(username), false) => {
let proposed_user_id =
UserId::parse_with_server_name(username.to_lowercase(), services().globals.server_name())
UserId::parse_with_server_name(username.to_lowercase(), services.globals.server_name())
.ok()
.filter(|user_id| !user_id.is_historical() && user_is_local(user_id))
.ok_or(Error::BadRequest(ErrorKind::InvalidUsername, "Username is invalid."))?;
if services().users.exists(&proposed_user_id)? {
if services.users.exists(&proposed_user_id)? {
return Err(Error::BadRequest(ErrorKind::UserInUse, "Desired user ID is already taken."));
}
if services()
if services
.globals
.forbidden_usernames()
.is_match(proposed_user_id.localpart())
@ -155,10 +156,10 @@ pub(crate) async fn register_route(
_ => loop {
let proposed_user_id = UserId::parse_with_server_name(
utils::random_string(RANDOM_USER_ID_LENGTH).to_lowercase(),
services().globals.server_name(),
services.globals.server_name(),
)
.unwrap();
if !services().users.exists(&proposed_user_id)? {
if !services.users.exists(&proposed_user_id)? {
break proposed_user_id;
}
},
@ -172,13 +173,13 @@ pub(crate) async fn register_route(
} else {
return Err(Error::BadRequest(ErrorKind::MissingToken, "Missing appservice token."));
}
} else if services().appservice.is_exclusive_user_id(&user_id).await {
} else if services.appservice.is_exclusive_user_id(&user_id).await {
return Err(Error::BadRequest(ErrorKind::Exclusive, "User ID reserved by appservice."));
}
// UIAA
let mut uiaainfo;
let skip_auth = if services().globals.config.registration_token.is_some() {
let skip_auth = if services.globals.config.registration_token.is_some() {
// Registration token required
uiaainfo = UiaaInfo {
flows: vec![AuthFlow {
@ -206,8 +207,8 @@ pub(crate) async fn register_route(
if !skip_auth {
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services().uiaa.try_auth(
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
let (worked, uiaainfo) = services.uiaa.try_auth(
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
"".into(),
auth,
&uiaainfo,
@ -218,8 +219,8 @@ pub(crate) async fn register_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services().uiaa.create(
&UserId::parse_with_server_name("", services().globals.server_name()).expect("we know this is valid"),
services.uiaa.create(
&UserId::parse_with_server_name("", services.globals.server_name()).expect("we know this is valid"),
"".into(),
&uiaainfo,
&json,
@ -237,25 +238,25 @@ pub(crate) async fn register_route(
};
// Create user
services().users.create(&user_id, password)?;
services.users.create(&user_id, password)?;
// Default to pretty displayname
let mut displayname = user_id.localpart().to_owned();
// If `new_user_displayname_suffix` is set, registration will push whatever
// content is set to the user's display name with a space before it
if !services().globals.new_user_displayname_suffix().is_empty() {
write!(displayname, " {}", services().globals.config.new_user_displayname_suffix)
if !services.globals.new_user_displayname_suffix().is_empty() {
write!(displayname, " {}", services.globals.config.new_user_displayname_suffix)
.expect("should be able to write to string buffer");
}
services()
services
.users
.set_displayname(&user_id, Some(displayname.clone()))
.await?;
// Initial account data
services().account_data.update(
services.account_data.update(
None,
&user_id,
GlobalAccountDataEventType::PushRules.to_string().into(),
@ -290,7 +291,7 @@ pub(crate) async fn register_route(
let token = utils::random_string(TOKEN_LENGTH);
// Create device for this account
services()
services
.users
.create_device(&user_id, &device_id, &token, body.initial_device_display_name.clone())?;
@ -299,7 +300,7 @@ pub(crate) async fn register_route(
// log in conduit admin channel if a non-guest user registered
if body.appservice_info.is_none() && !is_guest {
info!("New user \"{user_id}\" registered on this server.");
services()
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"New user \"{user_id}\" registered on this server from IP {client}."
@ -308,7 +309,7 @@ pub(crate) async fn register_route(
}
// log in conduit admin channel if a guest registered
if body.appservice_info.is_none() && is_guest && services().globals.log_guest_registrations() {
if body.appservice_info.is_none() && is_guest && services.globals.log_guest_registrations() {
info!("New guest user \"{user_id}\" registered on this server.");
if let Some(device_display_name) = &body.initial_device_display_name {
@ -317,7 +318,7 @@ pub(crate) async fn register_route(
.as_ref()
.is_some_and(|device_display_name| !device_display_name.is_empty())
{
services()
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with device display name `{device_display_name}` registered on this \
@ -325,7 +326,7 @@ pub(crate) async fn register_route(
)))
.await;
} else {
services()
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server from IP \
@ -334,7 +335,7 @@ pub(crate) async fn register_route(
.await;
}
} else {
services()
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"Guest user \"{user_id}\" with no device display name registered on this server from IP {client}.",
@ -347,12 +348,7 @@ pub(crate) async fn register_route(
// users Note: the server user, @conduit:servername, is generated first
if !is_guest {
if let Some(admin_room) = service::admin::Service::get_admin_room()? {
if services()
.rooms
.state_cache
.room_joined_count(&admin_room)?
== Some(1)
{
if services.rooms.state_cache.room_joined_count(&admin_room)? == Some(1) {
service::admin::make_user_admin(&user_id, displayname).await?;
warn!("Granting {user_id} admin privileges as the first user");
@ -361,14 +357,14 @@ pub(crate) async fn register_route(
}
if body.appservice_info.is_none()
&& !services().globals.config.auto_join_rooms.is_empty()
&& (services().globals.allow_guests_auto_join_rooms() || !is_guest)
&& !services.globals.config.auto_join_rooms.is_empty()
&& (services.globals.allow_guests_auto_join_rooms() || !is_guest)
{
for room in &services().globals.config.auto_join_rooms {
if !services()
for room in &services.globals.config.auto_join_rooms {
if !services
.rooms
.state_cache
.server_in_room(services().globals.server_name(), room)?
.server_in_room(services.globals.server_name(), room)?
{
warn!("Skipping room {room} to automatically join as we have never joined before.");
continue;
@ -376,10 +372,11 @@ pub(crate) async fn register_route(
if let Some(room_id_server_name) = room.server_name() {
if let Err(e) = join_room_by_id_helper(
services,
&user_id,
room,
Some("Automatically joining this room upon registration".to_owned()),
&[room_id_server_name.to_owned(), services().globals.server_name().to_owned()],
&[room_id_server_name.to_owned(), services.globals.server_name().to_owned()],
None,
)
.await
@ -421,7 +418,8 @@ pub(crate) async fn register_route(
/// - Triggers device list updates
#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
pub(crate) async fn change_password_route(
InsecureClientIp(client): InsecureClientIp, body: Ruma<change_password::v3::Request>,
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<change_password::v3::Request>,
) -> Result<change_password::v3::Response> {
// Authentication for this endpoint was made optional, but we need
// authentication currently
@ -442,7 +440,7 @@ pub(crate) async fn change_password_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services()
let (worked, uiaainfo) = services
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@ -451,7 +449,7 @@ pub(crate) async fn change_password_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services()
services
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@ -459,24 +457,24 @@ pub(crate) async fn change_password_route(
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
}
services()
services
.users
.set_password(sender_user, Some(&body.new_password))?;
if body.logout_devices {
// Logout all devices except the current one
for id in services()
for id in services
.users
.all_device_ids(sender_user)
.filter_map(Result::ok)
.filter(|id| id != sender_device)
{
services().users.remove_device(sender_user, &id)?;
services.users.remove_device(sender_user, &id)?;
}
}
info!("User {sender_user} changed their password.");
services()
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {sender_user} changed their password."
@ -491,14 +489,16 @@ pub(crate) async fn change_password_route(
/// Get `user_id` of the sender user.
///
/// Note: Also works for Application Services
pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoami::v3::Response> {
pub(crate) async fn whoami_route(
State(services): State<crate::State>, body: Ruma<whoami::v3::Request>,
) -> Result<whoami::v3::Response> {
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
let device_id = body.sender_device.clone();
Ok(whoami::v3::Response {
user_id: sender_user.clone(),
device_id,
is_guest: services().users.is_deactivated(sender_user)? && body.appservice_info.is_none(),
is_guest: services.users.is_deactivated(sender_user)? && body.appservice_info.is_none(),
})
}
@ -515,7 +515,8 @@ pub(crate) async fn whoami_route(body: Ruma<whoami::v3::Request>) -> Result<whoa
/// - Removes ability to log in again
#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
pub(crate) async fn deactivate_route(
InsecureClientIp(client): InsecureClientIp, body: Ruma<deactivate::v3::Request>,
State(services): State<crate::State>, InsecureClientIp(client): InsecureClientIp,
body: Ruma<deactivate::v3::Request>,
) -> Result<deactivate::v3::Response> {
// Authentication for this endpoint was made optional, but we need
// authentication currently
@ -536,7 +537,7 @@ pub(crate) async fn deactivate_route(
};
if let Some(auth) = &body.auth {
let (worked, uiaainfo) = services()
let (worked, uiaainfo) = services
.uiaa
.try_auth(sender_user, sender_device, auth, &uiaainfo)?;
if !worked {
@ -545,7 +546,7 @@ pub(crate) async fn deactivate_route(
// Success!
} else if let Some(json) = body.json_body {
uiaainfo.session = Some(utils::random_string(SESSION_ID_LENGTH));
services()
services
.uiaa
.create(sender_user, sender_device, &uiaainfo, &json)?;
return Err(Error::Uiaa(uiaainfo));
@ -554,23 +555,23 @@ pub(crate) async fn deactivate_route(
}
// Remove devices and mark account as deactivated
services().users.deactivate_account(sender_user)?;
services.users.deactivate_account(sender_user)?;
// Remove profile pictures and display name
let all_joined_rooms: Vec<OwnedRoomId> = services()
let all_joined_rooms: Vec<OwnedRoomId> = services
.rooms
.state_cache
.rooms_joined(sender_user)
.filter_map(Result::ok)
.collect();
super::update_displayname(sender_user.clone(), None, all_joined_rooms.clone()).await?;
super::update_avatar_url(sender_user.clone(), None, None, all_joined_rooms).await?;
super::update_displayname(services, sender_user.clone(), None, all_joined_rooms.clone()).await?;
super::update_avatar_url(services, sender_user.clone(), None, None, all_joined_rooms).await?;
// Make the user leave all rooms before deactivation
super::leave_all_rooms(sender_user).await;
super::leave_all_rooms(services, sender_user).await;
info!("User {sender_user} deactivated their account.");
services()
services
.admin
.send_message(RoomMessageEventContent::notice_plain(format!(
"User {sender_user} deactivated their account."
@ -632,9 +633,9 @@ pub(crate) async fn request_3pid_management_token_via_msisdn_route(
/// Currently does not have any ratelimiting, and this isn't very practical as
/// there is only one registration token allowed.
pub(crate) async fn check_registration_token_validity(
body: Ruma<check_registration_token_validity::v1::Request>,
State(services): State<crate::State>, body: Ruma<check_registration_token_validity::v1::Request>,
) -> Result<check_registration_token_validity::v1::Response> {
let Some(reg_token) = services().globals.config.registration_token.clone() else {
let Some(reg_token) = services.globals.config.registration_token.clone() else {
return Err(Error::BadRequest(
ErrorKind::forbidden(),
"Server does not allow token registration.",