delete bin endpoint
This commit is contained in:
parent
246dad5e87
commit
484a58e6f4
2 changed files with 76 additions and 5 deletions
|
@ -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()
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
Loading…
Reference in a new issue