Bug 910412 - Filesystem API permission request and checks. r=dhylands

This commit is contained in:
Yuan Xulei 2014-03-05 11:24:19 +08:00
parent 05cf321138
commit 4b1c019b74
17 changed files with 419 additions and 4 deletions

View File

@ -136,5 +136,11 @@ CreateDirectoryTask::HandlerCallback()
mPromise->MaybeResolve(dir);
}
void
CreateDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("create");
}
} // namespace dom
} // namespace mozilla

View File

@ -10,6 +10,7 @@
#include "mozilla/dom/FileSystemTaskBase.h"
#include "nsAutoPtr.h"
class nsCString;
class nsString;
namespace mozilla {
@ -36,6 +37,9 @@ public:
already_AddRefed<Promise>
GetPromise();
virtual void
GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;

View File

@ -7,9 +7,12 @@
#include "mozilla/dom/DeviceStorageFileSystem.h"
#include "DeviceStorage.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsDeviceStorage.h"
#include "nsIFile.h"
#include "nsPIDOMWindow.h"
@ -21,6 +24,8 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(
const nsAString& aStorageName)
: mDeviceStorage(nullptr)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
mStorageType = aStorageType;
mStorageName = aStorageName;
@ -30,6 +35,14 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(
mString.AppendLiteral("-");
mString.Append(mStorageName);
mIsTesting =
mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
// Get the permission name required to access the file system.
nsresult rv =
DeviceStorageTypeChecker::GetPermissionForType(mStorageType, mPermission);
NS_WARN_IF(NS_FAILED(rv));
// Get the local path of the file system root.
// Since the child process is not allowed to access the file system, we only
// do this from the parent process.
@ -42,6 +55,15 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(
getter_AddRefs(rootFile));
NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath)));
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
mNormalizedLocalRootPath);
// DeviceStorageTypeChecker is a singleton object and must be initialized on
// the main thread. We initialize it here so that we can use it on the worker
// thread.
DebugOnly<DeviceStorageTypeChecker*> typeChecker
= DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
}
DeviceStorageFileSystem::~DeviceStorageFileSystem()
@ -87,5 +109,29 @@ DeviceStorageFileSystem::GetRootName() const
return mStorageName;
}
bool
DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
{
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
"Should be on parent process!");
MOZ_ASSERT(aFile);
// Check if this file belongs to this storage.
nsAutoString path;
if (NS_FAILED(aFile->GetPath(path))) {
return false;
}
FileSystemUtils::LocalPathToNormalizedPath(path, path);
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
return false;
}
// Check if the file type is compatible with the storage type.
DeviceStorageTypeChecker* typeChecker
= DeviceStorageTypeChecker::CreateOrGet();
MOZ_ASSERT(typeChecker);
return typeChecker->Check(mStorageType, aFile);
}
} // namespace dom
} // namespace mozilla

View File

@ -35,6 +35,10 @@ public:
virtual const nsAString&
GetRootName() const MOZ_OVERRIDE;
virtual bool
IsSafeFile(nsIFile* aFile) const MOZ_OVERRIDE;
private:
virtual
~DeviceStorageFileSystem();
@ -45,6 +49,7 @@ private:
// The local path of the root. Only available in the parent process.
// In the child process, we don't use it and its value should be empty.
nsString mLocalRootPath;
nsString mNormalizedLocalRootPath;
nsDOMDeviceStorage* mDeviceStorage;
};

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/Directory.h"
#include "CreateDirectoryTask.h"
#include "FileSystemPermissionRequest.h"
#include "GetFileOrDirectoryTask.h"
#include "nsCharSeparatedTokenizer.h"
@ -39,7 +40,7 @@ Directory::GetRoot(FileSystemBase* aFileSystem)
{
nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
aFileSystem, EmptyString(), true);
task->Start();
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}
@ -102,7 +103,7 @@ Directory::CreateDirectory(const nsAString& aPath)
nsRefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
fs, realPath);
task->SetError(error);
task->Start();
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}
@ -118,7 +119,7 @@ Directory::Get(const nsAString& aPath)
nsRefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
fs, realPath, false);
task->SetError(error);
task->Start();
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}

View File

@ -43,6 +43,7 @@ FileSystemBase::FromString(const nsAString& aString)
}
FileSystemBase::FileSystemBase()
: mIsTesting(false)
{
}
@ -56,5 +57,11 @@ FileSystemBase::GetWindow() const
return nullptr;
}
bool
FileSystemBase::IsSafeFile(nsIFile* aFile) const
{
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -56,11 +56,34 @@ public:
*/
virtual const nsAString&
GetRootName() const = 0;
virtual bool
IsSafeFile(nsIFile* aFile) const;
/*
* Get the permission name required to access this file system.
*/
const nsCString&
GetPermission() const
{
return mPermission;
}
bool
IsTesting() const
{
return mIsTesting;
}
protected:
virtual ~FileSystemBase();
// The string representation of the file system.
nsString mString;
// The permission name required to access the file system.
nsCString mPermission;
bool mIsTesting;
};
} // namespace dom

View File

@ -0,0 +1,190 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FileSystemPermissionRequest.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/TabChild.h"
#include "nsIDocument.h"
#include "nsPIDOMWindow.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS2(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest)
// static
void
FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
{
MOZ_ASSERT(aTask, "aTask should not be null!");
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<FileSystemPermissionRequest> request =
new FileSystemPermissionRequest(aTask);
NS_DispatchToCurrentThread(request);
}
FileSystemPermissionRequest::FileSystemPermissionRequest(
FileSystemTaskBase* aTask)
: mTask(aTask)
{
MOZ_ASSERT(mTask, "aTask should not be null!");
MOZ_ASSERT(NS_IsMainThread());
mTask->GetPermissionAccessType(mPermissionAccess);
nsRefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
if (!filesystem) {
return;
}
mPermissionType = filesystem->GetPermission();
mWindow = filesystem->GetWindow();
if (!mWindow) {
return;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
if (!doc) {
return;
}
mPrincipal = doc->NodePrincipal();
}
FileSystemPermissionRequest::~FileSystemPermissionRequest()
{
}
bool
FileSystemPermissionRequest::Recv__delete__(const bool& aAllow,
const InfallibleTArray<PermissionChoice>& aChoices)
{
MOZ_ASSERT(aChoices.IsEmpty(),
"FileSystemPermissionRequest doesn't support permission choice");
if (aAllow) {
Allow(JS::UndefinedHandleValue);
} else {
Cancel();
}
return true;
}
void
FileSystemPermissionRequest::IPDLRelease()
{
Release();
}
NS_IMETHODIMP
FileSystemPermissionRequest::GetTypes(nsIArray** aTypes)
{
nsTArray<nsString> emptyOptions;
return CreatePermissionArray(mPermissionType,
mPermissionAccess,
emptyOptions,
aTypes);
}
NS_IMETHODIMP
FileSystemPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
{
NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
return NS_OK;
}
NS_IMETHODIMP
FileSystemPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow)
{
NS_IF_ADDREF(*aRequestingWindow = mWindow);
return NS_OK;
}
NS_IMETHODIMP
FileSystemPermissionRequest::GetElement(nsIDOMElement** aRequestingElement)
{
*aRequestingElement = nullptr;
return NS_OK;
}
NS_IMETHODIMP
FileSystemPermissionRequest::Cancel()
{
MOZ_ASSERT(NS_IsMainThread());
mTask->SetError(NS_ERROR_DOM_SECURITY_ERR);
mTask->Start();
return NS_OK;
}
NS_IMETHODIMP
FileSystemPermissionRequest::Allow(JS::HandleValue aChoices)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aChoices.isUndefined());
mTask->Start();
return NS_OK;
}
NS_IMETHODIMP
FileSystemPermissionRequest::Run()
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<FileSystemBase> filesystem = mTask->GetFileSystem();
if (!filesystem) {
Cancel();
return NS_OK;
}
if (filesystem->IsTesting()) {
Allow(JS::UndefinedHandleValue);
return NS_OK;
}
if (FileSystemUtils::IsParentProcess()) {
nsCOMPtr<nsIContentPermissionPrompt> prompt
= do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
if (!prompt || NS_FAILED(prompt->Prompt(this))) {
Cancel();
}
return NS_OK;
}
if (!mWindow) {
Cancel();
return NS_OK;
}
// because owner implements nsITabChild, we can assume that it is
// the one and only TabChild.
TabChild* child = TabChild::GetFrom(mWindow->GetDocShell());
if (!child) {
Cancel();
return NS_OK;
}
// Retain a reference so the object isn't deleted without IPDL's
// knowledge. Corresponding release occurs in
// DeallocPContentPermissionRequest.
AddRef();
nsTArray<PermissionRequest> permArray;
nsTArray<nsString> emptyOptions;
permArray.AppendElement(PermissionRequest(mPermissionType,
mPermissionAccess,
emptyOptions));
child->SendPContentPermissionRequestConstructor(
this, permArray, IPC::Principal(mPrincipal));
Sendprompt();
return NS_OK;
}
} /* namespace dom */
} /* namespace mozilla */

View File

@ -0,0 +1,61 @@
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_FileSystemPermissionRequest_h
#define mozilla_dom_FileSystemPermissionRequest_h
#include "PCOMContentPermissionRequestChild.h"
#include "nsAutoPtr.h"
#include "nsContentPermissionHelper.h"
#include "nsIRunnable.h"
class nsCString;
class nsPIDOMWindow;
namespace mozilla {
namespace dom {
class FileSystemTaskBase;
class FileSystemPermissionRequest MOZ_FINAL
: public nsIContentPermissionRequest
, public nsIRunnable
, public PCOMContentPermissionRequestChild
{
public:
// Request permission for the given task.
static void
RequestForTask(FileSystemTaskBase* aTask);
// Overrides PCOMContentPermissionRequestChild
virtual void
IPDLRelease() MOZ_OVERRIDE;
bool
Recv__delete__(const bool& aAllow,
const InfallibleTArray<PermissionChoice>& aChoices) MOZ_OVERRIDE;
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICONTENTPERMISSIONREQUEST
NS_DECL_NSIRUNNABLE
private:
FileSystemPermissionRequest(FileSystemTaskBase* aTask);
virtual
~FileSystemPermissionRequest();
nsCString mPermissionType;
nsCString mPermissionAccess;
nsRefPtr<FileSystemTaskBase> mTask;
nsCOMPtr<nsPIDOMWindow> mWindow;
nsCOMPtr<nsIPrincipal> mPrincipal;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_FileSystemPermissionRequest_h

View File

@ -8,6 +8,7 @@
#include "CreateDirectoryTask.h"
#include "GetFileOrDirectoryTask.h"
#include "mozilla/AppProcessChecker.h"
#include "mozilla/dom/FileSystemBase.h"
namespace mozilla {
@ -54,6 +55,22 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
return false;
}
if (!mFileSystem->IsTesting()) {
// Check the content process permission.
nsCString access;
task->GetPermissionAccessType(access);
nsAutoCString permissionName;
permissionName = mFileSystem->GetPermission();
permissionName.AppendLiteral("-");
permissionName.Append(access);
if (!AssertAppProcessPermission(aParent, permissionName.get())) {
return false;
}
}
task->Start();
return true;
}

View File

@ -44,6 +44,13 @@ FileSystemTaskBase::~FileSystemTaskBase()
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
}
already_AddRefed<FileSystemBase>
FileSystemTaskBase::GetFileSystem()
{
nsRefPtr<FileSystemBase> filesystem = do_QueryReferent(mFileSystem);
return filesystem.forget();
}
void
FileSystemTaskBase::Start()
{
@ -152,7 +159,8 @@ FileSystemTaskBase::SetError(const nsresult& aErrorValue)
{
uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
module == NS_ERROR_MODULE_DOM_FILE) {
module == NS_ERROR_MODULE_DOM_FILE ||
module == NS_ERROR_MODULE_DOM) {
mErrorValue = aErrorValue;
return;
}

View File

@ -126,6 +126,14 @@ public:
void
SetError(const nsresult& aErrorCode);
already_AddRefed<FileSystemBase>
GetFileSystem();
/*
* Get the type of permission access required to perform this task.
*/
virtual void
GetPermissionAccessType(nsCString& aAccess) const = 0;
NS_DECL_NSIRUNNABLE
protected:

View File

@ -47,6 +47,24 @@ FileSystemUtils::NormalizedPathToLocalPath(const nsAString& aNorm,
aLocal = result;
}
// static
bool
FileSystemUtils::IsDescendantPath(const nsAString& aPath,
const nsAString& aDescendantPath)
{
// The descendant path should begin with its ancestor path.
nsAutoString prefix;
prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR);
// Check the sub-directory path to see if it has the parent path as prefix.
if (aDescendantPath.Length() < prefix.Length() ||
!StringBeginsWith(aDescendantPath, prefix)) {
return false;
}
return true;
}
// static
bool
FileSystemUtils::IsParentProcess()

View File

@ -34,6 +34,13 @@ public:
static void
NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal);
/*
* Return true if aDescendantPath is a descendant of aPath. Both aPath and
* aDescendantPath are absolute DOM path.
*/
static bool
IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
static bool
IsParentProcess();

View File

@ -182,6 +182,11 @@ GetFileOrDirectoryTask::Work()
return;
}
if (!filesystem->IsSafeFile(file)) {
SetError(NS_ERROR_DOM_SECURITY_ERR);
return;
}
mTargetFile = new nsDOMFileFile(file);
}
}
@ -211,5 +216,11 @@ GetFileOrDirectoryTask::HandlerCallback()
mPromise->MaybeResolve(mTargetFile);
}
void
GetFileOrDirectoryTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("read");
}
} // namespace dom
} // namespace mozilla

View File

@ -39,6 +39,8 @@ public:
already_AddRefed<Promise>
GetPromise();
virtual void
GetPermissionAccessType(nsCString& aAccess) const MOZ_OVERRIDE;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aFileSystem) const MOZ_OVERRIDE;

View File

@ -18,6 +18,7 @@ SOURCES += [
'DeviceStorageFileSystem.cpp',
'Directory.cpp',
'FileSystemBase.cpp',
'FileSystemPermissionRequest.cpp',
'FileSystemRequestParent.cpp',
'FileSystemTaskBase.cpp',
'FileSystemUtils.cpp',