Skip to content
Open
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
23 changes: 20 additions & 3 deletions libmamba/src/api/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "mamba/core/channel_context.hpp"
#include "mamba/core/context.hpp"
#include "mamba/core/prefix_data.hpp"
#include "mamba/specs/conda_url.hpp"
#include "mamba/util/string.hpp"

namespace mamba
Expand All @@ -32,6 +33,7 @@ namespace mamba
bool canonical = false;
bool export_ = false;
bool revisions = false;
bool auth = false;
};

struct formatted_pkg
Expand Down Expand Up @@ -141,6 +143,11 @@ namespace mamba
}
PrefixData& prefix_data = sprefix_data.value();

if (options.auth && !options.explicit_)
{
LOG_WARNING << "Option --auth ignored because --explicit was not provided.";
}

if (options.full_name)
{
regex = '^' + regex + '$';
Expand Down Expand Up @@ -288,6 +295,15 @@ namespace mamba
LOG_WARNING
<< "Option --export ignored because --explicit was also provided.";
}
// By default, strip authentication (token, username/password) from the URL.
// So explicit list can be shared safely. If --auth is specified, keep it.
const auto credentials = options.auth ? specs::CondaURL::Credentials::Show
: specs::CondaURL::Credentials::Remove;
auto format_url = [&](const std::string& url) -> std::string
{
auto parsed = specs::CondaURL::parse(url);
return parsed ? parsed->str(credentials) : url;
};
for (auto p : packages)
{
if (options.md5 && options.sha256)
Expand All @@ -299,15 +315,15 @@ namespace mamba
}
if (options.md5)
{
std::cout << p.url << "#" << p.md5 << std::endl;
std::cout << format_url(p.url) << "#" << p.md5 << std::endl;
}
else if (options.sha256)
{
std::cout << p.url << "#" << p.sha256 << std::endl;
std::cout << format_url(p.url) << "#" << p.sha256 << std::endl;
}
else
{
std::cout << p.url << std::endl;
std::cout << format_url(p.url) << std::endl;
}
}
}
Expand Down Expand Up @@ -381,6 +397,7 @@ namespace mamba
options.canonical = config.at("canonical").value<bool>();
options.export_ = config.at("export").value<bool>();
options.revisions = config.at("revisions").value<bool>();
options.auth = config.at("auth").value<bool>();

auto channel_context = ChannelContext::make_conda_compatible(config.context());
detail::list_packages(config.context(), regex, channel_context, std::move(options));
Expand Down
9 changes: 9 additions & 0 deletions micromamba/src/list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ init_list_parser(CLI::App* subcom, Configuration& config)
Configurable("revisions", false).group("cli").description("List the revision history.")
);
subcom->add_flag("--revisions", revisions.get_cli_config<bool>(), revisions.description());

auto& auth = config.insert(
Configurable("auth", false)
.group("cli")
.description(
"Keep authentication (token, username/password) in the URL when using --explicit. Ignored if --revisions is also provided."
)
);
subcom->add_flag("--auth", auth.get_cli_config<bool>(), auth.description());
}

void
Expand Down
53 changes: 53 additions & 0 deletions micromamba/tests/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,59 @@ def test_list_subcommands(
assert len(output.split("=")) == 3


def _inject_token_into_conda_meta(prefix, token):
import json
import pathlib

conda_meta = pathlib.Path(prefix) / "conda-meta"
count = 0
for record in conda_meta.glob("*.json"):
data = json.loads(record.read_text())
url = data.get("url", "")
if "://" not in url or "/t/" in url:
continue
scheme, rest = url.split("://", 1)
host, path = rest.split("/", 1)
data["url"] = f"{scheme}://{host}/t/{token}/{path}"
record.write_text(json.dumps(data))
count += 1
return count


@pytest.mark.parametrize("hash_flag", ["", "--md5", "--sha256"])
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
def test_list_explicit_auth(tmp_home, tmp_root_prefix, tmp_xtensor_env, hash_flag):
token = "sometoken123abc"
assert _inject_token_into_conda_meta(tmp_xtensor_env, token) > 0
token_fragment = f"/t/{token}/"

# Default: Authentication stripped from URLs.
default_res = helpers.umamba_list("-p", tmp_xtensor_env, "--explicit", hash_flag)
default_urls = [line for line in default_res.splitlines() if "://" in line]
assert len(default_urls) > 0
for line in default_urls:
assert token_fragment not in line
assert line.startswith("https://")
if hash_flag == "--md5":
assert len(line.split("#")[-1].strip()) == 32
elif hash_flag == "--sha256":
assert len(line.split("#")[-1].strip()) == 64

# With --auth: Authentication preserved in URLs.
auth_res = helpers.umamba_list("-p", tmp_xtensor_env, "--explicit", "--auth", hash_flag)
auth_urls = [line for line in auth_res.splitlines() if "://" in line]
assert len(auth_urls) > 0
for line in auth_urls:
assert token_fragment in line


@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
def test_list_auth_without_explicit_is_ignored(tmp_home, tmp_root_prefix, tmp_xtensor_env):
res = helpers.umamba_list("-p", tmp_xtensor_env, "--auth")
assert "xtensor" in res
assert "xtl" in res


@pytest.mark.parametrize("quiet_flag", ["", "-q", "--quiet"])
@pytest.mark.parametrize("shared_pkgs_dirs", [True], indirect=True)
def test_list_name(tmp_home, tmp_root_prefix, tmp_xtensor_env, quiet_flag):
Expand Down
Loading