Home | History | Annotate | Download | only in common
      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