blob: 913a2b90519f403d5622fbfc95c93276d4ccf3ab [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "modules/credentialmanager/PasswordCredential.h"
#include "bindings/core/v8/Dictionary.h"
#include "bindings/core/v8/ExceptionState.h"
#include "core/HTMLNames.h"
#include "core/dom/ExecutionContext.h"
#include "core/dom/URLSearchParams.h"
#include "core/html/FormAssociatedElement.h"
#include "core/html/FormData.h"
#include "core/html/HTMLFormElement.h"
#include "modules/credentialmanager/FormDataOptions.h"
#include "modules/credentialmanager/PasswordCredentialData.h"
#include "platform/credentialmanager/PlatformPasswordCredential.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "public/platform/WebCredential.h"
#include "public/platform/WebPasswordCredential.h"
namespace blink {
PasswordCredential* PasswordCredential::create(WebPasswordCredential* webPasswordCredential)
{
return new PasswordCredential(webPasswordCredential);
}
PasswordCredential* PasswordCredential::create(const PasswordCredentialData& data, ExceptionState& exceptionState)
{
if (data.id().isEmpty()) {
exceptionState.throwTypeError("'id' must not be empty.");
return nullptr;
}
if (data.password().isEmpty()) {
exceptionState.throwTypeError("'password' must not be empty.");
return nullptr;
}
KURL iconURL = parseStringAsURL(data.iconURL(), exceptionState);
if (exceptionState.hadException())
return nullptr;
return new PasswordCredential(data.id(), data.password(), data.name(), iconURL);
}
// https://w3c.github.io/webappsec-credential-management/#passwordcredential-form-constructor
PasswordCredential* PasswordCredential::create(HTMLFormElement* form, ExceptionState& exceptionState)
{
// Extract data from the form, then use the extracted |formData| object's
// value to populate |data|.
FormData* formData = FormData::create(form);
PasswordCredentialData data;
AtomicString idName;
AtomicString passwordName;
for (FormAssociatedElement* element : form->associatedElements()) {
// If |element| isn't a "submittable element" with string data, then it
// won't have a matching value in |formData|, and we can safely skip it.
FileOrUSVString result;
formData->get(element->name(), result);
if (!result.isUSVString())
continue;
AtomicString autocomplete = toHTMLElement(element)->fastGetAttribute(HTMLNames::autocompleteAttr);
if (equalIgnoringCase(autocomplete, "current-password") || equalIgnoringCase(autocomplete, "new-password")) {
data.setPassword(result.getAsUSVString());
passwordName = element->name();
} else if (equalIgnoringCase(autocomplete, "photo")) {
data.setIconURL(result.getAsUSVString());
} else if (equalIgnoringCase(autocomplete, "name") || equalIgnoringCase(autocomplete, "nickname")) {
data.setName(result.getAsUSVString());
} else if (equalIgnoringCase(autocomplete, "username")) {
data.setId(result.getAsUSVString());
idName = element->name();
}
}
// Create a PasswordCredential using the data gathered above.
PasswordCredential* credential = PasswordCredential::create(data, exceptionState);
if (exceptionState.hadException())
return nullptr;
ASSERT(credential);
// After creating the Credential, populate its 'additionalData', 'idName', and 'passwordName' attributes.
// If the form's 'enctype' is anything other than multipart, generate a URLSearchParams using the
// data in |formData|.
credential->setIdName(idName);
credential->setPasswordName(passwordName);
FormDataOrURLSearchParams additionalData;
if (form->enctype() == "multipart/form-data") {
additionalData.setFormData(formData);
} else {
URLSearchParams* params = URLSearchParams::create(URLSearchParamsInit());
for (const FormData::Entry* entry : formData->entries()) {
if (entry->isString())
params->append(entry->name().data(), entry->value().data());
}
additionalData.setURLSearchParams(params);
}
credential->setAdditionalData(additionalData);
return credential;
}
PasswordCredential::PasswordCredential(WebPasswordCredential* webPasswordCredential)
: SiteBoundCredential(webPasswordCredential->getPlatformCredential())
, m_idName("username")
, m_passwordName("password")
{
}
PasswordCredential::PasswordCredential(const String& id, const String& password, const String& name, const KURL& icon)
: SiteBoundCredential(PlatformPasswordCredential::create(id, password, name, icon))
, m_idName("username")
, m_passwordName("password")
{
}
PassRefPtr<EncodedFormData> PasswordCredential::encodeFormData(String& contentType) const
{
if (m_additionalData.isURLSearchParams()) {
// If |additionalData| is a 'URLSearchParams' object, build a urlencoded response.
URLSearchParams* params = URLSearchParams::create(URLSearchParamsInit());
URLSearchParams* additionalData = m_additionalData.getAsURLSearchParams();
for (const auto& param : additionalData->params()) {
const String& name = param.first;
if (name != idName() && name != passwordName())
params->append(name, param.second);
}
params->append(idName(), id());
params->append(passwordName(), password());
contentType = AtomicString("application/x-www-form-urlencoded;charset=UTF-8");
return params->encodeFormData();
}
// Otherwise, we'll build a multipart response.
FormData* formData = FormData::create(nullptr);
if (m_additionalData.isFormData()) {
FormData* additionalData = m_additionalData.getAsFormData();
for (const FormData::Entry* entry : additionalData->entries()) {
const String& name = formData->decode(entry->name());
if (name == idName() || name == passwordName())
continue;
if (entry->blob())
formData->append(name, entry->blob(), entry->filename());
else
formData->append(name, formData->decode(entry->value()));
}
}
formData->append(idName(), id());
formData->append(passwordName(), password());
RefPtr<EncodedFormData> encodedData = formData->encodeMultiPartFormData();
contentType = AtomicString("multipart/form-data; boundary=") + encodedData->boundary().data();
return encodedData.release();
}
const String& PasswordCredential::password() const
{
return static_cast<PlatformPasswordCredential*>(m_platformCredential.get())->password();
}
DEFINE_TRACE(PasswordCredential)
{
SiteBoundCredential::trace(visitor);
visitor->trace(m_additionalData);
}
} // namespace blink