diff options
Diffstat (limited to 'chromium/components/policy/core/common/schema_registry.cc')
-rw-r--r-- | chromium/components/policy/core/common/schema_registry.cc | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/chromium/components/policy/core/common/schema_registry.cc b/chromium/components/policy/core/common/schema_registry.cc new file mode 100644 index 00000000000..3891636d268 --- /dev/null +++ b/chromium/components/policy/core/common/schema_registry.cc @@ -0,0 +1,279 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/policy/core/common/schema_registry.h" + +#include "base/check_op.h" +#include "base/notreached.h" +#include "base/observer_list.h" +#include "extensions/buildflags/buildflags.h" + +namespace policy { + +SchemaRegistry::Observer::~Observer() {} + +SchemaRegistry::InternalObserver::~InternalObserver() {} + +SchemaRegistry::SchemaRegistry() : schema_map_(new SchemaMap) { + for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i) + domains_ready_[i] = false; +#if !BUILDFLAG(ENABLE_EXTENSIONS) + SetExtensionsDomainsReady(); +#endif +} + +SchemaRegistry::~SchemaRegistry() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (auto& observer : internal_observers_) + observer.OnSchemaRegistryShuttingDown(this); +} + +void SchemaRegistry::RegisterComponent(const PolicyNamespace& ns, + const Schema& schema) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + ComponentMap map; + map[ns.component_id] = schema; + RegisterComponents(ns.domain, map); +} + +void SchemaRegistry::RegisterComponents(PolicyDomain domain, + const ComponentMap& components) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // Don't issue notifications if nothing is being registered. + if (components.empty()) + return; + // Assume that a schema was updated if the namespace was already registered + // before. + DomainMap map(schema_map_->GetDomains()); + for (auto it = components.begin(); it != components.end(); ++it) + map[domain][it->first] = it->second; + schema_map_ = new SchemaMap(std::move(map)); + Notify(true); +} + +void SchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DomainMap map(schema_map_->GetDomains()); + if (map[ns.domain].erase(ns.component_id) != 0) { + schema_map_ = new SchemaMap(std::move(map)); + Notify(false); + } else { + // Extension might be uninstalled before install so the associated policies + // are unregistered before registered. For example, a policy forced + // extension is removed from forced list during launch due to policy update. + DCHECK(ns.domain != POLICY_DOMAIN_CHROME); + } +} + +bool SchemaRegistry::IsReady() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i) { + if (!domains_ready_[i]) + return false; + } + return true; +} + +void SchemaRegistry::SetDomainReady(PolicyDomain domain) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (domains_ready_[domain]) + return; + domains_ready_[domain] = true; + if (IsReady()) { + for (auto& observer : observers_) + observer.OnSchemaRegistryReady(); + } +} + +void SchemaRegistry::SetAllDomainsReady() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (int i = 0; i < POLICY_DOMAIN_SIZE; ++i) + SetDomainReady(static_cast<PolicyDomain>(i)); +} + +void SchemaRegistry::SetExtensionsDomainsReady() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + SetDomainReady(POLICY_DOMAIN_EXTENSIONS); + SetDomainReady(POLICY_DOMAIN_SIGNIN_EXTENSIONS); +} + +void SchemaRegistry::AddObserver(Observer* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + observers_.AddObserver(observer); +} + +void SchemaRegistry::RemoveObserver(Observer* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + observers_.RemoveObserver(observer); +} + +void SchemaRegistry::AddInternalObserver(InternalObserver* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + internal_observers_.AddObserver(observer); +} + +void SchemaRegistry::RemoveInternalObserver(InternalObserver* observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + internal_observers_.RemoveObserver(observer); +} + +void SchemaRegistry::Notify(bool has_new_schemas) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + for (auto& observer : observers_) + observer.OnSchemaRegistryUpdated(has_new_schemas); +} + +CombinedSchemaRegistry::CombinedSchemaRegistry() + : own_schema_map_(new SchemaMap) { + // The combined registry is always ready, since it can always start tracking + // another registry that is not ready yet and going from "ready" to "not + // ready" is not allowed. + SetAllDomainsReady(); +} + +CombinedSchemaRegistry::~CombinedSchemaRegistry() {} + +void CombinedSchemaRegistry::Track(SchemaRegistry* registry) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + registries_.insert(registry); + registry->AddObserver(this); + registry->AddInternalObserver(this); + // Recombine the maps only if the |registry| has any components other than + // POLICY_DOMAIN_CHROME. + if (registry->schema_map()->HasComponents()) + Combine(true); +} + +void CombinedSchemaRegistry::RegisterComponents( + PolicyDomain domain, + const ComponentMap& components) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DomainMap map(own_schema_map_->GetDomains()); + for (auto it = components.begin(); it != components.end(); ++it) + map[domain][it->first] = it->second; + own_schema_map_ = new SchemaMap(std::move(map)); + Combine(true); +} + +void CombinedSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DomainMap map(own_schema_map_->GetDomains()); + if (map[ns.domain].erase(ns.component_id) != 0) { + own_schema_map_ = new SchemaMap(std::move(map)); + Combine(false); + } else { + NOTREACHED(); + } +} + +void CombinedSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + Combine(has_new_schemas); +} + +void CombinedSchemaRegistry::OnSchemaRegistryShuttingDown( + SchemaRegistry* registry) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + registry->RemoveObserver(this); + registry->RemoveInternalObserver(this); + if (registries_.erase(registry) != 0) { + if (registry->schema_map()->HasComponents()) + Combine(false); + } else { + NOTREACHED(); + } +} + +void CombinedSchemaRegistry::Combine(bool has_new_schemas) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // If two registries publish a Schema for the same component then it's + // undefined which version gets in the combined registry. + // + // The common case is that both registries want policy for the same component, + // and the Schemas should be the same; in that case this makes no difference. + // + // But if the Schemas are different then one of the components is out of date. + // In that case the policy loaded will be valid only for one of them, until + // the outdated components are updated. This is a known limitation of the + // way policies are loaded currently, but isn't a problem worth fixing for + // the time being. + DomainMap map(own_schema_map_->GetDomains()); + for (auto reg_it = registries_.begin(); reg_it != registries_.end(); + ++reg_it) { + const DomainMap& reg_domain_map = (*reg_it)->schema_map()->GetDomains(); + for (auto domain_it = reg_domain_map.begin(); + domain_it != reg_domain_map.end(); ++domain_it) { + const ComponentMap& reg_component_map = domain_it->second; + for (auto comp_it = reg_component_map.begin(); + comp_it != reg_component_map.end(); ++comp_it) { + map[domain_it->first][comp_it->first] = comp_it->second; + } + } + } + schema_map_ = new SchemaMap(std::move(map)); + Notify(has_new_schemas); +} + +ForwardingSchemaRegistry::ForwardingSchemaRegistry(SchemaRegistry* wrapped) + : wrapped_(wrapped) { + schema_map_ = wrapped_->schema_map(); + wrapped_->AddObserver(this); + wrapped_->AddInternalObserver(this); + UpdateReadiness(); +} + +ForwardingSchemaRegistry::~ForwardingSchemaRegistry() { + if (wrapped_) { + wrapped_->RemoveObserver(this); + wrapped_->RemoveInternalObserver(this); + } +} + +void ForwardingSchemaRegistry::RegisterComponents( + PolicyDomain domain, + const ComponentMap& components) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // POLICY_DOMAIN_CHROME is skipped to avoid spurious updates when a new + // Profile is created. If the ForwardingSchemaRegistry is used outside + // device-level accounts then this should become configurable. + if (wrapped_ && domain != POLICY_DOMAIN_CHROME) + wrapped_->RegisterComponents(domain, components); + // Ignore otherwise. +} + +void ForwardingSchemaRegistry::UnregisterComponent(const PolicyNamespace& ns) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (wrapped_) + wrapped_->UnregisterComponent(ns); + // Ignore otherwise. +} + +void ForwardingSchemaRegistry::OnSchemaRegistryUpdated(bool has_new_schemas) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + schema_map_ = wrapped_->schema_map(); + Notify(has_new_schemas); +} + +void ForwardingSchemaRegistry::OnSchemaRegistryReady() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + UpdateReadiness(); +} + +void ForwardingSchemaRegistry::OnSchemaRegistryShuttingDown( + SchemaRegistry* registry) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(wrapped_, registry); + wrapped_->RemoveObserver(this); + wrapped_->RemoveInternalObserver(this); + wrapped_ = nullptr; + // Keep serving the same |schema_map_|. +} + +void ForwardingSchemaRegistry::UpdateReadiness() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (wrapped_->IsReady()) + SetAllDomainsReady(); +} + +} // namespace policy |