delete bin endpoint

This commit is contained in:
Paul Zinselmeyer 2023-10-23 22:30:21 +02:00
parent 246dad5e87
commit 484a58e6f4
Signed by: pfzetto
GPG key ID: 4EEF46A5B276E648
2 changed files with 76 additions and 5 deletions

View file

@ -38,6 +38,12 @@ pub enum Error {
#[error("invalid ttl")] #[error("invalid ttl")]
InvalidTtl, InvalidTtl,
#[error("unauthorized")]
Unauthorized,
#[error("forbidden")]
Forbidden,
} }
impl IntoResponse for Error { impl IntoResponse for Error {
@ -54,6 +60,8 @@ impl IntoResponse for Error {
(StatusCode::BAD_REQUEST, "invalid multipart data").into_response() (StatusCode::BAD_REQUEST, "invalid multipart data").into_response()
} }
Self::InvalidTtl => (StatusCode::BAD_REQUEST, "invalid ttl specified").into_response(), Self::InvalidTtl => (StatusCode::BAD_REQUEST, "invalid ttl specified").into_response(),
Self::Unauthorized => (StatusCode::UNAUTHORIZED, "unauthorized\n").into_response(),
Self::Forbidden => (StatusCode::FORBIDDEN, "forbidden\n").into_response(),
_ => { _ => {
error!("{:?}", self); error!("{:?}", self);
(StatusCode::INTERNAL_SERVER_ERROR, "internal server error\n").into_response() (StatusCode::INTERNAL_SERVER_ERROR, "internal server error\n").into_response()

View file

@ -32,12 +32,12 @@ use chacha20::{
}; };
use futures_util::StreamExt; use futures_util::StreamExt;
use garbage_collector::GarbageCollector; use garbage_collector::GarbageCollector;
use log::debug; use log::{debug, warn};
use render::{html, raw}; use render::{html, raw};
use serde::Deserialize; use serde::Deserialize;
use sha3::{Digest, Sha3_256}; use sha3::{Digest, Sha3_256};
use tokio::{ use tokio::{
fs::File, fs::{self, File},
io::{AsyncWriteExt, BufReader, BufWriter}, io::{AsyncWriteExt, BufReader, BufWriter},
}; };
use util::{IdSalt, KeySalt}; use util::{IdSalt, KeySalt};
@ -147,7 +147,14 @@ async fn main() {
let app = Router::new() let app = Router::new()
.route("/", get(get_index)) .route("/", get(get_index))
.route("/:id", get(get_item).post(post_item).put(post_item)) .route(
"/:id",
get(get_item)
.post(upload_bin)
.put(upload_bin)
.delete(delete_bin),
)
.route("/:id/delete", get(delete_bin_interactive).post(delete_bin))
.with_state(state); .with_state(state);
axum::Server::bind(&"[::]:8080".parse().expect("valid listen address")) axum::Server::bind(&"[::]:8080".parse().expect("valid listen address"))
.serve(app.into_make_service()) .serve(app.into_make_service())
@ -195,12 +202,69 @@ async fn get_index(
))) )))
} }
async fn delete_bin(
Path(phrase): Path<String>,
State(app_state): State<AppState>,
oidc_extractor: Result<OidcExtractor<EmptyAdditionalClaims>, axum_oidc::error::Error>,
jwt_claims: Option<Claims<EmptyAdditionalClaims>>,
) -> HandlerResult<impl IntoResponse> {
let subject = match (oidc_extractor, jwt_claims) {
(_, Some(claims)) => claims.sub.to_string(),
(Ok(oidc), None) => oidc.claims.subject().to_string(),
(Err(_), None) => return Err(Error::Unauthorized),
};
let phrase = Phrase::from_str(&phrase)?;
let id = Id::from_phrase(&phrase, &app_state.id_salt);
let metadata_path = format!("{}/{}.toml", app_state.data, id);
let metadata = Metadata::from_file(&metadata_path).await?;
if metadata.subject != subject {
return Err(Error::Forbidden);
}
debug!("deleting bin {}", id);
let res_meta = fs::remove_file(&format!("{}/{}.toml", app_state.data, id)).await;
let res_data = fs::remove_file(&format!("{}/{}.dat", app_state.data, id)).await;
if res_meta.is_err() || res_data.is_err() {
warn!("failed to delete bin {} for manual deletion", id);
}
Ok("ok\n")
}
async fn delete_bin_interactive(
_: Path<String>,
_: OidcExtractor<EmptyAdditionalClaims>,
) -> HandlerResult<impl IntoResponse> {
let body = html! {
<html>
<head>
<title>{"zettoit bin"}</title>
<link rel={"icon"} type={"image/svg"} href={"https://static.zettoit.eu/img/zettoit-logo.svg"}/>
</head>
<body style={"font-family: monospace; background-color: black; color: white;"}>
<div style={"margin: auto; max-width: 80ch;"}>
<h2>{"Confirm Deletion"}</h2>
<p>{"The bin will be deleted. All data will be permanently lost."}</p>
<form method={"post"}>
<button type={"submit"}>{"Delete"}</button>
</form>
</div>
</body>
</html>
};
Ok(Html(body))
}
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct PostQuery { pub struct PostQuery {
ttl: Option<String>, ttl: Option<String>,
} }
async fn post_item( async fn upload_bin(
Path(phrase): Path<String>, Path(phrase): Path<String>,
Query(params): Query<PostQuery>, Query(params): Query<PostQuery>,
State(app_state): State<AppState>, State(app_state): State<AppState>,
@ -308,7 +372,6 @@ async fn post_item(
} }
} }
#[debug_handler]
async fn get_item( async fn get_item(
Path(phrase): Path<String>, Path(phrase): Path<String>,
State(app_state): State<AppState>, State(app_state): State<AppState>,