mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 08:12:40 +01:00
Add a CanCDN flag to uploaded files
Summary: CanCDN flag indicates that a file can be served + cached via anonymous content distribution networks. Once D10054 lands, any files that lack the CanCDN flag will require a one-time-use token and headers will prohibit cache to protect sensitive files from unauthorized access. This diff separates the CanCDN changes from the code that enforces these restrictions in D10054 so that the changes can be tested and refined independently. Test Plan: Work in progress Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: rush898, qgil, epriestley, aklapper, Korvin Maniphest Tasks: T5685 Differential Revision: https://secure.phabricator.com/D10166
This commit is contained in:
parent
c0585b7a34
commit
12aaa942ac
8 changed files with 75 additions and 1 deletions
|
@ -52,6 +52,7 @@ $file = PhabricatorFile::newFromFileData(
|
|||
$data,
|
||||
array(
|
||||
'name' => basename($path),
|
||||
'canCDN' => true,
|
||||
));
|
||||
|
||||
$macro = id(new PhabricatorFileImageMacro())
|
||||
|
|
|
@ -243,6 +243,7 @@ abstract class PhabricatorAuthProvider {
|
|||
$image_uri,
|
||||
array(
|
||||
'name' => $name,
|
||||
'canCDN' => true
|
||||
));
|
||||
unset($unguarded);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ final class PhabricatorImageTransformer {
|
|||
array(
|
||||
'name' => 'meme-'.$file->getName(),
|
||||
'ttl' => time() + 60 * 60 * 24,
|
||||
'canCDN' => true,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -30,6 +31,7 @@ final class PhabricatorImageTransformer {
|
|||
$image,
|
||||
array(
|
||||
'name' => 'thumb-'.$file->getName(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -45,6 +47,7 @@ final class PhabricatorImageTransformer {
|
|||
$image,
|
||||
array(
|
||||
'name' => 'profile-'.$file->getName(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -58,6 +61,7 @@ final class PhabricatorImageTransformer {
|
|||
$image,
|
||||
array(
|
||||
'name' => 'preview-'.$file->getName(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -79,6 +83,7 @@ final class PhabricatorImageTransformer {
|
|||
$image,
|
||||
array(
|
||||
'name' => 'conpherence-'.$file->getName(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ final class PhabricatorFileComposeController
|
|||
$data,
|
||||
array(
|
||||
'name' => 'project.png',
|
||||
'canCDN' => true,
|
||||
));
|
||||
|
||||
$content = array(
|
||||
|
|
|
@ -7,10 +7,12 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
PhabricatorFlaggableInterface,
|
||||
PhabricatorPolicyInterface {
|
||||
|
||||
const ONETIME_TEMPORARY_TOKEN_TYPE = 'file:onetime';
|
||||
const STORAGE_FORMAT_RAW = 'raw';
|
||||
|
||||
const METADATA_IMAGE_WIDTH = 'width';
|
||||
const METADATA_IMAGE_HEIGHT = 'height';
|
||||
const METADATA_CAN_CDN = 'cancdn';
|
||||
|
||||
protected $name;
|
||||
protected $mimeType;
|
||||
|
@ -202,7 +204,6 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
}
|
||||
|
||||
private static function buildFromFileData($data, array $params = array()) {
|
||||
$selector = PhabricatorEnv::newObjectFromConfig('storage.engine-selector');
|
||||
|
||||
if (isset($params['storageEngines'])) {
|
||||
$engines = $params['storageEngines'];
|
||||
|
@ -270,6 +271,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
$file->setViewPolicy($params['viewPolicy']);
|
||||
}
|
||||
|
||||
if (idx($params, 'canCDN')) {
|
||||
$file->setCanCDN(true);
|
||||
}
|
||||
|
||||
$file->setStorageEngine($engine_identifier);
|
||||
$file->setStorageHandle($data_handle);
|
||||
|
||||
|
@ -852,6 +857,63 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return idx($this->metadata, self::METADATA_IMAGE_WIDTH);
|
||||
}
|
||||
|
||||
public function getCanCDN() {
|
||||
if (!$this->isViewableImage()) {
|
||||
return false;
|
||||
}
|
||||
return idx($this->metadata, self::METADATA_CAN_CDN);
|
||||
}
|
||||
|
||||
public function setCanCDN($can_cdn) {
|
||||
$this->metadata[self::METADATA_CAN_CDN] = $can_cdn ? 1 : 0;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function generateOneTimeToken() {
|
||||
$key = Filesystem::readRandomCharacters(16);
|
||||
|
||||
// Save the new secret.
|
||||
return id(new PhabricatorAuthTemporaryToken())
|
||||
->setObjectPHID($this->getPHID())
|
||||
->setTokenType(self::ONETIME_TEMPORARY_TOKEN_TYPE)
|
||||
->setTokenExpires(time() + phutil_units('1 hour in seconds'))
|
||||
->setTokenCode(PhabricatorHash::digest($key))
|
||||
->save();
|
||||
}
|
||||
|
||||
public function validateOneTimeToken($token_code) {
|
||||
$token = id(new PhabricatorAuthTemporaryTokenQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withObjectPHIDs(array($this->getPHID()))
|
||||
->withTokenTypes(array(self::ONETIME_TEMPORARY_TOKEN_TYPE))
|
||||
->withExpired(false)
|
||||
->withTokenCodes(array($token_code))
|
||||
->executeOne();
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/** Get the CDN uri for this file
|
||||
* This will generate a one-time-use token if
|
||||
* security.alternate_file_domain is set in the config.
|
||||
*/
|
||||
public function getCDNURIWithToken() {
|
||||
if (!$this->getPHID()) {
|
||||
throw new Exception(
|
||||
'You must save a file before you can generate a CDN URI.');
|
||||
}
|
||||
$name = phutil_escape_uri($this->getName());
|
||||
|
||||
$path = '/file/data'
|
||||
.'/'.$this->getSecretKey()
|
||||
.'/'.$this->getPHID()
|
||||
.'/'.$this->generateOneTimeToken()
|
||||
.'/'.$name;
|
||||
return PhabricatorEnv::getCDNURI($path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write the policy edge between this file and some object.
|
||||
*
|
||||
|
|
|
@ -64,6 +64,7 @@ final class PhabricatorMacroEditController extends PhabricatorMacroController {
|
|||
'name' => $request->getStr('name'),
|
||||
'authorPHID' => $user->getPHID(),
|
||||
'isExplicitUpload' => true,
|
||||
'canCDN' => true,
|
||||
));
|
||||
} else if ($request->getStr('url')) {
|
||||
try {
|
||||
|
@ -73,6 +74,7 @@ final class PhabricatorMacroEditController extends PhabricatorMacroController {
|
|||
'name' => $request->getStr('name'),
|
||||
'authorPHID' => $user->getPHID(),
|
||||
'isExplicitUpload' => true,
|
||||
'canCDN' => true,
|
||||
));
|
||||
} catch (Exception $ex) {
|
||||
$errors[] = pht('Could not fetch URL: %s', $ex->getMessage());
|
||||
|
|
|
@ -53,6 +53,7 @@ final class PhabricatorPeopleProfilePictureController
|
|||
$_FILES['picture'],
|
||||
array(
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
} else {
|
||||
$e_file = pht('Required');
|
||||
|
|
|
@ -50,6 +50,7 @@ final class PhabricatorProjectEditPictureController
|
|||
$_FILES['picture'],
|
||||
array(
|
||||
'authorPHID' => $viewer->getPHID(),
|
||||
'canCDN' => true,
|
||||
));
|
||||
} else {
|
||||
$e_file = pht('Required');
|
||||
|
|
Loading…
Reference in a new issue