freighter_auth/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use async_trait::async_trait;
4use http::header::AUTHORIZATION;
5use http::{HeaderMap, StatusCode};
6
7#[cfg(feature = "yes-backend")]
8#[cfg_attr(docsrs, doc(cfg(feature = "yes-backend")))]
9pub mod yes_backend;
10
11pub mod no_backend;
12
13#[cfg(feature = "pg-backend")]
14#[cfg_attr(docsrs, doc(cfg(feature = "pg-backend")))]
15pub mod pg_backend;
16
17#[cfg(feature = "fs-backend")]
18#[cfg_attr(docsrs, doc(cfg(feature = "fs-backend")))]
19pub mod fs_backend;
20
21#[cfg(feature = "fs-backend")]
22mod base64_serde;
23
24mod error;
25
26#[cfg(feature = "cf-backend")]
27mod cf_access;
28
29#[cfg(feature = "cf-backend")]
30#[cfg_attr(docsrs, doc(cfg(feature = "cf-backend")))]
31pub mod cf_backend;
32
33pub use error::*;
34use freighter_api_types::ownership::response::ListedOwner;
35
36#[async_trait]
37pub trait AuthProvider {
38    type Config;
39
40    async fn healthcheck(&self) -> anyhow::Result<()>;
41
42    /// Register a new user, returning a token if successful.
43    async fn register(&self, username: &str) -> AuthResult<String>;
44
45    /// List the owners of a crate.
46    async fn list_owners(&self, token: &str, crate_name: &str) -> AuthResult<Vec<ListedOwner>>;
47    /// Add a new owner to a crate.
48    async fn add_owners(&self, token: &str, users: &[&str], crate_name: &str) -> AuthResult<()>;
49    /// Remove an owner from a crate.
50    async fn remove_owners(&self, token: &str, users: &[&str], crate_name: &str) -> AuthResult<()>;
51
52    /// Verify that a user has permission to publish new versions of a crate.
53    ///
54    /// If the crate has never been published before to the registry, the user should be given
55    /// ownership of the new crate.
56    async fn publish(&self, token: &str, crate_name: &str) -> AuthResult<()>;
57
58    /// Verify that a user has permission to yank or unyank versions of a crate.
59    async fn auth_yank(&self, token: &str, crate_name: &str) -> AuthResult<()>;
60
61    /// Verify that a user is allowed to look at the index entry for a given crate.
62    ///
63    /// This is currently only meaningful for registries which rely on experimental cargo features
64    /// to auth any access to the registry.
65    async fn auth_index_fetch(&self, token: &str, crate_name: &str) -> AuthResult<()> {
66        let _ = (token, crate_name);
67        Err(AuthError::Unimplemented)
68    }
69
70    /// Verify that a user is allowed to download a given crate.
71    ///
72    /// This is currently only meaningful for registries which rely on experimental cargo features
73    /// to auth any access to the registry.
74    async fn auth_crate_download(&self, token: &str, crate_name: &str) -> AuthResult<()> {
75        let _ = (token, crate_name);
76        Err(AuthError::Unimplemented)
77    }
78
79    /// Verify that a user is allowed to view the full index.
80    ///
81    /// This is used for both searching the index and listing all crates.
82    ///
83    /// This is currently only meaningful for registries which rely on experimental cargo features
84    /// to auth any access to the registry.
85    async fn auth_view_full_index(&self, token: &str) -> AuthResult<()> {
86        let _ = token;
87        Err(AuthError::Unimplemented)
88    }
89
90    /// Fetch of config.json. Called only if the server is configured to do so.
91    async fn auth_config(&self, token: &str) -> AuthResult<()> {
92        let _ = token;
93        Err(AuthError::Unimplemented)
94    }
95
96    fn token_from_headers<'h>(&self, headers: &'h HeaderMap) -> Result<Option<&'h str>, StatusCode> {
97        default_token_from_headers(headers)
98    }
99}
100
101pub(crate) fn default_token_from_headers(headers: &HeaderMap) -> Result<Option<&str>, StatusCode> {
102    match headers.get(AUTHORIZATION) {
103        Some(auth) => auth.to_str().map_err(|_| StatusCode::BAD_REQUEST).map(Some),
104        None => Ok(None),
105    }
106}