forked from jmug/stoatchat
Re-structure Permissions; add perm to view users from mutual groups.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use crate::database::*;
|
||||
use crate::notifications::events::ClientboundNotification;
|
||||
use crate::util::result::{Error, Result};
|
||||
use mongodb::bson::{doc, to_document};
|
||||
use mongodb::bson::{doc, from_document, to_document};
|
||||
use rocket_contrib::json::JsonValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -52,6 +52,20 @@ impl Channel {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(id: &str) -> Result<Channel> {
|
||||
let doc = get_collection("channels")
|
||||
.find_one(
|
||||
doc! { "_id": id },
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError { operation: "find_one", with: "channel" })?
|
||||
.ok_or_else(|| Error::UnknownChannel)?;
|
||||
|
||||
from_document::<Channel>(doc)
|
||||
.map_err(|_| Error::DatabaseError { operation: "from_document", with: "channel" })
|
||||
}
|
||||
|
||||
pub async fn publish(self) -> Result<()> {
|
||||
get_collection("channels")
|
||||
.insert_one(
|
||||
|
||||
@@ -44,7 +44,7 @@ impl Message {
|
||||
with: "message",
|
||||
})?;
|
||||
|
||||
// ! temp code
|
||||
// ! FIXME: temp code
|
||||
let channels = get_collection("channels");
|
||||
match channel {
|
||||
Channel::DirectMessage { id, .. } => {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
use crate::database::*;
|
||||
use crate::util::result::Result;
|
||||
|
||||
use super::PermissionCalculator;
|
||||
|
||||
use num_enum::TryFromPrimitive;
|
||||
use std::ops;
|
||||
|
||||
@@ -21,40 +25,90 @@ bitfield! {
|
||||
impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 });
|
||||
impl_op_ex_commutative!(+ |a: &u32, b: &ChannelPermission| -> u32 { *a | *b as u32 });
|
||||
|
||||
pub async fn calculate(user: &User, target: &Channel) -> ChannelPermissions<[u32; 1]> {
|
||||
/*pub async fn calculate(user: &User, target: &Channel) -> Result<u32> {
|
||||
match target {
|
||||
Channel::SavedMessages { user: owner, .. } => {
|
||||
if &user.id == owner {
|
||||
ChannelPermissions([ChannelPermission::View
|
||||
Ok(ChannelPermission::View
|
||||
+ ChannelPermission::SendMessage
|
||||
+ ChannelPermission::ManageMessages])
|
||||
+ ChannelPermission::ManageMessages)
|
||||
} else {
|
||||
ChannelPermissions([0])
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
Channel::DirectMessage { recipients, .. } => {
|
||||
if recipients.iter().find(|x| *x == &user.id).is_some() {
|
||||
if let Some(recipient) = recipients.iter().find(|x| *x != &user.id) {
|
||||
let perms = super::user::calculate(&user, recipient).await;
|
||||
let perms = super::user::get(&user, recipient).await?;
|
||||
|
||||
if perms.get_send_message() {
|
||||
return ChannelPermissions([
|
||||
ChannelPermission::View + ChannelPermission::SendMessage
|
||||
]);
|
||||
return Ok(ChannelPermission::View + ChannelPermission::SendMessage);
|
||||
}
|
||||
|
||||
return ChannelPermissions([ChannelPermission::View as u32]);
|
||||
return Ok(ChannelPermission::View as u32);
|
||||
}
|
||||
}
|
||||
|
||||
ChannelPermissions([0])
|
||||
Ok(0)
|
||||
}
|
||||
Channel::Group { recipients, .. } => {
|
||||
if recipients.iter().find(|x| *x == &user.id).is_some() {
|
||||
ChannelPermissions([ChannelPermission::View + ChannelPermission::SendMessage])
|
||||
Ok(ChannelPermission::View + ChannelPermission::SendMessage)
|
||||
} else {
|
||||
ChannelPermissions([0])
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(user: &User, target: &Channel) -> Result<ChannelPermissions<[u32; 1]>> {
|
||||
Ok(ChannelPermissions([calculate(&user, &target).await?]))
|
||||
}*/
|
||||
|
||||
impl<'a> PermissionCalculator<'a> {
|
||||
pub async fn calculate_channel(self) -> Result<u32> {
|
||||
let channel = if let Some(channel) = self.channel {
|
||||
channel
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
match channel {
|
||||
Channel::SavedMessages { user: owner, .. } => {
|
||||
if &self.perspective.id == owner {
|
||||
Ok(ChannelPermission::View
|
||||
+ ChannelPermission::SendMessage
|
||||
+ ChannelPermission::ManageMessages)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
Channel::DirectMessage { recipients, .. } => {
|
||||
if recipients.iter().find(|x| *x == &self.perspective.id).is_some() {
|
||||
if let Some(recipient) = recipients.iter().find(|x| *x != &self.perspective.id) {
|
||||
let perms = self.for_user(recipient).await?;
|
||||
|
||||
if perms.get_send_message() {
|
||||
return Ok(ChannelPermission::View + ChannelPermission::SendMessage);
|
||||
}
|
||||
|
||||
return Ok(ChannelPermission::View as u32);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
Channel::Group { recipients, .. } => {
|
||||
if recipients.iter().find(|x| *x == &self.perspective.id).is_some() {
|
||||
Ok(ChannelPermission::View + ChannelPermission::SendMessage)
|
||||
} else {
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn for_channel(self) -> Result<ChannelPermissions<[u32; 1]>> {
|
||||
Ok(ChannelPermissions([ self.calculate_channel().await? ]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,38 @@
|
||||
pub use crate::database::*;
|
||||
|
||||
pub mod channel;
|
||||
pub mod user;
|
||||
|
||||
pub use user::get_relationship;
|
||||
|
||||
pub struct PermissionCalculator<'a> {
|
||||
perspective: &'a User,
|
||||
|
||||
user: Option<&'a User>,
|
||||
channel: Option<&'a Channel>,
|
||||
}
|
||||
|
||||
impl<'a> PermissionCalculator<'a> {
|
||||
pub fn new(perspective: &'a User) -> PermissionCalculator {
|
||||
PermissionCalculator {
|
||||
perspective,
|
||||
|
||||
user: None,
|
||||
channel: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_user(self, user: &'a User) -> PermissionCalculator {
|
||||
PermissionCalculator {
|
||||
user: Some(&user),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_channel(self, channel: &'a Channel) -> PermissionCalculator {
|
||||
PermissionCalculator {
|
||||
channel: Some(&channel),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,33 @@
|
||||
use crate::database::*;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use crate::util::result::{Error, Result};
|
||||
|
||||
use super::PermissionCalculator;
|
||||
|
||||
use std::ops;
|
||||
use mongodb::bson::doc;
|
||||
use num_enum::TryFromPrimitive;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
|
||||
#[repr(u32)]
|
||||
pub enum UserPermission {
|
||||
Access = 1,
|
||||
SendMessage = 2,
|
||||
Invite = 4,
|
||||
ViewProfile = 2,
|
||||
SendMessage = 4,
|
||||
Invite = 8
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
pub struct UserPermissions(MSB0 [u32]);
|
||||
u32;
|
||||
pub get_access, _: 31;
|
||||
pub get_send_message, _: 30;
|
||||
pub get_invite, _: 29;
|
||||
pub get_view_profile, _: 30;
|
||||
pub get_send_message, _: 29;
|
||||
pub get_invite, _: 28;
|
||||
}
|
||||
|
||||
impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 });
|
||||
impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 });
|
||||
|
||||
pub async fn calculate(user: &User, target: &str) -> UserPermissions<[u32; 1]> {
|
||||
// if friends; Access + Message + Invite
|
||||
// if mutually know each other:
|
||||
// and has DMs from users enabled -> Access + Message
|
||||
// otherwise -> Access
|
||||
// otherwise; None
|
||||
|
||||
let mut permissions: u32 = 0;
|
||||
match get_relationship(&user, &target) {
|
||||
RelationshipStatus::Friend => {
|
||||
return UserPermissions([UserPermission::Access
|
||||
+ UserPermission::SendMessage
|
||||
+ UserPermission::Invite])
|
||||
}
|
||||
RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
|
||||
return UserPermissions([UserPermission::Access as u32])
|
||||
}
|
||||
RelationshipStatus::Incoming | RelationshipStatus::Outgoing => {
|
||||
permissions = UserPermission::Access as u32;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
UserPermissions([permissions])
|
||||
}
|
||||
|
||||
pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus {
|
||||
if a.id == b {
|
||||
return RelationshipStatus::Friend;
|
||||
@@ -60,3 +41,53 @@ pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus {
|
||||
|
||||
RelationshipStatus::None
|
||||
}
|
||||
|
||||
impl<'a> PermissionCalculator<'a> {
|
||||
pub async fn calculate_user(self, target: &str) -> Result<u32> {
|
||||
let mut permissions: u32 = 0;
|
||||
match get_relationship(&self.perspective, &target) {
|
||||
RelationshipStatus::Friend => {
|
||||
return Ok(u32::MAX)
|
||||
}
|
||||
RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
|
||||
return Ok(UserPermission::Access as u32)
|
||||
}
|
||||
RelationshipStatus::Incoming | RelationshipStatus::Outgoing => {
|
||||
permissions = UserPermission::Access as u32;
|
||||
// ! INFO: if we add boolean switch for permission to
|
||||
// ! message people who have mutual, we need to get
|
||||
// ! rid of this return statement.
|
||||
return Ok(permissions);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if get_collection("channels")
|
||||
.find_one(
|
||||
doc! {
|
||||
"type": "Group",
|
||||
"$and": {
|
||||
"recipients": &self.perspective.id,
|
||||
"recipients": target
|
||||
}
|
||||
},
|
||||
None
|
||||
)
|
||||
.await
|
||||
.map_err(|_| Error::DatabaseError { operation: "find", with: "channels" })?
|
||||
.is_some() {
|
||||
return Ok(UserPermission::Access as u32);
|
||||
}
|
||||
|
||||
Ok(permissions)
|
||||
}
|
||||
|
||||
pub async fn for_user(self, target: &str) -> Result<UserPermissions<[u32; 1]>> {
|
||||
Ok(UserPermissions([ self.calculate_user(&target).await? ]))
|
||||
}
|
||||
|
||||
pub async fn for_user_given(self) -> Result<UserPermissions<[u32; 1]>> {
|
||||
let id = &self.user.unwrap().id;
|
||||
Ok(UserPermissions([ self.calculate_user(&id).await? ]))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use mongodb::bson::doc;
|
||||
pub async fn req(user: User, target: Ref) -> Result<()> {
|
||||
let target = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &target).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&target)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use rocket_contrib::json::JsonValue;
|
||||
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
||||
let target = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &target).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&target)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
|
||||
}
|
||||
|
||||
let channel = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &channel).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&channel)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use mongodb::bson::doc;
|
||||
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
|
||||
let channel = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &channel).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&channel)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result<
|
||||
.map_err(|error| Error::FailedValidation { error })?;
|
||||
|
||||
let channel = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &channel).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&channel)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use rocket_contrib::json::JsonValue;
|
||||
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> {
|
||||
let channel = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &channel).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&channel)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
|
||||
|
||||
let target = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &target).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&target)
|
||||
.for_channel().await?;
|
||||
if !perm.get_view() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
|
||||
|
||||
let target = target.fetch_channel().await?;
|
||||
|
||||
let perm = permissions::channel::calculate(&user, &target).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_channel(&target)
|
||||
.for_channel().await?;
|
||||
if !perm.get_send_message() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
|
||||
|
||||
if user.id != target.id {
|
||||
// Check whether we are allowed to fetch this user.
|
||||
let perm = permissions::user::calculate(&user, &target.id).await;
|
||||
let perm = permissions::PermissionCalculator::new(&user)
|
||||
.with_user(&target)
|
||||
.for_user_given().await?;
|
||||
if !perm.get_access() {
|
||||
Err(Error::LabelMe)?
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ pub enum Error {
|
||||
NotFriends,
|
||||
|
||||
// ? Channel related errors.
|
||||
#[snafu(display("This channel does not exist!"))]
|
||||
UnknownChannel,
|
||||
#[snafu(display("Cannot edit someone else's message."))]
|
||||
CannotEditMessage,
|
||||
#[snafu(display("Cannot remove yourself from a group, use delete channel instead."))]
|
||||
@@ -81,6 +83,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
||||
Error::BlockedByOther => Status::Forbidden,
|
||||
Error::NotFriends => Status::Forbidden,
|
||||
|
||||
Error::UnknownChannel => Status::NotFound,
|
||||
Error::CannotEditMessage => Status::Forbidden,
|
||||
Error::CannotRemoveYourself => Status::BadRequest,
|
||||
Error::GroupTooLarge { .. } => Status::Forbidden,
|
||||
|
||||
Reference in New Issue
Block a user