1 // Copyright 2013 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 "components/policy/core/common/schema_registry.h"
6 
7 #include "base/logging.h"
8 #include "extensions/buildflags/buildflags.h"
9 
10 namespace policy {
11 
~Observer()12 SchemaRegistry::Observer::~Observer() {}
13 
~InternalObserver()14 SchemaRegistry::InternalObserver::~InternalObserver() {}
15 
SchemaRegistry()16 SchemaRegistry::SchemaRegistry() : schema_map_(new SchemaMap) {
17   for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
18     domains_ready_[i] = false;
19 #if !BUILDFLAG(ENABLE_EXTENSIONS)
20   SetExtensionsDomainsReady();
21 #endif
22 }
23 
~SchemaRegistry()24 SchemaRegistry::~SchemaRegistry() {
25   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
26   for (auto& observer : internal_observers_)
27     observer.OnSchemaRegistryShuttingDown(this);
28 }
29 
RegisterComponent(const PolicyNamespace & ns,const Schema & schema)30 void SchemaRegistry::RegisterComponent(const PolicyNamespace& ns,
31                                        const Schema& schema) {
32   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
33   ComponentMap map;
34   map[ns.component_id] = schema;
35   RegisterComponents(ns.domain, map);
36 }
37 
RegisterComponents(PolicyDomain domain,const ComponentMap & components)38 void SchemaRegistry::RegisterComponents(PolicyDomain domain,
39                                         const ComponentMap& components) {
40   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
41   // Don't issue notifications if nothing is being registered.
42   if (components.empty())
43     return;
44   // Assume that a schema was updated if the namespace was already registered
45   // before.
46   DomainMap map(schema_map_->GetDomains());
47   for (ComponentMap::const_iterator it = components.begin();
48        it != components.end(); ++it) {
49     map[domain][it->first] = it->second;
50   }
51   schema_map_ = new SchemaMap(map);
52   Notify(true);
53 }
54 
UnregisterComponent(const PolicyNamespace & ns)55 void SchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
56   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
57   DomainMap map(schema_map_->GetDomains());
58   if (map[ns.domain].erase(ns.component_id) != 0) {
59     schema_map_ = new SchemaMap(map);
60     Notify(false);
61   } else {
62     NOTREACHED();
63   }
64 }
65 
IsReady() const66 bool SchemaRegistry::IsReady() const {
67   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
68   for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i) {
69     if (!domains_ready_[i])
70       return false;
71   }
72   return true;
73 }
74 
SetDomainReady(PolicyDomain domain)75 void SchemaRegistry::SetDomainReady(PolicyDomain domain) {
76   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
77   if (domains_ready_[domain])
78     return;
79   domains_ready_[domain] = true;
80   if (IsReady()) {
81     for (auto& observer : observers_)
82       observer.OnSchemaRegistryReady();
83   }
84 }
85 
SetAllDomainsReady()86 void SchemaRegistry::SetAllDomainsReady() {
87   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
88   for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i)
89     SetDomainReady(static_cast<PolicyDomain>(i));
90 }
91 
SetExtensionsDomainsReady()92 void SchemaRegistry::SetExtensionsDomainsReady() {
93   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
94   SetDomainReady(POLICY_DOMAIN_EXTENSIONS);
95   SetDomainReady(POLICY_DOMAIN_SIGNIN_EXTENSIONS);
96 }
97 
AddObserver(Observer * observer)98 void SchemaRegistry::AddObserver(Observer* observer) {
99   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
100   observers_.AddObserver(observer);
101 }
102 
RemoveObserver(Observer * observer)103 void SchemaRegistry::RemoveObserver(Observer* observer) {
104   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
105   observers_.RemoveObserver(observer);
106 }
107 
AddInternalObserver(InternalObserver * observer)108 void SchemaRegistry::AddInternalObserver(InternalObserver* observer) {
109   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
110   internal_observers_.AddObserver(observer);
111 }
112 
RemoveInternalObserver(InternalObserver * observer)113 void SchemaRegistry::RemoveInternalObserver(InternalObserver* observer) {
114   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
115   internal_observers_.RemoveObserver(observer);
116 }
117 
Notify(bool has_new_schemas)118 void SchemaRegistry::Notify(bool has_new_schemas) {
119   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
120   for (auto& observer : observers_)
121     observer.OnSchemaRegistryUpdated(has_new_schemas);
122 }
123 
CombinedSchemaRegistry()124 CombinedSchemaRegistry::CombinedSchemaRegistry()
125     : own_schema_map_(new SchemaMap) {
126   // The combined registry is always ready, since it can always start tracking
127   // another registry that is not ready yet and going from "ready" to "not
128   // ready" is not allowed.
129   SetAllDomainsReady();
130 }
131 
~CombinedSchemaRegistry()132 CombinedSchemaRegistry::~CombinedSchemaRegistry() {}
133 
Track(SchemaRegistry * registry)134 void CombinedSchemaRegistry::Track(SchemaRegistry* registry) {
135   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
136   registries_.insert(registry);
137   registry->AddObserver(this);
138   registry->AddInternalObserver(this);
139   // Recombine the maps only if the |registry| has any components other than
140   // POLICY_DOMAIN_CHROME.
141   if (registry->schema_map()->HasComponents())
142     Combine(true);
143 }
144 
RegisterComponents(PolicyDomain domain,const ComponentMap & components)145 void CombinedSchemaRegistry::RegisterComponents(
146     PolicyDomain domain,
147     const ComponentMap& components) {
148   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
149   DomainMap map(own_schema_map_->GetDomains());
150   for (ComponentMap::const_iterator it = components.begin();
151        it != components.end(); ++it) {
152     map[domain][it->first] = it->second;
153   }
154   own_schema_map_ = new SchemaMap(map);
155   Combine(true);
156 }
157 
UnregisterComponent(const PolicyNamespace & ns)158 void CombinedSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
159   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
160   DomainMap map(own_schema_map_->GetDomains());
161   if (map[ns.domain].erase(ns.component_id) != 0) {
162     own_schema_map_ = new SchemaMap(map);
163     Combine(false);
164   } else {
165     NOTREACHED();
166   }
167 }
168 
OnSchemaRegistryUpdated(bool has_new_schemas)169 void CombinedSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) {
170   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
171   Combine(has_new_schemas);
172 }
173 
OnSchemaRegistryShuttingDown(SchemaRegistry * registry)174 void CombinedSchemaRegistry::OnSchemaRegistryShuttingDown(
175     SchemaRegistry* registry) {
176   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
177   registry->RemoveObserver(this);
178   registry->RemoveInternalObserver(this);
179   if (registries_.erase(registry) != 0) {
180     if (registry->schema_map()->HasComponents())
181       Combine(false);
182   } else {
183     NOTREACHED();
184   }
185 }
186 
Combine(bool has_new_schemas)187 void CombinedSchemaRegistry::Combine(bool has_new_schemas) {
188   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
189   // If two registries publish a Schema for the same component then it's
190   // undefined which version gets in the combined registry.
191   //
192   // The common case is that both registries want policy for the same component,
193   // and the Schemas should be the same; in that case this makes no difference.
194   //
195   // But if the Schemas are different then one of the components is out of date.
196   // In that case the policy loaded will be valid only for one of them, until
197   // the outdated components are updated. This is a known limitation of the
198   // way policies are loaded currently, but isn't a problem worth fixing for
199   // the time being.
200   DomainMap map(own_schema_map_->GetDomains());
201   for (std::set<SchemaRegistry*>::const_iterator reg_it = registries_.begin();
202        reg_it != registries_.end(); ++reg_it) {
203     const DomainMap& reg_domain_map = (*reg_it)->schema_map()->GetDomains();
204     for (DomainMap::const_iterator domain_it = reg_domain_map.begin();
205          domain_it != reg_domain_map.end(); ++domain_it) {
206       const ComponentMap& reg_component_map = domain_it->second;
207       for (ComponentMap::const_iterator comp_it = reg_component_map.begin();
208            comp_it != reg_component_map.end(); ++comp_it) {
209         map[domain_it->first][comp_it->first] = comp_it->second;
210       }
211     }
212   }
213   schema_map_ = new SchemaMap(map);
214   Notify(has_new_schemas);
215 }
216 
ForwardingSchemaRegistry(SchemaRegistry * wrapped)217 ForwardingSchemaRegistry::ForwardingSchemaRegistry(SchemaRegistry* wrapped)
218     : wrapped_(wrapped) {
219   schema_map_ = wrapped_->schema_map();
220   wrapped_->AddObserver(this);
221   wrapped_->AddInternalObserver(this);
222   UpdateReadiness();
223 }
224 
~ForwardingSchemaRegistry()225 ForwardingSchemaRegistry::~ForwardingSchemaRegistry() {
226   if (wrapped_) {
227     wrapped_->RemoveObserver(this);
228     wrapped_->RemoveInternalObserver(this);
229   }
230 }
231 
RegisterComponents(PolicyDomain domain,const ComponentMap & components)232 void ForwardingSchemaRegistry::RegisterComponents(
233     PolicyDomain domain,
234     const ComponentMap& components) {
235   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
236   // POLICY_DOMAIN_CHROME is skipped to avoid spurious updates when a new
237   // Profile is created. If the ForwardingSchemaRegistry is used outside
238   // device-level accounts then this should become configurable.
239   if (wrapped_ && domain != POLICY_DOMAIN_CHROME)
240     wrapped_->RegisterComponents(domain, components);
241   // Ignore otherwise.
242 }
243 
UnregisterComponent(const PolicyNamespace & ns)244 void ForwardingSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) {
245   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
246   if (wrapped_)
247     wrapped_->UnregisterComponent(ns);
248   // Ignore otherwise.
249 }
250 
OnSchemaRegistryUpdated(bool has_new_schemas)251 void ForwardingSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) {
252   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
253   schema_map_ = wrapped_->schema_map();
254   Notify(has_new_schemas);
255 }
256 
OnSchemaRegistryReady()257 void ForwardingSchemaRegistry::OnSchemaRegistryReady() {
258   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
259   UpdateReadiness();
260 }
261 
OnSchemaRegistryShuttingDown(SchemaRegistry * registry)262 void ForwardingSchemaRegistry::OnSchemaRegistryShuttingDown(
263     SchemaRegistry* registry) {
264   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
265   DCHECK_EQ(wrapped_, registry);
266   wrapped_->RemoveObserver(this);
267   wrapped_->RemoveInternalObserver(this);
268   wrapped_ = nullptr;
269   // Keep serving the same |schema_map_|.
270 }
271 
UpdateReadiness()272 void ForwardingSchemaRegistry::UpdateReadiness() {
273   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
274   if (wrapped_->IsReady())
275     SetAllDomainsReady();
276 }
277 
278 }  // namespace policy
279