You've already forked AstralRinth
forked from didirus/AstralRinth
Move descriptions to database, switch to SHA-512 hashes, fix declining invites not working, allow user deletion, fix broken permission checks for many things, security fixes
This commit is contained in:
@@ -48,6 +48,8 @@ pub enum CreateError {
|
||||
InvalidFileType(String),
|
||||
#[error("Authentication Error: {0}")]
|
||||
Unauthorized(#[from] AuthenticationError),
|
||||
#[error("Authentication Error: {0}")]
|
||||
CustomAuthenticationError(String),
|
||||
}
|
||||
|
||||
impl actix_web::ResponseError for CreateError {
|
||||
@@ -68,6 +70,7 @@ impl actix_web::ResponseError for CreateError {
|
||||
CreateError::InvalidCategory(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::InvalidFileType(..) => StatusCode::BAD_REQUEST,
|
||||
CreateError::Unauthorized(..) => StatusCode::UNAUTHORIZED,
|
||||
CreateError::CustomAuthenticationError(..) => StatusCode::UNAUTHORIZED,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +92,7 @@ impl actix_web::ResponseError for CreateError {
|
||||
CreateError::InvalidCategory(..) => "invalid_input",
|
||||
CreateError::InvalidFileType(..) => "invalid_input",
|
||||
CreateError::Unauthorized(..) => "unauthorized",
|
||||
CreateError::CustomAuthenticationError(..) => "unauthorized",
|
||||
},
|
||||
description: &self.to_string(),
|
||||
})
|
||||
@@ -334,18 +338,8 @@ async fn mod_create_inner(
|
||||
)));
|
||||
}
|
||||
}
|
||||
versions.push(
|
||||
create_initial_version(
|
||||
data,
|
||||
mod_id,
|
||||
current_user.id,
|
||||
&cdn_url,
|
||||
transaction,
|
||||
file_host,
|
||||
uploaded_files,
|
||||
)
|
||||
.await?,
|
||||
);
|
||||
versions
|
||||
.push(create_initial_version(data, mod_id, current_user.id, transaction).await?);
|
||||
}
|
||||
|
||||
mod_create_data = create_data;
|
||||
@@ -436,24 +430,6 @@ async fn mod_create_inner(
|
||||
categories.push(id);
|
||||
}
|
||||
|
||||
// Upload the mod desciption markdown to the CDN
|
||||
// TODO: Should we also process and upload an html version here for SSR?
|
||||
let body_path = format!("data/{}/description.md", mod_id);
|
||||
{
|
||||
let upload_data = file_host
|
||||
.upload_file(
|
||||
"text/plain",
|
||||
&body_path,
|
||||
mod_create_data.mod_body.into_bytes(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
uploaded_files.push(UploadedFile {
|
||||
file_id: upload_data.file_id,
|
||||
file_name: upload_data.file_name,
|
||||
});
|
||||
}
|
||||
|
||||
let team = models::team_item::TeamBuilder {
|
||||
members: vec![models::team_item::TeamMemberBuilder {
|
||||
user_id: current_user.id.into(),
|
||||
@@ -527,7 +503,7 @@ async fn mod_create_inner(
|
||||
team_id,
|
||||
title: mod_create_data.mod_name,
|
||||
description: mod_create_data.mod_description,
|
||||
body_url: format!("{}/{}", cdn_url, body_path),
|
||||
body: mod_create_data.mod_body,
|
||||
icon_url,
|
||||
issues_url: mod_create_data.issues_url,
|
||||
source_url: mod_create_data.source_url,
|
||||
@@ -553,7 +529,8 @@ async fn mod_create_inner(
|
||||
team: team_id.into(),
|
||||
title: mod_builder.title.clone(),
|
||||
description: mod_builder.description.clone(),
|
||||
body_url: mod_builder.body_url.clone(),
|
||||
body: mod_builder.body.clone(),
|
||||
body_url: None,
|
||||
published: now,
|
||||
updated: now,
|
||||
status: status.clone(),
|
||||
@@ -596,10 +573,7 @@ async fn create_initial_version(
|
||||
version_data: &InitialVersionData,
|
||||
mod_id: ModId,
|
||||
author: UserId,
|
||||
cdn_url: &str,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
file_host: &dyn FileHost,
|
||||
uploaded_files: &mut Vec<UploadedFile>,
|
||||
) -> Result<models::version_item::VersionBuilder, CreateError> {
|
||||
if version_data.mod_id.is_some() {
|
||||
return Err(CreateError::InvalidInput(String::from(
|
||||
@@ -613,30 +587,6 @@ async fn create_initial_version(
|
||||
// Randomly generate a new id to be used for the version
|
||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||
|
||||
// Upload the version's changelog to the CDN
|
||||
let changelog_path = if let Some(changelog) = &version_data.version_body {
|
||||
let changelog_path = format!(
|
||||
"data/{}/versions/{}/changelog.md",
|
||||
mod_id, version_data.version_number
|
||||
);
|
||||
|
||||
let uploaded_text = file_host
|
||||
.upload_file(
|
||||
"text/plain",
|
||||
&changelog_path,
|
||||
changelog.clone().into_bytes(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
uploaded_files.push(UploadedFile {
|
||||
file_id: uploaded_text.file_id,
|
||||
file_name: uploaded_text.file_name,
|
||||
});
|
||||
Some(changelog_path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let release_channel =
|
||||
models::ChannelId::get_id(version_data.release_channel.as_str(), &mut *transaction)
|
||||
.await?
|
||||
@@ -670,7 +620,10 @@ async fn create_initial_version(
|
||||
author_id: author.into(),
|
||||
name: version_data.version_title.clone(),
|
||||
version_number: version_data.version_number.clone(),
|
||||
changelog_url: changelog_path.map(|path| format!("{}/{}", cdn_url, path)),
|
||||
changelog: version_data
|
||||
.version_body
|
||||
.clone()
|
||||
.unwrap_or_else(|| "".to_string()),
|
||||
files: Vec::new(),
|
||||
dependencies,
|
||||
game_versions,
|
||||
|
||||
@@ -32,8 +32,8 @@ pub struct ModerationMod {
|
||||
pub title: String,
|
||||
/// A short description of the mod.
|
||||
pub description: String,
|
||||
/// The link to the long description of the mod.
|
||||
pub body_url: String,
|
||||
/// The long description of the mod.
|
||||
pub body: String,
|
||||
/// The date at which the mod was first published.
|
||||
pub published: DateTime<Utc>,
|
||||
/// The date at which the mod was first published.
|
||||
@@ -85,7 +85,7 @@ pub async fn mods(
|
||||
team: database::models::ids::TeamId(m.team_id).into(),
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
body_url: m.body_url,
|
||||
body: m.body,
|
||||
published: m.published,
|
||||
icon_url: m.icon_url,
|
||||
issues_url: m.issues_url,
|
||||
@@ -133,6 +133,7 @@ pub async fn versions(
|
||||
featured: m.featured,
|
||||
name: m.name,
|
||||
version_number: m.version_number,
|
||||
changelog: m.changelog,
|
||||
changelog_url: m.changelog_url,
|
||||
date_published: m.date_published,
|
||||
downloads: m.downloads as u32,
|
||||
|
||||
@@ -203,6 +203,7 @@ fn convert_mod(data: database::models::mod_item::QueryMod) -> models::mods::Mod
|
||||
team: m.team_id.into(),
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
body: m.body,
|
||||
body_url: m.body_url,
|
||||
published: m.published,
|
||||
updated: m.updated,
|
||||
@@ -282,7 +283,6 @@ pub async fn mod_edit(
|
||||
info: web::Path<(models::ids::ModId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
config: web::Data<SearchConfig>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
new_mod: web::Json<EditMod>,
|
||||
indexing_queue: Data<Arc<CreationQueue>>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
@@ -729,13 +729,18 @@ pub async fn mod_edit(
|
||||
));
|
||||
}
|
||||
|
||||
let body_path = format!("data/{}/description.md", mod_id);
|
||||
|
||||
file_host.delete_file_version("", &*body_path).await?;
|
||||
|
||||
file_host
|
||||
.upload_file("text/plain", &body_path, body.clone().into_bytes())
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE mods
|
||||
SET body = $1
|
||||
WHERE (id = $2)
|
||||
",
|
||||
body,
|
||||
id as database::models::ids::ModId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
}
|
||||
|
||||
transaction
|
||||
@@ -857,18 +862,13 @@ pub async fn mod_delete(
|
||||
let id = info.into_inner().0;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let mod_item = database::models::Mod::get(id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.ok_or_else(|| ApiError::InvalidInputError("Invalid Mod ID specified!".to_string()))?;
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
mod_item.team_id,
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| ApiError::InvalidInputError("Invalid Mod ID specified!".to_string()))?;
|
||||
let team_member =
|
||||
database::models::TeamMember::get_from_user_id_mod(id.into(), user.id.into(), &**pool)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("Invalid Mod ID specified!".to_string())
|
||||
})?;
|
||||
|
||||
if !team_member.permissions.contains(Permissions::DELETE_MOD) {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
|
||||
@@ -287,7 +287,8 @@ pub async fn remove_team_member(
|
||||
let user_id = ids.1.into();
|
||||
|
||||
let current_user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let team_member = TeamMember::get_from_user_id(id, current_user.id.into(), &**pool).await?;
|
||||
let team_member =
|
||||
TeamMember::get_from_user_id_pending(id, current_user.id.into(), &**pool).await?;
|
||||
|
||||
let member = match team_member {
|
||||
Some(m) => m,
|
||||
@@ -312,7 +313,7 @@ pub async fn remove_team_member(
|
||||
// Members other than the owner can either leave the team, or be
|
||||
// removed by a member with the REMOVE_MEMBER permission.
|
||||
if delete_member.user_id == member.user_id
|
||||
|| member.permissions.contains(Permissions::REMOVE_MEMBER)
|
||||
|| (member.permissions.contains(Permissions::REMOVE_MEMBER) && member.accepted)
|
||||
{
|
||||
TeamMember::delete(id, user_id, &**pool).await?;
|
||||
} else {
|
||||
@@ -321,7 +322,7 @@ pub async fn remove_team_member(
|
||||
));
|
||||
}
|
||||
} else if delete_member.user_id == member.user_id
|
||||
|| member.permissions.contains(Permissions::MANAGE_INVITES)
|
||||
|| (member.permissions.contains(Permissions::MANAGE_INVITES) && member.accepted)
|
||||
{
|
||||
// This is a pending invite rather than a member, so the
|
||||
// user being invited or team members with the MANAGE_INVITES
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::auth::{check_is_moderator_from_headers, get_user_from_headers};
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::database::models::{TeamMember, User};
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models::users::{Role, UserId};
|
||||
@@ -420,24 +420,42 @@ pub async fn user_icon_edit(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make this actually do stuff
|
||||
#[derive(Deserialize)]
|
||||
pub struct RemovalType {
|
||||
#[serde(default = "default_removal")]
|
||||
removal_type: String,
|
||||
}
|
||||
|
||||
fn default_removal() -> String {
|
||||
"partial".into()
|
||||
}
|
||||
|
||||
#[delete("{id}")]
|
||||
pub async fn user_delete(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(UserId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
removal_type: web::Query<RemovalType>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
check_is_moderator_from_headers(
|
||||
req.headers(),
|
||||
&mut *pool
|
||||
.acquire()
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?,
|
||||
)
|
||||
.await?;
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
let id = info.into_inner().0;
|
||||
|
||||
let _id = info.0;
|
||||
let result = Some(());
|
||||
if !user.role.is_mod() && user.id == user.id {
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You do not have permission to delete this user!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let result;
|
||||
if removal_type.removal_type == "full".to_string() {
|
||||
result = crate::database::models::User::remove_full(id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
} else {
|
||||
result = crate::database::models::User::remove(id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
};
|
||||
|
||||
if result.is_some() {
|
||||
Ok(HttpResponse::Ok().body(""))
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::file_hosting::FileHost;
|
||||
use crate::models::mods::{
|
||||
GameVersion, ModId, ModLoader, Version, VersionFile, VersionId, VersionType,
|
||||
};
|
||||
use crate::models::teams::Permissions;
|
||||
use crate::routes::mod_creation::{CreateError, UploadedFile};
|
||||
use actix_multipart::{Field, Multipart};
|
||||
use actix_web::web::Data;
|
||||
@@ -180,53 +181,29 @@ async fn version_create_inner(
|
||||
|
||||
// Check that the user creating this version is a team member
|
||||
// of the mod the version is being added to.
|
||||
let member_ids = sqlx::query!(
|
||||
"
|
||||
SELECT user_id FROM team_members tm
|
||||
INNER JOIN mods ON mods.team_id = tm.team_id
|
||||
WHERE mods.id = $1
|
||||
",
|
||||
mod_id as models::ModId,
|
||||
let team_member = models::TeamMember::get_from_user_id_mod(
|
||||
mod_id.into(),
|
||||
user.id.into(),
|
||||
&mut *transaction,
|
||||
)
|
||||
.fetch_all(&mut *transaction)
|
||||
.await?;
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload this version!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let member_ids: Vec<models::UserId> = member_ids
|
||||
.iter()
|
||||
.map(|m| models::UserId(m.user_id))
|
||||
.collect();
|
||||
|
||||
if !member_ids.contains(&user.id.into()) {
|
||||
// TODO: Some team members may not have the permissions
|
||||
// to upload mods; We need a more in depth permissions
|
||||
// system.
|
||||
return Err(CreateError::InvalidInput("Unauthorized".to_string()));
|
||||
if !team_member
|
||||
.permissions
|
||||
.contains(Permissions::UPLOAD_VERSION)
|
||||
{
|
||||
return Err(CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload this version!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let version_id: VersionId = models::generate_version_id(transaction).await?.into();
|
||||
|
||||
let body_path;
|
||||
|
||||
if let Some(body) = &version_create_data.version_body {
|
||||
let path = format!(
|
||||
"data/{}/versions/{}/changelog.md",
|
||||
version_create_data.mod_id.unwrap(),
|
||||
version_create_data.version_number
|
||||
);
|
||||
|
||||
let uploaded_text = file_host
|
||||
.upload_file("text/plain", &path, body.clone().into_bytes())
|
||||
.await?;
|
||||
|
||||
uploaded_files.push(UploadedFile {
|
||||
file_id: uploaded_text.file_id,
|
||||
file_name: uploaded_text.file_name.clone(),
|
||||
});
|
||||
body_path = Some(path);
|
||||
} else {
|
||||
body_path = None;
|
||||
}
|
||||
|
||||
let release_channel = models::ChannelId::get_id(
|
||||
version_create_data.release_channel.as_str(),
|
||||
&mut *transaction,
|
||||
@@ -256,7 +233,10 @@ async fn version_create_inner(
|
||||
author_id: user.id.into(),
|
||||
name: version_create_data.version_title.clone(),
|
||||
version_number: version_create_data.version_number.clone(),
|
||||
changelog_url: body_path.map(|path| format!("{}/{}", cdn_url, path)),
|
||||
changelog: version_create_data
|
||||
.version_body
|
||||
.clone()
|
||||
.unwrap_or_else(|| "".to_string()),
|
||||
files: Vec::new(),
|
||||
dependencies: version_create_data
|
||||
.dependencies
|
||||
@@ -303,7 +283,8 @@ async fn version_create_inner(
|
||||
featured: builder.featured,
|
||||
name: builder.name.clone(),
|
||||
version_number: builder.version_number.clone(),
|
||||
changelog_url: builder.changelog_url.clone(),
|
||||
changelog: builder.changelog.clone(),
|
||||
changelog_url: None,
|
||||
date_published: chrono::Utc::now(),
|
||||
downloads: 0,
|
||||
version_type: version_data.release_channel,
|
||||
@@ -418,8 +399,25 @@ async fn upload_file_to_version_inner(
|
||||
}
|
||||
};
|
||||
|
||||
if version.author_id as u64 != user.id.0 {
|
||||
return Err(CreateError::InvalidInput("Unauthorized".to_string()));
|
||||
let team_member = models::TeamMember::get_from_user_id_version(
|
||||
version_id.into(),
|
||||
user.id.into(),
|
||||
&mut *transaction,
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload files to this version!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if team_member
|
||||
.permissions
|
||||
.contains(Permissions::UPLOAD_VERSION)
|
||||
{
|
||||
return Err(CreateError::CustomAuthenticationError(
|
||||
"You don't have permission to upload files to this version!".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let mod_id = ModId(version.mod_id as u64);
|
||||
@@ -526,12 +524,20 @@ pub async fn upload_file(
|
||||
Ok(models::version_item::VersionFileBuilder {
|
||||
filename: file_name.to_string(),
|
||||
url: format!("{}/{}", cdn_url, upload_data.file_name),
|
||||
hashes: vec![models::version_item::HashBuilder {
|
||||
algorithm: "sha1".to_string(),
|
||||
// This is an invalid cast - the database expects the hash's
|
||||
// bytes, but this is the string version.
|
||||
hash: upload_data.content_sha1.into_bytes(),
|
||||
}],
|
||||
hashes: vec![
|
||||
models::version_item::HashBuilder {
|
||||
algorithm: "sha1".to_string(),
|
||||
// This is an invalid cast - the database expects the hash's
|
||||
// bytes, but this is the string version.
|
||||
hash: upload_data.content_sha1.into_bytes(),
|
||||
},
|
||||
models::version_item::HashBuilder {
|
||||
algorithm: "sha512".to_string(),
|
||||
// This is an invalid cast - the database expects the hash's
|
||||
// bytes, but this is the string version.
|
||||
hash: upload_data.content_sha512.into_bytes(),
|
||||
},
|
||||
],
|
||||
primary: uploaded_files.len() == 1,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::ApiError;
|
||||
use crate::auth::{check_is_moderator_from_headers, get_user_from_headers};
|
||||
use crate::auth::get_user_from_headers;
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::models;
|
||||
use crate::models::teams::Permissions;
|
||||
@@ -159,6 +159,7 @@ fn convert_version(data: database::models::version_item::QueryVersion) -> models
|
||||
featured: data.featured,
|
||||
name: data.name,
|
||||
version_number: data.version_number,
|
||||
changelog: data.changelog,
|
||||
changelog_url: data.changelog_url,
|
||||
date_published: data.date_published,
|
||||
downloads: data.downloads as u32,
|
||||
@@ -220,7 +221,6 @@ pub async fn version_edit(
|
||||
req: HttpRequest,
|
||||
info: web::Path<(models::ids::VersionId,)>,
|
||||
pool: web::Data<PgPool>,
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
new_version: web::Json<EditVersion>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
@@ -233,18 +233,8 @@ pub async fn version_edit(
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
if let Some(version_item) = result {
|
||||
let mod_item = database::models::Mod::get(version_item.mod_id, &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError(
|
||||
"Attempted to edit version not attached to mod. How did this happen?"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
mod_item.team_id,
|
||||
let team_member = database::models::TeamMember::get_from_user_id_version(
|
||||
version_item.id,
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
@@ -494,17 +484,18 @@ pub async fn version_edit(
|
||||
}
|
||||
|
||||
if let Some(body) = &new_version.changelog {
|
||||
let mod_id: models::mods::ModId = version_item.mod_id.into();
|
||||
let body_path = format!(
|
||||
"data/{}/versions/{}/changelog.md",
|
||||
mod_id, version_item.version_number
|
||||
);
|
||||
|
||||
file_host.delete_file_version("", &*body_path).await?;
|
||||
|
||||
file_host
|
||||
.upload_file("text/plain", &body_path, body.clone().into_bytes())
|
||||
.await?;
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE versions
|
||||
SET changelog = $1
|
||||
WHERE (id = $2)
|
||||
",
|
||||
body,
|
||||
id as database::models::ids::VersionId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
}
|
||||
|
||||
transaction
|
||||
@@ -532,20 +523,8 @@ pub async fn version_delete(
|
||||
let id = info.into_inner().0;
|
||||
|
||||
if !user.role.is_mod() {
|
||||
let version = database::models::Version::get(id.into(), &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("An invalid version ID was specified".to_string())
|
||||
})?;
|
||||
let mod_item = database::models::Mod::get(version.mod_id, &**pool)
|
||||
.await
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?
|
||||
.ok_or_else(|| {
|
||||
ApiError::InvalidInputError("The version is not attached to a mod".to_string())
|
||||
})?;
|
||||
let team_member = database::models::TeamMember::get_from_user_id(
|
||||
mod_item.team_id,
|
||||
let team_member = database::models::TeamMember::get_from_user_id_version(
|
||||
id.into(),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
@@ -735,7 +714,7 @@ pub async fn delete_file(
|
||||
file_host: web::Data<Arc<dyn FileHost + Send + Sync>>,
|
||||
algorithm: web::Query<Algorithm>,
|
||||
) -> Result<HttpResponse, ApiError> {
|
||||
check_is_moderator_from_headers(req.headers(), &**pool).await?;
|
||||
let user = get_user_from_headers(req.headers(), &**pool).await?;
|
||||
|
||||
let hash = info.into_inner().0;
|
||||
|
||||
@@ -754,6 +733,30 @@ pub async fn delete_file(
|
||||
.map_err(|e| ApiError::DatabaseError(e.into()))?;
|
||||
|
||||
if let Some(row) = result {
|
||||
if !user.role.is_mod() {
|
||||
let team_member = database::models::TeamMember::get_from_user_id_version(
|
||||
database::models::ids::VersionId(row.version_id),
|
||||
user.id.into(),
|
||||
&**pool,
|
||||
)
|
||||
.await
|
||||
.map_err(ApiError::DatabaseError)?
|
||||
.ok_or_else(|| {
|
||||
ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to delete this file!".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
if !team_member
|
||||
.permissions
|
||||
.contains(Permissions::DELETE_VERSION)
|
||||
{
|
||||
return Err(ApiError::CustomAuthenticationError(
|
||||
"You don't have permission to delete this file!".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut transaction = pool
|
||||
.begin()
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user