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