// 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 "extensions/common/permissions/api_permission_set.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "extensions/common/error_utils.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/permissions/permissions_info.h" namespace extensions { namespace errors = manifest_errors; namespace { // Helper object that is implicitly constructible from both a PermissionID and // from an APIPermission::ID. struct PermissionIDCompareHelper { PermissionIDCompareHelper(const PermissionID& id) : id(id.id()) {} PermissionIDCompareHelper(const APIPermission::ID id) : id(id) {} APIPermission::ID id; }; bool CreateAPIPermission( const std::string& permission_str, const base::Value* permission_value, APIPermissionSet::ParseSource source, APIPermissionSet* api_permissions, base::string16* error, std::vector* unhandled_permissions) { const APIPermissionInfo* permission_info = PermissionsInfo::GetInstance()->GetByName(permission_str); if (permission_info) { scoped_ptr permission( permission_info->CreateAPIPermission()); if (source != APIPermissionSet::kAllowInternalPermissions && permission_info->is_internal()) { // An internal permission specified in permissions list is an error. if (error) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kPermissionNotAllowedInManifest, permission_str); } return false; } std::string error_details; if (!permission->FromValue(permission_value, &error_details, unhandled_permissions)) { if (error) { if (error_details.empty()) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPermission, permission_info->name()); } else { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPermissionWithDetail, permission_info->name(), error_details); } return false; } LOG(WARNING) << "Parse permission failed."; } else { api_permissions->insert(permission.release()); } return true; } if (unhandled_permissions) unhandled_permissions->push_back(permission_str); else LOG(WARNING) << "Unknown permission[" << permission_str << "]."; return true; } bool ParseChildPermissions(const std::string& base_name, const base::Value* permission_value, APIPermissionSet::ParseSource source, APIPermissionSet* api_permissions, base::string16* error, std::vector* unhandled_permissions) { if (permission_value) { const base::ListValue* permissions; if (!permission_value->GetAsList(&permissions)) { if (error) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPermission, base_name); return false; } LOG(WARNING) << "Permission value is not a list."; // Failed to parse, but since error is NULL, failures are not fatal so // return true here anyway. return true; } for (size_t i = 0; i < permissions->GetSize(); ++i) { std::string permission_str; if (!permissions->GetString(i, &permission_str)) { // permission should be a string if (error) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPermission, base_name + '.' + base::SizeTToString(i)); return false; } LOG(WARNING) << "Permission is not a string."; continue; } if (!CreateAPIPermission( base_name + '.' + permission_str, NULL, source, api_permissions, error, unhandled_permissions)) return false; } } return CreateAPIPermission(base_name, NULL, source, api_permissions, error, NULL); } } // namespace void APIPermissionSet::insert(APIPermission::ID id) { const APIPermissionInfo* permission_info = PermissionsInfo::GetInstance()->GetByID(id); DCHECK(permission_info); insert(permission_info->CreateAPIPermission()); } void APIPermissionSet::insert(APIPermission* permission) { BaseSetOperators::insert(permission); } // static bool APIPermissionSet::ParseFromJSON( const base::ListValue* permissions, APIPermissionSet::ParseSource source, APIPermissionSet* api_permissions, base::string16* error, std::vector* unhandled_permissions) { for (size_t i = 0; i < permissions->GetSize(); ++i) { std::string permission_str; const base::Value* permission_value = NULL; if (!permissions->GetString(i, &permission_str)) { const base::DictionaryValue* dict = NULL; // permission should be a string or a single key dict. if (!permissions->GetDictionary(i, &dict) || dict->size() != 1) { if (error) { *error = ErrorUtils::FormatErrorMessageUTF16( errors::kInvalidPermission, base::SizeTToString(i)); return false; } LOG(WARNING) << "Permission is not a string or single key dict."; continue; } base::DictionaryValue::Iterator it(*dict); permission_str = it.key(); permission_value = &it.value(); } // Check if this permission is a special case where its value should // be treated as a list of child permissions. if (PermissionsInfo::GetInstance()->HasChildPermissions(permission_str)) { if (!ParseChildPermissions(permission_str, permission_value, source, api_permissions, error, unhandled_permissions)) return false; continue; } if (!CreateAPIPermission(permission_str, permission_value, source, api_permissions, error, unhandled_permissions)) return false; } return true; } PermissionID::PermissionID(APIPermission::ID id) : std::pair(id, base::string16()) { } PermissionID::PermissionID(APIPermission::ID id, const base::string16& parameter) : std::pair(id, parameter) { } PermissionID::~PermissionID() { } PermissionIDSet::PermissionIDSet() { } PermissionIDSet::PermissionIDSet(const PermissionIDSet& other) = default; PermissionIDSet::~PermissionIDSet() { } void PermissionIDSet::insert(APIPermission::ID permission_id) { insert(permission_id, base::string16()); } void PermissionIDSet::insert(APIPermission::ID permission_id, const base::string16& permission_detail) { permissions_.insert(PermissionID(permission_id, permission_detail)); } void PermissionIDSet::InsertAll(const PermissionIDSet& permission_set) { for (const auto& permission : permission_set.permissions_) { permissions_.insert(permission); } } void PermissionIDSet::erase(APIPermission::ID permission_id) { auto lower_bound = permissions_.lower_bound(PermissionID(permission_id)); auto upper_bound = lower_bound; while (upper_bound != permissions_.end() && upper_bound->id() == permission_id) { ++upper_bound; } permissions_.erase(lower_bound, upper_bound); } std::vector PermissionIDSet::GetAllPermissionParameters() const { std::vector params; for (const auto& permission : permissions_) { params.push_back(permission.parameter()); } return params; } bool PermissionIDSet::ContainsID(APIPermission::ID permission_id) const { auto it = permissions_.lower_bound(PermissionID(permission_id)); return it != permissions_.end() && it->id() == permission_id; } bool PermissionIDSet::ContainsAllIDs( const std::set& permission_ids) const { return std::includes(permissions_.begin(), permissions_.end(), permission_ids.begin(), permission_ids.end(), [] (const PermissionIDCompareHelper& lhs, const PermissionIDCompareHelper& rhs) { return lhs.id < rhs.id; }); } bool PermissionIDSet::ContainsAnyID( const std::set& permission_ids) const { for (APIPermission::ID id : permission_ids) { if (ContainsID(id)) return true; } return false; } PermissionIDSet PermissionIDSet::GetAllPermissionsWithID( APIPermission::ID permission_id) const { PermissionIDSet subset; auto it = permissions_.lower_bound(PermissionID(permission_id)); while (it != permissions_.end() && it->id() == permission_id) { subset.permissions_.insert(*it); ++it; } return subset; } PermissionIDSet PermissionIDSet::GetAllPermissionsWithIDs( const std::set& permission_ids) const { PermissionIDSet subset; for (const auto& permission : permissions_) { if (ContainsKey(permission_ids, permission.id())) { subset.permissions_.insert(permission); } } return subset; } bool PermissionIDSet::Includes(const PermissionIDSet& subset) const { return base::STLIncludes(permissions_, subset.permissions_); } bool PermissionIDSet::Equals(const PermissionIDSet& set) const { return permissions_ == set.permissions_; } // static PermissionIDSet PermissionIDSet::Difference(const PermissionIDSet& set_1, const PermissionIDSet& set_2) { return PermissionIDSet(base::STLSetDifference>( set_1.permissions_, set_2.permissions_)); } size_t PermissionIDSet::size() const { return permissions_.size(); } bool PermissionIDSet::empty() const { return permissions_.empty(); } PermissionIDSet::PermissionIDSet(const std::set& permissions) : permissions_(permissions) { } } // namespace extensions