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/ui/webui/options/supervised_user_import_handler.h"
6
7#include <set>
8
9#include "base/bind.h"
10#include "base/prefs/pref_service.h"
11#include "base/values.h"
12#include "chrome/browser/browser_process.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/profiles/profile_avatar_icon_util.h"
15#include "chrome/browser/profiles/profile_info_cache.h"
16#include "chrome/browser/profiles/profile_manager.h"
17#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18#include "chrome/browser/signin/signin_manager_factory.h"
19#include "chrome/browser/supervised_user/supervised_user_constants.h"
20#include "chrome/browser/supervised_user/supervised_user_shared_settings_service.h"
21#include "chrome/browser/supervised_user/supervised_user_shared_settings_service_factory.h"
22#include "chrome/browser/supervised_user/supervised_user_sync_service.h"
23#include "chrome/browser/supervised_user/supervised_user_sync_service_factory.h"
24#include "chrome/common/pref_names.h"
25#include "chrome/common/url_constants.h"
26#include "chrome/grit/generated_resources.h"
27#include "components/signin/core/browser/profile_oauth2_token_service.h"
28#include "components/signin/core/browser/signin_error_controller.h"
29#include "components/signin/core/browser/signin_manager.h"
30#include "content/public/browser/web_ui.h"
31#include "grit/theme_resources.h"
32
33namespace {
34
35scoped_ptr<base::ListValue> GetAvatarIcons() {
36  scoped_ptr<base::ListValue> avatar_icons(new base::ListValue);
37  for (size_t i = 0; i < profiles::GetDefaultAvatarIconCount(); ++i) {
38    std::string avatar_url = profiles::GetDefaultAvatarIconUrl(i);
39    avatar_icons->Append(new base::StringValue(avatar_url));
40  }
41
42  return avatar_icons.Pass();
43}
44
45}  // namespace
46
47namespace options {
48
49SupervisedUserImportHandler::SupervisedUserImportHandler()
50    : observer_(this),
51      weak_ptr_factory_(this) {}
52
53SupervisedUserImportHandler::~SupervisedUserImportHandler() {
54  Profile* profile = Profile::FromWebUI(web_ui());
55  if (!profile->IsSupervised()) {
56    SupervisedUserSyncService* service =
57        SupervisedUserSyncServiceFactory::GetForProfile(profile);
58    if (service)
59      service->RemoveObserver(this);
60    subscription_.reset();
61  }
62}
63
64void SupervisedUserImportHandler::GetLocalizedValues(
65    base::DictionaryValue* localized_strings) {
66  DCHECK(localized_strings);
67
68  static OptionsStringResource resources[] = {
69      { "supervisedUserImportTitle",
70          IDS_IMPORT_EXISTING_SUPERVISED_USER_TITLE },
71      { "supervisedUserImportText", IDS_IMPORT_EXISTING_SUPERVISED_USER_TEXT },
72      { "createNewUserLink", IDS_CREATE_NEW_USER_LINK },
73      { "supervisedUserImportOk", IDS_IMPORT_EXISTING_SUPERVISED_USER_OK },
74      { "supervisedUserImportSigninError",
75          IDS_SUPERVISED_USER_IMPORT_SIGN_IN_ERROR },
76      { "supervisedUserAlreadyOnThisDevice",
77          IDS_SUPERVISED_USER_ALREADY_ON_THIS_DEVICE },
78      { "noExistingSupervisedUsers", IDS_SUPERVISED_USER_NO_EXISTING_ERROR },
79      { "supervisedUserSelectAvatarTitle",
80          IDS_SUPERVISED_USER_SELECT_AVATAR_TITLE },
81      { "supervisedUserSelectAvatarText",
82          IDS_SUPERVISED_USER_SELECT_AVATAR_TEXT },
83      { "supervisedUserSelectAvatarOk", IDS_SUPERVISED_USER_SELECT_AVATAR_OK },
84  };
85
86  RegisterStrings(localized_strings, resources, arraysize(resources));
87  localized_strings->Set("avatarIcons", GetAvatarIcons().release());
88}
89
90void SupervisedUserImportHandler::InitializeHandler() {
91  Profile* profile = Profile::FromWebUI(web_ui());
92  if (!profile->IsSupervised()) {
93    SupervisedUserSyncService* sync_service =
94        SupervisedUserSyncServiceFactory::GetForProfile(profile);
95    if (sync_service) {
96      sync_service->AddObserver(this);
97      observer_.Add(ProfileOAuth2TokenServiceFactory::GetForProfile(profile)->
98                        signin_error_controller());
99      SupervisedUserSharedSettingsService* settings_service =
100          SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(
101              profile);
102      subscription_ = settings_service->Subscribe(
103          base::Bind(&SupervisedUserImportHandler::OnSharedSettingChanged,
104                     weak_ptr_factory_.GetWeakPtr()));
105    } else {
106      DCHECK(!SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(
107                 profile));
108      DCHECK(!ProfileOAuth2TokenServiceFactory::GetForProfile(profile));
109    }
110  }
111}
112
113void SupervisedUserImportHandler::RegisterMessages() {
114  web_ui()->RegisterMessageCallback("requestSupervisedUserImportUpdate",
115      base::Bind(&SupervisedUserImportHandler::
116                      RequestSupervisedUserImportUpdate,
117                 base::Unretained(this)));
118}
119
120void SupervisedUserImportHandler::OnSupervisedUsersChanged() {
121  FetchSupervisedUsers();
122}
123
124void SupervisedUserImportHandler::FetchSupervisedUsers() {
125  web_ui()->CallJavascriptFunction(
126      "options.SupervisedUserListData.resetPromise");
127  RequestSupervisedUserImportUpdate(NULL);
128}
129
130void SupervisedUserImportHandler::RequestSupervisedUserImportUpdate(
131    const base::ListValue* /* args */) {
132  if (Profile::FromWebUI(web_ui())->IsSupervised())
133    return;
134
135  if (!IsAccountConnected() || HasAuthError()) {
136    ClearSupervisedUsersAndShowError();
137  } else {
138    SupervisedUserSyncService* supervised_user_sync_service =
139        SupervisedUserSyncServiceFactory::GetForProfile(
140            Profile::FromWebUI(web_ui()));
141    if (supervised_user_sync_service) {
142      supervised_user_sync_service->GetSupervisedUsersAsync(
143          base::Bind(&SupervisedUserImportHandler::SendExistingSupervisedUsers,
144                     weak_ptr_factory_.GetWeakPtr()));
145    }
146  }
147}
148
149void SupervisedUserImportHandler::SendExistingSupervisedUsers(
150    const base::DictionaryValue* dict) {
151  DCHECK(dict);
152  const ProfileInfoCache& cache =
153      g_browser_process->profile_manager()->GetProfileInfoCache();
154
155  // Collect the ids of local supervised user profiles.
156  std::set<std::string> supervised_user_ids;
157  for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) {
158    if (cache.ProfileIsSupervisedAtIndex(i))
159      supervised_user_ids.insert(cache.GetSupervisedUserIdOfProfileAtIndex(i));
160  }
161
162  base::ListValue supervised_users;
163  Profile* profile = Profile::FromWebUI(web_ui());
164  SupervisedUserSharedSettingsService* service =
165      SupervisedUserSharedSettingsServiceFactory::GetForBrowserContext(profile);
166  for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) {
167    const base::DictionaryValue* value = NULL;
168    bool success = it.value().GetAsDictionary(&value);
169    DCHECK(success);
170    std::string name;
171    value->GetString(SupervisedUserSyncService::kName, &name);
172
173    base::DictionaryValue* supervised_user = new base::DictionaryValue;
174    supervised_user->SetString("id", it.key());
175    supervised_user->SetString("name", name);
176
177    int avatar_index = SupervisedUserSyncService::kNoAvatar;
178    const base::Value* avatar_index_value =
179        service->GetValue(it.key(), supervised_users::kChromeAvatarIndex);
180    if (avatar_index_value) {
181      success = avatar_index_value->GetAsInteger(&avatar_index);
182    } else {
183      // Check if there is a legacy avatar index stored.
184      std::string avatar_str;
185      value->GetString(SupervisedUserSyncService::kChromeAvatar, &avatar_str);
186      success =
187          SupervisedUserSyncService::GetAvatarIndex(avatar_str, &avatar_index);
188    }
189    DCHECK(success);
190    supervised_user->SetBoolean(
191        "needAvatar",
192        avatar_index == SupervisedUserSyncService::kNoAvatar);
193
194    std::string supervised_user_icon =
195        std::string(chrome::kChromeUIThemeURL) +
196        "IDR_SUPERVISED_USER_PLACEHOLDER";
197    std::string avatar_url =
198        avatar_index == SupervisedUserSyncService::kNoAvatar ?
199            supervised_user_icon :
200            profiles::GetDefaultAvatarIconUrl(avatar_index);
201    supervised_user->SetString("iconURL", avatar_url);
202    bool on_current_device =
203        supervised_user_ids.find(it.key()) != supervised_user_ids.end();
204    supervised_user->SetBoolean("onCurrentDevice", on_current_device);
205
206    supervised_users.Append(supervised_user);
207  }
208
209  web_ui()->CallJavascriptFunction(
210      "options.SupervisedUserListData.receiveExistingSupervisedUsers",
211      supervised_users);
212}
213
214void SupervisedUserImportHandler::ClearSupervisedUsersAndShowError() {
215  web_ui()->CallJavascriptFunction(
216      "options.SupervisedUserListData.onSigninError");
217}
218
219bool SupervisedUserImportHandler::IsAccountConnected() const {
220  Profile* profile = Profile::FromWebUI(web_ui());
221  SigninManagerBase* signin_manager =
222      SigninManagerFactory::GetForProfile(profile);
223  return signin_manager && signin_manager->IsAuthenticated();
224}
225
226bool SupervisedUserImportHandler::HasAuthError() const {
227  Profile* profile = Profile::FromWebUI(web_ui());
228  ProfileOAuth2TokenService* token_service =
229      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
230  if (!token_service)
231    return true;
232
233  SigninErrorController* error_controller =
234      token_service->signin_error_controller();
235
236  GoogleServiceAuthError::State state = error_controller->auth_error().state();
237
238  return state == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
239      state == GoogleServiceAuthError::USER_NOT_SIGNED_UP ||
240      state == GoogleServiceAuthError::ACCOUNT_DELETED ||
241      state == GoogleServiceAuthError::ACCOUNT_DISABLED;
242}
243
244void SupervisedUserImportHandler::OnSharedSettingChanged(
245    const std::string& supervised_user_id,
246    const std::string& key) {
247  if (key == supervised_users::kChromeAvatarIndex)
248    FetchSupervisedUsers();
249}
250
251void SupervisedUserImportHandler::OnErrorChanged() {
252  FetchSupervisedUsers();
253}
254
255}  // namespace options
256