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