| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h" |
| |
| #include <stddef.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/user_metrics_action.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_attributes_entry.h" |
| #include "chrome/browser/profiles/profile_attributes_storage.h" |
| #include "chrome/browser/profiles/profile_window.h" |
| #include "chrome/browser/signin/about_signin_internals_factory.h" |
| #include "chrome/browser/signin/account_tracker_service_factory.h" |
| #include "chrome/browser/signin/chrome_signin_client_factory.h" |
| #include "chrome/browser/signin/investigator_dependency_provider.h" |
| #include "chrome/browser/signin/local_auth.h" |
| #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" |
| #include "chrome/browser/signin/signin_error_controller_factory.h" |
| #include "chrome/browser/signin/signin_manager_factory.h" |
| #include "chrome/browser/signin/signin_promo.h" |
| #include "chrome/browser/sync/profile_sync_service_factory.h" |
| #include "chrome/browser/ui/browser_finder.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/browser/ui/chrome_pages.h" |
| #include "chrome/browser/ui/tab_modal_confirm_dialog.h" |
| #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/browser/ui/user_manager.h" |
| #include "chrome/browser/ui/webui/signin/get_auth_frame.h" |
| #include "chrome/browser/ui/webui/signin/login_ui_service.h" |
| #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/chromium_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "components/browser_sync/browser/profile_sync_service.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/signin/core/browser/about_signin_internals.h" |
| #include "components/signin/core/browser/account_tracker_service.h" |
| #include "components/signin/core/browser/profile_oauth2_token_service.h" |
| #include "components/signin/core/browser/signin_error_controller.h" |
| #include "components/signin/core/browser/signin_header_helper.h" |
| #include "components/signin/core/browser/signin_investigator.h" |
| #include "components/signin/core/browser/signin_metrics.h" |
| #include "components/signin/core/common/profile_management_switches.h" |
| #include "components/signin/core/common/signin_pref_names.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "content/public/browser/web_ui.h" |
| #include "google_apis/gaia/gaia_auth_fetcher.h" |
| #include "google_apis/gaia/gaia_auth_util.h" |
| #include "google_apis/gaia/gaia_constants.h" |
| #include "google_apis/gaia/gaia_urls.h" |
| #include "grit/components_strings.h" |
| #include "net/base/url_util.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace { |
| |
| void LogHistogramValue(int action) { |
| UMA_HISTOGRAM_ENUMERATION("Signin.AllAccessPointActions", action, |
| signin_metrics::HISTOGRAM_MAX); |
| } |
| |
| // Returns true if |profile| is a system profile or created from one. |
| bool IsSystemProfile(Profile* profile) { |
| return profile->GetOriginalProfile()->IsSystemProfile(); |
| } |
| |
| void RedirectToNtpOrAppsPage(content::WebContents* contents, |
| signin_metrics::AccessPoint access_point) { |
| // Do nothing if a navigation is pending, since this call can be triggered |
| // from DidStartLoading. This avoids deleting the pending entry while we are |
| // still navigating to it. See crbug/346632. |
| if (contents->GetController().GetPendingEntry()) |
| return; |
| |
| VLOG(1) << "RedirectToNtpOrAppsPage"; |
| // Redirect to NTP/Apps page and display a confirmation bubble |
| GURL url(access_point == |
| signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK |
| ? chrome::kChromeUIAppsURL |
| : chrome::kChromeUINewTabURL); |
| content::OpenURLParams params(url, |
| content::Referrer(), |
| CURRENT_TAB, |
| ui::PAGE_TRANSITION_AUTO_TOPLEVEL, |
| false); |
| contents->OpenURL(params); |
| } |
| |
| void RedirectToNtpOrAppsPageIfNecessary( |
| content::WebContents* contents, |
| signin_metrics::AccessPoint access_point) { |
| if (access_point != signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS) |
| RedirectToNtpOrAppsPage(contents, access_point); |
| } |
| |
| class ConfirmEmailDialogDelegate : public TabModalConfirmDialogDelegate { |
| public: |
| // Callback indicating action performed by the user. |
| typedef base::Callback<void(InlineSigninHelper::Action)> Callback; |
| |
| // Ask the user for confirmation before starting to sync. |
| static void AskForConfirmation(content::WebContents* contents, |
| const std::string& last_email, |
| const std::string& email, |
| Callback callback); |
| |
| private: |
| ConfirmEmailDialogDelegate(content::WebContents* contents, |
| const std::string& last_email, |
| const std::string& email, |
| Callback callback); |
| ~ConfirmEmailDialogDelegate() override; |
| |
| // TabModalConfirmDialogDelegate: |
| base::string16 GetTitle() override; |
| base::string16 GetDialogMessage() override; |
| base::string16 GetAcceptButtonTitle() override; |
| base::string16 GetCancelButtonTitle() override; |
| base::string16 GetLinkText() const override; |
| void OnAccepted() override; |
| void OnCanceled() override; |
| void OnClosed() override; |
| void OnLinkClicked(WindowOpenDisposition disposition) override; |
| |
| std::string last_email_; |
| std::string email_; |
| Callback callback_; |
| |
| // Web contents from which the "Learn more" link should be opened. |
| content::WebContents* web_contents_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ConfirmEmailDialogDelegate); |
| }; |
| |
| // static |
| void ConfirmEmailDialogDelegate::AskForConfirmation( |
| content::WebContents* contents, |
| const std::string& last_email, |
| const std::string& email, |
| Callback callback) { |
| content::RecordAction( |
| base::UserMetricsAction("Signin_Show_ImportDataPrompt")); |
| TabModalConfirmDialog::Create( |
| new ConfirmEmailDialogDelegate(contents, last_email, email, callback), |
| contents); |
| } |
| |
| ConfirmEmailDialogDelegate::ConfirmEmailDialogDelegate( |
| content::WebContents* contents, |
| const std::string& last_email, |
| const std::string& email, |
| Callback callback) |
| : TabModalConfirmDialogDelegate(contents), |
| last_email_(last_email), |
| email_(email), |
| callback_(callback), |
| web_contents_(contents) { |
| } |
| |
| ConfirmEmailDialogDelegate::~ConfirmEmailDialogDelegate() { |
| } |
| |
| base::string16 ConfirmEmailDialogDelegate::GetTitle() { |
| return l10n_util::GetStringUTF16( |
| IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_TITLE); |
| } |
| |
| base::string16 ConfirmEmailDialogDelegate::GetDialogMessage() { |
| return l10n_util::GetStringFUTF16( |
| IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_MESSAGE, |
| base::UTF8ToUTF16(last_email_), base::UTF8ToUTF16(email_)); |
| } |
| |
| base::string16 ConfirmEmailDialogDelegate::GetAcceptButtonTitle() { |
| return l10n_util::GetStringUTF16( |
| IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON); |
| } |
| |
| base::string16 ConfirmEmailDialogDelegate::GetCancelButtonTitle() { |
| return l10n_util::GetStringUTF16( |
| IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON); |
| } |
| |
| base::string16 ConfirmEmailDialogDelegate::GetLinkText() const { |
| return l10n_util::GetStringUTF16(IDS_LEARN_MORE); |
| } |
| |
| void ConfirmEmailDialogDelegate::OnAccepted() { |
| base::ResetAndReturn(&callback_).Run(InlineSigninHelper::CREATE_NEW_USER); |
| } |
| |
| void ConfirmEmailDialogDelegate::OnCanceled() { |
| base::ResetAndReturn(&callback_).Run(InlineSigninHelper::START_SYNC); |
| } |
| |
| void ConfirmEmailDialogDelegate::OnClosed() { |
| base::ResetAndReturn(&callback_).Run(InlineSigninHelper::CLOSE); |
| } |
| |
| void ConfirmEmailDialogDelegate::OnLinkClicked( |
| WindowOpenDisposition disposition) { |
| content::OpenURLParams params( |
| GURL(chrome::kChromeSyncMergeTroubleshootingURL), |
| content::Referrer(), |
| NEW_POPUP, |
| ui::PAGE_TRANSITION_AUTO_TOPLEVEL, |
| false); |
| // It is guaranteed that |web_contents_| is valid here because when it's |
| // deleted, the dialog is immediately closed and no further action can be |
| // performed. |
| web_contents_->OpenURL(params); |
| } |
| |
| void CloseModalSigninIfNeeded(InlineLoginHandlerImpl* handler) { |
| if (handler && switches::UsePasswordSeparatedSigninFlow()) { |
| Browser* browser = handler->GetDesktopBrowser(); |
| if (browser) |
| browser->CloseModalSigninWindow(); |
| } |
| } |
| |
| } // namespace |
| |
| InlineSigninHelper::InlineSigninHelper( |
| base::WeakPtr<InlineLoginHandlerImpl> handler, |
| net::URLRequestContextGetter* getter, |
| Profile* profile, |
| const GURL& current_url, |
| const std::string& email, |
| const std::string& gaia_id, |
| const std::string& password, |
| const std::string& session_index, |
| const std::string& auth_code, |
| const std::string& signin_scoped_device_id, |
| bool choose_what_to_sync, |
| bool confirm_untrusted_signin) |
| : gaia_auth_fetcher_(this, GaiaConstants::kChromeSource, getter), |
| handler_(handler), |
| profile_(profile), |
| current_url_(current_url), |
| email_(email), |
| gaia_id_(gaia_id), |
| password_(password), |
| session_index_(session_index), |
| auth_code_(auth_code), |
| choose_what_to_sync_(choose_what_to_sync), |
| confirm_untrusted_signin_(confirm_untrusted_signin) { |
| DCHECK(profile_); |
| DCHECK(!email_.empty()); |
| if (!auth_code_.empty()) { |
| gaia_auth_fetcher_.StartAuthCodeForOAuth2TokenExchangeWithDeviceId( |
| auth_code, signin_scoped_device_id); |
| } else { |
| DCHECK(!session_index_.empty()); |
| gaia_auth_fetcher_.StartCookieForOAuthLoginTokenExchangeWithDeviceId( |
| session_index_, signin_scoped_device_id); |
| } |
| } |
| |
| InlineSigninHelper::~InlineSigninHelper() {} |
| |
| void InlineSigninHelper::OnClientOAuthSuccess(const ClientOAuthResult& result) { |
| content::WebContents* contents = NULL; |
| Browser* browser = NULL; |
| if (handler_) { |
| contents = handler_->web_ui()->GetWebContents(); |
| browser = handler_->GetDesktopBrowser(); |
| } |
| |
| AboutSigninInternals* about_signin_internals = |
| AboutSigninInternalsFactory::GetForProfile(profile_); |
| about_signin_internals->OnRefreshTokenReceived("Successful"); |
| |
| // Prime the account tracker with this combination of gaia id/display email. |
| std::string account_id = |
| AccountTrackerServiceFactory::GetForProfile(profile_) |
| ->SeedAccountInfo(gaia_id_, email_); |
| |
| signin_metrics::AccessPoint access_point = |
| signin::GetAccessPointForPromoURL(current_url_); |
| signin_metrics::Reason reason = |
| signin::GetSigninReasonForPromoURL(current_url_); |
| |
| SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_); |
| std::string primary_email = |
| signin_manager->GetAuthenticatedAccountInfo().email; |
| if (gaia::AreEmailsSame(email_, primary_email) && |
| (reason == signin_metrics::Reason::REASON_REAUTHENTICATION || |
| reason == signin_metrics::Reason::REASON_UNLOCK) && |
| switches::IsNewProfileManagement() && !password_.empty() && |
| profiles::IsLockAvailable(profile_)) { |
| LocalAuth::SetLocalAuthCredentials(profile_, password_); |
| } |
| |
| if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION || |
| reason == signin_metrics::Reason::REASON_UNLOCK || |
| reason == signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT) { |
| ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)-> |
| UpdateCredentials(account_id, result.refresh_token); |
| |
| if (signin::IsAutoCloseEnabledInURL(current_url_)) { |
| // Close the gaia sign in tab via a task to make sure we aren't in the |
| // middle of any webui handler code. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&InlineLoginHandlerImpl::CloseTab, handler_, |
| signin::ShouldShowAccountManagement(current_url_))); |
| } |
| |
| if (reason == signin_metrics::Reason::REASON_REAUTHENTICATION || |
| reason == signin_metrics::Reason::REASON_UNLOCK) { |
| signin_manager->MergeSigninCredentialIntoCookieJar(); |
| } |
| LogSigninReason(reason); |
| } else { |
| ProfileSyncService* sync_service = |
| ProfileSyncServiceFactory::GetForProfile(profile_); |
| SigninErrorController* error_controller = |
| SigninErrorControllerFactory::GetForProfile(profile_); |
| |
| OneClickSigninSyncStarter::StartSyncMode start_mode = |
| OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST; |
| if (access_point == signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS || |
| choose_what_to_sync_) { |
| bool show_settings_without_configure = |
| error_controller->HasError() && sync_service && |
| sync_service->IsFirstSetupComplete(); |
| start_mode = show_settings_without_configure ? |
| OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE : |
| OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST; |
| } |
| |
| OneClickSigninSyncStarter::ConfirmationRequired confirmation_required = |
| confirm_untrusted_signin_ ? |
| OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN : |
| OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN; |
| |
| bool start_signin = !HandleCrossAccountError( |
| result.refresh_token, confirmation_required, start_mode); |
| if (start_signin) { |
| CreateSyncStarter(browser, contents, current_url_, |
| signin::GetNextPageURLForPromoURL(current_url_), |
| result.refresh_token, start_mode, |
| confirmation_required); |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| } |
| } |
| } |
| |
| void InlineSigninHelper::CreateSyncStarter( |
| Browser* browser, |
| content::WebContents* contents, |
| const GURL& current_url, |
| const GURL& continue_url, |
| const std::string& refresh_token, |
| OneClickSigninSyncStarter::StartSyncMode start_mode, |
| OneClickSigninSyncStarter::ConfirmationRequired confirmation_required) { |
| // OneClickSigninSyncStarter will delete itself once the job is done. |
| new OneClickSigninSyncStarter( |
| profile_, browser, gaia_id_, email_, password_, refresh_token, start_mode, |
| contents, confirmation_required, current_url, continue_url, |
| base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_)); |
| } |
| |
| bool InlineSigninHelper::HandleCrossAccountError( |
| const std::string& refresh_token, |
| OneClickSigninSyncStarter::ConfirmationRequired confirmation_required, |
| OneClickSigninSyncStarter::StartSyncMode start_mode) { |
| std::string last_email = |
| profile_->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername); |
| |
| InvestigatorDependencyProvider provider(profile_); |
| InvestigatedScenario scenario = |
| SigninInvestigator(email_, gaia_id_, &provider).Investigate(); |
| |
| // TODO(skym): Warn for high risk upgrade scenario, crbug.com/572754. |
| if (scenario != InvestigatedScenario::DIFFERENT_ACCOUNT) { |
| return false; |
| } |
| |
| Browser* browser = chrome::FindLastActiveWithProfile(profile_); |
| content::WebContents* web_contents = |
| browser->tab_strip_model()->GetActiveWebContents(); |
| |
| ConfirmEmailDialogDelegate::AskForConfirmation( |
| web_contents, |
| last_email, |
| email_, |
| base::Bind(&InlineSigninHelper::ConfirmEmailAction, |
| base::Unretained(this), |
| web_contents, |
| refresh_token, |
| confirmation_required, |
| start_mode)); |
| return true; |
| } |
| |
| void InlineSigninHelper::ConfirmEmailAction( |
| content::WebContents* web_contents, |
| const std::string& refresh_token, |
| OneClickSigninSyncStarter::ConfirmationRequired confirmation_required, |
| OneClickSigninSyncStarter::StartSyncMode start_mode, |
| InlineSigninHelper::Action action) { |
| Browser* browser = chrome::FindLastActiveWithProfile(profile_); |
| switch (action) { |
| case InlineSigninHelper::CREATE_NEW_USER: |
| content::RecordAction( |
| base::UserMetricsAction("Signin_ImportDataPrompt_DontImport")); |
| if (handler_) { |
| handler_->SyncStarterCallback( |
| OneClickSigninSyncStarter::SYNC_SETUP_FAILURE); |
| } |
| chrome::ShowSettingsSubPage(browser, |
| std::string(chrome::kCreateProfileSubPage)); |
| break; |
| case InlineSigninHelper::START_SYNC: |
| content::RecordAction( |
| base::UserMetricsAction("Signin_ImportDataPrompt_ImportData")); |
| CreateSyncStarter(browser, web_contents, current_url_, GURL(), |
| refresh_token, start_mode, confirmation_required); |
| break; |
| case InlineSigninHelper::CLOSE: |
| content::RecordAction( |
| base::UserMetricsAction("Signin_ImportDataPrompt_Cancel")); |
| if (handler_) { |
| handler_->SyncStarterCallback( |
| OneClickSigninSyncStarter::SYNC_SETUP_FAILURE); |
| } |
| break; |
| default: |
| DCHECK(false) << "Invalid action"; |
| } |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| } |
| |
| void InlineSigninHelper::OnClientOAuthFailure( |
| const GoogleServiceAuthError& error) { |
| if (handler_) |
| handler_->HandleLoginError(error.ToString()); |
| |
| AboutSigninInternals* about_signin_internals = |
| AboutSigninInternalsFactory::GetForProfile(profile_); |
| about_signin_internals->OnRefreshTokenReceived("Failure"); |
| |
| base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); |
| } |
| |
| InlineLoginHandlerImpl::InlineLoginHandlerImpl() |
| : confirm_untrusted_signin_(false), |
| weak_factory_(this) { |
| } |
| |
| InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {} |
| |
| // This method is not called with webview sign in enabled. |
| void InlineLoginHandlerImpl::DidCommitProvisionalLoadForFrame( |
| content::RenderFrameHost* render_frame_host, |
| const GURL& url, |
| ui::PageTransition transition_type) { |
| if (!web_contents()) |
| return; |
| |
| // Returns early if this is not a gaia webview navigation. |
| content::RenderFrameHost* gaia_frame = |
| signin::GetAuthFrame(web_contents(), "signin-frame"); |
| if (render_frame_host != gaia_frame) |
| return; |
| |
| // Loading any untrusted (e.g., HTTP) URLs in the privileged sign-in process |
| // will require confirmation before the sign in takes effect. |
| const GURL kGaiaExtOrigin( |
| GaiaUrls::GetInstance()->signin_completed_continue_url().GetOrigin()); |
| if (!url.is_empty()) { |
| GURL origin(url.GetOrigin()); |
| if (url.spec() != url::kAboutBlankURL && |
| origin != kGaiaExtOrigin && |
| !gaia::IsGaiaSignonRealm(origin)) { |
| confirm_untrusted_signin_ = true; |
| } |
| } |
| } |
| |
| // static |
| bool InlineLoginHandlerImpl::CanOffer(Profile* profile, |
| CanOfferFor can_offer_for, |
| const std::string& gaia_id, |
| const std::string& email, |
| std::string* error_message) { |
| if (error_message) |
| error_message->clear(); |
| |
| if (!profile) |
| return false; |
| |
| SigninManager* manager = SigninManagerFactory::GetForProfile(profile); |
| if (manager && !manager->IsSigninAllowed()) |
| return false; |
| |
| if (!ChromeSigninClient::ProfileAllowsSigninCookies(profile)) |
| return false; |
| |
| if (!email.empty()) { |
| if (!manager) |
| return false; |
| |
| // Make sure this username is not prohibited by policy. |
| if (!manager->IsAllowedUsername(email)) { |
| if (error_message) { |
| error_message->assign( |
| l10n_util::GetStringUTF8(IDS_SYNC_LOGIN_NAME_PROHIBITED)); |
| } |
| return false; |
| } |
| |
| if (can_offer_for == CAN_OFFER_FOR_SECONDARY_ACCOUNT) |
| return true; |
| |
| // If the signin manager already has an authenticated name, then this is a |
| // re-auth scenario. Make sure the email just signed in corresponds to |
| // the one sign in manager expects. |
| std::string current_email = manager->GetAuthenticatedAccountInfo().email; |
| const bool same_email = gaia::AreEmailsSame(current_email, email); |
| if (!current_email.empty() && !same_email) { |
| UMA_HISTOGRAM_ENUMERATION("Signin.Reauth", |
| signin_metrics::HISTOGRAM_ACCOUNT_MISSMATCH, |
| signin_metrics::HISTOGRAM_MAX); |
| if (error_message) { |
| error_message->assign( |
| l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL, |
| base::UTF8ToUTF16(current_email))); |
| } |
| return false; |
| } |
| |
| // If some profile, not just the current one, is already connected to this |
| // account, don't show the infobar. |
| if (g_browser_process && !same_email) { |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| if (profile_manager) { |
| std::vector<ProfileAttributesEntry*> entries = |
| profile_manager->GetProfileAttributesStorage(). |
| GetAllProfilesAttributes(); |
| |
| for (const ProfileAttributesEntry* entry : entries) { |
| // For backward compatibility, need to check also the username of the |
| // profile, since the GAIA ID may not have been set yet in the |
| // ProfileAttributesStorage. It will be set once the profile |
| // is opened. |
| std::string profile_gaia_id = entry->GetGAIAId(); |
| std::string profile_email = base::UTF16ToUTF8(entry->GetUserName()); |
| if (gaia_id == profile_gaia_id || |
| gaia::AreEmailsSame(email, profile_email)) { |
| if (error_message) { |
| error_message->assign( |
| l10n_util::GetStringUTF8(IDS_SYNC_USER_NAME_IN_USE_ERROR)); |
| } |
| return false; |
| } |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue& params) { |
| params.SetString("service", "chromiumsync"); |
| |
| // If this was called from the user manager to reauthenticate the profile, |
| // make sure the webui is aware. |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| if (IsSystemProfile(profile)) |
| params.SetBoolean("dontResizeNonEmbeddedPages", true); |
| |
| content::WebContents* contents = web_ui()->GetWebContents(); |
| const GURL& current_url = contents->GetURL(); |
| signin_metrics::Reason reason = |
| signin::GetSigninReasonForPromoURL(current_url); |
| |
| std::string is_constrained; |
| net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained); |
| |
| // Use new embedded flow if in constrained window. |
| if (is_constrained == "1") { |
| const bool is_new_gaia_flow = switches::UsePasswordSeparatedSigninFlow(); |
| const GURL& url = is_new_gaia_flow |
| ? GaiaUrls::GetInstance()->embedded_signin_url() |
| : GaiaUrls::GetInstance()->password_combined_embedded_signin_url(); |
| params.SetBoolean("isNewGaiaFlow", is_new_gaia_flow); |
| params.SetString("clientId", |
| GaiaUrls::GetInstance()->oauth2_chrome_client_id()); |
| params.SetString("gaiaPath", url.path().substr(1)); |
| |
| std::string flow; |
| switch (reason) { |
| case signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT: |
| flow = "addaccount"; |
| break; |
| case signin_metrics::Reason::REASON_REAUTHENTICATION: |
| case signin_metrics::Reason::REASON_UNLOCK: |
| flow = "reauth"; |
| break; |
| default: |
| flow = "signin"; |
| break; |
| } |
| params.SetString("flow", flow); |
| } |
| |
| content::WebContentsObserver::Observe(contents); |
| LogHistogramValue(signin_metrics::HISTOGRAM_SHOWN); |
| } |
| |
| void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue* args) { |
| content::WebContents* contents = web_ui()->GetWebContents(); |
| const GURL& current_url = contents->GetURL(); |
| |
| const base::DictionaryValue* dict = NULL; |
| args->GetDictionary(0, &dict); |
| |
| bool skip_for_now = false; |
| dict->GetBoolean("skipForNow", &skip_for_now); |
| if (skip_for_now) { |
| signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui())); |
| SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE); |
| return; |
| } |
| |
| // This value exists only for webview sign in. |
| bool trusted = false; |
| if (dict->GetBoolean("trusted", &trusted)) |
| confirm_untrusted_signin_ = !trusted; |
| |
| base::string16 email_string16; |
| dict->GetString("email", &email_string16); |
| DCHECK(!email_string16.empty()); |
| std::string email(base::UTF16ToASCII(email_string16)); |
| |
| base::string16 password_string16; |
| dict->GetString("password", &password_string16); |
| std::string password(base::UTF16ToASCII(password_string16)); |
| |
| base::string16 gaia_id_string16; |
| dict->GetString("gaiaId", &gaia_id_string16); |
| DCHECK(!gaia_id_string16.empty()); |
| std::string gaia_id = base::UTF16ToASCII(gaia_id_string16); |
| |
| std::string is_constrained; |
| net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained); |
| const bool is_password_separated_signin_flow = is_constrained == "1" && |
| switches::UsePasswordSeparatedSigninFlow(); |
| |
| base::string16 session_index_string16; |
| dict->GetString("sessionIndex", &session_index_string16); |
| std::string session_index = base::UTF16ToASCII(session_index_string16); |
| DCHECK(is_password_separated_signin_flow || !session_index.empty()); |
| |
| base::string16 auth_code_string16; |
| dict->GetString("authCode", &auth_code_string16); |
| std::string auth_code = base::UTF16ToASCII(auth_code_string16); |
| DCHECK(!is_password_separated_signin_flow || !auth_code.empty()); |
| |
| bool choose_what_to_sync = false; |
| dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync); |
| |
| content::StoragePartition* partition = |
| content::BrowserContext::GetStoragePartitionForSite( |
| contents->GetBrowserContext(), signin::GetSigninPartitionURL()); |
| |
| // If this was called from the user manager to reauthenticate the profile, |
| // the current profile is the system profile. In this case, use the email to |
| // find the right profile to reauthenticate. Otherwise the profile can be |
| // taken from web_ui(). |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| if (IsSystemProfile(profile)) { |
| // Switch to the profile and finish the login. Don't pass a handler pointer |
| // since it will be destroyed before the callback runs. |
| ProfileManager* manager = g_browser_process->profile_manager(); |
| base::FilePath path = profiles::GetPathOfProfileWithEmail(manager, email); |
| if (!path.empty()) { |
| FinishCompleteLoginParams params(nullptr, partition, current_url, path, |
| confirm_untrusted_signin_, email, |
| gaia_id, password, session_index, |
| auth_code, choose_what_to_sync); |
| ProfileManager::CreateCallback callback = base::Bind( |
| &InlineLoginHandlerImpl::FinishCompleteLogin, params); |
| profiles::SwitchToProfile(path, true, callback, |
| ProfileMetrics::SWITCH_PROFILE_UNLOCK); |
| } |
| } else { |
| FinishCompleteLogin( |
| FinishCompleteLoginParams(this, partition, current_url, |
| base::FilePath(), confirm_untrusted_signin_, |
| email, gaia_id, password, session_index, |
| auth_code, choose_what_to_sync), |
| profile, |
| Profile::CREATE_STATUS_CREATED); |
| } |
| } |
| |
| InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams( |
| InlineLoginHandlerImpl* handler, |
| content::StoragePartition* partition, |
| const GURL& url, |
| const base::FilePath& profile_path, |
| bool confirm_untrusted_signin, |
| const std::string& email, |
| const std::string& gaia_id, |
| const std::string& password, |
| const std::string& session_index, |
| const std::string& auth_code, |
| bool choose_what_to_sync) |
| : handler(handler), |
| partition(partition), |
| url(url), |
| profile_path(profile_path), |
| confirm_untrusted_signin(confirm_untrusted_signin), |
| email(email), |
| gaia_id(gaia_id), |
| password(password), |
| session_index(session_index), |
| auth_code(auth_code), |
| choose_what_to_sync(choose_what_to_sync) {} |
| |
| InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams( |
| const FinishCompleteLoginParams& other) = default; |
| |
| InlineLoginHandlerImpl:: |
| FinishCompleteLoginParams::~FinishCompleteLoginParams() {} |
| |
| // static |
| void InlineLoginHandlerImpl::FinishCompleteLogin( |
| const FinishCompleteLoginParams& params, |
| Profile* profile, |
| Profile::CreateStatus status) { |
| // When doing a SAML sign in, this email check may result in a false |
| // positive. This happens when the user types one email address in the |
| // gaia sign in page, but signs in to a different account in the SAML sign in |
| // page. |
| std::string default_email; |
| std::string validate_email; |
| if (net::GetValueForKeyInQuery(params.url, "email", &default_email) && |
| net::GetValueForKeyInQuery(params.url, "validateEmail", |
| &validate_email) && |
| validate_email == "1") { |
| if (!gaia::AreEmailsSame(params.email, default_email)) { |
| if (params.handler) { |
| params.handler->HandleLoginError( |
| l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL, |
| base::UTF8ToUTF16(default_email))); |
| } |
| return; |
| } |
| } |
| |
| signin_metrics::AccessPoint access_point = |
| signin::GetAccessPointForPromoURL(params.url); |
| signin_metrics::Reason reason = |
| signin::GetSigninReasonForPromoURL(params.url); |
| LogHistogramValue(signin_metrics::HISTOGRAM_ACCEPTED); |
| bool switch_to_advanced = |
| params.choose_what_to_sync && |
| (access_point != signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS); |
| LogHistogramValue( |
| switch_to_advanced ? signin_metrics::HISTOGRAM_WITH_ADVANCED : |
| signin_metrics::HISTOGRAM_WITH_DEFAULTS); |
| |
| CanOfferFor can_offer_for = CAN_OFFER_FOR_ALL; |
| switch (reason) { |
| case signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT: |
| can_offer_for = CAN_OFFER_FOR_SECONDARY_ACCOUNT; |
| break; |
| case signin_metrics::Reason::REASON_REAUTHENTICATION: |
| case signin_metrics::Reason::REASON_UNLOCK: { |
| std::string primary_username = |
| SigninManagerFactory::GetForProfile(profile) |
| ->GetAuthenticatedAccountInfo() |
| .email; |
| if (!gaia::AreEmailsSame(default_email, primary_username)) |
| can_offer_for = CAN_OFFER_FOR_SECONDARY_ACCOUNT; |
| break; |
| } |
| default: |
| // No need to change |can_offer_for|. |
| break; |
| } |
| |
| std::string error_msg; |
| bool can_offer = CanOffer(profile, can_offer_for, params.gaia_id, |
| params.email, &error_msg); |
| if (!can_offer) { |
| if (params.handler) |
| params.handler->HandleLoginError(error_msg); |
| return; |
| } |
| |
| AboutSigninInternals* about_signin_internals = |
| AboutSigninInternalsFactory::GetForProfile(profile); |
| about_signin_internals->OnAuthenticationResultReceived("Successful"); |
| |
| SigninClient* signin_client = |
| ChromeSigninClientFactory::GetForProfile(profile); |
| std::string signin_scoped_device_id = |
| signin_client->GetSigninScopedDeviceId(); |
| base::WeakPtr<InlineLoginHandlerImpl> handler_weak_ptr; |
| if (params.handler) |
| handler_weak_ptr = params.handler->GetWeakPtr(); |
| |
| // InlineSigninHelper will delete itself. |
| new InlineSigninHelper(handler_weak_ptr, |
| params.partition->GetURLRequestContext(), profile, |
| params.url, |
| params.email, params.gaia_id, params.password, |
| params.session_index, params.auth_code, |
| signin_scoped_device_id, |
| params.choose_what_to_sync, |
| params.confirm_untrusted_signin); |
| |
| // If opened from user manager to reauthenticate, make sure the user manager |
| // is closed and that the profile is marked as unlocked. |
| if (!params.profile_path.empty()) { |
| UserManager::Hide(); |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| if (profile_manager) { |
| ProfileAttributesEntry* entry; |
| if (profile_manager->GetProfileAttributesStorage() |
| .GetProfileAttributesWithPath(params.profile_path, &entry)) { |
| entry->SetIsSigninRequired(false); |
| } |
| } |
| } |
| |
| if (params.handler) |
| params.handler->web_ui()->CallJavascriptFunctionUnsafe( |
| "inline.login.closeDialog"); |
| } |
| |
| void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg) { |
| SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE); |
| Browser* browser = GetDesktopBrowser(); |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| |
| CloseModalSigninIfNeeded(this); |
| if (browser && !error_msg.empty()) { |
| LoginUIServiceFactory::GetForProfile(profile)-> |
| DisplayLoginResult(browser, base::UTF8ToUTF16(error_msg)); |
| } |
| } |
| |
| Browser* InlineLoginHandlerImpl::GetDesktopBrowser() { |
| Browser* browser = chrome::FindBrowserWithWebContents( |
| web_ui()->GetWebContents()); |
| if (!browser) |
| browser = chrome::FindLastActiveWithProfile(Profile::FromWebUI(web_ui())); |
| return browser; |
| } |
| |
| void InlineLoginHandlerImpl::SyncStarterCallback( |
| OneClickSigninSyncStarter::SyncSetupResult result) { |
| content::WebContents* contents = web_ui()->GetWebContents(); |
| |
| if (contents->GetController().GetPendingEntry()) { |
| // Do nothing if a navigation is pending, since this call can be triggered |
| // from DidStartLoading. This avoids deleting the pending entry while we are |
| // still navigating to it. See crbug/346632. |
| return; |
| } |
| |
| const GURL& current_url = contents->GetLastCommittedURL(); |
| signin_metrics::AccessPoint access_point = |
| signin::GetAccessPointForPromoURL(current_url); |
| bool auto_close = signin::IsAutoCloseEnabledInURL(current_url); |
| |
| if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE) { |
| RedirectToNtpOrAppsPage(contents, access_point); |
| } else if (auto_close) { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::Bind(&InlineLoginHandlerImpl::CloseTab, |
| weak_factory_.GetWeakPtr(), |
| signin::ShouldShowAccountManagement(current_url))); |
| } else { |
| RedirectToNtpOrAppsPageIfNecessary(contents, access_point); |
| } |
| } |
| |
| void InlineLoginHandlerImpl::CloseTab(bool show_account_management) { |
| content::WebContents* tab = web_ui()->GetWebContents(); |
| Browser* browser = chrome::FindBrowserWithWebContents(tab); |
| if (browser) { |
| TabStripModel* tab_strip_model = browser->tab_strip_model(); |
| if (tab_strip_model) { |
| int index = tab_strip_model->GetIndexOfWebContents(tab); |
| if (index != TabStripModel::kNoTab) { |
| tab_strip_model->ExecuteContextMenuCommand( |
| index, TabStripModel::CommandCloseTab); |
| } |
| } |
| |
| if (show_account_management) { |
| browser->window()->ShowAvatarBubbleFromAvatarButton( |
| BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT, |
| signin::ManageAccountsParams(), |
| signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN); |
| } |
| } |
| } |