mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
feat: Add user creation endpoint
This commit is contained in:
@@ -99,7 +99,7 @@ pub(crate) async fn register_route(
|
|||||||
.users
|
.users
|
||||||
.create_local_account(&user_id, password, identity.email)
|
.create_local_account(&user_id, password, identity.email)
|
||||||
.await;
|
.await;
|
||||||
|
services.users.join_auto_join_rooms(&user_id).await;
|
||||||
user_id
|
user_id
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,119 @@
|
|||||||
|
use axum::extract::State;
|
||||||
|
use conduwuit::{
|
||||||
|
Err, err, error, info,
|
||||||
|
utils::{IterStream, stream::BroadbandExt},
|
||||||
|
warn,
|
||||||
|
};
|
||||||
|
use futures::{FutureExt, StreamExt};
|
||||||
|
use ruma::UserId;
|
||||||
|
use ruminuwuity::admin::continuwuity::users;
|
||||||
|
use service::users::HashedPassword;
|
||||||
|
|
||||||
|
use crate::router::Ruma;
|
||||||
|
|
||||||
|
/// # `POST /_continuwuity/admin/v1/users/create`
|
||||||
|
///
|
||||||
|
/// Creates a new user.
|
||||||
|
pub(crate) async fn create_user_route(
|
||||||
|
State(services): State<crate::State>,
|
||||||
|
body: Ruma<users::create::v1::Request>,
|
||||||
|
) -> conduwuit::Result<users::create::v1::Response> {
|
||||||
|
let sender_user = body.sender_user();
|
||||||
|
|
||||||
|
if !services.users.is_admin(sender_user).await {
|
||||||
|
return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
|
||||||
|
}
|
||||||
|
let user_id =
|
||||||
|
&UserId::parse_with_server_name(&body.localpart, services.globals.server_name())?;
|
||||||
|
if services.users.is_active_local(user_id).await {
|
||||||
|
return Err!(Conflict("A user with this username already exists"));
|
||||||
|
}
|
||||||
|
|
||||||
|
services
|
||||||
|
.users
|
||||||
|
.create_local_account(
|
||||||
|
user_id,
|
||||||
|
HashedPassword::new(&body.password)?,
|
||||||
|
body.email
|
||||||
|
.clone()
|
||||||
|
.map(lettre::Address::try_from)
|
||||||
|
.transpose()
|
||||||
|
.map_err(|e| err!(Request(BadJson("Invalid email address: {e}"))))?,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
if body.suspended {
|
||||||
|
services.users.suspend_account(user_id, sender_user).await;
|
||||||
|
}
|
||||||
|
if body.locked {
|
||||||
|
services.users.lock_account(user_id, sender_user).await;
|
||||||
|
}
|
||||||
|
if body.login_disabled {
|
||||||
|
services.users.disable_login(user_id);
|
||||||
|
}
|
||||||
|
if let Some(ref value) = body.display_name {
|
||||||
|
services.users.set_profile_key(
|
||||||
|
user_id,
|
||||||
|
"displayname",
|
||||||
|
Some(serde_json::to_value(value)?),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(ref value) = body.avatar_url {
|
||||||
|
services
|
||||||
|
.users
|
||||||
|
.set_profile_key(user_id, "avatar_url", Some(serde_json::to_value(value)?));
|
||||||
|
}
|
||||||
|
if body.admin {
|
||||||
|
services
|
||||||
|
.admin
|
||||||
|
.make_user_admin(user_id)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| error!("failed to make new user {user_id} an admin: {e}"))
|
||||||
|
.ok();
|
||||||
|
}
|
||||||
|
if !body.skip_auto_join {
|
||||||
|
services.users.join_auto_join_rooms(user_id).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.auto_join_rooms
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.stream()
|
||||||
|
.broad_filter_map(|room| async move {
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.alias
|
||||||
|
.resolve_with_servers(&room, None)
|
||||||
|
.await
|
||||||
|
.inspect_err(|e| {
|
||||||
|
warn!(
|
||||||
|
"Failed to resolve room alias to room ID when attempting to auto join \
|
||||||
|
{room}: {e}"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.for_each_concurrent(None, |(room_id, servers)| async move {
|
||||||
|
match services
|
||||||
|
.rooms
|
||||||
|
.membership
|
||||||
|
.join_room(
|
||||||
|
user_id,
|
||||||
|
&room_id,
|
||||||
|
Some("Automatically joining this room upon registration".to_owned()),
|
||||||
|
servers.as_ref(),
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
| Err(e) => {
|
||||||
|
warn!("Failed to automatically join {user_id} to {room_id}: {e}");
|
||||||
|
},
|
||||||
|
| _ => {
|
||||||
|
info!("Automatically joined room {user_id} to {room_id}");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(users::create::v1::Response::new(user_id.to_owned()))
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
mod create;
|
||||||
mod list;
|
mod list;
|
||||||
|
|
||||||
pub(crate) use list::list_users_route;
|
pub(crate) use create::*;
|
||||||
|
pub(crate) use list::*;
|
||||||
|
|||||||
@@ -283,6 +283,7 @@ pub fn build(router: Router<State>, state: State) -> Router<State> {
|
|||||||
{
|
{
|
||||||
router = router
|
router = router
|
||||||
.ruma_route(&admin_api::users::list_users_route)
|
.ruma_route(&admin_api::users::list_users_route)
|
||||||
|
.ruma_route(&admin_api::users::create_user_route)
|
||||||
.ruma_route(&admin_api::rooms::ban_room)
|
.ruma_route(&admin_api::rooms::ban_room)
|
||||||
.ruma_route(&admin_api::rooms::legacy_list_rooms_route)
|
.ruma_route(&admin_api::rooms::legacy_list_rooms_route)
|
||||||
.ruma_route(&admin_api::rooms::list_rooms_route);
|
.ruma_route(&admin_api::rooms::list_rooms_route);
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ pub mod v1 {
|
|||||||
/// The user's desired password. Cannot be blank.
|
/// The user's desired password. Cannot be blank.
|
||||||
pub password: String,
|
pub password: String,
|
||||||
|
|
||||||
|
/// The user's email address, if any.
|
||||||
|
#[serde(default, skip_serializing_if = "ruma::serde::is_default")]
|
||||||
|
pub email: Option<String>,
|
||||||
|
|
||||||
/// The display name to set upon creation.
|
/// The display name to set upon creation.
|
||||||
#[serde(default, skip_serializing_if = "ruma::serde::is_default")]
|
#[serde(default, skip_serializing_if = "ruma::serde::is_default")]
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
@@ -80,6 +84,7 @@ pub mod v1 {
|
|||||||
Self {
|
Self {
|
||||||
localpart,
|
localpart,
|
||||||
password,
|
password,
|
||||||
|
email: None,
|
||||||
display_name: None,
|
display_name: None,
|
||||||
avatar_url: None,
|
avatar_url: None,
|
||||||
suspended: false,
|
suspended: false,
|
||||||
|
|||||||
@@ -229,6 +229,9 @@ impl Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new account for a local human or bot user.
|
/// Create a new account for a local human or bot user.
|
||||||
|
///
|
||||||
|
/// Does not automatically join the user to auto join rooms. Use
|
||||||
|
/// `join_auto_join_rooms` for that.
|
||||||
pub async fn create_local_account(
|
pub async fn create_local_account(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
@@ -303,8 +306,11 @@ impl Service {
|
|||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
info!("Created new user account for {user_id}");
|
||||||
|
}
|
||||||
|
|
||||||
// Autojoin the user to the configured autojoin rooms
|
/// Autojoin the user to the configured autojoin rooms
|
||||||
|
pub async fn join_auto_join_rooms(&self, user_id: &UserId) {
|
||||||
for room in &self.services.config.auto_join_rooms {
|
for room in &self.services.config.auto_join_rooms {
|
||||||
let Ok(room_id) = self.services.alias.resolve(room).await else {
|
let Ok(room_id) = self.services.alias.resolve(room).await else {
|
||||||
error!(
|
error!(
|
||||||
@@ -320,9 +326,7 @@ impl Service {
|
|||||||
.server_in_room(self.services.globals.server_name(), &room_id)
|
.server_in_room(self.services.globals.server_name(), &room_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
warn!(
|
warn!("Skipping auto-room {room} as we have never joined before.");
|
||||||
"Skipping room {room} to automatically join as we have never joined before."
|
|
||||||
);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,8 +358,6 @@ impl Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Created new user account for {user_id}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn determine_registration_user_id(
|
pub async fn determine_registration_user_id(
|
||||||
|
|||||||
@@ -519,6 +519,7 @@ async fn complete_registration(
|
|||||||
.registration_tokens
|
.registration_tokens
|
||||||
.mark_token_as_used(registration_token);
|
.mark_token_as_used(registration_token);
|
||||||
}
|
}
|
||||||
|
services.users.join_auto_join_rooms(&user_id).await;
|
||||||
|
|
||||||
let user_session = UserSession { user_id, last_login: SystemTime::now() };
|
let user_session = UserSession { user_id, last_login: SystemTime::now() };
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user