Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / policy / device_local_account_policy_service.cc
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/chromeos/policy/device_local_account_policy_service.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/files/file_enumerator.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/path_service.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/chromeos/policy/device_local_account.h"
23 #include "chrome/browser/chromeos/policy/device_local_account_external_data_service.h"
24 #include "chrome/browser/chromeos/policy/device_local_account_policy_store.h"
25 #include "chrome/browser/chromeos/settings/device_settings_service.h"
26 #include "chrome/common/chrome_content_client.h"
27 #include "chromeos/chromeos_paths.h"
28 #include "chromeos/dbus/session_manager_client.h"
29 #include "chromeos/settings/cros_settings_names.h"
30 #include "chromeos/settings/cros_settings_provider.h"
31 #include "components/policy/core/browser/browser_policy_connector.h"
32 #include "components/policy/core/common/cloud/cloud_policy_client.h"
33 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
34 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
35 #include "components/policy/core/common/cloud/device_management_service.h"
36 #include "components/policy/core/common/cloud/resource_cache.h"
37 #include "components/policy/core/common/cloud/system_policy_request_context.h"
38 #include "components/policy/core/common/policy_namespace.h"
39 #include "components/policy/core/common/policy_switches.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "net/url_request/url_request_context_getter.h"
42 #include "policy/policy_constants.h"
43 #include "policy/proto/device_management_backend.pb.h"
44 #include "url/gurl.h"
45
46 namespace em = enterprise_management;
47
48 namespace policy {
49
50 namespace {
51
52 // Creates and initializes a cloud policy client. Returns NULL if the device
53 // doesn't have credentials in device settings (i.e. is not
54 // enterprise-enrolled).
55 scoped_ptr<CloudPolicyClient> CreateClient(
56     chromeos::DeviceSettingsService* device_settings_service,
57     DeviceManagementService* device_management_service,
58     scoped_refptr<net::URLRequestContextGetter> system_request_context) {
59   const em::PolicyData* policy_data = device_settings_service->policy_data();
60   if (!policy_data ||
61       !policy_data->has_request_token() ||
62       !policy_data->has_device_id() ||
63       !device_management_service) {
64     return scoped_ptr<CloudPolicyClient>();
65   }
66
67   scoped_refptr<net::URLRequestContextGetter> request_context =
68       new SystemPolicyRequestContext(
69           system_request_context, GetUserAgent());
70
71   scoped_ptr<CloudPolicyClient> client(
72       new CloudPolicyClient(std::string(), std::string(),
73                             kPolicyVerificationKeyHash,
74                             USER_AFFILIATION_MANAGED,
75                             NULL, device_management_service, request_context));
76   client->SetupRegistration(policy_data->request_token(),
77                             policy_data->device_id());
78   return client.Pass();
79 }
80
81 // Get the subdirectory of the force-installed extension cache and the component
82 // policy cache used for |account_id|.
83 std::string GetCacheSubdirectoryForAccountID(const std::string& account_id) {
84   return base::HexEncode(account_id.c_str(), account_id.size());
85 }
86
87 // Cleans up the cache directory by removing subdirectories that are not found
88 // in |subdirectories_to_keep|. Only caches whose cache directory is found in
89 // |subdirectories_to_keep| may be running while the clean-up is in progress.
90 void DeleteOrphanedCaches(
91     const base::FilePath& cache_root_dir,
92     const std::set<std::string>& subdirectories_to_keep) {
93   base::FileEnumerator enumerator(cache_root_dir,
94                                   false,
95                                   base::FileEnumerator::DIRECTORIES);
96   for (base::FilePath path = enumerator.Next(); !path.empty();
97        path = enumerator.Next()) {
98     const std::string subdirectory(path.BaseName().MaybeAsASCII());
99     if (!ContainsKey(subdirectories_to_keep, subdirectory))
100       base::DeleteFile(path, true);
101   }
102 }
103
104 // Removes the subdirectory belonging to |account_id_to_delete| from the cache
105 // directory. No cache belonging to |account_id_to_delete| may be running while
106 // the removal is in progress.
107 void DeleteObsoleteExtensionCache(const std::string& account_id_to_delete) {
108   base::FilePath cache_root_dir;
109   CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
110                          &cache_root_dir));
111   const base::FilePath path = cache_root_dir.Append(
112       GetCacheSubdirectoryForAccountID(account_id_to_delete));
113   if (base::DirectoryExists(path))
114     base::DeleteFile(path, true);
115 }
116
117 }  // namespace
118
119 DeviceLocalAccountPolicyBroker::DeviceLocalAccountPolicyBroker(
120     const DeviceLocalAccount& account,
121     const base::FilePath& component_policy_cache_path,
122     scoped_ptr<DeviceLocalAccountPolicyStore> store,
123     scoped_refptr<DeviceLocalAccountExternalDataManager> external_data_manager,
124     const base::Closure& policy_update_callback,
125     const scoped_refptr<base::SequencedTaskRunner>& task_runner)
126     : account_id_(account.account_id),
127       user_id_(account.user_id),
128       component_policy_cache_path_(component_policy_cache_path),
129       store_(store.Pass()),
130       extension_tracker_(account, store_.get(), &schema_registry_),
131       external_data_manager_(external_data_manager),
132       core_(PolicyNamespaceKey(dm_protocol::kChromePublicAccountPolicyType,
133                                store_->account_id()),
134             store_.get(),
135             task_runner),
136       policy_update_callback_(policy_update_callback) {
137   base::FilePath cache_root_dir;
138   CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
139                          &cache_root_dir));
140   extension_loader_ = new chromeos::DeviceLocalAccountExternalPolicyLoader(
141       store_.get(),
142       cache_root_dir.Append(
143           GetCacheSubdirectoryForAccountID(account.account_id)));
144   store_->AddObserver(this);
145
146   // Unblock the |schema_registry_| so that the |component_policy_service_|
147   // starts using it.
148   schema_registry_.RegisterComponent(
149       PolicyNamespace(POLICY_DOMAIN_CHROME, ""),
150       g_browser_process->browser_policy_connector()->GetChromeSchema());
151   schema_registry_.SetReady(POLICY_DOMAIN_CHROME);
152   schema_registry_.SetReady(POLICY_DOMAIN_EXTENSIONS);
153 }
154
155 DeviceLocalAccountPolicyBroker::~DeviceLocalAccountPolicyBroker() {
156   store_->RemoveObserver(this);
157   external_data_manager_->SetPolicyStore(NULL);
158   external_data_manager_->Disconnect();
159 }
160
161 void DeviceLocalAccountPolicyBroker::Initialize() {
162   store_->Load();
163 }
164
165 void DeviceLocalAccountPolicyBroker::ConnectIfPossible(
166     chromeos::DeviceSettingsService* device_settings_service,
167     DeviceManagementService* device_management_service,
168     scoped_refptr<net::URLRequestContextGetter> request_context) {
169   if (core_.client())
170     return;
171
172   scoped_ptr<CloudPolicyClient> client(CreateClient(device_settings_service,
173                                                     device_management_service,
174                                                     request_context));
175   if (!client)
176     return;
177
178   core_.Connect(client.Pass());
179   external_data_manager_->Connect(request_context);
180   core_.StartRefreshScheduler();
181   UpdateRefreshDelay();
182   CreateComponentCloudPolicyService(request_context);
183 }
184
185 void DeviceLocalAccountPolicyBroker::UpdateRefreshDelay() {
186   if (core_.refresh_scheduler()) {
187     const base::Value* policy_value =
188         store_->policy_map().GetValue(key::kPolicyRefreshRate);
189     int delay = 0;
190     if (policy_value && policy_value->GetAsInteger(&delay))
191       core_.refresh_scheduler()->SetRefreshDelay(delay);
192   }
193 }
194
195 std::string DeviceLocalAccountPolicyBroker::GetDisplayName() const {
196   std::string display_name;
197   const base::Value* display_name_value =
198       store_->policy_map().GetValue(policy::key::kUserDisplayName);
199   if (display_name_value)
200     display_name_value->GetAsString(&display_name);
201   return display_name;
202 }
203
204 void DeviceLocalAccountPolicyBroker::OnStoreLoaded(CloudPolicyStore* store) {
205   UpdateRefreshDelay();
206   policy_update_callback_.Run();
207 }
208
209 void DeviceLocalAccountPolicyBroker::OnStoreError(CloudPolicyStore* store) {
210   policy_update_callback_.Run();
211 }
212
213 void DeviceLocalAccountPolicyBroker::OnComponentCloudPolicyUpdated() {
214   policy_update_callback_.Run();
215 }
216
217 void DeviceLocalAccountPolicyBroker::CreateComponentCloudPolicyService(
218     const scoped_refptr<net::URLRequestContextGetter>& request_context) {
219   if (CommandLine::ForCurrentProcess()->HasSwitch(
220           switches::kDisableComponentCloudPolicy)) {
221     // Disabled via the command line.
222     return;
223   }
224
225   scoped_ptr<ResourceCache> resource_cache(
226       new ResourceCache(component_policy_cache_path_,
227                         content::BrowserThread::GetMessageLoopProxyForThread(
228                             content::BrowserThread::FILE)));
229
230   component_policy_service_.reset(new ComponentCloudPolicyService(
231       this,
232       &schema_registry_,
233       core(),
234       resource_cache.Pass(),
235       request_context,
236       content::BrowserThread::GetMessageLoopProxyForThread(
237           content::BrowserThread::FILE),
238       content::BrowserThread::GetMessageLoopProxyForThread(
239           content::BrowserThread::IO)));
240 }
241
242 DeviceLocalAccountPolicyService::DeviceLocalAccountPolicyService(
243     chromeos::SessionManagerClient* session_manager_client,
244     chromeos::DeviceSettingsService* device_settings_service,
245     chromeos::CrosSettings* cros_settings,
246     scoped_refptr<base::SequencedTaskRunner> store_background_task_runner,
247     scoped_refptr<base::SequencedTaskRunner> extension_cache_task_runner,
248     scoped_refptr<base::SequencedTaskRunner>
249         external_data_service_backend_task_runner,
250     scoped_refptr<base::SequencedTaskRunner> io_task_runner,
251     scoped_refptr<net::URLRequestContextGetter> request_context)
252     : session_manager_client_(session_manager_client),
253       device_settings_service_(device_settings_service),
254       cros_settings_(cros_settings),
255       device_management_service_(NULL),
256       waiting_for_cros_settings_(false),
257       orphan_extension_cache_deletion_state_(NOT_STARTED),
258       store_background_task_runner_(store_background_task_runner),
259       extension_cache_task_runner_(extension_cache_task_runner),
260       request_context_(request_context),
261       local_accounts_subscription_(cros_settings_->AddSettingsObserver(
262           chromeos::kAccountsPrefDeviceLocalAccounts,
263           base::Bind(&DeviceLocalAccountPolicyService::
264                          UpdateAccountListIfNonePending,
265                      base::Unretained(this)))),
266       weak_factory_(this) {
267   CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_COMPONENT_POLICY,
268                          &component_policy_cache_root_));
269   external_data_service_.reset(new DeviceLocalAccountExternalDataService(
270       this,
271       external_data_service_backend_task_runner,
272       io_task_runner));
273   UpdateAccountList();
274 }
275
276 DeviceLocalAccountPolicyService::~DeviceLocalAccountPolicyService() {
277   DCHECK(!request_context_);
278   DCHECK(policy_brokers_.empty());
279 }
280
281 void DeviceLocalAccountPolicyService::Shutdown() {
282   device_management_service_ = NULL;
283   request_context_ = NULL;
284   DeleteBrokers(&policy_brokers_);
285 }
286
287 void DeviceLocalAccountPolicyService::Connect(
288     DeviceManagementService* device_management_service) {
289   DCHECK(!device_management_service_);
290   device_management_service_ = device_management_service;
291
292   // Connect the brokers.
293   for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
294        it != policy_brokers_.end(); ++it) {
295     it->second->ConnectIfPossible(device_settings_service_,
296                                   device_management_service_,
297                                   request_context_);
298   }
299 }
300
301 DeviceLocalAccountPolicyBroker*
302     DeviceLocalAccountPolicyService::GetBrokerForUser(
303         const std::string& user_id) {
304   PolicyBrokerMap::iterator entry = policy_brokers_.find(user_id);
305   if (entry == policy_brokers_.end())
306     return NULL;
307
308   return entry->second;
309 }
310
311 bool DeviceLocalAccountPolicyService::IsPolicyAvailableForUser(
312     const std::string& user_id) {
313   DeviceLocalAccountPolicyBroker* broker = GetBrokerForUser(user_id);
314   return broker && broker->core()->store()->is_managed();
315 }
316
317 void DeviceLocalAccountPolicyService::AddObserver(Observer* observer) {
318   observers_.AddObserver(observer);
319 }
320
321 void DeviceLocalAccountPolicyService::RemoveObserver(Observer* observer) {
322   observers_.RemoveObserver(observer);
323 }
324
325 bool DeviceLocalAccountPolicyService::IsExtensionCacheDirectoryBusy(
326     const std::string& account_id) {
327   return busy_extension_cache_directories_.find(account_id) !=
328             busy_extension_cache_directories_.end();
329 }
330
331 void DeviceLocalAccountPolicyService::StartExtensionCachesIfPossible() {
332   for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
333        it != policy_brokers_.end(); ++it) {
334     if (!it->second->extension_loader()->IsCacheRunning() &&
335         !IsExtensionCacheDirectoryBusy(it->second->account_id())) {
336       it->second->extension_loader()->StartCache(extension_cache_task_runner_);
337     }
338   }
339 }
340
341 bool DeviceLocalAccountPolicyService::StartExtensionCacheForAccountIfPresent(
342     const std::string& account_id) {
343   for (PolicyBrokerMap::iterator it = policy_brokers_.begin();
344        it != policy_brokers_.end(); ++it) {
345     if (it->second->account_id() == account_id) {
346       DCHECK(!it->second->extension_loader()->IsCacheRunning());
347       it->second->extension_loader()->StartCache(extension_cache_task_runner_);
348       return true;
349     }
350   }
351   return false;
352 }
353
354 void DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted() {
355   DCHECK_EQ(IN_PROGRESS, orphan_extension_cache_deletion_state_);
356
357   orphan_extension_cache_deletion_state_ = DONE;
358   StartExtensionCachesIfPossible();
359 }
360
361 void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown(
362     const std::string& account_id) {
363   DCHECK_NE(NOT_STARTED, orphan_extension_cache_deletion_state_);
364   DCHECK(IsExtensionCacheDirectoryBusy(account_id));
365
366   // The account with |account_id| was deleted and the broker for it has shut
367   // down completely.
368
369   if (StartExtensionCacheForAccountIfPresent(account_id)) {
370     // If another account with the same ID was created in the meantime, its
371     // extension cache is started, reusing the cache directory. The directory no
372     // longer needs to be marked as busy in this case.
373     busy_extension_cache_directories_.erase(account_id);
374     return;
375   }
376
377   // If no account with |account_id| exists anymore, the cache directory should
378   // be removed. The directory must stay marked as busy while the removal is in
379   // progress.
380   extension_cache_task_runner_->PostTaskAndReply(
381       FROM_HERE,
382       base::Bind(&DeleteObsoleteExtensionCache, account_id),
383       base::Bind(&DeviceLocalAccountPolicyService::
384                      OnObsoleteExtensionCacheDeleted,
385                  weak_factory_.GetWeakPtr(),
386                  account_id));
387 }
388
389 void DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheDeleted(
390     const std::string& account_id) {
391   DCHECK_EQ(DONE, orphan_extension_cache_deletion_state_);
392   DCHECK(IsExtensionCacheDirectoryBusy(account_id));
393
394   // The cache directory for |account_id| has been deleted. The directory no
395   // longer needs to be marked as busy.
396   busy_extension_cache_directories_.erase(account_id);
397
398   // If another account with the same ID was created in the meantime, start its
399   // extension cache, creating a new cache directory.
400   StartExtensionCacheForAccountIfPresent(account_id);
401 }
402
403 void DeviceLocalAccountPolicyService::UpdateAccountListIfNonePending() {
404   // Avoid unnecessary calls to UpdateAccountList(): If an earlier call is still
405   // pending (because the |cros_settings_| are not trusted yet), the updated
406   // account list will be processed by that call when it eventually runs.
407   if (!waiting_for_cros_settings_)
408     UpdateAccountList();
409 }
410
411 void DeviceLocalAccountPolicyService::UpdateAccountList() {
412   chromeos::CrosSettingsProvider::TrustedStatus status =
413       cros_settings_->PrepareTrustedValues(
414           base::Bind(&DeviceLocalAccountPolicyService::UpdateAccountList,
415                      weak_factory_.GetWeakPtr()));
416   switch (status) {
417     case chromeos::CrosSettingsProvider::TRUSTED:
418       waiting_for_cros_settings_ = false;
419       break;
420     case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED:
421       waiting_for_cros_settings_ = true;
422       return;
423     case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED:
424       waiting_for_cros_settings_ = false;
425       return;
426   }
427
428   // Update |policy_brokers_|, keeping existing entries.
429   PolicyBrokerMap old_policy_brokers;
430   policy_brokers_.swap(old_policy_brokers);
431   std::set<std::string> subdirectories_to_keep;
432   const std::vector<DeviceLocalAccount> device_local_accounts =
433       GetDeviceLocalAccounts(cros_settings_);
434   for (std::vector<DeviceLocalAccount>::const_iterator it =
435            device_local_accounts.begin();
436        it != device_local_accounts.end(); ++it) {
437     PolicyBrokerMap::iterator broker_it = old_policy_brokers.find(it->user_id);
438
439     scoped_ptr<DeviceLocalAccountPolicyBroker> broker;
440     bool broker_initialized = false;
441     if (broker_it != old_policy_brokers.end()) {
442       // Reuse the existing broker if present.
443       broker.reset(broker_it->second);
444       old_policy_brokers.erase(broker_it);
445       broker_initialized = true;
446     } else {
447       scoped_ptr<DeviceLocalAccountPolicyStore> store(
448           new DeviceLocalAccountPolicyStore(it->account_id,
449                                             session_manager_client_,
450                                             device_settings_service_,
451                                             store_background_task_runner_));
452       scoped_refptr<DeviceLocalAccountExternalDataManager>
453           external_data_manager =
454               external_data_service_->GetExternalDataManager(it->account_id,
455                                                              store.get());
456       broker.reset(new DeviceLocalAccountPolicyBroker(
457           *it,
458           component_policy_cache_root_.Append(
459               GetCacheSubdirectoryForAccountID(it->account_id)),
460           store.Pass(),
461           external_data_manager,
462           base::Bind(&DeviceLocalAccountPolicyService::NotifyPolicyUpdated,
463                      base::Unretained(this),
464                      it->user_id),
465           base::MessageLoopProxy::current()));
466     }
467
468     // Fire up the cloud connection for fetching policy for the account from
469     // the cloud if this is an enterprise-managed device.
470     broker->ConnectIfPossible(device_settings_service_,
471                               device_management_service_,
472                               request_context_);
473
474     policy_brokers_[it->user_id] = broker.release();
475     if (!broker_initialized) {
476       // The broker must be initialized after it has been added to
477       // |policy_brokers_|.
478       policy_brokers_[it->user_id]->Initialize();
479     }
480
481     subdirectories_to_keep.insert(
482         GetCacheSubdirectoryForAccountID(it->account_id));
483   }
484
485   if (orphan_extension_cache_deletion_state_ == NOT_STARTED) {
486     DCHECK(old_policy_brokers.empty());
487     DCHECK(busy_extension_cache_directories_.empty());
488
489     // If this method is running for the first time, no extension caches have
490     // been started yet. Take this opportunity to do a clean-up by removing
491     // orphaned cache directories not found in |subdirectories_to_keep| from the
492     // cache directory.
493     orphan_extension_cache_deletion_state_ = IN_PROGRESS;
494
495     base::FilePath cache_root_dir;
496     CHECK(PathService::Get(chromeos::DIR_DEVICE_LOCAL_ACCOUNT_EXTENSIONS,
497                            &cache_root_dir));
498     extension_cache_task_runner_->PostTaskAndReply(
499         FROM_HERE,
500         base::Bind(
501             &DeleteOrphanedCaches, cache_root_dir, subdirectories_to_keep),
502         base::Bind(
503             &DeviceLocalAccountPolicyService::OnOrphanedExtensionCachesDeleted,
504             weak_factory_.GetWeakPtr()));
505
506     // Start the extension caches for all brokers. These belong to accounts in
507     // |account_ids| and are not affected by the clean-up.
508     StartExtensionCachesIfPossible();
509   } else {
510     // If this method has run before, obsolete brokers may exist. Shut down
511     // their extension caches and delete the brokers.
512     DeleteBrokers(&old_policy_brokers);
513
514     if (orphan_extension_cache_deletion_state_ == DONE) {
515       // If the initial clean-up of orphaned cache directories has been
516       // complete, start any extension caches that are not running yet but can
517       // be started now because their cache directories are not busy.
518       StartExtensionCachesIfPossible();
519     }
520   }
521
522   // Purge the component policy caches of any accounts that have been removed.
523   // Do this only after any obsolete brokers have been destroyed.
524   // TODO(joaodasilva): for now this must be posted to the FILE thread,
525   // to avoid racing with the ComponentCloudPolicyStore. Use a task runner
526   // once that class supports another background thread too.
527   content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
528                                    base::Bind(&DeleteOrphanedCaches,
529                                               component_policy_cache_root_,
530                                               subdirectories_to_keep));
531
532   FOR_EACH_OBSERVER(Observer, observers_, OnDeviceLocalAccountsChanged());
533 }
534
535 void DeviceLocalAccountPolicyService::DeleteBrokers(PolicyBrokerMap* map) {
536   for (PolicyBrokerMap::iterator it = map->begin(); it != map->end(); ++it) {
537     scoped_refptr<chromeos::DeviceLocalAccountExternalPolicyLoader>
538         extension_loader = it->second->extension_loader();
539     if (extension_loader->IsCacheRunning()) {
540       DCHECK(!IsExtensionCacheDirectoryBusy(it->second->account_id()));
541       busy_extension_cache_directories_.insert(it->second->account_id());
542       extension_loader->StopCache(base::Bind(
543           &DeviceLocalAccountPolicyService::OnObsoleteExtensionCacheShutdown,
544           weak_factory_.GetWeakPtr(),
545           it->second->account_id()));
546     }
547
548     delete it->second;
549   }
550   map->clear();
551 }
552
553 DeviceLocalAccountPolicyBroker*
554     DeviceLocalAccountPolicyService::GetBrokerForStore(
555         CloudPolicyStore* store) {
556   for (PolicyBrokerMap::iterator it(policy_brokers_.begin());
557        it != policy_brokers_.end(); ++it) {
558     if (it->second->core()->store() == store)
559       return it->second;
560   }
561   return NULL;
562 }
563
564 void DeviceLocalAccountPolicyService::NotifyPolicyUpdated(
565     const std::string& user_id) {
566   FOR_EACH_OBSERVER(Observer, observers_, OnPolicyUpdated(user_id));
567 }
568
569 }  // namespace policy