1#![deny(missing_debug_implementations)]
35
36use log::trace;
37use serde::{Deserialize, Serialize};
38
39pub mod errors;
40pub mod mediatypes;
41pub mod reference;
42pub mod render;
43pub mod v2;
44
45use std::{collections::HashMap, io::Read};
46
47use base64::prelude::*;
48use errors::{Error, Result};
49
50pub static USER_AGENT: &str = concat!("clowdhaus/docker-registry/", env!("CARGO_PKG_VERSION"));
52
53pub fn get_credentials<T: Read>(reader: T, index: &str) -> Result<(Option<String>, Option<String>)> {
58 let map: Auths = serde_json::from_reader(reader)?;
59 let real_index = match index {
60 "docker.io" | "registry-1.docker.io" => "https://index.docker.io/v1/",
62 other => other,
63 };
64 let auth = match map.auths.get(real_index) {
65 Some(x) => BASE64_STANDARD.decode(x.auth.as_str())?,
66 None => return Err(Error::AuthInfoMissing(real_index.to_string())),
67 };
68 let s = String::from_utf8(auth)?;
69 let creds: Vec<&str> = s.splitn(2, ':').collect();
70 let up = match (creds.first(), creds.get(1)) {
71 (Some(&""), Some(p)) => (None, Some(p.to_string())),
72 (Some(u), Some(&"")) => (Some(u.to_string()), None),
73 (Some(u), Some(p)) => (Some(u.to_string()), Some(p.to_string())),
74 (_, _) => (None, None),
75 };
76 trace!("Found credentials for user={:?} on {}", up.0, index);
77 Ok(up)
78}
79
80#[derive(Debug, Deserialize, Serialize)]
81struct Auths {
82 auths: HashMap<String, AuthObj>,
83}
84
85#[derive(Debug, Default, Deserialize, Serialize)]
86struct AuthObj {
87 auth: String,
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_get_credentials_valid() {
96 let config = r#"{"auths":{"https://index.docker.io/v1/":{"auth":"dXNlcjpwYXNz"}}}"#;
97 let (user, pass) = get_credentials(config.as_bytes(), "docker.io").unwrap();
98 assert_eq!(user, Some("user".to_string()));
99 assert_eq!(pass, Some("pass".to_string()));
100 }
101
102 #[test]
103 fn test_get_credentials_registry1_docker_io() {
104 let config = r#"{"auths":{"https://index.docker.io/v1/":{"auth":"dXNlcjpwYXNz"}}}"#;
105 let (user, pass) = get_credentials(config.as_bytes(), "registry-1.docker.io").unwrap();
106 assert_eq!(user, Some("user".to_string()));
107 assert_eq!(pass, Some("pass".to_string()));
108 }
109
110 #[test]
111 fn test_get_credentials_custom_registry() {
112 let config = r#"{"auths":{"myregistry.example.com":{"auth":"YWRtaW46c2VjcmV0"}}}"#;
114 let (user, pass) = get_credentials(config.as_bytes(), "myregistry.example.com").unwrap();
115 assert_eq!(user, Some("admin".to_string()));
116 assert_eq!(pass, Some("secret".to_string()));
117 }
118
119 #[test]
120 fn test_get_credentials_missing_registry() {
121 let config = r#"{"auths":{"other.io":{"auth":"dXNlcjpwYXNz"}}}"#;
122 let result = get_credentials(config.as_bytes(), "missing.io");
123 assert!(result.is_err());
124 assert!(matches!(result.unwrap_err(), Error::AuthInfoMissing(_)));
125 }
126
127 #[test]
128 fn test_get_credentials_empty_user() {
129 let config = r#"{"auths":{"reg.io":{"auth":"OnBhc3N3b3Jk"}}}"#;
131 let (user, pass) = get_credentials(config.as_bytes(), "reg.io").unwrap();
132 assert_eq!(user, None);
133 assert_eq!(pass, Some("password".to_string()));
134 }
135
136 #[test]
137 fn test_get_credentials_empty_password() {
138 let config = r#"{"auths":{"reg.io":{"auth":"dXNlcjo="}}}"#;
140 let (user, pass) = get_credentials(config.as_bytes(), "reg.io").unwrap();
141 assert_eq!(user, Some("user".to_string()));
142 assert_eq!(pass, None);
143 }
144
145 #[test]
146 fn test_get_credentials_invalid_json() {
147 let config = r#"not json"#;
148 let result = get_credentials(config.as_bytes(), "reg.io");
149 assert!(result.is_err());
150 }
151
152 #[test]
153 fn test_get_credentials_invalid_base64() {
154 let config = r#"{"auths":{"reg.io":{"auth":"!!!invalid!!!"}}}"#;
155 let result = get_credentials(config.as_bytes(), "reg.io");
156 assert!(result.is_err());
157 }
158}