diff options
author | tim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-12 21:22:09 +0000 |
---|---|---|
committer | tim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-11-12 21:22:09 +0000 |
commit | 1fc9b3ffe78b02bcb819fefa2b0884a1e31d060b (patch) | |
tree | 56d7edd9250228f9b4c41ed87e1358ee37296a7e | |
parent | c8b4944b44f10dc92209737015d81ca5eac9c1e7 (diff) | |
download | chromium_src-1fc9b3ffe78b02bcb819fefa2b0884a1e31d060b.zip chromium_src-1fc9b3ffe78b02bcb819fefa2b0884a1e31d060b.tar.gz chromium_src-1fc9b3ffe78b02bcb819fefa2b0884a1e31d060b.tar.bz2 |
Implement the gaia captcha state and unlock capability in the sync setup wizard.
BUG=19738
TEST=Get a Google Account into captcha state. Start syncing. You should
be presented with a captcha image and textbox to answer the challenge.
Doing so correctly shoud unlock your account and proceed with sync setup.
Review URL: http://codereview.chromium.org/389017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@31829 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/resources/locale_settings.grd | 2 | ||||
-rw-r--r-- | chrome/browser/gtk/sync_setup_wizard_gtk.cc | 3 | ||||
-rw-r--r-- | chrome/browser/sync/engine/auth_watcher.cc | 12 | ||||
-rw-r--r-- | chrome/browser/sync/engine/auth_watcher.h | 18 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.cc | 23 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.h | 5 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.cc | 10 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.h | 6 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.cc | 5 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.h | 3 | ||||
-rw-r--r-- | chrome/browser/sync/resources/gaia_login.html | 177 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_flow.cc | 15 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_flow.h | 5 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard.cc | 2 | ||||
-rw-r--r-- | chrome/browser/sync/sync_setup_wizard_unittest.cc | 45 | ||||
-rw-r--r-- | chrome/test/live_sync/profile_sync_service_test_harness.cc | 2 |
16 files changed, 253 insertions, 80 deletions
diff --git a/chrome/app/resources/locale_settings.grd b/chrome/app/resources/locale_settings.grd index cf950bc..09505266 100644 --- a/chrome/app/resources/locale_settings.grd +++ b/chrome/app/resources/locale_settings.grd @@ -663,7 +663,7 @@ <!-- The height of the sync setup wizard / login dialog in lines. --> <message name="IDS_SYNC_SETUP_WIZARD_HEIGHT_LINES" use_name_for_id="true"> - 14 + 14.5 </message> <!-- The default width/height for confirmation dialogs. --> diff --git a/chrome/browser/gtk/sync_setup_wizard_gtk.cc b/chrome/browser/gtk/sync_setup_wizard_gtk.cc index 0bb8e61..7053520 100644 --- a/chrome/browser/gtk/sync_setup_wizard_gtk.cc +++ b/chrome/browser/gtk/sync_setup_wizard_gtk.cc @@ -116,7 +116,8 @@ void SyncSetupWizardGtk::OnDialogResponse(GtkWidget* widget, int response) { if (response == GTK_RESPONSE_ACCEPT) { service_->OnUserSubmittedAuth( gtk_entry_get_text(GTK_ENTRY(username_textbox_)), - gtk_entry_get_text(GTK_ENTRY(password_textbox_))); + gtk_entry_get_text(GTK_ENTRY(password_textbox_)), + std::string()); service_->SetSyncSetupCompleted(); } diff --git a/chrome/browser/sync/engine/auth_watcher.cc b/chrome/browser/sync/engine/auth_watcher.cc index 9d81d28..1f78d15 100644 --- a/chrome/browser/sync/engine/auth_watcher.cc +++ b/chrome/browser/sync/engine/auth_watcher.cc @@ -217,11 +217,19 @@ void AuthWatcher::DoAuthenticate(const AuthRequest& request) { SignIn const signin = user_settings_-> RecallSigninType(request.email, GMAIL_SIGNIN); + // We let the caller be lazy and try using the last captcha token seen by + // the gaia authenticator if they haven't provided a token but have sent + // a challenge response. Of course, if the captcha token is specified, + // we use that one instead. + std::string captcha_token(request.captcha_token); + if (!request.captcha_value.empty() && captcha_token.empty()) + captcha_token = gaia_->captcha_token(); + if (!request.password.empty()) { bool authenticated = false; - if (!request.captcha_token.empty() && !request.captcha_value.empty()) { + if (!captcha_token.empty()) { authenticated = gaia_->Authenticate(request.email, request.password, - save, request.captcha_token, + save, captcha_token, request.captcha_value, signin); } else { authenticated = gaia_->Authenticate(request.email, request.password, diff --git a/chrome/browser/sync/engine/auth_watcher.h b/chrome/browser/sync/engine/auth_watcher.h index 9457e33..20760e4 100644 --- a/chrome/browser/sync/engine/auth_watcher.h +++ b/chrome/browser/sync/engine/auth_watcher.h @@ -106,6 +106,10 @@ class AuthWatcher : public base::RefCountedThreadSafe<AuthWatcher> { // The following 3 flavors of authentication routines are asynchronous and can // be called from any thread. + // If |captcha_value| is specified but |captcha_token| is not, this will + // attempt authentication using the last observed captcha token out of + // convenience in the common case so the token doesn't have to be plumbed + // everywhere. void Authenticate(const std::string& email, const std::string& password, const std::string& captcha_token, const std::string& captcha_value, bool persist_creds_to_disk); @@ -171,13 +175,13 @@ class AuthWatcher : public base::RefCountedThreadSafe<AuthWatcher> { // A struct to marshal various data across to the auth_backend_thread_ on // Authenticate() and AuthenticateWithToken calls. struct AuthRequest { - std::string email; - std::string password; - std::string auth_token; - std::string captcha_token; - std::string captcha_value; - bool persist_creds_to_disk; - AuthWatcherEvent::AuthenticationTrigger trigger; + std::string email; + std::string password; + std::string auth_token; + std::string captcha_token; + std::string captcha_value; + bool persist_creds_to_disk; + AuthWatcherEvent::AuthenticationTrigger trigger; }; // The public interface Authenticate methods are proxies to these, which diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 7219abc..19225cf 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -706,7 +706,8 @@ class SyncManager::SyncInternal { // the SyncManager::Observer. It may, in turn, decide to try again with new // credentials. Calling this method again is the appropriate course of action // to "retry". - void Authenticate(const std::string& username, const std::string& password); + void Authenticate(const std::string& username, const std::string& password, + const std::string& captcha); // Call periodically from a database-safe thread to persist recent changes // to the syncapi model. @@ -934,8 +935,10 @@ bool SyncManager::Init(const FilePath& database_location, user_agent); } -void SyncManager::Authenticate(const char* username, const char* password) { - data_->Authenticate(std::string(username), std::string(password)); +void SyncManager::Authenticate(const char* username, const char* password, + const char* captcha) { + data_->Authenticate(std::string(username), std::string(password), + std::string(captcha)); } const std::string& SyncManager::GetAuthenticatedUsername() { @@ -1058,7 +1061,8 @@ void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() { } void SyncManager::SyncInternal::Authenticate(const std::string& username, - const std::string& password) { + const std::string& password, + const std::string& captcha) { DCHECK(username_for_share().empty() || username == username_for_share()) << "Username change from valid username detected"; if (allstatus()->status().authenticated) @@ -1070,7 +1074,8 @@ void SyncManager::SyncInternal::Authenticate(const std::string& username, // our GoogleServiceAuthError state to denote an error. RaiseAuthNeededEvent(); } - auth_watcher()->Authenticate(username, password, true); + auth_watcher()->Authenticate(username, password, std::string(), + captcha, true); } void SyncManager::SyncInternal::AuthenticateForLastKnownUser() { @@ -1435,11 +1440,9 @@ void SyncManager::SyncInternal::HandleAuthWatcherEvent( case AuthWatcherEvent::GAIA_AUTH_FAILED: // Invalid GAIA credentials. if (event.auth_results->auth_error == browser_sync::CaptchaRequired) { auth_problem_ = AuthError::CAPTCHA_REQUIRED; - GURL captcha("http://www.google.com/accounts/"); - GURL::Replacements replacer; - replacer.SetPathStr(captcha.path().append( - event.auth_results->captcha_url)); - captcha = captcha.ReplaceComponents(replacer); + std::string url_string("http://www.google.com/accounts/"); + url_string += event.auth_results->captcha_url; + GURL captcha(url_string); observer_->OnAuthError(AuthError::FromCaptchaChallenge( event.auth_results->captcha_token, captcha, GURL(event.auth_results->auth_error_url))); diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index f91d765..d2a519a 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -522,8 +522,9 @@ class SYNC_EXPORT SyncManager { // The Observer may, in turn, decide to try again with new // credentials. Calling this method again is the appropriate course of action // to "retry". - // |username| and |password| are expected to be owned by the caller. - void Authenticate(const char* username, const char* password); + // |username|, |password|, and |captcha| are owned by the caller. + void Authenticate(const char* username, const char* password, + const char* captcha); // Adds a listener to be notified of sync events. // NOTE: It is OK (in fact, it's probably a good idea) to call this before diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index dcce1d4..1c74419 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -56,10 +56,11 @@ void SyncBackendHost::Initialize( } void SyncBackendHost::Authenticate(const std::string& username, - const std::string& password) { + const std::string& password, + const std::string& captcha) { core_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoAuthenticate, - username, password)); + username, password, captcha)); } void SyncBackendHost::Shutdown(bool sync_disabled) { @@ -193,9 +194,10 @@ void SyncBackendHost::Core::DoInitialize( } void SyncBackendHost::Core::DoAuthenticate(const std::string& username, - const std::string& password) { + const std::string& password, + const std::string& captcha) { DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); - syncapi_->Authenticate(username.c_str(), password.c_str()); + syncapi_->Authenticate(username.c_str(), password.c_str(), captcha.c_str()); } void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index 7bc633e..8192b34 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -87,7 +87,8 @@ class SyncBackendHost { URLRequestContextGetter* baseline_context_getter); // Called on |frontend_loop_| to kick off asynchronous authentication. - void Authenticate(const std::string& username, const std::string& password); + void Authenticate(const std::string& username, const std::string& password, + const std::string& captcha); // Called on |frontend_loop_| to kick off shutdown. // |sync_disabled| indicates if syncing is being disabled or not. @@ -168,7 +169,8 @@ class SyncBackendHost { // Called on our SyncBackendHost's core_thread_ to perform authentication // on behalf of SyncBackendHost::Authenticate. void DoAuthenticate(const std::string& username, - const std::string& password); + const std::string& password, + const std::string& captcha); // The shutdown order is a bit complicated: // 1) From |core_thread_|, invoke the syncapi Shutdown call to do a final diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index 953ebd3..d380578 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -329,13 +329,14 @@ string16 ProfileSyncService::GetAuthenticatedUsername() const { } void ProfileSyncService::OnUserSubmittedAuth( - const std::string& username, const std::string& password) { + const std::string& username, const std::string& password, + const std::string& captcha) { last_attempted_user_email_ = username; is_auth_in_progress_ = true; FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); auth_start_time_ = base::TimeTicks::Now(); - backend_->Authenticate(username, password); + backend_->Authenticate(username, password, captcha); } void ProfileSyncService::OnUserAcceptedMergeAndSync() { diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index a738311..10cb7d5 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -118,7 +118,8 @@ class ProfileSyncService : public NotificationObserver, // Called when a user enters credentials through UI. virtual void OnUserSubmittedAuth(const std::string& username, - const std::string& password); + const std::string& password, + const std::string& captcha); // Called when a user decides whether to merge and sync or abort. virtual void OnUserAcceptedMergeAndSync(); diff --git a/chrome/browser/sync/resources/gaia_login.html b/chrome/browser/sync/resources/gaia_login.html index 1da763c..899444a 100644 --- a/chrome/browser/sync/resources/gaia_login.html +++ b/chrome/browser/sync/resources/gaia_login.html @@ -35,17 +35,71 @@ margin: 5px; } #gaia_loginform { - margin-bottom: 0.5em; + margin-bottom: 0; } - --></style> + + #captcha_wrapper { + -webkit-background-size: 200px 70px; + background: no-repeat; + background-position: center; + background-color: #e8eefa; + display: block; + } + .captcha_image { + display: block; + width: 200px; + height: 70px; + } + .toppageverticalspace { + height: 15px; + } + .bottompaddedcell { + padding-bottom: 3px; + } + .noverticalpadding { + padding-top: 0; + padding-bottom: 0; + } + .cancelspacenocaptcha { + height: 22px; + } + .cancelspaceforcaptcha { + height: 5px; + } + --> + </style> </head> <body bgcolor="#ffffff" vlink="#666666" i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize" - style="margin-bottom: 0" onload="initForm();"> + style="margin-bottom: 0; margin-top: 6px;" onload="initForm();"> <table width="100%" align="center" cellpadding="1" cellspacing="1"> <tr> <td valign="top"> <!-- LOGIN BOX --> - <script> + <script> + // Variable to track if a captcha challenge was issued. If this gets set to + // true, it stays that way until we are told about successful login from + // the browser. This means subsequent errors (like invalid password) are + // rendered in the captcha state, which is basically identical except we + // don't show the top error blurb "Error Signing in" or the "Create + // account" link. + var g_is_captcha_challenge_active = false; + + // Taken from new_new_tab.js. + // TODO(tim): Can this be unified? + function url(s) { + // http://www.w3.org/TR/css3-values/#uris + // Parentheses, commas, whitespace characters, single quotes (') and + // double quotes (") appearing in a URI must be escaped with a backslash + var s2 = s.replace(/(\(|\)|\,|\s|\'|\"|\\)/g, '\\$1'); + // WebKit has a bug when it comes to URLs that end with \ + // https://bugs.webkit.org/show_bug.cgi?id=28885 + if (/\\\\$/.test(s2)) { + // Add a space to work around the WebKit bug. + s2 += ' '; + } + return 'url("' + s2 + '")'; + } + function gaia_setFocus() { var f = null; if (document.getElementById) { @@ -73,8 +127,8 @@ throbber.style.display = "none"; var f = document.getElementById("gaia_loginform"); if (f) { - f.Email.value = args.user; - } + f.Email.value = args.user; + } if (1 == args.error) { setElementDisplay("errormsg_1_Password", 'table-row'); setBlurbError(); @@ -83,10 +137,37 @@ setElementDisplay("errormsg_0_Connection", 'table-row'); setBlurbError(); } + if (4 == args.error) { + showCaptcha(args); + } document.getElementById("signIn").disabled = false; gaia_setFocus(); } - + + function showCaptcha(args) { + g_is_captcha_challenge_active = true; + + // The captcha takes up lots of space, so make room. + setElementDisplay("top_blurb", "none"); + setElementDisplay("top_blurb_error", "none"); + setElementDisplay("createaccountdiv", "none"); + setElementDisplay("topspacer", "none"); + var gaiaTable = document.getElementById('gaia_table'); + gaiaTable.cellPadding = 0; + gaiaTable.cellSpacing = 1; + document.getElementById('cancelspacer').className = + "cancelspaceforcaptcha"; + document.getElementById('createaccountcell').height = 0; + + // It's showtime for the captcha now. + setElementDisplay("captchadiv", "block"); + document.getElementById('Email').disabled = true; + document.getElementById('Passwd').disabled = false; + document.getElementById('CaptchaValue').disabled = false; + document.getElementById('captcha_wrapper').style.backgroundImage = + url(args.captchaUrl); + } + function CloseDialog() { chrome.send("DialogClose", [""]); } @@ -126,12 +207,13 @@ document.getElementById('Email').disabled = true; document.getElementById('Passwd').disabled = true; - + document.getElementById('CaptchaValue').disabled = true; var throbber = document.getElementById('throbber_container'); throbber.style.display = "inline"; var f = document.getElementById("gaia_loginform"); var result = JSON.stringify({"user" : f.Email.value, - "pass" : f.Passwd.value}); + "pass" : f.Passwd.value, + "captcha" : f.CaptchaValue.value}); document.getElementById("signIn").disabled = true; chrome.send("SubmitAuth", [result]); } @@ -140,9 +222,11 @@ var d = document.getElementById(id); if (d) d.style.display = display; - } - + } + function setBlurbError() { + if (g_is_captcha_challenge_active) + return; // No blurb in captcha challenge mode. document.getElementById("top_blurb").style.display = "none"; document.getElementById("top_blurb_error").style.display = "block"; document.getElementById('Email').disabled = false; @@ -198,17 +282,19 @@ </div> <form id="gaia_loginform" onsubmit="sendCredentialsAndClose(); return false;"> <div id="gaia_loginbox"> -<table class="form-noindent" cellspacing="3" cellpadding="5" width="75%" - border="0" align="center"> +<table id="content_table" class="form-noindent" cellspacing="0" cellpadding="5" + width="75%" border="0" align="center"> <tr> <td valign="top" style="text-align:center" nowrap="nowrap" bgcolor="#e8eefa"> - <div class="loginBox"> + <div> <table id="gaia_table" align="center" border="0" cellpadding="1" cellspacing="0"> + <tr> + <td id="topspacer" class="toppageverticalspace" colspan="2"></td> + </tr> <tr> <td colspan="2" align="center"> - <font size="-1"><span i18n-content="signinprefix"></span></font> <table> <tr> <td valign="top" id="gaia_logo"> @@ -289,11 +375,40 @@ <span i18n-content="invalidcredentials"></span>[<a href="http://www.google.com/support/accounts/bin/answer.py?ctx=ch&answer=27444" target="_blank">?</a>] </div> </td> - </tr> + </tr> <tr> - <td> + <td colspan="2"> + <div id="captchadiv" style="display:none"> + <table cellpadding="1" cellspacing="0" border="0"> + <tbody> + <tr> + <td colspan="2" align="center" class="bottompaddedcell"> + <font size="-1"> + <span i18n-content="captchainstructions"></span> + </font> + </td> + </tr> + <tr> + <td colspan="2" align="center" class="bottompaddedcell"> + <span id="captcha_wrapper"> + <span class="captcha_image"></span> + </span> + </td> + </tr> + <tr> + <td colspan="2" align="center"> + <input type="text" name="CaptchaValue" + id="CaptchaValue" size="18" + value="" class="gaia le val" /> + </td> + </tr> + </tbody> + </table> + </div> </td> - <td> + </tr> + <tr> + <td colspan="2" align="center"> <div class="errormsg" id="errormsg_0_Connection" i18n-content="couldnotconnect"> </div> @@ -306,10 +421,8 @@ </td> </tr> <tr> - <td> - </td> - <td> - <table> + <td colspan="2"> + <table align="center" cellpadding="0" cellspacing="0"> <tr> <td> <div id="throbber_container" style="display:none"> @@ -318,7 +431,7 @@ </div> </div> </td> - <td> + <td class="noverticalpadding"> <input id="signIn" type="button" class="gaia le button" name="signIn" i18n-values="value:signin" onclick="sendCredentialsAndClose();" @@ -337,11 +450,13 @@ </td> </tr> <tr> - <td colspan="2" height="16.0" class="gaia le fpwd" - align="center" valign="bottom"> - <a href="https://www.google.com/accounts/NewAccount?service=chromiumsync" - i18n-content="createaccount" target="_blank"> - </a> + <td id="createaccountcell" colspan="2" height="16.0" + class="gaia le fpwd" align="center" valign="bottom"> + <div id="createaccountdiv"> + <a href="https://www.google.com/accounts/NewAccount?service=chromiumsync" + i18n-content="createaccount" target="_blank"> + </a> + </div> </td> </tr> </table> @@ -352,7 +467,11 @@ </div> </form> </td> -</tr> +</tr> +<tr> + <td class="cancelspacenocaptcha" id="cancelspacer" colspan="2"> + </td> +</tr> </table> <div class="endaligned"> <input type="button" name="cancel" i18n-values="value:cancel" diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc index 50a8c2c..7e4b140 100644 --- a/chrome/browser/sync/sync_setup_flow.cc +++ b/chrome/browser/sync/sync_setup_flow.cc @@ -55,15 +55,16 @@ void FlowHandler::RegisterMessages() { NewCallback(this, &FlowHandler::HandleSubmitMergeAndSync)); } -static bool GetUsernameAndPassword(const std::string& json, - std::string* username, std::string* password) { +static bool GetAuthData(const std::string& json, + std::string* username, std::string* password, std::string* captcha) { scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false)); if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY)) return false; DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get()); if (!result->GetString(L"user", username) || - !result->GetString(L"pass", password)) { + !result->GetString(L"pass", password) || + !result->GetString(L"captcha", captcha)) { return false; } return true; @@ -71,11 +72,11 @@ static bool GetUsernameAndPassword(const std::string& json, void FlowHandler::HandleSubmitAuth(const Value* value) { std::string json(GetJsonResponse(value)); - std::string username, password; + std::string username, password, captcha; if (json.empty()) return; - if (!GetUsernameAndPassword(json, &username, &password)) { + if (!GetAuthData(json, &username, &password, &captcha)) { // The page sent us something that we didn't understand. // This probably indicates a programming error. NOTREACHED(); @@ -83,7 +84,7 @@ void FlowHandler::HandleSubmitAuth(const Value* value) { } if (flow_) - flow_->OnUserSubmittedAuth(username, password); + flow_->OnUserSubmittedAuth(username, password, captcha); } void FlowHandler::HandleSubmitMergeAndSync(const Value* value) { @@ -223,6 +224,8 @@ void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service, args->SetString(L"user", user); args->SetInteger(L"error", user.empty() ? 0 : error.state()); } + + args->SetString(L"captchaUrl", error.captcha().image_url.spec()); } void SyncSetupFlow::GetDOMMessageHandlers( diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h index 98345fc..c089155 100644 --- a/chrome/browser/sync/sync_setup_flow.h +++ b/chrome/browser/sync/sync_setup_flow.h @@ -77,8 +77,9 @@ class SyncSetupFlow : public HtmlDialogUIDelegate { } void OnUserSubmittedAuth(const std::string& username, - const std::string& password) { - service_->OnUserSubmittedAuth(username, password); + const std::string& password, + const std::string& captcha) { + service_->OnUserSubmittedAuth(username, password, captcha); } void OnUserAcceptedMergeAndSync() { diff --git a/chrome/browser/sync/sync_setup_wizard.cc b/chrome/browser/sync/sync_setup_wizard.cc index b05fe30..957184c 100644 --- a/chrome/browser/sync/sync_setup_wizard.cc +++ b/chrome/browser/sync/sync_setup_wizard.cc @@ -85,6 +85,8 @@ void SyncResourcesSource::StartDataRequest(const std::string& path_raw, l10n_util::GetString(IDS_SYNC_SUCCESS)); localized_strings.SetString(L"errorsigningin", l10n_util::GetString(IDS_SYNC_ERROR_SIGNING_IN)); + localized_strings.SetString(L"captchainstructions", + l10n_util::GetString(IDS_SYNC_GAIA_CAPTCHA_INSTRUCTIONS)); static const base::StringPiece html(ResourceBundle::GetSharedInstance() .GetRawDataResource(IDR_GAIA_LOGIN_HTML)); SetFontAndTextDirection(&localized_strings); diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc index ba9e8d9..4fa299f 100644 --- a/chrome/browser/sync/sync_setup_wizard_unittest.cc +++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc @@ -21,6 +21,8 @@ static const char* kTestUser = "chrome.p13n.test@gmail.com"; static const char* kTestPassword = "passwd"; +static const char* kTestCaptcha = "pizzamyheart"; +static const char* kTestCaptchaUrl = "http://pizzamyheart/"; typedef GoogleServiceAuthError AuthError; @@ -36,9 +38,11 @@ class ProfileSyncServiceForWizardTest : public ProfileSyncService { virtual ~ProfileSyncServiceForWizardTest() { } virtual void OnUserSubmittedAuth(const std::string& username, - const std::string& password) { + const std::string& password, + const std::string& captcha) { username_ = username; password_ = password; + captcha_ = captcha; } virtual void OnUserAcceptedMergeAndSync() { user_accepted_merge_and_sync_ = true; @@ -52,20 +56,22 @@ class ProfileSyncServiceForWizardTest : public ProfileSyncService { } void set_auth_state(const std::string& last_email, - const AuthError::State& state) { + const AuthError& error) { last_attempted_user_email_ = last_email; - last_auth_error_ = AuthError(state); + last_auth_error_ = error; } void ResetTestStats() { username_.clear(); password_.clear(); + captcha_.clear(); user_accepted_merge_and_sync_ = false; user_cancelled_dialog_ = false; } std::string username_; std::string password_; + std::string captcha_; bool user_accepted_merge_and_sync_; bool user_cancelled_dialog_; @@ -188,7 +194,8 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) { ListValue credentials; std::string auth = "{\"user\":\""; auth += std::string(kTestUser) + "\",\"pass\":\""; - auth += std::string(kTestPassword) + "\"}"; + auth += std::string(kTestPassword) + "\",\"captcha\":\""; + auth += std::string(kTestCaptcha) + "\"}"; credentials.Append(new StringValue(auth)); EXPECT_FALSE(wizard_->IsVisible()); @@ -207,26 +214,43 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) { EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_); EXPECT_EQ(kTestUser, service_->username_); EXPECT_EQ(kTestPassword, service_->password_); + EXPECT_EQ(kTestCaptcha, service_->captcha_); EXPECT_FALSE(service_->user_accepted_merge_and_sync_); EXPECT_FALSE(service_->user_cancelled_dialog_); service_->ResetTestStats(); // Simulate failed credentials. - service_->set_auth_state(kTestUser, AuthError::INVALID_GAIA_CREDENTIALS); + AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS); + service_->set_auth_state(kTestUser, invalid_gaia); wizard_->Step(SyncSetupWizard::GAIA_LOGIN); EXPECT_TRUE(wizard_->IsVisible()); EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled()); EXPECT_EQ(SyncSetupWizard::GAIA_LOGIN, test_window_->flow()->current_state_); dialog_args.Clear(); SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args); - EXPECT_TRUE(2 == dialog_args.GetSize()); + EXPECT_TRUE(3 == dialog_args.GetSize()); std::string actual_user; dialog_args.GetString(L"user", &actual_user); EXPECT_EQ(kTestUser, actual_user); int error = -1; dialog_args.GetInteger(L"error", &error); EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error); - service_->set_auth_state(kTestUser, AuthError::NONE); + service_->set_auth_state(kTestUser, AuthError::None()); + + // Simulate captcha. + AuthError captcha_error(AuthError::FromCaptchaChallenge( + std::string(), GURL(kTestCaptchaUrl), GURL())); + service_->set_auth_state(kTestUser, captcha_error); + wizard_->Step(SyncSetupWizard::GAIA_LOGIN); + SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args); + EXPECT_TRUE(3 == dialog_args.GetSize()); + std::string captcha_url; + dialog_args.GetString(L"captchaUrl", &captcha_url); + EXPECT_EQ(kTestCaptchaUrl, GURL(captcha_url).spec()); + error = -1; + dialog_args.GetInteger(L"error", &error); + EXPECT_EQ(static_cast<int>(AuthError::CAPTCHA_REQUIRED), error); + service_->set_auth_state(kTestUser, AuthError::None()); // Simulate success. wizard_->Step(SyncSetupWizard::GAIA_SUCCESS); @@ -365,18 +389,19 @@ TEST_F(SyncSetupWizardTest, DiscreteRun) { wizard_->Step(SyncSetupWizard::GAIA_LOGIN); EXPECT_EQ(SyncSetupWizard::GAIA_SUCCESS, test_window_->flow()->end_state_); - service_->set_auth_state(kTestUser, AuthError::INVALID_GAIA_CREDENTIALS); + AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS); + service_->set_auth_state(kTestUser, invalid_gaia); wizard_->Step(SyncSetupWizard::GAIA_LOGIN); EXPECT_TRUE(wizard_->IsVisible()); SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args); - EXPECT_TRUE(2 == dialog_args.GetSize()); + EXPECT_TRUE(3 == dialog_args.GetSize()); std::string actual_user; dialog_args.GetString(L"user", &actual_user); EXPECT_EQ(kTestUser, actual_user); int error = -1; dialog_args.GetInteger(L"error", &error); EXPECT_EQ(static_cast<int>(AuthError::INVALID_GAIA_CREDENTIALS), error); - service_->set_auth_state(kTestUser, AuthError::NONE); + service_->set_auth_state(kTestUser, AuthError::None()); wizard_->Step(SyncSetupWizard::GAIA_SUCCESS); EXPECT_TRUE(test_window_->TestAndResetWasShowHTMLDialogCalled()); diff --git a/chrome/test/live_sync/profile_sync_service_test_harness.cc b/chrome/test/live_sync/profile_sync_service_test_harness.cc index c2202f7..c7dc73b 100644 --- a/chrome/test/live_sync/profile_sync_service_test_harness.cc +++ b/chrome/test/live_sync/profile_sync_service_test_harness.cc @@ -245,7 +245,7 @@ bool ProfileSyncServiceTestHarness::WaitForServiceInit() { } // Wait for the OnBackendInitialized callback. - service_->backend()->Authenticate(username_, password_); + service_->backend()->Authenticate(username_, password_, std::string()); EXPECT_EQ(wait_state_, WAITING_FOR_READY_TO_PROCESS_CHANGES); if (!AwaitStatusChangeWithTimeout(30, "Waiting on backend initialization.")) { return false; |