1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/basictypes.h" 6 #include "base/bind.h" 7 #include "base/files/scoped_temp_dir.h" 8 #include "base/stl_util.h" 9 #include "base/strings/string_util.h" 10 #include "base/synchronization/waitable_event.h" 11 #include "base/time/time.h" 12 #include "components/password_manager/core/browser/password_form_data.h" 13 #include "components/password_manager/core/browser/password_store_consumer.h" 14 #include "components/password_manager/core/browser/password_store_default.h" 15 #include "testing/gmock/include/gmock/gmock.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 using autofill::PasswordForm; 19 using base::WaitableEvent; 20 using testing::_; 21 using testing::DoAll; 22 using testing::WithArg; 23 24 namespace password_manager { 25 26 namespace { 27 28 class MockPasswordStoreConsumer : public PasswordStoreConsumer { 29 public: 30 MOCK_METHOD1(OnGetPasswordStoreResults, 31 void(const std::vector<PasswordForm*>&)); 32 }; 33 34 class StartSyncFlareMock { 35 public: 36 StartSyncFlareMock() {} 37 ~StartSyncFlareMock() {} 38 39 MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType)); 40 }; 41 42 } // namespace 43 44 class PasswordStoreTest : public testing::Test { 45 protected: 46 virtual void SetUp() OVERRIDE { 47 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 48 login_db_.reset(new LoginDatabase()); 49 ASSERT_TRUE(login_db_->Init(temp_dir_.path().Append( 50 FILE_PATH_LITERAL("login_test")))); 51 } 52 53 virtual void TearDown() OVERRIDE { 54 ASSERT_TRUE(temp_dir_.Delete()); 55 } 56 57 base::MessageLoopForUI message_loop_; 58 scoped_ptr<LoginDatabase> login_db_; 59 base::ScopedTempDir temp_dir_; 60 }; 61 62 ACTION(STLDeleteElements0) { 63 STLDeleteContainerPointers(arg0.begin(), arg0.end()); 64 } 65 66 TEST_F(PasswordStoreTest, IgnoreOldWwwGoogleLogins) { 67 scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault( 68 base::MessageLoopProxy::current(), 69 base::MessageLoopProxy::current(), 70 login_db_.release())); 71 store->Init(syncer::SyncableService::StartSyncFlare(), ""); 72 73 const time_t cutoff = 1325376000; // 00:00 Jan 1 2012 UTC 74 // The passwords are all empty because PasswordStoreDefault doesn't store the 75 // actual passwords on OS X (they're stored in the Keychain instead). We could 76 // special-case it, but it's easier to just have empty passwords. 77 static const PasswordFormData form_data[] = { 78 // A form on https://www.google.com/ older than the cutoff. Will be ignored. 79 { PasswordForm::SCHEME_HTML, 80 "https://www.google.com", 81 "https://www.google.com/origin", 82 "https://www.google.com/action", 83 L"submit_element", 84 L"username_element", 85 L"password_element", 86 L"username_value_1", 87 L"", 88 true, true, cutoff - 1 }, 89 // A form on https://www.google.com/ older than the cutoff. Will be ignored. 90 { PasswordForm::SCHEME_HTML, 91 "https://www.google.com", 92 "https://www.google.com/origin", 93 "https://www.google.com/action", 94 L"submit_element", 95 L"username_element", 96 L"password_element", 97 L"username_value_2", 98 L"", 99 true, true, cutoff - 1 }, 100 // A form on https://www.google.com/ newer than the cutoff. 101 { PasswordForm::SCHEME_HTML, 102 "https://www.google.com", 103 "https://www.google.com/origin", 104 "https://www.google.com/action", 105 L"submit_element", 106 L"username_element", 107 L"password_element", 108 L"username_value_3", 109 L"", 110 true, true, cutoff + 1 }, 111 // A form on https://accounts.google.com/ older than the cutoff. 112 { PasswordForm::SCHEME_HTML, 113 "https://accounts.google.com", 114 "https://accounts.google.com/origin", 115 "https://accounts.google.com/action", 116 L"submit_element", 117 L"username_element", 118 L"password_element", 119 L"username_value", 120 L"", 121 true, true, cutoff - 1 }, 122 // A form on http://bar.example.com/ older than the cutoff. 123 { PasswordForm::SCHEME_HTML, 124 "http://bar.example.com", 125 "http://bar.example.com/origin", 126 "http://bar.example.com/action", 127 L"submit_element", 128 L"username_element", 129 L"password_element", 130 L"username_value", 131 L"", 132 true, false, cutoff - 1 }, 133 }; 134 135 // Build the forms vector and add the forms to the store. 136 std::vector<PasswordForm*> all_forms; 137 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(form_data); ++i) { 138 PasswordForm* form = CreatePasswordFormFromData(form_data[i]); 139 all_forms.push_back(form); 140 store->AddLogin(*form); 141 } 142 base::MessageLoop::current()->RunUntilIdle(); 143 144 // We expect to get back only the "recent" www.google.com login. 145 // Theoretically these should never actually exist since there are no longer 146 // any login forms on www.google.com to save, but we technically allow them. 147 // We should not get back the older saved password though. 148 PasswordForm www_google; 149 www_google.scheme = PasswordForm::SCHEME_HTML; 150 www_google.signon_realm = "https://www.google.com"; 151 std::vector<PasswordForm*> www_google_expected; 152 www_google_expected.push_back(all_forms[2]); 153 154 // We should still get the accounts.google.com login even though it's older 155 // than our cutoff - this is the new location of all Google login forms. 156 PasswordForm accounts_google; 157 accounts_google.scheme = PasswordForm::SCHEME_HTML; 158 accounts_google.signon_realm = "https://accounts.google.com"; 159 std::vector<PasswordForm*> accounts_google_expected; 160 accounts_google_expected.push_back(all_forms[3]); 161 162 // Same thing for a generic saved login. 163 PasswordForm bar_example; 164 bar_example.scheme = PasswordForm::SCHEME_HTML; 165 bar_example.signon_realm = "http://bar.example.com"; 166 std::vector<PasswordForm*> bar_example_expected; 167 bar_example_expected.push_back(all_forms[4]); 168 169 MockPasswordStoreConsumer consumer; 170 171 // Expect the appropriate replies, as above, in reverse order than we will 172 // issue the queries. Each retires on saturation to avoid matcher spew. 173 EXPECT_CALL(consumer, 174 OnGetPasswordStoreResults(ContainsAllPasswordForms(bar_example_expected))) 175 .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation(); 176 EXPECT_CALL(consumer, 177 OnGetPasswordStoreResults( 178 ContainsAllPasswordForms(accounts_google_expected))) 179 .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation(); 180 EXPECT_CALL(consumer, 181 OnGetPasswordStoreResults( 182 ContainsAllPasswordForms(www_google_expected))) 183 .WillOnce(WithArg<0>(STLDeleteElements0())).RetiresOnSaturation(); 184 185 store->GetLogins(www_google, PasswordStore::ALLOW_PROMPT, &consumer); 186 store->GetLogins(accounts_google, PasswordStore::ALLOW_PROMPT, &consumer); 187 store->GetLogins(bar_example, PasswordStore::ALLOW_PROMPT, &consumer); 188 189 base::MessageLoop::current()->RunUntilIdle(); 190 191 STLDeleteElements(&all_forms); 192 store->Shutdown(); 193 base::MessageLoop::current()->RunUntilIdle(); 194 } 195 196 TEST_F(PasswordStoreTest, StartSyncFlare) { 197 scoped_refptr<PasswordStoreDefault> store(new PasswordStoreDefault( 198 base::MessageLoopProxy::current(), 199 base::MessageLoopProxy::current(), 200 login_db_.release())); 201 StartSyncFlareMock mock; 202 store->Init(base::Bind(&StartSyncFlareMock::StartSyncFlare, 203 base::Unretained(&mock)), 204 ""); 205 { 206 PasswordForm form; 207 EXPECT_CALL(mock, StartSyncFlare(syncer::PASSWORDS)); 208 store->AddLogin(form); 209 base::MessageLoop::current()->RunUntilIdle(); 210 } 211 store->Shutdown(); 212 base::MessageLoop::current()->RunUntilIdle(); 213 } 214 215 } // namespace password_manager 216