cli #9

Merged
pfzetto merged 6 commits from cli into master 2023-10-23 22:31:00 +02:00
2 changed files with 76 additions and 5 deletions
Showing only changes of commit 484a58e6f4 - Show all commits

View file

@ -38,6 +38,12 @@ pub enum Error {
#[error("invalid ttl")]
InvalidTtl,
#[error("unauthorized")]
Unauthorized,
#[error("forbidden")]
Forbidden,
}
impl IntoResponse for Error {
@ -54,6 +60,8 @@ impl IntoResponse for Error {
(StatusCode::BAD_REQUEST, "invalid multipart data").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);
(StatusCode::INTERNAL_SERVER_ERROR, "internal server error\n").into_response()

View file

@ -32,12 +32,12 @@ use chacha20::{
};
use futures_util::StreamExt;
use garbage_collector::GarbageCollector;
use log::debug;
use log::{debug, warn};
use render::{html, raw};
use serde::Deserialize;
use sha3::{Digest, Sha3_256};
use tokio::{
fs::File,
fs::{self, File},
io::{AsyncWriteExt, BufReader, BufWriter},
};
use util::{IdSalt, KeySalt};
@ -147,7 +147,14 @@ async fn main() {
let app = Router::new()
.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);
axum::Server::bind(&"[::]:8080".parse().expect("valid listen address"))
.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)]
pub struct PostQuery {
ttl: Option<String>,
}
async fn post_item(
async fn upload_bin(
Path(phrase): Path<String>,
Query(params): Query<PostQuery>,
State(app_state): State<AppState>,
@ -308,7 +372,6 @@ async fn post_item(
}
}
#[debug_handler]
async fn get_item(
Path(phrase): Path<String>,
State(app_state): State<AppState>,