1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-14 09:34:59 +00:00

pro: Allow passing Basic Auth credentials for HTTP URLs

Since our WebServer can already react to Basic Auth, now there is a way
to use that in SerenityOS :^)

This commit intentionally omits any Digest authentication.

NOTE: We specifically allow for empty credentials (just ':'), since
standard RFC7617 doesn't explicitly prohibit this.
This commit is contained in:
Thomas Keppler 2022-12-21 16:12:16 +01:00 committed by Ali Mohammad Pur
parent d2171abb2c
commit de23e7c2d3

View file

@ -5,10 +5,12 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Base64.h>
#include <AK/FileStream.h>
#include <AK/GenericLexer.h>
#include <AK/LexicalPath.h>
#include <AK/NumberFormat.h>
#include <AK/String.h>
#include <AK/URL.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/EventLoop.h>
@ -159,6 +161,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
DeprecatedString method = "GET";
StringView method_override;
HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> request_headers;
String credentials;
Core::ArgsParser args_parser;
args_parser.set_general_help(
@ -182,6 +185,26 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
request_headers.set(header.substring_view(0, split.value()), header.substring_view(split.value() + 1));
return true;
} });
args_parser.add_option(Core::ArgsParser::Option {
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
.help_string = "(HTTP only) Provide basic authentication credentials",
.long_name = "auth",
.short_name = 'u',
.value_name = "username:password",
.accept_value = [&](auto* s) {
StringView input { s, strlen(s) };
if (!input.contains(':'))
return false;
// NOTE: Input is explicitly not trimmed, but instad taken in raw;
// Space prepended usernames and appended passwords might be legal in the user's context.
auto maybe_credentials = String::from_utf8(input);
if (maybe_credentials.is_error())
return false;
credentials = maybe_credentials.release_value();
return true;
} });
args_parser.add_option(proxy_spec, "Specify a proxy server to use for this request (proto://ip:port)", "proxy", 'p', "proxy");
args_parser.add_option(verbose_output, "(HTTP only) Log request and response metadata", "verbose", 'v');
args_parser.add_positional_argument(url_str, "URL to download from", "url");
@ -222,6 +245,17 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
auto protocol_client = TRY(Protocol::RequestClient::try_create());
auto output_stream = ConditionalOutputFileStream { [&] { return should_save_stream_data; }, stdout };
// https://httpwg.org/specs/rfc9110.html#authentication
if (!credentials.is_empty() && is_http_url) {
// 11.2. Authentication Parameters
// The authentication scheme is followed by additional information necessary for achieving authentication via
// that scheme as (...) or a single sequence of characters capable of holding base64-encoded information.
// FIXME: Prevent overriding manually provided Authorization header
auto const encoded_credentials = TRY(encode_base64(credentials.bytes()));
auto const authorization = TRY(String::formatted("Basic {}", encoded_credentials));
request_headers.set("Authorization", authorization.to_deprecated_string());
}
Function<void()> setup_request = [&] {
if (!request) {
warnln("Failed to start request for '{}'", url_str);