1// Copyright 2014 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/chrome_signin_client.h"
6
7#include "base/command_line.h"
8#include "base/guid.h"
9#include "base/prefs/pref_service.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/content_settings/cookie_settings.h"
13#include "chrome/browser/net/chrome_cookie_notification_details.h"
14#include "chrome/browser/signin/local_auth.h"
15#include "chrome/browser/webdata/web_data_service_factory.h"
16#include "chrome/common/chrome_version_info.h"
17#include "components/metrics/metrics_service.h"
18#include "components/signin/core/common/profile_management_switches.h"
19#include "components/signin/core/common/signin_pref_names.h"
20#include "components/signin/core/common/signin_switches.h"
21#include "content/public/browser/notification_details.h"
22#include "content/public/browser/notification_source.h"
23#include "content/public/browser/render_process_host.h"
24#include "content/public/common/child_process_host.h"
25#include "url/gurl.h"
26
27#if defined(ENABLE_MANAGED_USERS)
28#include "chrome/browser/supervised_user/supervised_user_constants.h"
29#endif
30
31#if defined(OS_CHROMEOS)
32#include "components/user_manager/user_manager.h"
33#endif
34
35#if !defined(OS_ANDROID)
36#include "chrome/browser/first_run/first_run.h"
37#endif
38
39using content::ChildProcessHost;
40using content::RenderProcessHost;
41
42namespace {
43
44const char kGoogleAccountsUrl[] = "https://accounts.google.com";
45
46}  // namespace
47
48ChromeSigninClient::ChromeSigninClient(Profile* profile)
49    : profile_(profile), signin_host_id_(ChildProcessHost::kInvalidUniqueID) {
50  callbacks_.set_removal_callback(
51    base::Bind(&ChromeSigninClient::UnregisterForCookieChangedNotification,
52               base::Unretained(this)));
53}
54
55ChromeSigninClient::~ChromeSigninClient() {
56  UnregisterForCookieChangedNotification();
57
58  std::set<RenderProcessHost*>::iterator i;
59  for (i = signin_hosts_observed_.begin(); i != signin_hosts_observed_.end();
60       ++i) {
61    (*i)->RemoveObserver(this);
62  }
63}
64
65// static
66bool ChromeSigninClient::ProfileAllowsSigninCookies(Profile* profile) {
67  CookieSettings* cookie_settings =
68      CookieSettings::Factory::GetForProfile(profile).get();
69  return SettingsAllowSigninCookies(cookie_settings);
70}
71
72// static
73bool ChromeSigninClient::SettingsAllowSigninCookies(
74    CookieSettings* cookie_settings) {
75  return cookie_settings &&
76         cookie_settings->IsSettingCookieAllowed(GURL(kGoogleAccountsUrl),
77                                                 GURL(kGoogleAccountsUrl));
78}
79
80void ChromeSigninClient::SetSigninProcess(int process_id) {
81  if (process_id == signin_host_id_)
82    return;
83  DLOG_IF(WARNING, signin_host_id_ != ChildProcessHost::kInvalidUniqueID)
84      << "Replacing in-use signin process.";
85  signin_host_id_ = process_id;
86  RenderProcessHost* host = RenderProcessHost::FromID(process_id);
87  DCHECK(host);
88  host->AddObserver(this);
89  signin_hosts_observed_.insert(host);
90}
91
92void ChromeSigninClient::ClearSigninProcess() {
93  signin_host_id_ = ChildProcessHost::kInvalidUniqueID;
94}
95
96bool ChromeSigninClient::IsSigninProcess(int process_id) const {
97  return process_id != ChildProcessHost::kInvalidUniqueID &&
98         process_id == signin_host_id_;
99}
100
101bool ChromeSigninClient::HasSigninProcess() const {
102  return signin_host_id_ != ChildProcessHost::kInvalidUniqueID;
103}
104
105void ChromeSigninClient::RenderProcessHostDestroyed(RenderProcessHost* host) {
106  // It's possible we're listening to a "stale" renderer because it was replaced
107  // with a new process by process-per-site. In either case, stop observing it,
108  // but only reset signin_host_id_ tracking if this was from the current signin
109  // process.
110  signin_hosts_observed_.erase(host);
111  if (signin_host_id_ == host->GetID())
112    signin_host_id_ = ChildProcessHost::kInvalidUniqueID;
113}
114
115PrefService* ChromeSigninClient::GetPrefs() { return profile_->GetPrefs(); }
116
117scoped_refptr<TokenWebData> ChromeSigninClient::GetDatabase() {
118  return WebDataServiceFactory::GetTokenWebDataForProfile(
119      profile_, Profile::EXPLICIT_ACCESS);
120}
121
122bool ChromeSigninClient::CanRevokeCredentials() {
123#if defined(OS_CHROMEOS)
124  // UserManager may not exist in unit_tests.
125  if (user_manager::UserManager::IsInitialized() &&
126      user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) {
127    // Don't allow revoking credentials for Chrome OS supervised users.
128    // See http://crbug.com/332032
129    LOG(ERROR) << "Attempt to revoke supervised user refresh "
130               << "token detected, ignoring.";
131    return false;
132  }
133#else
134  // Don't allow revoking credentials for supervised users.
135  // See http://crbug.com/332032
136  if (profile_->IsSupervised()) {
137    LOG(ERROR) << "Attempt to revoke supervised user refresh "
138               << "token detected, ignoring.";
139    return false;
140  }
141#endif
142  return true;
143}
144
145std::string ChromeSigninClient::GetSigninScopedDeviceId() {
146  if (CommandLine::ForCurrentProcess()->HasSwitch(
147          switches::kDisableSigninScopedDeviceId)) {
148    return std::string();
149  }
150
151  std::string signin_scoped_device_id =
152      GetPrefs()->GetString(prefs::kGoogleServicesSigninScopedDeviceId);
153  if (signin_scoped_device_id.empty()) {
154    // If device_id doesn't exist then generate new and save in prefs.
155    signin_scoped_device_id = base::GenerateGUID();
156    DCHECK(!signin_scoped_device_id.empty());
157    GetPrefs()->SetString(prefs::kGoogleServicesSigninScopedDeviceId,
158                          signin_scoped_device_id);
159  }
160  return signin_scoped_device_id;
161}
162
163void ChromeSigninClient::ClearSigninScopedDeviceId() {
164  GetPrefs()->ClearPref(prefs::kGoogleServicesSigninScopedDeviceId);
165}
166
167net::URLRequestContextGetter* ChromeSigninClient::GetURLRequestContext() {
168  return profile_->GetRequestContext();
169}
170
171bool ChromeSigninClient::ShouldMergeSigninCredentialsIntoCookieJar() {
172  // If inline sign in is enabled, but account consistency is not, the user's
173  // credentials should be merge into the cookie jar.
174  return !switches::IsEnableWebBasedSignin() &&
175         !switches::IsEnableAccountConsistency();
176}
177
178std::string ChromeSigninClient::GetProductVersion() {
179  chrome::VersionInfo chrome_version;
180  if (!chrome_version.is_valid())
181    return "invalid";
182  return chrome_version.CreateVersionString();
183}
184
185bool ChromeSigninClient::IsFirstRun() const {
186#if defined(OS_ANDROID)
187  return false;
188#else
189  return first_run::IsChromeFirstRun();
190#endif
191}
192
193base::Time ChromeSigninClient::GetInstallDate() {
194  return base::Time::FromTimeT(
195      g_browser_process->metrics_service()->GetInstallDate());
196}
197
198scoped_ptr<SigninClient::CookieChangedCallbackList::Subscription>
199ChromeSigninClient::AddCookieChangedCallback(
200    const CookieChangedCallback& callback) {
201  scoped_ptr<SigninClient::CookieChangedCallbackList::Subscription>
202    subscription = callbacks_.Add(callback);
203  RegisterForCookieChangedNotification();
204  return subscription.Pass();
205}
206
207void ChromeSigninClient::GoogleSigninSucceeded(const std::string& account_id,
208                                               const std::string& username,
209                                               const std::string& password) {
210#if !defined(OS_ANDROID) && !defined(OS_IOS) && !defined(OS_CHROMEOS)
211  // Don't store password hash except for users of new profile management.
212  if (switches::IsNewProfileManagement())
213    chrome::SetLocalAuthCredentials(profile_, password);
214#endif
215}
216
217void ChromeSigninClient::Observe(int type,
218                                 const content::NotificationSource& source,
219                                 const content::NotificationDetails& details) {
220  switch (type) {
221    case chrome::NOTIFICATION_COOKIE_CHANGED: {
222      DCHECK(!callbacks_.empty());
223      const net::CanonicalCookie* cookie =
224          content::Details<ChromeCookieDetails>(details).ptr()->cookie;
225      callbacks_.Notify(cookie);
226      break;
227    }
228    default:
229      NOTREACHED();
230      break;
231  }
232}
233
234void ChromeSigninClient::RegisterForCookieChangedNotification() {
235  if (callbacks_.empty())
236    return;
237  content::Source<Profile> source(profile_);
238  if (!registrar_.IsRegistered(
239      this, chrome::NOTIFICATION_COOKIE_CHANGED, source))
240  registrar_.Add(this, chrome::NOTIFICATION_COOKIE_CHANGED, source);
241}
242
243void ChromeSigninClient::UnregisterForCookieChangedNotification() {
244  if (!callbacks_.empty())
245    return;
246  // Note that it's allowed to call this method multiple times without an
247  // intervening call to |RegisterForCookieChangedNotification()|.
248  content::Source<Profile> source(profile_);
249  if (!registrar_.IsRegistered(
250      this, chrome::NOTIFICATION_COOKIE_CHANGED, source))
251    return;
252  registrar_.Remove(this, chrome::NOTIFICATION_COOKIE_CHANGED, source);
253}
254