mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-13 12:16:54 +00:00
feat: Add admin command to issue access tokens
This commit is contained in:
1 parent
4cf743883a
commit
9a3496ae70
12 files changed
+79
-58
No files matched your search
@@ -3,7 +3,7 @@ use futures::StreamExt;
|
||||
use service::registration_tokens::TokenExpires;
|
||||
|
||||
impl crate::Context<'_> {
|
||||
pub(super) async fn issue_token(&self, expires: super::TokenExpires) -> Result {
|
||||
pub(super) async fn issue_registration_token(&self, expires: super::TokenExpires) -> Result {
|
||||
let expires = {
|
||||
if expires.immortal {
|
||||
None
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::admin_command_dispatch;
|
||||
pub enum TokenCommand {
|
||||
/// Issue a new registration token
|
||||
#[clap(name = "issue")]
|
||||
IssueToken {
|
||||
IssueRegistrationToken {
|
||||
/// When this token will expire.
|
||||
#[command(flatten)]
|
||||
expires: TokenExpires,
|
||||
|
||||
@@ -20,7 +20,7 @@ use ruma::{
|
||||
tag::{TagEvent, TagEventContent, TagInfo},
|
||||
},
|
||||
};
|
||||
use service::users::{AccountStatus, HashedPassword};
|
||||
use service::users::{AccountStatus, DeviceToken, HashedPassword};
|
||||
|
||||
use crate::{
|
||||
get_room_info,
|
||||
@@ -66,6 +66,29 @@ impl crate::Context<'_> {
|
||||
.await
|
||||
}
|
||||
|
||||
pub(super) async fn issue_access_token(&self, username: String, password: String) -> Result {
|
||||
let user_id = parse_active_local_user_id(self.services, &username).await?;
|
||||
|
||||
let user_id = self
|
||||
.services
|
||||
.users
|
||||
.check_password(&user_id, &password)
|
||||
.await?;
|
||||
let token = DeviceToken::new_random();
|
||||
|
||||
let device_id = self
|
||||
.services
|
||||
.users
|
||||
.create_device(&user_id, None, Some(token.clone()), None, None)
|
||||
.await?;
|
||||
|
||||
self.write_str(&format!(
|
||||
"Created device `{device_id}` with access token `{}` for {user_id}",
|
||||
token.into_token()
|
||||
))
|
||||
.await
|
||||
}
|
||||
|
||||
pub(super) async fn deactivate(&self, no_leave_rooms: bool, user_id: String) -> Result {
|
||||
// Validate user id
|
||||
let user_id = parse_local_user_id(self.services, &user_id)?;
|
||||
|
||||
@@ -18,6 +18,15 @@ pub enum UserCommand {
|
||||
password: Option<String>,
|
||||
},
|
||||
|
||||
/// Issue an access token for a user. This command will not work on
|
||||
/// shadow users, such as appservice puppets or accounts imported from
|
||||
/// an identity provider.
|
||||
#[clap(name = "issue-token")]
|
||||
IssueAccessToken {
|
||||
username: String,
|
||||
password: String,
|
||||
},
|
||||
|
||||
/// Reset user password
|
||||
ResetPassword {
|
||||
/// Log out existing sessions
|
||||
|
||||
@@ -25,7 +25,6 @@ use ruma::{
|
||||
};
|
||||
use service::{mailer::messages, uiaa::UiaaInitiator, users::HashedPassword};
|
||||
|
||||
use super::DEVICE_ID_LENGTH;
|
||||
use crate::{Ruma, router::ClientIdentity};
|
||||
|
||||
pub(crate) mod register;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, debug_info, info,
|
||||
utils::{self},
|
||||
};
|
||||
use conduwuit::{Err, Result, debug_info, info};
|
||||
use conduwuit_service::Services;
|
||||
use futures::StreamExt;
|
||||
use lettre::{Address, message::Mailbox};
|
||||
@@ -24,7 +21,6 @@ use service::{
|
||||
users::{DeviceToken, HashedPassword},
|
||||
};
|
||||
|
||||
use super::DEVICE_ID_LENGTH;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
|
||||
/// # `POST /_matrix/client/v3/register`
|
||||
@@ -114,26 +110,21 @@ pub(crate) async fn register_route(
|
||||
)));
|
||||
}
|
||||
|
||||
// Generate new device id if the user didn't specify one
|
||||
let device_id = body
|
||||
.device_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
||||
|
||||
// Generate new token for the device
|
||||
let new_token = DeviceToken::new_random();
|
||||
|
||||
// Create device for this account
|
||||
services
|
||||
let device_id = services
|
||||
.users
|
||||
.create_device(
|
||||
&user_id,
|
||||
&device_id,
|
||||
body.device_id.clone(),
|
||||
Some(new_token.clone()),
|
||||
body.initial_device_display_name.clone(),
|
||||
Some(client.to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
(Some(new_token), Some(device_id))
|
||||
} else {
|
||||
// Don't create a device for inhibited logins
|
||||
|
||||
@@ -88,7 +88,7 @@ pub(crate) async fn update_device_route(
|
||||
.users
|
||||
.create_device(
|
||||
sender_user,
|
||||
&body.device_id,
|
||||
Some(body.device_id.clone()),
|
||||
None,
|
||||
body.display_name.clone(),
|
||||
Some(client.to_string()),
|
||||
|
||||
@@ -90,8 +90,5 @@ pub(super) use user_directory::*;
|
||||
pub(super) use voip::*;
|
||||
pub(super) use well_known::*;
|
||||
|
||||
/// generated device ID length
|
||||
const DEVICE_ID_LENGTH: usize = 10;
|
||||
|
||||
/// generated user access token length
|
||||
const TOKEN_LENGTH: usize = 32;
|
||||
+11
-16
@@ -3,7 +3,7 @@ use std::time::Duration;
|
||||
use axum::extract::State;
|
||||
use conduwuit::{
|
||||
Err, Result, debug, err, info,
|
||||
utils::{self, ReadyExt, stream::BroadbandExt},
|
||||
utils::{ReadyExt, stream::BroadbandExt},
|
||||
warn,
|
||||
};
|
||||
use conduwuit_service::Services;
|
||||
@@ -30,7 +30,6 @@ use ruma::{
|
||||
};
|
||||
use service::users::DeviceToken;
|
||||
|
||||
use super::DEVICE_ID_LENGTH;
|
||||
use crate::{Ruma, client_ip::ClientIp};
|
||||
|
||||
/// # `GET /_matrix/client/v3/login`
|
||||
@@ -189,43 +188,39 @@ pub(crate) async fn login_route(
|
||||
},
|
||||
};
|
||||
|
||||
// Generate new device id if the user didn't specify one
|
||||
let device_id = body
|
||||
.device_id
|
||||
.clone()
|
||||
.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
||||
|
||||
// Generate a new token for the device
|
||||
let token = DeviceToken::new_random();
|
||||
|
||||
// Determine if device_id was provided and exists in the db for this user
|
||||
let device_exists = if body.device_id.is_some() {
|
||||
let existing_device_id = if let Some(device_id) = &body.device_id {
|
||||
services
|
||||
.users
|
||||
.all_device_ids(&user_id)
|
||||
.ready_any(|v| v == device_id)
|
||||
.ready_find(|v| v == device_id)
|
||||
.await
|
||||
} else {
|
||||
false
|
||||
None
|
||||
};
|
||||
|
||||
if device_exists {
|
||||
let device_id = if let Some(existing_device_id) = existing_device_id {
|
||||
services
|
||||
.users
|
||||
.set_token(&user_id, &device_id, token.clone())
|
||||
.set_token(&user_id, &existing_device_id, token.clone())
|
||||
.await?;
|
||||
|
||||
existing_device_id
|
||||
} else {
|
||||
services
|
||||
.users
|
||||
.create_device(
|
||||
&user_id,
|
||||
&device_id,
|
||||
body.device_id.clone(),
|
||||
Some(token.clone()),
|
||||
body.initial_device_display_name.clone(),
|
||||
Some(client.to_string()),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
.await?
|
||||
};
|
||||
|
||||
// send client well-known if specified so the client knows to reconfigure itself
|
||||
let client_discovery_info: Option<DiscoveryInfo> = services
|
||||
|
||||
+14
-14
@@ -547,7 +547,7 @@ impl Service {
|
||||
.iter()
|
||||
.find_map(|scope| {
|
||||
if let Scope::Device(device_id) = scope {
|
||||
Some(device_id)
|
||||
Some(device_id.to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -557,7 +557,7 @@ impl Service {
|
||||
if self
|
||||
.services
|
||||
.users
|
||||
.get_device_metadata(&authorizing_user, device_id)
|
||||
.get_device_metadata(&authorizing_user, &device_id)
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
@@ -567,11 +567,11 @@ impl Service {
|
||||
));
|
||||
}
|
||||
|
||||
self.services
|
||||
let device_id = self.services
|
||||
.users
|
||||
.create_device(
|
||||
&authorizing_user,
|
||||
device_id,
|
||||
Some(device_id),
|
||||
Some(access_token.clone()),
|
||||
client_name,
|
||||
None,
|
||||
@@ -581,8 +581,16 @@ impl Service {
|
||||
// failure during authentication, which should(?) be impossible(?)
|
||||
.expect("failed to create device");
|
||||
|
||||
info!(
|
||||
?client_id,
|
||||
?authorizing_user,
|
||||
?device_id,
|
||||
?requested_scopes,
|
||||
"Created new oauth session"
|
||||
);
|
||||
|
||||
self.db.userdeviceid_oauthsessioninfo.put(
|
||||
(&authorizing_user, device_id),
|
||||
(&authorizing_user, &device_id),
|
||||
Json(SessionInfo {
|
||||
client_id: client_id.clone(),
|
||||
current_refresh_token: refresh_token.clone(),
|
||||
@@ -595,18 +603,10 @@ impl Service {
|
||||
Json(RefreshTokenInfo {
|
||||
client_id: client_id.clone(),
|
||||
user_id: authorizing_user.clone(),
|
||||
device_id: device_id.to_owned(),
|
||||
device_id,
|
||||
}),
|
||||
);
|
||||
|
||||
info!(
|
||||
?client_id,
|
||||
?authorizing_user,
|
||||
?device_id,
|
||||
?requested_scopes,
|
||||
"Created new oauth session"
|
||||
);
|
||||
|
||||
Ok(TokenResponse {
|
||||
access_token: access_token.into_token(),
|
||||
token_type: TokenType::Bearer,
|
||||
|
||||
@@ -40,7 +40,7 @@ impl super::Service {
|
||||
|
||||
self.create_device(
|
||||
user_id,
|
||||
&request.device_id,
|
||||
Some(request.device_id.clone()),
|
||||
None,
|
||||
request.initial_device_display_name.clone(),
|
||||
None,
|
||||
|
||||
@@ -42,32 +42,39 @@ impl DeviceToken {
|
||||
impl super::Service {
|
||||
/// Adds a new device to a user.
|
||||
///
|
||||
/// If no `device_id` is provided, a random one will be generated.
|
||||
///
|
||||
/// If no `token` is provided, the device will not be able to be logged
|
||||
/// into.
|
||||
pub async fn create_device(
|
||||
&self,
|
||||
user_id: &UserId,
|
||||
device_id: &DeviceId,
|
||||
device_id: Option<OwnedDeviceId>,
|
||||
token: Option<DeviceToken>,
|
||||
initial_device_display_name: Option<String>,
|
||||
client_ip: Option<String>,
|
||||
) -> Result<()> {
|
||||
) -> Result<OwnedDeviceId> {
|
||||
const DEVICE_ID_LENGTH: usize = 10;
|
||||
|
||||
self.status(user_id).await.ensure_active()?;
|
||||
|
||||
let key = (user_id, device_id);
|
||||
let mut device = Device::new(device_id.into());
|
||||
let device_id =
|
||||
device_id.unwrap_or_else(|| utils::random_string(DEVICE_ID_LENGTH).into());
|
||||
|
||||
let mut device = Device::new(device_id.clone());
|
||||
device.display_name = initial_device_display_name;
|
||||
device.last_seen_ip = client_ip;
|
||||
device.last_seen_ts = Some(MilliSecondsSinceUnixEpoch::now());
|
||||
|
||||
let key = (user_id, &device_id);
|
||||
increment(&self.db.userid_devicelistversion, user_id.as_bytes());
|
||||
self.db.userdeviceid_metadata.put(key, Json(device));
|
||||
|
||||
if let Some(token) = token {
|
||||
self.set_token(user_id, device_id, token).await?;
|
||||
self.set_token(user_id, &device_id, token).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(device_id)
|
||||
}
|
||||
|
||||
/// Removes a device from a user.
|
||||
|
||||
Reference in new issue
Block a user