neocities_client/
auth.rs

1////////       This file is part of the source code for neocities-client, a Rust           ////////
2////////       library for interacting with the https://neocities.org/ API.                ////////
3////////                                                                                   ////////
4////////                           Copyright © 2024  André Kugland                         ////////
5////////                                                                                   ////////
6////////       This program is free software: you can redistribute it and/or modify        ////////
7////////       it under the terms of the GNU General Public License as published by        ////////
8////////       the Free Software Foundation, either version 3 of the License, or           ////////
9////////       (at your option) any later version.                                         ////////
10////////                                                                                   ////////
11////////       This program is distributed in the hope that it will be useful,             ////////
12////////       but WITHOUT ANY WARRANTY; without even the implied warranty of              ////////
13////////       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                ////////
14////////       GNU General Public License for more details.                                ////////
15////////                                                                                   ////////
16////////       You should have received a copy of the GNU General Public License           ////////
17////////       along with this program. If not, see https://www.gnu.org/licenses/.         ////////
18
19//! This module contains the `Auth` enum, which represents an authentication method for the API.
20
21use base64::{prelude::BASE64_STANDARD, Engine};
22use serde::{Deserialize, Serialize};
23use std::fmt::Debug;
24
25/// Authentication method for the API, either a username/password pair or an API key.
26///
27/// ```
28/// # use neocities_client::Auth;
29/// let credentials = Auth::from("username:password");
30/// assert_eq!(credentials.header(), "Basic dXNlcm5hbWU6cGFzc3dvcmQ=");
31/// let api_key = Auth::from("api_key");
32/// assert_eq!(api_key.header(), "Bearer api_key");
33/// ```
34#[derive(Clone, PartialEq, Serialize, Deserialize)]
35#[serde(from = "String", into = "String")]
36pub enum Auth {
37    Credentials(String, String),
38    ApiKey(String),
39}
40
41impl Auth {
42    /// Generate the Authorization HTTP header value for this authentication method.
43    pub fn header(&self) -> String {
44        match self {
45            Auth::Credentials(user, pass) => {
46                let val = format!("{}:{}", user, pass);
47                format!("Basic {}", BASE64_STANDARD.encode(val))
48            }
49            Auth::ApiKey(key) => format!("Bearer {}", key),
50        }
51    }
52}
53
54impl<S: AsRef<str>> From<S> for Auth {
55    fn from(s: S) -> Self {
56        let s = s.as_ref();
57        // If the string contains a colon, it's a username/password pair.
58        if let Some((user, pass)) = s.split_once(':') {
59            Auth::Credentials(user.to_owned(), pass.to_owned())
60        } else {
61            Auth::ApiKey(s.to_owned())
62        }
63    }
64}
65
66impl From<Auth> for String {
67    fn from(auth: Auth) -> Self {
68        match auth {
69            Auth::Credentials(user, pass) => format!("{}:{}", user, pass),
70            Auth::ApiKey(key) => key,
71        }
72    }
73}
74
75impl Debug for Auth {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        match self {
78            Self::Credentials(username, _) => f
79                .debug_tuple("Auth::Credentials")
80                .field(username)
81                .field(&"********")
82                .finish(),
83            Self::ApiKey(key) => f
84                .debug_tuple("Auth::ApiKey")
85                .field(&format!("{}{}", &key[0..6], "*".repeat(32 - 6)))
86                .finish(),
87        }
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn test_auth() {
97        let credentials = Auth::from("username:password");
98        assert_eq!(
99            credentials,
100            Auth::Credentials("username".to_string(), "password".to_string())
101        );
102        assert_eq!(credentials.header(), "Basic dXNlcm5hbWU6cGFzc3dvcmQ=");
103
104        let api_key = Auth::from("api_key");
105        assert_eq!(api_key, Auth::ApiKey("api_key".to_string()));
106        assert_eq!(api_key.header(), "Bearer api_key");
107    }
108}