Added groups + ran rust fmt / clippy.

This commit is contained in:
Paul Makles
2020-04-09 15:41:26 +01:00
parent 82f6e6215f
commit e6ed46af82
11 changed files with 192 additions and 90 deletions

View File

@@ -10,13 +10,18 @@ pub struct Channel {
pub last_message: Option<String>, pub last_message: Option<String>,
// for Direct Messages // for Direct Messages
pub recipients: Option<Vec<String>>,
pub active: Option<bool>, pub active: Option<bool>,
// for DMs / GDMs
pub recipients: Option<Vec<String>>,
// for GDMs
pub owner: Option<String>,
// for Guilds // for Guilds
pub name: Option<String>,
pub guild: Option<String>, pub guild: Option<String>,
// for Guilds and Group DMs // for Guilds and Group DMs
pub name: Option<String>,
pub description: Option<String>, pub description: Option<String>,
} }

View File

@@ -7,7 +7,7 @@ use mongodb::options::FindOneOptions;
pub fn find_member_permissions<C: Into<Option<String>>>( pub fn find_member_permissions<C: Into<Option<String>>>(
id: String, id: String,
guild: String, guild: String,
channel: C, _channel: C,
) -> u32 { ) -> u32 {
let col = get_collection("guilds"); let col = get_collection("guilds");
@@ -46,6 +46,7 @@ pub fn find_member_permissions<C: Into<Option<String>>>(
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct Member { pub struct Member {
pub id: String, pub id: String,
pub nickname: String,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]

View File

@@ -10,11 +10,11 @@ use num_enum::TryFromPrimitive;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
pub enum Relationship { pub enum Relationship {
FRIEND = 0, Friend = 0,
OUTGOING = 1, Outgoing = 1,
INCOMING = 2, Incoming = 2,
BLOCKED = 3, Blocked = 3,
BLOCKEDOTHER = 4, BlockedOther = 4,
NONE = 5, NONE = 5,
SELF = 6, SELF = 6,
} }
@@ -22,16 +22,16 @@ pub enum Relationship {
#[derive(Debug, PartialEq, Eq, TryFromPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u32)] #[repr(u32)]
pub enum Permission { pub enum Permission {
ACCESS = 1, Access = 1,
CREATE_INVITE = 2, CreateInvite = 2,
KICK_MEMBERS = 4, KickMembers = 4,
BAN_MEMBERS = 8, BanMembers = 8,
READ_MESSAGES = 16, ReadMessages = 16,
SEND_MESSAGES = 32, SendMessages = 32,
MANAGE_MESSAGES = 64, ManageMessages = 64,
MANAGE_CHANNELS = 128, ManageChannels = 128,
MANAGE_SERVER = 256, ManageServer = 256,
MANAGE_ROLES = 512, ManageRoles = 512,
} }
bitfield! { bitfield! {
@@ -62,11 +62,11 @@ pub fn get_relationship_internal(
for entry in arr { for entry in arr {
if entry.id == target_id { if entry.id == target_id {
match entry.status { match entry.status {
0 => return Relationship::FRIEND, 0 => return Relationship::Friend,
1 => return Relationship::OUTGOING, 1 => return Relationship::Outgoing,
2 => return Relationship::INCOMING, 2 => return Relationship::Incoming,
3 => return Relationship::BLOCKED, 3 => return Relationship::Blocked,
4 => return Relationship::BLOCKEDOTHER, 4 => return Relationship::BlockedOther,
_ => return Relationship::NONE, _ => return Relationship::NONE,
} }
} }
@@ -132,7 +132,7 @@ impl PermissionCalculator {
None None
}; };
let mut permissions = 0; let mut permissions: u32 = 0;
if let Some(guild) = guild { if let Some(guild) = guild {
if let Some(_data) = guild.fetch_data_given( if let Some(_data) = guild.fetch_data_given(
doc! { doc! {
@@ -148,14 +148,13 @@ impl PermissionCalculator {
return u32::MAX; return u32::MAX;
} }
permissions = guild.default_permissions; permissions = guild.default_permissions as u32;
} }
} }
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 = ""; let mut other_user = "";
for item in arr { for item in arr {
@@ -170,8 +169,8 @@ impl PermissionCalculator {
let relationship = let relationship =
get_relationship_internal(&self.user.id, &other_user, &relationships); get_relationship_internal(&self.user.id, &other_user, &relationships);
if relationship == Relationship::BLOCKED if relationship == Relationship::Blocked
|| relationship == Relationship::BLOCKEDOTHER || relationship == Relationship::BlockedOther
{ {
permissions = 1; permissions = 1;
} else if has_mutual_connection(self.user.id, other_user.to_string()) { } else if has_mutual_connection(self.user.id, other_user.to_string()) {
@@ -179,7 +178,22 @@ impl PermissionCalculator {
} }
} }
} }
1 => unreachable!(), 1 => {
if let Some(id) = &channel.owner {
if &self.user.id == id {
return u32::MAX;
}
}
if let Some(arr) = &channel.recipients {
for item in arr {
if item == &self.user.id {
permissions = 49;
break;
}
}
}
}
2 => { 2 => {
// nothing implemented yet // nothing implemented yet
} }
@@ -187,7 +201,7 @@ impl PermissionCalculator {
} }
} }
permissions as u32 permissions
} }
pub fn as_permission(self) -> MemberPermissions<[u32; 1]> { pub fn as_permission(self) -> MemberPermissions<[u32; 1]> {

View File

@@ -16,6 +16,33 @@ pub struct UserRef {
} }
impl UserRef { impl UserRef {
pub fn from(id: String) -> Option<UserRef> {
match database::get_collection("users").find_one(
doc! { "_id": id },
FindOneOptions::builder()
.projection(doc! {
"_id": 1,
"username": 1,
"email_verification.verified": 1,
})
.build(),
) {
Ok(result) => match result {
Some(doc) => Some(UserRef {
id: doc.get_str("_id").unwrap().to_string(),
username: doc.get_str("username").unwrap().to_string(),
email_verified: doc
.get_document("email_verification")
.unwrap()
.get_bool("verified")
.unwrap(),
}),
None => None,
},
Err(_) => None,
}
}
pub fn fetch_data(&self, projection: Document) -> Option<Document> { pub fn fetch_data(&self, projection: Document) -> Option<Document> {
database::get_collection("users") database::get_collection("users")
.find_one( .find_one(
@@ -126,30 +153,8 @@ impl<'r> FromParam<'r> for UserRef {
type Error = &'r RawStr; type Error = &'r RawStr;
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"); if let Some(user) = UserRef::from(param.to_string()) {
let result = database::get_collection("users") Ok(user)
.find_one(
doc! { "_id": param.to_string() },
FindOneOptions::builder()
.projection(doc! {
"_id": 1,
"username": 1,
"email_verification.verified": 1,
})
.build(),
)
.unwrap();
if let Some(user) = result {
Ok(UserRef {
id: user.get_str("_id").unwrap().to_string(),
username: user.get_str("username").unwrap().to_string(),
email_verified: user
.get_document("email_verification")
.unwrap()
.get_bool("verified")
.unwrap(),
})
} else { } else {
Err(param) Err(param)
} }

View File

@@ -36,6 +36,7 @@ pub struct ChannelRef {
// information required for permission calculations // information required for permission calculations
pub recipients: Option<Vec<String>>, pub recipients: Option<Vec<String>>,
pub guild: Option<String>, pub guild: Option<String>,
pub owner: Option<String>,
} }
impl ChannelRef { impl ChannelRef {

View File

@@ -7,7 +7,7 @@ use bson::{bson, doc, from_bson, Bson::UtcDatetime};
use chrono::prelude::*; use chrono::prelude::*;
use database::user::User; use database::user::User;
use rand::{distributions::Alphanumeric, Rng}; use rand::{distributions::Alphanumeric, Rng};
use rocket_contrib::json::{Json, JsonValue}; use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ulid::Ulid; use ulid::Ulid;
use validator::validate_email; use validator::validate_email;
@@ -167,7 +167,7 @@ pub fn resend_email(info: Json<Resend>) -> Response {
doc! { "email_verification.target": info.email.clone() }, doc! { "email_verification.target": info.email.clone() },
None, None,
) )
.expect("Failed user lookup") .expect("Failed user lookup.")
{ {
let user: User = from_bson(bson::Bson::Document(u)).expect("Failed to unwrap user."); let user: User = from_bson(bson::Bson::Document(u)).expect("Failed to unwrap user.");
let ev = user.email_verification; let ev = user.email_verification;

View File

@@ -1,15 +1,21 @@
use super::Response; use super::Response;
use crate::database::{self, message::Message, Permission, PermissionCalculator}; use crate::database::{
self, get_relationship_internal, message::Message, Permission, PermissionCalculator,
Relationship,
};
use crate::guards::auth::UserRef; use crate::guards::auth::UserRef;
use crate::guards::channel::ChannelRef; use crate::guards::channel::ChannelRef;
use bson::{bson, doc, from_bson, Bson::UtcDatetime}; use bson::{bson, doc, from_bson, Bson, Bson::UtcDatetime};
use chrono::prelude::*; use chrono::prelude::*;
use hashbrown::HashSet;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ulid::Ulid; use ulid::Ulid;
const MAXGROUPSIZE: usize = 50;
#[derive(Debug, TryFromPrimitive)] #[derive(Debug, TryFromPrimitive)]
#[repr(usize)] #[repr(usize)]
pub enum ChannelType { pub enum ChannelType {
@@ -32,6 +38,71 @@ macro_rules! with_permissions {
}}; }};
} }
#[derive(Serialize, Deserialize)]
pub struct CreateGroup {
name: String,
nonce: String,
users: Vec<String>,
}
/// create a new group
#[post("/create", data = "<info>")]
pub fn create_group(user: UserRef, info: Json<CreateGroup>) -> Response {
let name: String = info.name.chars().take(32).collect();
let nonce: String = info.nonce.chars().take(32).collect();
let mut set = HashSet::new();
set.insert(user.id.clone());
for item in &info.users {
set.insert(item.clone());
}
if set.len() > MAXGROUPSIZE {
return Response::BadRequest(json!({ "error": "Maximum group size is 50." }));
}
let col = database::get_collection("channels");
if let Some(_) = col.find_one(doc! { "nonce": nonce.clone() }, None).unwrap() {
return Response::BadRequest(json!({ "error": "Group already created!" }));
}
let relationships = user.fetch_relationships();
let mut users = vec![];
for item in set {
if let Some(target) = UserRef::from(item) {
if get_relationship_internal(&user.id, &target.id, &relationships)
!= Relationship::Friend
{
return Response::BadRequest(json!({ "error": "Not friends with user(s)." }));
}
users.push(Bson::String(target.id));
} else {
return Response::BadRequest(json!({ "error": "Specified non-existant user(s)." }));
}
}
let id = Ulid::new().to_string();
if col
.insert_one(
doc! {
"_id": id.clone(),
"nonce": nonce,
"type": ChannelType::GROUPDM as u32,
"recipients": users,
"name": name,
},
None,
)
.is_ok()
{
Response::Success(json!({ "id": id }))
} else {
Response::InternalServerError(json!({ "error": "Failed to create guild channel." }))
}
}
/// fetch channel information /// fetch channel information
#[get("/<target>")] #[get("/<target>")]
pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> { pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> {
@@ -71,20 +142,25 @@ pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> {
let permissions = with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_manage_channels() { if !permissions.get_manage_channels() {
return Some(Response::LackingPermission(Permission::MANAGE_CHANNELS)); return Some(Response::LackingPermission(Permission::ManageChannels));
} }
let col = database::get_collection("channels"); let col = database::get_collection("channels");
match target.channel_type { match target.channel_type {
0 => { 0 => {
if col.update_one( if col
doc! { "_id": target.id }, .update_one(
doc! { "$set": { "active": false } }, doc! { "_id": target.id },
None, doc! { "$set": { "active": false } },
).is_ok() { None,
)
.is_ok()
{
Some(Response::Result(super::Status::Ok)) Some(Response::Result(super::Status::Ok))
} else { } else {
Some(Response::InternalServerError(json!({ "error": "Failed to close channel." }))) Some(Response::InternalServerError(
json!({ "error": "Failed to close channel." }),
))
} }
} }
1 => { 1 => {
@@ -109,7 +185,7 @@ pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> {
let permissions = with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_read_messages() { if !permissions.get_read_messages() {
return Some(Response::LackingPermission(Permission::READ_MESSAGES)); return Some(Response::LackingPermission(Permission::ReadMessages));
} }
let col = database::get_collection("messages"); let col = database::get_collection("messages");
@@ -146,7 +222,7 @@ pub fn send_message(
let permissions = with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_send_messages() { if !permissions.get_send_messages() {
return Some(Response::LackingPermission(Permission::SEND_MESSAGES)); return Some(Response::LackingPermission(Permission::SendMessages));
} }
let content: String = message.content.chars().take(2000).collect(); let content: String = message.content.chars().take(2000).collect();
@@ -214,7 +290,7 @@ pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Optio
let permissions = with_permissions!(user, target); let permissions = with_permissions!(user, target);
if !permissions.get_read_messages() { if !permissions.get_read_messages() {
return Some(Response::LackingPermission(Permission::READ_MESSAGES)); return Some(Response::LackingPermission(Permission::ReadMessages));
} }
let prev = let prev =
@@ -317,7 +393,7 @@ pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Op
if !permissions.get_manage_messages() { if !permissions.get_manage_messages() {
if message.author != user.id { if message.author != user.id {
return Some(Response::LackingPermission(Permission::MANAGE_MESSAGES)); return Some(Response::LackingPermission(Permission::ManageMessages));
} }
} }

View File

@@ -3,12 +3,11 @@ use crate::database::{
self, self,
channel::Channel, channel::Channel,
guild::{find_member_permissions, Guild}, guild::{find_member_permissions, Guild},
user::User,
}; };
use crate::guards::auth::UserRef; use crate::guards::auth::UserRef;
use bson::{bson, doc, from_bson, Bson}; use bson::{bson, doc, from_bson, Bson};
use rocket_contrib::json::{Json, JsonValue}; use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use ulid::Ulid; use ulid::Ulid;
@@ -126,7 +125,7 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response {
let id = Ulid::new().to_string(); let id = Ulid::new().to_string();
let channel_id = Ulid::new().to_string(); let channel_id = Ulid::new().to_string();
if let Err(_) = channels.insert_one( if channels.insert_one(
doc! { doc! {
"_id": channel_id.clone(), "_id": channel_id.clone(),
"type": ChannelType::GUILDCHANNEL as u32, "type": ChannelType::GUILDCHANNEL as u32,
@@ -134,7 +133,7 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response {
"guild": id.clone(), "guild": id.clone(),
}, },
None, None,
) { ).is_err() {
return Response::InternalServerError( return Response::InternalServerError(
json!({ "error": "Failed to create guild channel." }), json!({ "error": "Failed to create guild channel." }),
); );

View File

@@ -87,6 +87,7 @@ pub fn mount(rocket: Rocket) -> Rocket {
.mount( .mount(
"/api/channels", "/api/channels",
routes![ routes![
channel::create_group,
channel::channel, channel::channel,
channel::delete, channel::delete,
channel::messages, channel::messages,

View File

@@ -64,7 +64,7 @@ pub fn lookup(user: UserRef, query: Json<Query>) -> Response {
if let Ok(doc) = item { if let Ok(doc) = item {
let id = doc.get_str("id").unwrap(); let id = doc.get_str("id").unwrap();
results.push(json!({ results.push(json!({
"id": id.clone(), "id": id,
"username": doc.get_str("username").unwrap(), "username": doc.get_str("username").unwrap(),
"relationship": get_relationship_internal(&user.id, &id, &relationships) as u8 "relationship": get_relationship_internal(&user.id, &id, &relationships) as u8
})); }));
@@ -174,11 +174,11 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
let col = database::get_collection("users"); let col = database::get_collection("users");
match relationship { match relationship {
Relationship::FRIEND => Response::BadRequest(json!({ "error": "Already friends." })), Relationship::Friend => Response::BadRequest(json!({ "error": "Already friends." })),
Relationship::OUTGOING => { Relationship::Outgoing => {
Response::BadRequest(json!({ "error": "Already sent a friend request." })) Response::BadRequest(json!({ "error": "Already sent a friend request." }))
} }
Relationship::INCOMING => { Relationship::Incoming => {
if col if col
.update_one( .update_one(
doc! { doc! {
@@ -187,7 +187,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
}, },
doc! { doc! {
"$set": { "$set": {
"relations.$.status": Relationship::FRIEND as i32 "relations.$.status": Relationship::Friend as i32
} }
}, },
None, None,
@@ -202,14 +202,14 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
}, },
doc! { doc! {
"$set": { "$set": {
"relations.$.status": Relationship::FRIEND as i32 "relations.$.status": Relationship::Friend as i32
} }
}, },
None, None,
) )
.is_ok() .is_ok()
{ {
Response::Success(json!({ "status": Relationship::FRIEND as u8 })) Response::Success(json!({ "status": Relationship::Friend as u8 }))
} else { } else {
Response::InternalServerError( Response::InternalServerError(
json!({ "error": "Failed to commit! Try re-adding them as a friend." }), json!({ "error": "Failed to commit! Try re-adding them as a friend." }),
@@ -221,10 +221,10 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
) )
} }
} }
Relationship::BLOCKED => { Relationship::Blocked => {
Response::BadRequest(json!({ "error": "You have blocked this person." })) Response::BadRequest(json!({ "error": "You have blocked this person." }))
} }
Relationship::BLOCKEDOTHER => { Relationship::BlockedOther => {
Response::Conflict(json!({ "error": "You have been blocked by this person." })) Response::Conflict(json!({ "error": "You have been blocked by this person." }))
} }
Relationship::NONE => { Relationship::NONE => {
@@ -237,7 +237,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
"$push": { "$push": {
"relations": { "relations": {
"id": target.id.clone(), "id": target.id.clone(),
"status": Relationship::OUTGOING as i32 "status": Relationship::Outgoing as i32
} }
} }
}, },
@@ -254,7 +254,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
"$push": { "$push": {
"relations": { "relations": {
"id": user.id, "id": user.id,
"status": Relationship::INCOMING as i32 "status": Relationship::Incoming as i32
} }
} }
}, },
@@ -262,7 +262,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response {
) )
.is_ok() .is_ok()
{ {
Response::Success(json!({ "status": Relationship::OUTGOING as u8 })) Response::Success(json!({ "status": Relationship::Outgoing as u8 }))
} else { } else {
Response::InternalServerError( Response::InternalServerError(
json!({ "error": "Failed to commit! Try re-adding them as a friend." }), json!({ "error": "Failed to commit! Try re-adding them as a friend." }),
@@ -287,7 +287,7 @@ pub fn remove_friend(user: UserRef, target: UserRef) -> Response {
let col = database::get_collection("users"); let col = database::get_collection("users");
match relationship { match relationship {
Relationship::FRIEND | Relationship::OUTGOING | Relationship::INCOMING => { Relationship::Friend | Relationship::Outgoing | Relationship::Incoming => {
if col if col
.update_one( .update_one(
doc! { doc! {
@@ -332,8 +332,8 @@ pub fn remove_friend(user: UserRef, target: UserRef) -> Response {
) )
} }
} }
Relationship::BLOCKED Relationship::Blocked
| Relationship::BLOCKEDOTHER | Relationship::BlockedOther
| Relationship::NONE | Relationship::NONE
| Relationship::SELF => Response::BadRequest(json!({ "error": "This has no effect." })), | Relationship::SELF => Response::BadRequest(json!({ "error": "This has no effect." })),
} }

View File

@@ -37,7 +37,7 @@ impl Handler for Server {
if let Value::String(packet_type) = &data["type"] { if let Value::String(packet_type) = &data["type"] {
match packet_type.as_str() { match packet_type.as_str() {
"authenticate" => { "authenticate" => {
if let Some(_) = self.id { if self.id.is_some() {
self.out.send( self.out.send(
json!({ json!({
"type": "authenticate", "type": "authenticate",
@@ -152,7 +152,7 @@ impl Handler for Server {
pub fn launch_server() { pub fn launch_server() {
unsafe { unsafe {
if let Err(_) = CLIENTS.set(RwLock::new(HashMap::new())) { if CLIENTS.set(RwLock::new(HashMap::new())).is_err() {
panic!("Failed to set CLIENTS map!"); panic!("Failed to set CLIENTS map!");
} }
} }