diff options
-rw-r--r-- | chrome/browser/automation/automation_provider.cc | 122 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider.h | 15 | ||||
-rw-r--r-- | chrome/browser/automation/automation_provider_observers.cc | 14 | ||||
-rw-r--r-- | chrome/test/functional/passwords.py | 37 | ||||
-rw-r--r-- | chrome/test/pyautolib/pyauto.py | 54 |
5 files changed, 182 insertions, 60 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index bf52686..5b3fe45 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -2536,35 +2536,87 @@ void AutomationProvider::ImportSettings(Browser* browser, new ProfileWriter(profile), first_run); } +namespace { + +// Translates a dictionary password to a PasswordForm struct. +webkit_glue::PasswordForm GetPasswordFormFromDict( + const DictionaryValue& password_dict) { + + // If the time is specified, change time to the specified time. + base::Time time = base::Time::Now(); + int it; + double dt; + if (password_dict.GetInteger(L"time", &it)) + time = base::Time::FromTimeT(it); + else if (password_dict.GetReal(L"time", &dt)) + time = base::Time::FromDoubleT(dt); + + std::string signon_realm; + string16 username_value; + string16 password_value; + string16 origin_url_text; + string16 username_element; + string16 password_element; + string16 submit_element; + string16 action_target_text; + bool blacklist = false; + string16 old_password_element; + string16 old_password_value; + + // We don't care if any of these fail - they are either optional or checked + // before this function is called. + password_dict.GetString(L"signon_realm", &signon_realm); + password_dict.GetStringAsUTF16(L"username_value", &username_value); + password_dict.GetStringAsUTF16(L"password_value", &password_value); + password_dict.GetStringAsUTF16(L"origin_url", &origin_url_text); + password_dict.GetStringAsUTF16(L"username_element", &username_element); + password_dict.GetStringAsUTF16(L"password_element", &password_element); + password_dict.GetStringAsUTF16(L"submit_element", &submit_element); + password_dict.GetStringAsUTF16(L"action_target", &action_target_text); + password_dict.GetBoolean(L"blacklist", &blacklist); + + GURL origin_gurl(origin_url_text); + GURL action_target(action_target_text); + + webkit_glue::PasswordForm password_form; + password_form.signon_realm = signon_realm; + password_form.username_value = username_value; + password_form.password_value = password_value; + password_form.origin = origin_gurl; + password_form.username_element = username_element; + password_form.password_element = password_element; + password_form.submit_element = submit_element; + password_form.action = action_target; + password_form.blacklisted_by_user = blacklist; + password_form.date_created = time; + + return password_form; +} + +} // namespace + // See AddSavedPassword() in chrome/test/functional/pyauto.py for sample json // input. // Sample json output: { "password_added": true } void AutomationProvider::AddSavedPassword(Browser* browser, DictionaryValue* args, IPC::Message* reply_message) { - string16 username; - string16 password; - base::Time time = base::Time::Now(); AutomationJSONReply reply(this, reply_message); + DictionaryValue* password_dict = NULL; - if (!args->GetStringAsUTF16(L"password", &password) || - !args->GetStringAsUTF16(L"username", &username)) { - reply.SendError("Username and password must be strings."); + if (!args->GetDictionary(L"password", &password_dict)) { + reply.SendError("Password must be a dictionary."); return; } - // If the time is specified, change time to the specified time. - int it; - double dt; - if (args->GetInteger(L"time", &it)) - time = base::Time::FromTimeT(it); - else if (args->GetReal(L"time", &dt)) - time = base::Time::FromDoubleT(dt); - - webkit_glue::PasswordForm new_password; - new_password.username_value = username; - new_password.password_value = password; - new_password.date_created = time; + // The signon realm is effectively the primary key and must be included. + // Check here before calling GetPasswordFormFromDict. + if (!password_dict->HasKey(L"signon_realm")) { + reply.SendError("Password must include signon_realm."); + return; + } + webkit_glue::PasswordForm new_password = + GetPasswordFormFromDict(*password_dict); Profile* profile = browser->profile(); // Use IMPLICIT_ACCESS since new passwords aren't added off the record. @@ -2585,6 +2637,38 @@ void AutomationProvider::AddSavedPassword(Browser* browser, reply.SendSuccess(return_value.get()); } +// See RemoveSavedPassword() in chrome/test/functional/pyauto.py for sample +// json input. +// Sample json output: {} +void AutomationProvider::RemoveSavedPassword(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message) { + AutomationJSONReply reply(this, reply_message); + DictionaryValue* password_dict = NULL; + + if (!args->GetDictionary(L"password", &password_dict)) { + reply.SendError("Password must be a dictionary."); + return; + } + + // The signon realm is effectively the primary key and must be included. + // Check here before calling GetPasswordFormFromDict. + if (!password_dict->HasKey(L"signon_realm")) { + reply.SendError("Password must include signon_realm."); + return; + } + webkit_glue::PasswordForm to_remove = + GetPasswordFormFromDict(*password_dict); + + Profile* profile = browser->profile(); + // Use EXPLICIT_ACCESS since passwords can be removed off the record. + PasswordStore* password_store = + profile->GetPasswordStore(Profile::EXPLICIT_ACCESS); + + password_store->RemoveLogin(to_remove); + reply.SendSuccess(NULL); +} + // Sample json input: { "command": "GetSavedPasswords" } // Refer to GetSavedPasswords() in chrome/test/pyautolib/pyauto.py for sample // json output. @@ -2998,6 +3082,8 @@ void AutomationProvider::SendJSONRequest(int handle, handler_map["ImportSettings"] = &AutomationProvider::ImportSettings; handler_map["AddSavedPassword"] = &AutomationProvider::AddSavedPassword; + handler_map["RemoveSavedPassword"] = + &AutomationProvider::RemoveSavedPassword; handler_map["GetSavedPasswords"] = &AutomationProvider::GetSavedPasswords; handler_map["ClearBrowsingData"] = &AutomationProvider::ClearBrowsingData; diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 0bc30fcc..dbaeb1f 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -62,6 +62,10 @@ namespace gfx { class Point; } +namespace webkit_glue { +struct PasswordForm; +} + class AutomationProvider : public base::RefCounted<AutomationProvider>, public IPC::Channel::Listener, public IPC::Message::Sender { @@ -467,12 +471,21 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, DictionaryValue* args, IPC::Message* reply_message); - // Add a new username-password combination to the saved passwords. + // Add a new entry to the password store based on the password information + // provided. This method can also be used to add a blacklisted site (which + // will never fill in the password). // Uses the JSON interface for input/output. void AddSavedPassword(Browser* browser, DictionaryValue* args, IPC::Message* reply_message); + // Removes the password matching the information provided. This method can + // also be used to remove a blacklisted site. + // Uses the JSON interface for input/output. + void RemoveSavedPassword(Browser* browser, + DictionaryValue* args, + IPC::Message* reply_message); + // Return the saved username/password combinations. // Uses the JSON interface for input/output. void GetSavedPasswords(Browser* browser, diff --git a/chrome/browser/automation/automation_provider_observers.cc b/chrome/browser/automation/automation_provider_observers.cc index 6725ae1..a9414be 100644 --- a/chrome/browser/automation/automation_provider_observers.cc +++ b/chrome/browser/automation/automation_provider_observers.cc @@ -1024,13 +1024,23 @@ void AutomationProviderGetPasswordsObserver::OnPasswordStoreRequestDone( result.begin(); it != result.end(); ++it) { DictionaryValue* password_val = new DictionaryValue; webkit_glue::PasswordForm* password_form = *it; - password_val->SetStringFromUTF16(L"username", + password_val->SetStringFromUTF16(L"username_value", password_form->username_value); - password_val->SetStringFromUTF16(L"password", + password_val->SetStringFromUTF16(L"password_value", password_form->password_value); + password_val->SetString(L"signon_realm", password_form->signon_realm); password_val->SetReal( L"time", static_cast<double>( password_form->date_created.ToDoubleT())); + password_val->SetString(L"origin_url", password_form->origin.spec()); + password_val->SetStringFromUTF16(L"username_element", + password_form->username_element); + password_val->SetStringFromUTF16(L"password_element", + password_form->password_element); + password_val->SetStringFromUTF16(L"submit_element", + password_form->submit_element); + password_val->SetString(L"action_target", password_form->action.spec()); + password_val->SetBoolean(L"blacklist", password_form->blacklisted_by_user); passwords->Append(password_val); } diff --git a/chrome/test/functional/passwords.py b/chrome/test/functional/passwords.py index f0d7e9e..7d88eb9 100644 --- a/chrome/test/functional/passwords.py +++ b/chrome/test/functional/passwords.py @@ -3,8 +3,6 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -import time - import pyauto_functional # Must be imported before pyauto import pyauto @@ -31,29 +29,18 @@ class PasswordTest(pyauto.PyUITest): def testSavePassword(self): """Test saving a password and getting saved passwords.""" - username1 = 'username1' - password1 = 'password1' - username2 = 'username2' - password2 = 'password2' - - self.AddSavedPassword(username1, password1) - passwords = self.GetSavedPasswords()['passwords'] - self.assertEqual(1, len(passwords)) - pw = passwords[0] - self.assertEqual(username1, pw['username']) - self.assertEqual(password1, pw['password']) - - now = time.time() - self.AddSavedPassword(username2, password2, now) - passwords = self.GetSavedPasswords()['passwords'] - self.assertEqual(2, len(passwords)) - pw1 = passwords[0] - pw2 = passwords[1] - self.assertEqual(username1, pw1['username']) - self.assertEqual(password1, pw1['password']) - self.assertEqual(username2, pw2['username']) - self.assertEqual(password2, pw2['password']) - self._AssertWithinOneSecond(now, pw2['time']) + password1 = { 'username_value': 'user@example.com', + 'password_value': 'test.password', + 'signon_realm': 'https://www.example.com/', + 'time': 1279650942.0, + 'origin_url': 'https://www.example.com/login', + 'username_element': 'username', + 'password_element': 'password', + 'submit_element': 'submit', + 'action_target': 'https://www.example.com/login/', + 'blacklist': False } + self.assertTrue(self.AddSavedPassword(password1)) + self.assertEquals(self.GetSavedPasswords(), [password1]) if __name__ == '__main__': diff --git a/chrome/test/pyautolib/pyauto.py b/chrome/test/pyautolib/pyauto.py index 716f14d..28e39ed 100644 --- a/chrome/test/pyautolib/pyauto.py +++ b/chrome/test/pyautolib/pyauto.py @@ -890,42 +890,68 @@ class PyUITest(pyautolib.PyUITestBase, unittest.TestCase): } return self._GetResultFromJSONRequest(cmd_dict) - def AddSavedPassword(self, username, password, time=None, window_index=0): + def AddSavedPassword(self, password_dict, window_index=0): """Adds the given username-password combination to the saved passwords. Args: - username: a string representing the username - password: a string representing the password + password_dict: a dictionary that represents a password. Example: + { 'username_value': 'user@example.com', # Required + 'password_value': 'test.password', # Required + 'signon_realm': 'https://www.example.com/', # Required + 'time': 1279317810.0, # Can get from time.time() + 'origin_url': 'https://www.example.com/login', + 'username_element': 'username', # The HTML element + 'password_element': 'password', # The HTML element + 'submit_element': 'submit', # The HTML element + 'action_target': 'https://www.example.com/login/', + 'blacklist': False } window_index: window index, defaults to 0 + *Blacklist notes* To blacklist a site, add a blacklist password with the + following dictionary items: origin_url, signon_realm, username_element, + password_element, action_target, and 'blacklist': True. Then all sites that + have password forms matching those are blacklisted. + Returns: - The success or failure of adding the password. In incognito mode, adding - the password should fail. Example return: - { "password_added": True } + True if adding the password succeeded, false otherwise. In incognito + mode, adding the password should fail. Raises: JSONInterfaceError on error. """ cmd_dict = { # Prepare command for the json interface 'command': 'AddSavedPassword', - 'username': username, - 'password': password, - 'time': time + 'password': password_dict } - return self._GetResultFromJSONRequest(cmd_dict, windex=window_index) + return self._GetResultFromJSONRequest( + cmd_dict, windex=window_index)['password_added'] + + def RemoveSavedPassword(self, password_dict): + """Removes the password matching the provided password dictionary. + + Args: + password_dict: A dictionary that represents a password. + For an example, see the dictionary in AddSavedPassword. + """ + cmd_dict = { # Prepare command for the json interface + 'command': 'RemoveSavedPassword', + 'password': password_dict + } + self._GetResultFromJSONRequest(cmd_dict, windex=window_index) def GetSavedPasswords(self): """Return the passwords currently saved. Returns: - A list of 2-item lists of username, password for all saved passwords. - Example: - { 'passwords': [['username1', 'password1'], ['username2', 'password2']] } + A list of dictionaries representing each password. For an example + dictionary see AddSavedPassword documentation. The overall structure will + be: + [ {password1 dictionary}, {password2 dictionary} ] """ cmd_dict = { # Prepare command for the json interface 'command': 'GetSavedPasswords' } - return self._GetResultFromJSONRequest(cmd_dict) + return self._GetResultFromJSONRequest(cmd_dict)['passwords'] def SetTheme(self, crx_file_path): """Installs the given theme synchronously. |