text: // 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 "components/password_manager/ios/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"
  
  namespace password_manager {
  
  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->Append(kTestWebOrigin);
    list_ptr->Append("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->Append(kTestWebOrigin);
    list_ptr->Append("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));
  }
  
  }  // namespace password_manager
  