| Index: chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
 | 
| diff --git a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
 | 
| index b97b51a4116eaa15a80684c327af073db9df4918..9488f74425b7e3c4f270c354c3c86ec0fc19f655 100644
 | 
| --- a/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
 | 
| +++ b/chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api_unittest.cc
 | 
| @@ -6,6 +6,8 @@
 | 
|  
 | 
|  #include "chrome/browser/chromeos/extensions/quick_unlock_private/quick_unlock_private_api.h"
 | 
|  
 | 
| +#include <algorithm>
 | 
| +
 | 
|  #include "base/bind.h"
 | 
|  #include "base/memory/ptr_util.h"
 | 
|  #include "chrome/browser/chromeos/login/quick_unlock/pin_storage.h"
 | 
| @@ -14,12 +16,16 @@
 | 
|  #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
 | 
|  #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h"
 | 
|  #include "chrome/browser/extensions/extension_api_unittest.h"
 | 
| +#include "chrome/common/pref_names.h"
 | 
|  #include "chromeos/login/auth/fake_extended_authenticator.h"
 | 
|  #include "extensions/browser/api_test_utils.h"
 | 
|  #include "extensions/browser/extension_function_dispatcher.h"
 | 
|  
 | 
|  using namespace extensions;
 | 
|  namespace quick_unlock_private = extensions::api::quick_unlock_private;
 | 
| +using CredentialCheck = quick_unlock_private::CredentialCheck;
 | 
| +using CredentialProblem = quick_unlock_private::CredentialProblem;
 | 
| +using CredentialRequirements = quick_unlock_private::CredentialRequirements;
 | 
|  using QuickUnlockMode = quick_unlock_private::QuickUnlockMode;
 | 
|  using QuickUnlockModeList = std::vector<QuickUnlockMode>;
 | 
|  using CredentialList = std::vector<std::string>;
 | 
| @@ -50,6 +56,15 @@ void FailIfCalled(const QuickUnlockModeList& modes) {
 | 
|    FAIL();
 | 
|  }
 | 
|  
 | 
| +enum ExpectedPinState {
 | 
| +  PIN_GOOD = 1 << 0,
 | 
| +  PIN_TOO_SHORT = 1 << 1,
 | 
| +  PIN_TOO_LONG = 1 << 2,
 | 
| +  PIN_WEAK_ERROR = 1 << 3,
 | 
| +  PIN_WEAK_WARNING = 1 << 4,
 | 
| +  PIN_CONTAINS_NONDIGIT = 1 << 5
 | 
| +};
 | 
| +
 | 
|  }  // namespace
 | 
|  
 | 
|  class QuickUnlockPrivateUnitTest : public ExtensionApiUnittest {
 | 
| @@ -129,6 +144,72 @@ class QuickUnlockPrivateUnitTest : public ExtensionApiUnittest {
 | 
|      return modes;
 | 
|    }
 | 
|  
 | 
| +  // Returns true if |problem| is contained in |problems|.
 | 
| +  bool HasProblem(CredentialProblem problem,
 | 
| +                  const std::vector<CredentialProblem> problems) {
 | 
| +    return std::find(problems.begin(), problems.end(), problem) !=
 | 
| +           problems.end();
 | 
| +  }
 | 
| +
 | 
| +  bool HasFlag(int outcome, int flag) { return (outcome & flag) != 0; }
 | 
| +
 | 
| +  // Helper function for checking whether |IsCredentialUsableUsingPin| will
 | 
| +  // return the right message given a pin.
 | 
| +  void CheckPin(int expected_outcome, const std::string& pin) {
 | 
| +    CredentialCheck result = CheckCredentialUsingPin(pin);
 | 
| +    const std::vector<CredentialProblem> errors(result.errors);
 | 
| +    const std::vector<CredentialProblem> warnings(result.warnings);
 | 
| +
 | 
| +    // A pin is considered good if it emits no errors or warnings.
 | 
| +    EXPECT_EQ(HasFlag(expected_outcome, PIN_GOOD),
 | 
| +              errors.empty() && warnings.empty());
 | 
| +    EXPECT_EQ(
 | 
| +        HasFlag(expected_outcome, PIN_TOO_SHORT),
 | 
| +        HasProblem(CredentialProblem::CREDENTIAL_PROBLEM_TOO_SHORT, errors));
 | 
| +    EXPECT_EQ(
 | 
| +        HasFlag(expected_outcome, PIN_TOO_LONG),
 | 
| +        HasProblem(CredentialProblem::CREDENTIAL_PROBLEM_TOO_LONG, errors));
 | 
| +    EXPECT_EQ(
 | 
| +        HasFlag(expected_outcome, PIN_WEAK_WARNING),
 | 
| +        HasProblem(CredentialProblem::CREDENTIAL_PROBLEM_TOO_WEAK, warnings));
 | 
| +    EXPECT_EQ(
 | 
| +        HasFlag(expected_outcome, PIN_WEAK_ERROR),
 | 
| +        HasProblem(CredentialProblem::CREDENTIAL_PROBLEM_TOO_WEAK, errors));
 | 
| +    EXPECT_EQ(
 | 
| +        HasFlag(expected_outcome, PIN_CONTAINS_NONDIGIT),
 | 
| +        HasProblem(CredentialProblem::CREDENTIAL_PROBLEM_CONTAINS_NONDIGIT,
 | 
| +                   errors));
 | 
| +  }
 | 
| +
 | 
| +  CredentialCheck CheckCredentialUsingPin(const std::string& pin) {
 | 
| +    auto params = base::MakeUnique<base::ListValue>();
 | 
| +    params->AppendString(ToString(QuickUnlockMode::QUICK_UNLOCK_MODE_PIN));
 | 
| +    params->AppendString(pin);
 | 
| +
 | 
| +    std::unique_ptr<base::Value> result = RunFunction(
 | 
| +        new QuickUnlockPrivateCheckCredentialFunction(), std::move(params));
 | 
| +
 | 
| +    CredentialCheck function_result;
 | 
| +    EXPECT_TRUE(CredentialCheck::Populate(*result, &function_result));
 | 
| +    return function_result;
 | 
| +  }
 | 
| +
 | 
| +  void CheckGetCredentialRequirements(int expected_pin_min_length,
 | 
| +                                      int expected_pin_max_length) {
 | 
| +    auto params = base::MakeUnique<base::ListValue>();
 | 
| +    params->AppendString(ToString(QuickUnlockMode::QUICK_UNLOCK_MODE_PIN));
 | 
| +
 | 
| +    std::unique_ptr<base::Value> result =
 | 
| +        RunFunction(new QuickUnlockPrivateGetCredentialRequirementsFunction(),
 | 
| +                    std::move(params));
 | 
| +
 | 
| +    CredentialRequirements function_result;
 | 
| +    EXPECT_TRUE(CredentialRequirements::Populate(*result, &function_result));
 | 
| +
 | 
| +    EXPECT_EQ(function_result.min_length, expected_pin_min_length);
 | 
| +    EXPECT_EQ(function_result.max_length, expected_pin_max_length);
 | 
| +  }
 | 
| +
 | 
|    // Wrapper for chrome.quickUnlockPrivate.setModes that automatically uses a
 | 
|    // valid password.
 | 
|    bool SetModes(const QuickUnlockModeList& modes,
 | 
| @@ -245,7 +326,7 @@ TEST_F(QuickUnlockPrivateUnitTest, SetModesFailsWithInvalidPassword) {
 | 
|    FailIfModesChanged();
 | 
|    EXPECT_FALSE(SetModesUsingPassword(
 | 
|        kInvalidPassword,
 | 
| -      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"}));
 | 
| +      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"1111"}));
 | 
|    EXPECT_EQ(GetActiveModes(), QuickUnlockModeList{});
 | 
|  }
 | 
|  
 | 
| @@ -263,10 +344,10 @@ TEST_F(QuickUnlockPrivateUnitTest, ModeChangeEventOnlyRaisedWhenModesChange) {
 | 
|    ExpectModesChanged(
 | 
|        QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN});
 | 
|    EXPECT_TRUE(SetModes(
 | 
| -      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"}));
 | 
| +      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"1111"}));
 | 
|    FailIfModesChanged();
 | 
|    EXPECT_TRUE(SetModes(
 | 
| -      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"22"}));
 | 
| +      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"2222"}));
 | 
|    EXPECT_TRUE(SetModes(
 | 
|        QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {""}));
 | 
|  }
 | 
| @@ -280,7 +361,7 @@ TEST_F(QuickUnlockPrivateUnitTest, SetModesAndGetActiveModes) {
 | 
|    ExpectModesChanged(
 | 
|        QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN});
 | 
|    EXPECT_TRUE(SetModes(
 | 
| -      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"}));
 | 
| +      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"1111"}));
 | 
|    EXPECT_EQ(GetActiveModes(),
 | 
|              QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN});
 | 
|    EXPECT_TRUE(pin_storage->IsPinSet());
 | 
| @@ -300,13 +381,13 @@ TEST_F(QuickUnlockPrivateUnitTest, VerifyAuthenticationAgainstPIN) {
 | 
|    EXPECT_FALSE(pin_storage->IsPinSet());
 | 
|  
 | 
|    EXPECT_TRUE(SetModes(
 | 
| -      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"11"}));
 | 
| +      QuickUnlockModeList{QuickUnlockMode::QUICK_UNLOCK_MODE_PIN}, {"1111"}));
 | 
|    EXPECT_TRUE(pin_storage->IsPinSet());
 | 
|  
 | 
|    pin_storage->MarkStrongAuth();
 | 
|    pin_storage->ResetUnlockAttemptCount();
 | 
| -  EXPECT_TRUE(pin_storage->TryAuthenticatePin("11"));
 | 
| -  EXPECT_FALSE(pin_storage->TryAuthenticatePin("00"));
 | 
| +  EXPECT_TRUE(pin_storage->TryAuthenticatePin("1111"));
 | 
| +  EXPECT_FALSE(pin_storage->TryAuthenticatePin("0000"));
 | 
|  }
 | 
|  
 | 
|  // Verifies that the number of modes and the number of passwords given must be
 | 
| @@ -316,4 +397,97 @@ TEST_F(QuickUnlockPrivateUnitTest, ThrowErrorOnMismatchedParameterCount) {
 | 
|    EXPECT_FALSE(SetModesWithError("[\"valid\", [], [\"11\"]]").empty());
 | 
|  }
 | 
|  
 | 
| +// Validates PIN error checking in conjuction with policy-related prefs.
 | 
| +TEST_F(QuickUnlockPrivateUnitTest, CheckCredentialProblemReporting) {
 | 
| +  PrefService* pref_service = profile()->GetPrefs();
 | 
| +
 | 
| +  // Verify the pin checks work with the default preferences which are minimum
 | 
| +  // length of 4, maximum length of 0 (no maximum) and no easy to guess check.
 | 
| +  CheckPin(PIN_GOOD, "1112");
 | 
| +  CheckPin(PIN_GOOD, "11112");
 | 
| +  CheckPin(PIN_GOOD, "1111111111111112");
 | 
| +  CheckPin(PIN_WEAK_WARNING, "1111");
 | 
| +  CheckPin(PIN_TOO_SHORT, "1");
 | 
| +  CheckPin(PIN_TOO_SHORT, "11");
 | 
| +  CheckPin(PIN_TOO_SHORT | PIN_WEAK_WARNING, "111");
 | 
| +  CheckPin(PIN_TOO_SHORT | PIN_CONTAINS_NONDIGIT, "a");
 | 
| +  CheckPin(PIN_CONTAINS_NONDIGIT, "aaab");
 | 
| +  CheckPin(PIN_CONTAINS_NONDIGIT | PIN_WEAK_WARNING, "aaaa");
 | 
| +  CheckPin(PIN_CONTAINS_NONDIGIT | PIN_WEAK_WARNING, "abcd");
 | 
| +
 | 
| +  // Verify that now if the minimum length is set to 3, PINs of length 3 are
 | 
| +  // accepted.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 3);
 | 
| +  CheckPin(PIN_WEAK_WARNING, "111");
 | 
| +
 | 
| +  // Verify setting a nonzero maximum length that is less than the minimum
 | 
| +  // length results in the pin only accepting PINs of length minimum length.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 2);
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 4);
 | 
| +  CheckPin(PIN_GOOD, "1112");
 | 
| +  CheckPin(PIN_TOO_SHORT, "112");
 | 
| +  CheckPin(PIN_TOO_LONG, "11112");
 | 
| +
 | 
| +  // Verify that now if the maximum length is set to 5, PINs longer than 5 are
 | 
| +  // considered too long and cannot be used.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 5);
 | 
| +  CheckPin(PIN_TOO_LONG | PIN_WEAK_WARNING, "111111");
 | 
| +  CheckPin(PIN_TOO_LONG | PIN_WEAK_WARNING, "1111111");
 | 
| +
 | 
| +  // Verify that if both the minimum length and maximum length is set to 4, only
 | 
| +  // 4 digit PINs can be used.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 4);
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 4);
 | 
| +  CheckPin(PIN_TOO_SHORT, "122");
 | 
| +  CheckPin(PIN_TOO_LONG, "12222");
 | 
| +  CheckPin(PIN_GOOD, "1222");
 | 
| +
 | 
| +  // Set the PINs minimum/maximum lengths back to their defaults.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 4);
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 0);
 | 
| +
 | 
| +  // Verify that PINs that are weak are flagged as such. See
 | 
| +  // IsPinDifficultEnough in quick_unlock_private_api.cc for the description of
 | 
| +  // a weak pin.
 | 
| +  pref_service->SetBoolean(prefs::kPinUnlockWeakPinsAllowed, false);
 | 
| +  // Good.
 | 
| +  CheckPin(PIN_GOOD, "1112");
 | 
| +  CheckPin(PIN_GOOD, "7890");
 | 
| +  CheckPin(PIN_GOOD, "0987");
 | 
| +  // Same digits.
 | 
| +  CheckPin(PIN_WEAK_ERROR, "1111");
 | 
| +  // Increasing.
 | 
| +  CheckPin(PIN_WEAK_ERROR, "0123");
 | 
| +  CheckPin(PIN_WEAK_ERROR, "3456789");
 | 
| +  // Decreasing.
 | 
| +  CheckPin(PIN_WEAK_ERROR, "3210");
 | 
| +  CheckPin(PIN_WEAK_ERROR, "987654");
 | 
| +  // Too common.
 | 
| +  CheckPin(PIN_WEAK_ERROR, "1212");
 | 
| +
 | 
| +  // Verify that if a PIN has more than one error, both are returned.
 | 
| +  CheckPin(PIN_TOO_SHORT | PIN_WEAK_ERROR, "111");
 | 
| +  CheckPin(PIN_TOO_SHORT | PIN_WEAK_ERROR, "234");
 | 
| +}
 | 
| +
 | 
| +TEST_F(QuickUnlockPrivateUnitTest, GetCredentialRequirements) {
 | 
| +  PrefService* pref_service = profile()->GetPrefs();
 | 
| +
 | 
| +  // Verify that trying out PINs under the minimum/maximum lengths will send the
 | 
| +  // minimum/maximum lengths as additional information for display purposes.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMinimumLength, 6);
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 8);
 | 
| +  CheckGetCredentialRequirements(6, 8);
 | 
| +
 | 
| +  // Verify that by setting a maximum length to be nonzero and smaller than the
 | 
| +  // minimum length, the resulting maxium length will be equal to the minimum
 | 
| +  // length pref.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, 4);
 | 
| +  CheckGetCredentialRequirements(6, 6);
 | 
| +
 | 
| +  // Verify that the values received from policy are sanitized.
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMinimumLength, -3);
 | 
| +  pref_service->SetInteger(prefs::kPinUnlockMaximumLength, -3);
 | 
| +  CheckGetCredentialRequirements(1, 0);
 | 
| +}
 | 
|  }  // namespace chromeos
 | 
| 
 |