@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+196 -4
+38 -2
src/applications/files/query/PhabricatorFileQuery.php
··· 117 117 // First, load the edges. 118 118 119 119 $edge_type = PhabricatorEdgeConfig::TYPE_FILE_HAS_OBJECT; 120 - $phids = mpull($files, 'getPHID'); 120 + $file_phids = mpull($files, 'getPHID'); 121 121 $edges = id(new PhabricatorEdgeQuery()) 122 - ->withSourcePHIDs($phids) 122 + ->withSourcePHIDs($file_phids) 123 123 ->withEdgeTypes(array($edge_type)) 124 124 ->execute(); 125 125 ··· 131 131 $object_phids[$phid] = true; 132 132 } 133 133 } 134 + 135 + // If this file is a transform of another file, load that file too. If you 136 + // can see the original file, you can see the thumbnail. 137 + 138 + // TODO: It might be nice to put this directly on PhabricatorFile and remove 139 + // the PhabricatorTransformedFile table, which would be a little simpler. 140 + 141 + $xforms = id(new PhabricatorTransformedFile())->loadAllWhere( 142 + 'transformedPHID IN (%Ls)', 143 + $file_phids); 144 + $xform_phids = mpull($xforms, 'getOriginalPHID', 'getTransformedPHID'); 145 + foreach ($xform_phids as $derived_phid => $original_phid) { 146 + $object_phids[$original_phid] = true; 147 + } 148 + 134 149 $object_phids = array_keys($object_phids); 135 150 136 151 // Now, load the objects. ··· 154 169 foreach ($files as $file) { 155 170 $file_objects = array_select_keys($objects, $file->getObjectPHIDs()); 156 171 $file->attachObjects($file_objects); 172 + } 173 + 174 + foreach ($files as $key => $file) { 175 + $original_phid = idx($xform_phids, $file->getPHID()); 176 + if ($original_phid == PhabricatorPHIDConstants::PHID_VOID) { 177 + // This is a special case for builtin files, which are handled 178 + // oddly. 179 + $original = null; 180 + } else if ($original_phid) { 181 + $original = idx($objects, $original_phid); 182 + if (!$original) { 183 + // If the viewer can't see the original file, also prevent them from 184 + // seeing the transformed file. 185 + $this->didRejectResult($file); 186 + unset($files[$key]); 187 + continue; 188 + } 189 + } else { 190 + $original = null; 191 + } 192 + $file->attachOriginalFile($original); 157 193 } 158 194 159 195 return $files;
+45 -2
src/applications/files/storage/PhabricatorFile.php
··· 50 50 51 51 private $objects = self::ATTACHABLE; 52 52 private $objectPHIDs = self::ATTACHABLE; 53 + private $originalFile = self::ATTACHABLE; 54 + 55 + public static function initializeNewFile() { 56 + return id(new PhabricatorFile()) 57 + ->attachOriginalFile(null) 58 + ->attachObjects(array()) 59 + ->attachObjectPHIDs(array()); 60 + } 53 61 54 62 public function getConfiguration() { 55 63 return array( ··· 190 198 $copy_of_byteSize = $file->getByteSize(); 191 199 $copy_of_mimeType = $file->getMimeType(); 192 200 193 - $new_file = new PhabricatorFile(); 201 + $new_file = PhabricatorFile::initializeNewFile(); 194 202 195 203 $new_file->setByteSize($copy_of_byteSize); 196 204 ··· 226 234 throw new Exception('No valid storage engines are available!'); 227 235 } 228 236 229 - $file = new PhabricatorFile(); 237 + $file = PhabricatorFile::initializeNewFile(); 230 238 231 239 $data_handle = null; 232 240 $engine_identifier = null; ··· 848 856 return $this; 849 857 } 850 858 859 + public function getOriginalFile() { 860 + return $this->assertAttached($this->originalFile); 861 + } 862 + 863 + public function attachOriginalFile(PhabricatorFile $file = null) { 864 + $this->originalFile = $file; 865 + return $this; 866 + } 867 + 851 868 public function getImageHeight() { 852 869 if (!$this->isViewableImage()) { 853 870 return null; ··· 943 960 944 961 945 962 /** 963 + * Remove the policy edge between this file and some object. 964 + * 965 + * @param phid Object PHID to detach from. 966 + * @return this 967 + */ 968 + public function detachFromObject($phid) { 969 + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE; 970 + 971 + id(new PhabricatorEdgeEditor()) 972 + ->removeEdge($phid, $edge_type, $this->getPHID()) 973 + ->save(); 974 + 975 + return $this; 976 + } 977 + 978 + 979 + /** 946 980 * Configure a newly created file object according to specified parameters. 947 981 * 948 982 * This method is called both when creating a file from fresh data, and ··· 1033 1067 1034 1068 switch ($capability) { 1035 1069 case PhabricatorPolicyCapability::CAN_VIEW: 1070 + // If you can see the file this file is a transform of, you can see 1071 + // this file. 1072 + if ($this->getOriginalFile()) { 1073 + return true; 1074 + } 1075 + 1036 1076 // If you can see any object this file is attached to, you can see 1037 1077 // the file. 1038 1078 return (count($this->getObjects()) > 0); ··· 1049 1089 $out[] = pht( 1050 1090 'Files attached to objects are visible to users who can view '. 1051 1091 'those objects.'); 1092 + $out[] = pht( 1093 + 'Thumbnails are visible only to users who can view the original '. 1094 + 'file.'); 1052 1095 break; 1053 1096 } 1054 1097
+113
src/applications/files/storage/__tests__/PhabricatorFileTestCase.php
··· 8 8 ); 9 9 } 10 10 11 + public function testFileVisibility() { 12 + $engine = new PhabricatorTestStorageEngine(); 13 + $data = Filesystem::readRandomCharacters(64); 14 + 15 + $author = $this->generateNewTestUser(); 16 + $viewer = $this->generateNewTestUser(); 17 + $users = array($author, $viewer); 18 + 19 + $params = array( 20 + 'name' => 'test.dat', 21 + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 22 + 'authorPHID' => $author->getPHID(), 23 + 'storageEngines' => array( 24 + $engine, 25 + ), 26 + ); 27 + 28 + $file = PhabricatorFile::newFromFileData($data, $params); 29 + $filter = new PhabricatorPolicyFilter(); 30 + 31 + // Test bare file policies. 32 + $this->assertEqual( 33 + array( 34 + true, 35 + false, 36 + ), 37 + $this->canViewFile($users, $file), 38 + pht('File Visibility')); 39 + 40 + // Create an object and test object policies. 41 + 42 + $object = ManiphestTask::initializeNewTask($author); 43 + $object->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()); 44 + $object->save(); 45 + 46 + $this->assertTrue( 47 + $filter->hasCapability( 48 + $author, 49 + $object, 50 + PhabricatorPolicyCapability::CAN_VIEW), 51 + pht('Object Visible to Author')); 52 + 53 + $this->assertTrue( 54 + $filter->hasCapability( 55 + $viewer, 56 + $object, 57 + PhabricatorPolicyCapability::CAN_VIEW), 58 + pht('Object Visible to Others')); 59 + 60 + // Attach the file to the object and test that the association opens a 61 + // policy exception for the non-author viewer. 62 + 63 + $file->attachToObject($author, $object->getPHID()); 64 + 65 + // Test the attached file's visibility. 66 + $this->assertEqual( 67 + array( 68 + true, 69 + true, 70 + ), 71 + $this->canViewFile($users, $file), 72 + pht('Attached File Visibility')); 73 + 74 + // Create a "thumbnail" of the original file. 75 + $params = array( 76 + 'name' => 'test.thumb.dat', 77 + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 78 + 'storageEngines' => array( 79 + $engine, 80 + ), 81 + ); 82 + 83 + $xform = PhabricatorFile::newFromFileData($data, $params); 84 + 85 + id(new PhabricatorTransformedFile()) 86 + ->setOriginalPHID($file->getPHID()) 87 + ->setTransform('test-thumb') 88 + ->setTransformedPHID($xform->getPHID()) 89 + ->save(); 90 + 91 + // Test the thumbnail's visibility. 92 + $this->assertEqual( 93 + array( 94 + true, 95 + true, 96 + ), 97 + $this->canViewFile($users, $xform), 98 + pht('Attached Thumbnail Visibility')); 99 + 100 + // Detach the object and make sure it affects the thumbnail. 101 + $file->detachFromObject($object->getPHID()); 102 + 103 + // Test the detached thumbnail's visibility. 104 + $this->assertEqual( 105 + array( 106 + true, 107 + false, 108 + ), 109 + $this->canViewFile($users, $xform), 110 + pht('Detached Thumbnail Visibility')); 111 + } 112 + 113 + private function canViewFile(array $users, PhabricatorFile $file) { 114 + $results = array(); 115 + foreach ($users as $user) { 116 + $results[] = (bool)id(new PhabricatorFileQuery()) 117 + ->setViewer($user) 118 + ->withPHIDs(array($file->getPHID())) 119 + ->execute(); 120 + } 121 + return $results; 122 + } 123 + 11 124 public function testFileStorageReadWrite() { 12 125 $engine = new PhabricatorTestStorageEngine(); 13 126