From 920a84c1fa9f353879fd8b7120d39694221a2d6d Mon Sep 17 00:00:00 2001 From: Yezzi Hsueh Date: Mon, 13 Nov 2023 10:15:00 +0800 Subject: [PATCH] feat: read auth info from local docker config --- Cargo.lock | 82 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 ++- src/registry.rs | 88 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 158 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b72664..f6e303c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,11 +193,34 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys", +] + [[package]] name = "docker-tags" version = "0.1.0" dependencies = [ + "base64", "clap", + "directories", "reqwest", "serde", "serde_json", @@ -306,6 +329,17 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.28.0" @@ -473,6 +507,17 @@ version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall", +] + [[package]] name = "linux-raw-sys" version = "0.4.11" @@ -604,6 +649,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "percent-encoding" version = "2.3.0" @@ -655,6 +706,17 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_users" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "reqwest" version = "0.11.22" @@ -873,6 +935,26 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 574a22d..cde49d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,4 +9,6 @@ license = "MIT" reqwest = { version = "0.11.22", features = ["blocking"] } serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -clap = { version = "4.4.7", features = ["derive"] } \ No newline at end of file +clap = { version = "4.4.7", features = ["derive"] } +directories = "5.0.1" +base64 = "0.21.5" \ No newline at end of file diff --git a/src/registry.rs b/src/registry.rs index 82c5862..9045a44 100644 --- a/src/registry.rs +++ b/src/registry.rs @@ -1,3 +1,6 @@ +use std::collections::HashMap; +use std::fs; +use directories::UserDirs; use serde::{Deserialize, Serialize}; use crate::{DockerTags, QueryArgs}; @@ -33,6 +36,7 @@ impl DockerTags for RegistryTagsFetcher { None => { "" } }; let token = get_token(namespace, repository).unwrap(); + let url = format!("https://registry-1.docker.io/v2/{namespace}/{repository}/tags/list"); let client = reqwest::blocking::Client::new(); let response = client.get(url) @@ -47,21 +51,75 @@ impl DockerTags for RegistryTagsFetcher { } } -fn get_token(namespace: &str, repository: &str) -> reqwest::Result { - let username = "simaek"; - let password = "snow@PX1314"; - let service = "registry.docker.io"; - let scope = format!("repository:{namespace}/{repository}:pull"); - let auth = format!("https://auth.docker.io/token?service={service}&scope={scope}"); - let client = reqwest::blocking::Client::new(); - let response = client.get(auth) - .basic_auth(username, Some(password)) - .send(); - match response { - Ok(r) => { - let json: RegistryTokenResponse = serde_json::from_str(r.text().unwrap().as_str()).unwrap(); - Ok(json.token.unwrap()) +fn get_token(namespace: &str, repository: &str) -> Option { + let config = read_config(); + match config { + Some(s) => { + // let username = "simaek"; + // let password = "snow@PX1314"; + let service = "registry.docker.io"; + let scope = format!("repository:{namespace}/{repository}:pull"); + let auth = format!("https://auth.docker.io/token?service={service}&scope={scope}"); + let client = reqwest::blocking::Client::new(); + let response = client.get(auth) + .basic_auth(s.username, Some(s.password)) + .send(); + match response { + Ok(r) => { + let json: RegistryTokenResponse = serde_json::from_str(r.text().unwrap().as_str()).unwrap(); + Some(json.token.unwrap()) + } + Err(_) => { None } + } } - Err(e) => { Err(e) } + _ => { None } } } + +#[derive(Serialize, Deserialize)] +struct DockerConfig { + auths: HashMap, +} + +#[derive(Serialize, Deserialize)] +struct DockerAuth { + auth: Option, +} + +use base64::{Engine as _, engine::general_purpose}; + +fn read_config() -> Option { + let user_dirs = UserDirs::new().unwrap(); + let home_dir = user_dirs.home_dir(); + let config_path = home_dir.join(".docker").join("config.json"); + let config = fs::read_to_string(config_path); + let json: DockerConfig = serde_json::from_str(config.unwrap().as_str()).unwrap(); + let auths = json.auths; + // let auth = auths.get("https://hub.docker.com/v2/"); + let auth = auths.get("https://index.docker.io/v1/"); + match auth { + Some(a) => { + let auth = a.auth.clone().unwrap(); + + let mut buffer = Vec::::new(); + let result = general_purpose::URL_SAFE_NO_PAD.decode_vec(auth.as_bytes(), &mut buffer); + let decoded = match std::str::from_utf8(&buffer) { + Ok(v) => v, + Err(e) => panic!("Invalid UTF-8 sequence: {}", e), + }; + let vec = decoded.split(":").collect::>(); + println!("{:?}", decoded); + Some(DockerPassword { + username: vec[0].parse().unwrap(), + password: vec[1].parse().unwrap(), + }) + } + _ => { None } + } +} + +#[derive(Serialize, Deserialize)] +struct DockerPassword { + username: String, + password: String, +} \ No newline at end of file