1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-30 02:32:42 +01:00

Make PhameBlogs respect policies

Summary:
Adds "can view" and "can edit" policies to blogs. Replaces "bloggers" with "can join".

This doesn't fully remove "bloggers" because I didn't want this to get too crazy/huge.

Test Plan: Created, edited, deleted blogs.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1373

Differential Revision: https://secure.phabricator.com/D3693
This commit is contained in:
epriestley 2012-10-15 14:49:52 -07:00
parent 304599eab0
commit dbcf2e44e8
13 changed files with 184 additions and 132 deletions

View file

@ -0,0 +1,18 @@
ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog`
ADD `viewPolicy` varchar(64) COLLATE utf8_bin;
UPDATE `{$NAMESPACE}_phame`.`phame_blog` SET viewPolicy = 'users'
WHERE viewPolicy IS NULL;
ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog`
ADD `editPolicy` varchar(64) COLLATE utf8_bin;
UPDATE `{$NAMESPACE}_phame`.`phame_blog` SET editPolicy = 'users'
WHERE editPolicy IS NULL;
ALTER TABLE `{$NAMESPACE}_phame`.`phame_blog`
ADD `joinPolicy` varchar(64) COLLATE utf8_bin;
UPDATE `{$NAMESPACE}_phame`.`phame_blog` SET joinPolicy = 'users'
WHERE joinPolicy IS NULL;

View file

@ -2276,13 +2276,17 @@ phutil_register_library_map(array(
'PhabricatorXHProfSampleListView' => 'AphrontView', 'PhabricatorXHProfSampleListView' => 'AphrontView',
'PhameAllBlogListController' => 'PhameBlogListBaseController', 'PhameAllBlogListController' => 'PhameBlogListBaseController',
'PhameAllPostListController' => 'PhamePostListBaseController', 'PhameAllPostListController' => 'PhamePostListBaseController',
'PhameBlog' => 'PhameDAO', 'PhameBlog' =>
array(
0 => 'PhameDAO',
1 => 'PhabricatorPolicyInterface',
),
'PhameBlogDeleteController' => 'PhameController', 'PhameBlogDeleteController' => 'PhameController',
'PhameBlogDetailView' => 'AphrontView', 'PhameBlogDetailView' => 'AphrontView',
'PhameBlogEditController' => 'PhameController', 'PhameBlogEditController' => 'PhameController',
'PhameBlogListBaseController' => 'PhameController', 'PhameBlogListBaseController' => 'PhameController',
'PhameBlogListView' => 'AphrontView', 'PhameBlogListView' => 'AphrontView',
'PhameBlogQuery' => 'PhabricatorOffsetPagedQuery', 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhameBlogSkin' => 'AphrontView', 'PhameBlogSkin' => 'AphrontView',
'PhameBlogViewController' => 'PhameController', 'PhameBlogViewController' => 'PhameController',
'PhameBloggerPostListController' => 'PhamePostListBaseController', 'PhameBloggerPostListController' => 'PhamePostListBaseController',

View file

@ -137,8 +137,18 @@ abstract class AphrontApplicationConfiguration {
if ($host != id(new PhutilURI($base_uri))->getDomain() && if ($host != id(new PhutilURI($base_uri))->getDomain() &&
$host != id(new PhutilURI($prod_uri))->getDomain() && $host != id(new PhutilURI($prod_uri))->getDomain() &&
$host != id(new PhutilURI($file_uri))->getDomain()) { $host != id(new PhutilURI($file_uri))->getDomain()) {
$blogs = id(new PhameBlogQuery())->withDomain($host)->execute();
$blog = reset($blogs); try {
$blog = id(new PhameBlogQuery())
->setViewer($request->getUser())
->withDomain($host)
->executeOne();
} catch (PhabricatorPolicyException $ex) {
throw new Exception(
"This blog is not visible to logged out users, so it can not be ".
"visited from a custom domain.");
}
if (!$blog) { if (!$blog) {
if ($prod_uri && $prod_uri != $base_uri) { if ($prod_uri && $prod_uri != $base_uri) {
$prod_str = ' or '.$prod_uri; $prod_str = ' or '.$prod_uri;

View file

@ -51,53 +51,23 @@ extends PhameController {
} }
public function processRequest() { public function processRequest() {
$blogger_edge_type = PhabricatorEdgeConfig::TYPE_BLOG_HAS_BLOGGER;
$post_edge_type = PhabricatorEdgeConfig::TYPE_BLOG_HAS_POST;
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$blog_phid = $this->getBlogPHID();
$blogs = id(new PhameBlogQuery()) $blog = id(new PhameBlogQuery())
->withPHIDs(array($blog_phid)) ->setViewer($user)
->execute(); ->withPHIDs(array($this->getBlogPHID()))
$blog = reset($blogs); ->requireCapabilities(
if (empty($blog)) { array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$blog) {
return new Aphront404Response(); return new Aphront404Response();
} }
$phids = array($blog_phid);
$edge_types = array(
PhabricatorEdgeConfig::TYPE_BLOG_HAS_BLOGGER,
PhabricatorEdgeConfig::TYPE_BLOG_HAS_POST,
);
$edges = id(new PhabricatorEdgeQuery())
->withSourcePHIDs($phids)
->withEdgeTypes($edge_types)
->execute();
$blogger_edges = $edges[$blog_phid][$blogger_edge_type];
// TODO -- make this check use a policy
if (!isset($blogger_edges[$user->getPHID()]) &&
!$user->isAdmin()) {
return new Aphront403Response();
}
$edit_uri = $blog->getEditURI();
if ($request->isFormPost()) { if ($request->isFormPost()) {
$blogger_phids = array_keys($blogger_edges);
$post_edges = $edges[$blog_phid][$post_edge_type];
$post_phids = array_keys($post_edges);
$editor = id(new PhabricatorEdgeEditor())
->setActor($user);
foreach ($blogger_phids as $phid) {
$editor->removeEdge($blog_phid, $blogger_edge_type, $phid);
}
foreach ($post_phids as $phid) {
$editor->removeEdge($blog_phid, $post_edge_type, $phid);
}
$editor->save();
$blog->delete(); $blog->delete();
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())
->setURI('/phame/blog/?deleted'); ->setURI('/phame/blog/?deleted');
@ -108,7 +78,7 @@ extends PhameController {
->setTitle('Delete blog?') ->setTitle('Delete blog?')
->appendChild('Really delete this blog? It will be gone forever.') ->appendChild('Really delete this blog? It will be gone forever.')
->addSubmitButton('Delete') ->addSubmitButton('Delete')
->addCancelButton($edit_uri); ->addCancelButton($blog->getEditURI());
return id(new AphrontDialogResponse())->setDialog($dialog); return id(new AphrontDialogResponse())->setDialog($dialog);
} }

View file

@ -76,28 +76,24 @@ final class PhameBlogEditController
public function processRequest() { public function processRequest() {
$request = $this->getRequest(); $request = $this->getRequest();
$user = $request->getUser(); $user = $request->getUser();
$e_name = null;
$e_bloggers = null; $e_name = true;
$e_custom_domain = null; $e_custom_domain = null;
$errors = array(); $errors = array();
if ($this->isBlogEdit()) { if ($this->isBlogEdit()) {
$blogs = id(new PhameBlogQuery()) $blog = id(new PhameBlogQuery())
->setViewer($user)
->withPHIDs(array($this->getBlogPHID())) ->withPHIDs(array($this->getBlogPHID()))
->execute(); ->requireCapabilities(
$blog = reset($blogs); array(
if (empty($blog)) { PhabricatorPolicyCapability::CAN_EDIT
))
->executeOne();
if (!$blog) {
return new Aphront404Response(); return new Aphront404Response();
} }
$bloggers = $blog->loadBloggers()->getBloggers();
// TODO -- make this check use a policy
if (!isset($bloggers[$user->getPHID()]) &&
!$user->isAdmin()) {
return new Aphront403Response();
}
$blogger_tokens = mpull($bloggers, 'getFullName', 'getPHID');
$submit_button = 'Save Changes'; $submit_button = 'Save Changes';
$delete_button = javelin_render_tag( $delete_button = javelin_render_tag(
'a', 'a',
@ -118,32 +114,13 @@ final class PhameBlogEditController
} }
if ($request->isFormPost()) { if ($request->isFormPost()) {
$saved = true;
$name = $request->getStr('name'); $name = $request->getStr('name');
$description = $request->getStr('description'); $description = $request->getStr('description');
$blogger_arr = $request->getArr('bloggers');
$custom_domain = $request->getStr('custom_domain'); $custom_domain = $request->getStr('custom_domain');
$skin = $request->getStr('skin'); $skin = $request->getStr('skin');
if (empty($blogger_arr)) {
$error = 'Bloggers must be nonempty.';
if ($this->isBlogEdit()) {
$error .= ' To delete the blog, use the delete button.';
} else {
$error .= ' A blog cannot exist without bloggers.';
}
$e_bloggers = 'Required';
$errors[] = $error;
}
$new_bloggers = array_values($blogger_arr);
if ($this->isBlogEdit()) {
$old_bloggers = array_keys($blogger_tokens);
} else {
$old_bloggers = array();
}
if (empty($name)) { if (empty($name)) {
$errors[] = 'Name must be nonempty.'; $errors[] = 'You must give the blog a name.';
$e_name = 'Required'; $e_name = 'Required';
} }
$blog->setName($name); $blog->setName($name);
@ -158,27 +135,19 @@ final class PhameBlogEditController
} }
$blog->setSkin($skin); $blog->setSkin($skin);
if (empty($errors)) { $blog->setViewPolicy($request->getStr('can_view'));
$blog->setEditPolicy($request->getStr('can_edit'));
$blog->setJoinPolicy($request->getStr('can_join'));
// Don't let users remove their ability to edit blogs.
PhabricatorPolicyFilter::mustRetainCapability(
$user,
$blog,
PhabricatorPolicyCapability::CAN_EDIT);
if (!$errors) {
$blog->save(); $blog->save();
$add_phids = $new_bloggers;
$rem_phids = array_diff($old_bloggers, $new_bloggers);
$editor = new PhabricatorEdgeEditor();
$edge_type = PhabricatorEdgeConfig::TYPE_BLOG_HAS_BLOGGER;
$editor->setActor($user);
foreach ($add_phids as $phid) {
$editor->addEdge($blog->getPHID(), $edge_type, $phid);
}
foreach ($rem_phids as $phid) {
$editor->removeEdge($blog->getPHID(), $edge_type, $phid);
}
$editor->save();
} else {
$saved = false;
}
if ($saved) {
$uri = new PhutilURI($blog->getViewURI()); $uri = new PhutilURI($blog->getViewURI());
if ($this->isBlogEdit()) { if ($this->isBlogEdit()) {
$uri->setQueryParam('edit', true); $uri->setQueryParam('edit', true);
@ -197,6 +166,11 @@ final class PhameBlogEditController
$panel->addButton($delete_button); $panel->addButton($delete_button);
} }
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($blog)
->execute();
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($user) ->setUser($user)
->appendChild( ->appendChild(
@ -212,18 +186,29 @@ final class PhameBlogEditController
->setLabel('Description') ->setLabel('Description')
->setName('description') ->setName('description')
->setValue($blog->getDescription()) ->setValue($blog->getDescription())
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setID('blog-description') ->setID('blog-description')
) )
->appendChild( ->appendChild(
id(new AphrontFormTokenizerControl()) id(new AphrontFormPolicyControl())
->setLabel('Bloggers')
->setName('bloggers')
->setValue($blogger_tokens)
->setUser($user) ->setUser($user)
->setDatasource('/typeahead/common/users/') ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setError($e_bloggers) ->setPolicyObject($blog)
) ->setPolicies($policies)
->setName('can_view'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($blog)
->setPolicies($policies)
->setName('can_edit'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_JOIN)
->setPolicyObject($blog)
->setPolicies($policies)
->setName('can_join'))
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel('Custom Domain') ->setLabel('Custom Domain')
@ -250,7 +235,7 @@ final class PhameBlogEditController
if ($errors) { if ($errors) {
$error_view = id(new AphrontErrorView()) $error_view = id(new AphrontErrorView())
->setTitle('Errors saving blog.') ->setTitle('Form Errors')
->setErrors($errors); ->setErrors($errors);
} else { } else {
$error_view = null; $error_view = null;

View file

@ -71,11 +71,10 @@ final class PhameBlogViewController
$user = $request->getUser(); $user = $request->getUser();
$blog_phid = $this->getBlogPHID(); $blog_phid = $this->getBlogPHID();
$blogs = id(new PhameBlogQuery()) $blog = id(new PhameBlogQuery())
->setViewer($user)
->withPHIDs(array($blog_phid)) ->withPHIDs(array($blog_phid))
->execute(); ->executeOne();
$blog = reset($blogs);
if (!$blog) { if (!$blog) {
return new Aphront404Response(); return new Aphront404Response();
} }

View file

@ -29,9 +29,13 @@ final class PhameAllBlogListController
public function processRequest() { public function processRequest() {
$user = $this->getRequest()->getUser(); $user = $this->getRequest()->getUser();
$pager = id(new AphrontCursorPagerView())
->readFromRequest($this->getRequest());
$blogs = id(new PhameBlogQuery()) $blogs = id(new PhameBlogQuery())
->setViewer($user)
->needBloggers(true) ->needBloggers(true)
->executeWithOffsetPager($this->getPager()); ->executeWithCursorPager($pager);
$this->setBlogs($blogs); $this->setBlogs($blogs);
$page_title = 'All Blogs'; $page_title = 'All Blogs';

View file

@ -48,10 +48,14 @@ final class PhameUserBlogListController
PhabricatorEdgeConfig::TYPE_BLOGGER_HAS_BLOG PhabricatorEdgeConfig::TYPE_BLOGGER_HAS_BLOG
); );
$pager = id(new AphrontCursorPagerView())
->readFromRequest($this->getRequest());
$blogs = id(new PhameBlogQuery()) $blogs = id(new PhameBlogQuery())
->setViewer($user)
->withPHIDs($blog_phids) ->withPHIDs($blog_phids)
->needBloggers(true) ->needBloggers(true)
->executeWithOffsetPager($this->getPager()); ->executeWithCursorPager($pager);
$this->setBlogs($blogs); $this->setBlogs($blogs);

View file

@ -400,6 +400,7 @@ final class PhamePostEditController
} }
$blogs = id(new PhameBlogQuery()) $blogs = id(new PhameBlogQuery())
->setViewer($this->getRequest()->getUser())
->withPHIDs(array_keys($all_blogs_assoc)) ->withPHIDs(array_keys($all_blogs_assoc))
->execute(); ->execute();
$blogs = mpull($blogs, null, 'getPHID'); $blogs = mpull($blogs, null, 'getPHID');

View file

@ -19,7 +19,7 @@
/** /**
* @group phame * @group phame
*/ */
final class PhameBlogQuery extends PhabricatorOffsetPagedQuery { final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $phids; private $phids;
private $domain; private $domain;
@ -40,7 +40,7 @@ final class PhameBlogQuery extends PhabricatorOffsetPagedQuery {
return $this; return $this;
} }
public function execute() { public function loadPage() {
$table = new PhameBlog(); $table = new PhameBlog();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
@ -102,22 +102,19 @@ final class PhameBlogQuery extends PhabricatorOffsetPagedQuery {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,
'phid IN (%Ls)', 'phid IN (%Ls)',
$this->phids $this->phids);
);
} }
if ($this->domain) { if ($this->domain) {
$where[] = qsprintf( $where[] = qsprintf(
$conn_r, $conn_r,
'domain = %s', 'domain = %s',
$this->domain $this->domain);
);
} }
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where); return $this->formatWhereClause($where);
} }
private function buildOrderClause($conn_r) {
return 'ORDER BY id DESC';
}
} }

View file

@ -19,7 +19,7 @@
/** /**
* @group phame * @group phame
*/ */
final class PhameBlog extends PhameDAO { final class PhameBlog extends PhameDAO implements PhabricatorPolicyInterface {
const SKIN_DEFAULT = 'PhabricatorBlogSkin'; const SKIN_DEFAULT = 'PhabricatorBlogSkin';
@ -30,8 +30,9 @@ final class PhameBlog extends PhameDAO {
protected $domain; protected $domain;
protected $configData; protected $configData;
protected $creatorPHID; protected $creatorPHID;
protected $viewPolicy;
private $skin; protected $editPolicy;
protected $joinPolicy;
private $bloggerPHIDs; private $bloggerPHIDs;
private $bloggers; private $bloggers;
@ -212,4 +213,54 @@ final class PhameBlog extends PhameDAO {
public static function getRequestBlog() { public static function getRequestBlog() {
return self::$requestBlog; return self::$requestBlog;
} }
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
PhabricatorPolicyCapability::CAN_JOIN,
);
}
public function getPolicy($capability) {
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 $user) {
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
$can_join = PhabricatorPolicyCapability::CAN_JOIN;
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
// Users who can edit or post to a blog can always view it.
if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) {
return true;
}
if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_join)) {
return true;
}
break;
case PhabricatorPolicyCapability::CAN_JOIN:
// Users who can edit a blog can always post to it.
if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) {
return true;
}
break;
}
return false;
}
} }

View file

@ -78,7 +78,12 @@ final class PhameBlogListView extends AphrontView {
'Custom Domain', 'Custom Domain',
$blog->getDomain()); $blog->getDomain());
if (isset($bloggers[$user->getPHID()])) { $can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$blog,
PhabricatorPolicyCapability::CAN_EDIT);
if ($can_edit) {
$item->addAttribute( $item->addAttribute(
phutil_render_tag( phutil_render_tag(
'a', 'a',

View file

@ -1004,6 +1004,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'php', 'type' => 'php',
'name' => $this->getPatchPath('ponder-mailkey-populate.php'), 'name' => $this->getPatchPath('ponder-mailkey-populate.php'),
), ),
'phamepolicy.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('phamepolicy.sql'),
),
); );
} }