J. King
6 years ago
29 changed files with 608 additions and 1764 deletions
@ -1,65 +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\User\Internal; |
|||
|
|||
use JKingWeb\Arsse\Arsse; |
|||
use JKingWeb\Arsse\User\Exception; |
|||
|
|||
trait InternalFunctions { |
|||
protected $actor = []; |
|||
|
|||
public function __construct() { |
|||
} |
|||
|
|||
public function auth(string $user, string $password): bool { |
|||
try { |
|||
$hash = Arsse::$db->userPasswordGet($user); |
|||
} catch (Exception $e) { |
|||
return false; |
|||
} |
|||
if ($password==="" && $hash==="") { |
|||
return true; |
|||
} |
|||
return password_verify($password, $hash); |
|||
} |
|||
|
|||
public function userExists(string $user): bool { |
|||
return Arsse::$db->userExists($user); |
|||
} |
|||
|
|||
public function userAdd(string $user, string $password = null): string { |
|||
return Arsse::$db->userAdd($user, $password); |
|||
} |
|||
|
|||
public function userRemove(string $user): bool { |
|||
return Arsse::$db->userRemove($user); |
|||
} |
|||
|
|||
public function userList(string $domain = null): array { |
|||
return Arsse::$db->userList($domain); |
|||
} |
|||
|
|||
public function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { |
|||
return Arsse::$db->userPasswordSet($user, $newPassword); |
|||
} |
|||
|
|||
public function userPropertiesGet(string $user): array { |
|||
return Arsse::$db->userPropertiesGet($user); |
|||
} |
|||
|
|||
public function userPropertiesSet(string $user, array $properties): array { |
|||
return Arsse::$db->userPropertiesSet($user, $properties); |
|||
} |
|||
|
|||
public function userRightsGet(string $user): int { |
|||
return Arsse::$db->userRightsGet($user); |
|||
} |
|||
|
|||
public function userRightsSet(string $user, int $level): bool { |
|||
return Arsse::$db->userRightsSet($user, $level); |
|||
} |
|||
} |
@ -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"); |
|||
} |
|||
} |
@ -0,0 +1,137 @@ |
|||
<?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\Database; |
|||
use JKingWeb\Arsse\User; |
|||
use JKingWeb\Arsse\AbstractException as Exception; |
|||
use JKingWeb\Arsse\User\Driver as DriverInterface; |
|||
use JKingWeb\Arsse\User\Internal\Driver; |
|||
use Phake; |
|||
|
|||
/** @covers \JKingWeb\Arsse\User\Internal\Driver */ |
|||
class TestInternal extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
|
|||
public function setUp() { |
|||
$this->clearData(); |
|||
$this->setConf(); |
|||
// create a mock database interface |
|||
Arsse::$db = Phake::mock(Database::class); |
|||
Phake::when(Arsse::$db)->begin->thenReturn(Phake::mock(\JKingWeb\Arsse\Db\Transaction::class)); |
|||
} |
|||
|
|||
public function testConstruct() { |
|||
$this->assertInstanceOf(DriverInterface::class, new Driver); |
|||
} |
|||
|
|||
public function testFetchDriverName() { |
|||
$this->assertTrue(strlen(Driver::driverName()) > 0); |
|||
} |
|||
|
|||
/** |
|||
* @dataProvider provideAuthentication |
|||
* @group slow |
|||
*/ |
|||
public function testAuthenticateAUser(bool $authorized, string $user, string $password, bool $exp) { |
|||
if ($authorized) { |
|||
Phake::when(Arsse::$db)->userPasswordGet("john.doe@example.com")->thenReturn('$2y$10$1zbqRJhxM8uUjeSBPp4IhO90xrqK0XjEh9Z16iIYEFRV4U.zeAFom'); // hash of "secret" |
|||
Phake::when(Arsse::$db)->userPasswordGet("jane.doe@example.com")->thenReturn('$2y$10$bK1ljXfTSyc2D.NYvT.Eq..OpehLRXVbglW.23ihVuyhgwJCd.7Im'); // hash of "superman" |
|||
Phake::when(Arsse::$db)->userPasswordGet("owen.hardy@example.com")->thenReturn(""); |
|||
Phake::when(Arsse::$db)->userPasswordGet("kira.nerys@example.com")->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist")); |
|||
} else { |
|||
Phake::when(Arsse::$db)->userPasswordGet->thenThrow(new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")); |
|||
} |
|||
$this->assertSame($exp, (new Driver)->auth($user, $password)); |
|||
} |
|||
|
|||
public function provideAuthentication() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
$owen = "owen.hardy@example.com"; |
|||
$kira = "kira.nerys@example.com"; |
|||
return [ |
|||
[false, $john, "secret", false], |
|||
[false, $jane, "superman", false], |
|||
[false, $owen, "", false], |
|||
[false, $kira, "ashalla", false], |
|||
[true, $john, "secret", true], |
|||
[true, $jane, "superman", true], |
|||
[true, $owen, "", true], |
|||
[true, $kira, "ashalla", false], |
|||
[true, $john, "top secret", false], |
|||
[true, $jane, "clark kent", false], |
|||
[true, $owen, "watchmaker", false], |
|||
[true, $kira, "singha", false], |
|||
[true, $john, "", false], |
|||
[true, $jane, "", false], |
|||
[true, $kira, "", false], |
|||
]; |
|||
} |
|||
|
|||
public function testAuthorizeAnAction() { |
|||
Phake::verifyNoFurtherInteraction(Arsse::$db); |
|||
$this->assertTrue((new Driver)->authorize("someone", "something")); |
|||
} |
|||
|
|||
public function testListUsers() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
Phake::when(Arsse::$db)->userList->thenReturn([$john, $jane])->thenReturn([$jane, $john]); |
|||
$driver = new Driver; |
|||
$this->assertSame([$john, $jane], $driver->userList()); |
|||
$this->assertSame([$jane, $john], $driver->userList()); |
|||
Phake::verify(Arsse::$db, Phake::times(2))->userList; |
|||
} |
|||
|
|||
public function testCheckThatAUserExists() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
Phake::when(Arsse::$db)->userExists($john)->thenReturn(true); |
|||
Phake::when(Arsse::$db)->userExists($jane)->thenReturn(false); |
|||
$driver = new Driver; |
|||
$this->assertTrue($driver->userExists($john)); |
|||
Phake::verify(Arsse::$db)->userExists($john); |
|||
$this->assertFalse($driver->userExists($jane)); |
|||
Phake::verify(Arsse::$db)->userExists($jane); |
|||
} |
|||
|
|||
public function testAddAUser() { |
|||
$john = "john.doe@example.com"; |
|||
Phake::when(Arsse::$db)->userAdd->thenReturnCallback(function($user, $pass) { |
|||
return $pass; |
|||
}); |
|||
$driver = new Driver; |
|||
$this->assertNull($driver->userAdd($john)); |
|||
$this->assertNull($driver->userAdd($john, null)); |
|||
$this->assertSame("secret", $driver->userAdd($john, "secret")); |
|||
Phake::verify(Arsse::$db)->userAdd($john, "secret"); |
|||
Phake::verify(Arsse::$db)->userAdd; |
|||
} |
|||
|
|||
public function testRemoveAUser() { |
|||
$john = "john.doe@example.com"; |
|||
Phake::when(Arsse::$db)->userRemove->thenReturn(true)->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist")); |
|||
$driver = new Driver; |
|||
$this->assertTrue($driver->userRemove($john)); |
|||
Phake::verify(Arsse::$db, Phake::times(1))->userRemove($john); |
|||
$this->assertException("doesNotExist", "User"); |
|||
try { |
|||
$this->assertFalse($driver->userRemove($john)); |
|||
} finally { |
|||
Phake::verify(Arsse::$db, Phake::times(2))->userRemove($john); |
|||
} |
|||
} |
|||
|
|||
public function testSetAPassword() { |
|||
$john = "john.doe@example.com"; |
|||
Phake::verifyNoFurtherInteraction(Arsse::$db); |
|||
$this->assertSame("superman", (new Driver)->userPasswordSet($john, "superman")); |
|||
$this->assertSame(null, (new Driver)->userPasswordSet($john, null)); |
|||
} |
|||
} |
@ -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; |
|||
} |
|||
} |
@ -0,0 +1,301 @@ |
|||
<?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\Database; |
|||
use JKingWeb\Arsse\User; |
|||
use JKingWeb\Arsse\AbstractException as Exception; |
|||
use JKingWeb\Arsse\User\Driver; |
|||
use JKingWeb\Arsse\User\Internal\Driver as InternalDriver; |
|||
use Phake; |
|||
|
|||
/** @covers \JKingWeb\Arsse\User */ |
|||
class TestUser extends \JKingWeb\Arsse\Test\AbstractTest { |
|||
|
|||
public function setUp() { |
|||
$this->clearData(); |
|||
$this->setConf(); |
|||
// create a mock database interface |
|||
Arsse::$db = Phake::mock(Database::class); |
|||
Phake::when(Arsse::$db)->begin->thenReturn(Phake::mock(\JKingWeb\Arsse\Db\Transaction::class)); |
|||
// create a mock user driver |
|||
$this->drv = Phake::mock(Driver::class); |
|||
} |
|||
|
|||
public function testListDrivers() { |
|||
$exp = [ |
|||
'JKingWeb\\Arsse\\User\\Internal\\Driver' => Arsse::$lang->msg("Driver.User.Internal.Name"), |
|||
]; |
|||
$this->assertArraySubset($exp, User::driverList()); |
|||
} |
|||
|
|||
public function testConstruct() { |
|||
$this->assertInstanceOf(User::class, new User($this->drv)); |
|||
$this->assertInstanceOf(User::class, new User); |
|||
} |
|||
|
|||
public function testConversionToString() { |
|||
$u = new User; |
|||
$u->id = "john.doe@example.com"; |
|||
$this->assertSame("john.doe@example.com", (string) $u); |
|||
$u->id = null; |
|||
$this->assertSame("", (string) $u); |
|||
} |
|||
|
|||
/** @dataProvider provideAuthentication */ |
|||
public function testAuthenticateAUser(bool $preAuth, string $user, string $password, bool $exp) { |
|||
Arsse::$conf->userPreAuth = $preAuth; |
|||
Phake::when($this->drv)->auth->thenReturn(false); |
|||
Phake::when($this->drv)->auth("john.doe@example.com", "secret")->thenReturn(true); |
|||
Phake::when($this->drv)->auth("jane.doe@example.com", "superman")->thenReturn(true); |
|||
Phake::when(Arsse::$db)->userExists("john.doe@example.com")->thenReturn(true); |
|||
Phake::when(Arsse::$db)->userExists("jane.doe@example.com")->thenReturn(false); |
|||
Phake::when(Arsse::$db)->userAdd->thenReturn(""); |
|||
$u = new User($this->drv); |
|||
$this->assertSame($exp, $u->auth($user, $password)); |
|||
$this->assertNull($u->id); |
|||
Phake::verify(Arsse::$db, Phake::times($exp ? 1 : 0))->userExists($user); |
|||
Phake::verify(Arsse::$db, Phake::times($exp && $user == "jane.doe@example.com" ? 1 : 0))->userAdd($user, $password); |
|||
} |
|||
|
|||
public function provideAuthentication() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
return [ |
|||
[false, $john, "secret", true], |
|||
[false, $john, "superman", false], |
|||
[false, $jane, "secret", false], |
|||
[false, $jane, "superman", true], |
|||
[true, $john, "secret", true], |
|||
[true, $john, "superman", true], |
|||
[true, $jane, "secret", true], |
|||
[true, $jane, "superman", true], |
|||
]; |
|||
} |
|||
|
|||
/** @dataProvider provideUserList */ |
|||
public function testListUsers(bool $authorized, $exp) { |
|||
$u = new User($this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userList->thenReturn(["john.doe@example.com", "jane.doe@example.com"]); |
|||
if ($exp instanceof Exception) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
} |
|||
$this->assertSame($exp, $u->list()); |
|||
} |
|||
|
|||
public function provideUserList() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
return [ |
|||
[false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[true, [$john, $jane]], |
|||
]; |
|||
} |
|||
|
|||
/** @dataProvider provideExistence */ |
|||
public function testCheckThatAUserExists(bool $authorized, string $user, $exp) { |
|||
$u = new User($this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userExists("john.doe@example.com")->thenReturn(true); |
|||
Phake::when($this->drv)->userExists("jane.doe@example.com")->thenReturn(false); |
|||
if ($exp instanceof Exception) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
} |
|||
$this->assertSame($exp, $u->exists($user)); |
|||
} |
|||
|
|||
public function provideExistence() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
return [ |
|||
[false, $john, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[false, $jane, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[true, $john, true], |
|||
[true, $jane, false], |
|||
]; |
|||
} |
|||
|
|||
/** @dataProvider provideAdditions */ |
|||
public function testAddAUser(bool $authorized, string $user, $password, $exp) { |
|||
$u = new User($this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userAdd("john.doe@example.com", $this->anything())->thenThrow(new \JKingWeb\Arsse\User\Exception("alreadyExists")); |
|||
Phake::when($this->drv)->userAdd("jane.doe@example.com", $this->anything())->thenReturnCallback(function($user, $pass) { |
|||
return $pass ?? "random password"; |
|||
}); |
|||
if ($exp instanceof Exception) { |
|||
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
} else { |
|||
$this->assertException("alreadyExists", "User"); |
|||
} |
|||
} |
|||
$this->assertSame($exp, $u->add($user, $password)); |
|||
} |
|||
|
|||
/** @dataProvider provideAdditions */ |
|||
public function testAddAUserWithARandomPassword(bool $authorized, string $user, $password, $exp) { |
|||
$u = Phake::partialMock(User::class, $this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userAdd($this->anything(), $this->isNull())->thenReturn(null); |
|||
Phake::when($this->drv)->userAdd("john.doe@example.com", $this->logicalNot($this->isNull()))->thenThrow(new \JKingWeb\Arsse\User\Exception("alreadyExists")); |
|||
Phake::when($this->drv)->userAdd("jane.doe@example.com", $this->logicalNot($this->isNull()))->thenReturnCallback(function($user, $pass) { |
|||
return $pass; |
|||
}); |
|||
if ($exp instanceof Exception) { |
|||
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
$calls = 0; |
|||
} else { |
|||
$this->assertException("alreadyExists", "User"); |
|||
$calls = 2; |
|||
} |
|||
} else { |
|||
$calls = 4; |
|||
} |
|||
try { |
|||
$pass1 = $u->add($user, null); |
|||
$pass2 = $u->add($user, null); |
|||
$this->assertNotEquals($pass1, $pass2); |
|||
} finally { |
|||
Phake::verify($this->drv, Phake::times($calls))->userAdd; |
|||
Phake::verify($u, Phake::times($calls / 2))->generatePassword; |
|||
} |
|||
} |
|||
|
|||
public function provideAdditions() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
return [ |
|||
[false, $john, "secret", new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[false, $jane, "superman", new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[true, $john, "secret", new \JKingWeb\Arsse\User\Exception("alreadyExists")], |
|||
[true, $jane, "superman", "superman"], |
|||
[true, $jane, null, "random password"], |
|||
]; |
|||
} |
|||
|
|||
/** @dataProvider provideRemovals */ |
|||
public function testRemoveAUser(bool $authorized, string $user, bool $exists, $exp) { |
|||
$u = new User($this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userRemove("john.doe@example.com")->thenReturn(true); |
|||
Phake::when($this->drv)->userRemove("jane.doe@example.com")->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist")); |
|||
Phake::when(Arsse::$db)->userExists->thenReturn($exists); |
|||
Phake::when(Arsse::$db)->userRemove->thenReturn(true); |
|||
if ($exp instanceof Exception) { |
|||
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
} else { |
|||
$this->assertException("doesNotExist", "User"); |
|||
} |
|||
} |
|||
try { |
|||
$this->assertSame($exp, $u->remove($user)); |
|||
} finally { |
|||
Phake::verify(Arsse::$db, Phake::times((int) $authorized))->userExists($user); |
|||
Phake::verify(Arsse::$db, Phake::times((int) ($authorized && $exists)))->userRemove($user); |
|||
} |
|||
} |
|||
|
|||
public function provideRemovals() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
return [ |
|||
[false, $john, true, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[false, $john, false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[false, $jane, true, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[false, $jane, false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[true, $john, true, true], |
|||
[true, $john, false, true], |
|||
[true, $jane, true, new \JKingWeb\Arsse\User\Exception("doesNotExist")], |
|||
[true, $jane, false, new \JKingWeb\Arsse\User\Exception("doesNotExist")], |
|||
]; |
|||
} |
|||
|
|||
/** @dataProvider providePasswordChanges */ |
|||
public function testChangeAPassword(bool $authorized, string $user, $password, bool $exists, $exp) { |
|||
$u = new User($this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userPasswordSet("john.doe@example.com", $this->anything(), $this->anything())->thenReturnCallback(function($user, $pass, $old) { |
|||
return $pass ?? "random password"; |
|||
}); |
|||
Phake::when($this->drv)->userPasswordSet("jane.doe@example.com", $this->anything(), $this->anything())->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist")); |
|||
Phake::when(Arsse::$db)->userExists->thenReturn($exists); |
|||
if ($exp instanceof Exception) { |
|||
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
} else { |
|||
$this->assertException("doesNotExist", "User"); |
|||
} |
|||
$calls = 0; |
|||
} else{ |
|||
$calls = 1; |
|||
} |
|||
try { |
|||
$this->assertSame($exp, $u->passwordSet($user, $password)); |
|||
} finally { |
|||
Phake::verify(Arsse::$db, Phake::times($calls))->userExists($user); |
|||
Phake::verify(Arsse::$db, Phake::times($exists ? $calls : 0))->userPasswordSet($user, $password ?? "random password", null); |
|||
} |
|||
} |
|||
|
|||
/** @dataProvider providePasswordChanges */ |
|||
public function testChangeAPasswordToARandomPassword(bool $authorized, string $user, $password, bool $exists, $exp) { |
|||
$u = Phake::partialMock(User::class, $this->drv); |
|||
Phake::when($this->drv)->authorize->thenReturn($authorized); |
|||
Phake::when($this->drv)->userPasswordSet($this->anything(), $this->isNull(), $this->anything())->thenReturn(null); |
|||
Phake::when($this->drv)->userPasswordSet("john.doe@example.com", $this->logicalNot($this->isNull()), $this->anything())->thenReturnCallback(function($user, $pass, $old) { |
|||
return $pass ?? "random password"; |
|||
}); |
|||
Phake::when($this->drv)->userPasswordSet("jane.doe@example.com", $this->logicalNot($this->isNull()), $this->anything())->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist")); |
|||
Phake::when(Arsse::$db)->userExists->thenReturn($exists); |
|||
if ($exp instanceof Exception) { |
|||
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) { |
|||
$this->assertException("notAuthorized", "User", "ExceptionAuthz"); |
|||
$calls = 0; |
|||
} else { |
|||
$this->assertException("doesNotExist", "User"); |
|||
$calls = 2; |
|||
} |
|||
} else { |
|||
$calls = 4; |
|||
} |
|||
try { |
|||
$pass1 = $u->passwordSet($user, null); |
|||
$pass2 = $u->passwordSet($user, null); |
|||
$this->assertNotEquals($pass1, $pass2); |
|||
} finally { |
|||
Phake::verify($this->drv, Phake::times($calls))->userPasswordSet; |
|||
Phake::verify($u, Phake::times($calls / 2))->generatePassword; |
|||
Phake::verify(Arsse::$db, Phake::times($calls==4 ? 2 : 0))->userExists($user); |
|||
if ($calls == 4) { |
|||
Phake::verify(Arsse::$db, Phake::times($exists ? 1 : 0))->userPasswordSet($user, $pass1, null); |
|||
Phake::verify(Arsse::$db, Phake::times($exists ? 1 : 0))->userPasswordSet($user, $pass2, null); |
|||
} else { |
|||
Phake::verify(Arsse::$db, Phake::times(0))->userPasswordSet; |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function providePasswordChanges() { |
|||
$john = "john.doe@example.com"; |
|||
$jane = "jane.doe@example.com"; |
|||
return [ |
|||
[false, $john, "secret", true, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[false, $jane, "superman", false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")], |
|||
[true, $john, "superman", true, "superman"], |
|||
[true, $john, null, true, "random password"], |
|||
[true, $john, "superman", false, "superman"], |
|||
[true, $john, null, false, "random password"], |
|||
[true, $jane, "secret", true, new \JKingWeb\Arsse\User\Exception("doesNotExist")], |
|||
]; |
|||
} |
|||
} |
@ -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