J. King
6 years ago
23 changed files with 54 additions and 1505 deletions
@ -1,338 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\TestCase\User; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\Conf; |
|||
use JKingWeb\Arsse\User; |
|||
use JKingWeb\Arsse\User\Driver; |
|||
use Phake; |
|||
|
|||
/** @covers \JKingWeb\Arsse\User */ |
|||
class TestAuthorization extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
const USERS = [ |
|||
'user@example.com' => Driver::RIGHTS_NONE, |
|||
'user@example.org' => Driver::RIGHTS_NONE, |
|||
'dman@example.com' => Driver::RIGHTS_DOMAIN_MANAGER, |
|||
'dman@example.org' => Driver::RIGHTS_DOMAIN_MANAGER, |
|||
'dadm@example.com' => Driver::RIGHTS_DOMAIN_ADMIN, |
|||
'dadm@example.org' => Driver::RIGHTS_DOMAIN_ADMIN, |
|||
'gman@example.com' => Driver::RIGHTS_GLOBAL_MANAGER, |
|||
'gman@example.org' => Driver::RIGHTS_GLOBAL_MANAGER, |
|||
'gadm@example.com' => Driver::RIGHTS_GLOBAL_ADMIN, |
|||
'gadm@example.org' => Driver::RIGHTS_GLOBAL_ADMIN, |
|||
// invalid rights levels |
|||
'bad1@example.com' => Driver::RIGHTS_NONE+1, |
|||
'bad1@example.org' => Driver::RIGHTS_NONE+1, |
|||
'bad2@example.com' => Driver::RIGHTS_DOMAIN_MANAGER+1, |
|||
'bad2@example.org' => Driver::RIGHTS_DOMAIN_MANAGER+1, |
|||
'bad3@example.com' => Driver::RIGHTS_DOMAIN_ADMIN+1, |
|||
'bad3@example.org' => Driver::RIGHTS_DOMAIN_ADMIN+1, |
|||
'bad4@example.com' => Driver::RIGHTS_GLOBAL_MANAGER+1, |
|||
'bad4@example.org' => Driver::RIGHTS_GLOBAL_MANAGER+1, |
|||
'bad5@example.com' => Driver::RIGHTS_GLOBAL_ADMIN+1, |
|||
'bad5@example.org' => Driver::RIGHTS_GLOBAL_ADMIN+1, |
|||
|
|||
]; |
|||
const LEVELS = [ |
|||
Driver::RIGHTS_NONE, |
|||
Driver::RIGHTS_DOMAIN_MANAGER, |
|||
Driver::RIGHTS_DOMAIN_ADMIN, |
|||
Driver::RIGHTS_GLOBAL_MANAGER, |
|||
Driver::RIGHTS_GLOBAL_ADMIN, |
|||
]; |
|||
const DOMAINS = [ |
|||
'@example.com', |
|||
'@example.org', |
|||
"", |
|||
]; |
|||
|
|||
protected $data; |
|||
|
|||
public function setUp(string $drv = \JkingWeb\Arsse\Test\User\DriverInternalMock::class, string $db = null) { |
|||
$this->clearData(); |
|||
$conf = new Conf(); |
|||
$conf->userDriver = $drv; |
|||
$conf->userPreAuth = false; |
|||
Arsse::$conf = $conf; |
|||
if ($db !== null) { |
|||
Arsse::$db = new $db(); |
|||
} |
|||
Arsse::$user = Phake::partialMock(User::class); |
|||
Phake::when(Arsse::$user)->authorize->thenReturn(true); |
|||
foreach (self::USERS as $user => $level) { |
|||
Arsse::$user->add($user, ""); |
|||
Arsse::$user->rightsSet($user, $level); |
|||
} |
|||
Phake::reset(Arsse::$user); |
|||
} |
|||
|
|||
public function tearDown() { |
|||
$this->clearData(); |
|||
} |
|||
|
|||
public function testToggleLogic() { |
|||
$this->assertTrue(Arsse::$user->authorizationEnabled()); |
|||
$this->assertTrue(Arsse::$user->authorizationEnabled(true)); |
|||
$this->assertFalse(Arsse::$user->authorizationEnabled(false)); |
|||
$this->assertFalse(Arsse::$user->authorizationEnabled(false)); |
|||
$this->assertFalse(Arsse::$user->authorizationEnabled(true)); |
|||
$this->assertTrue(Arsse::$user->authorizationEnabled(true)); |
|||
} |
|||
|
|||
public function testSelfActionLogic() { |
|||
foreach (array_keys(self::USERS) as $user) { |
|||
Arsse::$user->auth($user, ""); |
|||
// users should be able to do basic actions for themselves |
|||
$this->assertTrue(Arsse::$user->authorize($user, "userExists"), "User $user could not act for themselves."); |
|||
$this->assertTrue(Arsse::$user->authorize($user, "userRemove"), "User $user could not act for themselves."); |
|||
} |
|||
} |
|||
|
|||
public function testRegularUserLogic() { |
|||
foreach (self::USERS as $actor => $rights) { |
|||
if ($rights != Driver::RIGHTS_NONE) { |
|||
continue; |
|||
} |
|||
Arsse::$user->auth($actor, ""); |
|||
foreach (array_keys(self::USERS) as $affected) { |
|||
// regular users should only be able to act for themselves |
|||
if ($actor==$affected) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// they should never be able to set rights |
|||
foreach (self::LEVELS as $level) { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); |
|||
} |
|||
} |
|||
// they should not be able to list users |
|||
foreach (self::DOMAINS as $domain) { |
|||
$this->assertFalse(Arsse::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function testDomainManagerLogic() { |
|||
foreach (self::USERS as $actor => $actorRights) { |
|||
if ($actorRights != Driver::RIGHTS_DOMAIN_MANAGER) { |
|||
continue; |
|||
} |
|||
$actorDomain = substr($actor, strrpos($actor, "@")+1); |
|||
Arsse::$user->auth($actor, ""); |
|||
foreach (self::USERS as $affected => $affectedRights) { |
|||
$affectedDomain = substr($affected, strrpos($affected, "@")+1); |
|||
// domain managers should be able to check any user on the same domain |
|||
if ($actorDomain==$affectedDomain) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// they should only be able to act for regular users on the same domain |
|||
if ($actor==$affected || ($actorDomain==$affectedDomain && $affectedRights==User\Driver::RIGHTS_NONE)) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// and they should only be able to set their own rights to regular user |
|||
foreach (self::LEVELS as $level) { |
|||
if ($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, Driver::RIGHTS_DOMAIN_MANAGER])) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
// they should also be able to list all users on their own domain |
|||
foreach (self::DOMAINS as $domain) { |
|||
if ($domain=="@".$actorDomain) { |
|||
$this->assertTrue(Arsse::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function testDomainAdministratorLogic() { |
|||
foreach (self::USERS as $actor => $actorRights) { |
|||
if ($actorRights != Driver::RIGHTS_DOMAIN_ADMIN) { |
|||
continue; |
|||
} |
|||
$actorDomain = substr($actor, strrpos($actor, "@")+1); |
|||
Arsse::$user->auth($actor, ""); |
|||
$allowed = [User\Driver::RIGHTS_NONE,User\Driver::RIGHTS_DOMAIN_MANAGER,User\Driver::RIGHTS_DOMAIN_ADMIN]; |
|||
foreach (self::USERS as $affected => $affectedRights) { |
|||
$affectedDomain = substr($affected, strrpos($affected, "@")+1); |
|||
// domain admins should be able to check any user on the same domain |
|||
if ($actorDomain==$affectedDomain) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// they should be able to act for any user on the same domain who is not a global manager or admin |
|||
if ($actorDomain==$affectedDomain && in_array($affectedRights, $allowed)) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// they should be able to set rights for any user on their domain who is not a global manager or admin, up to domain admin level |
|||
foreach (self::LEVELS as $level) { |
|||
if ($actorDomain==$affectedDomain && in_array($affectedRights, $allowed) && in_array($level, $allowed)) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
// they should also be able to list all users on their own domain |
|||
foreach (self::DOMAINS as $domain) { |
|||
if ($domain=="@".$actorDomain) { |
|||
$this->assertTrue(Arsse::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function testGlobalManagerLogic() { |
|||
foreach (self::USERS as $actor => $actorRights) { |
|||
if ($actorRights != Driver::RIGHTS_GLOBAL_MANAGER) { |
|||
continue; |
|||
} |
|||
$actorDomain = substr($actor, strrpos($actor, "@")+1); |
|||
Arsse::$user->auth($actor, ""); |
|||
foreach (self::USERS as $affected => $affectedRights) { |
|||
$affectedDomain = substr($affected, strrpos($affected, "@")+1); |
|||
// global managers should be able to check any user |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); |
|||
// they should only be able to act for regular users |
|||
if ($actor==$affected || $affectedRights==User\Driver::RIGHTS_NONE) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// and they should only be able to set their own rights to regular user |
|||
foreach (self::LEVELS as $level) { |
|||
if ($actor==$affected && in_array($level, [User\Driver::RIGHTS_NONE, Driver::RIGHTS_GLOBAL_MANAGER])) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
// they should also be able to list all users |
|||
foreach (self::DOMAINS as $domain) { |
|||
$this->assertTrue(Arsse::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function testGlobalAdministratorLogic() { |
|||
foreach (self::USERS as $actor => $actorRights) { |
|||
if ($actorRights != Driver::RIGHTS_GLOBAL_ADMIN) { |
|||
continue; |
|||
} |
|||
Arsse::$user->auth($actor, ""); |
|||
// global admins can do anything |
|||
foreach (self::USERS as $affected => $affectedRights) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); |
|||
foreach (self::LEVELS as $level) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied."); |
|||
} |
|||
} |
|||
foreach (self::DOMAINS as $domain) { |
|||
$this->assertTrue(Arsse::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function testInvalidLevelLogic() { |
|||
foreach (self::USERS as $actor => $rights) { |
|||
if (in_array($rights, self::LEVELS)) { |
|||
continue; |
|||
} |
|||
Arsse::$user->auth($actor, ""); |
|||
foreach (array_keys(self::USERS) as $affected) { |
|||
// users with unknown/invalid rights should be treated just like regular users and only be able to act for themselves |
|||
if ($actor==$affected) { |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied."); |
|||
$this->assertTrue(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied."); |
|||
} else { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed."); |
|||
} |
|||
// they should never be able to set rights |
|||
foreach (self::LEVELS as $level) { |
|||
$this->assertFalse(Arsse::$user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed."); |
|||
} |
|||
} |
|||
// they should not be able to list users |
|||
foreach (self::DOMAINS as $domain) { |
|||
$this->assertFalse(Arsse::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed."); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function testInternalExceptionLogic() { |
|||
$tests = [ |
|||
// methods of User class to test, with parameters besides affected user |
|||
'exists' => [], |
|||
'remove' => [], |
|||
'add' => [''], |
|||
'passwordSet' => [''], |
|||
'propertiesGet' => [], |
|||
'propertiesSet' => [[]], |
|||
'rightsGet' => [], |
|||
'rightsSet' => [User\Driver::RIGHTS_GLOBAL_ADMIN], |
|||
'list' => [], |
|||
]; |
|||
// try first with a global admin (there should be no exception) |
|||
Arsse::$user->auth("gadm@example.com", ""); |
|||
$this->assertCount(0, $this->checkExceptions("user@example.org", $tests)); |
|||
// next try with a regular user acting on another user (everything should fail) |
|||
Arsse::$user->auth("user@example.com", ""); |
|||
$this->assertCount(sizeof($tests), $this->checkExceptions("user@example.org", $tests)); |
|||
} |
|||
|
|||
public function testExternalExceptionLogic() { |
|||
// set up the test for an external driver |
|||
$this->setUp(\JKingWeb\Arsse\Test\User\DriverExternalMock::class, \JKingWeb\Arsse\Test\User\Database::class); |
|||
// run the previous test with the external driver set up |
|||
$this->testInternalExceptionLogic(); |
|||
} |
|||
|
|||
// meat of testInternalExceptionLogic and testExternalExceptionLogic |
|||
// calls each requested function with supplied arguments, catches authorization exceptions, and returns an array of caught failed calls |
|||
protected function checkExceptions(string $user, $tests): array { |
|||
$err = []; |
|||
foreach ($tests as $func => $args) { |
|||
// list method does not take an affected user, so do not unshift for that one |
|||
if ($func != "list") { |
|||
array_unshift($args, $user); |
|||
} |
|||
try { |
|||
call_user_func_array(array(Arsse::$user, $func), $args); |
|||
} catch (\JKingWeb\Arsse\User\ExceptionAuthz $e) { |
|||
$err[] = $func; |
|||
} |
|||
} |
|||
return $err; |
|||
} |
|||
|
|||
public function testMissingUserLogic() { |
|||
Arsse::$user->auth("gadm@example.com", ""); |
|||
$this->assertTrue(Arsse::$user->authorize("user@example.com", "someFunction")); |
|||
$this->assertException("doesNotExist", "User"); |
|||
Arsse::$user->authorize("this_user_does_not_exist@example.org", "someFunction"); |
|||
} |
|||
} |
@ -1,17 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\TestCase\User; |
|||
|
|||
/** @covers \JKingWeb\Arsse\User */ |
|||
class TestMockExternal extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\User\CommonTests; |
|||
|
|||
const USER1 = "john.doe@example.com"; |
|||
const USER2 = "jane.doe@example.com"; |
|||
|
|||
public $drv = \JKingWeb\Arsse\Test\User\DriverExternalMock::class; |
|||
} |
@ -1,23 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\TestCase\User; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
|
|||
/** @covers \JKingWeb\Arsse\User */ |
|||
class TestMockInternal extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\User\CommonTests; |
|||
|
|||
const USER1 = "john.doe@example.com"; |
|||
const USER2 = "jane.doe@example.com"; |
|||
|
|||
public $drv = \JKingWeb\Arsse\Test\User\DriverInternalMock::class; |
|||
|
|||
public function setUpSeries() { |
|||
Arsse::$db = null; |
|||
} |
|||
} |
@ -1,20 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\TestCase\User; |
|||
|
|||
/** |
|||
* @covers \JKingWeb\Arsse\User |
|||
* @covers \JKingWeb\Arsse\User\Internal\Driver |
|||
* @covers \JKingWeb\Arsse\User\Internal\InternalFunctions */ |
|||
class TestInternal extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
use \JKingWeb\Arsse\Test\User\CommonTests; |
|||
|
|||
const USER1 = "john.doe@example.com"; |
|||
const USER2 = "jane.doe@example.com"; |
|||
|
|||
public $drv = \JKingWeb\Arsse\User\Internal\Driver::class; |
|||
} |
@ -1,154 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\Test\User; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\Conf; |
|||
use JKingWeb\Arsse\User; |
|||
use JKingWeb\Arsse\User\Driver; |
|||
use Phake; |
|||
|
|||
trait CommonTests { |
|||
public function setUp() { |
|||
$this->clearData(); |
|||
$conf = new Conf(); |
|||
$conf->userDriver = $this->drv; |
|||
$conf->userPreAuth = false; |
|||
Arsse::$conf = $conf; |
|||
Arsse::$db = new Database(); |
|||
Arsse::$user = Phake::partialMock(User::class); |
|||
Phake::when(Arsse::$user)->authorize->thenReturn(true); |
|||
$_SERVER['PHP_AUTH_USER'] = self::USER1; |
|||
$_SERVER['PHP_AUTH_PW'] = "secret"; |
|||
// call the additional setup method if it exists |
|||
if (method_exists($this, "setUpSeries")) { |
|||
$this->setUpSeries(); |
|||
} |
|||
} |
|||
|
|||
public function tearDown() { |
|||
$this->clearData(); |
|||
// call the additional teardiwn method if it exists |
|||
if (method_exists($this, "tearDownSeries")) { |
|||
$this->tearDownSeries(); |
|||
} |
|||
} |
|||
|
|||
public function testListUsers() { |
|||
$this->assertCount(0, Arsse::$user->list()); |
|||
} |
|||
|
|||
public function testCheckIfAUserDoesNotExist() { |
|||
$this->assertFalse(Arsse::$user->exists(self::USER1)); |
|||
} |
|||
|
|||
public function testAddAUser() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
$this->assertCount(1, Arsse::$user->list()); |
|||
} |
|||
|
|||
public function testCheckIfAUserDoesExist() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
$this->assertTrue(Arsse::$user->exists(self::USER1)); |
|||
} |
|||
|
|||
public function testAddADuplicateUser() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
$this->assertException("alreadyExists", "User"); |
|||
Arsse::$user->add(self::USER1, ""); |
|||
} |
|||
|
|||
public function testAddMultipleUsers() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
Arsse::$user->add(self::USER2, ""); |
|||
$this->assertCount(2, Arsse::$user->list()); |
|||
} |
|||
|
|||
public function testRemoveAUser() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
$this->assertCount(1, Arsse::$user->list()); |
|||
Arsse::$user->remove(self::USER1); |
|||
$this->assertCount(0, Arsse::$user->list()); |
|||
} |
|||
|
|||
public function testRemoveAMissingUser() { |
|||
$this->assertException("doesNotExist", "User"); |
|||
Arsse::$user->remove(self::USER1); |
|||
} |
|||
|
|||
/** @group slow */ |
|||
public function testAuthenticateAUser() { |
|||
$_SERVER['PHP_AUTH_USER'] = self::USER1; |
|||
$_SERVER['PHP_AUTH_PW'] = "secret"; |
|||
Arsse::$user->add(self::USER1, "secret"); |
|||
Arsse::$user->add(self::USER2, ""); |
|||
$this->assertTrue(Arsse::$user->auth()); |
|||
$this->assertTrue(Arsse::$user->auth(self::USER1, "secret")); |
|||
$this->assertFalse(Arsse::$user->auth(self::USER1, "superman")); |
|||
$this->assertTrue(Arsse::$user->auth(self::USER2, "")); |
|||
} |
|||
|
|||
/** @group slow */ |
|||
public function testChangeAPassword() { |
|||
Arsse::$user->add(self::USER1, "secret"); |
|||
$this->assertEquals("superman", Arsse::$user->passwordSet(self::USER1, "superman")); |
|||
$this->assertTrue(Arsse::$user->auth(self::USER1, "superman")); |
|||
$this->assertFalse(Arsse::$user->auth(self::USER1, "secret")); |
|||
$this->assertEquals("", Arsse::$user->passwordSet(self::USER1, "")); |
|||
$this->assertTrue(Arsse::$user->auth(self::USER1, "")); |
|||
$this->assertEquals(Arsse::$conf->userTempPasswordLength, strlen(Arsse::$user->passwordSet(self::USER1))); |
|||
} |
|||
|
|||
public function testChangeAPasswordForAMissingUser() { |
|||
$this->assertException("doesNotExist", "User"); |
|||
Arsse::$user->passwordSet(self::USER1, "superman"); |
|||
} |
|||
|
|||
public function testGetThePropertiesOfAUser() { |
|||
Arsse::$user->add(self::USER1, "secret"); |
|||
$p = Arsse::$user->propertiesGet(self::USER1); |
|||
$this->assertArrayHasKey('id', $p); |
|||
$this->assertArrayHasKey('name', $p); |
|||
$this->assertArrayHasKey('domain', $p); |
|||
$this->assertArrayHasKey('rights', $p); |
|||
$this->assertArrayNotHasKey('password', $p); |
|||
$this->assertEquals(self::USER1, $p['name']); |
|||
} |
|||
|
|||
public function testSetThePropertiesOfAUser() { |
|||
$pSet = [ |
|||
'name' => 'John Doe', |
|||
'id' => 'invalid', |
|||
'domain' => 'localhost', |
|||
'rights' => Driver::RIGHTS_GLOBAL_ADMIN, |
|||
'password' => 'superman', |
|||
]; |
|||
$pGet = [ |
|||
'name' => 'John Doe', |
|||
'id' => self::USER1, |
|||
'domain' => 'example.com', |
|||
'rights' => Driver::RIGHTS_NONE, |
|||
]; |
|||
Arsse::$user->add(self::USER1, "secret"); |
|||
Arsse::$user->propertiesSet(self::USER1, $pSet); |
|||
$p = Arsse::$user->propertiesGet(self::USER1); |
|||
$this->assertArraySubset($pGet, $p); |
|||
$this->assertArrayNotHasKey('password', $p); |
|||
$this->assertFalse(Arsse::$user->auth(self::USER1, "superman")); |
|||
} |
|||
|
|||
public function testGetTheRightsOfAUser() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
$this->assertEquals(Driver::RIGHTS_NONE, Arsse::$user->rightsGet(self::USER1)); |
|||
} |
|||
|
|||
public function testSetTheRightsOfAUser() { |
|||
Arsse::$user->add(self::USER1, ""); |
|||
Arsse::$user->rightsSet(self::USER1, Driver::RIGHTS_GLOBAL_ADMIN); |
|||
$this->assertEquals(Driver::RIGHTS_GLOBAL_ADMIN, Arsse::$user->rightsGet(self::USER1)); |
|||
} |
|||
} |
@ -1,133 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\Test\User; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\User\Driver; |
|||
use JKingWeb\Arsse\User\Exception; |
|||
use JKingWeb\Arsse\User\ExceptionAuthz; |
|||
use PasswordGenerator\Generator as PassGen; |
|||
|
|||
class Database extends DriverSkeleton { |
|||
public $db = []; |
|||
|
|||
public function __construct() { |
|||
} |
|||
|
|||
public function userExists(string $user): bool { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userExists($user); |
|||
} |
|||
|
|||
public function userAdd(string $user, string $password = null): string { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if ($this->userExists($user)) { |
|||
throw new Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if ($password===null) { |
|||
$password = (new PassGen)->length(Arsse::$conf->userTempPasswordLength)->get(); |
|||
} |
|||
return parent::userAdd($user, $password); |
|||
} |
|||
|
|||
public function userRemove(string $user): bool { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userRemove($user); |
|||
} |
|||
|
|||
public function userList(string $domain = null): array { |
|||
if ($domain===null) { |
|||
if (!Arsse::$user->authorize("", __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); |
|||
} |
|||
return parent::userList(); |
|||
} else { |
|||
$suffix = '@'.$domain; |
|||
if (!Arsse::$user->authorize($suffix, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); |
|||
} |
|||
return parent::userList($domain); |
|||
} |
|||
} |
|||
|
|||
public function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if ($newPassword===null) { |
|||
$newPassword = (new PassGen)->length(Arsse::$conf->userTempPasswordLength)->get(); |
|||
} |
|||
return parent::userPasswordSet($user, $newPassword); |
|||
} |
|||
|
|||
public function userPropertiesGet(string $user): array { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
$out = parent::userPropertiesGet($user); |
|||
unset($out['password']); |
|||
return $out; |
|||
} |
|||
|
|||
public function userPropertiesSet(string $user, array $properties): array { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
parent::userPropertiesSet($user, $properties); |
|||
return $this->userPropertiesGet($user); |
|||
} |
|||
|
|||
public function userRightsGet(string $user): int { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userRightsGet($user); |
|||
} |
|||
|
|||
public function userRightsSet(string $user, int $level): bool { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userRightsSet($user, $level); |
|||
} |
|||
|
|||
// specific to mock database |
|||
|
|||
public function userPasswordGet(string $user): string { |
|||
if (!Arsse::$user->authorize($user, __FUNCTION__)) { |
|||
throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return $this->db[$user]['password']; |
|||
} |
|||
} |
@ -1,127 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\Test\User; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\User\Driver; |
|||
use JKingWeb\Arsse\User\Exception; |
|||
use PasswordGenerator\Generator as PassGen; |
|||
|
|||
class DriverExternalMock extends DriverSkeleton implements Driver { |
|||
public $db = []; |
|||
protected $functions = [ |
|||
"auth" => Driver::FUNC_EXTERNAL, |
|||
"userList" => Driver::FUNC_EXTERNAL, |
|||
"userExists" => Driver::FUNC_EXTERNAL, |
|||
"userAdd" => Driver::FUNC_EXTERNAL, |
|||
"userRemove" => Driver::FUNC_EXTERNAL, |
|||
"userPasswordSet" => Driver::FUNC_EXTERNAL, |
|||
"userPropertiesGet" => Driver::FUNC_EXTERNAL, |
|||
"userPropertiesSet" => Driver::FUNC_EXTERNAL, |
|||
"userRightsGet" => Driver::FUNC_EXTERNAL, |
|||
"userRightsSet" => Driver::FUNC_EXTERNAL, |
|||
]; |
|||
|
|||
public static function driverName(): string { |
|||
return "Mock External Driver"; |
|||
} |
|||
|
|||
public function driverFunctions(string $function = null) { |
|||
if ($function===null) { |
|||
return $this->functions; |
|||
} |
|||
if (array_key_exists($function, $this->functions)) { |
|||
return $this->functions[$function]; |
|||
} else { |
|||
return Driver::FUNC_NOT_IMPLEMENTED; |
|||
} |
|||
} |
|||
|
|||
public function __construct() { |
|||
} |
|||
|
|||
public function auth(string $user, string $password): bool { |
|||
if (!$this->userExists($user)) { |
|||
return false; |
|||
} |
|||
if ($password==="" && $this->db[$user]['password']==="") { |
|||
return true; |
|||
} |
|||
if (password_verify($password, $this->db[$user]['password'])) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
public function userExists(string $user): bool { |
|||
return parent::userExists($user); |
|||
} |
|||
|
|||
public function userAdd(string $user, string $password = null): string { |
|||
if ($this->userExists($user)) { |
|||
throw new Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if ($password===null) { |
|||
$password = (new PassGen)->length(Arsse::$conf->userTempPasswordLength)->get(); |
|||
} |
|||
return parent::userAdd($user, $password); |
|||
} |
|||
|
|||
public function userRemove(string $user): bool { |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userRemove($user); |
|||
} |
|||
|
|||
public function userList(string $domain = null): array { |
|||
if ($domain===null) { |
|||
return parent::userList(); |
|||
} else { |
|||
return parent::userList($domain); |
|||
} |
|||
} |
|||
|
|||
public function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
if ($newPassword===null) { |
|||
$newPassword = (new PassGen)->length(Arsse::$conf->userTempPasswordLength)->get(); |
|||
} |
|||
return parent::userPasswordSet($user, $newPassword); |
|||
} |
|||
|
|||
public function userPropertiesGet(string $user): array { |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userPropertiesGet($user); |
|||
} |
|||
|
|||
public function userPropertiesSet(string $user, array $properties): array { |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
parent::userPropertiesSet($user, $properties); |
|||
return $this->userPropertiesGet($user); |
|||
} |
|||
|
|||
public function userRightsGet(string $user): int { |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userRightsGet($user); |
|||
} |
|||
|
|||
public function userRightsSet(string $user, int $level): bool { |
|||
if (!$this->userExists($user)) { |
|||
throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); |
|||
} |
|||
return parent::userRightsSet($user, $level); |
|||
} |
|||
} |
@ -1,56 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\Test\User; |
|||
|
|||
use JKingWeb\Arsse\User\Driver; |
|||
|
|||
class DriverInternalMock extends Database implements Driver { |
|||
public $db = []; |
|||
protected $functions = [ |
|||
"auth" => Driver::FUNC_INTERNAL, |
|||
"userList" => Driver::FUNC_INTERNAL, |
|||
"userExists" => Driver::FUNC_INTERNAL, |
|||
"userAdd" => Driver::FUNC_INTERNAL, |
|||
"userRemove" => Driver::FUNC_INTERNAL, |
|||
"userPasswordSet" => Driver::FUNC_INTERNAL, |
|||
"userPropertiesGet" => Driver::FUNC_INTERNAL, |
|||
"userPropertiesSet" => Driver::FUNC_INTERNAL, |
|||
"userRightsGet" => Driver::FUNC_INTERNAL, |
|||
"userRightsSet" => Driver::FUNC_INTERNAL, |
|||
]; |
|||
|
|||
public static function driverName(): string { |
|||
return "Mock Internal Driver"; |
|||
} |
|||
|
|||
public function driverFunctions(string $function = null) { |
|||
if ($function===null) { |
|||
return $this->functions; |
|||
} |
|||
if (array_key_exists($function, $this->functions)) { |
|||
return $this->functions[$function]; |
|||
} else { |
|||
return Driver::FUNC_NOT_IMPLEMENTED; |
|||
} |
|||
} |
|||
|
|||
public function __construct() { |
|||
} |
|||
|
|||
public function auth(string $user, string $password): bool { |
|||
if (!$this->userExists($user)) { |
|||
return false; |
|||
} |
|||
if ($password==="" && $this->db[$user]['password']==="") { |
|||
return true; |
|||
} |
|||
if (password_verify($password, $this->db[$user]['password'])) { |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
} |
@ -1,72 +0,0 @@ |
|||
<?php |
|||
/** @license MIT |
|||
* Copyright 2017 J. King, Dustin Wilson et al. |
|||
* See LICENSE and AUTHORS files for details */ |
|||
|
|||
declare(strict_types=1); |
|||
namespace JKingWeb\Arsse\Test\User; |
|||
|
|||
use JKingWeb\Arsse\Lang; |
|||
use JKingWeb\Arsse\User\Driver; |
|||
use JKingWeb\Arsse\User\Exception; |
|||
use JKingWeb\Arsse\User\ExceptionAuthz; |
|||
use PasswordGenerator\Generator as PassGen; |
|||
|
|||
abstract class DriverSkeleton { |
|||
protected $db = []; |
|||
|
|||
public function userExists(string $user): bool { |
|||
return array_key_exists($user, $this->db); |
|||
} |
|||
|
|||
public function userAdd(string $user, string $password = null): string { |
|||
$u = [ |
|||
'password' => $password ? password_hash($password, \PASSWORD_DEFAULT) : "", |
|||
'rights' => Driver::RIGHTS_NONE, |
|||
]; |
|||
$this->db[$user] = $u; |
|||
return $password; |
|||
} |
|||
|
|||
public function userRemove(string $user): bool { |
|||
unset($this->db[$user]); |
|||
return true; |
|||
} |
|||
|
|||
public function userList(string $domain = null): array { |
|||
$list = array_keys($this->db); |
|||
if ($domain===null) { |
|||
return $list; |
|||
} else { |
|||
$suffix = '@'.$domain; |
|||
$len = -1 * strlen($suffix); |
|||
return array_filter($list, function ($user) use ($suffix, $len) { |
|||
return substr_compare($user, $suffix, $len); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { |
|||
$this->db[$user]['password'] = password_hash($newPassword, \PASSWORD_DEFAULT); |
|||
return $newPassword; |
|||
} |
|||
|
|||
public function userPropertiesGet(string $user): array { |
|||
$out = $this->db[$user]; |
|||
return $out; |
|||
} |
|||
|
|||
public function userPropertiesSet(string $user, array $properties): array { |
|||
$this->db[$user] = array_merge($this->db[$user], $properties); |
|||
return $this->userPropertiesGet($user); |
|||
} |
|||
|
|||
public function userRightsGet(string $user): int { |
|||
return $this->db[$user]['rights']; |
|||
} |
|||
|
|||
public function userRightsSet(string $user, int $level): bool { |
|||
$this->db[$user]['rights'] = $level; |
|||
return true; |
|||
} |
|||
} |
Loading…
Reference in new issue