feat: initial oauth2 token revoking

This commit is contained in:
Zomatree
2025-07-02 03:45:43 +01:00
parent 5a5f84f207
commit 660e646b2b
19 changed files with 358 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
mod model;
mod ops;
pub use model::*;
pub use ops::*;

View File

@@ -0,0 +1,27 @@
use iso8601_timestamp::Timestamp;
auto_derived! (
/// Unique id of the user and bot
pub struct AuthorizedBotId {
/// User id
pub user: String,
/// Bot Id
pub bot: String,
}
pub struct AuthorizedBot {
/// Unique Id
#[serde(rename = "_id")]
pub id: AuthorizedBotId,
/// When the authorized oauth2 bot connection was created at
pub created_at: Timestamp,
/// If and when the authorized oauth2 bot connection was revoked at
pub deauthorized_at: Option<Timestamp>,
/// Scopes the bot has access to
pub scope: String
}
);

View File

@@ -0,0 +1,27 @@
use revolt_result::Result;
use crate::{AuthorizedBot, AuthorizedBotId};
mod mongodb;
mod reference;
#[async_trait]
pub trait AbstractAuthorizedBots: Sync + Send {
/// Insert emoji into database.
async fn insert_authorized_bot(&self, authorised_bot: &AuthorizedBot) -> Result<()>;
/// Fetch an emoji by its id
async fn fetch_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot>;
/// Fetch all authorized bots for a user
async fn fetch_users_authorized_bots(&self, user_id: &str) -> Result<Vec<AuthorizedBot>>;
/// Deletes an authorized bot
async fn delete_authorized_bot(&self, id: &AuthorizedBotId) -> Result<()>;
/// Deauthorizes a bot access to a user's information
async fn deauthorize_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot>;
/// Fetches all deauthorized bots
async fn fetch_deauthorized_authorized_bots(&self) -> Result<Vec<AuthorizedBot>>;
}

View File

@@ -0,0 +1,61 @@
use bson::{Document, to_bson};
use revolt_result::Result;
use iso8601_timestamp::Timestamp;
use crate::{MongoDb, AuthorizedBot, AuthorizedBotId};
use super::AbstractAuthorizedBots;
static COL: &str = "authorized_bots";
#[async_trait]
impl AbstractAuthorizedBots for MongoDb {
/// Insert emoji into database.
async fn insert_authorized_bot(&self, authorised_bot: &AuthorizedBot) -> Result<()> {
query!(self, insert_one, COL, &authorised_bot).map(|_| ())
}
/// Fetch an emoji by its id
async fn fetch_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
query!(self, find_one, COL, doc! { "id.user": &id.user, "id.bot": &id.bot })?.ok_or_else(|| create_error!(NotFound))
}
/// Fetch an emoji by its id
async fn fetch_users_authorized_bots(&self, user_id: &str) -> Result<Vec<AuthorizedBot>> {
query!(self, find, COL, doc! { "id.user": &user_id })
}
/// Deletes an authori
async fn delete_authorized_bot(&self, id: &AuthorizedBotId) -> Result<()> {
query!(self, delete_one, COL, doc! { "id.user": &id.user, "id.bot": &id.bot }).map(|_| ())
}
async fn deauthorize_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
self.col::<AuthorizedBot>(COL)
.find_one_and_update(
doc! {
"id.user": &id.user,
"id.bot": &id.bot
},
doc! {
"$set": {
"unauthorized_at": to_bson(&Timestamp::now_utc()).unwrap()
}
}
)
.await
.map_err(|_| create_database_error!("find_one_and_update", COL))
.and_then(|opt| opt.ok_or_else(|| create_database_error!("find_one_and_update", COL)))
}
async fn fetch_deauthorized_authorized_bots(&self) -> Result<Vec<AuthorizedBot>> {
query!(
self,
find,
COL,
doc! {
"deauthorized_at": { "$exists": true }
}
)
}
}

View File

@@ -0,0 +1,38 @@
use bson::Document;
use revolt_result::Result;
use crate::{ReferenceDb, AuthorizedBot, AuthorizedBotId};
use super::AbstractAuthorizedBots;
static COL: &str = "authorized_bots";
#[async_trait]
impl AbstractAuthorizedBots for ReferenceDb {
/// Insert emoji into database.
async fn insert_authorized_bot(&self, authorised_bot: &AuthorizedBot) -> Result<()> {
todo!()
}
/// Fetch an emoji by its id
async fn fetch_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
todo!()
}
async fn fetch_users_authorized_bots(&self, user_id: &str) -> Result<Vec<AuthorizedBot>> {
todo!()
}
/// Deletes an authori
async fn delete_authorized_bot(&self, id: &AuthorizedBotId) -> Result<()> {
todo!()
}
async fn deauthorize_authorized_bot(&self, id: &AuthorizedBotId) -> Result<AuthorizedBot> {
todo!()
}
async fn fetch_deauthorized_authorized_bots(&self) -> Result<Vec<AuthorizedBot>> {
todo!()
}
}

View File

@@ -1,4 +1,5 @@
mod admin_migrations;
mod authorized_bots;
mod bots;
mod channel_invites;
mod channel_unreads;
@@ -19,6 +20,7 @@ mod user_settings;
mod users;
pub use admin_migrations::*;
pub use authorized_bots::*;
pub use bots::*;
pub use channel_invites::*;
pub use channel_unreads::*;
@@ -46,6 +48,7 @@ use crate::MongoDb;
pub trait AbstractDatabase:
Sync
+ Send
+ authorized_bots::AbstractAuthorizedBots
+ admin_migrations::AbstractMigrations
+ bots::AbstractBots
+ channels::AbstractChannels

View File

@@ -1454,3 +1454,43 @@ impl From<FieldsMessage> for crate::FieldsMessage {
}
}
}
impl From<AuthorizedBotId> for crate::AuthorizedBotId {
fn from(value: AuthorizedBotId) -> Self {
crate::AuthorizedBotId {
user: value.user,
bot: value.bot
}
}
}
impl From<crate::AuthorizedBotId> for AuthorizedBotId {
fn from(value: crate::AuthorizedBotId) -> Self {
AuthorizedBotId {
user: value.user,
bot: value.bot
}
}
}
impl From<AuthorizedBot> for crate::AuthorizedBot {
fn from(value: AuthorizedBot) -> Self {
crate::AuthorizedBot {
id: value.id.into(),
created_at: value.created_at,
deauthorized_at: value.deauthorized_at,
scope: value.scope
}
}
}
impl From<crate::AuthorizedBot> for AuthorizedBot {
fn from(value: crate::AuthorizedBot) -> Self {
AuthorizedBot {
id: value.id.into(),
created_at: value.created_at,
deauthorized_at: value.deauthorized_at,
scope: value.scope
}
}
}