diff options
Diffstat (limited to 'extensions/common/permissions')
-rw-r--r-- | extensions/common/permissions/api_permission.cc | 143 | ||||
-rw-r--r-- | extensions/common/permissions/api_permission.h | 333 | ||||
-rw-r--r-- | extensions/common/permissions/api_permission_set.cc | 343 | ||||
-rw-r--r-- | extensions/common/permissions/api_permission_set.h | 173 | ||||
-rw-r--r-- | extensions/common/permissions/api_permission_set_unittest.cc | 333 | ||||
-rw-r--r-- | extensions/common/permissions/permissions_info.cc | 101 | ||||
-rw-r--r-- | extensions/common/permissions/permissions_info.h | 81 |
7 files changed, 1507 insertions, 0 deletions
diff --git a/extensions/common/permissions/api_permission.cc b/extensions/common/permissions/api_permission.cc new file mode 100644 index 0000000..f183ed2 --- /dev/null +++ b/extensions/common/permissions/api_permission.cc @@ -0,0 +1,143 @@ +// 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.h" + +#include "ui/base/l10n/l10n_util.h" + +namespace { + +using extensions::APIPermission; +using extensions::APIPermissionInfo; +using extensions::PermissionMessage; +using extensions::PermissionMessages; + +class SimpleAPIPermission : public APIPermission { + public: + explicit SimpleAPIPermission(const APIPermissionInfo* permission) + : APIPermission(permission) { } + + virtual ~SimpleAPIPermission() { } + + virtual bool HasMessages() const OVERRIDE { + return info()->message_id() > PermissionMessage::kNone; + } + + virtual PermissionMessages GetMessages() const OVERRIDE { + DCHECK(HasMessages()); + PermissionMessages result; + result.push_back(GetMessage_()); + return result; + } + + virtual bool Check( + const APIPermission::CheckParam* param) const OVERRIDE { + return !param; + } + + virtual bool Contains(const APIPermission* rhs) const OVERRIDE { + CHECK(info() == rhs->info()); + return true; + } + + virtual bool Equal(const APIPermission* rhs) const OVERRIDE { + if (this == rhs) + return true; + CHECK(info() == rhs->info()); + return true; + } + + virtual bool FromValue(const base::Value* value) OVERRIDE { + if (value) + return false; + return true; + } + + virtual scoped_ptr<base::Value> ToValue() const OVERRIDE { + return scoped_ptr<base::Value>(); + } + + virtual APIPermission* Clone() const OVERRIDE { + return new SimpleAPIPermission(info()); + } + + virtual APIPermission* Diff(const APIPermission* rhs) const OVERRIDE { + CHECK(info() == rhs->info()); + return NULL; + } + + virtual APIPermission* Union(const APIPermission* rhs) const OVERRIDE { + CHECK(info() == rhs->info()); + return new SimpleAPIPermission(info()); + } + + virtual APIPermission* Intersect(const APIPermission* rhs) const OVERRIDE { + CHECK(info() == rhs->info()); + return new SimpleAPIPermission(info()); + } + + virtual void Write(IPC::Message* m) const OVERRIDE { } + + virtual bool Read(const IPC::Message* m, PickleIterator* iter) OVERRIDE { + return true; + } + + virtual void Log(std::string* log) const OVERRIDE { } +}; + +} // namespace + +namespace extensions { + +APIPermission::APIPermission(const APIPermissionInfo* info) + : info_(info) { + DCHECK(info_); +} + +APIPermission::~APIPermission() { } + +APIPermission::ID APIPermission::id() const { + return info()->id(); +} + +const char* APIPermission::name() const { + return info()->name(); +} + +PermissionMessage APIPermission::GetMessage_() const { + return info()->GetMessage_(); +} + +// +// APIPermissionInfo +// + +APIPermissionInfo::APIPermissionInfo( + APIPermission::ID id, + const char* name, + int l10n_message_id, + PermissionMessage::ID message_id, + int flags, + APIPermissionConstructor api_permission_constructor) + : id_(id), + name_(name), + flags_(flags), + l10n_message_id_(l10n_message_id), + message_id_(message_id), + api_permission_constructor_(api_permission_constructor) { } + + +APIPermissionInfo::~APIPermissionInfo() { } + +APIPermission* APIPermissionInfo::CreateAPIPermission() const { + return api_permission_constructor_ ? + api_permission_constructor_(this) : new SimpleAPIPermission(this); +} + +PermissionMessage APIPermissionInfo::GetMessage_() const { + return PermissionMessage( + message_id_, l10n_util::GetStringUTF16(l10n_message_id_)); +} + +} // namespace extensions diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h new file mode 100644 index 0000000..6fbe479 --- /dev/null +++ b/extensions/common/permissions/api_permission.h @@ -0,0 +1,333 @@ +// 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. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/pickle.h" +#include "base/values.h" +#include "extensions/common/permissions/permission_message.h" + +namespace IPC { +class Message; +} + +namespace extensions { + +class APIPermissionInfo; +class ChromeAPIPermissions; + +// APIPermission is for handling some complex permissions. Please refer to +// extensions::SocketPermission as an example. +// There is one instance per permission per loaded extension. +class APIPermission { + public: + enum ID { + // Error codes. + kInvalid = -2, + kUnknown = -1, + + // Real permissions. + kActiveTab, + kActivityLogPrivate, + kAdView, + kAlarms, + kAppCurrentWindowInternal, + kAppRuntime, + kAppWindow, + kAudio, + kAudioCapture, + kAutoTestPrivate, + kBackground, + kBluetooth, + kBookmark, + kBookmarkManagerPrivate, + kBrailleDisplayPrivate, + kBrowsingData, + kCast, + kChromeosInfoPrivate, + kClipboardRead, + kClipboardWrite, + kCloudPrintPrivate, + kCommandLinePrivate, + kContentSettings, + kContextMenus, + kCookie, + kDiagnostics, + kDial, + kDebugger, + kDeclarative, + kDeclarativeContent, + kDeclarativeWebRequest, + kDesktopCapture, + kDeveloperPrivate, + kDevtools, + kDns, + kDownloads, + kDownloadsInternal, + kDownloadsOpen, + kDownloadsShelf, + kEchoPrivate, + kEnterprisePlatformKeysPrivate, + kExperimental, + kFeedbackPrivate, + kFileBrowserHandler, + kFileBrowserHandlerInternal, + kFileBrowserPrivate, + kFileSystem, + kFileSystemDirectory, + kFileSystemRetainEntries, + kFileSystemWrite, + kFileSystemWriteDirectory, + kFontSettings, + kFullscreen, + kGeolocation, + kHistory, + kIdentity, + kIdentityPrivate, + kIdltest, + kIdle, + kInfobars, + kInput, + kInputMethodPrivate, + kLocation, + kLogPrivate, + kManagement, + kMediaGalleries, + kMediaGalleriesPrivate, + kMediaPlayerPrivate, + kMetricsPrivate, + kMDns, + kMusicManagerPrivate, + kNativeMessaging, + kNetworkingPrivate, + kNotification, + kPageCapture, + kPointerLock, + kPlugin, + kPower, + kPreferencesPrivate, + kPrincipalsPrivate, + kPrivacy, + kProcesses, + kProxy, + kPushMessaging, + kImageWriterPrivate, + kRtcPrivate, + kScreensaver, + kSerial, + kSessions, + kSignedInDevices, + kSocket, + kSocketsUdp, + kStorage, + kStreamsPrivate, + kSyncFileSystem, + kSystemPrivate, + kSystemIndicator, + kSystemDisplay, + kSystemStorage, + kTab, + kTabCapture, + kTabCaptureForTab, + kTerminalPrivate, + kTopSites, + kTts, + kTtsEngine, + kUnlimitedStorage, + kUsb, + kUsbDevice, + kVideoCapture, + kVirtualKeyboardPrivate, + kWallpaper, + kWallpaperPrivate, + kWebConnectable, // for externally_connectable manifest key + kWebNavigation, + kWebRequest, + kWebRequestBlocking, + kWebRequestInternal, + kWebRtc, + kWebrtcLoggingPrivate, + kWebstorePrivate, + kWebView, + kSystemCpu, + kSystemMemory, + kSystemInfoCpu, + kSystemInfoMemory, + kEnumBoundary + }; + + struct CheckParam { + }; + + explicit APIPermission(const APIPermissionInfo* info); + + virtual ~APIPermission(); + + // Returns the id of this permission. + ID id() const; + + // Returns the name of this permission. + const char* name() const; + + // Returns the APIPermission of this permission. + const APIPermissionInfo* info() const { + return info_; + } + + // Returns true if this permission has any PermissionMessages. + virtual bool HasMessages() const = 0; + + // Returns the localized permission messages of this permission. + virtual PermissionMessages GetMessages() const = 0; + + // Returns true if the given permission is allowed. + virtual bool Check(const CheckParam* param) const = 0; + + // Returns true if |rhs| is a subset of this. + virtual bool Contains(const APIPermission* rhs) const = 0; + + // Returns true if |rhs| is equal to this. + virtual bool Equal(const APIPermission* rhs) const = 0; + + // Parses the APIPermission from |value|. Returns false if error happens. + virtual bool FromValue(const base::Value* value) = 0; + + // Stores this into a new created |value|. + virtual scoped_ptr<base::Value> ToValue() const = 0; + + // Clones this. + virtual APIPermission* Clone() const = 0; + + // Returns a new API permission which equals this - |rhs|. + virtual APIPermission* Diff(const APIPermission* rhs) const = 0; + + // Returns a new API permission which equals the union of this and |rhs|. + virtual APIPermission* Union(const APIPermission* rhs) const = 0; + + // Returns a new API permission which equals the intersect of this and |rhs|. + virtual APIPermission* Intersect(const APIPermission* rhs) const = 0; + + // IPC functions + // Writes this into the given IPC message |m|. + virtual void Write(IPC::Message* m) const = 0; + + // Reads from the given IPC message |m|. + virtual bool Read(const IPC::Message* m, PickleIterator* iter) = 0; + + // Logs this permission. + virtual void Log(std::string* log) const = 0; + + protected: + // Returns the localized permission message associated with this api. + // Use GetMessage_ to avoid name conflict with macro GetMessage on Windows. + PermissionMessage GetMessage_() const; + + private: + const APIPermissionInfo* const info_; +}; + + +// The APIPermissionInfo is an immutable class that describes a single +// named permission (API permission). +// There is one instance per permission. +class APIPermissionInfo { + public: + enum Flag { + kFlagNone = 0, + + // Indicates if the permission implies full access (native code). + kFlagImpliesFullAccess = 1 << 0, + + // Indicates if the permission implies full URL access. + kFlagImpliesFullURLAccess = 1 << 1, + + // Indicates that extensions cannot specify the permission as optional. + kFlagCannotBeOptional = 1 << 3, + + // Indicates that the permission is internal to the extensions + // system and cannot be specified in the "permissions" list. + kFlagInternal = 1 << 4, + }; + + typedef APIPermission* (*APIPermissionConstructor)(const APIPermissionInfo*); + + typedef std::set<APIPermission::ID> IDSet; + + ~APIPermissionInfo(); + + // Creates a APIPermission instance. + APIPermission* CreateAPIPermission() const; + + int flags() const { return flags_; } + + APIPermission::ID id() const { return id_; } + + // Returns the message id associated with this permission. + PermissionMessage::ID message_id() const { + return message_id_; + } + + // Returns the name of this permission. + const char* name() const { return name_; } + + // Returns true if this permission implies full access (e.g., native code). + bool implies_full_access() const { + return (flags_ & kFlagImpliesFullAccess) != 0; + } + + // Returns true if this permission implies full URL access. + bool implies_full_url_access() const { + return (flags_ & kFlagImpliesFullURLAccess) != 0; + } + + // Returns true if this permission can be added and removed via the + // optional permissions extension API. + bool supports_optional() const { + return (flags_ & kFlagCannotBeOptional) == 0; + } + + // Returns true if this permission is internal rather than a + // "permissions" list entry. + bool is_internal() const { + return (flags_ & kFlagInternal) != 0; + } + + private: + // Instances should only be constructed from within a + // PermissionsInfo::Delegate. + friend class ChromeAPIPermissions; + // Implementations of APIPermission will want to get the permission message, + // but this class's implementation should be hidden from everyone else. + friend class APIPermission; + + explicit APIPermissionInfo( + APIPermission::ID id, + const char* name, + int l10n_message_id, + PermissionMessage::ID message_id, + int flags, + APIPermissionConstructor api_permission_constructor); + + // Returns the localized permission message associated with this api. + // Use GetMessage_ to avoid name conflict with macro GetMessage on Windows. + PermissionMessage GetMessage_() const; + + const APIPermission::ID id_; + const char* const name_; + const int flags_; + const int l10n_message_id_; + const PermissionMessage::ID message_id_; + const APIPermissionConstructor api_permission_constructor_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_H_ diff --git a/extensions/common/permissions/api_permission_set.cc b/extensions/common/permissions/api_permission_set.cc new file mode 100644 index 0000000..ebaf8d8 --- /dev/null +++ b/extensions/common/permissions/api_permission_set.cc @@ -0,0 +1,343 @@ +// 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 { + +bool CreateAPIPermission( + const std::string& permission_str, + const base::Value* permission_value, + APIPermissionSet::ParseSource source, + APIPermissionSet* api_permissions, + string16* error, + std::vector<std::string>* unhandled_permissions) { + + const APIPermissionInfo* permission_info = + PermissionsInfo::GetInstance()->GetByName(permission_str); + if (permission_info) { + scoped_ptr<APIPermission> 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; + } + + if (!permission->FromValue(permission_value)) { + if (error) { + *error = ErrorUtils::FormatErrorMessageUTF16( + errors::kInvalidPermission, permission_info->name()); + 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, + string16* error, + std::vector<std::string>* 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::IntToString(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 + +APIPermissionSet::APIPermissionSet() { +} + +APIPermissionSet::APIPermissionSet(const APIPermissionSet& set) { + this->operator=(set); +} + +APIPermissionSet::~APIPermissionSet() { +} + +APIPermissionSet::const_iterator::const_iterator( + const APIPermissionMap::const_iterator& it) + : it_(it) { +} + +APIPermissionSet::const_iterator::const_iterator( + const const_iterator& ids_it) + : it_(ids_it.it_) { +} + +APIPermissionSet& APIPermissionSet::operator=(const APIPermissionSet& rhs) { + const_iterator it = rhs.begin(); + const const_iterator end = rhs.end(); + while (it != end) { + insert(it->Clone()); + ++it; + } + return *this; +} + +bool APIPermissionSet::operator==(const APIPermissionSet& rhs) const { + const_iterator it = begin(); + const_iterator rhs_it = rhs.begin(); + const_iterator it_end = end(); + const_iterator rhs_it_end = rhs.end(); + + while (it != it_end && rhs_it != rhs_it_end) { + if (!it->Equal(*rhs_it)) + return false; + ++it; + ++rhs_it; + } + return it == it_end && rhs_it == rhs_it_end; +} + +void APIPermissionSet::insert(APIPermission::ID id) { + const APIPermissionInfo* permission_info = + PermissionsInfo::GetInstance()->GetByID(id); + insert(permission_info->CreateAPIPermission()); +} + +void APIPermissionSet::insert(APIPermission* permission) { + map_[permission->id()].reset(permission); +} + +bool APIPermissionSet::Contains(const APIPermissionSet& rhs) const { + APIPermissionSet::const_iterator it1 = begin(); + APIPermissionSet::const_iterator it2 = rhs.begin(); + APIPermissionSet::const_iterator end1 = end(); + APIPermissionSet::const_iterator end2 = rhs.end(); + + while (it1 != end1 && it2 != end2) { + if (it1->id() > it2->id()) { + return false; + } else if (it1->id() < it2->id()) { + ++it1; + } else { + if (!it1->Contains(*it2)) + return false; + ++it1; + ++it2; + } + } + + return it2 == end2; +} + +void APIPermissionSet::Difference( + const APIPermissionSet& set1, + const APIPermissionSet& set2, + APIPermissionSet* set3) { + CHECK(set3); + set3->clear(); + + APIPermissionSet::const_iterator it1 = set1.begin(); + APIPermissionSet::const_iterator it2 = set2.begin(); + const APIPermissionSet::const_iterator end1 = set1.end(); + const APIPermissionSet::const_iterator end2 = set2.end(); + + while (it1 != end1 && it2 != end2) { + if (it1->id() < it2->id()) { + set3->insert(it1->Clone()); + ++it1; + } else if (it1->id() > it2->id()) { + ++it2; + } else { + APIPermission* p = it1->Diff(*it2); + if (p) + set3->insert(p); + ++it1; + ++it2; + } + } + + while (it1 != end1) { + set3->insert(it1->Clone()); + ++it1; + } +} + +void APIPermissionSet::Intersection( + const APIPermissionSet& set1, + const APIPermissionSet& set2, + APIPermissionSet* set3) { + DCHECK(set3); + set3->clear(); + + APIPermissionSet::const_iterator it1 = set1.begin(); + APIPermissionSet::const_iterator it2 = set2.begin(); + const APIPermissionSet::const_iterator end1 = set1.end(); + const APIPermissionSet::const_iterator end2 = set2.end(); + + while (it1 != end1 && it2 != end2) { + if (it1->id() < it2->id()) { + ++it1; + } else if (it1->id() > it2->id()) { + ++it2; + } else { + APIPermission* p = it1->Intersect(*it2); + if (p) + set3->insert(p); + ++it1; + ++it2; + } + } +} + +void APIPermissionSet::Union( + const APIPermissionSet& set1, + const APIPermissionSet& set2, + APIPermissionSet* set3) { + DCHECK(set3); + set3->clear(); + + APIPermissionSet::const_iterator it1 = set1.begin(); + APIPermissionSet::const_iterator it2 = set2.begin(); + const APIPermissionSet::const_iterator end1 = set1.end(); + const APIPermissionSet::const_iterator end2 = set2.end(); + + while (true) { + if (it1 == end1) { + while (it2 != end2) { + set3->insert(it2->Clone()); + ++it2; + } + break; + } + if (it2 == end2) { + while (it1 != end1) { + set3->insert(it1->Clone()); + ++it1; + } + break; + } + if (it1->id() < it2->id()) { + set3->insert(it1->Clone()); + ++it1; + } else if (it1->id() > it2->id()) { + set3->insert(it2->Clone()); + ++it2; + } else { + set3->insert(it1->Union(*it2)); + ++it1; + ++it2; + } + } +} + +// static +bool APIPermissionSet::ParseFromJSON( + const base::ListValue* permissions, + APIPermissionSet::ParseSource source, + APIPermissionSet* api_permissions, + string16* error, + std::vector<std::string>* 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::IntToString(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; +} + +void APIPermissionSet::AddImpliedPermissions() { + // The fileSystem.write and fileSystem.directory permissions imply + // fileSystem.writeDirectory. + // TODO(sammc): Remove this. See http://crbug.com/284849. + if (ContainsKey(map_, APIPermission::kFileSystemWrite) && + ContainsKey(map_, APIPermission::kFileSystemDirectory)) { + insert(APIPermission::kFileSystemWriteDirectory); + } +} + +} // namespace extensions diff --git a/extensions/common/permissions/api_permission_set.h b/extensions/common/permissions/api_permission_set.h new file mode 100644 index 0000000..9b6f26d --- /dev/null +++ b/extensions/common/permissions/api_permission_set.h @@ -0,0 +1,173 @@ +// 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. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_SET_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_SET_H_ + +#include <iterator> +#include <map> + +#include "base/memory/linked_ptr.h" +#include "extensions/common/permissions/api_permission.h" + +namespace base { +class ListValue; +} // namespace base + +namespace extensions { + +class Extension; + +typedef std::map<APIPermission::ID, + linked_ptr<APIPermission> > APIPermissionMap; + +class APIPermissionSet { + public: + class const_iterator : + public std::iterator<std::input_iterator_tag, const APIPermission*> { + public: + const_iterator(const APIPermissionMap::const_iterator& it); + const_iterator(const const_iterator& ids_it); + + const_iterator& operator++() { + ++it_; + return *this; + } + + const_iterator operator++(int) { + const_iterator tmp(it_++); + return tmp; + } + + bool operator==(const const_iterator& rhs) const { + return it_ == rhs.it_; + } + + bool operator!=(const const_iterator& rhs) const { + return it_ != rhs.it_; + } + + const APIPermission* operator*() const { + return it_->second.get(); + } + + const APIPermission* operator->() const { + return it_->second.get(); + } + + private: + APIPermissionMap::const_iterator it_; + }; + + enum ParseSource { + // Don't allow internal permissions to be parsed (e.g. entries in the + // "permissions" list in a manifest). + kDisallowInternalPermissions, + + // Allow internal permissions to be parsed (e.g. from the "api" field of a + // permissions list in the prefs). + kAllowInternalPermissions, + }; + + APIPermissionSet(); + + APIPermissionSet(const APIPermissionSet& set); + + ~APIPermissionSet(); + + const_iterator begin() const { + return const_iterator(map().begin()); + } + + const_iterator end() const { + return map().end(); + } + + const_iterator find(APIPermission::ID id) const { + return map().find(id); + } + + const APIPermissionMap& map() const { + return map_; + } + + APIPermissionMap& map() { + return map_; + } + + void clear() { + map_.clear(); + } + + size_t count(APIPermission::ID id) const { + return map().count(id); + } + + bool empty() const { + return map().empty(); + } + + size_t erase(APIPermission::ID id) { + return map().erase(id); + } + + size_t size() const { + return map().size(); + } + + APIPermissionSet& operator=(const APIPermissionSet& rhs); + + bool operator==(const APIPermissionSet& rhs) const; + + bool operator!=(const APIPermissionSet& rhs) const { + return !operator==(rhs); + } + + void insert(APIPermission::ID id); + + // Insert |permission| into the APIPermissionSet. The APIPermissionSet will + // take the ownership of |permission|, + void insert(APIPermission* permission); + + bool Contains(const APIPermissionSet& rhs) const; + + static void Difference( + const APIPermissionSet& set1, + const APIPermissionSet& set2, + APIPermissionSet* set3); + + static void Intersection( + const APIPermissionSet& set1, + const APIPermissionSet& set2, + APIPermissionSet* set3); + + static void Union( + const APIPermissionSet& set1, + const APIPermissionSet& set2, + APIPermissionSet* set3); + + // Parses permissions from |permissions| and adds the parsed permissions to + // |api_permissions|. If |source| is kDisallowInternalPermissions, treat + // permissions with kFlagInternal as errors. If |unhandled_permissions| + // is not NULL, the names of all permissions that couldn't be parsed will be + // added to this vector. If |error| is NULL, parsing will continue with the + // next permission if invalid data is detected. If |error| is not NULL, it + // will be set to an error message and false is returned when an invalid + // permission is found. + static bool ParseFromJSON( + const base::ListValue* permissions, + ParseSource source, + APIPermissionSet* api_permissions, + string16* error, + std::vector<std::string>* unhandled_permissions); + + void AddImpliedPermissions(); + + private: + APIPermissionMap map_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_API_PERMISSION_SET_H_ diff --git a/extensions/common/permissions/api_permission_set_unittest.cc b/extensions/common/permissions/api_permission_set_unittest.cc new file mode 100644 index 0000000..85f8ccc --- /dev/null +++ b/extensions/common/permissions/api_permission_set_unittest.cc @@ -0,0 +1,333 @@ +// 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 "base/pickle.h" +#include "base/values.h" +#include "chrome/common/extensions/extension_messages.h" +#include "extensions/common/permissions/api_permission_set.h" +#include "extensions/common/permissions/permissions_info.h" +#include "ipc/ipc_message.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +TEST(APIPermissionSetTest, General) { + APIPermissionSet apis; + apis.insert(APIPermission::kTab); + apis.insert(APIPermission::kBackground); + apis.insert(APIPermission::kProxy); + apis.insert(APIPermission::kClipboardWrite); + apis.insert(APIPermission::kPlugin); + + EXPECT_EQ(apis.find(APIPermission::kProxy)->id(), APIPermission::kProxy); + EXPECT_TRUE(apis.find(APIPermission::kSocket) == apis.end()); + + EXPECT_EQ(apis.size(), 5u); + + EXPECT_EQ(apis.erase(APIPermission::kTab), 1u); + EXPECT_EQ(apis.size(), 4u); + + EXPECT_EQ(apis.erase(APIPermission::kTab), 0u); + EXPECT_EQ(apis.size(), 4u); +} + +TEST(APIPermissionSetTest, CreateUnion) { + APIPermission* permission = NULL; + + APIPermissionSet apis1; + APIPermissionSet apis2; + APIPermissionSet expected_apis; + APIPermissionSet result; + + const APIPermissionInfo* permission_info = + PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + + // Union with an empty set. + apis1.insert(APIPermission::kTab); + apis1.insert(APIPermission::kBackground); + apis1.insert(permission->Clone()); + expected_apis.insert(APIPermission::kTab); + expected_apis.insert(APIPermission::kBackground); + expected_apis.insert(permission); + + APIPermissionSet::Union(apis1, apis2, &result); + + EXPECT_TRUE(apis1.Contains(apis2)); + EXPECT_TRUE(apis1.Contains(result)); + EXPECT_FALSE(apis2.Contains(apis1)); + EXPECT_FALSE(apis2.Contains(result)); + EXPECT_TRUE(result.Contains(apis1)); + EXPECT_TRUE(result.Contains(apis2)); + + EXPECT_EQ(expected_apis, result); + + // Now use a real second set. + apis2.insert(APIPermission::kTab); + apis2.insert(APIPermission::kProxy); + apis2.insert(APIPermission::kClipboardWrite); + apis2.insert(APIPermission::kPlugin); + + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-send-to::8899")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + apis2.insert(permission); + + expected_apis.insert(APIPermission::kTab); + expected_apis.insert(APIPermission::kProxy); + expected_apis.insert(APIPermission::kClipboardWrite); + expected_apis.insert(APIPermission::kPlugin); + + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + value->Append(new base::StringValue("udp-send-to::8899")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + // Insert a new socket permission which will replace the old one. + expected_apis.insert(permission); + + APIPermissionSet::Union(apis1, apis2, &result); + + EXPECT_FALSE(apis1.Contains(apis2)); + EXPECT_FALSE(apis1.Contains(result)); + EXPECT_FALSE(apis2.Contains(apis1)); + EXPECT_FALSE(apis2.Contains(result)); + EXPECT_TRUE(result.Contains(apis1)); + EXPECT_TRUE(result.Contains(apis2)); + + EXPECT_EQ(expected_apis, result); +} + +TEST(APIPermissionSetTest, CreateIntersection) { + APIPermission* permission = NULL; + + APIPermissionSet apis1; + APIPermissionSet apis2; + APIPermissionSet expected_apis; + APIPermissionSet result; + + const APIPermissionInfo* permission_info = + PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket); + + // Intersection with an empty set. + apis1.insert(APIPermission::kTab); + apis1.insert(APIPermission::kBackground); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + apis1.insert(permission); + + APIPermissionSet::Intersection(apis1, apis2, &result); + EXPECT_TRUE(apis1.Contains(result)); + EXPECT_TRUE(apis2.Contains(result)); + EXPECT_TRUE(apis1.Contains(apis2)); + EXPECT_FALSE(apis2.Contains(apis1)); + EXPECT_FALSE(result.Contains(apis1)); + EXPECT_TRUE(result.Contains(apis2)); + + EXPECT_TRUE(result.empty()); + EXPECT_EQ(expected_apis, result); + + // Now use a real second set. + apis2.insert(APIPermission::kTab); + apis2.insert(APIPermission::kProxy); + apis2.insert(APIPermission::kClipboardWrite); + apis2.insert(APIPermission::kPlugin); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + value->Append(new base::StringValue("udp-send-to::8899")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + apis2.insert(permission); + + expected_apis.insert(APIPermission::kTab); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + expected_apis.insert(permission); + + APIPermissionSet::Intersection(apis1, apis2, &result); + + EXPECT_TRUE(apis1.Contains(result)); + EXPECT_TRUE(apis2.Contains(result)); + EXPECT_FALSE(apis1.Contains(apis2)); + EXPECT_FALSE(apis2.Contains(apis1)); + EXPECT_FALSE(result.Contains(apis1)); + EXPECT_FALSE(result.Contains(apis2)); + + EXPECT_EQ(expected_apis, result); +} + +TEST(APIPermissionSetTest, CreateDifference) { + APIPermission* permission = NULL; + + APIPermissionSet apis1; + APIPermissionSet apis2; + APIPermissionSet expected_apis; + APIPermissionSet result; + + const APIPermissionInfo* permission_info = + PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket); + + // Difference with an empty set. + apis1.insert(APIPermission::kTab); + apis1.insert(APIPermission::kBackground); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + apis1.insert(permission); + + APIPermissionSet::Difference(apis1, apis2, &result); + + EXPECT_EQ(apis1, result); + + // Now use a real second set. + apis2.insert(APIPermission::kTab); + apis2.insert(APIPermission::kProxy); + apis2.insert(APIPermission::kClipboardWrite); + apis2.insert(APIPermission::kPlugin); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-send-to::8899")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + apis2.insert(permission); + + expected_apis.insert(APIPermission::kBackground); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + expected_apis.insert(permission); + + APIPermissionSet::Difference(apis1, apis2, &result); + + EXPECT_TRUE(apis1.Contains(result)); + EXPECT_FALSE(apis2.Contains(result)); + + EXPECT_EQ(expected_apis, result); + + // |result| = |apis1| - |apis2| --> |result| intersect |apis2| == empty_set + APIPermissionSet result2; + APIPermissionSet::Intersection(result, apis2, &result2); + EXPECT_TRUE(result2.empty()); +} + +TEST(APIPermissionSetTest, IPC) { + APIPermission* permission = NULL; + + APIPermissionSet apis; + APIPermissionSet expected_apis; + + const APIPermissionInfo* permission_info = + PermissionsInfo::GetInstance()->GetByID(APIPermission::kSocket); + + apis.insert(APIPermission::kTab); + apis.insert(APIPermission::kBackground); + permission = permission_info->CreateAPIPermission(); + { + scoped_ptr<base::ListValue> value(new base::ListValue()); + value->Append(new base::StringValue("tcp-connect:*.example.com:80")); + value->Append(new base::StringValue("udp-bind::8080")); + value->Append(new base::StringValue("udp-send-to::8888")); + if (!permission->FromValue(value.get())) { + NOTREACHED(); + } + } + apis.insert(permission); + + EXPECT_NE(apis, expected_apis); + + IPC::Message m; + WriteParam(&m, apis); + PickleIterator iter(m); + CHECK(ReadParam(&m, &iter, &expected_apis)); + EXPECT_EQ(apis, expected_apis); +} + +TEST(APIPermissionSetTest, ImplicitPermissions) { + APIPermissionSet apis; + apis.insert(APIPermission::kFileSystemWrite); + apis.AddImpliedPermissions(); + + EXPECT_EQ(apis.find(APIPermission::kFileSystemWrite)->id(), + APIPermission::kFileSystemWrite); + EXPECT_EQ(apis.size(), 1u); + + apis.erase(APIPermission::kFileSystemWrite); + apis.insert(APIPermission::kFileSystemDirectory); + apis.AddImpliedPermissions(); + + EXPECT_EQ(apis.find(APIPermission::kFileSystemDirectory)->id(), + APIPermission::kFileSystemDirectory); + EXPECT_EQ(apis.size(), 1u); + + apis.insert(APIPermission::kFileSystemWrite); + apis.AddImpliedPermissions(); + + EXPECT_EQ(apis.find(APIPermission::kFileSystemWrite)->id(), + APIPermission::kFileSystemWrite); + EXPECT_EQ(apis.find(APIPermission::kFileSystemDirectory)->id(), + APIPermission::kFileSystemDirectory); + EXPECT_EQ(apis.find(APIPermission::kFileSystemWriteDirectory)->id(), + APIPermission::kFileSystemWriteDirectory); + EXPECT_EQ(apis.size(), 3u); +} + +} // namespace extensions diff --git a/extensions/common/permissions/permissions_info.cc b/extensions/common/permissions/permissions_info.cc new file mode 100644 index 0000000..bac3d9c1 --- /dev/null +++ b/extensions/common/permissions/permissions_info.cc @@ -0,0 +1,101 @@ +// 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/permissions_info.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "extensions/common/extensions_client.h" + +namespace extensions { + +static base::LazyInstance<PermissionsInfo> g_permissions_info = + LAZY_INSTANCE_INITIALIZER; + +// static +PermissionsInfo* PermissionsInfo::GetInstance() { + return g_permissions_info.Pointer(); +} + +PermissionsInfo::~PermissionsInfo() { + STLDeleteContainerPairSecondPointers(id_map_.begin(), id_map_.end()); +} + +const APIPermissionInfo* PermissionsInfo::GetByID( + APIPermission::ID id) const { + IDMap::const_iterator i = id_map_.find(id); + return (i == id_map_.end()) ? NULL : i->second; +} + +const APIPermissionInfo* PermissionsInfo::GetByName( + const std::string& name) const { + NameMap::const_iterator i = name_map_.find(name); + return (i == name_map_.end()) ? NULL : i->second; +} + +APIPermissionSet PermissionsInfo::GetAll() const { + APIPermissionSet permissions; + for (IDMap::const_iterator i = id_map_.begin(); i != id_map_.end(); ++i) + permissions.insert(i->second->id()); + return permissions; +} + +APIPermissionSet PermissionsInfo::GetAllByName( + const std::set<std::string>& permission_names) const { + APIPermissionSet permissions; + for (std::set<std::string>::const_iterator i = permission_names.begin(); + i != permission_names.end(); ++i) { + const APIPermissionInfo* permission_info = GetByName(*i); + if (permission_info) + permissions.insert(permission_info->id()); + } + return permissions; +} + +bool PermissionsInfo::HasChildPermissions(const std::string& name) const { + NameMap::const_iterator i = name_map_.lower_bound(name + '.'); + if (i == name_map_.end()) return false; + return StartsWithASCII(i->first, name + '.', true); +} + +PermissionsInfo::PermissionsInfo() + : hosted_app_permission_count_(0), + permission_count_(0) { + DCHECK(ExtensionsClient::Get()); + InitializeWithProvider(ExtensionsClient::Get()->GetPermissionsProvider()); +} + +void PermissionsInfo::InitializeWithProvider( + const PermissionsProvider& provider) { + std::vector<APIPermissionInfo*> permissions = provider.GetAllPermissions(); + std::vector<PermissionsProvider::AliasInfo> aliases = + provider.GetAllAliases(); + + for (size_t i = 0; i < permissions.size(); ++i) + RegisterPermission(permissions[i]); + for (size_t i = 0; i < aliases.size(); ++i) + RegisterAlias(aliases[i].name, aliases[i].alias); +} + +void PermissionsInfo::RegisterAlias( + const char* name, + const char* alias) { + DCHECK(ContainsKey(name_map_, name)); + DCHECK(!ContainsKey(name_map_, alias)); + name_map_[alias] = name_map_[name]; +} + +void PermissionsInfo::RegisterPermission(APIPermissionInfo* permission) { + DCHECK(!ContainsKey(id_map_, permission->id())); + DCHECK(!ContainsKey(name_map_, permission->name())); + + id_map_[permission->id()] = permission; + name_map_[permission->name()] = permission; + + permission_count_++; +} + +} // namespace extensions diff --git a/extensions/common/permissions/permissions_info.h b/extensions/common/permissions/permissions_info.h new file mode 100644 index 0000000..9be7e6d --- /dev/null +++ b/extensions/common/permissions/permissions_info.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_INFO_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_INFO_H_ + +#include <map> +#include <set> +#include <string> + +#include "base/callback.h" +#include "base/lazy_instance.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/api_permission_set.h" +#include "extensions/common/permissions/permission_message.h" +#include "extensions/common/permissions/permissions_provider.h" + +namespace extensions { + +// A global object that holds the extension permission instances and provides +// methods for accessing them. +class PermissionsInfo { + public: + static PermissionsInfo* GetInstance(); + + virtual ~PermissionsInfo(); + + // Returns the permission with the given |id|, and NULL if it doesn't exist. + const APIPermissionInfo* GetByID(APIPermission::ID id) const; + + // Returns the permission with the given |name|, and NULL if none + // exists. + const APIPermissionInfo* GetByName(const std::string& name) const; + + // Returns a set containing all valid api permission ids. + APIPermissionSet GetAll() const; + + // Converts all the permission names in |permission_names| to permission ids. + APIPermissionSet GetAllByName( + const std::set<std::string>& permission_names) const; + + // Checks if any permissions have names that start with |name| followed by a + // period. + bool HasChildPermissions(const std::string& name) const; + + // Gets the total number of API permissions. + size_t get_permission_count() const { return permission_count_; } + + private: + friend struct base::DefaultLazyInstanceTraits<PermissionsInfo>; + + PermissionsInfo(); + + // Initializes the permissions from the provider. + void InitializeWithProvider(const PermissionsProvider& provider); + + // Registers an |alias| for a given permission |name|. + void RegisterAlias(const char* name, const char* alias); + + // Registers a permission with the specified attributes and flags. + void RegisterPermission(APIPermissionInfo* permission); + + // Maps permission ids to permissions. + typedef std::map<APIPermission::ID, APIPermissionInfo*> IDMap; + + // Maps names and aliases to permissions. + typedef std::map<std::string, APIPermissionInfo*> NameMap; + + IDMap id_map_; + NameMap name_map_; + + size_t hosted_app_permission_count_; + size_t permission_count_; + + DISALLOW_COPY_AND_ASSIGN(PermissionsInfo); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_INFO_H_ |