1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-20 12:30:56 +01:00

Apply hierarchical policy checks to Phriction

Summary: Ref T4029. When checking the view policy of a document, require the viewer to also be able to see all of the ancestors.

Test Plan:
  - Hard-coded `/x/y/` to "no one".
    - Checked that `/x/y/` is not visible.
    - Checked that `/x/y/z/` is not visible.
    - Checked that `/x/`, `/x/q/`, etc., are still visible.
  - Tested project pages and sub-pages for project visibility.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4029

Differential Revision: https://secure.phabricator.com/D9199
This commit is contained in:
epriestley 2014-05-19 12:41:30 -07:00
parent 4d7c1026f4
commit 9cb4047134
5 changed files with 126 additions and 20 deletions

View file

@ -1,8 +1,5 @@
<?php
/**
* @group phriction
*/
final class PhrictionDocumentController
extends PhrictionController {
@ -118,7 +115,7 @@ final class PhrictionDocumentController
$new_doc = id(new PhrictionDocumentQuery())
->setViewer($user)
->withIDs(array($new_doc_id))
->exectueOne();
->executeOne();
$slug_uri = PhrictionDocument::getSlugURI($new_doc->getSlug());

View file

@ -25,8 +25,8 @@ final class PhrictionPHIDTypeDocument extends PhabricatorPHIDType {
array $phids) {
return id(new PhrictionDocumentQuery())
->needContent(true)
->withPHIDs($phids);
->withPHIDs($phids)
->needContent(true);
}
public function loadHandles(

View file

@ -49,21 +49,97 @@ final class PhrictionDocumentQuery
}
protected function loadPage() {
$document = new PhrictionDocument();
$conn_r = $document->establishConnection('r');
$table = new PhrictionDocument();
$conn_r = $table->establishConnection('r');
$rows = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$document->getTableName(),
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $document->loadAllFromArray($rows);
$documents = $table->loadAllFromArray($rows);
if ($documents) {
$ancestor_slugs = array();
foreach ($documents as $key => $document) {
$document_slug = $document->getSlug();
foreach (PhabricatorSlug::getAncestry($document_slug) as $ancestor) {
$ancestor_slugs[$ancestor][] = $key;
}
}
if ($ancestor_slugs) {
$ancestors = queryfx_all(
$conn_r,
'SELECT * FROM %T WHERE slug IN (%Ls)',
$document->getTableName(),
array_keys($ancestor_slugs));
$ancestors = $table->loadAllFromArray($ancestors);
$ancestors = mpull($ancestors, null, 'getSlug');
foreach ($ancestor_slugs as $ancestor_slug => $document_keys) {
$ancestor = idx($ancestors, $ancestor_slug);
foreach ($document_keys as $document_key) {
$documents[$document_key]->attachAncestor(
$ancestor_slug,
$ancestor);
}
}
}
}
return $documents;
}
protected function willFilterPage(array $documents) {
// To view a Phriction document, you must also be able to view all of the
// ancestor documents. Filter out documents which have ancestors that are
// not visible.
$document_map = array();
foreach ($documents as $document) {
$document_map[$document->getSlug()] = $document;
foreach ($document->getAncestors() as $key => $ancestor) {
if ($ancestor) {
$document_map[$key] = $ancestor;
}
}
}
$filtered_map = $this->applyPolicyFilter(
$document_map,
array(PhabricatorPolicyCapability::CAN_VIEW));
// Filter all of the documents where a parent is not visible.
foreach ($documents as $document_key => $document) {
// If the document itself is not visible, filter it.
if (!isset($filtered_map[$document->getSlug()])) {
$this->didRejectResult($documents[$document_key]);
unset($documents[$document_key]);
continue;
}
// If an ancestor exists but is not visible, filter the document.
foreach ($document->getAncestors() as $ancestor_key => $ancestor) {
if (!$ancestor) {
continue;
}
if (!isset($filtered_map[$ancestor_key])) {
$this->didRejectResult($documents[$document_key]);
unset($documents[$document_key]);
break;
}
}
}
if (!$documents) {
return $documents;
}
if ($this->needContent) {
$contents = id(new PhrictionContent())->loadAllWhere(
'id IN (%Ld)',

View file

@ -1,8 +1,5 @@
<?php
/**
* @group phriction
*/
final class PhrictionDocument extends PhrictionDAO
implements
PhabricatorPolicyInterface,
@ -16,6 +13,7 @@ final class PhrictionDocument extends PhrictionDAO
protected $status;
private $contentObject = self::ATTACHABLE;
private $ancestors = array();
// TODO: This should be `self::ATTACHABLE`, but there are still a lot of call
// sites which load PhrictionDocuments directly.
@ -84,6 +82,19 @@ final class PhrictionDocument extends PhrictionDAO
return (bool)$this->getProject();
}
public function getAncestors() {
return $this->ancestors;
}
public function getAncestor($slug) {
return $this->assertAttachedKey($this->ancestors, $slug);
}
public function attachAncestor($slug, $ancestor) {
$this->ancestors[$slug] = $ancestor;
return $this;
}
public static function isProjectSlug($slug) {
$slug = PhabricatorSlug::normalize($slug);
$prefix = 'projects/';
@ -119,6 +130,7 @@ final class PhrictionDocument extends PhrictionDAO
if ($this->hasProject()) {
return $this->getProject()->getPolicy($capability);
}
return PhabricatorPolicies::POLICY_USER;
}
@ -134,6 +146,14 @@ final class PhrictionDocument extends PhrictionDAO
return pht(
"This is a project wiki page, and inherits the project's policies.");
}
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return pht(
'To view a wiki document, you must also be able to view all '.
'of its parents.');
}
return null;
}

View file

@ -302,19 +302,32 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery {
private function getPolicyFilter() {
$filter = new PhabricatorPolicyFilter();
$filter->setViewer($this->viewer);
if (!$this->capabilities) {
$capabilities = array(
PhabricatorPolicyCapability::CAN_VIEW,
);
} else {
$capabilities = $this->capabilities;
}
$capabilities = $this->getRequiredCapabilities();
$filter->requireCapabilities($capabilities);
$filter->raisePolicyExceptions($this->shouldRaisePolicyExceptions());
return $filter;
}
protected function getRequiredCapabilities() {
if ($this->capabilities) {
return $this->capabilities;
}
return array(
PhabricatorPolicyCapability::CAN_VIEW,
);
}
protected function applyPolicyFilter(array $objects, array $capabilities) {
if ($this->shouldDisablePolicyFiltering()) {
return $objects;
}
$filter = $this->getPolicyFilter();
$filter->requireCapabilities($capabilities);
return $filter->apply($objects);
}
protected function didRejectResult(PhabricatorPolicyInterface $object) {
$this->getPolicyFilter()->rejectObject(
$object,