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):
params = {'authenticatorId': authenticatorId}
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):
params = {'authenticatorId': authenticatorId}
return self.ExecuteCommand(Command.REMOVE_ALL_CREDENTIALS, params)
......
......@@ -179,6 +179,10 @@ class Command(object):
GET_CREDENTIALS = (
_Method.GET,
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials')
REMOVE_CREDENTIAL = (
_Method.DELETE,
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/'
':credentialId')
REMOVE_ALL_CREDENTIALS = (
_Method.DELETE,
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials')
......
......@@ -777,6 +777,14 @@ HttpHandler::HttpHandler(
base::BindRepeating(
&ExecuteWebAuthnCommand,
base::BindRepeating(&ExecuteGetCredentials)))),
CommandMapping(
kDelete,
"session/:sessionId/webauthn/authenticator/:authenticatorId/"
"credentials/:credentialId",
WrapToCommand("RemoveCredential",
base::BindRepeating(
&ExecuteWebAuthnCommand,
base::BindRepeating(&ExecuteRemoveCredential)))),
CommandMapping(
kDelete,
"session/:sessionId/webauthn/authenticator/:authenticatorId/"
......
......@@ -225,6 +225,7 @@ _ANDROID_NEGATIVE_FILTER['chrome'] = (
'ChromeDriverSecureContextTest.testRemoveVirtualAuthenticator',
'ChromeDriverSecureContextTest.testAddCredential',
'ChromeDriverSecureContextTest.testGetCredentials',
'ChromeDriverSecureContextTest.testRemoveCredential',
'ChromeDriverSecureContextTest.testRemoveAllCredentials',
'ChromeDriverSecureContextTest.testSetUserVerified',
# Covered by Desktop tests; can't create 2 browsers in Android
......@@ -2053,7 +2054,8 @@ class ChromeDriverSecureContextTest(ChromeDriverBaseTest):
@staticmethod
def UrlSafeBase64Decode(string):
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)
def setUp(self):
......@@ -2197,6 +2199,39 @@ class ChromeDriverSecureContextTest(ChromeDriverBaseTest):
self.assertEquals(1, credentials[0]['signCount'])
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):
register_credential_script = """
let done = arguments[0];
......
......@@ -165,6 +165,23 @@ Status ExecuteGetCredentials(WebView* web_view,
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,
const base::Value& params,
std::unique_ptr<base::Value>* value) {
......
......@@ -49,6 +49,11 @@ Status ExecuteGetCredentials(WebView* web_view,
const base::Value& params,
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.
Status ExecuteRemoveAllCredentials(WebView* web_view,
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