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}