1// Copyright (c) 2012 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/policy_ui.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback.h"
10#include "base/compiler_specific.h"
11#include "base/json/json_writer.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/memory/weak_ptr.h"
15#include "base/strings/string16.h"
16#include "base/time/time.h"
17#include "base/values.h"
18#include "chrome/browser/browser_process.h"
19#include "chrome/browser/chrome_notification_types.h"
20#include "chrome/browser/policy/profile_policy_connector.h"
21#include "chrome/browser/policy/profile_policy_connector_factory.h"
22#include "chrome/browser/policy/schema_registry_service.h"
23#include "chrome/browser/policy/schema_registry_service_factory.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/common/url_constants.h"
26#include "components/policy/core/browser/browser_policy_connector.h"
27#include "components/policy/core/browser/cloud/message_util.h"
28#include "components/policy/core/browser/configuration_policy_handler_list.h"
29#include "components/policy/core/browser/policy_error_map.h"
30#include "components/policy/core/common/cloud/cloud_policy_client.h"
31#include "components/policy/core/common/cloud/cloud_policy_constants.h"
32#include "components/policy/core/common/cloud/cloud_policy_core.h"
33#include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
34#include "components/policy/core/common/cloud/cloud_policy_store.h"
35#include "components/policy/core/common/cloud/cloud_policy_validator.h"
36#include "components/policy/core/common/policy_map.h"
37#include "components/policy/core/common/policy_namespace.h"
38#include "components/policy/core/common/policy_service.h"
39#include "components/policy/core/common/policy_types.h"
40#include "components/policy/core/common/schema.h"
41#include "components/policy/core/common/schema_map.h"
42#include "components/policy/core/common/schema_registry.h"
43#include "content/public/browser/notification_observer.h"
44#include "content/public/browser/notification_registrar.h"
45#include "content/public/browser/notification_service.h"
46#include "content/public/browser/web_ui.h"
47#include "content/public/browser/web_ui_data_source.h"
48#include "content/public/browser/web_ui_message_handler.h"
49#include "google_apis/gaia/gaia_auth_util.h"
50#include "grit/browser_resources.h"
51#include "grit/components_strings.h"
52#include "policy/policy_constants.h"
53#include "policy/proto/device_management_backend.pb.h"
54#include "ui/base/l10n/l10n_util.h"
55#include "ui/base/l10n/time_format.h"
56
57#if defined(OS_CHROMEOS)
58#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
59#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
60#include "chrome/browser/chromeos/policy/device_local_account_policy_service.h"
61#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
62#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_factory_chromeos.h"
63#include "components/user_manager/user_manager.h"
64#else
65#include "chrome/browser/policy/cloud/user_cloud_policy_manager_factory.h"
66#include "components/policy/core/common/cloud/user_cloud_policy_manager.h"
67#include "content/public/browser/web_contents.h"
68#endif
69
70#if !defined(OS_ANDROID) && !defined(OS_IOS)
71#include "chrome/browser/extensions/extension_service.h"
72#include "extensions/browser/extension_system.h"
73#include "extensions/common/extension.h"
74#include "extensions/common/extension_set.h"
75#include "extensions/common/manifest.h"
76#include "extensions/common/manifest_constants.h"
77#endif
78
79namespace em = enterprise_management;
80
81namespace {
82
83content::WebUIDataSource* CreatePolicyUIHTMLSource() {
84  content::WebUIDataSource* source =
85      content::WebUIDataSource::Create(chrome::kChromeUIPolicyHost);
86
87  // Localized strings.
88  source->AddLocalizedString("title", IDS_POLICY_TITLE);
89  source->AddLocalizedString("filterPlaceholder",
90                             IDS_POLICY_FILTER_PLACEHOLDER);
91  source->AddLocalizedString("reloadPolicies", IDS_POLICY_RELOAD_POLICIES);
92  source->AddLocalizedString("status", IDS_POLICY_STATUS);
93  source->AddLocalizedString("statusDevice", IDS_POLICY_STATUS_DEVICE);
94  source->AddLocalizedString("statusUser", IDS_POLICY_STATUS_USER);
95  source->AddLocalizedString("labelDomain", IDS_POLICY_LABEL_DOMAIN);
96  source->AddLocalizedString("labelUsername", IDS_POLICY_LABEL_USERNAME);
97  source->AddLocalizedString("labelClientId", IDS_POLICY_LABEL_CLIENT_ID);
98  source->AddLocalizedString("labelTimeSinceLastRefresh",
99                             IDS_POLICY_LABEL_TIME_SINCE_LAST_REFRESH);
100  source->AddLocalizedString("labelRefreshInterval",
101                             IDS_POLICY_LABEL_REFRESH_INTERVAL);
102  source->AddLocalizedString("labelStatus", IDS_POLICY_LABEL_STATUS);
103  source->AddLocalizedString("showUnset", IDS_POLICY_SHOW_UNSET);
104  source->AddLocalizedString("noPoliciesSet", IDS_POLICY_NO_POLICIES_SET);
105  source->AddLocalizedString("headerScope", IDS_POLICY_HEADER_SCOPE);
106  source->AddLocalizedString("headerLevel", IDS_POLICY_HEADER_LEVEL);
107  source->AddLocalizedString("headerName", IDS_POLICY_HEADER_NAME);
108  source->AddLocalizedString("headerValue", IDS_POLICY_HEADER_VALUE);
109  source->AddLocalizedString("headerStatus", IDS_POLICY_HEADER_STATUS);
110  source->AddLocalizedString("showExpandedValue",
111                             IDS_POLICY_SHOW_EXPANDED_VALUE);
112  source->AddLocalizedString("hideExpandedValue",
113                             IDS_POLICY_HIDE_EXPANDED_VALUE);
114  source->AddLocalizedString("scopeUser", IDS_POLICY_SCOPE_USER);
115  source->AddLocalizedString("scopeDevice", IDS_POLICY_SCOPE_DEVICE);
116  source->AddLocalizedString("levelRecommended", IDS_POLICY_LEVEL_RECOMMENDED);
117  source->AddLocalizedString("levelMandatory", IDS_POLICY_LEVEL_MANDATORY);
118  source->AddLocalizedString("ok", IDS_POLICY_OK);
119  source->AddLocalizedString("unset", IDS_POLICY_UNSET);
120  source->AddLocalizedString("unknown", IDS_POLICY_UNKNOWN);
121
122  source->SetUseJsonJSFormatV2();
123  source->SetJsonPath("strings.js");
124
125  // Add required resources.
126  source->AddResourcePath("policy.css", IDR_POLICY_CSS);
127  source->AddResourcePath("policy.js", IDR_POLICY_JS);
128  source->AddResourcePath("uber_utils.js", IDR_UBER_UTILS_JS);
129  source->SetDefaultResource(IDR_POLICY_HTML);
130
131  return source;
132}
133
134// Formats the association state indicated by |data|. If |data| is NULL, the
135// state is considered to be UNMANAGED.
136base::string16 FormatAssociationState(const em::PolicyData* data) {
137  if (data) {
138    switch (data->state()) {
139      case em::PolicyData::ACTIVE:
140        return l10n_util::GetStringUTF16(IDS_POLICY_ASSOCIATION_STATE_ACTIVE);
141      case em::PolicyData::UNMANAGED:
142        return l10n_util::GetStringUTF16(
143            IDS_POLICY_ASSOCIATION_STATE_UNMANAGED);
144      case em::PolicyData::DEPROVISIONED:
145        return l10n_util::GetStringUTF16(
146            IDS_POLICY_ASSOCIATION_STATE_DEPROVISIONED);
147    }
148    NOTREACHED() << "Unknown state " << data->state();
149  }
150
151  // Default to UNMANAGED for the case of missing policy or bad state enum.
152  return l10n_util::GetStringUTF16(IDS_POLICY_ASSOCIATION_STATE_UNMANAGED);
153}
154
155void GetStatusFromCore(const policy::CloudPolicyCore* core,
156                       base::DictionaryValue* dict) {
157  const policy::CloudPolicyStore* store = core->store();
158  const policy::CloudPolicyClient* client = core->client();
159  const policy::CloudPolicyRefreshScheduler* refresh_scheduler =
160        core->refresh_scheduler();
161
162  // CloudPolicyStore errors take precedence to show in the status message.
163  // Other errors (such as transient policy fetching problems) get displayed
164  // only if CloudPolicyStore is in STATUS_OK.
165  base::string16 status =
166      policy::FormatStoreStatus(store->status(), store->validation_status());
167  if (store->status() == policy::CloudPolicyStore::STATUS_OK) {
168    if (client && client->status() != policy::DM_STATUS_SUCCESS)
169      status = policy::FormatDeviceManagementStatus(client->status());
170    else if (!store->is_managed())
171      status = FormatAssociationState(store->policy());
172  }
173
174  const em::PolicyData* policy = store->policy();
175  std::string client_id = policy ? policy->device_id() : std::string();
176  std::string username = policy ? policy->username() : std::string();
177  base::TimeDelta refresh_interval =
178      base::TimeDelta::FromMilliseconds(refresh_scheduler ?
179          refresh_scheduler->refresh_delay() :
180          policy::CloudPolicyRefreshScheduler::kDefaultRefreshDelayMs);
181  base::Time last_refresh_time = refresh_scheduler ?
182      refresh_scheduler->last_refresh() : base::Time();
183
184  bool no_error = store->status() == policy::CloudPolicyStore::STATUS_OK &&
185                  client && client->status() == policy::DM_STATUS_SUCCESS;
186  dict->SetBoolean("error", !no_error);
187  dict->SetString("status", status);
188  dict->SetString("clientId", client_id);
189  dict->SetString("username", username);
190  dict->SetString("refreshInterval",
191                  ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_DURATION,
192                                         ui::TimeFormat::LENGTH_SHORT,
193                                         refresh_interval));
194  dict->SetString("timeSinceLastRefresh", last_refresh_time.is_null() ?
195      l10n_util::GetStringUTF16(IDS_POLICY_NEVER_FETCHED) :
196      ui::TimeFormat::Simple(ui::TimeFormat::FORMAT_ELAPSED,
197                             ui::TimeFormat::LENGTH_SHORT,
198                             base::Time::NowFromSystemTime() -
199                                 last_refresh_time));
200}
201
202void ExtractDomainFromUsername(base::DictionaryValue* dict) {
203  std::string username;
204  dict->GetString("username", &username);
205  if (!username.empty())
206    dict->SetString("domain", gaia::ExtractDomainName(username));
207}
208
209// Utility function that returns a JSON serialization of the given |dict|.
210scoped_ptr<base::StringValue> DictionaryToJSONString(
211    const base::DictionaryValue* dict) {
212  std::string json_string;
213  base::JSONWriter::WriteWithOptions(dict,
214                                     base::JSONWriter::OPTIONS_PRETTY_PRINT,
215                                     &json_string);
216  return make_scoped_ptr(new base::StringValue(json_string));
217}
218
219// Returns a copy of |value| with some values converted to a representation that
220// i18n_template.js will display in a nicer way.
221scoped_ptr<base::Value> CopyAndConvert(const base::Value* value) {
222  const base::DictionaryValue* dict = NULL;
223  if (value->GetAsDictionary(&dict))
224    return DictionaryToJSONString(dict).PassAs<base::Value>();
225
226  scoped_ptr<base::Value> copy(value->DeepCopy());
227  base::ListValue* list = NULL;
228  if (copy->GetAsList(&list)) {
229    for (size_t i = 0; i < list->GetSize(); ++i) {
230      if (list->GetDictionary(i, &dict))
231        list->Set(i, DictionaryToJSONString(dict).release());
232    }
233  }
234
235  return copy.Pass();
236}
237
238}  // namespace
239
240// An interface for querying the status of cloud policy.
241class CloudPolicyStatusProvider {
242 public:
243  CloudPolicyStatusProvider();
244  virtual ~CloudPolicyStatusProvider();
245
246  // Sets a callback to invoke upon status changes.
247  void SetStatusChangeCallback(const base::Closure& callback);
248
249  virtual void GetStatus(base::DictionaryValue* dict);
250
251 protected:
252  void NotifyStatusChange();
253
254 private:
255  base::Closure callback_;
256
257  DISALLOW_COPY_AND_ASSIGN(CloudPolicyStatusProvider);
258};
259
260// Status provider implementation that pulls cloud policy status from a
261// CloudPolicyCore instance provided at construction time. Also listens for
262// changes on that CloudPolicyCore and reports them through the status change
263// callback.
264class CloudPolicyCoreStatusProvider
265    : public CloudPolicyStatusProvider,
266      public policy::CloudPolicyStore::Observer {
267 public:
268  explicit CloudPolicyCoreStatusProvider(policy::CloudPolicyCore* core);
269  virtual ~CloudPolicyCoreStatusProvider();
270
271  // policy::CloudPolicyStore::Observer implementation.
272  virtual void OnStoreLoaded(policy::CloudPolicyStore* store) OVERRIDE;
273  virtual void OnStoreError(policy::CloudPolicyStore* store) OVERRIDE;
274
275 protected:
276  // Policy status is read from the CloudPolicyClient, CloudPolicyStore and
277  // CloudPolicyRefreshScheduler hosted by this |core_|.
278  policy::CloudPolicyCore* core_;
279
280  DISALLOW_COPY_AND_ASSIGN(CloudPolicyCoreStatusProvider);
281};
282
283// A cloud policy status provider for user policy.
284class UserPolicyStatusProvider : public CloudPolicyCoreStatusProvider {
285 public:
286  explicit UserPolicyStatusProvider(policy::CloudPolicyCore* core);
287  virtual ~UserPolicyStatusProvider();
288
289  // CloudPolicyCoreStatusProvider implementation.
290  virtual void GetStatus(base::DictionaryValue* dict) OVERRIDE;
291
292 private:
293  DISALLOW_COPY_AND_ASSIGN(UserPolicyStatusProvider);
294};
295
296#if defined(OS_CHROMEOS)
297// A cloud policy status provider for device policy.
298class DevicePolicyStatusProvider : public CloudPolicyCoreStatusProvider {
299 public:
300  explicit DevicePolicyStatusProvider(
301      policy::BrowserPolicyConnectorChromeOS* connector);
302  virtual ~DevicePolicyStatusProvider();
303
304  // CloudPolicyCoreStatusProvider implementation.
305  virtual void GetStatus(base::DictionaryValue* dict) OVERRIDE;
306
307 private:
308  std::string domain_;
309
310  DISALLOW_COPY_AND_ASSIGN(DevicePolicyStatusProvider);
311};
312
313// A cloud policy status provider that reads policy status from the policy core
314// associated with the device-local account specified by |user_id| at
315// construction time. The indirection via user ID and
316// DeviceLocalAccountPolicyService is necessary because the device-local account
317// may go away any time behind the scenes, at which point the status message
318// text will indicate CloudPolicyStore::STATUS_BAD_STATE.
319class DeviceLocalAccountPolicyStatusProvider
320    : public CloudPolicyStatusProvider,
321      public policy::DeviceLocalAccountPolicyService::Observer {
322 public:
323  DeviceLocalAccountPolicyStatusProvider(
324      const std::string& user_id,
325      policy::DeviceLocalAccountPolicyService* service);
326  virtual ~DeviceLocalAccountPolicyStatusProvider();
327
328  // CloudPolicyStatusProvider implementation.
329  virtual void GetStatus(base::DictionaryValue* dict) OVERRIDE;
330
331  // policy::DeviceLocalAccountPolicyService::Observer implementation.
332  virtual void OnPolicyUpdated(const std::string& user_id) OVERRIDE;
333  virtual void OnDeviceLocalAccountsChanged() OVERRIDE;
334
335 private:
336  const std::string user_id_;
337  policy::DeviceLocalAccountPolicyService* service_;
338
339  DISALLOW_COPY_AND_ASSIGN(DeviceLocalAccountPolicyStatusProvider);
340};
341#endif
342
343// The JavaScript message handler for the chrome://policy page.
344class PolicyUIHandler : public content::NotificationObserver,
345                        public content::WebUIMessageHandler,
346                        public policy::PolicyService::Observer {
347 public:
348  PolicyUIHandler();
349  virtual ~PolicyUIHandler();
350
351  // content::NotificationObserver implementation.
352  virtual void Observe(int type,
353                       const content::NotificationSource& source,
354                       const content::NotificationDetails& details) OVERRIDE;
355
356  // content::WebUIMessageHandler implementation.
357  virtual void RegisterMessages() OVERRIDE;
358
359  // policy::PolicyService::Observer implementation.
360  virtual void OnPolicyUpdated(const policy::PolicyNamespace& ns,
361                               const policy::PolicyMap& previous,
362                               const policy::PolicyMap& current) OVERRIDE;
363
364 private:
365  // Send a dictionary containing the names of all known policies to the UI.
366  void SendPolicyNames() const;
367
368  // Send information about the current policy values to the UI. For each policy
369  // whose value has been set, a dictionary containing the value and additional
370  // metadata is sent.
371  void SendPolicyValues() const;
372
373  // Send the status of cloud policy to the UI. For each scope that has cloud
374  // policy enabled (device and/or user), a dictionary containing status
375  // information is sent.
376  void SendStatus() const;
377
378  // Inserts a description of each policy in |policy_map| into |values|, using
379  // the optional errors in |errors| to determine the status of each policy.
380  void GetPolicyValues(const policy::PolicyMap& policy_map,
381                       policy::PolicyErrorMap* errors,
382                       base::DictionaryValue* values) const;
383
384  void GetChromePolicyValues(base::DictionaryValue* values) const;
385
386  void HandleInitialized(const base::ListValue* args);
387  void HandleReloadPolicies(const base::ListValue* args);
388
389  void OnRefreshPoliciesDone() const;
390
391  policy::PolicyService* GetPolicyService() const;
392
393  bool initialized_;
394  std::string device_domain_;
395
396  // Providers that supply status dictionaries for user and device policy,
397  // respectively. These are created on initialization time as appropriate for
398  // the platform (Chrome OS / desktop) and type of policy that is in effect.
399  scoped_ptr<CloudPolicyStatusProvider> user_status_provider_;
400  scoped_ptr<CloudPolicyStatusProvider> device_status_provider_;
401
402  content::NotificationRegistrar registrar_;
403
404  base::WeakPtrFactory<PolicyUIHandler> weak_factory_;
405
406  DISALLOW_COPY_AND_ASSIGN(PolicyUIHandler);
407};
408
409CloudPolicyStatusProvider::CloudPolicyStatusProvider() {
410}
411
412CloudPolicyStatusProvider::~CloudPolicyStatusProvider() {
413}
414
415void CloudPolicyStatusProvider::SetStatusChangeCallback(
416    const base::Closure& callback) {
417  callback_ = callback;
418}
419
420void CloudPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
421}
422
423void CloudPolicyStatusProvider::NotifyStatusChange() {
424  if (!callback_.is_null())
425    callback_.Run();
426}
427
428CloudPolicyCoreStatusProvider::CloudPolicyCoreStatusProvider(
429    policy::CloudPolicyCore* core) : core_(core) {
430  core_->store()->AddObserver(this);
431  // TODO(bartfab): Add an observer that watches for client errors. Observing
432  // core_->client() directly is not safe as the client may be destroyed and
433  // (re-)created anytime if the user signs in or out on desktop platforms.
434}
435
436CloudPolicyCoreStatusProvider::~CloudPolicyCoreStatusProvider() {
437  core_->store()->RemoveObserver(this);
438}
439
440void CloudPolicyCoreStatusProvider::OnStoreLoaded(
441    policy::CloudPolicyStore* store) {
442  NotifyStatusChange();
443}
444
445void CloudPolicyCoreStatusProvider::OnStoreError(
446    policy::CloudPolicyStore* store) {
447  NotifyStatusChange();
448}
449
450UserPolicyStatusProvider::UserPolicyStatusProvider(
451    policy::CloudPolicyCore* core) : CloudPolicyCoreStatusProvider(core) {
452}
453
454UserPolicyStatusProvider::~UserPolicyStatusProvider() {
455}
456
457void UserPolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
458  if (!core_->store()->is_managed())
459    return;
460  GetStatusFromCore(core_, dict);
461  ExtractDomainFromUsername(dict);
462}
463
464#if defined(OS_CHROMEOS)
465DevicePolicyStatusProvider::DevicePolicyStatusProvider(
466    policy::BrowserPolicyConnectorChromeOS* connector)
467      : CloudPolicyCoreStatusProvider(
468            connector->GetDeviceCloudPolicyManager()->core()) {
469  domain_ = connector->GetEnterpriseDomain();
470}
471
472DevicePolicyStatusProvider::~DevicePolicyStatusProvider() {
473}
474
475void DevicePolicyStatusProvider::GetStatus(base::DictionaryValue* dict) {
476  GetStatusFromCore(core_, dict);
477  dict->SetString("domain", domain_);
478}
479
480DeviceLocalAccountPolicyStatusProvider::DeviceLocalAccountPolicyStatusProvider(
481    const std::string& user_id,
482    policy::DeviceLocalAccountPolicyService* service)
483      : user_id_(user_id),
484        service_(service) {
485  service_->AddObserver(this);
486}
487
488DeviceLocalAccountPolicyStatusProvider::
489    ~DeviceLocalAccountPolicyStatusProvider() {
490  service_->RemoveObserver(this);
491}
492
493void DeviceLocalAccountPolicyStatusProvider::GetStatus(
494    base::DictionaryValue* dict) {
495  const policy::DeviceLocalAccountPolicyBroker* broker =
496      service_->GetBrokerForUser(user_id_);
497  if (broker) {
498    GetStatusFromCore(broker->core(), dict);
499  } else {
500    dict->SetBoolean("error", true);
501    dict->SetString("status",
502                    policy::FormatStoreStatus(
503                        policy::CloudPolicyStore::STATUS_BAD_STATE,
504                        policy::CloudPolicyValidatorBase::VALIDATION_OK));
505    dict->SetString("username", std::string());
506  }
507  ExtractDomainFromUsername(dict);
508  dict->SetBoolean("publicAccount", true);
509}
510
511void DeviceLocalAccountPolicyStatusProvider::OnPolicyUpdated(
512    const std::string& user_id) {
513  if (user_id == user_id_)
514    NotifyStatusChange();
515}
516
517void DeviceLocalAccountPolicyStatusProvider::OnDeviceLocalAccountsChanged() {
518  NotifyStatusChange();
519}
520#endif
521
522PolicyUIHandler::PolicyUIHandler()
523    : initialized_(false),
524      weak_factory_(this) {
525}
526
527PolicyUIHandler::~PolicyUIHandler() {
528  GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
529  GetPolicyService()->RemoveObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
530}
531
532void PolicyUIHandler::RegisterMessages() {
533#if defined(OS_CHROMEOS)
534  policy::BrowserPolicyConnectorChromeOS* connector =
535      g_browser_process->platform_part()->browser_policy_connector_chromeos();
536  if (connector->IsEnterpriseManaged())
537    device_status_provider_.reset(new DevicePolicyStatusProvider(connector));
538
539  const user_manager::UserManager* user_manager =
540      user_manager::UserManager::Get();
541  if (user_manager->IsLoggedInAsPublicAccount()) {
542    policy::DeviceLocalAccountPolicyService* local_account_service =
543        connector->GetDeviceLocalAccountPolicyService();
544    if (local_account_service) {
545      user_status_provider_.reset(
546          new DeviceLocalAccountPolicyStatusProvider(
547              user_manager->GetLoggedInUser()->email(), local_account_service));
548    }
549  } else {
550    policy::UserCloudPolicyManagerChromeOS* user_cloud_policy_manager =
551        policy::UserCloudPolicyManagerFactoryChromeOS::GetForProfile(
552            Profile::FromWebUI(web_ui()));
553    if (user_cloud_policy_manager) {
554      user_status_provider_.reset(
555          new UserPolicyStatusProvider(user_cloud_policy_manager->core()));
556    }
557  }
558#else
559  policy::UserCloudPolicyManager* user_cloud_policy_manager =
560      policy::UserCloudPolicyManagerFactory::GetForBrowserContext(
561          web_ui()->GetWebContents()->GetBrowserContext());
562  if (user_cloud_policy_manager) {
563    user_status_provider_.reset(
564        new UserPolicyStatusProvider(user_cloud_policy_manager->core()));
565  }
566#endif
567
568  if (!user_status_provider_.get())
569    user_status_provider_.reset(new CloudPolicyStatusProvider());
570  if (!device_status_provider_.get())
571    device_status_provider_.reset(new CloudPolicyStatusProvider());
572
573  base::Closure update_callback(base::Bind(&PolicyUIHandler::SendStatus,
574                                           base::Unretained(this)));
575  user_status_provider_->SetStatusChangeCallback(update_callback);
576  device_status_provider_->SetStatusChangeCallback(update_callback);
577  GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
578  GetPolicyService()->AddObserver(policy::POLICY_DOMAIN_EXTENSIONS, this);
579
580  registrar_.Add(this,
581                 extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED,
582                 content::NotificationService::AllSources());
583  registrar_.Add(this,
584                 extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
585                 content::NotificationService::AllSources());
586
587  web_ui()->RegisterMessageCallback(
588      "initialized",
589      base::Bind(&PolicyUIHandler::HandleInitialized, base::Unretained(this)));
590  web_ui()->RegisterMessageCallback(
591      "reloadPolicies",
592      base::Bind(&PolicyUIHandler::HandleReloadPolicies,
593                 base::Unretained(this)));
594}
595
596void PolicyUIHandler::Observe(int type,
597                              const content::NotificationSource& source,
598                              const content::NotificationDetails& details) {
599  DCHECK(type == extensions::NOTIFICATION_EXTENSION_LOADED_DEPRECATED ||
600         type == extensions::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED);
601  SendPolicyNames();
602  SendPolicyValues();
603}
604
605void PolicyUIHandler::OnPolicyUpdated(const policy::PolicyNamespace& ns,
606                                      const policy::PolicyMap& previous,
607                                      const policy::PolicyMap& current) {
608  SendPolicyValues();
609}
610
611void PolicyUIHandler::SendPolicyNames() const {
612  base::DictionaryValue names;
613
614  Profile* profile = Profile::FromWebUI(web_ui());
615  policy::SchemaRegistry* registry =
616      policy::SchemaRegistryServiceFactory::GetForContext(
617          profile->GetOriginalProfile())->registry();
618  scoped_refptr<policy::SchemaMap> schema_map = registry->schema_map();
619
620  // Add Chrome policy names.
621  base::DictionaryValue* chrome_policy_names = new base::DictionaryValue;
622  policy::PolicyNamespace chrome_ns(policy::POLICY_DOMAIN_CHROME, "");
623  const policy::Schema* chrome_schema = schema_map->GetSchema(chrome_ns);
624  for (policy::Schema::Iterator it = chrome_schema->GetPropertiesIterator();
625       !it.IsAtEnd(); it.Advance()) {
626    chrome_policy_names->SetBoolean(it.key(), true);
627  }
628  names.Set("chromePolicyNames", chrome_policy_names);
629
630#if !defined(OS_ANDROID) && !defined(OS_IOS)
631  // Add extension policy names.
632  base::DictionaryValue* extension_policy_names = new base::DictionaryValue;
633
634  extensions::ExtensionSystem* extension_system =
635      extensions::ExtensionSystem::Get(profile);
636  const extensions::ExtensionSet* extensions =
637      extension_system->extension_service()->extensions();
638
639  for (extensions::ExtensionSet::const_iterator it = extensions->begin();
640       it != extensions->end(); ++it) {
641    const extensions::Extension* extension = it->get();
642    // Skip this extension if it's not an enterprise extension.
643    if (!extension->manifest()->HasPath(
644        extensions::manifest_keys::kStorageManagedSchema))
645      continue;
646    base::DictionaryValue* extension_value = new base::DictionaryValue;
647    extension_value->SetString("name", extension->name());
648    const policy::Schema* schema =
649        schema_map->GetSchema(policy::PolicyNamespace(
650            policy::POLICY_DOMAIN_EXTENSIONS, extension->id()));
651    base::DictionaryValue* policy_names = new base::DictionaryValue;
652    if (schema && schema->valid()) {
653      // Get policy names from the extension's policy schema.
654      // Store in a map, not an array, for faster lookup on JS side.
655      for (policy::Schema::Iterator prop = schema->GetPropertiesIterator();
656           !prop.IsAtEnd(); prop.Advance()) {
657        policy_names->SetBoolean(prop.key(), true);
658      }
659    }
660    extension_value->Set("policyNames", policy_names);
661    extension_policy_names->Set(extension->id(), extension_value);
662  }
663  names.Set("extensionPolicyNames", extension_policy_names);
664#endif
665
666  web_ui()->CallJavascriptFunction("policy.Page.setPolicyNames", names);
667}
668
669void PolicyUIHandler::SendPolicyValues() const {
670  base::DictionaryValue all_policies;
671
672  // Add Chrome policy values.
673  base::DictionaryValue* chrome_policies = new base::DictionaryValue;
674  GetChromePolicyValues(chrome_policies);
675  all_policies.Set("chromePolicies", chrome_policies);
676
677#if !defined(OS_ANDROID) && !defined(OS_IOS)
678  // Add extension policy values.
679  extensions::ExtensionSystem* extension_system =
680      extensions::ExtensionSystem::Get(Profile::FromWebUI(web_ui()));
681  const extensions::ExtensionSet* extensions =
682      extension_system->extension_service()->extensions();
683  base::DictionaryValue* extension_values = new base::DictionaryValue;
684
685  for (extensions::ExtensionSet::const_iterator it = extensions->begin();
686       it != extensions->end(); ++it) {
687    const extensions::Extension* extension = it->get();
688    // Skip this extension if it's not an enterprise extension.
689    if (!extension->manifest()->HasPath(
690        extensions::manifest_keys::kStorageManagedSchema))
691      continue;
692    base::DictionaryValue* extension_policies = new base::DictionaryValue;
693    policy::PolicyNamespace policy_namespace = policy::PolicyNamespace(
694        policy::POLICY_DOMAIN_EXTENSIONS, extension->id());
695    policy::PolicyErrorMap empty_error_map;
696    GetPolicyValues(GetPolicyService()->GetPolicies(policy_namespace),
697                    &empty_error_map, extension_policies);
698    extension_values->Set(extension->id(), extension_policies);
699  }
700  all_policies.Set("extensionPolicies", extension_values);
701#endif
702  web_ui()->CallJavascriptFunction("policy.Page.setPolicyValues", all_policies);
703}
704
705void PolicyUIHandler::GetPolicyValues(const policy::PolicyMap& map,
706                                      policy::PolicyErrorMap* errors,
707                                      base::DictionaryValue* values) const {
708  for (policy::PolicyMap::const_iterator entry = map.begin();
709       entry != map.end(); ++entry) {
710    base::DictionaryValue* value = new base::DictionaryValue;
711    value->Set("value", CopyAndConvert(entry->second.value).release());
712    if (entry->second.scope == policy::POLICY_SCOPE_USER)
713      value->SetString("scope", "user");
714    else
715      value->SetString("scope", "machine");
716    if (entry->second.level == policy::POLICY_LEVEL_RECOMMENDED)
717      value->SetString("level", "recommended");
718    else
719      value->SetString("level", "mandatory");
720    base::string16 error = errors->GetErrors(entry->first);
721    if (!error.empty())
722      value->SetString("error", error);
723    values->Set(entry->first, value);
724  }
725}
726
727void PolicyUIHandler::GetChromePolicyValues(
728    base::DictionaryValue* values) const {
729  policy::PolicyService* policy_service = GetPolicyService();
730  policy::PolicyMap map;
731
732  // Make a copy that can be modified, since some policy values are modified
733  // before being displayed.
734  map.CopyFrom(policy_service->GetPolicies(
735      policy::PolicyNamespace(policy::POLICY_DOMAIN_CHROME, std::string())));
736
737  // Get a list of all the errors in the policy values.
738  const policy::ConfigurationPolicyHandlerList* handler_list =
739      g_browser_process->browser_policy_connector()->GetHandlerList();
740  policy::PolicyErrorMap errors;
741  handler_list->ApplyPolicySettings(map, NULL, &errors);
742
743  // Convert dictionary values to strings for display.
744  handler_list->PrepareForDisplaying(&map);
745
746  GetPolicyValues(map, &errors, values);
747}
748
749void PolicyUIHandler::SendStatus() const {
750  scoped_ptr<base::DictionaryValue> device_status(new base::DictionaryValue);
751  device_status_provider_->GetStatus(device_status.get());
752  if (!device_domain_.empty())
753    device_status->SetString("domain", device_domain_);
754  scoped_ptr<base::DictionaryValue> user_status(new base::DictionaryValue);
755  user_status_provider_->GetStatus(user_status.get());
756  std::string username;
757  user_status->GetString("username", &username);
758  if (!username.empty())
759    user_status->SetString("domain", gaia::ExtractDomainName(username));
760
761  base::DictionaryValue status;
762  if (!device_status->empty())
763    status.Set("device", device_status.release());
764  if (!user_status->empty())
765    status.Set("user", user_status.release());
766
767  web_ui()->CallJavascriptFunction("policy.Page.setStatus", status);
768}
769
770void PolicyUIHandler::HandleInitialized(const base::ListValue* args) {
771  SendPolicyNames();
772  SendPolicyValues();
773  SendStatus();
774}
775
776void PolicyUIHandler::HandleReloadPolicies(const base::ListValue* args) {
777  GetPolicyService()->RefreshPolicies(
778      base::Bind(&PolicyUIHandler::OnRefreshPoliciesDone,
779                 weak_factory_.GetWeakPtr()));
780}
781
782void PolicyUIHandler::OnRefreshPoliciesDone() const {
783  web_ui()->CallJavascriptFunction("policy.Page.reloadPoliciesDone");
784}
785
786policy::PolicyService* PolicyUIHandler::GetPolicyService() const {
787  return policy::ProfilePolicyConnectorFactory::GetForProfile(
788      Profile::FromWebUI(web_ui()))->policy_service();
789}
790
791PolicyUI::PolicyUI(content::WebUI* web_ui) : WebUIController(web_ui) {
792  web_ui->AddMessageHandler(new PolicyUIHandler);
793  content::WebUIDataSource::Add(Profile::FromWebUI(web_ui),
794                                CreatePolicyUIHTMLSource());
795}
796
797PolicyUI::~PolicyUI() {
798}
799