Merge branch 'main' into feat/livekit
This commit is contained in:
@@ -16,7 +16,7 @@ pub async fn fetch_public_bot(
|
||||
target: Reference,
|
||||
) -> Result<Json<PublicBot>> {
|
||||
let bot = db.fetch_bot(&target.id).await?;
|
||||
if !bot.public && user.map_or(true, |x| x.id != bot.owner) {
|
||||
if !bot.public && user.is_none_or(|x| x.id != bot.owner) {
|
||||
return Err(create_error!(NotFound));
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,8 @@ pub async fn message_send(
|
||||
// Create model user / members
|
||||
let model_user = user
|
||||
.clone()
|
||||
.into_known_static(revolt_presence::is_online(&user.id).await).await;
|
||||
.into_known_static(revolt_presence::is_online(&user.id).await)
|
||||
.await;
|
||||
|
||||
let model_member: Option<v0::Member> = query
|
||||
.member_ref()
|
||||
@@ -495,7 +496,7 @@ mod test {
|
||||
let (_, _, other_user) = harness.new_user().await;
|
||||
let (server, _) = harness.new_server(&user).await;
|
||||
let channel = harness.new_channel(&server).await;
|
||||
let (role_id, mut role) = harness
|
||||
let (role_id, _role) = harness
|
||||
.new_role(
|
||||
&server,
|
||||
1,
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use revolt_database::{events::client::EventV1, Database, Report, Snapshot, SnapshotContent, User};
|
||||
use revolt_models::v0::{ReportStatus, ReportedContent};
|
||||
use revolt_result::{create_error, Result};
|
||||
use serde::Deserialize;
|
||||
use ulid::Ulid;
|
||||
use validator::Validate;
|
||||
use revolt_database::{Database, User};
|
||||
use revolt_result::Result;
|
||||
|
||||
use rocket::{serde::json::Json, State};
|
||||
use rocket::State;
|
||||
|
||||
/// # Acknowledge Policy Changes
|
||||
///
|
||||
|
||||
@@ -153,7 +153,6 @@ pub async fn root() -> Result<Json<RevoltConfig>> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "FIXME: THIS TEST CAUSES cargo test TO SEG FAULT, I HAVE NO CLUE HOW")]
|
||||
mod test {
|
||||
use crate::rocket;
|
||||
use rocket::http::Status;
|
||||
|
||||
@@ -18,6 +18,7 @@ mod roles_create;
|
||||
mod roles_delete;
|
||||
mod roles_edit;
|
||||
mod roles_fetch;
|
||||
mod roles_edit_positions;
|
||||
mod server_ack;
|
||||
mod server_create;
|
||||
mod server_delete;
|
||||
@@ -47,6 +48,7 @@ pub fn routes() -> (Vec<Route>, OpenApi) {
|
||||
roles_delete::delete,
|
||||
permissions_set::set_role_permission,
|
||||
permissions_set_default::set_default_permissions,
|
||||
emoji_list::list_emoji
|
||||
emoji_list::list_emoji,
|
||||
roles_edit_positions::edit_role_ranks
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ use validator::Validate;
|
||||
///
|
||||
/// Edit a role by its id.
|
||||
#[openapi(tag = "Server Permissions")]
|
||||
#[patch("/<target>/roles/<role_id>", data = "<data>")]
|
||||
#[patch("/<target>/roles/<role_id>", data = "<data>", rank = 1)]
|
||||
pub async fn edit(
|
||||
db: &State<Database>,
|
||||
voice_client: &State<VoiceClient>,
|
||||
@@ -47,22 +47,14 @@ pub async fn edit(
|
||||
name,
|
||||
colour,
|
||||
hoist,
|
||||
rank,
|
||||
remove,
|
||||
..
|
||||
} = data;
|
||||
|
||||
// Prevent us from moving a role above other roles
|
||||
if let Some(rank) = &rank {
|
||||
if rank <= &member_rank {
|
||||
return Err(create_error!(NotElevated));
|
||||
}
|
||||
}
|
||||
|
||||
let partial = PartialRole {
|
||||
name,
|
||||
colour,
|
||||
hoist,
|
||||
rank,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
177
crates/delta/src/routes/servers/roles_edit_positions.rs
Normal file
177
crates/delta/src/routes/servers/roles_edit_positions.rs
Normal file
@@ -0,0 +1,177 @@
|
||||
use revolt_database::{
|
||||
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
||||
Database, User,
|
||||
};
|
||||
use revolt_models::v0;
|
||||
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
||||
use revolt_result::{create_error, Result};
|
||||
use rocket::{serde::json::Json, State};
|
||||
|
||||
/// # Edits server roles ranks
|
||||
///
|
||||
/// Edit's server role's ranks.
|
||||
#[openapi(tag = "Server Permissions")]
|
||||
#[patch("/<target>/roles/ranks", data = "<data>")]
|
||||
pub async fn edit_role_ranks(
|
||||
db: &State<Database>,
|
||||
user: User,
|
||||
target: Reference,
|
||||
data: Json<v0::DataEditRoleRanks>,
|
||||
) -> Result<Json<v0::Server>> {
|
||||
let data = data.into_inner();
|
||||
|
||||
let mut server = target.as_server(db).await?;
|
||||
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
||||
calculate_server_permissions(&mut query)
|
||||
.await
|
||||
.throw_if_lacking_channel_permission(ChannelPermission::ManageRole)?;
|
||||
|
||||
let existing_order = server
|
||||
.ordered_roles()
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let new_order = data.ranks.clone().into_iter().collect::<Vec<_>>();
|
||||
|
||||
// Verify all roles are in the new ordering
|
||||
if data.ranks.len() != server.roles.len()
|
||||
|| !server.roles.iter().all(|(id, _)| data.ranks.contains(id))
|
||||
{
|
||||
return Err(create_error!(InvalidOperation));
|
||||
}
|
||||
|
||||
// Don't have to check what the user can't modify if they are the server owner
|
||||
if server.owner != user.id {
|
||||
let member_top_rank = query.get_member_rank();
|
||||
|
||||
if server
|
||||
.roles
|
||||
.iter()
|
||||
// Find all roles above the member which we should not be able to reorder
|
||||
.filter(|(_, role)| {
|
||||
if let Some(top_rank) = member_top_rank {
|
||||
role.rank <= top_rank
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
// Check if user is trying to reorder roles they can't reorder (as found previously)
|
||||
.any(|(id, _)| {
|
||||
existing_order
|
||||
.iter()
|
||||
.position(|existing_id| id == existing_id)
|
||||
!= new_order.iter().position(|new_id| id == new_id)
|
||||
})
|
||||
{
|
||||
return Err(create_error!(NotElevated));
|
||||
}
|
||||
}
|
||||
|
||||
server.set_role_ordering(db, new_order).await?;
|
||||
|
||||
Ok(Json(server.into()))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use revolt_database::fixture;
|
||||
use revolt_models::v0;
|
||||
use rocket::http::{ContentType, Header, Status};
|
||||
|
||||
use crate::util::test::TestHarness;
|
||||
|
||||
#[rocket::async_test]
|
||||
async fn edit_role_rankings() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
fixture!(harness.db, "server_with_many_roles",
|
||||
owner user 0
|
||||
moderator user 1
|
||||
server server 4);
|
||||
|
||||
// Moderator can re-order the roles below them
|
||||
let (_, moderator_session) = harness.account_from_user(moderator.id).await;
|
||||
let mut target_order: Vec<String> = server
|
||||
.ordered_roles()
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect();
|
||||
|
||||
// Swap the two lower ranked roles
|
||||
target_order.swap(2, 3);
|
||||
|
||||
let response = harness
|
||||
.client
|
||||
.patch(format!("/servers/{}/roles/ranks", server.id))
|
||||
.header(ContentType::JSON)
|
||||
.body(
|
||||
json!(v0::DataEditRoleRanks {
|
||||
ranks: target_order.clone()
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.header(Header::new(
|
||||
"x-session-token",
|
||||
moderator_session.token.to_string(),
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
drop(response);
|
||||
|
||||
// ... but not above them
|
||||
let mut target_order: Vec<String> = server
|
||||
.ordered_roles()
|
||||
.into_iter()
|
||||
.map(|(id, _)| id)
|
||||
.collect();
|
||||
|
||||
// Swap the two lower ranked roles
|
||||
target_order.swap(0, 1);
|
||||
|
||||
let response = harness
|
||||
.client
|
||||
.patch(format!("/servers/{}/roles/ranks", server.id))
|
||||
.header(ContentType::JSON)
|
||||
.body(
|
||||
json!(v0::DataEditRoleRanks {
|
||||
ranks: target_order.clone()
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.header(Header::new(
|
||||
"x-session-token",
|
||||
moderator_session.token.to_string(),
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), Status::Forbidden);
|
||||
drop(response);
|
||||
|
||||
// The owner can set any order they want
|
||||
let (_, owner_session) = harness.account_from_user(owner.id).await;
|
||||
|
||||
let response = harness
|
||||
.client
|
||||
.patch(format!("/servers/{}/roles/ranks", server.id))
|
||||
.header(ContentType::JSON)
|
||||
.body(
|
||||
json!(v0::DataEditRoleRanks {
|
||||
ranks: target_order.clone()
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.header(Header::new(
|
||||
"x-session-token",
|
||||
owner_session.token.to_string(),
|
||||
))
|
||||
.dispatch()
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
drop(response);
|
||||
}
|
||||
}
|
||||
@@ -36,11 +36,11 @@ pub async fn webhook_execute(
|
||||
let permissions: PermissionValue = webhook.permissions.into();
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::SendMessage)?;
|
||||
|
||||
if data.attachments.as_ref().map_or(false, |v| !v.is_empty()) {
|
||||
if data.attachments.as_ref().is_some_and(|v| !v.is_empty()) {
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::UploadFiles)?;
|
||||
}
|
||||
|
||||
if data.embeds.as_ref().map_or(false, |v| !v.is_empty()) {
|
||||
if data.embeds.as_ref().is_some_and(|v| !v.is_empty()) {
|
||||
permissions.throw_if_lacking_channel_permission(ChannelPermission::SendEmbeds)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -621,7 +621,7 @@ pub struct Event {
|
||||
#[derive(Debug, JsonSchema)]
|
||||
pub struct EventHeader<'r>(pub &'r str);
|
||||
|
||||
impl<'r> std::ops::Deref for EventHeader<'r> {
|
||||
impl std::ops::Deref for EventHeader<'_> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
|
||||
@@ -109,8 +109,6 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r
|
||||
};
|
||||
|
||||
if let Some(segment) = segment {
|
||||
let resource = resource;
|
||||
|
||||
let method = request.method();
|
||||
match (segment, resource, method) {
|
||||
("users", target, Method::Patch) => ("user_edit", target),
|
||||
@@ -254,7 +252,7 @@ impl<'r> FromRequest<'r> for Ratelimiter {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> OpenApiFromRequest<'r> for Ratelimiter {
|
||||
impl OpenApiFromRequest<'_> for Ratelimiter {
|
||||
fn from_request_input(
|
||||
_gen: &mut OpenApiGenerator,
|
||||
_name: String,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use authifier::{
|
||||
models::{Account, Session},
|
||||
models::{Account, EmailVerification, Session},
|
||||
Authifier,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
@@ -83,30 +83,41 @@ impl TestHarness {
|
||||
}
|
||||
|
||||
pub async fn new_user(&self) -> (Account, Session, User) {
|
||||
let account = Account::new(
|
||||
&self.authifier,
|
||||
format!("{}@revolt.chat", TestHarness::rand_string()),
|
||||
"password".to_string(),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
.expect("`Account`");
|
||||
let user = User::create(&self.db, TestHarness::rand_string(), None, None)
|
||||
.await
|
||||
.expect("`User`");
|
||||
|
||||
let (account, session) = self.account_from_user(user.id.clone()).await;
|
||||
|
||||
(account, session, user)
|
||||
}
|
||||
|
||||
pub async fn account_from_user(&self, id: String) -> (Account, Session) {
|
||||
let account = Account {
|
||||
id,
|
||||
email: format!("{}@revolt.chat", TestHarness::rand_string()),
|
||||
password: Default::default(),
|
||||
email_normalised: Default::default(),
|
||||
deletion: None,
|
||||
disabled: false,
|
||||
lockout: None,
|
||||
mfa: Default::default(),
|
||||
password_reset: None,
|
||||
verification: EmailVerification::Verified,
|
||||
};
|
||||
|
||||
self.authifier
|
||||
.database
|
||||
.save_account(&account)
|
||||
.await
|
||||
.expect("`Account`");
|
||||
|
||||
let session = account
|
||||
.create_session(&self.authifier, String::new())
|
||||
.await
|
||||
.expect("`Session`");
|
||||
|
||||
let user = User::create(
|
||||
&self.db,
|
||||
TestHarness::rand_string(),
|
||||
account.id.to_string(),
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.expect("`User`");
|
||||
|
||||
(account, session, user)
|
||||
(account, session)
|
||||
}
|
||||
|
||||
pub async fn new_server(&self, user: &User) -> (Server, Vec<Channel>) {
|
||||
@@ -199,10 +210,7 @@ impl TestHarness {
|
||||
(channel.clone(), member, message)
|
||||
}
|
||||
|
||||
pub async fn with_session<'c>(
|
||||
session: Session,
|
||||
request: LocalRequest<'c>,
|
||||
) -> LocalResponse<'c> {
|
||||
pub async fn with_session(session: Session, request: LocalRequest<'_>) -> LocalResponse<'_> {
|
||||
request
|
||||
.header(Header::new("x-session-token", session.token.to_string()))
|
||||
.dispatch()
|
||||
|
||||
Reference in New Issue
Block a user