Implement SSO/OIDC support. (closes #7)
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
mod account_data;
|
||||
mod appservice;
|
||||
mod globals;
|
||||
mod oauth;
|
||||
mod presence;
|
||||
mod pusher;
|
||||
mod raw;
|
||||
@@ -18,10 +19,10 @@ use tuwunel_core::Result;
|
||||
|
||||
use self::{
|
||||
account_data::AccountDataCommand, appservice::AppserviceCommand, globals::GlobalsCommand,
|
||||
presence::PresenceCommand, pusher::PusherCommand, raw::RawCommand, resolver::ResolverCommand,
|
||||
room_alias::RoomAliasCommand, room_state_cache::RoomStateCacheCommand,
|
||||
room_timeline::RoomTimelineCommand, sending::SendingCommand, short::ShortCommand,
|
||||
sync::SyncCommand, users::UsersCommand,
|
||||
oauth::OauthCommand, presence::PresenceCommand, pusher::PusherCommand, raw::RawCommand,
|
||||
resolver::ResolverCommand, room_alias::RoomAliasCommand,
|
||||
room_state_cache::RoomStateCacheCommand, room_timeline::RoomTimelineCommand,
|
||||
sending::SendingCommand, short::ShortCommand, sync::SyncCommand, users::UsersCommand,
|
||||
};
|
||||
use crate::admin_command_dispatch;
|
||||
|
||||
@@ -81,6 +82,10 @@ pub(super) enum QueryCommand {
|
||||
#[command(subcommand)]
|
||||
Sync(SyncCommand),
|
||||
|
||||
/// - oauth service
|
||||
#[command(subcommand)]
|
||||
Oauth(OauthCommand),
|
||||
|
||||
/// - raw service
|
||||
#[command(subcommand)]
|
||||
Raw(RawCommand),
|
||||
|
||||
172
src/admin/query/oauth.rs
Normal file
172
src/admin/query/oauth.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
use clap::Subcommand;
|
||||
use futures::{StreamExt, TryStreamExt};
|
||||
use ruma::OwnedUserId;
|
||||
use tuwunel_core::{Err, Result, utils::stream::IterStream};
|
||||
use tuwunel_service::{
|
||||
Services,
|
||||
oauth::{Provider, Session},
|
||||
};
|
||||
|
||||
use crate::{admin_command, admin_command_dispatch};
|
||||
|
||||
#[admin_command_dispatch(handler_prefix = "oauth")]
|
||||
#[derive(Debug, Subcommand)]
|
||||
/// Query OAuth service state
|
||||
pub(crate) enum OauthCommand {
|
||||
/// List configured OAuth providers.
|
||||
ListProviders,
|
||||
|
||||
/// List users associated with an OAuth provider
|
||||
ListUsers,
|
||||
|
||||
/// Show active configuration of a provider.
|
||||
ShowProvider {
|
||||
id: String,
|
||||
|
||||
#[arg(long)]
|
||||
config: bool,
|
||||
},
|
||||
|
||||
/// Show session state
|
||||
ShowSession {
|
||||
id: String,
|
||||
},
|
||||
|
||||
/// Token introspection request to provider.
|
||||
TokenInfo {
|
||||
id: String,
|
||||
},
|
||||
|
||||
/// Revoke token for user_id or sess_id.
|
||||
Revoke {
|
||||
id: String,
|
||||
},
|
||||
|
||||
/// Remove oauth state (DANGER!)
|
||||
Remove {
|
||||
id: String,
|
||||
|
||||
#[arg(long)]
|
||||
force: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_list_providers(&self) -> Result {
|
||||
self.services
|
||||
.config
|
||||
.identity_provider
|
||||
.iter()
|
||||
.try_stream()
|
||||
.map_ok(Provider::id)
|
||||
.map_ok(|id| format!("{id}\n"))
|
||||
.try_for_each(async |id| self.write_str(&id).await)
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_list_users(&self) -> Result {
|
||||
self.services
|
||||
.oauth
|
||||
.sessions
|
||||
.users()
|
||||
.map(|id| format!("{id}\n"))
|
||||
.map(Ok)
|
||||
.try_for_each(async |id: String| self.write_str(&id).await)
|
||||
.await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_show_provider(&self, id: String, config: bool) -> Result {
|
||||
if config {
|
||||
let config = self.services.oauth.providers.get_config(&id)?;
|
||||
|
||||
self.write_str(&format!("{config:#?}\n")).await?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let provider = self.services.oauth.providers.get(&id).await?;
|
||||
|
||||
self.write_str(&format!("{provider:#?}\n")).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_show_session(&self, id: String) -> Result {
|
||||
let session = find_session(self.services, &id).await?;
|
||||
|
||||
self.write_str(&format!("{session:#?}\n")).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_token_info(&self, id: String) -> Result {
|
||||
let session = find_session(self.services, &id).await?;
|
||||
|
||||
let provider = self
|
||||
.services
|
||||
.oauth
|
||||
.sessions
|
||||
.provider(&session)
|
||||
.await?;
|
||||
|
||||
let tokeninfo = self
|
||||
.services
|
||||
.oauth
|
||||
.request_tokeninfo((&provider, &session))
|
||||
.await;
|
||||
|
||||
self.write_str(&format!("{tokeninfo:#?}\n")).await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_revoke(&self, id: String) -> Result {
|
||||
let session = find_session(self.services, &id).await?;
|
||||
|
||||
let provider = self
|
||||
.services
|
||||
.oauth
|
||||
.sessions
|
||||
.provider(&session)
|
||||
.await?;
|
||||
|
||||
self.services
|
||||
.oauth
|
||||
.revoke_token((&provider, &session))
|
||||
.await?;
|
||||
|
||||
self.write_str("done").await
|
||||
}
|
||||
|
||||
#[admin_command]
|
||||
pub(super) async fn oauth_remove(&self, id: String, force: bool) -> Result {
|
||||
let session = find_session(self.services, &id).await?;
|
||||
|
||||
let Some(sess_id) = session.sess_id else {
|
||||
return Err!("Missing sess_id in oauth Session state");
|
||||
};
|
||||
|
||||
if !force {
|
||||
return Err!(
|
||||
"Deleting these records can cause registration conflicts. Use --force to be sure."
|
||||
);
|
||||
}
|
||||
|
||||
self.services
|
||||
.oauth
|
||||
.sessions
|
||||
.delete(&sess_id)
|
||||
.await;
|
||||
|
||||
self.write_str("done").await
|
||||
}
|
||||
|
||||
async fn find_session(services: &Services, id: &str) -> Result<Session> {
|
||||
if let Ok(user_id) = OwnedUserId::parse(id) {
|
||||
services
|
||||
.oauth
|
||||
.sessions
|
||||
.get_by_user(&user_id)
|
||||
.await
|
||||
} else {
|
||||
services.oauth.sessions.get(id).await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user