mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 16:18:12 +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:
parent
d2171abb2c
commit
de23e7c2d3
1 changed files with 34 additions and 0 deletions
|
@ -5,10 +5,12 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/Base64.h>
|
||||||
#include <AK/FileStream.h>
|
#include <AK/FileStream.h>
|
||||||
#include <AK/GenericLexer.h>
|
#include <AK/GenericLexer.h>
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
#include <AK/NumberFormat.h>
|
#include <AK/NumberFormat.h>
|
||||||
|
#include <AK/String.h>
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/EventLoop.h>
|
#include <LibCore/EventLoop.h>
|
||||||
|
@ -159,6 +161,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
DeprecatedString method = "GET";
|
DeprecatedString method = "GET";
|
||||||
StringView method_override;
|
StringView method_override;
|
||||||
HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> request_headers;
|
HashMap<DeprecatedString, DeprecatedString, CaseInsensitiveStringTraits> request_headers;
|
||||||
|
String credentials;
|
||||||
|
|
||||||
Core::ArgsParser args_parser;
|
Core::ArgsParser args_parser;
|
||||||
args_parser.set_general_help(
|
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));
|
request_headers.set(header.substring_view(0, split.value()), header.substring_view(split.value() + 1));
|
||||||
return true;
|
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(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_option(verbose_output, "(HTTP only) Log request and response metadata", "verbose", 'v');
|
||||||
args_parser.add_positional_argument(url_str, "URL to download from", "url");
|
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 protocol_client = TRY(Protocol::RequestClient::try_create());
|
||||||
auto output_stream = ConditionalOutputFileStream { [&] { return should_save_stream_data; }, stdout };
|
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 = [&] {
|
Function<void()> setup_request = [&] {
|
||||||
if (!request) {
|
if (!request) {
|
||||||
warnln("Failed to start request for '{}'", url_str);
|
warnln("Failed to start request for '{}'", url_str);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue