Page MenuHomePhabricator

D7751.id17532.diff

D7751.id17532.diff

diff --git a/resources/sql/patches/20131210.filenonce.sql b/resources/sql/patches/20131210.filenonce.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/patches/20131210.filenonce.sql
@@ -0,0 +1,9 @@
+CREATE TABLE {$NAMESPACE}_file.file_nonce (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ filePHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
+ nonce VARCHAR(32) NOT NULL COLLATE utf8_bin,
+ dateExpiry INT UNSIGNED NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_nonce` (filePHID, nonce)
+) ENGINE=InnoDB, COLLATE utf8_general_ci;
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1413,6 +1413,7 @@
'PhabricatorFileLinkListView' => 'view/layout/PhabricatorFileLinkListView.php',
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
+ 'PhabricatorFileNonce' => 'applications/files/storage/PhabricatorFileNonce.php',
'PhabricatorFilePHIDTypeFile' => 'applications/files/phid/PhabricatorFilePHIDTypeFile.php',
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
'PhabricatorFileSearchEngine' => 'applications/files/query/PhabricatorFileSearchEngine.php',
@@ -2176,6 +2177,7 @@
'PhortuneTestPaymentProvider' => 'applications/phortune/provider/PhortuneTestPaymentProvider.php',
'PhortuneWePayPaymentProvider' => 'applications/phortune/provider/PhortuneWePayPaymentProvider.php',
'PhragmentBrowseController' => 'applications/phragment/controller/PhragmentBrowseController.php',
+ 'PhragmentCapabilityCanCreate' => 'applications/phragment/capability/PhragmentCapabilityCanCreate.php',
'PhragmentController' => 'applications/phragment/controller/PhragmentController.php',
'PhragmentCreateController' => 'applications/phragment/controller/PhragmentCreateController.php',
'PhragmentDAO' => 'applications/phragment/storage/PhragmentDAO.php',
@@ -2189,6 +2191,7 @@
'PhragmentPHIDTypeSnapshot' => 'applications/phragment/phid/PhragmentPHIDTypeSnapshot.php',
'PhragmentPatchController' => 'applications/phragment/controller/PhragmentPatchController.php',
'PhragmentPatchUtil' => 'applications/phragment/util/PhragmentPatchUtil.php',
+ 'PhragmentPolicyController' => 'applications/phragment/controller/PhragmentPolicyController.php',
'PhragmentRevertController' => 'applications/phragment/controller/PhragmentRevertController.php',
'PhragmentSnapshot' => 'applications/phragment/storage/PhragmentSnapshot.php',
'PhragmentSnapshotChild' => 'applications/phragment/storage/PhragmentSnapshotChild.php',
@@ -3932,6 +3935,7 @@
0 => 'PhabricatorFileController',
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
),
+ 'PhabricatorFileNonce' => 'PhabricatorFileDAO',
'PhabricatorFilePHIDTypeFile' => 'PhabricatorPHIDType',
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorFileSearchEngine' => 'PhabricatorApplicationSearchEngine',
@@ -4782,6 +4786,7 @@
'PhortuneTestPaymentProvider' => 'PhortunePaymentProvider',
'PhortuneWePayPaymentProvider' => 'PhortunePaymentProvider',
'PhragmentBrowseController' => 'PhragmentController',
+ 'PhragmentCapabilityCanCreate' => 'PhabricatorPolicyCapability',
'PhragmentController' => 'PhabricatorController',
'PhragmentCreateController' => 'PhragmentController',
'PhragmentDAO' => 'PhabricatorLiskDAO',
@@ -4803,6 +4808,7 @@
'PhragmentPHIDTypeSnapshot' => 'PhabricatorPHIDType',
'PhragmentPatchController' => 'PhragmentController',
'PhragmentPatchUtil' => 'Phobject',
+ 'PhragmentPolicyController' => 'PhragmentController',
'PhragmentRevertController' => 'PhragmentController',
'PhragmentSnapshot' =>
array(
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
@@ -67,13 +67,35 @@
$response->setMimeType($file->getViewableMimeType());
} else {
if (!$request->isHTTPPost()) {
+ // Check if a nonce has been provided. This is used so that
+ // Phabricator can redirect users to the direct download without
+ // going through a POST submission (e.g. when a controller does
+ // a simple redirect). These tokens have a short life time (60
+ // seconds), can only be used once, and can only be generated from
+ // within Phabricator itself.
+ $nonce_exists = false;
+ if ($request->getExists('nonce')) {
+ $token = $request->getStr('nonce');
+ $nonce = id(new PhabricatorFileNonce())->loadOneWhere(
+ 'filePHID = %s AND nonce = %s AND dateExpiry < %d',
+ $file->getPHID(),
+ $request->getStr('nonce'),
+ time());
+ if ($nonce !== null) {
+ $nonce->delete();
+ $nonce_exists = true;
+ }
+ }
+
// NOTE: Require POST to download files. We'd rather go full-bore and
// do a real CSRF check, but can't currently authenticate users on the
// file domain. This should blunt any attacks based on iframes, script
// tags, applet tags, etc., at least. Send the user to the "info" page
// if they're using some other method.
- return id(new AphrontRedirectResponse())
- ->setURI(PhabricatorEnv::getProductionURI($file->getBestURI()));
+ if (!$nonce_exists) {
+ return id(new AphrontRedirectResponse())
+ ->setURI(PhabricatorEnv::getProductionURI($file->getBestURI()));
+ }
}
$response->setMimeType($file->getMimeType());
$response->setDownload($file->getName());
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
@@ -512,6 +512,30 @@
return (string) $uri;
}
+ /**
+ * Provides a download URI to the file with a one-time use token to permit
+ * GET requests. This should be used when redirecting the user to download
+ * the file directly.
+ *
+ * The expiry time is set to 60 seconds, as these URIs are not expected to
+ * be long lived.
+ */
+ public function getNonceURI($expiry = 60) {
+ $token = Filesystem::readRandomCharacters(32);
+
+ $nonce = id(new PhabricatorFileNonce());
+ $nonce->setFilePHID($this->getPHID());
+ $nonce->setNonce($token);
+ $nonce->setDateExpiry(time() + $expiry);
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ $nonce->save();
+ unset($unguarded);
+
+ $uri = id(new PhutilURI($this->getViewURI()))
+ ->setQueryParam('nonce', $token);
+ return (string) $uri;
+ }
+
public function getProfileThumbURI() {
$path = '/file/xform/thumb-profile/'.$this->getPHID().'/'
.$this->getSecretKey().'/';
@@ -877,8 +901,12 @@
}
public function getPolicy($capability) {
- // TODO: Implement proper per-object policies.
- return PhabricatorPolicies::POLICY_USER;
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ return $this->getViewPolicy();
+ default:
+ return PhabricatorPolicies::POLICY_USER;
+ }
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
diff --git a/src/applications/files/storage/PhabricatorFileNonce.php b/src/applications/files/storage/PhabricatorFileNonce.php
new file mode 100644
--- /dev/null
+++ b/src/applications/files/storage/PhabricatorFileNonce.php
@@ -0,0 +1,19 @@
+<?php
+
+final class PhabricatorFileNonce extends PhabricatorFileDAO {
+
+ protected $filePHID;
+ protected $nonce;
+ protected $dateExpiry;
+
+ private $file = self::ATTACHABLE;
+
+ public function getFile() {
+ return $this->assertAttached($this->file);
+ }
+
+ public function attachFile(PhabricatorFile $file) {
+ return $this->file = $file;
+ }
+
+}
diff --git a/src/applications/phragment/application/PhabricatorApplicationPhragment.php b/src/applications/phragment/application/PhabricatorApplicationPhragment.php
--- a/src/applications/phragment/application/PhabricatorApplicationPhragment.php
+++ b/src/applications/phragment/application/PhabricatorApplicationPhragment.php
@@ -37,6 +37,7 @@
'browse/(?P<dblob>.*)' => 'PhragmentBrowseController',
'create/(?P<dblob>.*)' => 'PhragmentCreateController',
'update/(?P<dblob>.*)' => 'PhragmentUpdateController',
+ 'policy/(?P<dblob>.*)' => 'PhragmentPolicyController',
'history/(?P<dblob>.*)' => 'PhragmentHistoryController',
'zip/(?P<dblob>.*)' => 'PhragmentZIPController',
'zip@(?P<snapshot>[^/]+)/(?P<dblob>.*)' => 'PhragmentZIPController',
@@ -56,5 +57,12 @@
);
}
+ protected function getCustomCapabilities() {
+ return array(
+ PhragmentCapabilityCanCreate::CAPABILITY => array(
+ ),
+ );
+ }
+
}
diff --git a/src/applications/phragment/capability/PhragmentCapabilityCanCreate.php b/src/applications/phragment/capability/PhragmentCapabilityCanCreate.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phragment/capability/PhragmentCapabilityCanCreate.php
@@ -0,0 +1,20 @@
+<?php
+
+final class PhragmentCapabilityCanCreate
+ extends PhabricatorPolicyCapability {
+
+ const CAPABILITY = 'phragment.create';
+
+ public function getCapabilityKey() {
+ return self::CAPABILITY;
+ }
+
+ public function getCapabilityName() {
+ return pht('Can Create Fragments');
+ }
+
+ public function describeCapabilityRejection() {
+ return pht('You do not have permission to create fragments.');
+ }
+
+}
diff --git a/src/applications/phragment/controller/PhragmentBrowseController.php b/src/applications/phragment/controller/PhragmentBrowseController.php
--- a/src/applications/phragment/controller/PhragmentBrowseController.php
+++ b/src/applications/phragment/controller/PhragmentBrowseController.php
@@ -4,6 +4,10 @@
private $dblob;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
}
@@ -24,11 +28,14 @@
}
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
- $crumbs->addAction(
- id(new PHUIListItemView())
- ->setName(pht('Create Fragment'))
- ->setHref($this->getApplicationURI('/create/'.$path))
- ->setIcon('create'));
+ if ($this->hasApplicationCapability(
+ PhragmentCapabilityCanCreate::CAPABILITY)) {
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Create Fragment'))
+ ->setHref($this->getApplicationURI('/create/'.$path))
+ ->setIcon('create'));
+ }
$current_box = $this->createCurrentFragmentView($current, false);
diff --git a/src/applications/phragment/controller/PhragmentController.php b/src/applications/phragment/controller/PhragmentController.php
--- a/src/applications/phragment/controller/PhragmentController.php
+++ b/src/applications/phragment/controller/PhragmentController.php
@@ -79,20 +79,29 @@
$file = null;
$file_uri = null;
if (!$fragment->isDirectory()) {
- $file = id(new PhabricatorFileQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
- ->executeOne();
- if ($file !== null) {
- $file_uri = $file->getBestURI();
+ try {
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
+ ->executeOne();
+ if ($file !== null) {
+ $file_uri = $file->getDownloadURI();
+ }
+ } catch (PhabricatorPolicyException $ex) {
+ // The download link won't be usable.
}
}
$header = id(new PHUIHeaderView())
->setHeader($fragment->getName())
->setPolicyObject($fragment)
->setUser($viewer);
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $fragment,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($fragment)
@@ -102,28 +111,39 @@
->setName(pht('Download Fragment'))
->setHref($file_uri)
->setDisabled($file === null)
+ ->setRenderAsForm(true)
+ ->setDownload(true)
+ ->setAnonymous(true)
->setIcon('download'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Contents as ZIP'))
->setHref($this->getApplicationURI("zip/".$fragment->getPath()))
- ->setDisabled(false) // TODO: Policy
->setIcon('zip'));
if (!$fragment->isDirectory()) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Update Fragment'))
->setHref($this->getApplicationURI("update/".$fragment->getPath()))
- ->setDisabled(false) // TODO: Policy
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
->setIcon('edit'));
} else {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Convert to File'))
->setHref($this->getApplicationURI("update/".$fragment->getPath()))
- ->setDisabled(false) // TODO: Policy
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
->setIcon('edit'));
}
+ $actions->addAction(
+ id(new PhabricatorActionView())
+ ->setName(pht('Set Fragment Policies'))
+ ->setHref($this->getApplicationURI("policy/".$fragment->getPath()))
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
+ ->setIcon('edit'));
if ($is_history_view) {
$actions->addAction(
id(new PhabricatorActionView())
@@ -142,15 +162,17 @@
->setName(pht('Create Snapshot'))
->setHref($this->getApplicationURI(
"snapshot/create/".$fragment->getPath()))
- ->setDisabled(false) // TODO: Policy
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
->setIcon('snapshot'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Promote Snapshot to Here'))
->setHref($this->getApplicationURI(
"snapshot/promote/latest/".$fragment->getPath()))
->setWorkflow(true)
- ->setDisabled(false) // TODO: Policy
+ ->setDisabled(!$can_edit)
+ ->setWorkflow(!$can_edit)
->setIcon('promote'));
$properties = id(new PHUIPropertyListView())
diff --git a/src/applications/phragment/controller/PhragmentHistoryController.php b/src/applications/phragment/controller/PhragmentHistoryController.php
--- a/src/applications/phragment/controller/PhragmentHistoryController.php
+++ b/src/applications/phragment/controller/PhragmentHistoryController.php
@@ -4,6 +4,10 @@
private $dblob;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
}
@@ -21,11 +25,14 @@
$path = $current->getPath();
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
- $crumbs->addAction(
- id(new PHUIListItemView())
- ->setName(pht('Create Fragment'))
- ->setHref($this->getApplicationURI('/create/'.$path))
- ->setIcon('create'));
+ if ($this->hasApplicationCapability(
+ PhragmentCapabilityCanCreate::CAPABILITY)) {
+ $crumbs->addAction(
+ id(new PHUIListItemView())
+ ->setName(pht('Create Fragment'))
+ ->setHref($this->getApplicationURI('/create/'.$path))
+ ->setIcon('create'));
+ }
$current_box = $this->createCurrentFragmentView($current, true);
@@ -44,6 +51,11 @@
->execute();
$files = mpull($files, null, 'getPHID');
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $current,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$first = true;
foreach ($versions as $version) {
$item = id(new PHUIObjectItemView());
@@ -58,7 +70,7 @@
$item->addAttribute('Deletion');
}
- if (!$first) {
+ if (!$first && $can_edit) {
$item->addAction(id(new PHUIListItemView())
->setIcon('undo')
->setRenderNameAsTooltip(true)
@@ -68,16 +80,22 @@
"revert/".$version->getID()."/".$current->getPath())));
}
- $disabled = !isset($files[$version->getFilePHID()]);
- $action = id(new PHUIListItemView())
- ->setIcon('download')
- ->setDisabled($disabled)
- ->setRenderNameAsTooltip(true)
- ->setName(pht("Download"));
- if (!$disabled) {
- $action->setHref($files[$version->getFilePHID()]->getBestURI());
+ // We can't display the download links here for unauthenticated users
+ // as they won't be able to see the info page. If they click through
+ // to the version they can download it from there anyway.
+ if (!$request->getUser()) {
+ $disabled = !isset($files[$version->getFilePHID()]);
+ $action = id(new PHUIListItemView())
+ ->setIcon('download')
+ ->setDisabled($disabled)
+ ->setRenderNameAsTooltip(true)
+ ->setName(pht("Download"));
+ if (!$disabled) {
+ $action->setHref($files[$version->getFilePHID()]->getBestURI());
+ }
+ $item->addAction($action);
}
- $item->addAction($action);
+
$list->addItem($item);
$first = false;
diff --git a/src/applications/phragment/controller/PhragmentPatchController.php b/src/applications/phragment/controller/PhragmentPatchController.php
--- a/src/applications/phragment/controller/PhragmentPatchController.php
+++ b/src/applications/phragment/controller/PhragmentPatchController.php
@@ -5,6 +5,10 @@
private $aid;
private $bid;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->aid = idx($data, "aid", 0);
$this->bid = idx($data, "bid", 0);
@@ -61,7 +65,9 @@
$patch = PhragmentPatchUtil::calculatePatch($file_a, $file_b);
if ($patch === null) {
- throw new Exception("Unable to compute patch!");
+ // There are no differences between the two files, so we output
+ // an empty patch.
+ $patch = '';
}
$a_sequence = 'x';
@@ -81,6 +87,9 @@
'mime-type' => 'text/plain',
'ttl' => time() + 60 * 60 * 24,
));
+
+ // FIXME: Because Phabricator will deduplicate the file above, the patch
+ // won't necessarily be visible to the user and they'll get a login page :(
return id(new AphrontRedirectResponse())
->setURI($result->getBestURI());
}
diff --git a/src/applications/phragment/controller/PhragmentPolicyController.php b/src/applications/phragment/controller/PhragmentPolicyController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/phragment/controller/PhragmentPolicyController.php
@@ -0,0 +1,151 @@
+<?php
+
+final class PhragmentPolicyController extends PhragmentController {
+
+ private $dblob;
+
+ public function willProcessRequest(array $data) {
+ $this->dblob = idx($data, "dblob", "");
+ }
+
+ public function processRequest() {
+ $request = $this->getRequest();
+ $viewer = $request->getUser();
+
+ $parents = $this->loadParentFragments($this->dblob);
+ if ($parents === null) {
+ return new Aphront404Response();
+ }
+ $fragment = idx($parents, count($parents) - 1, null);
+
+ $error_view = null;
+
+ if ($request->isFormPost()) {
+ $errors = array();
+
+ $v_view_policy = $request->getStr('viewPolicy');
+ $v_edit_policy = $request->getStr('editPolicy');
+ $v_replace_children = $request->getBool('replacePoliciesOnChildren');
+ $v_set_file_policy = $request->getBool('setFilePolicies');
+
+ $fragment->setViewPolicy($v_view_policy);
+ $fragment->setEditPolicy($v_edit_policy);
+
+ $fragment->save();
+
+ if ($v_set_file_policy) {
+ $versions = id(new PhragmentFragmentVersionQuery())
+ ->setViewer($viewer)
+ ->withFragmentPHIDs(array($fragment->getPHID()))
+ ->execute();
+ $file_phids = mpull($versions, 'getFilePHID');
+
+ $files = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($file_phids)
+ ->execute();
+ foreach ($files as $file) {
+ $file->setViewPolicy($v_view_policy);
+ $file->save();
+ }
+ }
+
+ if ($v_replace_children) {
+ // If you can edit a fragment, you can forcibly set the policies
+ // on child fragments, regardless of whether you can see them or not.
+ $children = id(new PhragmentFragmentQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withLeadingPath($fragment->getPath().'/')
+ ->execute();
+ $children_phids = mpull($children, 'getPHID');
+
+ $fragment->openTransaction();
+ foreach ($children as $child) {
+ $child->setViewPolicy($v_view_policy);
+ $child->setEditPolicy($v_edit_policy);
+ $child->save();
+ }
+ $fragment->saveTransaction();
+
+ if ($v_set_file_policy) {
+ $versions = id(new PhragmentFragmentVersionQuery())
+ ->setViewer($viewer)
+ ->withFragmentPHIDs($children_phids)
+ ->execute();
+ $file_phids = mpull($versions, 'getFilePHID');
+
+ $files = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withPHIDs($file_phids)
+ ->execute();
+ foreach ($files as $file) {
+ $file->setViewPolicy($v_view_policy);
+ $file->save();
+ }
+ }
+ }
+
+ return id(new AphrontRedirectResponse())
+ ->setURI('/phragment/browse/'.$fragment->getPath());
+ }
+
+ $policies = id(new PhabricatorPolicyQuery())
+ ->setViewer($viewer)
+ ->setObject($fragment)
+ ->execute();
+
+ $form = id(new AphrontFormView())
+ ->setUser($viewer)
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('viewPolicy')
+ ->setPolicyObject($fragment)
+ ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormPolicyControl())
+ ->setName('editPolicy')
+ ->setPolicyObject($fragment)
+ ->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
+ ->setPolicies($policies))
+ ->appendChild(
+ id(new AphrontFormCheckboxControl())
+ ->addCheckbox(
+ 'replacePoliciesOnChildren',
+ 'true',
+ pht(
+ 'Replace policies on child fragments with '.
+ 'the policies above.'))
+ ->addCheckbox(
+ 'setFilePolicies',
+ 'true',
+ pht(
+ 'Replace policies on files associated with all versions '.
+ 'of this fragment. You must have view access to the files '.
+ 'to be able to set their policies.')))
+ ->appendChild(
+ id(new AphrontFormSubmitControl())
+ ->setValue(pht('Save Fragment Policies'))
+ ->addCancelButton(
+ $this->getApplicationURI('browse/'.$fragment->getPath())));
+
+ $crumbs = $this->buildApplicationCrumbsWithPath($parents);
+ $crumbs->addCrumb(
+ id(new PhabricatorCrumbView())
+ ->setName(pht('Edit Fragment Policies')));
+
+ $box = id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Edit Fragment Policies: %s', $fragment->getPath()))
+ ->setValidationException(null)
+ ->setForm($form);
+
+ return $this->buildApplicationPage(
+ array(
+ $crumbs,
+ $box),
+ array(
+ 'title' => pht('Edit Fragment Policies'),
+ 'device' => true));
+ }
+
+}
diff --git a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotCreateController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotCreateController.php
@@ -21,6 +21,11 @@
return new Aphront404Response();
}
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $fragment,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$children = id(new PhragmentFragmentQuery())
->setViewer($viewer)
->needLatestVersion(true)
diff --git a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotDeleteController.php
@@ -20,6 +20,11 @@
return new Aphront404Response();
}
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $snapshot,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
if ($request->isDialogFormPost()) {
$fragment_uri = $snapshot->getPrimaryFragment()->getURI();
diff --git a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotPromoteController.php
@@ -29,6 +29,11 @@
return new Aphront404Response();
}
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $this->targetFragment,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$this->snapshots = id(new PhragmentSnapshotQuery())
->setViewer($viewer)
->withPrimaryFragmentPHIDs(array($this->targetFragment->getPHID()))
@@ -46,6 +51,11 @@
return new Aphront404Response();
}
+ PhabricatorPolicyFilter::requireCapability(
+ $viewer,
+ $this->targetSnapshot,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
$this->snapshots = id(new PhragmentSnapshotQuery())
->setViewer($viewer)
->withPrimaryFragmentPHIDs(array(
diff --git a/src/applications/phragment/controller/PhragmentSnapshotViewController.php b/src/applications/phragment/controller/PhragmentSnapshotViewController.php
--- a/src/applications/phragment/controller/PhragmentSnapshotViewController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotViewController.php
@@ -4,6 +4,10 @@
private $id;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->id = idx($data, "id", "");
}
diff --git a/src/applications/phragment/controller/PhragmentVersionController.php b/src/applications/phragment/controller/PhragmentVersionController.php
--- a/src/applications/phragment/controller/PhragmentVersionController.php
+++ b/src/applications/phragment/controller/PhragmentVersionController.php
@@ -4,6 +4,10 @@
private $id;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->id = idx($data, "id", 0);
}
@@ -41,7 +45,7 @@
->withPHIDs(array($version->getFilePHID()))
->executeOne();
if ($file !== null) {
- $file_uri = $file->getBestURI();
+ $file_uri = $file->getDownloadURI();
}
$header = id(new PHUIHeaderView())
@@ -61,6 +65,9 @@
->setName(pht('Download Version'))
->setHref($file_uri)
->setDisabled($file === null)
+ ->setRenderAsForm(true)
+ ->setDownload(true)
+ ->setAnonymous(true)
->setIcon('download'));
$properties = id(new PHUIPropertyListView())
diff --git a/src/applications/phragment/controller/PhragmentZIPController.php b/src/applications/phragment/controller/PhragmentZIPController.php
--- a/src/applications/phragment/controller/PhragmentZIPController.php
+++ b/src/applications/phragment/controller/PhragmentZIPController.php
@@ -7,6 +7,10 @@
private $snapshotCache;
+ public function shouldAllowPublic() {
+ return true;
+ }
+
public function willProcessRequest(array $data) {
$this->dblob = idx($data, "dblob", "");
$this->snapshot = idx($data, "snapshot", null);
@@ -87,7 +91,9 @@
}
foreach ($mappings as $path => $file) {
- $zip->addFromString($path, $file->loadFileData());
+ if ($file !== null) {
+ $zip->addFromString($path, $file->loadFileData());
+ }
}
$zip->close();
@@ -104,7 +110,7 @@
'ttl' => time() + 60 * 60 * 24,
));
return id(new AphrontRedirectResponse())
- ->setURI($file->getBestURI());
+ ->setURI($file->getNonceURI());
}
/**
diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
--- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
+++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
@@ -1828,6 +1828,10 @@
'type' => 'sql',
'name' => $this->getPatchPath('20131208.phragmentsnapshot.sql'),
),
+ '20131210.filenonce.sql' => array(
+ 'type' => 'sql',
+ 'name' => $this->getPatchPath('20131210.filenonce.sql'),
+ ),
);
}
}
diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php
--- a/src/view/layout/PhabricatorActionView.php
+++ b/src/view/layout/PhabricatorActionView.php
@@ -11,6 +11,7 @@
private $renderAsForm;
private $download;
private $objectURI;
+ private $anonymous;
public function setObjectURI($object_uri) {
$this->objectURI = $object_uri;
@@ -40,7 +41,7 @@
* viewing.
*/
public function getHref() {
- if ($this->workflow || $this->renderAsForm) {
+ if (($this->workflow || $this->renderAsForm) && !$this->anonymous) {
if (!$this->user || !$this->user->isLoggedIn()) {
return id(new PhutilURI('/auth/start/'))
->setQueryParam('next', (string)$this->getObjectURI());
@@ -75,6 +76,11 @@
return $this;
}
+ public function setAnonymous($anonymous) {
+ $this->anonymous = $anonymous;
+ return $this;
+ }
+
public function setRenderAsForm($form) {
$this->renderAsForm = $form;
return $this;

File Metadata

Mime Type
text/x-diff
Storage Engine
amazon-s3
Storage Format
Raw Data
Storage Handle
phabricator/cb/3s/e3zancxidid7w3kx
Default Alt Text
D7751.id17532.diff (30 KB)

Event Timeline