1 // Copyright 2020 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/dice_signed_in_profile_creator.h"
6 
7 #include "base/check.h"
8 #include "base/location.h"
9 #include "base/memory/ptr_util.h"
10 #include "base/no_destructor.h"
11 #include "base/scoped_observer.h"
12 #include "base/strings/string16.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/profiles/profile_attributes_storage.h"
16 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
17 #include "chrome/browser/profiles/profile_manager.h"
18 #include "chrome/browser/signin/identity_manager_factory.h"
19 #include "components/keyed_service/content/browser_context_keyed_service_shutdown_notifier_factory.h"
20 #include "components/keyed_service/core/keyed_service_shutdown_notifier.h"
21 #include "components/signin/public/identity_manager/accounts_mutator.h"
22 #include "components/signin/public/identity_manager/identity_manager.h"
23 
24 namespace {
25 
26 // A helper class to watch identity manager lifetime.
27 class DiceSignedInProfileCreatorShutdownNotifierFactory
28     : public BrowserContextKeyedServiceShutdownNotifierFactory {
29  public:
GetInstance()30   static DiceSignedInProfileCreatorShutdownNotifierFactory* GetInstance() {
31     static base::NoDestructor<DiceSignedInProfileCreatorShutdownNotifierFactory>
32         factory;
33     return factory.get();
34   }
35 
36   DiceSignedInProfileCreatorShutdownNotifierFactory(
37       const DiceSignedInProfileCreatorShutdownNotifierFactory&) = delete;
38   DiceSignedInProfileCreatorShutdownNotifierFactory& operator=(
39       const DiceSignedInProfileCreatorShutdownNotifierFactory&) = delete;
40 
41  private:
42   friend class base::NoDestructor<
43       DiceSignedInProfileCreatorShutdownNotifierFactory>;
44 
DiceSignedInProfileCreatorShutdownNotifierFactory()45   DiceSignedInProfileCreatorShutdownNotifierFactory()
46       : BrowserContextKeyedServiceShutdownNotifierFactory(
47             "DiceSignedInProfileCreatorShutdownNotifier") {
48     DependsOn(IdentityManagerFactory::GetInstance());
49   }
50   ~DiceSignedInProfileCreatorShutdownNotifierFactory() override = default;
51 };
52 
53 }  // namespace
54 
55 // Waits until the tokens are loaded and calls the callback. The callback is
56 // called immediately if the tokens are already loaded, and called with nullptr
57 // if the profile is destroyed before the tokens are loaded.
58 class TokensLoadedCallbackRunner : public signin::IdentityManager::Observer {
59  public:
60   ~TokensLoadedCallbackRunner() override = default;
61   TokensLoadedCallbackRunner(const TokensLoadedCallbackRunner&) = delete;
62   TokensLoadedCallbackRunner& operator=(const TokensLoadedCallbackRunner&) =
63       delete;
64 
65   // Runs the callback when the tokens are loaded. If tokens are already loaded
66   // the callback is called synchronously and this returns nullptr.
67   static std::unique_ptr<TokensLoadedCallbackRunner> RunWhenLoaded(
68       Profile* profile,
69       base::OnceCallback<void(Profile*)> callback);
70 
71  private:
72   TokensLoadedCallbackRunner(Profile* profile,
73                              base::OnceCallback<void(Profile*)> callback);
74 
75   // signin::IdentityManager::Observer implementation:
OnRefreshTokensLoaded()76   void OnRefreshTokensLoaded() override {
77     shutdown_subscription_.reset();
78     scoped_identity_manager_observer_.RemoveAll();
79     std::move(callback_).Run(profile_);
80   }
81 
OnShutdown()82   void OnShutdown() {
83     scoped_identity_manager_observer_.RemoveAll();
84     shutdown_subscription_.reset();
85     std::move(callback_).Run(nullptr);
86   }
87 
88   Profile* profile_;
89   signin::IdentityManager* identity_manager_;
90   ScopedObserver<signin::IdentityManager, signin::IdentityManager::Observer>
91       scoped_identity_manager_observer_{this};
92   base::OnceCallback<void(Profile*)> callback_;
93   std::unique_ptr<KeyedServiceShutdownNotifier::Subscription>
94       shutdown_subscription_;
95 };
96 
97 // static
98 std::unique_ptr<TokensLoadedCallbackRunner>
RunWhenLoaded(Profile * profile,base::OnceCallback<void (Profile *)> callback)99 TokensLoadedCallbackRunner::RunWhenLoaded(
100     Profile* profile,
101     base::OnceCallback<void(Profile*)> callback) {
102   if (IdentityManagerFactory::GetForProfile(profile)
103           ->AreRefreshTokensLoaded()) {
104     std::move(callback).Run(profile);
105     return nullptr;
106   }
107 
108   return base::WrapUnique(
109       new TokensLoadedCallbackRunner(profile, std::move(callback)));
110 }
111 
TokensLoadedCallbackRunner(Profile * profile,base::OnceCallback<void (Profile *)> callback)112 TokensLoadedCallbackRunner::TokensLoadedCallbackRunner(
113     Profile* profile,
114     base::OnceCallback<void(Profile*)> callback)
115     : profile_(profile),
116       identity_manager_(IdentityManagerFactory::GetForProfile(profile)),
117       callback_(std::move(callback)) {
118   DCHECK(profile_);
119   DCHECK(identity_manager_);
120   DCHECK(callback_);
121   DCHECK(!identity_manager_->AreRefreshTokensLoaded());
122 
123   // To catch the case where the profile is destroyed before the tokens are
124   // loaded.
125   shutdown_subscription_ =
126       DiceSignedInProfileCreatorShutdownNotifierFactory::GetInstance()
127           ->Get(profile)
128           ->Subscribe(base::Bind(&TokensLoadedCallbackRunner::OnShutdown,
129                                  base::Unretained(this)));
130   scoped_identity_manager_observer_.Add(identity_manager_);
131 }
132 
DiceSignedInProfileCreator(Profile * source_profile,CoreAccountId account_id,const base::string16 & local_profile_name,base::Optional<size_t> icon_index,base::OnceCallback<void (Profile *)> callback)133 DiceSignedInProfileCreator::DiceSignedInProfileCreator(
134     Profile* source_profile,
135     CoreAccountId account_id,
136     const base::string16& local_profile_name,
137     base::Optional<size_t> icon_index,
138     base::OnceCallback<void(Profile*)> callback)
139     : source_profile_(source_profile),
140       account_id_(account_id),
141       callback_(std::move(callback)) {
142   ProfileAttributesStorage& storage =
143       g_browser_process->profile_manager()->GetProfileAttributesStorage();
144   if (!icon_index.has_value())
145     icon_index = storage.ChooseAvatarIconIndexForNewProfile();
146   base::string16 name = local_profile_name.empty()
147                             ? storage.ChooseNameForNewProfile(*icon_index)
148                             : local_profile_name;
149   ProfileManager::CreateMultiProfileAsync(
150       name, profiles::GetDefaultAvatarIconUrl(*icon_index),
151       base::BindRepeating(&DiceSignedInProfileCreator::OnNewProfileCreated,
152                           weak_pointer_factory_.GetWeakPtr()));
153 }
154 
DiceSignedInProfileCreator(Profile * source_profile,CoreAccountId account_id,const base::FilePath & target_profile_path,base::OnceCallback<void (Profile *)> callback)155 DiceSignedInProfileCreator::DiceSignedInProfileCreator(
156     Profile* source_profile,
157     CoreAccountId account_id,
158     const base::FilePath& target_profile_path,
159     base::OnceCallback<void(Profile*)> callback)
160     : source_profile_(source_profile),
161       account_id_(account_id),
162       callback_(std::move(callback)) {
163   // Make sure the callback is not called synchronously.
164   base::ThreadTaskRunnerHandle::Get()->PostTask(
165       FROM_HERE,
166       base::BindOnce(
167           base::IgnoreResult(&ProfileManager::LoadProfileByPath),
168           base::Unretained(g_browser_process->profile_manager()),
169           target_profile_path, /*incognito=*/false,
170           base::BindOnce(&DiceSignedInProfileCreator::OnNewProfileInitialized,
171                          weak_pointer_factory_.GetWeakPtr())));
172 }
173 
174 DiceSignedInProfileCreator::~DiceSignedInProfileCreator() = default;
175 
OnNewProfileCreated(Profile * new_profile,Profile::CreateStatus status)176 void DiceSignedInProfileCreator::OnNewProfileCreated(
177     Profile* new_profile,
178     Profile::CreateStatus status) {
179   switch (status) {
180     case Profile::CREATE_STATUS_CREATED:
181       // Ignore this, wait for profile to be initialized.
182       return;
183     case Profile::CREATE_STATUS_INITIALIZED:
184       OnNewProfileInitialized(new_profile);
185       return;
186     case Profile::CREATE_STATUS_REMOTE_FAIL:
187     case Profile::CREATE_STATUS_CANCELED:
188     case Profile::MAX_CREATE_STATUS:
189       NOTREACHED() << "Invalid profile creation status";
190       FALLTHROUGH;
191     case Profile::CREATE_STATUS_LOCAL_FAIL:
192       NOTREACHED() << "Error creating new profile";
193       if (callback_)
194         std::move(callback_).Run(nullptr);
195       return;
196   }
197 }
198 
OnNewProfileInitialized(Profile * new_profile)199 void DiceSignedInProfileCreator::OnNewProfileInitialized(Profile* new_profile) {
200   if (!new_profile) {
201     if (callback_)
202       std::move(callback_).Run(nullptr);
203     return;
204   }
205 
206   DCHECK(!tokens_loaded_callback_runner_);
207   // base::Unretained is fine because the runner is owned by this.
208   auto tokens_loaded_callback_runner =
209       TokensLoadedCallbackRunner::RunWhenLoaded(
210           new_profile,
211           base::BindOnce(&DiceSignedInProfileCreator::OnNewProfileTokensLoaded,
212                          base::Unretained(this)));
213   // If the callback was called synchronously, |this| may have been deleted.
214   if (tokens_loaded_callback_runner) {
215     tokens_loaded_callback_runner_ = std::move(tokens_loaded_callback_runner);
216   }
217 }
218 
OnNewProfileTokensLoaded(Profile * new_profile)219 void DiceSignedInProfileCreator::OnNewProfileTokensLoaded(
220     Profile* new_profile) {
221   tokens_loaded_callback_runner_.reset();
222   if (!new_profile) {
223     if (callback_)
224       std::move(callback_).Run(nullptr);
225     return;
226   }
227 
228   auto* accounts_mutator =
229       IdentityManagerFactory::GetForProfile(source_profile_)
230           ->GetAccountsMutator();
231   auto* new_profile_accounts_mutator =
232       IdentityManagerFactory::GetForProfile(new_profile)->GetAccountsMutator();
233   accounts_mutator->MoveAccount(new_profile_accounts_mutator, account_id_);
234   if (callback_)
235     std::move(callback_).Run(new_profile);
236 }
237