Rust implementation of OCI Distribution Spec with granular access control
1use crate::errors::{ErrorCode, OciErrorResponse};
2use axum::{body::Body, http::Response, http::StatusCode, response::IntoResponse};
3
4pub(crate) fn unauthorized(host: &str) -> Response<Body> {
5 let error = OciErrorResponse::new(ErrorCode::Unauthorized, "authentication required");
6
7 Response::builder()
8 .status(StatusCode::UNAUTHORIZED)
9 .header(
10 "WWW-Authenticate",
11 format!("Basic realm=\"{}\", charset=\"UTF-8\"", host),
12 )
13 .header("Content-Type", "application/json")
14 .body(Body::from(serde_json::to_string(&error).unwrap_or_else(
15 |_| {
16 r#"{"errors":[{"code":"UNAUTHORIZED","message":"authentication required"}]}"#
17 .to_string()
18 },
19 )))
20 .unwrap()
21}
22
23pub(crate) fn forbidden() -> Response<Body> {
24 OciErrorResponse::new(ErrorCode::Denied, "access denied: insufficient permissions")
25 .into_response()
26}
27
28pub(crate) fn not_found() -> Response<Body> {
29 OciErrorResponse::new(ErrorCode::BlobUnknown, "resource not found").into_response()
30}
31
32pub(crate) fn blob_unknown(digest: &str) -> Response<Body> {
33 OciErrorResponse::with_detail(
34 ErrorCode::BlobUnknown,
35 "blob unknown to registry",
36 format!("digest: {}", digest),
37 )
38 .into_response()
39}
40
41pub(crate) fn manifest_unknown(reference: &str) -> Response<Body> {
42 OciErrorResponse::with_detail(
43 ErrorCode::ManifestUnknown,
44 "manifest unknown to registry",
45 format!("reference: {}", reference),
46 )
47 .into_response()
48}
49
50pub(crate) fn digest_invalid(digest: &str) -> Response<Body> {
51 OciErrorResponse::with_detail(
52 ErrorCode::DigestInvalid,
53 "provided digest did not match uploaded content",
54 format!("digest: {}", digest),
55 )
56 .into_response()
57}
58
59pub(crate) fn manifest_invalid(reason: &str) -> Response<Body> {
60 OciErrorResponse::with_detail(ErrorCode::ManifestInvalid, "manifest invalid", reason)
61 .into_response()
62}
63
64#[allow(dead_code)]
65pub(crate) fn name_invalid(name: &str) -> Response<Body> {
66 OciErrorResponse::with_detail(ErrorCode::NameInvalid, "invalid repository name", name)
67 .into_response()
68}
69
70pub(crate) fn blob_upload_unknown(uuid: &str) -> Response<Body> {
71 OciErrorResponse::with_detail(
72 ErrorCode::BlobUploadUnknown,
73 "upload session not found",
74 format!("uuid: {}", uuid),
75 )
76 .into_response()
77}
78
79pub(crate) fn internal_error() -> Response<Body> {
80 Response::builder()
81 .status(StatusCode::INTERNAL_SERVER_ERROR)
82 .header("Content-Type", "application/json")
83 .body(Body::from(
84 r#"{"errors":[{"code":"UNKNOWN","message":"internal server error"}]}"#,
85 ))
86 .unwrap()
87}
88
89pub(crate) fn conflict(message: &str) -> Response<Body> {
90 Response::builder()
91 .status(StatusCode::CONFLICT)
92 .header("Content-Type", "application/json")
93 .body(Body::from(format!(
94 r#"{{"errors":[{{"code":"UNSUPPORTED","message":"{}"}}]}}"#,
95 message
96 )))
97 .unwrap()
98}