Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client::new_with_auth for user-created StoredAuth #153

Merged
merged 4 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ocipkg-cli/src/bin/ocipkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ fn main() -> Result<()> {
password,
} => {
let url = url::Url::parse(&registry)?;
let mut auth = ocipkg::distribution::StoredAuth::load()?;
let mut auth = ocipkg::distribution::StoredAuth::load().unwrap_or_default();
match (username, password) {
(Some(username), Some(password)) => {
auth.add(
Expand Down
87 changes: 44 additions & 43 deletions ocipkg/src/distribution/auth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use base64::engine::{general_purpose::STANDARD, Engine};
use oci_spec::distribution::ErrorResponse;
use serde::{Deserialize, Serialize};
Expand All @@ -14,32 +14,22 @@ pub struct StoredAuth {
impl StoredAuth {
/// Load authentication info stored by ocipkg
pub fn load() -> Result<Self> {
let mut auth = StoredAuth::default();
if let Some(path) = auth_path() {
let new = Self::from_path(&path)?;
auth.append(new)?;
}
Ok(auth)
Self::from_path(&auth_path()?)
}

/// Load authentication info with docker and podman setting
pub fn load_all() -> Result<Self> {
let mut auth = StoredAuth::default();
if let Some(path) = docker_auth_path() {
let mut auth = None;
for path in [docker_auth_path(), podman_auth_path(), auth_path()]
.into_iter()
.filter_map(|x| x.ok())
{
if let Ok(new) = Self::from_path(&path) {
auth.append(new)?;
log::info!("Loaded auth info from: {}", path.display());
auth.get_or_insert_with(Self::default).append(new);
}
}
if let Some(path) = podman_auth_path() {
if let Ok(new) = Self::from_path(&path) {
auth.append(new)?;
}
}
if let Some(path) = auth_path() {
let new = Self::from_path(&path)?;
auth.append(new)?;
}
Ok(auth)
auth.context("No valid auth info found")
}

pub fn add(&mut self, domain: &str, username: &str, password: &str) {
Expand Down Expand Up @@ -94,22 +84,24 @@ impl StoredAuth {
Ok(token.token)
}

pub fn append(&mut self, other: Self) -> Result<()> {
pub fn append(&mut self, other: Self) {
for (key, value) in other.auths.into_iter() {
if value.is_valid() {
self.auths.insert(key, value);
}
}
Ok(())
}

fn from_path(path: &Path) -> Result<Self> {
if path.is_file() {
let f = fs::File::open(path)?;
Ok(serde_json::from_reader(io::BufReader::new(f))?)
} else {
Ok(Self::default())
/// Load auth info from file
pub fn from_path(path: &Path) -> Result<Self> {
if !path.is_file() {
bail!("Auth file not found: {}", path.display());
}
let f = fs::File::open(path)?;
let loaded = serde_json::from_reader(io::BufReader::new(f))?;
let mut out = Self::default();
out.append(loaded);
Ok(out)
}
}

Expand All @@ -134,25 +126,34 @@ impl Auth {
}
}

fn auth_path() -> Option<PathBuf> {
directories::ProjectDirs::from("", "", "ocipkg")
.and_then(|dirs| Some(dirs.runtime_dir()?.join("auth.json")))
.or_else(|| {
// Most of container does not set XDG_RUNTIME_DIR,
// and then this fallback to `~/.ocipkg/config.json` like docker.
let dirs = directories::BaseDirs::new()?;
Some(dirs.home_dir().join(".ocipkg/config.json"))
})
fn home_dir() -> Result<PathBuf> {
let dirs = directories::BaseDirs::new().context("Cannot get $HOME directory")?;
Ok(dirs.home_dir().to_path_buf())
}

fn auth_path() -> Result<PathBuf> {
let dirs = directories::ProjectDirs::from("", "", "ocipkg")
.context("Cannot get project directory of ocipkg")?;
if let Some(runtime_dir) = dirs.runtime_dir() {
Ok(runtime_dir.join("auth.json"))
} else {
// Most of container does not set XDG_RUNTIME_DIR,
// and then this fallback to `~/.ocipkg/config.json` like docker.
Ok(home_dir()?.join(".ocipkg/config.json"))
}
}

fn docker_auth_path() -> Option<PathBuf> {
let dirs = directories::BaseDirs::new()?;
Some(dirs.home_dir().join(".docker/config.json"))
fn docker_auth_path() -> Result<PathBuf> {
Ok(home_dir()?.join(".docker/config.json"))
}

fn podman_auth_path() -> Option<PathBuf> {
let dirs = directories::ProjectDirs::from("", "", "containers")?;
Some(dirs.runtime_dir()?.join("auth.json"))
fn podman_auth_path() -> Result<PathBuf> {
let dirs = directories::ProjectDirs::from("", "", "containers")
.context("Cannot get the project directory of podman")?;
Ok(dirs
.runtime_dir()
.context("Cannot get runtime directory of podman")?
.join("auth.json"))
}

/// WWW-Authentication challenge
Expand Down
14 changes: 11 additions & 3 deletions ocipkg/src/distribution/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ pub struct Client {

impl Client {
pub fn new(url: Url, name: Name) -> Result<Self> {
let auth = StoredAuth::load_all()?;
let auth = StoredAuth::load_all().unwrap_or_default();
Self::new_with_auth(url, name, auth)
}

pub fn from_image_name(image: &ImageName) -> Result<Self> {
Self::new(image.registry_url()?, image.name.clone())
}

pub fn new_with_auth(url: Url, name: Name, auth: StoredAuth) -> Result<Self> {
Ok(Client {
agent: ureq::Agent::new(),
url,
Expand All @@ -28,8 +36,8 @@ impl Client {
})
}

pub fn from_image_name(image: &ImageName) -> Result<Self> {
Self::new(image.registry_url()?, image.name.clone())
pub fn from_image_name_with_auth(image: &ImageName, auth: StoredAuth) -> Result<Self> {
Self::new_with_auth(image.registry_url()?, image.name.clone(), auth)
}

pub fn add_basic_auth(&mut self, domain: &str, username: &str, password: &str) {
Expand Down
Loading