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