1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 08:52:39 +01:00

Bind file thumbnail visibility to the visibility of the original files

Summary:
Ref T6013. Currently, when we create a thumbnail, it gets its own (default) file visibility policy.

In particular, this causes the issue in T6013: thumbnails get "all users" visibility, which does not include logged-out users.

Instead, a thumbnail should just have the same visibility as the original file does. Enforce this:

  - When loading thumbnails, reject thumbnails with invisible originals.
  - When filtering thumbnails, permit thumbnails with visible originals.

Test Plan: As a logged-out user, thumbnails are now visible when the original files are attached to visible objects.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T6013

Differential Revision: https://secure.phabricator.com/D10410
This commit is contained in:
epriestley 2014-09-04 12:49:31 -07:00
parent 8efea3abe9
commit 2e0361bd98
3 changed files with 196 additions and 4 deletions

View file

@ -117,9 +117,9 @@ final class PhabricatorFileQuery
// First, load the edges.
$edge_type = PhabricatorEdgeConfig::TYPE_FILE_HAS_OBJECT;
$phids = mpull($files, 'getPHID');
$file_phids = mpull($files, 'getPHID');
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withSourcePHIDs($file_phids)
->withEdgeTypes(array($edge_type))
->execute();
@ -131,6 +131,21 @@ final class PhabricatorFileQuery
$object_phids[$phid] = true;
}
}
// If this file is a transform of another file, load that file too. If you
// can see the original file, you can see the thumbnail.
// TODO: It might be nice to put this directly on PhabricatorFile and remove
// the PhabricatorTransformedFile table, which would be a little simpler.
$xforms = id(new PhabricatorTransformedFile())->loadAllWhere(
'transformedPHID IN (%Ls)',
$file_phids);
$xform_phids = mpull($xforms, 'getOriginalPHID', 'getTransformedPHID');
foreach ($xform_phids as $derived_phid => $original_phid) {
$object_phids[$original_phid] = true;
}
$object_phids = array_keys($object_phids);
// Now, load the objects.
@ -156,6 +171,27 @@ final class PhabricatorFileQuery
$file->attachObjects($file_objects);
}
foreach ($files as $key => $file) {
$original_phid = idx($xform_phids, $file->getPHID());
if ($original_phid == PhabricatorPHIDConstants::PHID_VOID) {
// This is a special case for builtin files, which are handled
// oddly.
$original = null;
} else if ($original_phid) {
$original = idx($objects, $original_phid);
if (!$original) {
// If the viewer can't see the original file, also prevent them from
// seeing the transformed file.
$this->didRejectResult($file);
unset($files[$key]);
continue;
}
} else {
$original = null;
}
$file->attachOriginalFile($original);
}
return $files;
}

View file

@ -50,6 +50,14 @@ final class PhabricatorFile extends PhabricatorFileDAO
private $objects = self::ATTACHABLE;
private $objectPHIDs = self::ATTACHABLE;
private $originalFile = self::ATTACHABLE;
public static function initializeNewFile() {
return id(new PhabricatorFile())
->attachOriginalFile(null)
->attachObjects(array())
->attachObjectPHIDs(array());
}
public function getConfiguration() {
return array(
@ -190,7 +198,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
$copy_of_byteSize = $file->getByteSize();
$copy_of_mimeType = $file->getMimeType();
$new_file = new PhabricatorFile();
$new_file = PhabricatorFile::initializeNewFile();
$new_file->setByteSize($copy_of_byteSize);
@ -226,7 +234,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
throw new Exception('No valid storage engines are available!');
}
$file = new PhabricatorFile();
$file = PhabricatorFile::initializeNewFile();
$data_handle = null;
$engine_identifier = null;
@ -848,6 +856,15 @@ final class PhabricatorFile extends PhabricatorFileDAO
return $this;
}
public function getOriginalFile() {
return $this->assertAttached($this->originalFile);
}
public function attachOriginalFile(PhabricatorFile $file = null) {
$this->originalFile = $file;
return $this;
}
public function getImageHeight() {
if (!$this->isViewableImage()) {
return null;
@ -942,6 +959,23 @@ final class PhabricatorFile extends PhabricatorFileDAO
}
/**
* Remove the policy edge between this file and some object.
*
* @param phid Object PHID to detach from.
* @return this
*/
public function detachFromObject($phid) {
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE;
id(new PhabricatorEdgeEditor())
->removeEdge($phid, $edge_type, $this->getPHID())
->save();
return $this;
}
/**
* Configure a newly created file object according to specified parameters.
*
@ -1033,6 +1067,12 @@ final class PhabricatorFile extends PhabricatorFileDAO
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
// If you can see the file this file is a transform of, you can see
// this file.
if ($this->getOriginalFile()) {
return true;
}
// If you can see any object this file is attached to, you can see
// the file.
return (count($this->getObjects()) > 0);
@ -1049,6 +1089,9 @@ final class PhabricatorFile extends PhabricatorFileDAO
$out[] = pht(
'Files attached to objects are visible to users who can view '.
'those objects.');
$out[] = pht(
'Thumbnails are visible only to users who can view the original '.
'file.');
break;
}

View file

@ -8,6 +8,119 @@ final class PhabricatorFileTestCase extends PhabricatorTestCase {
);
}
public function testFileVisibility() {
$engine = new PhabricatorTestStorageEngine();
$data = Filesystem::readRandomCharacters(64);
$author = $this->generateNewTestUser();
$viewer = $this->generateNewTestUser();
$users = array($author, $viewer);
$params = array(
'name' => 'test.dat',
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
'authorPHID' => $author->getPHID(),
'storageEngines' => array(
$engine,
),
);
$file = PhabricatorFile::newFromFileData($data, $params);
$filter = new PhabricatorPolicyFilter();
// Test bare file policies.
$this->assertEqual(
array(
true,
false,
),
$this->canViewFile($users, $file),
pht('File Visibility'));
// Create an object and test object policies.
$object = ManiphestTask::initializeNewTask($author);
$object->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy());
$object->save();
$this->assertTrue(
$filter->hasCapability(
$author,
$object,
PhabricatorPolicyCapability::CAN_VIEW),
pht('Object Visible to Author'));
$this->assertTrue(
$filter->hasCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_VIEW),
pht('Object Visible to Others'));
// Attach the file to the object and test that the association opens a
// policy exception for the non-author viewer.
$file->attachToObject($author, $object->getPHID());
// Test the attached file's visibility.
$this->assertEqual(
array(
true,
true,
),
$this->canViewFile($users, $file),
pht('Attached File Visibility'));
// Create a "thumbnail" of the original file.
$params = array(
'name' => 'test.thumb.dat',
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
'storageEngines' => array(
$engine,
),
);
$xform = PhabricatorFile::newFromFileData($data, $params);
id(new PhabricatorTransformedFile())
->setOriginalPHID($file->getPHID())
->setTransform('test-thumb')
->setTransformedPHID($xform->getPHID())
->save();
// Test the thumbnail's visibility.
$this->assertEqual(
array(
true,
true,
),
$this->canViewFile($users, $xform),
pht('Attached Thumbnail Visibility'));
// Detach the object and make sure it affects the thumbnail.
$file->detachFromObject($object->getPHID());
// Test the detached thumbnail's visibility.
$this->assertEqual(
array(
true,
false,
),
$this->canViewFile($users, $xform),
pht('Detached Thumbnail Visibility'));
}
private function canViewFile(array $users, PhabricatorFile $file) {
$results = array();
foreach ($users as $user) {
$results[] = (bool)id(new PhabricatorFileQuery())
->setViewer($user)
->withPHIDs(array($file->getPHID()))
->execute();
}
return $results;
}
public function testFileStorageReadWrite() {
$engine = new PhabricatorTestStorageEngine();