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
4 changes: 4 additions & 0 deletions src/curl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,10 @@ impl CurlCommand<'_> {
curl_command.arg("--ssl-revoke-best-effort");
}

// Use credentials from ~/.netrc (%USERPROFILE%\_netrc or .netrc on
// Windows) for hosts that have an entry. Missing file is not an error.
curl_command.arg("--netrc-optional");

curl_command.arg("--user-agent");
curl_command.arg(USER_AGENT);

Expand Down
21 changes: 19 additions & 2 deletions src/http_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@

use std::path::Path;

use anyhow::Context as _;
use serde::Deserialize;
use serde_json::Value;

use crate::config::ArtifactEntry;
use crate::curl::CurlCommand;
use crate::curl::CurlError;
use crate::curl::FetchContext;
use crate::provider::Provider;
use crate::util::FileLock;
use crate::util::HttpStatus;

const AUTH_HINT: &str = "the server rejected the request as unauthorized; to authenticate, add a \
`machine <host> login <user> password <token>` entry to your ~/.netrc file \
(%USERPROFILE%\\_netrc on Windows)";

pub struct HttpProvider {}

Expand Down Expand Up @@ -47,7 +52,19 @@ impl Provider for HttpProvider {
};
curl_cmd
.get_request(destination, &fetch_context)
.with_context(|| format!("failed to fetch `{}`", url))?;
.map_err(|e| {
let is_auth_error = matches!(
e,
CurlError::HttpStatus(_, HttpStatus::Unauthorized | HttpStatus::Forbidden)
);
let err = anyhow::Error::new(e);
let err = if is_auth_error {
err.context(AUTH_HINT)
} else {
err
};
err.context(format!("failed to fetch `{}`", url))
})?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion tests/dotslash_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ caused by: no providers succeeded. warnings:
failed to fetch artifact: failed to fetch `[PROVIDER_URL]`

Caused by:
0: `curl --location --retry 3 --fail --silent --show-error {}--user-agent [DOTSLASH_USER_AGENT] [PROVIDER_URL] --output [OUTPUT_FILE][..]`
0: `curl --location --retry 3 --fail --silent --show-error {}--netrc-optional --user-agent [DOTSLASH_USER_AGENT] [PROVIDER_URL] --output [OUTPUT_FILE][..]`
1: 404 Not Found
", if cfg!(windows) { "--ssl-revoke-best-effort "} else {""}),
);
Expand Down
13 changes: 13 additions & 0 deletions website/docs/dotslash-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,19 @@ Mozilla/5.0 (compatible; DotSlash/0.1.0; +https://dotslash-cli.com)

:::

#### Authentication

`curl` is invoked with `--netrc-optional`, so URLs that require HTTP basic
authentication work when the host has an entry in your `~/.netrc` file:

```text
machine artifacts.example.com login myuser password mytoken
```

On Windows, curl also reads `%USERPROFILE%\_netrc` and `%USERPROFILE%\.netrc`.
Hosts without an entry are fetched without credentials. Credentials are never
stored in the DotSlash file.

Note that in order to facilitate creating a DotSlash file by hand, you can use
DotSlash's `create-url-entry` subcommand to generate the boilerplate for a
platform entry based on a URL as follows:
Expand Down