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/extensions/api/identity/account_tracker.h"
6
7 #include "base/logging.h"
8 #include "base/stl_util.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/extension_system.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/signin/profile_oauth2_token_service.h"
14 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
15 #include "chrome/browser/signin/signin_manager_base.h"
16 #include "content/public/browser/notification_details.h"
17
18 namespace extensions {
19
AccountTracker(Profile * profile)20 AccountTracker::AccountTracker(Profile* profile) : profile_(profile) {
21 registrar_.Add(this,
22 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
23 content::Source<Profile>(profile_));
24
25 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->AddObserver(this);
26 SigninGlobalError::GetForProfile(profile_)->AddProvider(this);
27 }
28
~AccountTracker()29 AccountTracker::~AccountTracker() {}
30
ReportAuthError(const std::string & account_id,const GoogleServiceAuthError & error)31 void AccountTracker::ReportAuthError(const std::string& account_id,
32 const GoogleServiceAuthError& error) {
33 account_errors_.insert(make_pair(account_id, error));
34 SigninGlobalError::GetForProfile(profile_)->AuthStatusChanged();
35 UpdateSignInState(account_id, false);
36 }
37
Shutdown()38 void AccountTracker::Shutdown() {
39 STLDeleteValues(&user_info_requests_);
40 SigninGlobalError::GetForProfile(profile_)->RemoveProvider(this);
41 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
42 RemoveObserver(this);
43 }
44
AddObserver(Observer * observer)45 void AccountTracker::AddObserver(Observer* observer) {
46 observer_list_.AddObserver(observer);
47 }
48
RemoveObserver(Observer * observer)49 void AccountTracker::RemoveObserver(Observer* observer) {
50 observer_list_.RemoveObserver(observer);
51 }
52
OnRefreshTokenAvailable(const std::string & account_id)53 void AccountTracker::OnRefreshTokenAvailable(const std::string& account_id) {
54 // Ignore refresh tokens if there is no primary account ID at all.
55 ProfileOAuth2TokenService* token_service =
56 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
57 if (token_service->GetPrimaryAccountId().empty())
58 return;
59
60 DVLOG(1) << "AVAILABLE " << account_id;
61 ClearAuthError(account_id);
62 UpdateSignInState(account_id, true);
63 }
64
OnRefreshTokenRevoked(const std::string & account_id)65 void AccountTracker::OnRefreshTokenRevoked(const std::string& account_id) {
66 DVLOG(1) << "REVOKED " << account_id;
67 UpdateSignInState(account_id, false);
68 }
69
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)70 void AccountTracker::Observe(int type,
71 const content::NotificationSource& source,
72 const content::NotificationDetails& details) {
73 switch (type) {
74 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
75 StopTrackingAccount(content::Details<GoogleServiceSignoutDetails>(
76 details)->username);
77 break;
78 default:
79 NOTREACHED();
80 }
81 }
82
NotifyAccountAdded(const AccountState & account)83 void AccountTracker::NotifyAccountAdded(const AccountState& account) {
84 DCHECK(!account.ids.gaia.empty());
85 FOR_EACH_OBSERVER(
86 Observer, observer_list_, OnAccountAdded(account.ids));
87 }
88
NotifyAccountRemoved(const AccountState & account)89 void AccountTracker::NotifyAccountRemoved(const AccountState& account) {
90 DCHECK(!account.ids.gaia.empty());
91 FOR_EACH_OBSERVER(
92 Observer, observer_list_, OnAccountRemoved(account.ids));
93 }
94
NotifySignInChanged(const AccountState & account)95 void AccountTracker::NotifySignInChanged(const AccountState& account) {
96 DCHECK(!account.ids.gaia.empty());
97 FOR_EACH_OBSERVER(Observer,
98 observer_list_,
99 OnAccountSignInChanged(account.ids, account.is_signed_in));
100 }
101
ClearAuthError(const std::string & account_key)102 void AccountTracker::ClearAuthError(const std::string& account_key) {
103 account_errors_.erase(account_key);
104 SigninGlobalError::GetForProfile(profile_)->AuthStatusChanged();
105 }
106
UpdateSignInState(const std::string & account_key,bool is_signed_in)107 void AccountTracker::UpdateSignInState(const std::string& account_key,
108 bool is_signed_in) {
109 StartTrackingAccount(account_key);
110 AccountState& account = accounts_[account_key];
111 bool needs_gaia_id = account.ids.gaia.empty();
112 bool was_signed_in = account.is_signed_in;
113 account.is_signed_in = is_signed_in;
114
115 if (needs_gaia_id && is_signed_in)
116 StartFetchingUserInfo(account_key);
117
118 if (!needs_gaia_id && (was_signed_in != is_signed_in))
119 NotifySignInChanged(account);
120 }
121
StartTrackingAccount(const std::string & account_key)122 void AccountTracker::StartTrackingAccount(const std::string& account_key) {
123 if (!ContainsKey(accounts_, account_key)) {
124 DVLOG(1) << "StartTracking " << account_key;
125 AccountState account_state;
126 account_state.ids.account_key = account_key;
127 account_state.ids.email = account_key;
128 account_state.is_signed_in = false;
129 accounts_.insert(make_pair(account_key, account_state));
130 }
131 }
132
StopTrackingAccount(const std::string & account_key)133 void AccountTracker::StopTrackingAccount(const std::string& account_key) {
134 if (ContainsKey(accounts_, account_key)) {
135 AccountState& account = accounts_[account_key];
136 if (!account.ids.gaia.empty()) {
137 UpdateSignInState(account_key, false);
138 NotifyAccountRemoved(account);
139 }
140 accounts_.erase(account_key);
141 }
142
143 ClearAuthError(account_key);
144
145 if (ContainsKey(user_info_requests_, account_key))
146 DeleteFetcher(user_info_requests_[account_key]);
147 }
148
StartFetchingUserInfo(const std::string & account_key)149 void AccountTracker::StartFetchingUserInfo(const std::string& account_key) {
150 if (ContainsKey(user_info_requests_, account_key))
151 DeleteFetcher(user_info_requests_[account_key]);
152
153 DVLOG(1) << "StartFetching " << account_key;
154 AccountIdFetcher* fetcher =
155 new AccountIdFetcher(profile_, this, account_key);
156 user_info_requests_[account_key] = fetcher;
157 fetcher->Start();
158 }
159
OnUserInfoFetchSuccess(AccountIdFetcher * fetcher,const std::string & gaia_id)160 void AccountTracker::OnUserInfoFetchSuccess(AccountIdFetcher* fetcher,
161 const std::string& gaia_id) {
162 const std::string& account_key = fetcher->account_key();
163 DCHECK(ContainsKey(accounts_, account_key));
164 AccountState& account = accounts_[account_key];
165
166 account.ids.gaia = gaia_id;
167 NotifyAccountAdded(account);
168
169 if (account.is_signed_in)
170 NotifySignInChanged(account);
171
172 DeleteFetcher(fetcher);
173 }
174
OnUserInfoFetchFailure(AccountIdFetcher * fetcher)175 void AccountTracker::OnUserInfoFetchFailure(AccountIdFetcher* fetcher) {
176 LOG(WARNING) << "Failed to get UserInfo for " << fetcher->account_key();
177 std::string key = fetcher->account_key();
178 DeleteFetcher(fetcher);
179 StopTrackingAccount(key);
180 }
181
GetAccountId() const182 std::string AccountTracker::GetAccountId() const {
183 if (account_errors_.size() == 0)
184 return std::string();
185 else
186 return account_errors_.begin()->first;
187 }
188
GetAuthStatus() const189 GoogleServiceAuthError AccountTracker::GetAuthStatus() const {
190 if (account_errors_.size() == 0)
191 return GoogleServiceAuthError::AuthErrorNone();
192 else
193 return account_errors_.begin()->second;
194 }
195
DeleteFetcher(AccountIdFetcher * fetcher)196 void AccountTracker::DeleteFetcher(AccountIdFetcher* fetcher) {
197 const std::string& account_key = fetcher->account_key();
198 DCHECK(ContainsKey(user_info_requests_, account_key));
199 DCHECK_EQ(fetcher, user_info_requests_[account_key]);
200 user_info_requests_.erase(account_key);
201 delete fetcher;
202 }
203
AccountIdFetcher(Profile * profile,AccountTracker * tracker,const std::string & account_key)204 AccountIdFetcher::AccountIdFetcher(Profile* profile,
205 AccountTracker* tracker,
206 const std::string& account_key)
207 : profile_(profile),
208 tracker_(tracker),
209 account_key_(account_key) {}
210
~AccountIdFetcher()211 AccountIdFetcher::~AccountIdFetcher() {}
212
Start()213 void AccountIdFetcher::Start() {
214 ProfileOAuth2TokenService* service =
215 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
216 login_token_request_ = service->StartRequest(
217 account_key_, OAuth2TokenService::ScopeSet(), this);
218 }
219
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)220 void AccountIdFetcher::OnGetTokenSuccess(
221 const OAuth2TokenService::Request* request,
222 const std::string& access_token,
223 const base::Time& expiration_time) {
224 DCHECK_EQ(request, login_token_request_.get());
225
226 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient(
227 g_browser_process->system_request_context()));
228
229 const int kMaxGetUserIdRetries = 3;
230 gaia_oauth_client_->GetUserId(access_token, kMaxGetUserIdRetries, this);
231 }
232
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)233 void AccountIdFetcher::OnGetTokenFailure(
234 const OAuth2TokenService::Request* request,
235 const GoogleServiceAuthError& error) {
236 LOG(ERROR) << "OnGetTokenFailure: " << error.error_message();
237 DCHECK_EQ(request, login_token_request_.get());
238 tracker_->OnUserInfoFetchFailure(this);
239 }
240
OnGetUserIdResponse(const std::string & gaia_id)241 void AccountIdFetcher::OnGetUserIdResponse(const std::string& gaia_id) {
242 tracker_->OnUserInfoFetchSuccess(this, gaia_id);
243 }
244
OnOAuthError()245 void AccountIdFetcher::OnOAuthError() {
246 LOG(ERROR) << "OnOAuthError";
247 tracker_->OnUserInfoFetchFailure(this);
248 }
249
OnNetworkError(int response_code)250 void AccountIdFetcher::OnNetworkError(int response_code) {
251 LOG(ERROR) << "OnNetworkError " << response_code;
252 tracker_->OnUserInfoFetchFailure(this);
253 }
254
255 } // namespace extensions
256