| // 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)); |
| } |