More restrictive privilege system

This commit is contained in:
Marcin Kurczewski 2014-05-06 13:07:24 +02:00
parent 04481122ce
commit 26f2c46e5b
11 changed files with 236 additions and 174 deletions

View file

@ -25,8 +25,7 @@ class Access
$minAccessRank = TextHelper::resolveConstant($minAccessRankName, 'AccessRank');
self::$privileges[$key] = $minAccessRank;
if (!isset(self::$privileges[$privilegeName]) or
self::$privileges[$privilegeName] > $minAccessRank)
if (!isset(self::$privileges[$privilegeName]))
{
self::$privileges[$privilegeName] = $minAccessRank;
}

View file

@ -38,4 +38,16 @@ class AbstractTest
$comment->setText('test test');
return CommentModel::save($comment);
}
protected function grantAccess($privilege)
{
getConfig()->privileges->$privilege = 'anonymous';
Access::init();
}
protected function revokeAccess($privilege)
{
getConfig()->privileges->$privilege = 'nobody';
Access::init();
}
}

View file

@ -3,6 +3,9 @@ class BasicAuthTest extends AbstractTest
{
public function testValidPassword()
{
getConfig()->registration->staffActivation = false;
getConfig()->registration->needEmailForRegistering = false;
$user = $this->prepareValidUser();
UserModel::save($user);
@ -43,6 +46,9 @@ class BasicAuthTest extends AbstractTest
public function testBanned()
{
getConfig()->registration->staffActivation = false;
getConfig()->registration->needEmailForRegistering = false;
$user = $this->prepareValidUser();
$user->ban();
UserModel::save($user);
@ -56,6 +62,7 @@ class BasicAuthTest extends AbstractTest
public function testStaffConfirmationEnabled()
{
getConfig()->registration->staffActivation = true;
getConfig()->registration->needEmailForRegistering = false;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
@ -70,6 +77,7 @@ class BasicAuthTest extends AbstractTest
public function testStaffConfirmationDisabled()
{
getConfig()->registration->staffActivation = false;
getConfig()->registration->needEmailForRegistering = false;
$user = $this->prepareValidUser();
$user->staffConfirmed = false;
@ -83,6 +91,7 @@ class BasicAuthTest extends AbstractTest
public function testMailConfirmationEnabledFail1()
{
getConfig()->registration->staffActivation = false;
getConfig()->registration->needEmailForRegistering = true;
$user = $this->prepareValidUser();
@ -97,6 +106,7 @@ class BasicAuthTest extends AbstractTest
public function testMailConfirmationEnabledFail2()
{
getConfig()->registration->staffActivation = false;
getConfig()->registration->needEmailForRegistering = true;
$user = $this->prepareValidUser();
@ -112,6 +122,7 @@ class BasicAuthTest extends AbstractTest
public function testMailConfirmationEnabledPass()
{
getConfig()->registration->staffActivation = false;
getConfig()->registration->needEmailForRegistering = true;
$user = $this->prepareValidUser();

View file

@ -24,12 +24,22 @@ class AddCommentJobTest extends AbstractTest
});
}
public function testEmailActivation()
{
$this->prepare();
getConfig()->registration->needEmailForCommenting = true;
$this->assert->throws(function()
{
$this->runApi('alohaaaa');
}, 'Need e-mail');
}
public function testAlmostTooShortText()
{
$this->prepare();
$this->assert->doesNotThrow(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->minLength));
$this->runApi(str_repeat('b', getConfig()->comments->minLength));
});
}
@ -38,7 +48,7 @@ class AddCommentJobTest extends AbstractTest
$this->prepare();
$this->assert->doesNotThrow(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->maxLength));
$this->runApi(str_repeat('b', getConfig()->comments->maxLength));
});
}
@ -47,7 +57,7 @@ class AddCommentJobTest extends AbstractTest
$this->prepare();
$this->assert->throws(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->minLength - 1));
$this->runApi(str_repeat('b', getConfig()->comments->minLength - 1));
}, 'Comment must have at least');
}
@ -56,7 +66,7 @@ class AddCommentJobTest extends AbstractTest
$this->prepare();
$this->assert->throws(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->maxLength + 1));
$this->runApi(str_repeat('b', getConfig()->comments->maxLength + 1));
}, 'Comment must have at most');
}
@ -65,35 +75,28 @@ class AddCommentJobTest extends AbstractTest
$this->prepare();
Auth::setCurrentUser(null);
$this->assert->throws(function()
$this->assert->doesNotThrow(function()
{
$this->assert->isFalse(Auth::isLoggedIn());
return $this->runApi('alohaaaaaaa');
}, 'Insufficient privileges');
$this->runApi('alohaaaaaaa');
});
}
public function testAccessDenial()
{
$this->prepare();
getConfig()->privileges->addComment = 'nobody';
Access::init();
$this->assert->isFalse(Access::check(new Privilege(Privilege::AddComment)));
$this->revokeAccess('addComment');
$this->assert->throws(function()
{
return $this->runApi('alohaaaaaaa');
$this->runApi('alohaaaaaaa');
}, 'Insufficient privileges');
}
public function testAnonymous()
{
$this->prepare();
$this->grantAccess('addComment');
Auth::setCurrentUser(null);
getConfig()->privileges->addComment = 'anonymous';
Access::init();
$this->assert->isTrue(Access::check(new Privilege(Privilege::AddComment)));
$text = 'alohaaaaaaa';
$comment = $this->assert->doesNotThrow(function() use ($text)
@ -106,29 +109,13 @@ class AddCommentJobTest extends AbstractTest
$this->assert->areEqual(UserModel::getAnonymousName(), $comment->getCommenter()->getName());
}
public function testPrivilegeDependancies()
{
$this->prepare();
getConfig()->privileges->{'editComment'} = 'nobody';
getConfig()->privileges->{'editComment.own'} = 'nobody';
getConfig()->privileges->{'editComment.all'} = 'nobody';
Access::init();
$this->assert->isTrue(Access::check(new Privilege(Privilege::AddComment)));
$this->assert->isFalse(Access::check(new Privilege(Privilege::EditComment)));
$this->assert->doesNotThrow(function()
{
return $this->runApi('alohaaaaaaa');
}, 'insufficient privileges');
}
public function testWrongPostId()
{
$this->prepare();
$this->grantAccess('addComment');
$this->assert->throws(function()
{
return Api::run(
Api::run(
new AddCommentJob(),
[
AddCommentJob::POST_ID => 100,
@ -152,6 +139,8 @@ class AddCommentJobTest extends AbstractTest
protected function prepare()
{
getConfig()->registration->needEmailForCommenting = false;
$this->grantAccess('addComment');
$this->login($this->mockUser());
}
}

View file

@ -4,10 +4,11 @@ class DeleteCommentJobTest extends AbstractTest
public function testOwn()
{
$this->prepare();
$this->grantAccess('deleteComment');
$this->assert->doesNotThrow(function()
{
return $this->runApi();
$this->runApi();
});
$this->assert->areEqual(0, CommentModel::getCount());
@ -20,8 +21,7 @@ class DeleteCommentJobTest extends AbstractTest
$this->assert->throws(function()
{
$this->assert->isFalse(Auth::isLoggedIn());
return $this->runApi();
$this->runApi();
}, 'Not logged in');
}
@ -29,52 +29,39 @@ class DeleteCommentJobTest extends AbstractTest
{
$this->prepare();
getConfig()->privileges->{'deleteComment.own'} = 'nobody';
Access::init();
$this->assert->isFalse(Access::check(new Privilege(Privilege::DeleteComment)));
$this->assert->throws(function()
{
return $this->runApi();
$this->runApi();
}, 'Insufficient privileges');
}
public function testOtherAccessGrant()
{
$this->prepare();
$this->grantAccess('deleteComment.all');
getConfig()->privileges->{'deleteComment.all'} = 'nobody';
Access::init();
$this->assert->isTrue(Access::check(new Privilege(Privilege::DeleteComment)));
$comment = $this->mockComment(Auth::getCurrentUser());
//login as someone else
$this->login($this->mockUser());
$this->assert->doesNotThrow(function()
$this->assert->doesNotThrow(function() use ($comment)
{
return $this->runApi();
$this->runApi($comment);
});
}
public function testOtherAccessDenial()
{
$this->prepare();
$this->grantAccess('deleteComment.own');
$comment = $this->mockComment(Auth::getCurrentUser());
//login as someone else
$this->login($this->mockUser());
getConfig()->privileges->{'deleteComment.all'} = 'nobody';
Access::init();
$this->assert->isTrue(Access::check(new Privilege(Privilege::DeleteComment)));
$this->assert->isTrue(Access::check(new Privilege(Privilege::DeleteComment, 'own')));
$this->assert->isFalse(Access::check(new Privilege(Privilege::DeleteComment, 'all')));
$this->assert->throws(function() use ($comment)
{
Api::run(
new DeleteCommentJob(),
[
DeleteCommentJob::COMMENT_ID => $comment->getId(),
]);
$this->runApi($comment);
}, 'Insufficient privileges');
}
@ -83,7 +70,7 @@ class DeleteCommentJobTest extends AbstractTest
$this->prepare();
$this->assert->throws(function()
{
return Api::run(
Api::run(
new DeleteCommentJob(),
[
DeleteCommentJob::COMMENT_ID => 100,
@ -92,8 +79,9 @@ class DeleteCommentJobTest extends AbstractTest
}
protected function runApi()
protected function runApi($comment = null)
{
if ($comment === null)
$comment = $this->mockComment(Auth::getCurrentUser());
return Api::run(

View file

@ -4,6 +4,7 @@ class EditCommentJobTest extends AbstractTest
public function testOwn()
{
$this->prepare();
$this->grantAccess('editComment.own');
$text = 'alohaaaaaaa';
$comment = $this->assert->doesNotThrow(function() use ($text)
@ -24,48 +25,52 @@ class EditCommentJobTest extends AbstractTest
public function testOwnAlmostTooShortText()
{
$this->prepare();
$this->grantAccess('editComment.own');
$this->assert->doesNotThrow(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->minLength));
$this->runApi(str_repeat('b', getConfig()->comments->minLength));
});
}
public function testOwnAlmostTooLongText()
{
$this->prepare();
$this->grantAccess('editComment.own');
$this->assert->doesNotThrow(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->maxLength));
$this->runApi(str_repeat('b', getConfig()->comments->maxLength));
});
}
public function testOwnTooShortText()
{
$this->prepare();
$this->grantAccess('editComment.own');
$this->assert->throws(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->minLength - 1));
$this->runApi(str_repeat('b', getConfig()->comments->minLength - 1));
}, 'Comment must have at least');
}
public function testOwnTooLongText()
{
$this->prepare();
$this->grantAccess('editComment.own');
$this->assert->throws(function()
{
return $this->runApi(str_repeat('b', getConfig()->comments->maxLength + 1));
$this->runApi(str_repeat('b', getConfig()->comments->maxLength + 1));
}, 'Comment must have at most');
}
public function testNoAuth()
{
$this->prepare();
$this->grantAccess('editComment');
Auth::setCurrentUser(null);
$this->assert->throws(function()
{
$this->assert->isFalse(Auth::isLoggedIn());
return $this->runApi('alohaaaaaaa');
$this->runApi('alohaaaaaaa');
}, 'Not logged in');
}
@ -73,44 +78,42 @@ class EditCommentJobTest extends AbstractTest
{
$this->prepare();
getConfig()->privileges->{'editComment.own'} = 'nobody';
Access::init();
$this->assert->isFalse(Access::check(new Privilege(Privilege::EditComment)));
$this->assert->throws(function()
{
return $this->runApi('alohaaaaaaa');
$this->runApi('alohaaaaaaa');
}, 'Insufficient privileges');
}
public function testOtherAccessGrant()
{
$this->prepare();
getConfig()->privileges->{'editComment.all'} = 'nobody';
Access::init();
$this->assert->isTrue(Access::check(new Privilege(Privilege::EditComment)));
$this->assert->doesNotThrow(function()
{
return $this->runApi('alohaaaaaaa');
});
}
public function testOtherAccessDenial()
{
$this->prepare();
$this->grantAccess('editComment.all');
$comment = $this->mockComment(Auth::getCurrentUser());
//login as someone else
$this->login($this->mockUser());
getConfig()->privileges->{'editComment.all'} = 'nobody';
Access::init();
$this->assert->isTrue(Access::check(new Privilege(Privilege::EditComment)));
$this->assert->isTrue(Access::check(new Privilege(Privilege::EditComment, 'own')));
$this->assert->isFalse(Access::check(new Privilege(Privilege::EditComment, 'all')));
$this->assert->doesNotThrow(function() use ($comment)
{
Api::run(
new EditCommentJob(),
[
EditCommentJob::COMMENT_ID => $comment->getId(),
EditCommentJob::TEXT => 'alohaa',
]);
});
}
public function testOtherAccessDenial()
{
$this->prepare();
$this->grantAccess('editComment.own');
$comment = $this->mockComment(Auth::getCurrentUser());
//login as someone else
$this->login($this->mockUser());
$this->assert->throws(function() use ($comment)
{
@ -128,7 +131,7 @@ class EditCommentJobTest extends AbstractTest
$this->prepare();
$this->assert->throws(function()
{
return Api::run(
Api::run(
new EditCommentJob(),
[
EditCommentJob::COMMENT_ID => 100,

View file

@ -3,6 +3,8 @@ class ListCommentJobTest extends AbstractTest
{
public function testNone()
{
$this->grantAccess('listComments');
$this->assert->areEqual(0, CommentModel::getCount());
$ret = $this->runApi(1);
@ -11,6 +13,9 @@ class ListCommentJobTest extends AbstractTest
public function testSingle()
{
$this->grantAccess('listComments');
$this->grantAccess('listPosts');
$this->assert->areEqual(0, CommentModel::getCount());
$this->mockComment($this->mockUser());
@ -32,6 +37,9 @@ class ListCommentJobTest extends AbstractTest
public function testPaging()
{
$this->grantAccess('listComments');
$this->grantAccess('listPosts');
getConfig()->comments->commentsPerPage = 2;
$this->assert->areEqual(0, CommentModel::getCount());
@ -49,17 +57,12 @@ class ListCommentJobTest extends AbstractTest
public function testAccessDenial()
{
getConfig()->privileges->listComments = 'nobody';
Access::init();
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListComments)));
$this->assert->throws(function()
{
$this->runApi(1);
}, 'Insufficient privileges');
}
protected function runApi($page)
{
return Api::run(

View file

@ -62,20 +62,16 @@ class PreviewCommentJobTest extends AbstractTest
$this->prepare();
Auth::setCurrentUser(null);
$this->assert->throws(function()
$this->assert->doesNotThrow(function()
{
$this->assert->isFalse(Auth::isLoggedIn());
return $this->runApi('alohaaaaaaa');
}, 'Insufficient privileges');
});
}
public function testAccessDenial()
{
$this->prepare();
getConfig()->privileges->addComment = 'nobody';
Access::init();
$this->assert->isFalse(Access::check(new Privilege(Privilege::AddComment)));
$this->revokeAccess('addComment');
$this->assert->throws(function()
{
@ -98,6 +94,8 @@ class PreviewCommentJobTest extends AbstractTest
protected function prepare()
{
getConfig()->registration->needEmailForCommenting = false;
$this->grantAccess('addComment');
$this->login($this->mockUser());
}
}

View file

@ -0,0 +1,124 @@
<?php
class AccessTest extends AbstractTest
{
public function testDefaultPrivilege()
{
//by default, all privileges are set to false
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts)));
}
public function testAccessRanks1()
{
$user = $this->mockUser();
$user->setAccessRank(new AccessRank(AccessRank::Admin));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Moderator));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::PowerUser));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Registered));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Nobody));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
}
public function testAccessRanks2()
{
getConfig()->privileges->listPosts = 'power-user';
Access::init();
$user = $this->mockUser();
$user->setAccessRank(new AccessRank(AccessRank::Admin));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Moderator));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::PowerUser));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Registered));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Nobody));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
}
public function testSubPrivileges1()
{
getConfig()->privileges->{'listPosts.dummy'} = 'power-user';
Access::init();
$user = $this->mockUser();
$user->setAccessRank(new AccessRank(AccessRank::PowerUser));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'dummy'), $user));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'baka'), $user));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
}
public function testSubPrivileges2a()
{
getConfig()->privileges->{'listPosts.dummy'} = 'power-user';
getConfig()->privileges->{'listPosts'} = 'admin';
Access::init();
$this->testSubPrivileges2();
}
public function testSubPrivileges2b()
{
getConfig()->privileges->{'listPosts'} = 'admin';
getConfig()->privileges->{'listPosts.dummy'} = 'power-user';
Access::init();
$this->testSubPrivileges2();
}
protected function testSubPrivileges2()
{
$user = $this->mockUser();
$user->setAccessRank(new AccessRank(AccessRank::PowerUser));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'dummy'), $user));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts, 'baka'), $user));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Admin));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'dummy'), $user));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'baka'), $user));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts), $user));
}
public function testSubPrivileges3a()
{
getConfig()->privileges->{'listPosts.dummy'} = 'power-user';
getConfig()->privileges->{'listPosts.baka'} = 'admin';
getConfig()->privileges->{'listPosts'} = 'nobody';
Access::init();
$this->testSubPrivileges3();
}
public function testSubPrivileges3b()
{
getConfig()->privileges->{'listPosts'} = 'nobody';
getConfig()->privileges->{'listPosts.dummy'} = 'power-user';
getConfig()->privileges->{'listPosts.baka'} = 'admin';
Access::init();
$this->testSubPrivileges3();
}
protected function testSubPrivileges3()
{
$user = $this->mockUser();
$user->setAccessRank(new AccessRank(AccessRank::PowerUser));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'dummy'), $user));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts, 'baka'), $user));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
$user->setAccessRank(new AccessRank(AccessRank::Admin));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'dummy'), $user));
$this->assert->isTrue(Access::check(new Privilege(Privilege::ListPosts, 'baka'), $user));
$this->assert->isFalse(Access::check(new Privilege(Privilege::ListPosts), $user));
}
}

View file

@ -52,9 +52,6 @@ userNameMinLength = 3
userNameMaxLength = 20
userNameRegex = "/^[\w_-]+$/ui"
needEmailForRegistering = 0
needEmailForCommenting = 0
needEmailForUploading = 0
confirmationEmailEnabled = 1
confirmationEmailSenderName = "{host} mailing system"
confirmationEmailSenderEmail = "noreply@{host}"
@ -66,65 +63,3 @@ passwordResetEmailSubject = "{host} - password reset"
passwordResetEmailBody = "Hello,{nl}{nl}You received this e-mail because someone requested a password reset for user with this e-mail address at {host}. If it's you, visit {link} to finish password reset process, otherwise you may ignore and delete this e-mail.{nl}{nl}Kind regards,{nl}{host} mailing system"
[privileges]
registerAccount=anonymous
;registerAccount=nobody
uploadPost=registered
listPosts=anonymous
listPosts.sketchy=registered
listPosts.unsafe=registered
listPosts.hidden=moderator
viewPost=anonymous
viewPost.sketchy=registered
viewPost.unsafe=registered
viewPost.hidden=moderator
retrievePost=anonymous
favoritePost=registered
editPostSafety.own=registered
editPostSafety.all=moderator
editPostTags=registered
editPostThumb=moderator
editPostSource=moderator
editPostRelations.own=registered
editPostRelations.all=moderator
editPostFile=moderator
massTag.own=registered
massTag.all=power-user
hidePost=moderator
deletePost=moderator
featurePost=moderator
scorePost=registered
flagPost=registered
listUsers=registered
viewUser=registered
viewUserEmail.all=admin
viewUserEmail.own=registered
changeUserPassword.own=registered
changeUserPassword.all=admin
changeUserEmail.own=registered
changeUserEmail.all=admin
changeUserAccessRank=admin
changeUserName=moderator
changeUserSettings.all=nobody
changeUserSettings.own=registered
acceptUserRegistration=moderator
banUser.own=nobody
banUser.all=admin
deleteUser.own=registered
deleteUser.all=nobody
flagUser=registered
listComments=anonymous
addComment=registered
deleteComment.own=registered
deleteComment.all=moderator
editComment.own=registered
editComment.all=admin
listTags=anonymous
mergeTags=moderator
renameTags=moderator
listLogs=moderator
viewLog=moderator

View file

@ -66,7 +66,7 @@ function getTestMethods($filter)
$reflectionClass = new ReflectionClass($class);
foreach ($reflectionClass->getMethods() as $method)
{
if (preg_match('/test/i', $method->name))
if (preg_match('/test/i', $method->name) and $method->isPublic())
{
$testMethods []= $method;
}