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