Implement all API according to swagger spec #27

Merged
blek merged 16 commits from delete-upload-api-methods into 0.2-dev 2023-12-14 11:56:46 +01:00
4 changed files with 78 additions and 11 deletions
Showing only changes of commit 5fab110513 - Show all commits

View File

@ -74,7 +74,7 @@ paths:
example: {}
401:
description: |-
This error code is returned if one of the two conditions are met:
This error code is returned if one of these conditions are met:
1. The instance does not allow deleting files via API.
2. The file has been uploaded from another IP, which is not this one, and the API was not authorized via an API key.
@ -90,6 +90,9 @@ paths:
fid:
type: string
example: ID or name of the file. It is the NAME in file.blek.codes/uploads/NAME
api_key:
type: string
example: '123'
/api/files/upload:
post:
summary: Upload a file

View File

@ -49,10 +49,10 @@ fn check_api_pass(state: &SharedState, key: String) -> Result<(), WithStatus<Jso
}
}
fn function_disabled_err() -> WithStatus<Json> {
fn function_disabled_err(status: StatusCode) -> WithStatus<Json> {
warp::reply::with_status(
json(&ErrorMessage::new(Error::APIFunctionDisabled)),
StatusCode::SERVICE_UNAVAILABLE
status
)
}

View File

@ -1,26 +1,56 @@
use std::collections::HashMap;
use std::{collections::HashMap, net::IpAddr};
use warp::{reply::{Reply, json}, reject::Rejection, Filter, http::StatusCode};
use warp::{reply::{Reply, json, with_status}, reject::Rejection, Filter, http::StatusCode};
use serde::{Serialize, Deserialize};
use warp_real_ip::real_ip;
use crate::web::{state::SharedState, rejection::HttpReject, api::types::{ErrorMessage, Error}};
use crate::{web::{state::SharedState, rejection::HttpReject, api::types::{ErrorMessage, Error}}, files::File};
use super::{function_disabled_err, check_api_enabled};
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DeleteFunctionPayload {
pub fid: String
pub fid: String,
pub api_key: Option<String>
}
pub async fn delete(state: SharedState, body: DeleteFunctionPayload) -> Result<Box<dyn Reply>, Rejection> {
pub async fn delete(state: SharedState, body: DeleteFunctionPayload, ip: Option<IpAddr>) -> Result<Box<dyn Reply>, Rejection> {
if let Err(res) = check_api_enabled(&state) {
return Ok(Box::new(res));
}
if (!state.config.api.delete) || (!state.config.api.enabled) {
return Ok(Box::new(function_disabled_err()))
return Ok(Box::new(function_disabled_err(StatusCode::UNAUTHORIZED)))
}
let mut sudo_authorized = false;
let mut blocked = false;
if let Some(keys) = state.config.api.apikeys.clone() {
if let Some(key) = body.api_key {
if keys.contains(&key) {
sudo_authorized = true;
blocked = false;
} else {
sudo_authorized = false;
blocked = true;
}
} else {
sudo_authorized = false;
blocked = true
}
}
if ! sudo_authorized {
if ip.is_none() { // need the ip if sudo is not authorized
blocked = true // to check if the file is the own file
}
}
let ip = ip.unwrap();
let id = body.fid;
let mut file = state.file_mgr.find_by_hash(id.clone())
.map_err(|x| HttpReject::StringError(x.to_string()))?;
@ -30,7 +60,7 @@ pub async fn delete(state: SharedState, body: DeleteFunctionPayload) -> Result<B
.map_err(|x| HttpReject::StringError(x.to_string()))?;
}
if let None = file {
if let None = file.clone() {
return Ok(
Box::new(
warp::reply::with_status(
@ -46,12 +76,46 @@ pub async fn delete(state: SharedState, body: DeleteFunctionPayload) -> Result<B
)
}
let file: File = file.unwrap();
if let Some(uploader) = file.uploader_ip {
if uploader != ip && (!sudo_authorized) {
blocked = true;
}
} else {
blocked = true;
}
if blocked {
return Ok(
Box::new(
with_status(
json(
&ErrorMessage {
error: Error::APIPasswordDenied,
details: Some(
"Request has been denied for one of the following reasons: password auth did not pass, file was uploaded by someone else, the instance does not allow deleting files via the API".into()
)
}
),
StatusCode::UNAUTHORIZED
)
)
)
}
file.delete(state).await;
Ok(Box::new(json(&HashMap::<(), ()>::new())))
}
pub fn delete_f(state: SharedState) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
let proxy_ip = state.env.proxy_addr;
warp::path!("api" / "files" / "delete")
.map(move || state.clone())
.and(warp::body::json())
.and(real_ip(vec![proxy_ip]))
.and_then(delete)
}

View File

@ -13,7 +13,7 @@ pub async fn get_all(state: SharedState, ip: Option<IpAddr>) -> Result<Box<dyn R
}
if (!state.config.api.get_all) || (!state.config.api.enabled) {
return Ok(Box::new(function_disabled_err()))
return Ok(Box::new(function_disabled_err(StatusCode::UNAUTHORIZED)))
}
let found =