blob: 94e8ebc6ea9a030ffc1ebd9fffe353026144f5ea [file] [log] [blame]
// Copyright (c) 2015 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/policy_loader_mac.h"
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/policy/core/common/external_data_fetcher.h"
#include "components/policy/core/common/mac_util.h"
#include "components/policy/core/common/policy_bundle.h"
#include "components/policy/core/common/policy_load_status.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/preferences_mac.h"
#include "components/policy/core/common/schema.h"
#include "components/policy/core/common/schema_map.h"
using base::ScopedCFTypeRef;
namespace policy {
PolicyLoaderMac::PolicyLoaderMac(
scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::FilePath& managed_policy_path,
MacPreferences* preferences)
: AsyncPolicyLoader(task_runner),
preferences_(preferences),
managed_policy_path_(managed_policy_path),
application_id_(kCFPreferencesCurrentApplication) {
}
PolicyLoaderMac::PolicyLoaderMac(
scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::FilePath& managed_policy_path,
MacPreferences* preferences,
CFStringRef application_id)
: AsyncPolicyLoader(task_runner),
preferences_(preferences),
managed_policy_path_(managed_policy_path),
application_id_(application_id) {
}
PolicyLoaderMac::~PolicyLoaderMac() {
}
void PolicyLoaderMac::InitOnBackgroundThread() {
if (!managed_policy_path_.empty()) {
watcher_.Watch(
managed_policy_path_, false,
base::Bind(&PolicyLoaderMac::OnFileUpdated, base::Unretained(this)));
}
}
std::unique_ptr<PolicyBundle> PolicyLoaderMac::Load() {
preferences_->AppSynchronize(application_id_);
std::unique_ptr<PolicyBundle> bundle(new PolicyBundle());
// Load Chrome's policy.
PolicyMap& chrome_policy =
bundle->Get(PolicyNamespace(POLICY_DOMAIN_CHROME, std::string()));
PolicyLoadStatusSample status;
bool policy_present = false;
const Schema* schema =
schema_map()->GetSchema(PolicyNamespace(POLICY_DOMAIN_CHROME, ""));
for (Schema::Iterator it = schema->GetPropertiesIterator(); !it.IsAtEnd();
it.Advance()) {
base::ScopedCFTypeRef<CFStringRef> name(
base::SysUTF8ToCFStringRef(it.key()));
base::ScopedCFTypeRef<CFPropertyListRef> value(
preferences_->CopyAppValue(name, application_id_));
if (!value)
continue;
policy_present = true;
bool forced = preferences_->AppValueIsForced(name, application_id_);
PolicyLevel level =
forced ? POLICY_LEVEL_MANDATORY : POLICY_LEVEL_RECOMMENDED;
// TODO(joaodasilva): figure the policy scope.
std::unique_ptr<base::Value> policy = PropertyToValue(value);
if (policy) {
chrome_policy.Set(it.key(), level, POLICY_SCOPE_USER,
POLICY_SOURCE_PLATFORM, std::move(policy), nullptr);
} else {
status.Add(POLICY_LOAD_STATUS_PARSE_ERROR);
}
}
if (!policy_present)
status.Add(POLICY_LOAD_STATUS_NO_POLICY);
// Load policy for the registered components.
LoadPolicyForDomain(POLICY_DOMAIN_EXTENSIONS, "extensions", bundle.get());
return bundle;
}
base::Time PolicyLoaderMac::LastModificationTime() {
base::File::Info file_info;
if (!base::GetFileInfo(managed_policy_path_, &file_info) ||
file_info.is_directory) {
return base::Time();
}
return file_info.last_modified;
}
#if defined(OS_MACOSX) && !defined(OS_IOS)
base::FilePath PolicyLoaderMac::GetManagedPolicyPath(CFStringRef bundle_id) {
// This constructs the path to the plist file in which Mac OS X stores the
// managed preference for the application. This is undocumented and therefore
// fragile, but if it doesn't work out, AsyncPolicyLoader has a task that
// polls periodically in order to reload managed preferences later even if we
// missed the change.
base::FilePath path;
if (!base::mac::GetLocalDirectory(NSLibraryDirectory, &path))
return base::FilePath();
path = path.Append(FILE_PATH_LITERAL("Managed Preferences"));
char* login = getlogin();
if (!login)
return base::FilePath();
path = path.AppendASCII(login);
return path.Append(base::SysCFStringRefToUTF8(bundle_id) + ".plist");
}
#endif
void PolicyLoaderMac::LoadPolicyForDomain(PolicyDomain domain,
const std::string& domain_name,
PolicyBundle* bundle) {
std::string id_prefix(base::mac::BaseBundleID());
id_prefix.append(".").append(domain_name).append(".");
const ComponentMap* components = schema_map()->GetComponents(domain);
if (!components)
return;
for (ComponentMap::const_iterator it = components->begin();
it != components->end(); ++it) {
PolicyMap policy;
LoadPolicyForComponent(id_prefix + it->first, it->second, &policy);
if (!policy.empty())
bundle->Get(PolicyNamespace(domain, it->first)).Swap(&policy);
}
}
void PolicyLoaderMac::LoadPolicyForComponent(
const std::string& bundle_id_string,
const Schema& schema,
PolicyMap* policy) {
// TODO(joaodasilva): Extensions may be registered in a ComponentMap
// without a schema, to allow a graceful update of the Legacy Browser Support
// extension on Windows. Remove this check once that support is removed.
if (!schema.valid())
return;
base::ScopedCFTypeRef<CFStringRef> bundle_id(
base::SysUTF8ToCFStringRef(bundle_id_string));
preferences_->AppSynchronize(bundle_id);
for (Schema::Iterator it = schema.GetPropertiesIterator(); !it.IsAtEnd();
it.Advance()) {
base::ScopedCFTypeRef<CFStringRef> pref_name(
base::SysUTF8ToCFStringRef(it.key()));
base::ScopedCFTypeRef<CFPropertyListRef> value(
preferences_->CopyAppValue(pref_name, bundle_id));
if (!value)
continue;
bool forced = preferences_->AppValueIsForced(pref_name, bundle_id);
PolicyLevel level =
forced ? POLICY_LEVEL_MANDATORY : POLICY_LEVEL_RECOMMENDED;
std::unique_ptr<base::Value> policy_value = PropertyToValue(value);
if (policy_value) {
policy->Set(it.key(), level, POLICY_SCOPE_USER, POLICY_SOURCE_PLATFORM,
std::move(policy_value), nullptr);
}
}
}
void PolicyLoaderMac::OnFileUpdated(const base::FilePath& path, bool error) {
if (!error)
Reload(false);
}
} // namespace policy