cli #9
2 changed files with 76 additions and 5 deletions
|
@ -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()
|
||||
|
|
|
@ -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>,
|
||||
|
|
Loading…
Reference in a new issue