Prompt user for credentials when http request needs it (#34620)

* prompt user to get their credentials

Signed-off-by: Lloyd Massiah artmis9@protonmail.com

move credential prompt to a function

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* add prompt for step 15.4

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* add new prompt definition for user credentials

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* remove default implementation for HttpState which allowed making the embedder_proxy non-optional

- default implementation was only used in tests so created an alternative create_http_state function

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

add credentials to authentication cache

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* add tests that are successful for the happy path

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* add test for user cancels prompt and user inputs incorrect credentials, and refactor shared code between tests

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* handle error when setting username and password in Url and ran formatting

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

renaming test functions

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* change authentication flag to false for proxy authentication. The spec doesn't specify that the flag should be true, and the flag is by default false

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* clean up test code a bit

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* add skeleton implementation to support open harmony and android

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* update warning message to include Android

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* fix build error for OH os and Android

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

* remove unused import to fix warning

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>

---------

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>
Co-authored-by: lazypassion <25536767+lazypassion@users.noreply.github.com>
This commit is contained in:
arthmis 2024-12-28 15:24:11 -05:00 committed by GitHub
parent a9539d8b03
commit aa40b8f820
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 379 additions and 51 deletions

View file

@ -16,6 +16,9 @@ use devtools_traits::{
ChromeToDevtoolsControlMsg, DevtoolsControlMsg, HttpRequest as DevtoolsHttpRequest,
HttpResponse as DevtoolsHttpResponse, NetworkEvent,
};
use embedder_traits::{
EmbedderMsg, EmbedderProxy, PromptCredentialsInput, PromptDefinition, PromptOrigin,
};
use futures::{future, StreamExt, TryFutureExt, TryStreamExt};
use headers::authorization::Basic;
use headers::{
@ -60,10 +63,7 @@ use tokio::sync::mpsc::{
use tokio_stream::wrappers::ReceiverStream;
use crate::async_runtime::HANDLE;
use crate::connector::{
create_http_client, create_tls_config, CACertificates, CertificateErrorOverrideManager,
Connector,
};
use crate::connector::{CertificateErrorOverrideManager, Connector};
use crate::cookie::ServoCookie;
use crate::cookie_storage::CookieStorage;
use crate::decoder::Decoder;
@ -72,7 +72,7 @@ use crate::fetch::headers::{SecFetchDest, SecFetchMode, SecFetchSite, SecFetchUs
use crate::fetch::methods::{main_fetch, Data, DoneChannel, FetchContext, Target};
use crate::hsts::HstsList;
use crate::http_cache::{CacheKey, HttpCache};
use crate::resource_thread::AuthCache;
use crate::resource_thread::{AuthCache, AuthCacheEntry};
/// <https://fetch.spec.whatwg.org/#document-accept-header-value>
pub const DOCUMENT_ACCEPT_HEADER_VALUE: HeaderValue =
@ -103,26 +103,7 @@ pub struct HttpState {
pub history_states: RwLock<HashMap<HistoryStateId, Vec<u8>>>,
pub client: Client<Connector, Body>,
pub override_manager: CertificateErrorOverrideManager,
}
impl Default for HttpState {
fn default() -> Self {
let override_manager = CertificateErrorOverrideManager::new();
Self {
hsts_list: RwLock::new(HstsList::default()),
cookie_jar: RwLock::new(CookieStorage::new(150)),
auth_cache: RwLock::new(AuthCache::default()),
history_states: RwLock::new(HashMap::new()),
http_cache: RwLock::new(HttpCache::default()),
http_cache_state: Mutex::new(HashMap::new()),
client: create_http_client(create_tls_config(
CACertificates::Default,
false, /* ignore_certificate_errors */
override_manager.clone(),
)),
override_manager,
}
}
pub embedder_proxy: Mutex<EmbedderProxy>,
}
/// Step 13 of <https://fetch.spec.whatwg.org/#concept-fetch>.
@ -1590,12 +1571,26 @@ async fn http_network_or_cache_fetch(
// Step 14.3 If requests use-URL-credentials flag is unset or isAuthenticationFetch is true, then:
if !http_request.use_url_credentials || authentication_fetch_flag {
// TODO(#33616, #27439): Prompt the user for username and password from the window
let Some(credentials) = prompt_user_for_credentials(&context.state.embedder_proxy)
else {
return response;
};
let Some(username) = credentials.username else {
return response;
};
let Some(password) = credentials.password else {
return response;
};
// Wrong, but will have to do until we are able to prompt the user
// otherwise this creates an infinite loop
// We basically pretend that the user declined to enter credentials (#33616)
return response;
if let Err(err) = http_request.current_url_mut().set_username(&username) {
error!("error setting username for url: {:?}", err);
return response;
};
if let Err(err) = http_request.current_url_mut().set_password(Some(&password)) {
error!("error setting password for url: {:?}", err);
return response;
};
}
// Make sure this is set to None,
@ -1627,15 +1622,43 @@ async fn http_network_or_cache_fetch(
// TODO(#33616): Step 15.3 If fetchParams is canceled, then return
// the appropriate network error for fetchParams.
// TODO(#33616): Step 15.4 Prompt the end user as appropriate in requests window and store the
// result as a proxy-authentication entry.
// Step 15.4 Prompt the end user as appropriate in requests window
// window and store the result as a proxy-authentication entry.
let Some(credentials) = prompt_user_for_credentials(&context.state.embedder_proxy) else {
return response;
};
let Some(user_name) = credentials.username else {
return response;
};
let Some(password) = credentials.password else {
return response;
};
// store the credentials as a proxy-authentication entry.
let entry = AuthCacheEntry {
user_name,
password,
};
{
let mut auth_cache = context.state.auth_cache.write().unwrap();
let key = http_request.current_url().origin().ascii_serialization();
auth_cache.entries.insert(key, entry);
}
// Make sure this is set to None,
// since we're about to start a new `http_network_or_cache_fetch`.
*done_chan = None;
// Step 15.5 Set response to the result of running HTTP-network-or-cache fetch given fetchParams.
// Wrong, but will have to do until we are able to prompt the user
// otherwise this creates an infinite loop
// We basically pretend that the user declined to enter credentials (#33616)
return response;
response = http_network_or_cache_fetch(
http_request,
false, /* authentication flag */
cors_flag,
done_chan,
context,
)
.await;
}
// TODO(#33616): Step 16. If all of the following are true:
@ -1737,6 +1760,29 @@ impl Drop for ResponseEndTimer {
}
}
fn prompt_user_for_credentials(
embedder_proxy: &Mutex<EmbedderProxy>,
) -> Option<PromptCredentialsInput> {
let proxy = embedder_proxy.lock().unwrap();
let (ipc_sender, ipc_receiver) = ipc::channel().unwrap();
proxy.send((
None,
EmbedderMsg::Prompt(
PromptDefinition::Credentials(ipc_sender),
PromptOrigin::Trusted,
),
));
let Ok(credentials) = ipc_receiver.recv() else {
warn!("error getting user credentials");
return None;
};
Some(credentials)
}
/// [HTTP network fetch](https://fetch.spec.whatwg.org/#http-network-fetch)
async fn http_network_fetch(
request: &mut Request,