containerregistry_auth/
credential.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, Default, PartialEq, Eq)]
7pub enum Credential {
8 #[default]
10 Anonymous,
11
12 Basic { username: String, password: String },
14
15 Bearer(String),
17
18 IdentityToken(String),
20}
21
22impl Credential {
23 pub fn anonymous() -> Self {
25 Credential::Anonymous
26 }
27
28 pub fn basic(username: impl Into<String>, password: impl Into<String>) -> Self {
30 Credential::Basic {
31 username: username.into(),
32 password: password.into(),
33 }
34 }
35
36 pub fn bearer(token: impl Into<String>) -> Self {
38 Credential::Bearer(token.into())
39 }
40
41 pub fn identity_token(token: impl Into<String>) -> Self {
43 Credential::IdentityToken(token.into())
44 }
45
46 pub fn is_anonymous(&self) -> bool {
48 matches!(self, Credential::Anonymous)
49 }
50
51 pub fn username(&self) -> Option<&str> {
53 match self {
54 Credential::Basic { username, .. } => Some(username),
55 _ => None,
56 }
57 }
58
59 pub fn password(&self) -> Option<&str> {
61 match self {
62 Credential::Basic { password, .. } => Some(password),
63 _ => None,
64 }
65 }
66
67 pub fn authorization_header(&self) -> Option<String> {
69 match self {
70 Credential::Anonymous => None,
71 Credential::Basic { username, password } => {
72 use base64::Engine;
73 let encoded = base64::engine::general_purpose::STANDARD
74 .encode(format!("{}:{}", username, password));
75 Some(format!("Basic {}", encoded))
76 }
77 Credential::Bearer(token) => Some(format!("Bearer {}", token)),
78 Credential::IdentityToken(token) => Some(format!("Bearer {}", token)),
79 }
80 }
81}
82
83#[derive(Clone, Debug, Deserialize, Serialize)]
85#[serde(rename_all = "PascalCase")]
86pub struct HelperCredential {
87 #[serde(default, skip_serializing_if = "Option::is_none")]
89 pub username: Option<String>,
90
91 #[serde(default, rename = "Secret", skip_serializing_if = "Option::is_none")]
93 pub secret: Option<String>,
94
95 #[serde(default, rename = "ServerURL", skip_serializing_if = "Option::is_none")]
97 pub server_url: Option<String>,
98}
99
100impl HelperCredential {
101 pub fn into_credential(self) -> Credential {
103 match (self.username, self.secret) {
104 (Some(username), Some(secret)) if !username.is_empty() => {
105 Credential::basic(username, secret)
106 }
107 (_, Some(secret)) if !secret.is_empty() => {
108 Credential::identity_token(secret)
110 }
111 _ => Credential::Anonymous,
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_credential_anonymous() {
122 let cred = Credential::anonymous();
123 assert!(cred.is_anonymous());
124 assert_eq!(cred.authorization_header(), None);
125 }
126
127 #[test]
128 fn test_credential_basic() {
129 let cred = Credential::basic("user", "pass");
130 assert!(!cred.is_anonymous());
131 assert_eq!(cred.username(), Some("user"));
132 assert_eq!(cred.password(), Some("pass"));
133
134 let header = cred.authorization_header().unwrap();
135 assert!(header.starts_with("Basic "));
136 }
137
138 #[test]
139 fn test_credential_bearer() {
140 let cred = Credential::bearer("my-token");
141 assert!(!cred.is_anonymous());
142 assert_eq!(
143 cred.authorization_header(),
144 Some("Bearer my-token".to_string())
145 );
146 }
147
148 #[test]
149 fn test_helper_credential_to_credential() {
150 let helper = HelperCredential {
151 username: Some("user".to_string()),
152 secret: Some("pass".to_string()),
153 server_url: None,
154 };
155 let cred = helper.into_credential();
156 assert_eq!(cred.username(), Some("user"));
157 assert_eq!(cred.password(), Some("pass"));
158 }
159
160 #[test]
161 fn test_helper_credential_identity_token() {
162 let helper = HelperCredential {
163 username: None,
164 secret: Some("token".to_string()),
165 server_url: None,
166 };
167 let cred = helper.into_credential();
168 assert!(matches!(cred, Credential::IdentityToken(_)));
169 }
170}