mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-17 18:21:11 +01:00
Add View, Edit and Join policies to PhabricatorProject
Summary: - In ProjectQuery, always load the viewer's membership in the project because we need it to perform a CAN_VIEW test. - Add storage for the view, edit and join policies. - A user can always view a project if they are a member. - A user can always join a project if they can edit it. - Editing a project requires both "view" and "edit" permissions, and edit does not imply view. - This has no effect on the application yet. Test Plan: See next diff. Reviewers: vrana, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D3219
This commit is contained in:
parent
6cbc67ea75
commit
bd0be1c650
4 changed files with 106 additions and 16 deletions
8
resources/sql/patches/policy-project.sql
Normal file
8
resources/sql/patches/policy-project.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
ALTER TABLE `{$NAMESPACE}_project`.`project`
|
||||
ADD `viewPolicy` varchar(64) COLLATE utf8_bin;
|
||||
|
||||
ALTER TABLE `{$NAMESPACE}_project`.`project`
|
||||
ADD `editPolicy` varchar(64) COLLATE utf8_bin;
|
||||
|
||||
ALTER TABLE `{$NAMESPACE}_project`.`project`
|
||||
ADD `joinPolicy` varchar(64) COLLATE utf8_bin;
|
|
@ -68,27 +68,50 @@ final class PhabricatorProjectQuery extends PhabricatorCursorPagedPolicyQuery {
|
|||
$table = new PhabricatorProject();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
// NOTE: Because visibility checks for projects depend on whether or not
|
||||
// the user is a project member, we always load their membership. If we're
|
||||
// loading all members anyway we can piggyback on that; otherwise we
|
||||
// do an explicit join.
|
||||
|
||||
$select_clause = '';
|
||||
if (!$this->needMembers) {
|
||||
$select_clause = ', vm.dst viewerIsMember';
|
||||
}
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT p.* FROM %T p %Q %Q %Q %Q %Q',
|
||||
'SELECT p.* %Q FROM %T p %Q %Q %Q %Q %Q',
|
||||
$select_clause,
|
||||
$table->getTableName(),
|
||||
$this->buildJoinClause($conn_r),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildGroupClause($conn_r),
|
||||
'ORDER BY name',
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
$projects = $table->loadAllFromArray($data);
|
||||
|
||||
if ($projects && $this->needMembers) {
|
||||
$etype = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER;
|
||||
$members = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(mpull($projects, 'getPHID'))
|
||||
->withEdgeTypes(array($etype))
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
$phid = $project->getPHID();
|
||||
$project->attachMemberPHIDs(array_keys($members[$phid][$etype]));
|
||||
if ($projects) {
|
||||
$viewer_phid = $this->getViewer()->getPHID();
|
||||
if ($this->needMembers) {
|
||||
$etype = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER;
|
||||
$members = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(mpull($projects, 'getPHID'))
|
||||
->withEdgeTypes(array($etype))
|
||||
->execute();
|
||||
foreach ($projects as $project) {
|
||||
$phid = $project->getPHID();
|
||||
$project->attachMemberPHIDs(array_keys($members[$phid][$etype]));
|
||||
$project->setIsUserMember(
|
||||
$viewer_phid,
|
||||
isset($members[$phid][$etype][$viewer_phid]));
|
||||
}
|
||||
} else {
|
||||
foreach ($data as $row) {
|
||||
$projects[$row['id']]->setIsUserMember(
|
||||
$viewer_phid,
|
||||
($row['viewerIsMember'] !== null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -139,8 +162,7 @@ final class PhabricatorProjectQuery extends PhabricatorCursorPagedPolicyQuery {
|
|||
if ($this->memberPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'e.type = %s AND e.dst IN (%Ls)',
|
||||
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER,
|
||||
'e.dst IN (%Ls)',
|
||||
$this->memberPHIDs);
|
||||
}
|
||||
|
||||
|
@ -160,11 +182,21 @@ final class PhabricatorProjectQuery extends PhabricatorCursorPagedPolicyQuery {
|
|||
private function buildJoinClause($conn_r) {
|
||||
$joins = array();
|
||||
|
||||
if (!$this->needMembers) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'LEFT JOIN %T vm ON vm.src = p.phid AND vm.type = %d AND vm.dst = %s',
|
||||
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
||||
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER,
|
||||
$this->getViewer()->getPHID());
|
||||
}
|
||||
|
||||
if ($this->memberPHIDs) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'JOIN %T e ON e.src = p.phid',
|
||||
PhabricatorEdgeConfig::TABLE_NAME_EDGE);
|
||||
'JOIN %T e ON e.src = p.phid AND e.type = %d',
|
||||
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
|
||||
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER);
|
||||
}
|
||||
|
||||
return implode(' ', $joins);
|
||||
|
|
|
@ -26,23 +26,69 @@ final class PhabricatorProject extends PhabricatorProjectDAO
|
|||
protected $subprojectPHIDs = array();
|
||||
protected $phrictionSlug;
|
||||
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $joinPolicy;
|
||||
|
||||
private $subprojectsNeedUpdate;
|
||||
private $memberPHIDs;
|
||||
private $sparseMembers = array();
|
||||
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
PhabricatorPolicyCapability::CAN_JOIN,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return PhabricatorPolicies::POLICY_USER;
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
return $this->getViewPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
return $this->getEditPolicy();
|
||||
case PhabricatorPolicyCapability::CAN_JOIN:
|
||||
return $this->getJoinPolicy();
|
||||
}
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||
|
||||
switch ($capability) {
|
||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||
if ($this->isUserMember($viewer->getPHID())) {
|
||||
// Project members can always view a project.
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||
break;
|
||||
case PhabricatorPolicyCapability::CAN_JOIN:
|
||||
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
|
||||
if (PhabricatorPolicyFilter::hasCapability($viewer, $this, $can_edit)) {
|
||||
// Project editors can always join a project.
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isUserMember($user_phid) {
|
||||
if (!isset($this->sparseMembers[$user_phid])) {
|
||||
throw new Exception(
|
||||
"Call setIsUserMember() before isUserMember()!");
|
||||
}
|
||||
return $this->sparseMembers[$user_phid];
|
||||
}
|
||||
|
||||
public function setIsUserMember($user_phid, $is_member) {
|
||||
$this->sparseMembers[$user_phid] = $is_member;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
|
|
|
@ -960,6 +960,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('ponder.sql')
|
||||
),
|
||||
'policy-project.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('policy-project.sql'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue