mod.rs
1 use rocket::{ 2 http::Status, 3 request::{self, FromRequest}, 4 Request, State, 5 }; 6 7 use crate::{ 8 api::container_spec::auth_service::accounts_rs::AccountsRsUserResponse, config::Config, 9 }; 10 11 use self::errors::UnauthorizedResponse; 12 13 pub mod auth_service; 14 pub mod blobs; 15 pub mod errors; 16 pub mod manifests; 17 pub mod tags; 18 19 const CONTENT_TYPE_HEADER_NAME: &str = "Content-Type"; 20 const CONTENT_RANGE_HEADER_NAME: &str = "Content-Range"; 21 const CONTENT_LENGTH_HEADER_NAME: &str = "Content-Length"; 22 const LOCATION_HEADER_NAME: &str = "Location"; 23 const RANGE_HEADER_NAME: &str = "Range"; 24 const DOCKER_UPLOAD_UUID_HEADER_NAME: &str = "Docker-Upload-UUID"; 25 const DOCKER_CONTENT_DIGEST_HEADER_NAME: &str = "Docker-Content-Digest"; 26 const APPLICATION_TYPE_OCTET_STREAM: &str = "application/octet-stream"; 27 const OCI_SUBJECT_HEADER_NAME: &str = "OCI-Subject"; 28 29 pub struct Auth { 30 username: String, 31 } 32 33 #[derive(Responder, Debug, Clone)] 34 pub enum AuthFailure { 35 Unauthorized(UnauthorizedResponse), 36 InternalServerError(String), 37 } 38 39 #[rocket::async_trait] 40 impl<'r> FromRequest<'r> for Auth { 41 type Error = AuthFailure; 42 43 async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> { 44 let config = match req.guard::<&State<Config>>().await { 45 rocket::outcome::Outcome::Success(s) => s, 46 _ => { 47 return request::Outcome::Error(( 48 Status::InternalServerError, 49 AuthFailure::InternalServerError("Failed to retrieve config!".to_string()), 50 )) 51 } 52 }; 53 54 let Some(auth_header) = req.headers().get_one("authorization") else { 55 warn!("Request missing authorization header"); 56 return auth_failure(req, config); 57 }; 58 59 if !auth_header.starts_with("Bearer ") { 60 error!("Auth header doesn't start with 'Bearer '?"); 61 return auth_failure(req, config); 62 } 63 64 let client = reqwest::Client::new(); 65 let resp = match client 66 .get(&config.accounts_rs_me_endpoint) 67 .header("Authorization", auth_header) 68 .send() 69 .await 70 { 71 Ok(resp) => resp, 72 Err(e) => { 73 error!("Failed to send user request to accounts service, err: {e:?}"); 74 return auth_failure(req, config); 75 } 76 }; 77 78 let resp_status = resp.status(); 79 if !resp_status.is_success() { 80 error!("Got error response (status {resp_status}) from accounts service"); 81 return auth_failure(req, config); 82 } 83 84 let user_info: AccountsRsUserResponse = match resp.json().await { 85 Ok(u) => u, 86 Err(e) => { 87 error!("Failed to deserialize user request to accounts service, err: {e:?}"); 88 return auth_failure(req, config); 89 } 90 }; 91 92 request::Outcome::Success(Auth { 93 username: user_info.success.email, 94 }) 95 } 96 } 97 98 fn auth_failure<'r>(request: &'r Request, config: &Config) -> request::Outcome<Auth, AuthFailure> { 99 let auth_failure = AuthFailure::Unauthorized(UnauthorizedResponse::new(config)); 100 request.local_cache(|| auth_failure.clone()); 101 return request::Outcome::Error((Status::Unauthorized, auth_failure)); 102 } 103 104 #[derive(Responder)] 105 pub enum SpecComplianceResponse { 106 #[response(status = 200)] 107 Ok(()), 108 #[response(status = 401)] 109 Unauthorized(UnauthorizedResponse), 110 #[response(status = 500)] 111 InternalServerError(()), 112 } 113 114 #[get("/v2")] 115 pub fn get_spec_compliance(auth: Result<Auth, AuthFailure>) -> SpecComplianceResponse { 116 match auth { 117 Ok(_) => SpecComplianceResponse::Ok(()), 118 Err(AuthFailure::Unauthorized(resp)) => SpecComplianceResponse::Unauthorized(resp), 119 Err(AuthFailure::InternalServerError(err)) => { 120 error!("Internal server error {err:?}"); 121 SpecComplianceResponse::InternalServerError(()) 122 } 123 } 124 }