Page MenuHomePhabricator

D10054.id24177.diff
No OneTemporary

D10054.id24177.diff

diff --git a/src/applications/files/application/PhabricatorFilesApplication.php b/src/applications/files/application/PhabricatorFilesApplication.php
--- a/src/applications/files/application/PhabricatorFilesApplication.php
+++ b/src/applications/files/application/PhabricatorFilesApplication.php
@@ -51,6 +51,8 @@
'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorFileCommentController',
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorFileDeleteController',
'info/(?P<phid>[^/]+)/' => 'PhabricatorFileInfoController',
+ 'data/(?P<key>[^/]+)/(?P<phid>[^/]+)/(?P<token>[^/]+)/.*'
+ => 'PhabricatorFileDataController',
'data/(?P<key>[^/]+)/(?P<phid>[^/]+)/.*'
=> 'PhabricatorFileDataController',
'proxy/' => 'PhabricatorFileProxyController',
diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php
--- a/src/applications/files/controller/PhabricatorFileDataController.php
+++ b/src/applications/files/controller/PhabricatorFileDataController.php
@@ -4,10 +4,12 @@
private $phid;
private $key;
+ private $token;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
$this->key = $data['key'];
+ $this->token = isset($data['token']) ? $data['token'] : null;
}
public function shouldRequireLogin() {
@@ -17,18 +19,6 @@
public function processRequest() {
$request = $this->getRequest();
- $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
- $uri = new PhutilURI($alt);
- $alt_domain = $uri->getDomain();
- if ($alt_domain && ($alt_domain != $request->getHost())) {
- return id(new AphrontRedirectResponse())
- ->setURI($uri->setPath($request->getPath()));
- }
-
- // NOTE: This endpoint will ideally be accessed via CDN or otherwise on
- // a non-credentialed domain. Knowing the file's secret key gives you
- // access, regardless of authentication on the request itself.
-
$file = id(new PhabricatorFileQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($this->phid))
@@ -41,6 +31,36 @@
return new Aphront403Response();
}
+ // NOTE: This endpoint will ideally be accessed via CDN or otherwise on
+ // a non-credentialed domain. Knowing the file's secret key gives you
+ // access, regardless of authentication on the request itself.
+
+ $alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
+ $uri = new PhutilURI($alt);
+ $alt_domain = $uri->getDomain();
+
+ $path = $request->getPath();
+ if (!$file->getCanCDN()) {
+ if ($alt_domain != $request->getHost()) {
+ // generrate a one time token and redirect to the alt domain with
+ // token in the 3rd path component
+ $token = $file->generateOneTimeToken();
+ $path = explode('/', $path, 4);
+ $path = join('/', $path[0], $path[1],
+ $token->getTokenCode(),
+ $path[count($path) - 1]);
+ return id(new AphrontRedirectResponse())
+ ->setURI($uri->setPath($path));
+ } else if (empty($this->token)
+ || !$file->validateOneTimeToken($this->token)) {
+ $base_uri = PhabricatorEnv::getEnvConfig('phabrircator.base-uri');
+ $base_uri = new PhutilURI($base_uri);
+ $base_domain = $base_uri->getDomain();
+ return id(new AphrontRedirectResponse())
+ ->setURI($base_uri->setPath($request->getPath()));
+ }
+ }
+
$data = $file->loadFileData();
$response = new AphrontFileResponse();
$response->setContent($data);
diff --git a/src/applications/files/storage/PhabricatorFile.php b/src/applications/files/storage/PhabricatorFile.php
--- a/src/applications/files/storage/PhabricatorFile.php
+++ b/src/applications/files/storage/PhabricatorFile.php
@@ -7,10 +7,12 @@
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_NO_CDN = 'nocdn';
protected $name;
protected $mimeType;
@@ -848,6 +850,39 @@
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 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)
+ ->executeOne();
+ if ($token) {
+ $token->delete();
+ return true;
+ }
+ return false;
+ }
+
/**
* Write the policy edge between this file and some object.
*

File Metadata

Mime Type
text/plain
Expires
Mon, Aug 4, 1:01 PM (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
8864888
Default Alt Text
D10054.id24177.diff (5 KB)

Event Timeline