Home | History | Annotate | Download | only in signin
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/signin/signin_promo.h"
      6 
      7 #include "base/prefs/pref_service.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/strings/stringprintf.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/browser_process.h"
     13 #include "chrome/browser/first_run/first_run.h"
     14 #include "chrome/browser/google/google_brand.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/profiles/profile_info_cache.h"
     17 #include "chrome/browser/profiles/profile_manager.h"
     18 #include "chrome/browser/signin/signin_manager_factory.h"
     19 #include "chrome/browser/ui/webui/options/core_options_handler.h"
     20 #include "chrome/browser/ui/webui/theme_source.h"
     21 #include "chrome/common/net/url_util.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "chrome/common/url_constants.h"
     24 #include "components/google/core/browser/google_util.h"
     25 #include "components/pref_registry/pref_registry_syncable.h"
     26 #include "components/signin/core/browser/signin_manager.h"
     27 #include "components/signin/core/common/profile_management_switches.h"
     28 #include "content/public/browser/url_data_source.h"
     29 #include "content/public/browser/web_contents.h"
     30 #include "content/public/browser/web_ui.h"
     31 #include "content/public/browser/web_ui_data_source.h"
     32 #include "google_apis/gaia/gaia_urls.h"
     33 #include "grit/browser_resources.h"
     34 #include "grit/generated_resources.h"
     35 #include "grit/theme_resources.h"
     36 #include "net/base/escape.h"
     37 #include "net/base/network_change_notifier.h"
     38 #include "net/base/url_util.h"
     39 #include "ui/base/l10n/l10n_util.h"
     40 #include "url/gurl.h"
     41 
     42 using content::WebContents;
     43 using net::GetValueForKeyInQuery;
     44 
     45 namespace {
     46 
     47 // Gaia cannot support about:blank as a continue URL, so using a hosted blank
     48 // page instead.
     49 const char kSignInLandingUrlPrefix[] =
     50     "https://www.google.com/intl/%s/chrome/blank.html";
     51 
     52 // The maximum number of times we want to show the sign in promo at startup.
     53 const int kSignInPromoShowAtStartupMaximum = 10;
     54 
     55 // Forces the web based signin flow when set.
     56 bool g_force_web_based_signin_flow = false;
     57 
     58 // Checks we want to show the sign in promo for the given brand.
     59 bool AllowPromoAtStartupForCurrentBrand() {
     60   std::string brand;
     61   google_brand::GetBrand(&brand);
     62 
     63   if (brand.empty())
     64     return true;
     65 
     66   if (google_brand::IsInternetCafeBrandCode(brand))
     67     return false;
     68 
     69   // Enable for both organic and distribution.
     70   return true;
     71 }
     72 
     73 // Returns true if a user has seen the sign in promo at startup previously.
     74 bool HasShownPromoAtStartup(Profile* profile) {
     75   return profile->GetPrefs()->HasPrefPath(prefs::kSignInPromoStartupCount);
     76 }
     77 
     78 // Returns true if the user has previously skipped the sign in promo.
     79 bool HasUserSkippedPromo(Profile* profile) {
     80   return profile->GetPrefs()->GetBoolean(prefs::kSignInPromoUserSkipped);
     81 }
     82 
     83 }  // namespace
     84 
     85 namespace signin {
     86 
     87 bool ShouldShowPromo(Profile* profile) {
     88 #if defined(OS_CHROMEOS)
     89   // There's no need to show the sign in promo on cros since cros users are
     90   // already logged in.
     91   return false;
     92 #else
     93 
     94   // Don't bother if we don't have any kind of network connection.
     95   if (net::NetworkChangeNotifier::IsOffline())
     96     return false;
     97 
     98   // Don't show for supervised profiles.
     99   if (profile->IsSupervised())
    100     return false;
    101 
    102   // Display the signin promo if the user is not signed in.
    103   SigninManager* signin = SigninManagerFactory::GetForProfile(
    104       profile->GetOriginalProfile());
    105   return !signin->AuthInProgress() && signin->IsSigninAllowed() &&
    106       signin->GetAuthenticatedUsername().empty();
    107 #endif
    108 }
    109 
    110 bool ShouldShowPromoAtStartup(Profile* profile, bool is_new_profile) {
    111   DCHECK(profile);
    112 
    113   // Don't show if the profile is an incognito.
    114   if (profile->IsOffTheRecord())
    115     return false;
    116 
    117   if (!ShouldShowPromo(profile))
    118     return false;
    119 
    120   if (!is_new_profile) {
    121     if (!HasShownPromoAtStartup(profile))
    122       return false;
    123   }
    124 
    125   if (HasUserSkippedPromo(profile))
    126     return false;
    127 
    128   // For Chinese users skip the sign in promo.
    129   if (g_browser_process->GetApplicationLocale() == "zh-CN")
    130     return false;
    131 
    132   PrefService* prefs = profile->GetPrefs();
    133   int show_count = prefs->GetInteger(prefs::kSignInPromoStartupCount);
    134   if (show_count >= kSignInPromoShowAtStartupMaximum)
    135     return false;
    136 
    137   // This pref can be set in the master preferences file to allow or disallow
    138   // showing the sign in promo at startup.
    139   if (prefs->HasPrefPath(prefs::kSignInPromoShowOnFirstRunAllowed))
    140     return prefs->GetBoolean(prefs::kSignInPromoShowOnFirstRunAllowed);
    141 
    142   // For now don't show the promo for some brands.
    143   if (!AllowPromoAtStartupForCurrentBrand())
    144     return false;
    145 
    146   // Default to show the promo for Google Chrome builds.
    147 #if defined(GOOGLE_CHROME_BUILD)
    148   return true;
    149 #else
    150   return false;
    151 #endif
    152 }
    153 
    154 void DidShowPromoAtStartup(Profile* profile) {
    155   int show_count = profile->GetPrefs()->GetInteger(
    156       prefs::kSignInPromoStartupCount);
    157   show_count++;
    158   profile->GetPrefs()->SetInteger(prefs::kSignInPromoStartupCount, show_count);
    159 }
    160 
    161 void SetUserSkippedPromo(Profile* profile) {
    162   profile->GetPrefs()->SetBoolean(prefs::kSignInPromoUserSkipped, true);
    163 }
    164 
    165 GURL GetLandingURL(const char* option, int value) {
    166   const std::string& locale = g_browser_process->GetApplicationLocale();
    167   std::string url = base::StringPrintf(kSignInLandingUrlPrefix, locale.c_str());
    168   base::StringAppendF(&url, "?%s=%d", option, value);
    169   return GURL(url);
    170 }
    171 
    172 GURL GetPromoURL(Source source, bool auto_close) {
    173   return GetPromoURL(source, auto_close, false /* is_constrained */);
    174 }
    175 
    176 GURL GetPromoURL(Source source, bool auto_close, bool is_constrained) {
    177   return GetPromoURLWithContinueURL(source, auto_close, is_constrained, GURL());
    178 }
    179 
    180 GURL GetPromoURLWithContinueURL(Source source,
    181                                 bool auto_close,
    182                                 bool is_constrained,
    183                                 GURL continue_url) {
    184   DCHECK_NE(SOURCE_UNKNOWN, source);
    185 
    186   if (!switches::IsEnableWebBasedSignin()) {
    187     std::string url(chrome::kChromeUIChromeSigninURL);
    188     base::StringAppendF(&url, "?%s=%d", kSignInPromoQueryKeySource, source);
    189     if (auto_close)
    190       base::StringAppendF(&url, "&%s=1", kSignInPromoQueryKeyAutoClose);
    191     if (is_constrained)
    192       base::StringAppendF(&url, "&%s=1", kSignInPromoQueryKeyConstrained);
    193     if (!continue_url.is_empty()) {
    194       DCHECK(continue_url.is_valid());
    195       std::string escaped_continue_url =
    196           net::EscapeQueryParamValue(continue_url.spec(), false);
    197       base::StringAppendF(&url,
    198                           "&%s=%s",
    199                           kSignInPromoQueryKeyContinue,
    200                           escaped_continue_url.c_str());
    201     }
    202     return GURL(url);
    203   }
    204 
    205   // Build a Gaia-based URL that can be used to sign the user into chrome.
    206   // There are required request parameters:
    207   //
    208   //  - tell Gaia which service the user is signing into.  In this case,
    209   //    a chrome sign in uses the service "chromiumsync"
    210   //  - provide a continue URL.  This is the URL that Gaia will redirect to
    211   //    once the sign is complete.
    212   //
    213   // The continue URL includes a source parameter that can be extracted using
    214   // the function GetSourceForSignInPromoURL() below.  This is used to know
    215   // which of the chrome sign in access points was used to sign the user in.
    216   // It is also parsed for the |auto_close| flag, which indicates that the tab
    217   // must be closed after sync setup is successful.
    218   // See OneClickSigninHelper for details.
    219   std::string query_string = "?service=chromiumsync&sarp=1";
    220 
    221   DCHECK(continue_url.is_empty());
    222   std::string continue_url_str = GetLandingURL(kSignInPromoQueryKeySource,
    223                                                static_cast<int>(source)).spec();
    224   if (auto_close) {
    225     base::StringAppendF(
    226         &continue_url_str, "&%s=1", kSignInPromoQueryKeyAutoClose);
    227   }
    228 
    229   base::StringAppendF(
    230       &query_string,
    231       "&%s=%s",
    232       kSignInPromoQueryKeyContinue,
    233       net::EscapeQueryParamValue(continue_url_str, false).c_str());
    234 
    235   return GaiaUrls::GetInstance()->service_login_url().Resolve(query_string);
    236 }
    237 
    238 GURL GetReauthURL(Profile* profile, const std::string& account_id) {
    239   if (switches::IsEnableWebBasedSignin()) {
    240     return net::AppendQueryParameter(
    241         signin::GetPromoURL(signin::SOURCE_SETTINGS, true),
    242         "Email",
    243         account_id);
    244   }
    245 
    246   signin::Source source = switches::IsNewProfileManagement() ?
    247       signin::SOURCE_REAUTH : signin::SOURCE_SETTINGS;
    248 
    249   GURL url = signin::GetPromoURL(
    250       source, true /* auto_close */,
    251       switches::IsNewProfileManagement() /* is_constrained */);
    252   url = net::AppendQueryParameter(url, "email", account_id);
    253   url = net::AppendQueryParameter(url, "validateEmail", "1");
    254   return net::AppendQueryParameter(url, "readOnlyEmail", "1");
    255 }
    256 
    257 GURL GetNextPageURLForPromoURL(const GURL& url) {
    258   std::string value;
    259   if (GetValueForKeyInQuery(url, kSignInPromoQueryKeyContinue, &value)) {
    260     GURL continue_url = GURL(value);
    261     if (continue_url.is_valid())
    262       return continue_url;
    263   }
    264 
    265   return GURL();
    266 }
    267 
    268 Source GetSourceForPromoURL(const GURL& url) {
    269   std::string value;
    270   if (GetValueForKeyInQuery(url, kSignInPromoQueryKeySource, &value)) {
    271     int source = 0;
    272     if (base::StringToInt(value, &source) && source >= SOURCE_START_PAGE &&
    273         source < SOURCE_UNKNOWN) {
    274       return static_cast<Source>(source);
    275     }
    276   }
    277   return SOURCE_UNKNOWN;
    278 }
    279 
    280 bool IsAutoCloseEnabledInURL(const GURL& url) {
    281   std::string value;
    282   if (GetValueForKeyInQuery(url, kSignInPromoQueryKeyAutoClose, &value)) {
    283     int enabled = 0;
    284     if (base::StringToInt(value, &enabled) && enabled == 1)
    285       return true;
    286   }
    287   return false;
    288 }
    289 
    290 bool ShouldShowAccountManagement(const GURL& url) {
    291   std::string value;
    292   if (GetValueForKeyInQuery(
    293           url, kSignInPromoQueryKeyShowAccountManagement, &value)) {
    294     int enabled = 0;
    295     if (base::StringToInt(value, &enabled) && enabled == 1)
    296       return true;
    297   }
    298   return false;
    299 }
    300 
    301 bool IsContinueUrlForWebBasedSigninFlow(const GURL& url) {
    302   GURL::Replacements replacements;
    303   replacements.ClearQuery();
    304   const std::string& locale = g_browser_process->GetApplicationLocale();
    305   GURL continue_url =
    306       GURL(base::StringPrintf(kSignInLandingUrlPrefix, locale.c_str()));
    307   return (
    308       google_util::IsGoogleDomainUrl(
    309           url,
    310           google_util::ALLOW_SUBDOMAIN,
    311           google_util::DISALLOW_NON_STANDARD_PORTS) &&
    312       url.ReplaceComponents(replacements).path() ==
    313         continue_url.ReplaceComponents(replacements).path());
    314 }
    315 
    316 void ForceWebBasedSigninFlowForTesting(bool force) {
    317   g_force_web_based_signin_flow = force;
    318 }
    319 
    320 void RegisterProfilePrefs(
    321     user_prefs::PrefRegistrySyncable* registry) {
    322   registry->RegisterIntegerPref(
    323       prefs::kSignInPromoStartupCount,
    324       0,
    325       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    326   registry->RegisterBooleanPref(
    327       prefs::kSignInPromoUserSkipped,
    328       false,
    329       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    330   registry->RegisterBooleanPref(
    331       prefs::kSignInPromoShowOnFirstRunAllowed,
    332       true,
    333       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    334   registry->RegisterBooleanPref(
    335       prefs::kSignInPromoShowNTPBubble,
    336       false,
    337       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    338 }
    339 
    340 }  // namespace signin
    341