blob: f4f17f233c5772227274c1e2c330dd1169e35013 [file] [log] [blame]
// Copyright 2017 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 "ios/chrome/browser/passwords/credential_manager_util.h"
#include <memory>
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
using password_manager::CredentialInfo;
using password_manager::CredentialMediationRequirement;
using password_manager::CredentialType;
namespace {
constexpr char kTestWebOrigin[] = "https://example.com/";
constexpr char kTestIconUrl[] = "https://www.google.com/favicon.ico";
base::DictionaryValue BuildExampleCredential() {
base::DictionaryValue json;
json.SetString(kCredentialIdKey, "john@doe.com");
json.SetString(kCredentialNameKey, "John Doe");
json.SetString(kCredentialIconKey, kTestIconUrl);
return json;
}
// Builds a dictionary representing valid PasswordCredential.
base::DictionaryValue BuildExampleValidPasswordCredential() {
base::DictionaryValue json = BuildExampleCredential();
json.SetString(kPasswordCredentialPasswordKey, "admin123");
json.SetString(kCredentialTypeKey, kCredentialTypePassword);
return json;
}
// Builds a dictionary representing valid FederatedCredential.
base::DictionaryValue BuildExampleValidFederatedCredential() {
base::DictionaryValue json = BuildExampleCredential();
json.SetString(kFederatedCredentialProviderKey, kTestWebOrigin);
json.SetString(kCredentialTypeKey, kCredentialTypeFederated);
return json;
}
} // namespace
using CredentialManagerUtilTest = PlatformTest;
// Checks that CredentialRequestOptions.password field is parsed
// correctly.
TEST_F(CredentialManagerUtilTest, ParseIncludePasswords) {
base::DictionaryValue json;
bool include_passwords = true;
// Default value should be false.
EXPECT_TRUE(ParseIncludePasswords(json, &include_passwords));
EXPECT_FALSE(include_passwords);
// true/false values should be parsed correctly.
json.SetBoolean(kCredentialRequestPasswordKey, true);
EXPECT_TRUE(ParseIncludePasswords(json, &include_passwords));
EXPECT_TRUE(include_passwords);
json.SetBoolean(kCredentialRequestPasswordKey, false);
EXPECT_TRUE(ParseIncludePasswords(json, &include_passwords));
EXPECT_FALSE(include_passwords);
// Test against random string.
json.SetString(kCredentialRequestPasswordKey, "yes");
EXPECT_FALSE(ParseIncludePasswords(json, &include_passwords));
}
// Checks that CredentialRequestOptions.mediation field is parsed
// correctly.
TEST_F(CredentialManagerUtilTest, ParseMediationRequirement) {
base::DictionaryValue json;
CredentialMediationRequirement mediation;
// Default value should be kOptional.
EXPECT_TRUE(ParseMediationRequirement(json, &mediation));
EXPECT_EQ(CredentialMediationRequirement::kOptional, mediation);
// "silent"/"optional"/"required" values should be parsed correctly.
json.SetString(kCredentialRequestMediationKey, kMediationRequirementSilent);
EXPECT_TRUE(ParseMediationRequirement(json, &mediation));
EXPECT_EQ(CredentialMediationRequirement::kSilent, mediation);
json.SetString(kCredentialRequestMediationKey, kMediationRequirementOptional);
EXPECT_TRUE(ParseMediationRequirement(json, &mediation));
EXPECT_EQ(CredentialMediationRequirement::kOptional, mediation);
json.SetString(kCredentialRequestMediationKey, kMediationRequirementRequired);
EXPECT_TRUE(ParseMediationRequirement(json, &mediation));
EXPECT_EQ(CredentialMediationRequirement::kRequired, mediation);
// Test against random string.
json.SetString(kCredentialRequestMediationKey, "dksjl");
EXPECT_FALSE(ParseMediationRequirement(json, &mediation));
}
// Checks that Credential.type field is parsed correctly.
TEST_F(CredentialManagerUtilTest, ParseCredentialType) {
base::DictionaryValue json;
CredentialType type = CredentialType::CREDENTIAL_TYPE_EMPTY;
// JS object Credential must contain |type| field.
EXPECT_FALSE(ParseCredentialType(json, &type));
// "PasswordCredential"/"FederatedCredential" values should be parsed
// correctly.
json.SetString(kCredentialTypeKey, kCredentialTypePassword);
EXPECT_TRUE(ParseCredentialType(json, &type));
EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_PASSWORD, type);
json.SetString(kCredentialTypeKey, kCredentialTypeFederated);
EXPECT_TRUE(ParseCredentialType(json, &type));
EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_FEDERATED, type);
// "Credential" is not a valid type.
json.SetString(kCredentialTypeKey, "Credential");
EXPECT_FALSE(ParseCredentialType(json, &type));
// Empty string is also not allowed.
json.SetString(kCredentialTypeKey, "");
EXPECT_FALSE(ParseCredentialType(json, &type));
}
// Checks that common fields of PasswordCredential and FederatedCredential are
// parsed correctly.
TEST_F(CredentialManagerUtilTest, ParseCommonCredentialFields) {
// Building PasswordCredential because ParseCredentialDictionary for
// Credential containing only common fields would return false.
base::DictionaryValue json = BuildExampleValidPasswordCredential();
CredentialInfo credential;
std::string reason;
// Valid dictionary should be parsed correctly and ParseCredentialDictionary
// should return true.
EXPECT_TRUE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(base::ASCIIToUTF16("john@doe.com"), credential.id);
EXPECT_EQ(base::ASCIIToUTF16("John Doe"), credential.name);
EXPECT_EQ(GURL(kTestIconUrl), credential.icon);
// |id| field is required.
json.Remove(kCredentialIdKey, nullptr);
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "no valid 'id' field");
// |iconURL| field is not required.
json = BuildExampleValidPasswordCredential();
json.Remove(kCredentialIconKey, nullptr);
EXPECT_TRUE(ParseCredentialDictionary(json, &credential, &reason));
// If Credential has |iconURL| field, it must be a valid URL.
json.SetString(kCredentialIconKey, "not a valid url");
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "iconURL is either invalid or insecure URL");
// If Credential has |iconURL| field, it must be a secure URL.
reason = std::string();
json.SetString(kCredentialIconKey, "http://example.com");
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "iconURL is either invalid or insecure URL");
// Check that empty |iconURL| field is treated as no |iconURL| field.
json.SetString(kCredentialIconKey, "");
EXPECT_TRUE(ParseCredentialDictionary(json, &credential, &reason));
}
// Checks that |password| and |type| fields of PasswordCredential are parsed
// correctly.
TEST_F(CredentialManagerUtilTest, ParsePasswordCredential) {
base::DictionaryValue json = BuildExampleValidPasswordCredential();
CredentialInfo credential;
std::string reason;
// Valid dictionary should be parsed correctly and ParseCredentialDictionary
// should return true.
EXPECT_TRUE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_PASSWORD, credential.type);
EXPECT_EQ(base::ASCIIToUTF16("admin123"), credential.password);
// |password| field is required.
json.Remove(kPasswordCredentialPasswordKey, nullptr);
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "no valid 'password' field");
// |password| field is cannot be an empty string.
json.SetString(kPasswordCredentialPasswordKey, "");
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "no valid 'password' field");
}
// Checks that |provider| and |type| fields of FederatedCredential are parsed
// correctly.
TEST_F(CredentialManagerUtilTest, ParseFederatedCredential) {
base::DictionaryValue json = BuildExampleValidFederatedCredential();
CredentialInfo credential;
std::string reason;
// Valid dictionary should be parsed correctly and ParseCredentialDictionary
// should return true.
EXPECT_TRUE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(CredentialType::CREDENTIAL_TYPE_FEDERATED, credential.type);
EXPECT_EQ(GURL(kTestWebOrigin), credential.federation.GetURL());
// |provider| field is required.
json.Remove(kFederatedCredentialProviderKey, nullptr);
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "no valid 'provider' field");
// |provider| field cannot be an empty string.
json.SetString(kFederatedCredentialProviderKey, "");
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "no valid 'provider' field");
// |provider| field must be a valid URL.
json.SetString(kFederatedCredentialProviderKey, "not a valid URL");
EXPECT_FALSE(ParseCredentialDictionary(json, &credential, &reason));
EXPECT_EQ(reason, "no valid 'provider' field");
}
// Checks that |providers| field of FederatedCredentialRequestOptions is
// parsed correctly.
TEST_F(CredentialManagerUtilTest, ParseFederations) {
base::DictionaryValue json;
// Build example valid |providers| list.
std::unique_ptr<base::ListValue> list_ptr =
std::make_unique<base::ListValue>();
list_ptr->GetList().emplace_back(kTestWebOrigin);
list_ptr->GetList().emplace_back("https://google.com");
json.SetList(kCredentialRequestProvidersKey, std::move(list_ptr));
std::vector<GURL> federations;
// Check that parsing valid |providers| results in correct |federations| list.
EXPECT_TRUE(ParseFederations(json, &federations));
EXPECT_THAT(federations, testing::ElementsAre(GURL(kTestWebOrigin),
GURL("https://google.com")));
// ParseFederations should skip invalid URLs.
list_ptr = std::make_unique<base::ListValue>();
list_ptr->GetList().emplace_back(kTestWebOrigin);
list_ptr->GetList().emplace_back("not a valid url");
json.SetList(kCredentialRequestProvidersKey, std::move(list_ptr));
EXPECT_TRUE(ParseFederations(json, &federations));
EXPECT_THAT(federations, testing::ElementsAre(GURL(kTestWebOrigin)));
// If |providers| is not a valid list, ParseFederations should return false.
json.SetString(kCredentialRequestProvidersKey, kTestWebOrigin);
EXPECT_FALSE(ParseFederations(json, &federations));
}