2017-02-28 04:04:13 +00:00
< ? php
declare ( strict_types = 1 );
2017-03-28 04:12:12 +00:00
namespace JKingWeb\Arsse ;
2017-05-11 22:00:35 +00:00
use Phake ;
2017-02-28 04:04:13 +00:00
2017-07-20 22:36:03 +00:00
/** @covers \JKingWeb\Arsse\User */
2017-07-08 01:06:38 +00:00
class TestAuthorization extends Test\AbstractTest {
2017-04-07 01:41:21 +00:00
const USERS = [
'user@example.com' => User\Driver :: RIGHTS_NONE ,
'user@example.org' => User\Driver :: RIGHTS_NONE ,
'dman@example.com' => User\Driver :: RIGHTS_DOMAIN_MANAGER ,
'dman@example.org' => User\Driver :: RIGHTS_DOMAIN_MANAGER ,
'dadm@example.com' => User\Driver :: RIGHTS_DOMAIN_ADMIN ,
'dadm@example.org' => User\Driver :: RIGHTS_DOMAIN_ADMIN ,
'gman@example.com' => User\Driver :: RIGHTS_GLOBAL_MANAGER ,
'gman@example.org' => User\Driver :: RIGHTS_GLOBAL_MANAGER ,
'gadm@example.com' => User\Driver :: RIGHTS_GLOBAL_ADMIN ,
'gadm@example.org' => User\Driver :: RIGHTS_GLOBAL_ADMIN ,
// invalid rights levels
'bad1@example.com' => User\Driver :: RIGHTS_NONE + 1 ,
'bad1@example.org' => User\Driver :: RIGHTS_NONE + 1 ,
'bad2@example.com' => User\Driver :: RIGHTS_DOMAIN_MANAGER + 1 ,
'bad2@example.org' => User\Driver :: RIGHTS_DOMAIN_MANAGER + 1 ,
'bad3@example.com' => User\Driver :: RIGHTS_DOMAIN_ADMIN + 1 ,
'bad3@example.org' => User\Driver :: RIGHTS_DOMAIN_ADMIN + 1 ,
'bad4@example.com' => User\Driver :: RIGHTS_GLOBAL_MANAGER + 1 ,
'bad4@example.org' => User\Driver :: RIGHTS_GLOBAL_MANAGER + 1 ,
'bad5@example.com' => User\Driver :: RIGHTS_GLOBAL_ADMIN + 1 ,
'bad5@example.org' => User\Driver :: RIGHTS_GLOBAL_ADMIN + 1 ,
2017-02-28 04:04:13 +00:00
2017-04-07 01:41:21 +00:00
];
const LEVELS = [
User\Driver :: RIGHTS_NONE ,
User\Driver :: RIGHTS_DOMAIN_MANAGER ,
User\Driver :: RIGHTS_DOMAIN_ADMIN ,
User\Driver :: RIGHTS_GLOBAL_MANAGER ,
User\Driver :: RIGHTS_GLOBAL_ADMIN ,
];
const DOMAINS = [
'@example.com' ,
'@example.org' ,
" " ,
];
protected $data ;
2017-02-28 04:04:13 +00:00
2017-02-28 17:37:45 +00:00
function setUp ( string $drv = Test\User\DriverInternalMock :: class , string $db = null ) {
2017-04-07 01:41:21 +00:00
$this -> clearData ();
$conf = new Conf ();
$conf -> userDriver = $drv ;
2017-07-15 17:33:17 +00:00
$conf -> userPreAuth = false ;
2017-04-07 01:41:21 +00:00
$conf -> userComposeNames = true ;
2017-07-17 11:47:57 +00:00
Arsse :: $conf = $conf ;
2017-04-07 01:41:21 +00:00
if ( $db !== null ) {
2017-07-17 11:47:57 +00:00
Arsse :: $db = new $db ();
2017-04-07 01:41:21 +00:00
}
2017-07-17 11:47:57 +00:00
Arsse :: $user = Phake :: PartialMock ( User :: class );
Phake :: when ( Arsse :: $user ) -> authorize -> thenReturn ( true );
2017-04-07 01:41:21 +00:00
foreach ( self :: USERS as $user => $level ) {
2017-07-17 11:47:57 +00:00
Arsse :: $user -> add ( $user , " " );
Arsse :: $user -> rightsSet ( $user , $level );
2017-04-07 01:41:21 +00:00
}
2017-07-17 11:47:57 +00:00
Phake :: reset ( Arsse :: $user );
2017-04-07 01:41:21 +00:00
}
2017-03-28 22:50:00 +00:00
2017-04-07 01:41:21 +00:00
function tearDown () {
$this -> clearData ();
}
2017-02-28 04:04:13 +00:00
2017-05-11 22:00:35 +00:00
function testToggleLogic () {
2017-07-17 11:47:57 +00:00
$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 ));
2017-05-11 22:00:35 +00:00
}
2017-04-07 01:41:21 +00:00
function testSelfActionLogic () {
foreach ( array_keys ( self :: USERS ) as $user ) {
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $user , " " );
2017-04-07 01:41:21 +00:00
// users should be able to do basic actions for themselves
2017-07-17 11:47:57 +00:00
$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. " );
2017-04-07 01:41:21 +00:00
}
}
2017-02-28 15:52:02 +00:00
2017-04-07 01:41:21 +00:00
function testRegularUserLogic () {
foreach ( self :: USERS as $actor => $rights ) {
2017-07-21 02:40:09 +00:00
if ( $rights != User\Driver :: RIGHTS_NONE ) {
continue ;
}
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $actor , " " );
2017-04-07 01:41:21 +00:00
foreach ( array_keys ( self :: USERS ) as $affected ) {
// regular users should only be able to act for themselves
if ( $actor == $affected ) {
2017-07-17 11:47:57 +00:00
$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. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$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. " );
2017-04-07 01:41:21 +00:00
}
// they should never be able to set rights
foreach ( self :: LEVELS as $level ) {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted improperly for $affected settings rights level $level , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
// they should not be able to list users
foreach ( self :: DOMAINS as $domain ) {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor improperly checked user list for domain ' $domain ', but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
2017-02-28 15:52:02 +00:00
2017-04-07 01:41:21 +00:00
function testDomainManagerLogic () {
foreach ( self :: USERS as $actor => $actorRights ) {
2017-07-21 02:40:09 +00:00
if ( $actorRights != User\Driver :: RIGHTS_DOMAIN_MANAGER ) {
continue ;
}
2017-04-07 01:41:21 +00:00
$actorDomain = substr ( $actor , strrpos ( $actor , " @ " ) + 1 );
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $actor , " " );
2017-04-07 01:41:21 +00:00
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 ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userExists " ), " User $actor acted properly for $affected , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userExists " ), " User $actor acted improperly for $affected , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
// they should only be able to act for regular users on the same domain
if ( $actor == $affected || ( $actorDomain == $affectedDomain && $affectedRights == User\Driver :: RIGHTS_NONE )) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRemove " ), " User $actor acted properly for $affected , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRemove " ), " User $actor acted improperly for $affected , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
// 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 , User\Driver :: RIGHTS_DOMAIN_MANAGER ])) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted properly for $affected settings rights level $level , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted improperly for $affected settings rights level $level , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
// they should also be able to list all users on their own domain
foreach ( self :: DOMAINS as $domain ) {
if ( $domain == " @ " . $actorDomain ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor properly checked user list for domain ' $domain ', but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor improperly checked user list for domain ' $domain ', but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
}
2017-02-28 15:52:02 +00:00
2017-04-07 01:41:21 +00:00
function testDomainAdministratorLogic () {
foreach ( self :: USERS as $actor => $actorRights ) {
2017-07-21 02:40:09 +00:00
if ( $actorRights != User\Driver :: RIGHTS_DOMAIN_ADMIN ) {
continue ;
}
2017-04-07 01:41:21 +00:00
$actorDomain = substr ( $actor , strrpos ( $actor , " @ " ) + 1 );
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $actor , " " );
2017-04-07 01:41:21 +00:00
$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 ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userExists " ), " User $actor acted properly for $affected , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userExists " ), " User $actor acted improperly for $affected , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
// 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 )) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRemove " ), " User $actor acted properly for $affected , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRemove " ), " User $actor acted improperly for $affected , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
// 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 )) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted properly for $affected settings rights level $level , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted improperly for $affected settings rights level $level , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
// they should also be able to list all users on their own domain
foreach ( self :: DOMAINS as $domain ) {
if ( $domain == " @ " . $actorDomain ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor properly checked user list for domain ' $domain ', but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor improperly checked user list for domain ' $domain ', but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
}
2017-02-28 15:52:02 +00:00
2017-04-07 01:41:21 +00:00
function testGlobalManagerLogic () {
foreach ( self :: USERS as $actor => $actorRights ) {
2017-07-21 02:40:09 +00:00
if ( $actorRights != User\Driver :: RIGHTS_GLOBAL_MANAGER ) {
continue ;
}
2017-04-07 01:41:21 +00:00
$actorDomain = substr ( $actor , strrpos ( $actor , " @ " ) + 1 );
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $actor , " " );
2017-04-07 01:41:21 +00:00
foreach ( self :: USERS as $affected => $affectedRights ) {
$affectedDomain = substr ( $affected , strrpos ( $affected , " @ " ) + 1 );
// global managers should be able to check any user
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userExists " ), " User $actor acted properly for $affected , but the action was denied. " );
2017-04-07 01:41:21 +00:00
// they should only be able to act for regular users
if ( $actor == $affected || $affectedRights == User\Driver :: RIGHTS_NONE ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRemove " ), " User $actor acted properly for $affected , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRemove " ), " User $actor acted improperly for $affected , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
// 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 , User\Driver :: RIGHTS_GLOBAL_MANAGER ])) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted properly for $affected settings rights level $level , but the action was denied. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted improperly for $affected settings rights level $level , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
// they should also be able to list all users
foreach ( self :: DOMAINS as $domain ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor properly checked user list for domain ' $domain ', but the action was denied. " );
2017-04-07 01:41:21 +00:00
}
}
}
2017-02-28 15:52:02 +00:00
2017-04-07 01:41:21 +00:00
function testGlobalAdministratorLogic () {
foreach ( self :: USERS as $actor => $actorRights ) {
2017-07-21 02:40:09 +00:00
if ( $actorRights != User\Driver :: RIGHTS_GLOBAL_ADMIN ) {
continue ;
}
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $actor , " " );
2017-04-07 01:41:21 +00:00
// global admins can do anything
foreach ( self :: USERS as $affected => $affectedRights ) {
2017-07-17 11:47:57 +00:00
$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. " );
2017-04-07 01:41:21 +00:00
foreach ( self :: LEVELS as $level ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted properly for $affected settings rights level $level , but the action was denied. " );
2017-04-07 01:41:21 +00:00
}
}
foreach ( self :: DOMAINS as $domain ) {
2017-07-17 11:47:57 +00:00
$this -> assertTrue ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor properly checked user list for domain ' $domain ', but the action was denied. " );
2017-04-07 01:41:21 +00:00
}
}
}
2017-02-28 16:19:33 +00:00
2017-04-07 01:41:21 +00:00
function testInvalidLevelLogic () {
foreach ( self :: USERS as $actor => $rights ) {
2017-07-21 02:40:09 +00:00
if ( in_array ( $rights , self :: LEVELS )) {
continue ;
}
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( $actor , " " );
2017-04-07 01:41:21 +00:00
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 ) {
2017-07-17 11:47:57 +00:00
$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. " );
2017-04-07 01:41:21 +00:00
} else {
2017-07-17 11:47:57 +00:00
$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. " );
2017-04-07 01:41:21 +00:00
}
// they should never be able to set rights
foreach ( self :: LEVELS as $level ) {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $affected , " userRightsSet " , $level ), " User $actor acted improperly for $affected settings rights level $level , but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
// they should not be able to list users
foreach ( self :: DOMAINS as $domain ) {
2017-07-17 11:47:57 +00:00
$this -> assertFalse ( Arsse :: $user -> authorize ( $domain , " userList " ), " User $actor improperly checked user list for domain ' $domain ', but the action was allowed. " );
2017-04-07 01:41:21 +00:00
}
}
}
2017-02-28 17:37:45 +00:00
2017-04-07 01:41:21 +00:00
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)
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( " gadm@example.com " , " " );
2017-04-07 01:41:21 +00:00
$this -> assertCount ( 0 , $this -> checkExceptions ( " user@example.org " , $tests ));
// next try with a regular user acting on another user (everything should fail)
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( " user@example.com " , " " );
2017-04-07 01:41:21 +00:00
$this -> assertCount ( sizeof ( $tests ), $this -> checkExceptions ( " user@example.org " , $tests ));
}
2017-02-28 17:37:45 +00:00
2017-04-07 01:41:21 +00:00
function testExternalExceptionLogic () {
// set up the test for an external driver
$this -> setUp ( Test\User\DriverExternalMock :: class , Test\User\Database :: class );
// run the previous test with the external driver set up
$this -> testInternalExceptionLogic ();
}
2017-02-28 17:37:45 +00:00
2017-04-07 01:41:21 +00:00
// 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
2017-07-21 02:40:09 +00:00
if ( $func != " list " ) {
array_unshift ( $args , $user );
}
2017-04-07 01:41:21 +00:00
try {
2017-07-17 11:47:57 +00:00
call_user_func_array ( array ( Arsse :: $user , $func ), $args );
2017-04-07 01:41:21 +00:00
} catch ( User\ExceptionAuthz $e ) {
$err [] = $func ;
}
}
return $err ;
}
2017-05-11 22:00:35 +00:00
function testMissingUserLogic () {
2017-07-17 11:47:57 +00:00
Arsse :: $user -> auth ( " gadm@example.com " , " " );
$this -> assertTrue ( Arsse :: $user -> authorize ( " user@example.com " , " someFunction " ));
2017-05-11 22:00:35 +00:00
$this -> assertException ( " doesNotExist " , " User " );
2017-07-17 11:47:57 +00:00
Arsse :: $user -> authorize ( " this_user_does_not_exist@example.org " , " someFunction " );
2017-05-11 22:00:35 +00:00
}
2017-02-28 04:04:13 +00:00
}