From 478094abcf6f79ddb4e13e5763f5827208363ae7 Mon Sep 17 00:00:00 2001 From: Ilion Beyst Date: Sun, 19 Jun 2022 22:33:44 +0200 Subject: [PATCH] basic docker login PoC --- planetwars-server/Cargo.toml | 1 + planetwars-server/src/modules/registry.rs | 57 +++++++++++++++++------ 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/planetwars-server/Cargo.toml b/planetwars-server/Cargo.toml index 3e1f05e..d85a011 100644 --- a/planetwars-server/Cargo.toml +++ b/planetwars-server/Cargo.toml @@ -28,6 +28,7 @@ planetwars-matchrunner = { path = "../planetwars-matchrunner" } config = { version = "0.12", features = ["toml"] } thiserror = "1.0.31" sha2 = "0.10" +tokio-util = { version="0.7.3", features=["io"] } # TODO: remove me shlex = "1.1" diff --git a/planetwars-server/src/modules/registry.rs b/planetwars-server/src/modules/registry.rs index 6095527..9d71dd7 100644 --- a/planetwars-server/src/modules/registry.rs +++ b/planetwars-server/src/modules/registry.rs @@ -1,9 +1,11 @@ -use axum::body::{Body, StreamBody}; -use axum::extract::{BodyStream, Path, Query}; +use axum::body::{Body, Bytes, StreamBody}; +use axum::extract::{BodyStream, FromRequest, Path, Query, RequestParts, TypedHeader}; use axum::handler::Handler; +use axum::headers::authorization::Basic; +use axum::headers::Authorization; use axum::response::{IntoResponse, Response}; use axum::routing::{get, head, post, put}; -use axum::Router; +use axum::{async_trait, Router}; use hyper::StatusCode; use serde::Serialize; use sha2::{Digest, Sha256}; @@ -16,7 +18,8 @@ use crate::util::gen_alphanumeric; const REGISTRY_PATH: &'static str = "./data/registry"; pub fn registry_service() -> Router { Router::new() - .nest("/v2", registry_api_v2()) + // The docker API requires this trailing slash + .nest("/v2/", registry_api_v2()) .fallback(fallback.into_service()) } @@ -41,8 +44,41 @@ async fn fallback(request: axum::http::Request) -> impl IntoResponse { StatusCode::NOT_FOUND } -// root should return 200 OK to confirm api compliance -async fn root_handler() -> Response { +type AuthorizationHeader = TypedHeader>; + +struct RegistryAuth; + +#[async_trait] +impl FromRequest for RegistryAuth +where + B: Send, +{ + type Rejection = Response>; + + async fn from_request(req: &mut RequestParts) -> Result { + let TypedHeader(Authorization(_basic)) = + AuthorizationHeader::from_request(req).await.map_err(|_| { + let err = RegistryErrors { + errors: vec![RegistryError { + code: "UNAUTHORIZED".to_string(), + message: "please log in".to_string(), + detail: serde_json::Value::Null, + }], + }; + Response::builder() + .status(StatusCode::UNAUTHORIZED) + .header("Docker-Distribution-API-Version", "registry/2.0") + .header("WWW-Authenticate", "Basic") + .body(axum::body::Full::from(serde_json::to_vec(&err).unwrap())) + .unwrap() + })?; + + Ok(RegistryAuth) + } +} + +async fn root_handler(_auth: RegistryAuth) -> impl IntoResponse { + // root should return 200 OK to confirm api compliance Response::builder() .status(StatusCode::OK) .header("Docker-Distribution-API-Version", "registry/2.0") @@ -89,15 +125,6 @@ async fn get_blob( } async fn blob_upload(Path(repository_name): Path) -> impl IntoResponse { - // let value = json!({ - // "errors": [ - // { - // "code": "UNSUPPORTED", - // "message": "not implemented yet lol", - // } - // ] - // }); - let uuid = gen_alphanumeric(16); tokio::fs::File::create(PathBuf::from(REGISTRY_PATH).join("uploads").join(&uuid)) .await