summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-12 21:22:09 +0000
committertim@chromium.org <tim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-12 21:22:09 +0000
commit1fc9b3ffe78b02bcb819fefa2b0884a1e31d060b (patch)
tree56d7edd9250228f9b4c41ed87e1358ee37296a7e
parentc8b4944b44f10dc92209737015d81ca5eac9c1e7 (diff)
downloadchromium_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.grd2
-rw-r--r--chrome/browser/gtk/sync_setup_wizard_gtk.cc3
-rw-r--r--chrome/browser/sync/engine/auth_watcher.cc12
-rw-r--r--chrome/browser/sync/engine/auth_watcher.h18
-rw-r--r--chrome/browser/sync/engine/syncapi.cc23
-rw-r--r--chrome/browser/sync/engine/syncapi.h5
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc10
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h6
-rw-r--r--chrome/browser/sync/profile_sync_service.cc5
-rw-r--r--chrome/browser/sync/profile_sync_service.h3
-rw-r--r--chrome/browser/sync/resources/gaia_login.html177
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc15
-rw-r--r--chrome/browser/sync/sync_setup_flow.h5
-rw-r--r--chrome/browser/sync/sync_setup_wizard.cc2
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc45
-rw-r--r--chrome/test/live_sync/profile_sync_service_test_harness.cc2
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;