| // 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 |