Implement channel permissions.

This commit is contained in:
Paul Makles
2020-04-09 11:38:32 +01:00
parent 7af9a77df4
commit 82f6e6215f
8 changed files with 360 additions and 240 deletions

View File

@@ -8,7 +8,7 @@ pub fn find_member_permissions<C: Into<Option<String>>>(
id: String, id: String,
guild: String, guild: String,
channel: C, channel: C,
) -> u8 { ) -> u32 {
let col = get_collection("guilds"); let col = get_collection("guilds");
match col.find_one( match col.find_one(
@@ -31,10 +31,10 @@ pub fn find_member_permissions<C: Into<Option<String>>>(
Ok(result) => { Ok(result) => {
if let Some(doc) = result { if let Some(doc) = result {
if doc.get_str("owner").unwrap() == id { if doc.get_str("owner").unwrap() == id {
return u8::MAX; return u32::MAX;
} }
doc.get_i32("default_permissions").unwrap() as u8 doc.get_i32("default_permissions").unwrap() as u32
} else { } else {
0 0
} }

View File

@@ -27,6 +27,7 @@ pub fn get_collection(collection: &str) -> Collection {
pub mod channel; pub mod channel;
pub mod guild; pub mod guild;
pub mod message; pub mod message;
pub mod mutual;
pub mod permissions; pub mod permissions;
pub mod user; pub mod user;

35
src/database/mutual.rs Normal file
View File

@@ -0,0 +1,35 @@
use super::get_collection;
use bson::{bson, doc};
use mongodb::options::FindOneOptions;
/*pub struct MutualGuild {
}
pub fn find_mutual_guilds(user_id: String, target_id: String) -> Vec<> {
}*/
pub fn has_mutual_connection(user_id: String, target_id: String) -> bool {
let col = get_collection("guilds");
if let Ok(result) = col.find_one(
doc! {
"$and": [
{ "members": { "$elemMatch": { "id": user_id } } },
{ "members": { "$elemMatch": { "id": target_id } } },
]
},
FindOneOptions::builder()
.projection(doc! { "_id": 1 })
.build(),
) {
if result.is_some() {
true
} else {
false
}
} else {
false
}
}

View File

@@ -1,31 +1,99 @@
bitfield! { use super::mutual::has_mutual_connection;
pub struct MemberPermissions(MSB0 [u8]); use crate::database::user::UserRelationship;
u8; use crate::guards::auth::UserRef;
pub get_access, set_access: 7;
pub get_create_invite, set_create_invite: 6;
pub get_kick_members, set_kick_members: 5;
pub get_ban_members, set_ban_members: 4;
pub get_read_messages, set_read_messages: 3;
pub get_send_messages, set_send_messages: 2;
}
use super::get_collection;
use crate::guards::channel::ChannelRef; use crate::guards::channel::ChannelRef;
use crate::guards::guild::GuildRef; use crate::guards::guild::GuildRef;
use bson::{bson, doc}; use bson::{bson, doc};
use mongodb::options::FindOneOptions; use num_enum::TryFromPrimitive;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum Relationship {
FRIEND = 0,
OUTGOING = 1,
INCOMING = 2,
BLOCKED = 3,
BLOCKEDOTHER = 4,
NONE = 5,
SELF = 6,
}
#[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u32)]
pub enum Permission {
ACCESS = 1,
CREATE_INVITE = 2,
KICK_MEMBERS = 4,
BAN_MEMBERS = 8,
READ_MESSAGES = 16,
SEND_MESSAGES = 32,
MANAGE_MESSAGES = 64,
MANAGE_CHANNELS = 128,
MANAGE_SERVER = 256,
MANAGE_ROLES = 512,
}
bitfield! {
pub struct MemberPermissions(MSB0 [u32]);
u8;
pub get_access, set_access: 31;
pub get_create_invite, set_create_invite: 30;
pub get_kick_members, set_kick_members: 29;
pub get_ban_members, set_ban_members: 28;
pub get_read_messages, set_read_messages: 27;
pub get_send_messages, set_send_messages: 26;
pub get_manage_messages, set_manage_messages: 25;
pub get_manage_channels, set_manage_channels: 24;
pub get_manage_server, set_manage_server: 23;
pub get_manage_roles, set_manage_roles: 22;
}
pub fn get_relationship_internal(
user_id: &str,
target_id: &str,
relationships: &Option<Vec<UserRelationship>>,
) -> Relationship {
if user_id == target_id {
return Relationship::SELF;
}
if let Some(arr) = &relationships {
for entry in arr {
if entry.id == target_id {
match entry.status {
0 => return Relationship::FRIEND,
1 => return Relationship::OUTGOING,
2 => return Relationship::INCOMING,
3 => return Relationship::BLOCKED,
4 => return Relationship::BLOCKEDOTHER,
_ => return Relationship::NONE,
}
}
}
}
Relationship::NONE
}
pub fn get_relationship(a: &UserRef, b: &UserRef) -> Relationship {
if a.id == b.id {
return Relationship::SELF;
}
get_relationship_internal(&a.id, &b.id, &a.fetch_relationships())
}
pub struct PermissionCalculator { pub struct PermissionCalculator {
pub user_id: String, pub user: UserRef,
pub channel: Option<ChannelRef>, pub channel: Option<ChannelRef>,
pub guild: Option<GuildRef>, pub guild: Option<GuildRef>,
} }
impl PermissionCalculator { impl PermissionCalculator {
pub fn new(user_id: String) -> PermissionCalculator { pub fn new(user: UserRef) -> PermissionCalculator {
PermissionCalculator { PermissionCalculator {
user_id, user,
channel: None, channel: None,
guild: None, guild: None,
} }
@@ -45,7 +113,7 @@ impl PermissionCalculator {
} }
} }
pub fn calculate(self) -> u8 { pub fn calculate(self) -> u32 {
let guild = if let Some(value) = self.guild { let guild = if let Some(value) = self.guild {
Some(value) Some(value)
} else if let Some(channel) = &self.channel { } else if let Some(channel) = &self.channel {
@@ -70,14 +138,14 @@ impl PermissionCalculator {
doc! { doc! {
"members": { "members": {
"$elemMatch": { "$elemMatch": {
"id": &self.user_id, "id": &self.user.id,
} }
} }
}, },
doc! { } doc! {},
) { ) {
if guild.owner == self.user_id { if guild.owner == self.user.id {
return u8::MAX; return u32::MAX;
} }
permissions = guild.default_permissions; permissions = guild.default_permissions;
@@ -87,29 +155,42 @@ impl PermissionCalculator {
if let Some(channel) = &self.channel { if let Some(channel) = &self.channel {
match channel.channel_type { match channel.channel_type {
0 => { 0 => {
// ? check user is part of the channel
if let Some(arr) = &channel.recipients { if let Some(arr) = &channel.recipients {
let mut other_user = "";
for item in arr { for item in arr {
if item == &self.user_id { if item == &self.user.id {
permissions = 49; permissions = 49;
break; } else {
other_user = item;
} }
} }
let relationships = self.user.fetch_relationships();
let relationship =
get_relationship_internal(&self.user.id, &other_user, &relationships);
if relationship == Relationship::BLOCKED
|| relationship == Relationship::BLOCKEDOTHER
{
permissions = 1;
} else if has_mutual_connection(self.user.id, other_user.to_string()) {
permissions = 49;
}
} }
}, }
1 => { 1 => unreachable!(),
unreachable!()
},
2 => { 2 => {
// nothing implemented yet // nothing implemented yet
}, }
_ => {} _ => {}
} }
} }
permissions as u8 permissions as u32
} }
pub fn as_permission(self) -> MemberPermissions<[u8; 1]> { pub fn as_permission(self) -> MemberPermissions<[u32; 1]> {
MemberPermissions([ self.calculate() ]) MemberPermissions([self.calculate()])
} }
} }

View File

@@ -1,14 +1,14 @@
use bson::{bson, doc, from_bson, Document};
use mongodb::options::FindOneOptions;
use rocket::http::{RawStr, Status}; use rocket::http::{RawStr, Status};
use rocket::request::{self, FromParam, FromRequest, Request}; use rocket::request::{self, FromParam, FromRequest, Request};
use rocket::Outcome; use rocket::Outcome;
use bson::{bson, doc, from_bson, Document};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use mongodb::options::FindOneOptions;
use crate::database; use crate::database;
use database::user::{User, UserRelationship}; use database::user::{User, UserRelationship};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub struct UserRef { pub struct UserRef {
pub id: String, pub id: String,
pub username: String, pub username: String,
@@ -29,7 +29,9 @@ impl UserRef {
let user = database::get_collection("users") let user = database::get_collection("users")
.find_one( .find_one(
doc! { "_id": &self.id }, doc! { "_id": &self.id },
FindOneOptions::builder().projection(doc! { "relations": 1 }).build(), FindOneOptions::builder()
.projection(doc! { "relations": 1 })
.build(),
) )
.expect("Failed to fetch user relationships from database.") .expect("Failed to fetch user relationships from database.")
.expect("Missing user document."); .expect("Missing user document.");
@@ -77,13 +79,15 @@ impl<'a, 'r> FromRequest<'a, 'r> for UserRef {
.unwrap(); .unwrap();
if let Some(user) = result { if let Some(user) = result {
Outcome::Success( Outcome::Success(UserRef {
UserRef { id: user.get_str("_id").unwrap().to_string(),
id: user.get_str("_id").unwrap().to_string(), username: user.get_str("username").unwrap().to_string(),
username: user.get_str("username").unwrap().to_string(), email_verified: user
email_verified: user.get_document("email_verification").unwrap().get_bool("verified").unwrap(), .get_document("email_verification")
} .unwrap()
) .get_bool("verified")
.unwrap(),
})
} else { } else {
Outcome::Failure((Status::Forbidden, AuthError::Invalid)) Outcome::Failure((Status::Forbidden, AuthError::Invalid))
} }
@@ -124,26 +128,28 @@ impl<'r> FromParam<'r> for UserRef {
fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
let col = database::get_db().collection("users"); let col = database::get_db().collection("users");
let result = database::get_collection("users") let result = database::get_collection("users")
.find_one( .find_one(
doc! { "_id": param.to_string() }, doc! { "_id": param.to_string() },
FindOneOptions::builder() FindOneOptions::builder()
.projection(doc! { .projection(doc! {
"_id": 1, "_id": 1,
"username": 1, "username": 1,
"email_verification.verified": 1, "email_verification.verified": 1,
}) })
.build(), .build(),
) )
.unwrap(); .unwrap();
if let Some(user) = result { if let Some(user) = result {
Ok( Ok(UserRef {
UserRef { id: user.get_str("_id").unwrap().to_string(),
id: user.get_str("_id").unwrap().to_string(), username: user.get_str("username").unwrap().to_string(),
username: user.get_str("username").unwrap().to_string(), email_verified: user
email_verified: user.get_document("email_verification").unwrap().get_bool("verified").unwrap(), .get_document("email_verification")
} .unwrap()
) .get_bool("verified")
.unwrap(),
})
} else { } else {
Err(param) Err(param)
} }

View File

@@ -1,8 +1,7 @@
use super::Response; use super::Response;
use crate::database::{self, channel::Channel, message::Message, user::User, PermissionCalculator}; use crate::database::{self, message::Message, Permission, PermissionCalculator};
use crate::guards::channel::ChannelRef;
use crate::guards::auth::UserRef; use crate::guards::auth::UserRef;
use crate::websocket; use crate::guards::channel::ChannelRef;
use bson::{bson, doc, from_bson, Bson::UtcDatetime}; use bson::{bson, doc, from_bson, Bson::UtcDatetime};
use chrono::prelude::*; use chrono::prelude::*;
@@ -20,19 +19,17 @@ pub enum ChannelType {
} }
macro_rules! with_permissions { macro_rules! with_permissions {
($user: expr, $target: expr) => { ($user: expr, $target: expr) => {{
{ let permissions = PermissionCalculator::new($user.clone())
let permissions = PermissionCalculator::new($user.id.clone()) .channel($target.clone())
.channel($target.clone()) .as_permission();
.as_permission();
if !permissions.get_access() { if !permissions.get_access() {
return None; return None;
}
permissions
} }
};
permissions
}};
} }
/// fetch channel information /// fetch channel information
@@ -41,34 +38,28 @@ pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> {
with_permissions!(user, target); with_permissions!(user, target);
match target.channel_type { match target.channel_type {
0..=1 => Some(Response::Success( 0..=1 => Some(Response::Success(json!({
json!({ "id": target.id,
"id": target.id, "type": target.channel_type,
"type": target.channel_type, "recipients": target.recipients,
"recipients": target.recipients, }))),
})
)),
2 => { 2 => {
if let Some(info) = target.fetch_data( if let Some(info) = target.fetch_data(doc! {
doc! { "name": 1,
"name": 1, "description": 1,
"description": 1, }) {
} Some(Response::Success(json!({
) { "id": target.id,
Some(Response::Success( "type": target.channel_type,
json!({ "guild": target.guild,
"id": target.id, "name": info.get_str("name").unwrap(),
"type": target.channel_type, "description": info.get_str("description").unwrap_or(""),
"guild": target.guild, })))
"name": info.get_str("name").unwrap(),
"description": info.get_str("description").unwrap_or(""),
})
))
} else { } else {
None None
} }
}, }
_ => unreachable!() _ => unreachable!(),
} }
} }
@@ -77,38 +68,49 @@ pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> {
/// or close DM conversation /// or close DM conversation
#[delete("/<target>")] #[delete("/<target>")]
pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> { pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> {
with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_manage_channels() {
return Some(Response::LackingPermission(Permission::MANAGE_CHANNELS));
}
let col = database::get_collection("channels"); let col = database::get_collection("channels");
Some(match target.channel_type { match target.channel_type {
0 => { 0 => {
col.update_one( if col.update_one(
doc! { "_id": target.id }, doc! { "_id": target.id },
doc! { "$set": { "active": false } }, doc! { "$set": { "active": false } },
None, None,
) ).is_ok() {
.expect("Failed to update channel."); Some(Response::Result(super::Status::Ok))
} else {
Response::Result(super::Status::Ok) Some(Response::InternalServerError(json!({ "error": "Failed to close channel." })))
}
} }
1 => { 1 => {
// ? TODO: group dm // ? TODO: group dm
Response::Result(super::Status::Ok) Some(Response::Result(super::Status::Ok))
} }
2 => { 2 => {
// ? TODO: guild // ? TODO: guild
Response::Result(super::Status::Ok) Some(Response::Result(super::Status::Ok))
} }
_ => Response::InternalServerError(json!({ "error": "Unknown error has occurred." })), _ => Some(Response::InternalServerError(
}) json!({ "error": "Unknown error has occurred." }),
)),
}
} }
/// fetch channel messages /// fetch channel messages
#[get("/<target>/messages")] #[get("/<target>/messages")]
pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> { pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> {
with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_read_messages() {
return Some(Response::LackingPermission(Permission::READ_MESSAGES));
}
let col = database::get_collection("messages"); let col = database::get_collection("messages");
let result = col.find(doc! { "channel": target.id }, None).unwrap(); let result = col.find(doc! { "channel": target.id }, None).unwrap();
@@ -136,8 +138,16 @@ pub struct SendMessage {
/// send a message to a channel /// send a message to a channel
#[post("/<target>/messages", data = "<message>")] #[post("/<target>/messages", data = "<message>")]
pub fn send_message(user: UserRef, target: ChannelRef, message: Json<SendMessage>) -> Option<Response> { pub fn send_message(
with_permissions!(user, target); user: UserRef,
target: ChannelRef,
message: Json<SendMessage>,
) -> Option<Response> {
let permissions = with_permissions!(user, target);
if !permissions.get_send_messages() {
return Some(Response::LackingPermission(Permission::SEND_MESSAGES));
}
let content: String = message.content.chars().take(2000).collect(); let content: String = message.content.chars().take(2000).collect();
let nonce: String = message.nonce.chars().take(32).collect(); let nonce: String = message.nonce.chars().take(32).collect();
@@ -201,7 +211,11 @@ pub fn send_message(user: UserRef, target: ChannelRef, message: Json<SendMessage
/// get a message /// get a message
#[get("/<target>/messages/<message>")] #[get("/<target>/messages/<message>")]
pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> { pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> {
with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_read_messages() {
return Some(Response::LackingPermission(Permission::READ_MESSAGES));
}
let prev = let prev =
// ! CHECK IF USER HAS PERMISSION TO VIEW EDITS OF MESSAGES // ! CHECK IF USER HAS PERMISSION TO VIEW EDITS OF MESSAGES
@@ -243,87 +257,90 @@ pub fn edit_message(
) -> Option<Response> { ) -> Option<Response> {
with_permissions!(user, target); with_permissions!(user, target);
Some(if message.author != user.id { if message.author != user.id {
Response::Unauthorized(json!({ "error": "You did not send this message." })) return Some(Response::Unauthorized(
json!({ "error": "You did not send this message." }),
));
}
let col = database::get_collection("messages");
let time = if let Some(edited) = message.edited {
edited.0
} else { } else {
let col = database::get_collection("messages"); Ulid::from_string(&message.id).unwrap().datetime()
};
let time = if let Some(edited) = message.edited { let edited = Utc::now();
edited.0 match col.update_one(
} else { doc! { "_id": message.id.clone() },
Ulid::from_string(&message.id).unwrap().datetime() doc! {
}; "$set": {
"content": edit.content.clone(),
let edited = Utc::now(); "edited": UtcDatetime(edited.clone())
match col.update_one(
doc! { "_id": message.id.clone() },
doc! {
"$set": {
"content": edit.content.clone(),
"edited": UtcDatetime(edited.clone())
},
"$push": {
"previous_content": {
"content": message.content,
"time": time,
}
},
}, },
None, "$push": {
) { "previous_content": {
Ok(_) => { "content": message.content,
/*websocket::queue_message( "time": time,
get_recipients(&target), }
json!({ },
"type": "message_update", },
"data": { None,
"id": message.id, ) {
"channel": target.id, Ok(_) => {
"content": edit.content.clone(), /*websocket::queue_message(
"edited": edited.timestamp() get_recipients(&target),
}, json!({
}) "type": "message_update",
.to_string(), "data": {
);*/ "id": message.id,
"channel": target.id,
"content": edit.content.clone(),
"edited": edited.timestamp()
},
})
.to_string(),
);*/
Response::Result(super::Status::Ok) Some(Response::Result(super::Status::Ok))
}
Err(_) => {
Response::InternalServerError(json!({ "error": "Failed to update message." }))
}
} }
}) Err(_) => Some(Response::InternalServerError(
json!({ "error": "Failed to update message." }),
)),
}
} }
/// delete a message /// delete a message
#[delete("/<target>/messages/<message>")] #[delete("/<target>/messages/<message>")]
pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> { pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Option<Response> {
with_permissions!(user, target); let permissions = with_permissions!(user, target);
Some(if message.author != user.id { if !permissions.get_manage_messages() {
Response::Unauthorized(json!({ "error": "You did not send this message." })) if message.author != user.id {
} else { return Some(Response::LackingPermission(Permission::MANAGE_MESSAGES));
let col = database::get_collection("messages");
match col.delete_one(doc! { "_id": message.id.clone() }, None) {
Ok(_) => {
/*websocket::queue_message(
get_recipients(&target),
json!({
"type": "message_delete",
"data": {
"id": message.id,
"channel": target.id
},
})
.to_string(),
);*/
Response::Result(super::Status::Ok)
}
Err(_) => {
Response::InternalServerError(json!({ "error": "Failed to delete message." }))
}
} }
}) }
let col = database::get_collection("messages");
match col.delete_one(doc! { "_id": message.id.clone() }, None) {
Ok(_) => {
/*websocket::queue_message(
get_recipients(&target),
json!({
"type": "message_delete",
"data": {
"id": message.id,
"channel": target.id
},
})
.to_string(),
);*/
Some(Response::Result(super::Status::Ok))
}
Err(_) => Some(Response::InternalServerError(
json!({ "error": "Failed to delete message." }),
)),
}
} }

View File

@@ -3,6 +3,8 @@ pub use rocket::response::Redirect;
use rocket::Rocket; use rocket::Rocket;
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
use crate::database::Permission;
pub mod account; pub mod account;
pub mod channel; pub mod channel;
pub mod guild; pub mod guild;
@@ -21,6 +23,8 @@ pub enum Response {
BadRequest(JsonValue), BadRequest(JsonValue),
#[response(status = 401)] #[response(status = 401)]
Unauthorized(JsonValue), Unauthorized(JsonValue),
#[response(status = 401)]
LackingPermission(Permission),
#[response(status = 404)] #[response(status = 404)]
NotFound(JsonValue), NotFound(JsonValue),
#[response(status = 406)] #[response(status = 406)]
@@ -37,6 +41,22 @@ pub enum Response {
InternalServerError(JsonValue), InternalServerError(JsonValue),
} }
use rocket::http::ContentType;
use rocket::request::Request;
use std::io::Cursor;
impl<'a> rocket::response::Responder<'a> for Permission {
fn respond_to(self, _: &Request) -> rocket::response::Result<'a> {
rocket::response::Response::build()
.header(ContentType::JSON)
.sized_body(Cursor::new(format!(
"{{\"error\":\"Lacking {:?} permission.\"}}",
self
)))
.ok()
}
}
pub fn mount(rocket: Rocket) -> Rocket { pub fn mount(rocket: Rocket) -> Rocket {
rocket rocket
.mount("/api", routes![root::root]) .mount("/api", routes![root::root])

View File

@@ -1,7 +1,8 @@
use super::Response; use super::Response;
use crate::database::{self, channel::Channel, user::UserRelationship}; use crate::database::{self, channel::Channel};
use crate::routes::channel; use crate::database::{get_relationship, get_relationship_internal, Relationship};
use crate::guards::auth::UserRef; use crate::guards::auth::UserRef;
use crate::routes::channel;
use bson::{bson, doc, from_bson}; use bson::{bson, doc, from_bson};
use mongodb::options::FindOptions; use mongodb::options::FindOptions;
@@ -9,61 +10,20 @@ use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ulid::Ulid; use ulid::Ulid;
enum Relationship {
FRIEND = 0,
OUTGOING = 1,
INCOMING = 2,
BLOCKED = 3,
BLOCKEDOTHER = 4,
NONE = 5,
SELF = 6,
}
fn get_relationship_internal(user_id: &str, target_id: &str, relationships: &Option<Vec<UserRelationship>>) -> Relationship {
if user_id == target_id {
return Relationship::SELF;
}
if let Some(arr) = &relationships {
for entry in arr {
if entry.id == target_id {
match entry.status {
0 => return Relationship::FRIEND,
1 => return Relationship::OUTGOING,
2 => return Relationship::INCOMING,
3 => return Relationship::BLOCKED,
4 => return Relationship::BLOCKEDOTHER,
_ => return Relationship::NONE,
}
}
}
}
Relationship::NONE
}
fn get_relationship(a: &UserRef, b: &UserRef) -> Relationship {
if a.id == b.id {
return Relationship::SELF;
}
get_relationship_internal(&a.id, &b.id, &a.fetch_relationships())
}
/// retrieve your user information /// retrieve your user information
#[get("/@me")] #[get("/@me")]
pub fn me(user: UserRef) -> Response { pub fn me(user: UserRef) -> Response {
if let Some(info) = user.fetch_data(doc! { "email": 1 }) { if let Some(info) = user.fetch_data(doc! { "email": 1 }) {
Response::Success( Response::Success(json!({
json!({ "id": user.id,
"id": user.id, "username": user.username,
"username": user.username, "email": info.get_str("email").unwrap(),
"email": info.get_str("email").unwrap(), "verified": user.email_verified,
"verified": user.email_verified, }))
})
)
} else { } else {
Response::InternalServerError(json!({ "error": "Failed to fetch information from database." })) Response::InternalServerError(
json!({ "error": "Failed to fetch information from database." }),
)
} }
} }