Skip to main content

zammad_api/
auth.rs

1use reqwest::RequestBuilder;
2
3/// Authentication strategy for the Zammad API.
4///
5/// Zammad accepts personal access tokens (`Token token=...`), OAuth bearer
6/// tokens (`Bearer ...`) and HTTP Basic auth on every endpoint.
7#[derive(Clone)]
8pub enum Auth {
9    /// Personal access token issued from a user's profile.
10    Token(String),
11    /// OAuth2 bearer token.
12    Bearer(String),
13    /// HTTP Basic auth.
14    Basic {
15        /// Username or login email.
16        username: String,
17        /// Password - `None` is valid (e.g. for token-as-username schemes).
18        password: Option<String>,
19    },
20}
21
22impl Auth {
23    /// Build a personal-access-token credential.
24    pub fn token(t: impl Into<String>) -> Self {
25        Self::Token(t.into())
26    }
27
28    /// Build an OAuth2 bearer credential.
29    pub fn bearer(t: impl Into<String>) -> Self {
30        Self::Bearer(t.into())
31    }
32
33    /// Build an HTTP Basic credential with a password.
34    pub fn basic(username: impl Into<String>, password: impl Into<String>) -> Self {
35        Self::Basic {
36            username: username.into(),
37            password: Some(password.into()),
38        }
39    }
40
41    /// Build an HTTP Basic credential without a password (some Zammad
42    /// proxies accept a bare token as the username).
43    pub fn basic_no_password(username: impl Into<String>) -> Self {
44        Self::Basic {
45            username: username.into(),
46            password: None,
47        }
48    }
49
50    pub(crate) fn apply(&self, req: RequestBuilder) -> RequestBuilder {
51        match self {
52            Self::Token(t) => req.header("Authorization", format!("Token token={t}")),
53            Self::Bearer(t) => req.bearer_auth(t),
54            Self::Basic { username, password } => req.basic_auth(username, password.as_deref()),
55        }
56    }
57}
58
59impl std::fmt::Debug for Auth {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        match self {
62            Self::Token(_) => f.debug_tuple("Token").field(&"<redacted>").finish(),
63            Self::Bearer(_) => f.debug_tuple("Bearer").field(&"<redacted>").finish(),
64            Self::Basic { username, .. } => f
65                .debug_struct("Basic")
66                .field("username", username)
67                .field("password", &"<redacted>")
68                .finish(),
69        }
70    }
71}