From 113ef375b02397c182ee28ad265b91b0e8f1c58a Mon Sep 17 00:00:00 2001 From: Dana Keeler Date: Wed, 9 Aug 2023 20:47:56 +0000 Subject: [PATCH] Bug 1676679 - support virtual authenticator functions in webdriver r=webdriver-reviewers,jgraham,whimboo Depends on D185198 Differential Revision: https://phabricator.services.mozilla.com/D162624 --- .../geckodriver/marionette/src/webdriver.rs | 60 +++++++++ testing/geckodriver/src/capabilities.rs | 20 +++ testing/geckodriver/src/marionette.rs | 118 ++++++++++++++++-- .../wptrunner/executors/executormarionette.py | 19 +-- testing/webdriver/src/capabilities.rs | 71 ++++++++++- testing/webdriver/src/command.rs | 113 ++++++++++++++++- testing/webdriver/src/common.rs | 17 +++ testing/webdriver/src/httpapi.rs | 42 +++++++ testing/webdriver/src/response.rs | 7 +- 9 files changed, 442 insertions(+), 25 deletions(-) diff --git a/testing/geckodriver/marionette/src/webdriver.rs b/testing/geckodriver/marionette/src/webdriver.rs index fa1a48927c6d..87283804a09e 100644 --- a/testing/geckodriver/marionette/src/webdriver.rs +++ b/testing/geckodriver/marionette/src/webdriver.rs @@ -129,6 +129,52 @@ impl Default for PrintMargins { } } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum WebAuthnProtocol { + #[serde(rename = "ctap1/u2f")] + Ctap1U2f, + #[serde(rename = "ctap2")] + Ctap2, + #[serde(rename = "ctap2_1")] + Ctap2_1, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub enum AuthenticatorTransport { + Usb, + Nfc, + Ble, + SmartCard, + Hybrid, + Internal, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct AuthenticatorParameters { + pub protocol: WebAuthnProtocol, + pub transport: AuthenticatorTransport, + pub has_resident_key: bool, + pub has_user_verification: bool, + pub is_user_consenting: bool, + pub is_user_verified: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct CredentialParameters { + pub credential_id: String, + pub is_resident_credential: bool, + pub rp_id: String, + pub private_key: String, + pub user_handle: String, + pub sign_count: u64, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct UserVerificationParameters { + pub is_user_verified: bool, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ScreenshotOptions { pub id: Option, @@ -296,6 +342,20 @@ pub enum Command { TakeFullScreenshot(ScreenshotOptions), #[serde(rename = "WebDriver:TakeScreenshot")] TakeScreenshot(ScreenshotOptions), + #[serde(rename = "WebAuthn:AddVirtualAuthenticator")] + WebAuthnAddVirtualAuthenticator(AuthenticatorParameters), + #[serde(rename = "WebAuthn:RemoveVirtualAuthenticator")] + WebAuthnRemoveVirtualAuthenticator, + #[serde(rename = "WebAuthn:AddCredential")] + WebAuthnAddCredential(CredentialParameters), + #[serde(rename = "WebAuthn:GetCredentials")] + WebAuthnGetCredentials, + #[serde(rename = "WebAuthn:RemoveCredential")] + WebAuthnRemoveCredential, + #[serde(rename = "WebAuthn:RemoveAllCredentials")] + WebAuthnRemoveAllCredentials, + #[serde(rename = "WebAuthn:SetUserVerified")] + WebAuthnSetUserVerified(UserVerificationParameters), } #[cfg(test)] diff --git a/testing/geckodriver/src/capabilities.rs b/testing/geckodriver/src/capabilities.rs index eda75ad62b96..40b67d9be8b2 100644 --- a/testing/geckodriver/src/capabilities.rs +++ b/testing/geckodriver/src/capabilities.rs @@ -184,6 +184,26 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> { Ok(true) } + fn webauthn_virtual_authenticators(&mut self, _: &Capabilities) -> WebDriverResult { + Ok(true) + } + + fn webauthn_extension_uvm(&mut self, _: &Capabilities) -> WebDriverResult { + Ok(false) + } + + fn webauthn_extension_prf(&mut self, _: &Capabilities) -> WebDriverResult { + Ok(false) + } + + fn webauthn_extension_large_blob(&mut self, _: &Capabilities) -> WebDriverResult { + Ok(false) + } + + fn webauthn_extension_cred_blob(&mut self, _: &Capabilities) -> WebDriverResult { + Ok(false) + } + fn validate_custom(&mut self, name: &str, value: &Value) -> WebDriverResult<()> { if !name.starts_with("moz:") { return Ok(()); diff --git a/testing/geckodriver/src/marionette.rs b/testing/geckodriver/src/marionette.rs index 836382b7dbf8..1e3c5d9dcd1d 100644 --- a/testing/geckodriver/src/marionette.rs +++ b/testing/geckodriver/src/marionette.rs @@ -17,11 +17,15 @@ use marionette_rs::common::{ use marionette_rs::marionette::AppStatus; use marionette_rs::message::{Command, Message, MessageId, Request}; use marionette_rs::webdriver::{ - Command as MarionetteWebDriverCommand, Keys as MarionetteKeys, Locator as MarionetteLocator, - NewWindow as MarionetteNewWindow, PrintMargins as MarionettePrintMargins, - PrintOrientation as MarionettePrintOrientation, PrintPage as MarionettePrintPage, - PrintParameters as MarionettePrintParameters, ScreenshotOptions, Script as MarionetteScript, - Selector as MarionetteSelector, Url as MarionetteUrl, WindowRect as MarionetteWindowRect, + AuthenticatorParameters as MarionetteAuthenticatorParameters, + AuthenticatorTransport as MarionetteAuthenticatorTransport, + Command as MarionetteWebDriverCommand, CredentialParameters as MarionetteCredentialParameters, + Keys as MarionetteKeys, Locator as MarionetteLocator, NewWindow as MarionetteNewWindow, + PrintMargins as MarionettePrintMargins, PrintOrientation as MarionettePrintOrientation, + PrintPage as MarionettePrintPage, PrintParameters as MarionettePrintParameters, + ScreenshotOptions, Script as MarionetteScript, Selector as MarionetteSelector, + Url as MarionetteUrl, UserVerificationParameters as MarionetteUserVerificationParameters, + WebAuthnProtocol as MarionetteWebAuthnProtocol, WindowRect as MarionetteWindowRect, }; use mozdevice::AndroidStorageInput; use serde::de::{self, Deserialize, Deserializer}; @@ -49,18 +53,21 @@ use webdriver::command::WebDriverCommand::{ GetWindowRect, GoBack, GoForward, IsDisplayed, IsEnabled, IsSelected, MaximizeWindow, MinimizeWindow, NewSession, NewWindow, PerformActions, Print, Refresh, ReleaseActions, SendAlertText, SetTimeouts, SetWindowRect, Status, SwitchToFrame, SwitchToParentFrame, - SwitchToWindow, TakeElementScreenshot, TakeScreenshot, + SwitchToWindow, TakeElementScreenshot, TakeScreenshot, WebAuthnAddCredential, + WebAuthnAddVirtualAuthenticator, WebAuthnGetCredentials, WebAuthnRemoveAllCredentials, + WebAuthnRemoveCredential, WebAuthnRemoveVirtualAuthenticator, WebAuthnSetUserVerified, }; use webdriver::command::{ - ActionsParameters, AddCookieParameters, GetNamedCookieParameters, GetParameters, - JavascriptCommandParameters, LocatorParameters, NewSessionParameters, NewWindowParameters, - PrintMargins, PrintOrientation, PrintPage, PrintParameters, SendKeysParameters, - SwitchToFrameParameters, SwitchToWindowParameters, TimeoutsParameters, WindowRectParameters, + ActionsParameters, AddCookieParameters, AuthenticatorParameters, AuthenticatorTransport, + GetNamedCookieParameters, GetParameters, JavascriptCommandParameters, LocatorParameters, + NewSessionParameters, NewWindowParameters, PrintMargins, PrintOrientation, PrintPage, + PrintParameters, SendKeysParameters, SwitchToFrameParameters, SwitchToWindowParameters, + TimeoutsParameters, UserVerificationParameters, WebAuthnProtocol, WindowRectParameters, }; use webdriver::command::{WebDriverCommand, WebDriverMessage}; use webdriver::common::{ - Cookie, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, ELEMENT_KEY, FRAME_KEY, - SHADOW_KEY, WINDOW_KEY, + Cookie, CredentialParameters, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, + ELEMENT_KEY, FRAME_KEY, SHADOW_KEY, WINDOW_KEY, }; use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult}; use webdriver::response::{ @@ -447,7 +454,14 @@ impl MarionetteSession { | GetAlertText | TakeScreenshot | Print(_) - | TakeElementScreenshot(_) => { + | TakeElementScreenshot(_) + | WebAuthnAddVirtualAuthenticator(_) + | WebAuthnRemoveVirtualAuthenticator + | WebAuthnAddCredential(_) + | WebAuthnGetCredentials + | WebAuthnRemoveCredential + | WebAuthnRemoveAllCredentials + | WebAuthnSetUserVerified(_) => { WebDriverResponse::Generic(resp.into_value_response(true)?) } GetTimeouts => { @@ -949,6 +963,27 @@ fn try_convert_to_marionette_message( Print(ref x) => Some(Command::WebDriver(MarionetteWebDriverCommand::Print( x.to_marionette()?, ))), + WebAuthnAddVirtualAuthenticator(ref x) => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnAddVirtualAuthenticator(x.to_marionette()?), + )), + WebAuthnRemoveVirtualAuthenticator => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnRemoveVirtualAuthenticator, + )), + WebAuthnAddCredential(ref x) => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnAddCredential(x.to_marionette()?), + )), + WebAuthnGetCredentials => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnGetCredentials, + )), + WebAuthnRemoveCredential => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnRemoveCredential, + )), + WebAuthnRemoveAllCredentials => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnRemoveAllCredentials, + )), + WebAuthnSetUserVerified(ref x) => Some(Command::WebDriver( + MarionetteWebDriverCommand::WebAuthnSetUserVerified(x.to_marionette()?), + )), Refresh => Some(Command::WebDriver(MarionetteWebDriverCommand::Refresh)), ReleaseActions => Some(Command::WebDriver( MarionetteWebDriverCommand::ReleaseActions, @@ -1467,6 +1502,63 @@ impl ToMarionette for PrintMargins { } } +impl ToMarionette for AuthenticatorParameters { + fn to_marionette(&self) -> WebDriverResult { + Ok(MarionetteAuthenticatorParameters { + protocol: self.protocol.to_marionette()?, + transport: self.transport.to_marionette()?, + has_resident_key: self.has_resident_key, + has_user_verification: self.has_user_verification, + is_user_consenting: self.is_user_consenting, + is_user_verified: self.is_user_verified, + }) + } +} + +impl ToMarionette for AuthenticatorTransport { + fn to_marionette(&self) -> WebDriverResult { + Ok(match self { + AuthenticatorTransport::Usb => MarionetteAuthenticatorTransport::Usb, + AuthenticatorTransport::Nfc => MarionetteAuthenticatorTransport::Nfc, + AuthenticatorTransport::Ble => MarionetteAuthenticatorTransport::Ble, + AuthenticatorTransport::SmartCard => MarionetteAuthenticatorTransport::SmartCard, + AuthenticatorTransport::Hybrid => MarionetteAuthenticatorTransport::Hybrid, + AuthenticatorTransport::Internal => MarionetteAuthenticatorTransport::Internal, + }) + } +} + +impl ToMarionette for CredentialParameters { + fn to_marionette(&self) -> WebDriverResult { + Ok(MarionetteCredentialParameters { + credential_id: self.credential_id.clone(), + is_resident_credential: self.is_resident_credential, + rp_id: self.rp_id.clone(), + private_key: self.private_key.clone(), + user_handle: self.user_handle.clone(), + sign_count: self.sign_count, + }) + } +} + +impl ToMarionette for UserVerificationParameters { + fn to_marionette(&self) -> WebDriverResult { + Ok(MarionetteUserVerificationParameters { + is_user_verified: self.is_user_verified, + }) + } +} + +impl ToMarionette for WebAuthnProtocol { + fn to_marionette(&self) -> WebDriverResult { + Ok(match self { + WebAuthnProtocol::Ctap1U2f => MarionetteWebAuthnProtocol::Ctap1U2f, + WebAuthnProtocol::Ctap2 => MarionetteWebAuthnProtocol::Ctap2, + WebAuthnProtocol::Ctap2_1 => MarionetteWebAuthnProtocol::Ctap2_1, + }) + } +} + impl ToMarionette> for ActionsParameters { fn to_marionette(&self) -> WebDriverResult> { Ok(try_opt!( diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py index 95f186082042..a3b56fe08df1 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/executormarionette.py @@ -48,10 +48,11 @@ from .protocol import (AccessibilityProtocolPart, def do_delayed_imports(): - global errors, marionette, Addons + global errors, marionette, Addons, WebAuthn from marionette_driver import marionette, errors from marionette_driver.addons import Addons + from marionette_driver.webauthn import WebAuthn def _switch_to_window(marionette, handle): @@ -590,28 +591,28 @@ class MarionetteGenerateTestReportProtocolPart(GenerateTestReportProtocolPart): class MarionetteVirtualAuthenticatorProtocolPart(VirtualAuthenticatorProtocolPart): def setup(self): - self.marionette = self.parent.marionette + self.webauthn = WebAuthn(self.parent.marionette) def add_virtual_authenticator(self, config): - raise NotImplementedError("add_virtual_authenticator not yet implemented") + return self.webauthn.add_virtual_authenticator(config) def remove_virtual_authenticator(self, authenticator_id): - raise NotImplementedError("remove_virtual_authenticator not yet implemented") + self.webauthn.remove_virtual_authenticator(authenticator_id) def add_credential(self, authenticator_id, credential): - raise NotImplementedError("add_credential not yet implemented") + self.webauthn.add_credential(authenticator_id, credential) def get_credentials(self, authenticator_id): - raise NotImplementedError("get_credentials not yet implemented") + return self.webauthn.get_credentials(authenticator_id) def remove_credential(self, authenticator_id, credential_id): - raise NotImplementedError("remove_credential not yet implemented") + self.webauthn.remove_credential(authenticator_id, credential_id) def remove_all_credentials(self, authenticator_id): - raise NotImplementedError("remove_all_credentials not yet implemented") + self.webauthn.remove_all_credentials(authenticator_id) def set_user_verified(self, authenticator_id, uv): - raise NotImplementedError("set_user_verified not yet implemented") + self.webauthn.set_user_verified(authenticator_id, uv) class MarionetteSetPermissionProtocolPart(SetPermissionProtocolPart): diff --git a/testing/webdriver/src/capabilities.rs b/testing/webdriver/src/capabilities.rs index 53dd8306b043..ee588f93fbdc 100644 --- a/testing/webdriver/src/capabilities.rs +++ b/testing/webdriver/src/capabilities.rs @@ -53,6 +53,25 @@ pub trait BrowserCapabilities { /// Whether a WebSocket URL for the created session has to be returned fn web_socket_url(&mut self, _: &Capabilities) -> WebDriverResult; + /// Indicates whether the endpoint node supports all Virtual Authenticators commands. + fn webauthn_virtual_authenticators(&mut self, _: &Capabilities) -> WebDriverResult; + + /// Indicates whether the endpoint node WebAuthn WebDriver implementation supports the User + /// Verification Method extension. + fn webauthn_extension_uvm(&mut self, _: &Capabilities) -> WebDriverResult; + + /// Indicates whether the endpoint node WebAuthn WebDriver implementation supports the prf + /// extension. + fn webauthn_extension_prf(&mut self, _: &Capabilities) -> WebDriverResult; + + /// Indicates whether the endpoint node WebAuthn WebDriver implementation supports the + /// largeBlob extension. + fn webauthn_extension_large_blob(&mut self, _: &Capabilities) -> WebDriverResult; + + /// Indicates whether the endpoint node WebAuthn WebDriver implementation supports the credBlob + /// extension. + fn webauthn_extension_cred_blob(&mut self, _: &Capabilities) -> WebDriverResult; + fn accept_proxy( &mut self, proxy_settings: &Map, @@ -136,7 +155,12 @@ impl SpecNewSessionParameters { x @ "acceptInsecureCerts" | x @ "setWindowRect" | x @ "strictFileInteractability" - | x @ "webSocketUrl" => { + | x @ "webSocketUrl" + | x @ "webauthn:virtualAuthenticators" + | x @ "webauthn:extension:uvm" + | x @ "webauthn:extension:prf" + | x @ "webauthn:extension:largeBlob" + | x @ "webauthn:extension:credBlob" => { if !value.is_boolean() { return Err(WebDriverError::new( ErrorStatus::InvalidArgument, @@ -533,6 +557,51 @@ impl CapabilitiesMatching for SpecNewSessionParameters { return false; } } + "webauthn:virtualAuthenticators" => { + if value.as_bool().unwrap_or(false) + && !browser_capabilities + .webauthn_virtual_authenticators(merged) + .unwrap_or(false) + { + return false; + } + } + "webauthn:extension:uvm" => { + if value.as_bool().unwrap_or(false) + && !browser_capabilities + .webauthn_extension_uvm(merged) + .unwrap_or(false) + { + return false; + } + } + "webauthn:extension:prf" => { + if value.as_bool().unwrap_or(false) + && !browser_capabilities + .webauthn_extension_prf(merged) + .unwrap_or(false) + { + return false; + } + } + "webauthn:extension:largeBlob" => { + if value.as_bool().unwrap_or(false) + && !browser_capabilities + .webauthn_extension_large_blob(merged) + .unwrap_or(false) + { + return false; + } + } + "webauthn:extension:credBlob" => { + if value.as_bool().unwrap_or(false) + && !browser_capabilities + .webauthn_extension_cred_blob(merged) + .unwrap_or(false) + { + return false; + } + } name => { if name.contains(':') { if !browser_capabilities diff --git a/testing/webdriver/src/command.rs b/testing/webdriver/src/command.rs index 91cde6ebbb75..f29615832a81 100644 --- a/testing/webdriver/src/command.rs +++ b/testing/webdriver/src/command.rs @@ -7,7 +7,9 @@ use crate::capabilities::{ BrowserCapabilities, Capabilities, CapabilitiesMatching, LegacyNewSessionParameters, SpecNewSessionParameters, }; -use crate::common::{Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, MAX_SAFE_INTEGER}; +use crate::common::{ + CredentialParameters, Date, FrameId, LocatorStrategy, ShadowRoot, WebElement, MAX_SAFE_INTEGER, +}; use crate::error::{ErrorStatus, WebDriverError, WebDriverResult}; use crate::httpapi::{Route, VoidWebDriverExtensionRoute, WebDriverExtensionRoute}; use crate::Parameters; @@ -79,6 +81,13 @@ pub enum WebDriverCommand { Print(PrintParameters), Status, Extension(T), + WebAuthnAddVirtualAuthenticator(AuthenticatorParameters), + WebAuthnRemoveVirtualAuthenticator, + WebAuthnAddCredential(CredentialParameters), + WebAuthnGetCredentials, + WebAuthnRemoveCredential, + WebAuthnRemoveAllCredentials, + WebAuthnSetUserVerified(UserVerificationParameters), } pub trait WebDriverExtensionCommand: Clone + Send { @@ -401,6 +410,21 @@ impl WebDriverMessage { Route::Print => WebDriverCommand::Print(serde_json::from_str(raw_body)?), Route::Status => WebDriverCommand::Status, Route::Extension(ref extension) => extension.command(params, &body_data)?, + Route::WebAuthnAddVirtualAuthenticator => { + WebDriverCommand::WebAuthnAddVirtualAuthenticator(serde_json::from_str(raw_body)?) + } + Route::WebAuthnRemoveVirtualAuthenticator => { + WebDriverCommand::WebAuthnRemoveVirtualAuthenticator + } + Route::WebAuthnAddCredential => { + WebDriverCommand::WebAuthnAddCredential(serde_json::from_str(raw_body)?) + } + Route::WebAuthnGetCredentials => WebDriverCommand::WebAuthnGetCredentials, + Route::WebAuthnRemoveCredential => WebDriverCommand::WebAuthnRemoveCredential, + Route::WebAuthnRemoveAllCredentials => WebDriverCommand::WebAuthnRemoveAllCredentials, + Route::WebAuthnSetUserVerified => { + WebDriverCommand::WebAuthnSetUserVerified(serde_json::from_str(raw_body)?) + } }; Ok(WebDriverMessage::new(session_id, command)) } @@ -630,6 +654,52 @@ impl Default for PrintMargins { } } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum WebAuthnProtocol { + #[serde(rename = "ctap1/u2f")] + Ctap1U2f, + #[serde(rename = "ctap2")] + Ctap2, + #[serde(rename = "ctap2_1")] + Ctap2_1, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "kebab-case")] +pub enum AuthenticatorTransport { + Usb, + Nfc, + Ble, + SmartCard, + Hybrid, + Internal, +} + +fn default_as_true() -> bool { + true +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct AuthenticatorParameters { + pub protocol: WebAuthnProtocol, + pub transport: AuthenticatorTransport, + #[serde(default)] + pub has_resident_key: bool, + #[serde(default)] + pub has_user_verification: bool, + #[serde(default = "default_as_true")] + pub is_user_consenting: bool, + #[serde(default)] + pub is_user_verified: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct UserVerificationParameters { + #[serde(rename = "isUserVerified")] + pub is_user_verified: bool, +} + fn deserialize_to_positive_f64<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -1310,6 +1380,47 @@ mod tests { assert!(serde_json::from_value::(json!({"scale": 3})).is_err()); } + #[test] + fn test_json_authenticator() { + let params = AuthenticatorParameters { + protocol: WebAuthnProtocol::Ctap1U2f, + transport: AuthenticatorTransport::Usb, + has_resident_key: false, + has_user_verification: false, + is_user_consenting: false, + is_user_verified: false, + }; + assert_de( + ¶ms, + json!({"protocol": "ctap1/u2f", "transport": "usb", "hasResidentKey": false, "hasUserVerification": false, "isUserConsenting": false, "isUserVerified": false}), + ); + } + + #[test] + fn test_json_credential() { + let encoded_string = base64::encode_config(b"hello internet~", base64::URL_SAFE); + let params = CredentialParameters { + credential_id: r"c3VwZXIgcmVhZGVy".to_string(), + is_resident_credential: true, + rp_id: "valid.rpid".to_string(), + private_key: encoded_string.clone(), + user_handle: encoded_string.clone(), + sign_count: 0, + }; + assert_de( + ¶ms, + json!({"credentialId": r"c3VwZXIgcmVhZGVy", "isResidentCredential": true, "rpId": "valid.rpid", "privateKey": encoded_string, "userHandle": encoded_string, "signCount": 0}), + ); + } + + #[test] + fn test_json_user_verification() { + let params = UserVerificationParameters { + is_user_verified: false, + }; + assert_de(¶ms, json!({"isUserVerified": false})); + } + #[test] fn test_json_send_keys_parameters_with_value() { assert_de( diff --git a/testing/webdriver/src/common.rs b/testing/webdriver/src/common.rs index f297affcd80b..59224f9ff58a 100644 --- a/testing/webdriver/src/common.rs +++ b/testing/webdriver/src/common.rs @@ -32,6 +32,23 @@ pub struct Cookie { pub same_site: Option, } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct CredentialParameters { + #[serde(rename = "credentialId")] + pub credential_id: String, + #[serde(rename = "isResidentCredential")] + pub is_resident_credential: bool, + #[serde(rename = "rpId")] + pub rp_id: String, + #[serde(rename = "privateKey")] + pub private_key: String, + #[serde(rename = "userHandle")] + #[serde(default)] + pub user_handle: String, + #[serde(rename = "signCount")] + pub sign_count: u64, +} + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Date(pub u64); diff --git a/testing/webdriver/src/httpapi.rs b/testing/webdriver/src/httpapi.rs index 4454d3a27f75..8a239923898f 100644 --- a/testing/webdriver/src/httpapi.rs +++ b/testing/webdriver/src/httpapi.rs @@ -308,6 +308,41 @@ pub fn standard_routes() -> Vec<(Method, &'static st Route::ReleaseActions, ), (Method::POST, "/session/{sessionId}/print", Route::Print), + ( + Method::POST, + "/sessions/{sessionId}/webauthn/authenticator", + Route::WebAuthnAddVirtualAuthenticator, + ), + ( + Method::DELETE, + "/sessions/{sessionId}/webauthn/authenticator/{authenticatorId}", + Route::WebAuthnRemoveVirtualAuthenticator, + ), + ( + Method::POST, + "/sessions/{sessionId}/webauthn/authenticator/{authenticatorId}/credential", + Route::WebAuthnAddCredential, + ), + ( + Method::GET, + "/sessions/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials", + Route::WebAuthnGetCredentials, + ), + ( + Method::DELETE, + "/sessions/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials/{credentialId}", + Route::WebAuthnRemoveCredential, + ), + ( + Method::DELETE, + "/sessions/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials", + Route::WebAuthnRemoveAllCredentials, + ), + ( + Method::POST, + "/sessions/{sessionId}/webauthn/authenticator/{authenticatorId}/uv", + Route::WebAuthnSetUserVerified, + ), (Method::GET, "/status", Route::Status), ] } @@ -381,6 +416,13 @@ pub enum Route { Print, Status, Extension(U), + WebAuthnAddVirtualAuthenticator, + WebAuthnRemoveVirtualAuthenticator, + WebAuthnAddCredential, + WebAuthnGetCredentials, + WebAuthnRemoveCredential, + WebAuthnRemoveAllCredentials, + WebAuthnSetUserVerified, } pub trait WebDriverExtensionRoute: Clone + Send + PartialEq { diff --git a/testing/webdriver/src/response.rs b/testing/webdriver/src/response.rs index a12b91905d94..3b4010c7984f 100644 --- a/testing/webdriver/src/response.rs +++ b/testing/webdriver/src/response.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::common::Cookie; +use crate::common::{Cookie, CredentialParameters}; use serde::ser::{Serialize, Serializer}; use serde_json::Value; @@ -16,6 +16,8 @@ pub enum WebDriverResponse { DeleteSession, ElementRect(ElementRectResponse), Generic(ValueResponse), + WebAuthnAddVirtualAuthenticator(u64), + WebAuthnGetCredentials(GetCredentialsResponse), NewSession(NewSessionResponse), Timeouts(TimeoutsResponse), Void, @@ -78,6 +80,9 @@ pub struct ElementRectResponse { pub height: f64, } +#[derive(Debug, PartialEq, Serialize)] +pub struct GetCredentialsResponse(pub Vec); + #[derive(Debug, PartialEq, Serialize)] pub struct NewSessionResponse { #[serde(rename = "sessionId")]