Commit 6cc1d3cc authored by Nina Satragno's avatar Nina Satragno Committed by Commit Bot

[chromedriver] Add RemoveCredential WebAuthn command

Add the `RemoveCredential` WebAuthn command to ChromeDriver.

This is one in a series of patches intended to create a Testing API for
WebAuthn, for use in Web Platform Tests and by external webauthn tests.

For an overview of overall design, please see
https://docs.google.com/document/d/1bp2cMgjm2HSpvL9-WsJoIQMsBi1oKGQY6CvWD-9WmIQ/edit?usp=sharing

Bug: 922572
Change-Id: I3a547e5cc63af1099327606ccf22e8f916ea34d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1764634
Commit-Queue: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: default avatarJohn Chen <johnchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#689557}
parent fe5f00ca
...@@ -668,6 +668,11 @@ class ChromeDriver(object): ...@@ -668,6 +668,11 @@ class ChromeDriver(object):
params = {'authenticatorId': authenticatorId} params = {'authenticatorId': authenticatorId}
return self.ExecuteCommand(Command.GET_CREDENTIALS, params) return self.ExecuteCommand(Command.GET_CREDENTIALS, params)
def RemoveCredential(self, authenticatorId, credentialId):
params = {'authenticatorId': authenticatorId,
'credentialId': credentialId}
return self.ExecuteCommand(Command.REMOVE_CREDENTIAL, params)
def RemoveAllCredentials(self, authenticatorId): def RemoveAllCredentials(self, authenticatorId):
params = {'authenticatorId': authenticatorId} params = {'authenticatorId': authenticatorId}
return self.ExecuteCommand(Command.REMOVE_ALL_CREDENTIALS, params) return self.ExecuteCommand(Command.REMOVE_ALL_CREDENTIALS, params)
......
...@@ -179,6 +179,10 @@ class Command(object): ...@@ -179,6 +179,10 @@ class Command(object):
GET_CREDENTIALS = ( GET_CREDENTIALS = (
_Method.GET, _Method.GET,
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials') '/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials')
REMOVE_CREDENTIAL = (
_Method.DELETE,
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/'
':credentialId')
REMOVE_ALL_CREDENTIALS = ( REMOVE_ALL_CREDENTIALS = (
_Method.DELETE, _Method.DELETE,
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials') '/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials')
......
...@@ -777,6 +777,14 @@ HttpHandler::HttpHandler( ...@@ -777,6 +777,14 @@ HttpHandler::HttpHandler(
base::BindRepeating( base::BindRepeating(
&ExecuteWebAuthnCommand, &ExecuteWebAuthnCommand,
base::BindRepeating(&ExecuteGetCredentials)))), base::BindRepeating(&ExecuteGetCredentials)))),
CommandMapping(
kDelete,
"session/:sessionId/webauthn/authenticator/:authenticatorId/"
"credentials/:credentialId",
WrapToCommand("RemoveCredential",
base::BindRepeating(
&ExecuteWebAuthnCommand,
base::BindRepeating(&ExecuteRemoveCredential)))),
CommandMapping( CommandMapping(
kDelete, kDelete,
"session/:sessionId/webauthn/authenticator/:authenticatorId/" "session/:sessionId/webauthn/authenticator/:authenticatorId/"
......
...@@ -225,6 +225,7 @@ _ANDROID_NEGATIVE_FILTER['chrome'] = ( ...@@ -225,6 +225,7 @@ _ANDROID_NEGATIVE_FILTER['chrome'] = (
'ChromeDriverSecureContextTest.testRemoveVirtualAuthenticator', 'ChromeDriverSecureContextTest.testRemoveVirtualAuthenticator',
'ChromeDriverSecureContextTest.testAddCredential', 'ChromeDriverSecureContextTest.testAddCredential',
'ChromeDriverSecureContextTest.testGetCredentials', 'ChromeDriverSecureContextTest.testGetCredentials',
'ChromeDriverSecureContextTest.testRemoveCredential',
'ChromeDriverSecureContextTest.testRemoveAllCredentials', 'ChromeDriverSecureContextTest.testRemoveAllCredentials',
'ChromeDriverSecureContextTest.testSetUserVerified', 'ChromeDriverSecureContextTest.testSetUserVerified',
# Covered by Desktop tests; can't create 2 browsers in Android # Covered by Desktop tests; can't create 2 browsers in Android
...@@ -2053,7 +2054,8 @@ class ChromeDriverSecureContextTest(ChromeDriverBaseTest): ...@@ -2053,7 +2054,8 @@ class ChromeDriverSecureContextTest(ChromeDriverBaseTest):
@staticmethod @staticmethod
def UrlSafeBase64Decode(string): def UrlSafeBase64Decode(string):
string = string.encode("utf-8") string = string.encode("utf-8")
string += "=" * (4 - len(string) % 4) if len(string) % 4 != 0:
string += "=" * (4 - len(string) % 4)
return base64.urlsafe_b64decode(string) return base64.urlsafe_b64decode(string)
def setUp(self): def setUp(self):
...@@ -2197,6 +2199,39 @@ class ChromeDriverSecureContextTest(ChromeDriverBaseTest): ...@@ -2197,6 +2199,39 @@ class ChromeDriverSecureContextTest(ChromeDriverBaseTest):
self.assertEquals(1, credentials[0]['signCount']) self.assertEquals(1, credentials[0]['signCount'])
self.assertTrue(credentials[0]['privateKey']) self.assertTrue(credentials[0]['privateKey'])
def testRemoveCredential(self):
script = """
let done = arguments[0];
registerCredential().then(done);
"""
self._driver.Load(self.GetHttpsUrlForFile(
'/chromedriver/webauthn_test.html', 'chromedriver.test'))
authenticatorId = self._driver.AddVirtualAuthenticator(
protocol = 'ctap2',
transport = 'usb',
hasResidentKey = True,
hasUserVerification = True,
)['authenticatorId']
# Register two credentials.
result = self._driver.ExecuteAsyncScript(script)
self.assertEquals('OK', result['status'])
credential1Id = result['credential']['id']
result = self._driver.ExecuteAsyncScript(script)
self.assertEquals('OK', result['status'])
credential2Id = result['credential']['id']
# GetCredentials should return both credentials.
credentials = self._driver.GetCredentials(authenticatorId)['credentials']
self.assertEquals(2, len(credentials))
# Removing the first credential should leave only the first one.
self._driver.RemoveCredential(authenticatorId, credential1Id)
credentials = self._driver.GetCredentials(authenticatorId)['credentials']
self.assertEquals(1, len(credentials))
self.assertEquals(credential2Id, credentials[0]['credentialId'])
def testRemoveAllCredentials(self): def testRemoveAllCredentials(self):
register_credential_script = """ register_credential_script = """
let done = arguments[0]; let done = arguments[0];
......
...@@ -165,6 +165,23 @@ Status ExecuteGetCredentials(WebView* web_view, ...@@ -165,6 +165,23 @@ Status ExecuteGetCredentials(WebView* web_view,
return status; return status;
} }
Status ExecuteRemoveCredential(WebView* web_view,
const base::Value& params,
std::unique_ptr<base::Value>* value) {
base::DictionaryValue mapped_params = MapParams(
{
{"authenticatorId", "authenticatorId"},
{"credentialId", "credentialId"},
},
params);
Status status = ConvertBase64UrlToBase64(&mapped_params, {"credentialId"});
if (status.IsError())
return status;
return web_view->SendCommandAndGetResult("WebAuthn.removeCredential",
std::move(mapped_params), value);
}
Status ExecuteRemoveAllCredentials(WebView* web_view, Status ExecuteRemoveAllCredentials(WebView* web_view,
const base::Value& params, const base::Value& params,
std::unique_ptr<base::Value>* value) { std::unique_ptr<base::Value>* value) {
......
...@@ -49,6 +49,11 @@ Status ExecuteGetCredentials(WebView* web_view, ...@@ -49,6 +49,11 @@ Status ExecuteGetCredentials(WebView* web_view,
const base::Value& params, const base::Value& params,
std::unique_ptr<base::Value>* value); std::unique_ptr<base::Value>* value);
// Remove a single credential stored in an authenticator.
Status ExecuteRemoveCredential(WebView* web_view,
const base::Value& params,
std::unique_ptr<base::Value>* value);
// Remove all the credentials stored in an authenticator. // Remove all the credentials stored in an authenticator.
Status ExecuteRemoveAllCredentials(WebView* web_view, Status ExecuteRemoveAllCredentials(WebView* web_view,
const base::Value& params, const base::Value& params,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment