Better privilege checking for batch operations
This commit is contained in:
parent
cd437ca036
commit
410237d678
30 changed files with 314 additions and 85 deletions
|
@ -73,7 +73,6 @@ passwordResetEmailBody = "Hello,{nl}{nl}You received this e-mail because someone
|
||||||
registerAccount=anonymous
|
registerAccount=anonymous
|
||||||
;registerAccount=nobody
|
;registerAccount=nobody
|
||||||
|
|
||||||
uploadPost=registered
|
|
||||||
listPosts=anonymous
|
listPosts=anonymous
|
||||||
listPosts.sketchy=registered
|
listPosts.sketchy=registered
|
||||||
listPosts.unsafe=registered
|
listPosts.unsafe=registered
|
||||||
|
@ -84,6 +83,16 @@ viewPost.unsafe=registered
|
||||||
viewPost.hidden=moderator
|
viewPost.hidden=moderator
|
||||||
retrievePost=anonymous
|
retrievePost=anonymous
|
||||||
favoritePost=registered
|
favoritePost=registered
|
||||||
|
|
||||||
|
addPost=registered
|
||||||
|
addPostSafety=registered
|
||||||
|
addPostTags=registered
|
||||||
|
addPostThumb=power-user
|
||||||
|
addPostSource=registered
|
||||||
|
addPostRelations=power-user
|
||||||
|
addPostContent=registered
|
||||||
|
|
||||||
|
editPost=registered
|
||||||
editPostSafety.own=registered
|
editPostSafety.own=registered
|
||||||
editPostSafety.all=moderator
|
editPostSafety.all=moderator
|
||||||
editPostTags=registered
|
editPostTags=registered
|
||||||
|
@ -92,6 +101,7 @@ editPostSource=moderator
|
||||||
editPostRelations.own=registered
|
editPostRelations.own=registered
|
||||||
editPostRelations.all=moderator
|
editPostRelations.all=moderator
|
||||||
editPostContent=moderator
|
editPostContent=moderator
|
||||||
|
|
||||||
massTag.own=registered
|
massTag.own=registered
|
||||||
massTag.all=power-user
|
massTag.all=power-user
|
||||||
hidePost=moderator
|
hidePost=moderator
|
||||||
|
|
|
@ -86,7 +86,7 @@ class Access
|
||||||
public static function assert(Privilege $privilege, $user = null)
|
public static function assert(Privilege $privilege, $user = null)
|
||||||
{
|
{
|
||||||
if (!self::check($privilege, $user))
|
if (!self::check($privilege, $user))
|
||||||
self::fail();
|
self::fail('Insufficient privileges (' . $privilege->toString() . ')');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function assertEmailConfirmation($user = null)
|
public static function assertEmailConfirmation($user = null)
|
||||||
|
@ -95,9 +95,9 @@ class Access
|
||||||
self::fail('Need e-mail address confirmation to continue');
|
self::fail('Need e-mail address confirmation to continue');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function fail($message = 'Insufficient privileges')
|
public static function fail($message)
|
||||||
{
|
{
|
||||||
throw new SimpleException($message);
|
throw new AccessException($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getIdentity($user)
|
public static function getIdentity($user)
|
||||||
|
|
4
src/AccessException.php
Normal file
4
src/AccessException.php
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
class AccessException extends SimpleException
|
||||||
|
{
|
||||||
|
}
|
|
@ -1,6 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
abstract class AbstractJob
|
abstract class AbstractJob
|
||||||
{
|
{
|
||||||
|
const CONTEXT_NORMAL = 1;
|
||||||
|
const CONTEXT_BATCH_EDIT = 2;
|
||||||
|
const CONTEXT_BATCH_ADD = 3;
|
||||||
|
|
||||||
const COMMENT_ID = 'comment-id';
|
const COMMENT_ID = 'comment-id';
|
||||||
const LOG_ID = 'log-id';
|
const LOG_ID = 'log-id';
|
||||||
|
|
||||||
|
@ -21,6 +25,7 @@ abstract class AbstractJob
|
||||||
const STATE = 'state';
|
const STATE = 'state';
|
||||||
|
|
||||||
protected $arguments = [];
|
protected $arguments = [];
|
||||||
|
protected $context = self::CONTEXT_NORMAL;
|
||||||
|
|
||||||
public function prepare()
|
public function prepare()
|
||||||
{
|
{
|
||||||
|
@ -28,6 +33,11 @@ abstract class AbstractJob
|
||||||
|
|
||||||
public abstract function execute();
|
public abstract function execute();
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public function requiresAuthentication()
|
public function requiresAuthentication()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -43,6 +53,16 @@ abstract class AbstractJob
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getContext()
|
||||||
|
{
|
||||||
|
return $this->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setContext($context)
|
||||||
|
{
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
public function getArgument($key)
|
public function getArgument($key)
|
||||||
{
|
{
|
||||||
if (!$this->hasArgument($key))
|
if (!$this->hasArgument($key))
|
||||||
|
|
|
@ -8,6 +8,10 @@ final class Api
|
||||||
return \Chibi\Database::transaction(function() use ($job, $jobArgs)
|
return \Chibi\Database::transaction(function() use ($job, $jobArgs)
|
||||||
{
|
{
|
||||||
$job->setArguments($jobArgs);
|
$job->setArguments($jobArgs);
|
||||||
|
|
||||||
|
if (!$job->isSatisfied())
|
||||||
|
throw new ApiJobUnsatisfiedException($job);
|
||||||
|
|
||||||
$job->prepare();
|
$job->prepare();
|
||||||
|
|
||||||
self::checkPrivileges($job);
|
self::checkPrivileges($job);
|
||||||
|
|
8
src/Api/ApiJobUnsatisfiedException.php
Normal file
8
src/Api/ApiJobUnsatisfiedException.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php
|
||||||
|
class ApiJobUnsatisfiedException extends SimpleException
|
||||||
|
{
|
||||||
|
public function __construct(AbstractJob $job)
|
||||||
|
{
|
||||||
|
parent::__construct(get_class($job) . ' cannot be run due to unsatisfied execution conditions.');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
abstract class AbstractPostEditJob extends AbstractPostJob
|
|
||||||
{
|
|
||||||
protected $skipSaving = false;
|
|
||||||
|
|
||||||
public function skipSaving()
|
|
||||||
{
|
|
||||||
$this->skipSaving = true;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<?php
|
|
||||||
abstract class AbstractUserEditJob extends AbstractUserJob
|
|
||||||
{
|
|
||||||
protected $skipSaving = false;
|
|
||||||
|
|
||||||
public function skipSaving()
|
|
||||||
{
|
|
||||||
$this->skipSaving = true;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ class AddPostJob extends AbstractJob
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = PostModel::spawn();
|
$post = PostModel::spawn();
|
||||||
Logger::bufferChanges();
|
|
||||||
|
|
||||||
//basic stuff
|
//basic stuff
|
||||||
$anonymous = $this->getArgument(self::ANONYMOUS);
|
$anonymous = $this->getArgument(self::ANONYMOUS);
|
||||||
|
@ -20,14 +19,16 @@ class AddPostJob extends AbstractJob
|
||||||
//warning: it uses internally the same privileges as post editing
|
//warning: it uses internally the same privileges as post editing
|
||||||
$arguments = $this->getArguments();
|
$arguments = $this->getArguments();
|
||||||
$arguments[EditPostJob::POST_ENTITY] = $post;
|
$arguments[EditPostJob::POST_ENTITY] = $post;
|
||||||
Api::run((new EditPostJob)->skipSaving(), $arguments);
|
|
||||||
|
Logger::bufferChanges();
|
||||||
|
$job = new EditPostJob();
|
||||||
|
$job->setContext(AbstractJob::CONTEXT_BATCH_ADD);
|
||||||
|
Api::run($job, $arguments);
|
||||||
|
Logger::setBuffer([]);
|
||||||
|
|
||||||
//save to db
|
//save to db
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
//clean edit log
|
|
||||||
Logger::setBuffer([]);
|
|
||||||
|
|
||||||
//log
|
//log
|
||||||
Logger::log('{user} added {post} (tags: {tags}, safety: {safety}, source: {source})', [
|
Logger::log('{user} added {post} (tags: {tags}, safety: {safety}, source: {source})', [
|
||||||
'user' => ($anonymous and !getConfig()->misc->logAnonymousUploads)
|
'user' => ($anonymous and !getConfig()->misc->logAnonymousUploads)
|
||||||
|
@ -46,7 +47,7 @@ class AddPostJob extends AbstractJob
|
||||||
|
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(Privilege::UploadPost);
|
return new Privilege(Privilege::AddPost);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function requiresConfirmedEmail()
|
public function requiresConfirmedEmail()
|
||||||
|
|
|
@ -20,7 +20,9 @@ class AddUserJob extends AbstractJob
|
||||||
|
|
||||||
Logger::bufferChanges();
|
Logger::bufferChanges();
|
||||||
Access::disablePrivilegeChecking();
|
Access::disablePrivilegeChecking();
|
||||||
Api::run((new EditUserJob)->skipSaving(), $arguments);
|
$job = new EditUserJob();
|
||||||
|
$job->setContext(self::CONTEXT_BATCH_ADD);
|
||||||
|
Api::run($job, $arguments);
|
||||||
Access::enablePrivilegeChecking();
|
Access::enablePrivilegeChecking();
|
||||||
Logger::setBuffer([]);
|
Logger::setBuffer([]);
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostContentJob extends AbstractPostEditJob
|
class EditPostContentJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
const POST_CONTENT = 'post-content';
|
const POST_CONTENT = 'post-content';
|
||||||
const POST_CONTENT_URL = 'post-content-url';
|
const POST_CONTENT_URL = 'post-content-url';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::POST_CONTENT)
|
||||||
|
or $this->hasArgument(self::POST_CONTENT_URL);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
|
@ -19,7 +25,7 @@ class EditPostContentJob extends AbstractPostEditJob
|
||||||
$post->setContentFromPath($file->filePath, $file->fileName);
|
$post->setContentFromPath($file->filePath, $file->fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
Logger::log('{user} changed contents of {post}', [
|
Logger::log('{user} changed contents of {post}', [
|
||||||
|
@ -32,7 +38,9 @@ class EditPostContentJob extends AbstractPostEditJob
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(
|
return new Privilege(
|
||||||
Privilege::EditPostContent,
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostContent
|
||||||
|
: Privilege::EditPostContent,
|
||||||
Access::getIdentity($this->post->getUploader()));
|
Access::getIdentity($this->post->getUploader()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostJob extends AbstractPostEditJob
|
class EditPostJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
|
@ -19,8 +19,9 @@ class EditPostJob extends AbstractPostEditJob
|
||||||
|
|
||||||
foreach ($subJobs as $subJob)
|
foreach ($subJobs as $subJob)
|
||||||
{
|
{
|
||||||
if ($this->skipSaving)
|
$subJob->setContext($this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
$subJob->skipSaving();
|
? self::CONTEXT_BATCH_ADD
|
||||||
|
: self::CONTEXT_BATCH_EDIT);
|
||||||
|
|
||||||
$args = $this->getArguments();
|
$args = $this->getArguments();
|
||||||
$args[self::POST_ENTITY] = $post;
|
$args[self::POST_ENTITY] = $post;
|
||||||
|
@ -28,15 +29,24 @@ class EditPostJob extends AbstractPostEditJob
|
||||||
{
|
{
|
||||||
Api::run($subJob, $args);
|
Api::run($subJob, $args);
|
||||||
}
|
}
|
||||||
catch (ApiMissingArgumentException $e)
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == AbstractJob::CONTEXT_NORMAL)
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
Logger::flush();
|
Logger::flush();
|
||||||
return $post;
|
return $post;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function requiresPrivilege()
|
||||||
|
{
|
||||||
|
return new Privilege(
|
||||||
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPost
|
||||||
|
: Privilege::EditPost,
|
||||||
|
Access::getIdentity($this->post->getUploader()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostRelationsJob extends AbstractPostEditJob
|
class EditPostRelationsJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
const RELATED_POST_IDS = 'related-post-ids';
|
const RELATED_POST_IDS = 'related-post-ids';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::RELATED_POST_IDS);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
|
@ -12,7 +17,7 @@ class EditPostRelationsJob extends AbstractPostEditJob
|
||||||
$post->setRelationsFromText($relations);
|
$post->setRelationsFromText($relations);
|
||||||
$newRelatedIds = array_map(function($post) { return $post->getId(); }, $post->getRelations());
|
$newRelatedIds = array_map(function($post) { return $post->getId(); }, $post->getRelations());
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
foreach (array_diff($oldRelatedIds, $newRelatedIds) as $post2id)
|
foreach (array_diff($oldRelatedIds, $newRelatedIds) as $post2id)
|
||||||
|
@ -37,7 +42,9 @@ class EditPostRelationsJob extends AbstractPostEditJob
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(
|
return new Privilege(
|
||||||
Privilege::EditPostRelations,
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostRelations
|
||||||
|
: Privilege::EditPostRelations,
|
||||||
Access::getIdentity($this->post->getUploader()));
|
Access::getIdentity($this->post->getUploader()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostSafetyJob extends AbstractPostEditJob
|
class EditPostSafetyJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
const SAFETY = 'safety';
|
const SAFETY = 'safety';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::SAFETY);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
|
@ -11,7 +16,7 @@ class EditPostSafetyJob extends AbstractPostEditJob
|
||||||
$oldSafety = $post->getSafety();
|
$oldSafety = $post->getSafety();
|
||||||
$post->setSafety($newSafety);
|
$post->setSafety($newSafety);
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
if ($oldSafety != $newSafety)
|
if ($oldSafety != $newSafety)
|
||||||
|
@ -28,7 +33,9 @@ class EditPostSafetyJob extends AbstractPostEditJob
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(
|
return new Privilege(
|
||||||
Privilege::EditPostSafety,
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostSafety
|
||||||
|
: Privilege::EditPostSafety,
|
||||||
Access::getIdentity($this->post->getUploader()));
|
Access::getIdentity($this->post->getUploader()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostSourceJob extends AbstractPostEditJob
|
class EditPostSourceJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
const SOURCE = 'source';
|
const SOURCE = 'source';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::SOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
|
@ -11,7 +16,7 @@ class EditPostSourceJob extends AbstractPostEditJob
|
||||||
$oldSource = $post->source;
|
$oldSource = $post->source;
|
||||||
$post->setSource($newSource);
|
$post->setSource($newSource);
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
if ($oldSource != $newSource)
|
if ($oldSource != $newSource)
|
||||||
|
@ -28,7 +33,9 @@ class EditPostSourceJob extends AbstractPostEditJob
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(
|
return new Privilege(
|
||||||
Privilege::EditPostSource,
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostSource
|
||||||
|
: Privilege::EditPostSource,
|
||||||
Access::getIdentity($this->post->getUploader()));
|
Access::getIdentity($this->post->getUploader()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostTagsJob extends AbstractPostEditJob
|
class EditPostTagsJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::TAG_NAMES);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
|
@ -10,7 +15,7 @@ class EditPostTagsJob extends AbstractPostEditJob
|
||||||
$post->setTagsFromText($tags);
|
$post->setTagsFromText($tags);
|
||||||
$newTags = array_map(function($tag) { return $tag->getName(); }, $post->getTags());
|
$newTags = array_map(function($tag) { return $tag->getName(); }, $post->getTags());
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
{
|
{
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
TagModel::removeUnused();
|
TagModel::removeUnused();
|
||||||
|
@ -38,7 +43,9 @@ class EditPostTagsJob extends AbstractPostEditJob
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(
|
return new Privilege(
|
||||||
Privilege::EditPostTags,
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostTags
|
||||||
|
: Privilege::EditPostTags,
|
||||||
Access::getIdentity($this->post->getUploader()));
|
Access::getIdentity($this->post->getUploader()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditPostThumbJob extends AbstractPostEditJob
|
class EditPostThumbJob extends AbstractPostJob
|
||||||
{
|
{
|
||||||
const THUMB_CONTENT = 'thumb-content';
|
const THUMB_CONTENT = 'thumb-content';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::THUMB_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$post = $this->post;
|
$post = $this->post;
|
||||||
|
@ -10,7 +15,7 @@ class EditPostThumbJob extends AbstractPostEditJob
|
||||||
|
|
||||||
$post->setCustomThumbnailFromPath($file->filePath);
|
$post->setCustomThumbnailFromPath($file->filePath);
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
PostModel::save($post);
|
PostModel::save($post);
|
||||||
|
|
||||||
Logger::log('{user} changed thumb of {post}', [
|
Logger::log('{user} changed thumb of {post}', [
|
||||||
|
@ -23,7 +28,9 @@ class EditPostThumbJob extends AbstractPostEditJob
|
||||||
public function requiresPrivilege()
|
public function requiresPrivilege()
|
||||||
{
|
{
|
||||||
return new Privilege(
|
return new Privilege(
|
||||||
Privilege::EditPostThumb,
|
$this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
|
? Privilege::AddPostThumb
|
||||||
|
: Privilege::EditPostThumb,
|
||||||
Access::getIdentity($this->post->getUploader()));
|
Access::getIdentity($this->post->getUploader()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditUserAccessRankJob extends AbstractUserEditJob
|
class EditUserAccessRankJob extends AbstractUserJob
|
||||||
{
|
{
|
||||||
const NEW_ACCESS_RANK = 'new-access-rank';
|
const NEW_ACCESS_RANK = 'new-access-rank';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::NEW_ACCESS_RANK);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$user = $this->user;
|
$user = $this->user;
|
||||||
|
@ -14,7 +19,7 @@ class EditUserAccessRankJob extends AbstractUserEditJob
|
||||||
|
|
||||||
$user->setAccessRank($newAccessRank);
|
$user->setAccessRank($newAccessRank);
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
UserModel::save($user);
|
UserModel::save($user);
|
||||||
|
|
||||||
Logger::log('{user} changed {subject}\'s access rank to {rank}', [
|
Logger::log('{user} changed {subject}\'s access rank to {rank}', [
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditUserEmailJob extends AbstractUserEditJob
|
class EditUserEmailJob extends AbstractUserJob
|
||||||
{
|
{
|
||||||
const NEW_EMAIL = 'new-email';
|
const NEW_EMAIL = 'new-email';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::NEW_EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
if (getConfig()->registration->needEmailForRegistering)
|
if (getConfig()->registration->needEmailForRegistering)
|
||||||
|
@ -29,7 +34,7 @@ class EditUserEmailJob extends AbstractUserEditJob
|
||||||
$user->confirmEmail();
|
$user->confirmEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
UserModel::save($user);
|
UserModel::save($user);
|
||||||
|
|
||||||
Logger::log('{user} changed {subject}\'s e-mail to {mail}', [
|
Logger::log('{user} changed {subject}\'s e-mail to {mail}', [
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
class EditUserJob extends AbstractUserEditJob
|
class EditUserJob extends AbstractUserJob
|
||||||
{
|
{
|
||||||
protected $subJobs;
|
protected $subJobs;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ class EditUserJob extends AbstractUserEditJob
|
||||||
Api::checkPrivileges($subJob);
|
Api::checkPrivileges($subJob);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (SimpleException $e)
|
catch (AccessException $e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,9 @@ class EditUserJob extends AbstractUserEditJob
|
||||||
|
|
||||||
foreach ($this->subJobs as $subJob)
|
foreach ($this->subJobs as $subJob)
|
||||||
{
|
{
|
||||||
if ($this->skipSaving)
|
$subJob->setContext($this->getContext() == self::CONTEXT_BATCH_ADD
|
||||||
$subJob->skipSaving();
|
? self::CONTEXT_BATCH_ADD
|
||||||
|
: self::CONTEXT_BATCH_EDIT);
|
||||||
|
|
||||||
$args = $this->getArguments();
|
$args = $this->getArguments();
|
||||||
$args[self::USER_ENTITY] = $user;
|
$args[self::USER_ENTITY] = $user;
|
||||||
|
@ -49,12 +50,12 @@ class EditUserJob extends AbstractUserEditJob
|
||||||
{
|
{
|
||||||
Api::run($subJob, $args);
|
Api::run($subJob, $args);
|
||||||
}
|
}
|
||||||
catch (ApiMissingArgumentException $e)
|
catch (ApiJobUnsatisfiedException $e)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
UserModel::save($user);
|
UserModel::save($user);
|
||||||
|
|
||||||
Logger::flush();
|
Logger::flush();
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditUserNameJob extends AbstractUserEditJob
|
class EditUserNameJob extends AbstractUserJob
|
||||||
{
|
{
|
||||||
const NEW_USER_NAME = 'new-user-name';
|
const NEW_USER_NAME = 'new-user-name';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::NEW_USER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$user = $this->user;
|
$user = $this->user;
|
||||||
|
@ -15,7 +20,7 @@ class EditUserNameJob extends AbstractUserEditJob
|
||||||
$user->setName($newName);
|
$user->setName($newName);
|
||||||
UserModel::validateUserName($user);
|
UserModel::validateUserName($user);
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
UserModel::save($user);
|
UserModel::save($user);
|
||||||
|
|
||||||
Logger::log('{user} renamed {old} to {new}', [
|
Logger::log('{user} renamed {old} to {new}', [
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
class EditUserPasswordJob extends AbstractUserEditJob
|
class EditUserPasswordJob extends AbstractUserJob
|
||||||
{
|
{
|
||||||
const NEW_PASSWORD = 'new-password';
|
const NEW_PASSWORD = 'new-password';
|
||||||
|
|
||||||
|
public function isSatisfied()
|
||||||
|
{
|
||||||
|
return $this->hasArgument(self::NEW_PASSWORD);
|
||||||
|
}
|
||||||
|
|
||||||
public function execute()
|
public function execute()
|
||||||
{
|
{
|
||||||
$user = $this->user;
|
$user = $this->user;
|
||||||
|
@ -15,7 +20,7 @@ class EditUserPasswordJob extends AbstractUserEditJob
|
||||||
|
|
||||||
$user->passHash = $newPasswordHash;
|
$user->passHash = $newPasswordHash;
|
||||||
|
|
||||||
if (!$this->skipSaving)
|
if ($this->getContext() == self::CONTEXT_NORMAL)
|
||||||
UserModel::save($user);
|
UserModel::save($user);
|
||||||
|
|
||||||
Logger::log('{user} changed {subject}\'s password', [
|
Logger::log('{user} changed {subject}\'s password', [
|
||||||
|
|
|
@ -13,7 +13,7 @@ abstract class Enum
|
||||||
public function toDisplayString()
|
public function toDisplayString()
|
||||||
{
|
{
|
||||||
return TextCaseConverter::convert($this->toString(),
|
return TextCaseConverter::convert($this->toString(),
|
||||||
TextCaseConverter::SNAKE_CASE,
|
TextCaseConverter::CAMEL_CASE,
|
||||||
TextCaseConverter::BLANK_CASE);
|
TextCaseConverter::BLANK_CASE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,21 +2,30 @@
|
||||||
class Privilege extends Enum
|
class Privilege extends Enum
|
||||||
{
|
{
|
||||||
const ListPosts = 1;
|
const ListPosts = 1;
|
||||||
const UploadPost = 2;
|
|
||||||
const ViewPost = 3;
|
const ViewPost = 3;
|
||||||
const RetrievePost = 4;
|
const RetrievePost = 4;
|
||||||
const FavoritePost = 5;
|
const FavoritePost = 5;
|
||||||
|
const HidePost = 9;
|
||||||
|
const DeletePost = 10;
|
||||||
|
const FeaturePost = 25;
|
||||||
|
const ScorePost = 31;
|
||||||
|
const FlagPost = 34;
|
||||||
|
|
||||||
|
const EditPost = 45;
|
||||||
const EditPostSafety = 6;
|
const EditPostSafety = 6;
|
||||||
const EditPostTags = 7;
|
const EditPostTags = 7;
|
||||||
const EditPostThumb = 8;
|
const EditPostThumb = 8;
|
||||||
const EditPostSource = 26;
|
const EditPostSource = 26;
|
||||||
const EditPostRelations = 30;
|
const EditPostRelations = 30;
|
||||||
const EditPostContent = 36;
|
const EditPostContent = 36;
|
||||||
const HidePost = 9;
|
|
||||||
const DeletePost = 10;
|
const AddPost = 2;
|
||||||
const FeaturePost = 25;
|
const AddPostSafety = 39;
|
||||||
const ScorePost = 31;
|
const AddPostTags = 40;
|
||||||
const FlagPost = 34;
|
const AddPostThumb = 41;
|
||||||
|
const AddPostSource = 42;
|
||||||
|
const AddPostRelations = 43;
|
||||||
|
const AddPostContent = 44;
|
||||||
|
|
||||||
const RegisterAccount = 38;
|
const RegisterAccount = 38;
|
||||||
const ListUsers = 11;
|
const ListUsers = 11;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
$activeController == 'post' and $activeAction != 'upload');
|
$activeController == 'post' and $activeAction != 'upload');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Access::check(new Privilege(Privilege::UploadPost)))
|
if (Access::check(new Privilege(Privilege::AddPost)))
|
||||||
{
|
{
|
||||||
$registerNavItem(
|
$registerNavItem(
|
||||||
'Upload',
|
'Upload',
|
||||||
|
|
|
@ -59,4 +59,9 @@ class AbstractTest
|
||||||
getConfig()->privileges->$privilege = 'nobody';
|
getConfig()->privileges->$privilege = 'nobody';
|
||||||
Access::init();
|
Access::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getPath($name)
|
||||||
|
{
|
||||||
|
return getConfig()->rootDir . DS . 'tests' . DS . 'TestFiles' . DS . $name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,9 +15,8 @@ class ApiPrivilegeTest extends AbstractFullApiTest
|
||||||
$this->testRegularPrivilege(new ActivateUserEmailJob(), false);
|
$this->testRegularPrivilege(new ActivateUserEmailJob(), false);
|
||||||
$this->testRegularPrivilege(new AddCommentJob(), new Privilege(Privilege::AddComment));
|
$this->testRegularPrivilege(new AddCommentJob(), new Privilege(Privilege::AddComment));
|
||||||
$this->testRegularPrivilege(new PreviewCommentJob(), new Privilege(Privilege::AddComment));
|
$this->testRegularPrivilege(new PreviewCommentJob(), new Privilege(Privilege::AddComment));
|
||||||
$this->testRegularPrivilege(new AddPostJob(), new Privilege(Privilege::UploadPost));
|
$this->testRegularPrivilege(new AddPostJob(), new Privilege(Privilege::AddPost));
|
||||||
$this->testRegularPrivilege(new AddUserJob(), new Privilege(Privilege::RegisterAccount));
|
$this->testRegularPrivilege(new AddUserJob(), new Privilege(Privilege::RegisterAccount));
|
||||||
$this->testRegularPrivilege(new EditPostJob(), false);
|
|
||||||
$this->testRegularPrivilege(new EditUserJob(), false);
|
$this->testRegularPrivilege(new EditUserJob(), false);
|
||||||
$this->testRegularPrivilege(new GetLogJob(), new Privilege(Privilege::ViewLog));
|
$this->testRegularPrivilege(new GetLogJob(), new Privilege(Privilege::ViewLog));
|
||||||
$this->testRegularPrivilege(new ListCommentsJob(), new Privilege(Privilege::ListComments));
|
$this->testRegularPrivilege(new ListCommentsJob(), new Privilege(Privilege::ListComments));
|
||||||
|
@ -42,12 +41,27 @@ class ApiPrivilegeTest extends AbstractFullApiTest
|
||||||
$this->login($this->mockUser());
|
$this->login($this->mockUser());
|
||||||
|
|
||||||
$this->testDynamicPostPrivilege(new DeletePostJob(), new Privilege(Privilege::DeletePost));
|
$this->testDynamicPostPrivilege(new DeletePostJob(), new Privilege(Privilege::DeletePost));
|
||||||
|
$this->testDynamicPostPrivilege(new EditPostJob(), new Privilege(Privilege::EditPost));
|
||||||
$this->testDynamicPostPrivilege(new EditPostContentJob(), new Privilege(Privilege::EditPostContent));
|
$this->testDynamicPostPrivilege(new EditPostContentJob(), new Privilege(Privilege::EditPostContent));
|
||||||
$this->testDynamicPostPrivilege(new EditPostRelationsJob(), new Privilege(Privilege::EditPostRelations));
|
$this->testDynamicPostPrivilege(new EditPostRelationsJob(), new Privilege(Privilege::EditPostRelations));
|
||||||
$this->testDynamicPostPrivilege(new EditPostSafetyJob(), new Privilege(Privilege::EditPostSafety));
|
$this->testDynamicPostPrivilege(new EditPostSafetyJob(), new Privilege(Privilege::EditPostSafety));
|
||||||
$this->testDynamicPostPrivilege(new EditPostSourceJob(), new Privilege(Privilege::EditPostSource));
|
$this->testDynamicPostPrivilege(new EditPostSourceJob(), new Privilege(Privilege::EditPostSource));
|
||||||
$this->testDynamicPostPrivilege(new EditPostTagsJob(), new Privilege(Privilege::EditPostTags));
|
$this->testDynamicPostPrivilege(new EditPostTagsJob(), new Privilege(Privilege::EditPostTags));
|
||||||
$this->testDynamicPostPrivilege(new EditPostThumbJob(), new Privilege(Privilege::EditPostThumb));
|
$this->testDynamicPostPrivilege(new EditPostThumbJob(), new Privilege(Privilege::EditPostThumb));
|
||||||
|
|
||||||
|
$ctx = function($job)
|
||||||
|
{
|
||||||
|
$job->setContext(AbstractJob::CONTEXT_BATCH_ADD);
|
||||||
|
return $job;
|
||||||
|
};
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostJob), new Privilege(Privilege::AddPost));
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostContentJob), new Privilege(Privilege::AddPostContent));
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostRelationsJob), new Privilege(Privilege::AddPostRelations));
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostSafetyJob), new Privilege(Privilege::AddPostSafety));
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostSourceJob), new Privilege(Privilege::AddPostSource));
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostTagsJob), new Privilege(Privilege::AddPostTags));
|
||||||
|
$this->testDynamicPostPrivilege($ctx(new EditPostThumbJob), new Privilege(Privilege::AddPostThumb));
|
||||||
|
|
||||||
$this->testDynamicPostPrivilege(new FeaturePostJob(), new Privilege(Privilege::FeaturePost));
|
$this->testDynamicPostPrivilege(new FeaturePostJob(), new Privilege(Privilege::FeaturePost));
|
||||||
$this->testDynamicPostPrivilege(new FlagPostJob(), new Privilege(Privilege::FlagPost));
|
$this->testDynamicPostPrivilege(new FlagPostJob(), new Privilege(Privilege::FlagPost));
|
||||||
$this->testDynamicPostPrivilege(new ScorePostJob(), new Privilege(Privilege::ScorePost));
|
$this->testDynamicPostPrivilege(new ScorePostJob(), new Privilege(Privilege::ScorePost));
|
||||||
|
|
55
tests/JobTests/AddPostJobTest.php
Normal file
55
tests/JobTests/AddPostJobTest.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
class AddPostJobTest extends AbstractTest
|
||||||
|
{
|
||||||
|
public function testSaving()
|
||||||
|
{
|
||||||
|
$this->prepare();
|
||||||
|
|
||||||
|
$this->grantAccess('addPost');
|
||||||
|
$this->grantAccess('addPostSafety');
|
||||||
|
$this->grantAccess('addPostTags');
|
||||||
|
$this->grantAccess('addPostSource');
|
||||||
|
$this->grantAccess('addPostContent');
|
||||||
|
|
||||||
|
$args =
|
||||||
|
[
|
||||||
|
AddPostJob::ANONYMOUS => false,
|
||||||
|
EditPostSafetyJob::SAFETY => PostSafety::Safe,
|
||||||
|
EditPostSourceJob::SOURCE => '',
|
||||||
|
EditPostContentJob::POST_CONTENT => new ApiFileInput($this->getPath('image.jpg'), 'test.jpg'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assert->doesNotThrow(function() use ($args)
|
||||||
|
{
|
||||||
|
Api::run(new AddPostJob(), $args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrivilegeFail()
|
||||||
|
{
|
||||||
|
$this->prepare();
|
||||||
|
|
||||||
|
$this->grantAccess('addPost');
|
||||||
|
$this->grantAccess('addPostSafety');
|
||||||
|
$this->grantAccess('addPostTags');
|
||||||
|
$this->grantAccess('addPostContent');
|
||||||
|
|
||||||
|
$args =
|
||||||
|
[
|
||||||
|
AddPostJob::ANONYMOUS => false,
|
||||||
|
EditPostSafetyJob::SAFETY => PostSafety::Safe,
|
||||||
|
EditPostSourceJob::SOURCE => '',
|
||||||
|
EditPostContentJob::POST_CONTENT => new ApiFileInput($this->getPath('image.jpg'), 'test.jpg'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assert->throws(function() use ($args)
|
||||||
|
{
|
||||||
|
Api::run(new AddPostJob(), $args);
|
||||||
|
}, 'Insufficient privilege');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepare()
|
||||||
|
{
|
||||||
|
getConfig()->registration->needEmailForUploading = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -162,9 +162,4 @@ class EditPostContentJobTest extends AbstractTest
|
||||||
|
|
||||||
return $post;
|
return $post;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getPath($name)
|
|
||||||
{
|
|
||||||
return getConfig()->rootDir . DS . 'tests' . DS . 'TestFiles' . DS . $name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
50
tests/JobTests/EditPostJobTest.php
Normal file
50
tests/JobTests/EditPostJobTest.php
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
<?php
|
||||||
|
class EditPostJobTest extends AbstractTest
|
||||||
|
{
|
||||||
|
public function testSaving()
|
||||||
|
{
|
||||||
|
$this->grantAccess('editPost');
|
||||||
|
$this->grantAccess('editPostSafety');
|
||||||
|
$this->grantAccess('editPostTags');
|
||||||
|
$this->grantAccess('editPostSource');
|
||||||
|
$this->grantAccess('editPostContent');
|
||||||
|
|
||||||
|
$post = $this->mockPost(Auth::getCurrentUser());
|
||||||
|
|
||||||
|
$args =
|
||||||
|
[
|
||||||
|
EditPostJob::POST_ID => $post->getId(),
|
||||||
|
EditPostSafetyJob::SAFETY => PostSafety::Safe,
|
||||||
|
EditPostSourceJob::SOURCE => '',
|
||||||
|
EditPostContentJob::POST_CONTENT => new ApiFileInput($this->getPath('image.jpg'), 'test.jpg'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assert->doesNotThrow(function() use ($args)
|
||||||
|
{
|
||||||
|
Api::run(new EditPostJob(), $args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testPrivilegeFail()
|
||||||
|
{
|
||||||
|
$this->grantAccess('editPost');
|
||||||
|
$this->grantAccess('editPostSafety');
|
||||||
|
$this->grantAccess('editPostTags');
|
||||||
|
$this->grantAccess('editPostContent');
|
||||||
|
|
||||||
|
$post = $this->mockPost(Auth::getCurrentUser());
|
||||||
|
|
||||||
|
$args =
|
||||||
|
[
|
||||||
|
EditPostJob::POST_ID => $post->getId(),
|
||||||
|
EditPostSafetyJob::SAFETY => PostSafety::Safe,
|
||||||
|
EditPostSourceJob::SOURCE => '',
|
||||||
|
EditPostContentJob::POST_CONTENT => new ApiFileInput($this->getPath('image.jpg'), 'test.jpg'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assert->throws(function() use ($args)
|
||||||
|
{
|
||||||
|
Api::run(new EditPostJob(), $args);
|
||||||
|
}, 'Insufficient privilege');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue