* feat: add rename endpoint with rename-only update Signed-off-by: Gabriel <60961939+gabrielfordevelopment@users.noreply.github.com> * fix: enforce detached emoji edit restrictions Signed-off-by: Gabriel <60961939+gabrielfordevelopment@users.noreply.github.com> * fix: always enforce emoji edit permissions Signed-off-by: Gabriel <60961939+gabrielfordevelopment@users.noreply.github.com> --------- Signed-off-by: Gabriel <60961939+gabrielfordevelopment@users.noreply.github.com>
213 lines
6.6 KiB
Rust
213 lines
6.6 KiB
Rust
use revolt_database::{
|
|
util::{permissions::DatabasePermissionQuery, reference::Reference},
|
|
Database, EmojiParent, PartialEmoji, User,
|
|
};
|
|
use revolt_models::v0;
|
|
use revolt_permissions::{calculate_server_permissions, ChannelPermission};
|
|
use revolt_result::{create_error, Result};
|
|
use rocket::{serde::json::Json, State};
|
|
use validator::Validate;
|
|
|
|
/// # Edit Emoji
|
|
///
|
|
/// Edit an emoji by its id.
|
|
#[openapi(tag = "Emojis")]
|
|
#[patch("/emoji/<emoji_id>", data = "<data>")]
|
|
pub async fn edit_emoji(
|
|
db: &State<Database>,
|
|
user: User,
|
|
emoji_id: Reference<'_>,
|
|
data: Json<v0::DataEditEmoji>,
|
|
) -> Result<Json<v0::Emoji>> {
|
|
let data = data.into_inner();
|
|
data.validate().map_err(|error| {
|
|
create_error!(FailedValidation {
|
|
error: error.to_string()
|
|
})
|
|
})?;
|
|
|
|
let mut emoji = emoji_id.as_emoji(db).await?;
|
|
|
|
match &emoji.parent {
|
|
EmojiParent::Server { id } => {
|
|
let server = db.fetch_server(id.as_str()).await?;
|
|
|
|
let mut query = DatabasePermissionQuery::new(db, &user).server(&server);
|
|
calculate_server_permissions(&mut query)
|
|
.await
|
|
.throw_if_lacking_channel_permission(ChannelPermission::ManageCustomisation)?;
|
|
}
|
|
EmojiParent::Detached => return Err(create_error!(NotAuthenticated)),
|
|
}
|
|
|
|
if data.name.is_none() {
|
|
return Ok(Json(emoji.into()));
|
|
}
|
|
|
|
let partial = PartialEmoji { name: data.name };
|
|
emoji.update(db, partial).await?;
|
|
|
|
Ok(Json(emoji.into()))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use crate::util::test::TestHarness;
|
|
use revolt_database::{Emoji, EmojiParent, Member};
|
|
use revolt_models::v0;
|
|
use rocket::http::{ContentType, Header, Status};
|
|
use ulid::Ulid;
|
|
|
|
#[rocket::async_test]
|
|
async fn edit_emoji_name_as_creator() {
|
|
let harness = TestHarness::new().await;
|
|
let (_, session, user) = harness.new_user().await;
|
|
let (server, _) = harness.new_server(&user).await;
|
|
|
|
let emoji_id = Ulid::new().to_string();
|
|
let emoji = Emoji {
|
|
id: emoji_id.clone(),
|
|
parent: EmojiParent::Server {
|
|
id: server.id.clone(),
|
|
},
|
|
creator_id: user.id.clone(),
|
|
name: "initial_name".to_string(),
|
|
animated: false,
|
|
nsfw: false,
|
|
};
|
|
emoji.create(&harness.db).await.expect("`Emoji` created");
|
|
|
|
let response = harness
|
|
.client
|
|
.patch(format!("/custom/emoji/{emoji_id}"))
|
|
.header(Header::new("x-session-token", session.token.to_string()))
|
|
.header(ContentType::JSON)
|
|
.body(
|
|
json!(v0::DataEditEmoji {
|
|
name: Some("renamed_emoji".to_string()),
|
|
})
|
|
.to_string(),
|
|
)
|
|
.dispatch()
|
|
.await;
|
|
|
|
assert_eq!(response.status(), Status::Ok);
|
|
|
|
let edited: v0::Emoji = response.into_json().await.expect("`Emoji`");
|
|
assert_eq!(edited.name, "renamed_emoji");
|
|
}
|
|
|
|
#[rocket::async_test]
|
|
async fn reject_invalid_emoji_name() {
|
|
let harness = TestHarness::new().await;
|
|
let (_, session, user) = harness.new_user().await;
|
|
let (server, _) = harness.new_server(&user).await;
|
|
|
|
let emoji_id = Ulid::new().to_string();
|
|
let emoji = Emoji {
|
|
id: emoji_id.clone(),
|
|
parent: EmojiParent::Server {
|
|
id: server.id.clone(),
|
|
},
|
|
creator_id: user.id.clone(),
|
|
name: "valid_name".to_string(),
|
|
animated: false,
|
|
nsfw: false,
|
|
};
|
|
emoji.create(&harness.db).await.expect("`Emoji` created");
|
|
|
|
let response = harness
|
|
.client
|
|
.patch(format!("/custom/emoji/{emoji_id}"))
|
|
.header(Header::new("x-session-token", session.token.to_string()))
|
|
.header(ContentType::JSON)
|
|
.body(
|
|
json!(v0::DataEditEmoji {
|
|
name: Some("Invalid Name".to_string()),
|
|
})
|
|
.to_string(),
|
|
)
|
|
.dispatch()
|
|
.await;
|
|
|
|
assert_eq!(response.status(), Status::BadRequest);
|
|
}
|
|
|
|
#[rocket::async_test]
|
|
async fn reject_edit_for_detached_emoji() {
|
|
let harness = TestHarness::new().await;
|
|
let (_, session, user) = harness.new_user().await;
|
|
|
|
let emoji_id = Ulid::new().to_string();
|
|
let emoji = Emoji {
|
|
id: emoji_id.clone(),
|
|
parent: EmojiParent::Detached,
|
|
creator_id: user.id.clone(),
|
|
name: "detached_name".to_string(),
|
|
animated: false,
|
|
nsfw: false,
|
|
};
|
|
emoji.create(&harness.db).await.expect("`Emoji` created");
|
|
|
|
let response = harness
|
|
.client
|
|
.patch(format!("/custom/emoji/{emoji_id}"))
|
|
.header(Header::new("x-session-token", session.token.to_string()))
|
|
.header(ContentType::JSON)
|
|
.body(
|
|
json!(v0::DataEditEmoji {
|
|
name: Some("should_not_apply".to_string()),
|
|
})
|
|
.to_string(),
|
|
)
|
|
.dispatch()
|
|
.await;
|
|
|
|
assert_eq!(response.status(), Status::Unauthorized);
|
|
}
|
|
|
|
#[rocket::async_test]
|
|
async fn reject_edit_for_creator_without_manage_customisation() {
|
|
let harness = TestHarness::new().await;
|
|
let (_, _, owner) = harness.new_user().await;
|
|
let (_, creator_session, creator) = harness.new_user().await;
|
|
let (server, _) = harness.new_server(&owner).await;
|
|
|
|
Member::create(&harness.db, &server, &creator, None)
|
|
.await
|
|
.expect("`Member` created");
|
|
|
|
let emoji_id = Ulid::new().to_string();
|
|
let emoji = Emoji {
|
|
id: emoji_id.clone(),
|
|
parent: EmojiParent::Server {
|
|
id: server.id.clone(),
|
|
},
|
|
creator_id: creator.id.clone(),
|
|
name: "member_uploaded_name".to_string(),
|
|
animated: false,
|
|
nsfw: false,
|
|
};
|
|
emoji.create(&harness.db).await.expect("`Emoji` created");
|
|
|
|
let response = harness
|
|
.client
|
|
.patch(format!("/custom/emoji/{emoji_id}"))
|
|
.header(Header::new(
|
|
"x-session-token",
|
|
creator_session.token.to_string(),
|
|
))
|
|
.header(ContentType::JSON)
|
|
.body(
|
|
json!(v0::DataEditEmoji {
|
|
name: Some("renamed_without_permission".to_string()),
|
|
})
|
|
.to_string(),
|
|
)
|
|
.dispatch()
|
|
.await;
|
|
|
|
assert_eq!(response.status(), Status::Forbidden);
|
|
}
|
|
}
|