diff --git a/sqlx-data.json b/sqlx-data.json index 08bd6d70..48b05618 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -2741,18 +2741,6 @@ }, "query": "\n INSERT INTO dependencies (dependent_id, dependency_type, dependency_id, mod_dependency_id, dependency_file_name)\n VALUES ($1, $2, $3, $4, $5)\n " }, - "602f3113c933780c672862bc27d1bcd5398bde58cb1b769ee21da7088fce8a83": { - "describe": { - "columns": [], - "nullable": [], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n DELETE FROM signing_keys WHERE id = $1\n " - }, "61a7f29e024bf2f1368370e3f6e8ef70317c7e8545b5b6d4235f21164948ba27": { "describe": { "columns": [], @@ -4088,26 +4076,6 @@ }, "query": "\n DELETE FROM mods_categories\n WHERE joining_mod_id = $1\n " }, - "a72657376764f8d478b828c32093df6da30c8551f39cd9b9eba446353d44d370": { - "describe": { - "columns": [ - { - "name": "exists", - "ordinal": 0, - "type_info": "Bool" - } - ], - "nullable": [ - null - ], - "parameters": { - "Left": [ - "Int8" - ] - } - }, - "query": "\n SELECT EXISTS(SELECT 1 FROM signing_keys WHERE id = $1)\n " - }, "a90bb6904e1b790c0e29e060dac5ba4c2a6087e07c1197dc1f59f0aff31944c9": { "describe": { "columns": [], @@ -5321,6 +5289,18 @@ }, "query": "\n UPDATE team_members\n SET accepted = TRUE\n WHERE (team_id = $1 AND user_id = $2)\n " }, + "d44a9ef55fa7988c53b6062e31da52c88845fc772f5d726a3e32db0b55f748b6": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Left": [ + "Int8Array" + ] + } + }, + "query": "\n DELETE FROM signing_keys\n WHERE id = ANY($1)\n " + }, "d4e545d7fdcaee6f621058d827bc199634cc3f71561ae5596d9cfa973d9b1129": { "describe": { "columns": [ diff --git a/src/database/models/ids.rs b/src/database/models/ids.rs index 233f1a5d..5192eb7f 100644 --- a/src/database/models/ids.rs +++ b/src/database/models/ids.rs @@ -249,6 +249,11 @@ impl From for ids::NotificationId { ids::NotificationId(id.0 as u64) } } +impl From for SigningKeyId { + fn from(id: ids::SigningKeyId) -> Self { + SigningKeyId(id.0 as i64) + } +} impl From for ids::SigningKeyId { fn from(id: SigningKeyId) -> Self { ids::SigningKeyId(id.0 as u64) diff --git a/src/database/models/signing_key_item.rs b/src/database/models/signing_key_item.rs index 551dbe60..467e350d 100644 --- a/src/database/models/signing_key_item.rs +++ b/src/database/models/signing_key_item.rs @@ -118,33 +118,27 @@ impl SigningKey { .await } - pub async fn remove_full<'a, E>( + pub async fn remove( id: SigningKeyId, - exec: E, - ) -> Result, sqlx::Error> - where - E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy, - { - let result = sqlx::query!( - " - SELECT EXISTS(SELECT 1 FROM signing_keys WHERE id = $1) - ", - id as SigningKeyId - ) - .fetch_one(exec) - .await?; + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + ) -> Result, sqlx::error::Error> { + Self::remove_many(&[id], transaction).await + } - if !result.exists.unwrap_or(false) { - return Ok(None); - } + pub async fn remove_many( + key_ids: &[SigningKeyId], + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + ) -> Result, sqlx::error::Error> { + let key_ids_parsed: Vec = key_ids.iter().map(|x| x.0).collect(); sqlx::query!( " - DELETE FROM signing_keys WHERE id = $1 + DELETE FROM signing_keys + WHERE id = ANY($1) ", - id as SigningKeyId, + &key_ids_parsed ) - .execute(exec) + .execute(&mut *transaction) .await?; Ok(Some(())) diff --git a/src/routes/v2/mod.rs b/src/routes/v2/mod.rs index c4418406..1cfc0e6d 100644 --- a/src/routes/v2/mod.rs +++ b/src/routes/v2/mod.rs @@ -5,6 +5,7 @@ mod notifications; pub(crate) mod project_creation; mod projects; mod reports; +mod signing_keys; mod statistics; mod tags; mod teams; @@ -23,6 +24,7 @@ pub fn config(cfg: &mut actix_web::web::ServiceConfig) { .configure(moderation::config) .configure(notifications::config) .configure(project_creation::config) + .configure(signing_keys::config) // SHOULD CACHE .configure(projects::config) .configure(reports::config) diff --git a/src/routes/v2/signing_keys.rs b/src/routes/v2/signing_keys.rs new file mode 100644 index 00000000..fd63637a --- /dev/null +++ b/src/routes/v2/signing_keys.rs @@ -0,0 +1,117 @@ +use crate::models::signing_keys::SigningKey; +use crate::routes::ApiError; +use crate::util::auth::get_user_from_headers; +use crate::{database, models::ids::SigningKeyId}; +use actix_web::{delete, get, web, HttpRequest, HttpResponse}; +use serde::{Deserialize, Serialize}; +use sqlx::PgPool; + +use database::models::signing_key_item::SigningKey as DBSigningKey; +use database::models::SigningKeyId as DBSigningKeyId; + +pub fn config(cfg: &mut web::ServiceConfig) { + cfg.service(keys_get); + cfg.service(keys_delete); + + cfg.service(web::scope("key").service(key_get).service(key_delete)); +} + +#[derive(Serialize, Deserialize)] +pub struct SigningKeyIds { + pub ids: String, +} + +#[get("keys")] +pub async fn keys_get( + web::Query(ids): web::Query, + pool: web::Data, +) -> Result { + let key_ids: Vec = + serde_json::from_str::>(ids.ids.as_str())? + .into_iter() + .map(DBSigningKeyId::from) + .collect(); + + let keys_data: Vec = + DBSigningKey::get_many(&key_ids, &**pool).await?; + + let keys: Vec = + keys_data.into_iter().map(SigningKey::from).collect(); + + Ok(HttpResponse::Ok().json(keys)) +} + +#[get("{id}")] +pub async fn key_get( + info: web::Path<(SigningKeyId,)>, + pool: web::Data, +) -> Result { + let id = info.into_inner().0; + + let Some(key_data) = DBSigningKey::get(id.into(), &**pool).await? else { + return Ok(HttpResponse::NotFound().body("")); + }; + + Ok(HttpResponse::Ok().json(SigningKey::from(key_data))) +} + +#[delete("{id}")] +pub async fn key_delete( + req: HttpRequest, + info: web::Path<(SigningKeyId,)>, + pool: web::Data, +) -> Result { + let user = get_user_from_headers(req.headers(), &**pool).await?; + + let id = info.into_inner().0; + + let Some(data) = DBSigningKey::get(id.into(), &**pool).await? else { + return Ok(HttpResponse::NotFound().body("")); + }; + + if data.owner_id == user.id.into() || user.role.is_admin() { + let mut transaction = pool.begin().await?; + + DBSigningKey::remove(id.into(), &mut transaction).await?; + + transaction.commit().await?; + + Ok(HttpResponse::NoContent().body("")) + } else { + Err(ApiError::CustomAuthentication( + "You are not authorized to delete this key!".to_string(), + )) + } +} + +#[delete("keys")] +pub async fn keys_delete( + req: HttpRequest, + web::Query(ids): web::Query, + pool: web::Data, +) -> Result { + let user = get_user_from_headers(req.headers(), &**pool).await?; + + let key_ids = serde_json::from_str::>(&ids.ids)? + .into_iter() + .map(|x| x.into()) + .collect::>(); + + let mut transaction = pool.begin().await?; + + let keys_data = DBSigningKey::get_many(&key_ids, &**pool).await?; + + let mut keys: Vec = Vec::new(); + + for key in keys_data { + if key.owner_id == user.id.into() || user.role.is_admin() { + keys.push(key.id); + } + } + + DBSigningKey::remove_many(&keys, &mut transaction).await?; + + transaction.commit().await?; + + Ok(HttpResponse::NoContent().body("")) +}