diff --git a/java/src/org/openqa/selenium/BUILD.bazel b/java/src/org/openqa/selenium/BUILD.bazel index d94215af87b8e..1e494729adfe6 100644 --- a/java/src/org/openqa/selenium/BUILD.bazel +++ b/java/src/org/openqa/selenium/BUILD.bazel @@ -12,6 +12,7 @@ java_export( name = "core", srcs = glob([ "*.java", + "federatedcredentialmanagement/*.java", "html5/*.java", "internal/*.java", "interactions/**/*.java", diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java new file mode 100644 index 0000000000000..98913f28720c9 --- /dev/null +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementAccount.java @@ -0,0 +1,83 @@ +// 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.federatedcredentialmanagement; + +import java.util.Map; + +public class FederatedCredentialManagementAccount { + private final String accountId; + private final String email; + private final String name; + private final String givenName; + private final String pictureUrl; + private final String idpConfigUrl; + private final String loginState; + private final String termsOfServiceUrl; + private final String privacyPolicyUrl; + + public static final String LOGIN_STATE_SIGNIN = "SignIn"; + public static final String LOGIN_STATE_SIGNUP = "SignUp"; + + public FederatedCredentialManagementAccount(Map dict) { + accountId = (String) dict.getOrDefault("accountId", null); + email = (String) dict.getOrDefault("email", null); + name = (String) dict.getOrDefault("name", null); + givenName = (String) dict.getOrDefault("givenName", null); + pictureUrl = (String) dict.getOrDefault("pictureUrl", null); + idpConfigUrl = (String) dict.getOrDefault("idpConfigUrl", null); + loginState = (String) dict.getOrDefault("loginState", null); + termsOfServiceUrl = (String) dict.getOrDefault("termsOfServiceUrl", null); + privacyPolicyUrl = (String) dict.getOrDefault("privacyPolicyUrl", null); + } + + public String getAccountid() { + return accountId; + } + + public String getEmail() { + return email; + } + + public String getName() { + return name; + } + + public String getGivenName() { + return givenName; + } + + public String getPictureUrl() { + return pictureUrl; + } + + public String getIdpConfigUrl() { + return idpConfigUrl; + } + + public String getLoginState() { + return loginState; + } + + public String getTermsOfServiceUrl() { + return termsOfServiceUrl; + } + + public String getPrivacyPolicyUrl() { + return privacyPolicyUrl; + } +}; diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java new file mode 100644 index 0000000000000..0ab5b0bf36970 --- /dev/null +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/FederatedCredentialManagementDialog.java @@ -0,0 +1,38 @@ +// 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.federatedcredentialmanagement; + +import java.util.List; + +public interface FederatedCredentialManagementDialog { + + String DIALOG_TYPE_ACCOUNT_LIST = "AccountChooser"; + String DIALOG_TYPE_AUTH_REAUTH = "AutoReauthn"; + + void cancelDialog(); + + void selectAccount(int index); + + String getDialogType(); + + String getTitle(); + + String getSubtitle(); + + List getAccounts(); +} diff --git a/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java b/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java new file mode 100644 index 0000000000000..70f3bc0afb8e0 --- /dev/null +++ b/java/src/org/openqa/selenium/federatedcredentialmanagement/HasFederatedCredentialManagement.java @@ -0,0 +1,41 @@ +// 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.federatedcredentialmanagement; + +import org.openqa.selenium.Beta; + +/** + * Used by classes to indicate that they can interact with FedCM dialogs. + */ +@Beta +public interface HasFederatedCredentialManagement { + // FedCM by default delays promise resolution in failure cases for privacy + // reasons (https://fedidcg.github.io/FedCM/#ref-for-setdelayenabled); + // this function allows turning it off to let tests run faster where this + // is not relevant. + void setDelayEnabled(boolean enabled); + + // If a user agent triggers a cooldown when the account chooser is dismissed, + // this function resets that cooldown so that the dialog can be triggered + // again immediately. + void resetCooldown(); + + // Gets the currently open FedCM dialog, or null if there is no dialog. + FederatedCredentialManagementDialog getFederatedCredentialManagementDialog(); +} + diff --git a/java/src/org/openqa/selenium/remote/DriverCommand.java b/java/src/org/openqa/selenium/remote/DriverCommand.java index 993129d30bea7..3d6c90ab8ae5c 100644 --- a/java/src/org/openqa/selenium/remote/DriverCommand.java +++ b/java/src/org/openqa/selenium/remote/DriverCommand.java @@ -162,6 +162,15 @@ public interface DriverCommand { String REMOVE_CREDENTIAL = "removeCredential"; String REMOVE_ALL_CREDENTIALS = "removeAllCredentials"; String SET_USER_VERIFIED = "setUserVerified"; + // Federated Credential Management API + // https://fedidcg.github.io/FedCM/#automation + String CANCEL_DIALOG = "cancelDialog"; + String SELECT_ACCOUNT = "selectAccount"; + String GET_ACCOUNTS = "getAccounts"; + String GET_FEDCM_TITLE = "getFedCmTitle"; + String GET_FEDCM_DIALOG_TYPE = "getFedCmDialogType"; + String SET_DELAY_ENABLED = "setDelayEnabled"; + String RESET_COOLDOWN = "resetCooldown"; static CommandPayload NEW_SESSION(Capabilities capabilities) { Require.nonNull("Capabilities", capabilities); @@ -401,4 +410,14 @@ static CommandPayload SET_CURRENT_WINDOW_SIZE(Dimension targetSize) { SET_CURRENT_WINDOW_SIZE, ImmutableMap.of("width", targetSize.width, "height", targetSize.height)); } + + static CommandPayload SELECT_ACCOUNT(int index) { + return new CommandPayload( + SELECT_ACCOUNT, ImmutableMap.of("accountIndex", index)); + } + + static CommandPayload SET_DELAY_ENABLED(boolean enabled) { + return new CommandPayload( + SET_DELAY_ENABLED, ImmutableMap.of("enabled", enabled)); + } } diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index 8f08c52d0e691..1c4753ac867b7 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java @@ -54,6 +54,7 @@ import org.openqa.selenium.ImmutableCapabilities; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.MutableCapabilities; +import org.openqa.selenium.NoAlertPresentException; import org.openqa.selenium.NoSuchFrameException; import org.openqa.selenium.NoSuchWindowException; import org.openqa.selenium.OutputType; @@ -72,6 +73,8 @@ import org.openqa.selenium.bidi.HasBiDi; import org.openqa.selenium.devtools.DevTools; import org.openqa.selenium.devtools.HasDevTools; +import org.openqa.selenium.federatedcredentialmanagement.HasFederatedCredentialManagement; +import org.openqa.selenium.federatedcredentialmanagement.FederatedCredentialManagementDialog; import org.openqa.selenium.interactions.Interactive; import org.openqa.selenium.interactions.Sequence; import org.openqa.selenium.internal.Require; @@ -97,6 +100,7 @@ public class RemoteWebDriver implements WebDriver, JavascriptExecutor, HasCapabilities, + HasFederatedCredentialManagement, HasVirtualAuthenticator, Interactive, PrintsPage, @@ -622,6 +626,28 @@ public void removeVirtualAuthenticator(VirtualAuthenticator authenticator) { ImmutableMap.of("authenticatorId", authenticator.getId())); } + @Override + public void setDelayEnabled(boolean enabled) { + execute(DriverCommand.SET_DELAY_ENABLED(enabled)); + } + + @Override + public void resetCooldown() { + execute(DriverCommand.RESET_COOLDOWN); + } + + @Override + public FederatedCredentialManagementDialog getFederatedCredentialManagementDialog() { + FederatedCredentialManagementDialog dialog = new FedCmDialogImpl(executeMethod); + try { + // As long as this does not throw, we're good. + dialog.getDialogType(); + return dialog; + } catch (NoAlertPresentException e) { + return null; + } + } + /** * Override this to be notified at key points in the execution of a command. * diff --git a/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java b/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java index 7542d80896249..ff7adad37bd8c 100644 --- a/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/AbstractHttpCommandCodec.java @@ -26,6 +26,7 @@ import static org.openqa.selenium.remote.DriverCommand.ADD_COOKIE; import static org.openqa.selenium.remote.DriverCommand.ADD_CREDENTIAL; import static org.openqa.selenium.remote.DriverCommand.ADD_VIRTUAL_AUTHENTICATOR; +import static org.openqa.selenium.remote.DriverCommand.CANCEL_DIALOG; import static org.openqa.selenium.remote.DriverCommand.CLEAR_ELEMENT; import static org.openqa.selenium.remote.DriverCommand.CLICK_ELEMENT; import static org.openqa.selenium.remote.DriverCommand.CLOSE; @@ -39,6 +40,7 @@ import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENTS; import static org.openqa.selenium.remote.DriverCommand.FULLSCREEN_CURRENT_WINDOW; import static org.openqa.selenium.remote.DriverCommand.GET; +import static org.openqa.selenium.remote.DriverCommand.GET_ACCOUNTS; import static org.openqa.selenium.remote.DriverCommand.GET_ALL_COOKIES; import static org.openqa.selenium.remote.DriverCommand.GET_ALL_SESSIONS; import static org.openqa.selenium.remote.DriverCommand.GET_APP_CACHE_STATUS; @@ -55,6 +57,8 @@ import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_TAG_NAME; import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_TEXT; import static org.openqa.selenium.remote.DriverCommand.GET_ELEMENT_VALUE_OF_CSS_PROPERTY; +import static org.openqa.selenium.remote.DriverCommand.GET_FEDCM_DIALOG_TYPE; +import static org.openqa.selenium.remote.DriverCommand.GET_FEDCM_TITLE; import static org.openqa.selenium.remote.DriverCommand.GET_LOCATION; import static org.openqa.selenium.remote.DriverCommand.GET_LOG; import static org.openqa.selenium.remote.DriverCommand.GET_NETWORK_CONNECTION; @@ -75,10 +79,13 @@ import static org.openqa.selenium.remote.DriverCommand.REMOVE_ALL_CREDENTIALS; import static org.openqa.selenium.remote.DriverCommand.REMOVE_CREDENTIAL; import static org.openqa.selenium.remote.DriverCommand.REMOVE_VIRTUAL_AUTHENTICATOR; +import static org.openqa.selenium.remote.DriverCommand.RESET_COOLDOWN; import static org.openqa.selenium.remote.DriverCommand.SCREENSHOT; +import static org.openqa.selenium.remote.DriverCommand.SELECT_ACCOUNT; import static org.openqa.selenium.remote.DriverCommand.SEND_KEYS_TO_ELEMENT; import static org.openqa.selenium.remote.DriverCommand.SET_ALERT_CREDENTIALS; import static org.openqa.selenium.remote.DriverCommand.SET_BROWSER_ONLINE; +import static org.openqa.selenium.remote.DriverCommand.SET_DELAY_ENABLED; import static org.openqa.selenium.remote.DriverCommand.SET_LOCATION; import static org.openqa.selenium.remote.DriverCommand.SET_NETWORK_CONNECTION; import static org.openqa.selenium.remote.DriverCommand.SET_SCREEN_ORIENTATION; @@ -220,6 +227,16 @@ public AbstractHttpCommandCodec() { defineCommand(REMOVE_CREDENTIAL, delete(webauthnId + "/credentials/:credentialId")); defineCommand(REMOVE_ALL_CREDENTIALS, delete(webauthnId + "/credentials")); defineCommand(SET_USER_VERIFIED, post(webauthnId + "/uv")); + + // Federated Credential Management API + String fedcm = sessionId + "/fedcm"; + defineCommand(CANCEL_DIALOG, post(fedcm + "/canceldialog")); + defineCommand(SELECT_ACCOUNT, post(fedcm + "/selectaccount")); + defineCommand(GET_ACCOUNTS, get(fedcm + "/accountlist")); + defineCommand(GET_FEDCM_TITLE, get(fedcm + "/gettitle")); + defineCommand(GET_FEDCM_DIALOG_TYPE, get(fedcm + "/getdialogtype")); + defineCommand(SET_DELAY_ENABLED, post(fedcm + "/setdelayenabled")); + defineCommand(RESET_COOLDOWN, post(fedcm + "/resetCooldown")); } protected static CommandSpec delete(String path) {