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 #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_H_
5 #define COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_H_
6 
7 #include <memory>
8 #include <string>
9 #include <vector>
10 
11 #include "base/callback_forward.h"
12 #include "base/compiler_specific.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/macros.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/observer_list.h"
17 #include "base/threading/thread_checker.h"
18 #include "base/time/time.h"
19 #include "base/timer/timer.h"
20 #include "build/build_config.h"
21 #include "components/content_settings/core/browser/content_settings_observer.h"
22 #include "components/content_settings/core/common/content_settings_pattern.h"
23 #include "components/keyed_service/core/keyed_service.h"
24 #include "components/signin/core/browser/account_reconcilor_delegate.h"
25 #include "components/signin/core/browser/signin_header_helper.h"
26 #include "components/signin/public/base/signin_client.h"
27 #include "components/signin/public/base/signin_metrics.h"
28 #include "components/signin/public/identity_manager/identity_manager.h"
29 #include "google_apis/gaia/google_service_auth_error.h"
30 
31 namespace signin {
32 class AccountReconcilorDelegate;
33 enum class SetAccountsInCookieResult;
34 }
35 
36 class SigninClient;
37 
38 class AccountReconcilor : public KeyedService,
39                           public content_settings::Observer,
40                           public signin::IdentityManager::Observer {
41  public:
42   // When an instance of this class exists, the account reconcilor is suspended.
43   // It will automatically restart when all instances of Lock have been
44   // destroyed.
45   class Lock final {
46    public:
47     explicit Lock(AccountReconcilor* reconcilor);
48     ~Lock();
49 
50    private:
51     base::WeakPtr<AccountReconcilor> reconcilor_;
52     THREAD_CHECKER(thread_checker_);
53     DISALLOW_COPY_AND_ASSIGN(Lock);
54   };
55 
56   // Helper class to indicate that synced data is being deleted. The object
57   // must be destroyed when the data deletion is complete.
58   class ScopedSyncedDataDeletion {
59    public:
60     ~ScopedSyncedDataDeletion();
61 
62    private:
63     friend class AccountReconcilor;
64     explicit ScopedSyncedDataDeletion(AccountReconcilor* reconcilor);
65     base::WeakPtr<AccountReconcilor> reconcilor_;
66     DISALLOW_COPY_AND_ASSIGN(ScopedSyncedDataDeletion);
67   };
68 
69   class Observer {
70    public:
~Observer()71     virtual ~Observer() {}
72 
73     // The typical order of events is:
74     // - When reconcile is blocked:
75     //   1. current reconcile is aborted with AbortReconcile(),
76     //   2. OnStateChanged() is called with SCHEDULED.
77     //   3. OnBlockReconcile() is called.
78     // - When reconcile is unblocked:
79     //   1. OnUnblockReconcile() is called,
80     //   2. reconcile is restarted if needed with StartReconcile(), which
81     //     triggers a call to OnStateChanged() with RUNNING.
82 
83     // Called whe reconcile starts.
OnStateChanged(signin_metrics::AccountReconcilorState state)84     virtual void OnStateChanged(signin_metrics::AccountReconcilorState state) {}
85     // Called when the AccountReconcilor is blocked.
OnBlockReconcile()86     virtual void OnBlockReconcile() {}
87     // Called when the AccountReconcilor is unblocked.
OnUnblockReconcile()88     virtual void OnUnblockReconcile() {}
89   };
90 
91   AccountReconcilor(
92       signin::IdentityManager* identity_manager,
93       SigninClient* client,
94       std::unique_ptr<signin::AccountReconcilorDelegate> delegate);
95   ~AccountReconcilor() override;
96 
97   // Initializes the account reconcilor. Should be called once after
98   // construction.
99   void Initialize(bool start_reconcile_if_tokens_available);
100 
101   // Enables and disables the reconciliation.
102   void EnableReconcile();
103   void DisableReconcile(bool logout_all_gaia_accounts);
104 
105   // Signal that an X-Chrome-Manage-Accounts was received from GAIA. Pass the
106   // ServiceType specified by GAIA in the 204 response.
107   // Virtual for testing.
108   virtual void OnReceivedManageAccountsResponse(
109       signin::GAIAServiceType service_type);
110 
111   // KeyedService implementation.
112   void Shutdown() override;
113 
114   // Determine what the reconcilor is currently doing.
115   signin_metrics::AccountReconcilorState GetState();
116 
117   // Adds ands removes observers.
118   void AddObserver(Observer* observer);
119   void RemoveObserver(Observer* observer);
120 
121   // ScopedSyncedDataDeletion can be created when synced data is being removed
122   // and destroyed when the deletion is complete. It prevents the Sync account
123   // from being invalidated during the deletion.
124   std::unique_ptr<ScopedSyncedDataDeletion> GetScopedSyncDataDeletion();
125 
126   // Returns true if reconcilor is blocked.
127   bool IsReconcileBlocked() const;
128 
129  private:
130   friend class AccountReconcilorTest;
131   friend class DiceBrowserTest;
132   friend class BaseAccountReconcilorTestTable;
133   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestForceDiceMigration,
134                            TableRowTestCheckNoOp);
135   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
136                            IdentityManagerRegistration);
137   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, Reauth);
138   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
139                            ProfileAlreadyConnected);
140   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestTable, TableRowTest);
141   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestTable,
142                            InconsistencyReasonLogging);
143   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestDiceMultilogin, TableRowTest);
144   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestMirrorMultilogin, TableRowTest);
145   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestMiceMultilogin, TableRowTest);
146   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMiceTest,
147                            AccountReconcilorStateScheduled);
148   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
149                            DiceTokenServiceRegistration);
150   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
151                            DiceReconcileWithoutSignin);
152   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
153                            DiceReconcileNoop);
154   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
155                            DiceLastKnownFirstAccount);
156   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
157                            UnverifiedAccountNoop);
158   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
159                            UnverifiedAccountMerge);
160   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
161                            HandleSigninDuringReconcile);
162   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorDiceEndpointParamTest,
163                            DiceReconcileReuseGaiaFirstAccount);
164   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DiceDeleteCookie);
165   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, TokensNotLoaded);
166   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
167                            StartReconcileCookiesDisabled);
168   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
169                            StartReconcileContentSettings);
170   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
171                            StartReconcileContentSettingsGaiaUrl);
172   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
173                            StartReconcileContentSettingsNonGaiaUrl);
174   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
175                            StartReconcileContentSettingsInvalidPattern);
176   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
177                            GetAccountsFromCookieSuccess);
178   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
179                            EnableReconcileWhileAlreadyRunning);
180   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
181                            GetAccountsFromCookieFailure);
182   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
183                            ExtraCookieChangeNotification);
184   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, StartReconcileNoop);
185   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
186                            StartReconcileNoopWithDots);
187   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
188                            StartReconcileNoopMultiple);
189   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
190                            StartReconcileAddToCookie);
191   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, AuthErrorTriggersListAccount);
192   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
193                            SignoutAfterErrorDoesNotRecordUma);
194   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, TokenErrorOnPrimary);
195   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
196                            StartReconcileRemoveFromCookie);
197   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
198                            StartReconcileAddToCookieTwice);
199   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
200                            StartReconcileBadPrimary);
201   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, StartReconcileOnlyOnce);
202   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, Lock);
203   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMethodParamTest,
204                            StartReconcileWithSessionInfoExpiredDefault);
205   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMethodParamTest,
206                            AccountReconcilorStateScheduled);
207   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
208                            AddAccountToCookieCompletedWithBogusAccount);
209   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest, NoLoopWithBadPrimary);
210   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
211                            WontMergeAccountsWithError);
212   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, DelegateTimeoutIsCalled);
213   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorMirrorTest,
214                            DelegateTimeoutIsNotCalled);
215   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest,
216                            DelegateTimeoutIsNotCalledIfTimeoutIsNotReached);
217   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, MultiloginLogout);
218   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestForceDiceMigration,
219                            TableRowTest);
220   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestActiveDirectory,
221                            TableRowTestMergeSession);
222   FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestActiveDirectory,
223                            TableRowTestMultilogin);
224 
225   void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer);
226 
IsRegisteredWithIdentityManager()227   bool IsRegisteredWithIdentityManager() const {
228     return registered_with_identity_manager_;
229   }
230 
231   // Register and unregister with dependent services.
232   void RegisterWithAllDependencies();
233   void UnregisterWithAllDependencies();
234   void RegisterWithIdentityManager();
235   void UnregisterWithIdentityManager();
236   void RegisterWithContentSettings();
237   void UnregisterWithContentSettings();
238 
239   // All actions with side effects, only doing meaningful work if account
240   // consistency is enabled. Virtual so that they can be overridden in tests.
241   virtual void PerformMergeAction(const CoreAccountId& account_id);
242   virtual void PerformLogoutAllAccountsAction();
243   virtual void PerformSetCookiesAction(
244       const signin::MultiloginParameters& parameters);
245 
246   // Used during periodic reconciliation.
247   void StartReconcile();
248   // |gaia_accounts| are the accounts in the Gaia cookie.
249   void FinishReconcile(const CoreAccountId& primary_account,
250                        const std::vector<CoreAccountId>& chrome_accounts,
251                        std::vector<gaia::ListedAccount>&& gaia_accounts);
252   void AbortReconcile();
253   void CalculateIfReconcileIsDone();
254   void ScheduleStartReconcileIfChromeAccountsChanged();
255 
256   // Returns the list of valid accounts from the TokenService.
257   std::vector<CoreAccountId> LoadValidAccountsFromTokenService() const;
258 
259   // Note internally that this |account_id| is added to the cookie jar.
260   bool MarkAccountAsAddedToCookie(const CoreAccountId& account_id);
261 
262   // The reconcilor only starts when the token service is ready.
263   bool IsIdentityManagerReady();
264 
265   // Overridden from content_settings::Observer.
266   void OnContentSettingChanged(const ContentSettingsPattern& primary_pattern,
267                                const ContentSettingsPattern& secondary_pattern,
268                                ContentSettingsType content_type) override;
269 
270   // Overridden from signin::IdentityManager::Observer.
271   void OnEndBatchOfRefreshTokenStateChanges() override;
272   void OnRefreshTokensLoaded() override;
273   void OnErrorStateOfRefreshTokenUpdatedForAccount(
274       const CoreAccountInfo& account_info,
275       const GoogleServiceAuthError& error) override;
276   void OnAccountsInCookieUpdated(
277       const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
278       const GoogleServiceAuthError& error) override;
279   void OnAccountsCookieDeletedByUserAction() override;
280 
281   void FinishReconcileWithMultiloginEndpoint(
282       const CoreAccountId& primary_account,
283       const std::vector<CoreAccountId>& chrome_accounts,
284       std::vector<gaia::ListedAccount>&& gaia_accounts);
285 
286   void OnAddAccountToCookieCompleted(const CoreAccountId& account_id,
287                                      const GoogleServiceAuthError& error);
288   void OnSetAccountsInCookieCompleted(signin::SetAccountsInCookieResult result);
289   void OnLogOutFromCookieCompleted(const GoogleServiceAuthError& error);
290 
291   // Lock related methods.
292   void IncrementLockCount();
293   void DecrementLockCount();
294   void BlockReconcile();
295   void UnblockReconcile();
296 
297   void HandleReconcileTimeout();
298 
299   // Returns true is multilogin endpoint can be enabled.
300   bool IsMultiloginEndpointEnabled() const;
301 
302   // Returns true if current array of existing accounts in cookie is different
303   // from the desired one. If this returns false, the multilogin call would be a
304   // no-op.
305   bool CookieNeedsUpdate(
306       const signin::MultiloginParameters& parameters,
307       const std::vector<gaia::ListedAccount>& existing_accounts);
308 
309   // Sets the reconcilor state and calls Observer::OnStateChanged() if needed.
310   void SetState(signin_metrics::AccountReconcilorState state);
311 
312   std::unique_ptr<signin::AccountReconcilorDelegate> delegate_;
313 
314   // The IdentityManager associated with this reconcilor.
315   signin::IdentityManager* identity_manager_;
316 
317   // The SigninClient associated with this reconcilor.
318   SigninClient* client_;
319 
320   bool registered_with_identity_manager_;
321   bool registered_with_content_settings_;
322 
323   // True while the reconcilor is busy checking or managing the accounts in
324   // this profile.
325   bool is_reconcile_started_;
326   base::Time reconcile_start_time_;
327 
328   // True iff this is the first time the reconcilor is executing.
329   bool first_execution_;
330 
331   // 'Most severe' error encountered during the last attempt to reconcile. If
332   // the last reconciliation attempt was successful, this will be
333   // |GoogleServiceAuthError::State::NONE|.
334   // Severity of an error is defined on the basis of
335   // |GoogleServiceAuthError::IsPersistentError()| only, i.e. any persistent
336   // error is considered more severe than all non-persistent errors, but
337   // persistent (or non-persistent) errors do not have an internal severity
338   // ordering among themselves.
339   GoogleServiceAuthError error_during_last_reconcile_;
340 
341   // Used for Dice migration: migration can happen if the accounts are
342   // consistent, which is indicated by reconcile being a no-op.
343   bool reconcile_is_noop_;
344 
345   // Used during reconcile action.
346   std::vector<CoreAccountId> add_to_cookie_;  // Progress of AddAccount calls.
347   bool set_accounts_in_progress_;             // Progress of SetAccounts calls.
348   bool log_out_in_progress_;                  // Progress of LogOut calls.
349   bool chrome_accounts_changed_;
350 
351   // Used for the Lock.
352   // StartReconcile() is blocked while this is > 0.
353   int account_reconcilor_lock_count_;
354   // StartReconcile() should be started when the reconcilor is unblocked.
355   bool reconcile_on_unblock_;
356 
357   base::ObserverList<Observer, true>::Unchecked observer_list_;
358 
359   // A timer to set off reconciliation timeout handlers, if account
360   // reconciliation does not happen in a given |timeout_| duration.
361   // Any delegate that wants to use this feature must override
362   // |AccountReconcilorDelegate::GetReconcileTimeout|.
363   // Note: This is intended as a safeguard for delegates that want a 'guarantee'
364   // of reconciliation completing within a finite time. It is technically
365   // possible for account reconciliation to be running/waiting forever in cases
366   // such as a network connection not being present.
367   std::unique_ptr<base::OneShotTimer> timer_;
368   base::TimeDelta timeout_;
369 
370   // Greater than 0 when synced data is being deleted, and it is important to
371   // not invalidate the primary token while this is happening.
372   int synced_data_deletion_in_progress_count_ = 0;
373 
374   signin_metrics::AccountReconcilorState state_;
375 
376   base::WeakPtrFactory<AccountReconcilor> weak_factory_{this};
377 
378   DISALLOW_COPY_AND_ASSIGN(AccountReconcilor);
379 };
380 
381 #endif  // COMPONENTS_SIGNIN_CORE_BROWSER_ACCOUNT_RECONCILOR_H_
382