diff --git a/java/src/org/openqa/selenium/bidi/module/Network.java b/java/src/org/openqa/selenium/bidi/module/Network.java index 5050e368b5519..1237fceeb5023 100644 --- a/java/src/org/openqa/selenium/bidi/module/Network.java +++ b/java/src/org/openqa/selenium/bidi/module/Network.java @@ -169,11 +169,11 @@ public void onResponseCompleted(Consumer consumer) { } } - public void onAuthRequired(Consumer consumer) { + public long onAuthRequired(Consumer consumer) { if (browsingContextIds.isEmpty()) { - this.bidi.addListener(authRequired, consumer); + return this.bidi.addListener(authRequired, consumer); } else { - this.bidi.addListener(browsingContextIds, authRequired, consumer); + return this.bidi.addListener(browsingContextIds, authRequired, consumer); } } diff --git a/java/src/org/openqa/selenium/remote/BUILD.bazel b/java/src/org/openqa/selenium/remote/BUILD.bazel index 07cf0a0ffea52..ff6969e73fcc4 100644 --- a/java/src/org/openqa/selenium/remote/BUILD.bazel +++ b/java/src/org/openqa/selenium/remote/BUILD.bazel @@ -60,6 +60,7 @@ java_library( "//java/src/org/openqa/selenium/bidi", "//java/src/org/openqa/selenium/bidi/log", "//java/src/org/openqa/selenium/bidi/module", + "//java/src/org/openqa/selenium/bidi/network", "//java/src/org/openqa/selenium/bidi/script", "//java/src/org/openqa/selenium/concurrent", "//java/src/org/openqa/selenium/devtools", diff --git a/java/src/org/openqa/selenium/remote/Network.java b/java/src/org/openqa/selenium/remote/Network.java new file mode 100644 index 0000000000000..7c2a4b6893eba --- /dev/null +++ b/java/src/org/openqa/selenium/remote/Network.java @@ -0,0 +1,31 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote; + +import org.openqa.selenium.Beta; +import org.openqa.selenium.UsernameAndPassword; + +@Beta +public interface Network { + + long addAuthenticationHandler(UsernameAndPassword usernameAndPassword); + + void removeAuthenticationHandler(long id); + + void clearAuthenticationHandlers(); +} diff --git a/java/src/org/openqa/selenium/remote/RemoteNetwork.java b/java/src/org/openqa/selenium/remote/RemoteNetwork.java new file mode 100644 index 0000000000000..539be1bbfa5d9 --- /dev/null +++ b/java/src/org/openqa/selenium/remote/RemoteNetwork.java @@ -0,0 +1,79 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.remote; + +import java.util.HashMap; +import java.util.Map; +import org.openqa.selenium.Beta; +import org.openqa.selenium.UsernameAndPassword; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.bidi.BiDi; +import org.openqa.selenium.bidi.HasBiDi; +import org.openqa.selenium.bidi.network.AddInterceptParameters; +import org.openqa.selenium.bidi.network.InterceptPhase; + +@Beta +class RemoteNetwork implements Network { + + private final BiDi biDi; + private final org.openqa.selenium.bidi.module.Network network; + + private final Map authCallbackIdMap = new HashMap<>(); + + public RemoteNetwork(WebDriver driver) { + this.biDi = ((HasBiDi) driver).getBiDi(); + this.network = new org.openqa.selenium.bidi.module.Network(driver); + } + + @Override + public long addAuthenticationHandler(UsernameAndPassword usernameAndPassword) { + String intercept = + network.addIntercept(new AddInterceptParameters(InterceptPhase.AUTH_REQUIRED)); + + long id = + network.onAuthRequired( + responseDetails -> + network.continueWithAuth( + responseDetails.getRequest().getRequestId(), usernameAndPassword)); + + authCallbackIdMap.put(id, intercept); + return id; + } + + @Override + public void removeAuthenticationHandler(long id) { + String intercept = authCallbackIdMap.get(id); + + if (intercept != null) { + network.removeIntercept(intercept); + this.biDi.removeListener(id); + authCallbackIdMap.remove(id); + } + } + + @Override + public void clearAuthenticationHandlers() { + authCallbackIdMap.forEach( + (callback, intercept) -> { + network.removeIntercept(intercept); + this.biDi.removeListener(callback); + }); + + authCallbackIdMap.clear(); + } +} diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index ed3a445a26909..4afa13ea9be83 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java @@ -130,6 +130,8 @@ public class RemoteWebDriver private Script remoteScript; + private Network remoteNetwork; + // For cglib protected RemoteWebDriver() { this.capabilities = init(new ImmutableCapabilities()); @@ -504,6 +506,13 @@ public Script script() { return this.remoteScript; } + public Network network() { + if (this.remoteNetwork == null) { + this.remoteNetwork = new RemoteNetwork(this); + } + return this.remoteNetwork; + } + protected JsonToWebElementConverter getElementConverter() { return converter; } diff --git a/java/test/org/openqa/selenium/WebNetworkTest.java b/java/test/org/openqa/selenium/WebNetworkTest.java new file mode 100644 index 0000000000000..0684544f3c48e --- /dev/null +++ b/java/test/org/openqa/selenium/WebNetworkTest.java @@ -0,0 +1,99 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.environment.webserver.AppServer; +import org.openqa.selenium.environment.webserver.NettyAppServer; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.testing.JupiterTestBase; +import org.openqa.selenium.testing.NotYetImplemented; +import org.openqa.selenium.testing.drivers.Browser; + +class WebNetworkTest extends JupiterTestBase { + + private String page; + private AppServer server; + + @BeforeEach + public void setUp() { + server = new NettyAppServer(); + server.start(); + } + + @AfterEach + public void cleanUp() { + driver.quit(); + } + + @Test + @NotYetImplemented(Browser.CHROME) + @NotYetImplemented(Browser.EDGE) + void canAddAuthenticationHandler() { + ((RemoteWebDriver) driver) + .network() + .addAuthenticationHandler(new UsernameAndPassword("test", "test")); + + page = server.whereIs("basicAuth"); + driver.get(page); + + assertThat(driver.findElement(By.tagName("h1")).getText()).isEqualTo("authorized"); + } + + @Test + @NotYetImplemented(Browser.CHROME) + @NotYetImplemented(Browser.EDGE) + void canRemoveAuthenticationHandler() { + long id = + ((RemoteWebDriver) driver) + .network() + .addAuthenticationHandler(new UsernameAndPassword("test", "test")); + + ((RemoteWebDriver) driver).network().removeAuthenticationHandler(id); + page = server.whereIs("basicAuth"); + driver.get(page); + + assertThatExceptionOfType(UnhandledAlertException.class) + .isThrownBy(() -> driver.findElement(By.tagName("h1"))); + } + + @Test + @NotYetImplemented(Browser.CHROME) + @NotYetImplemented(Browser.EDGE) + void canClearAuthenticationHandlers() { + ((RemoteWebDriver) driver) + .network() + .addAuthenticationHandler(new UsernameAndPassword("test", "test")); + + ((RemoteWebDriver) driver) + .network() + .addAuthenticationHandler(new UsernameAndPassword("test1", "test1")); + + ((RemoteWebDriver) driver).network().clearAuthenticationHandlers(); + page = server.whereIs("basicAuth"); + driver.get(page); + + assertThatExceptionOfType(UnhandledAlertException.class) + .isThrownBy(() -> driver.findElement(By.tagName("h1"))); + } +}