diff --git a/lib/AbstractException.php b/lib/AbstractException.php index 2412f86d..e427e9d4 100644 --- a/lib/AbstractException.php +++ b/lib/AbstractException.php @@ -38,7 +38,7 @@ abstract class AbstractException extends \Exception { "User/Exception.alreadyExists" => 10403, "User/Exception.authMissing" => 10411, "User/Exception.authFailed" => 10412, - "User/Exception.notAuthorized" => 10421, + "User/ExceptionAuthz.notAuthorized" => 10421, "Feed/Exception.invalidCertificate" => 10501, "Feed/Exception.invalidUrl" => 10502, "Feed/Exception.maxRedirect" => 10503, @@ -60,7 +60,7 @@ abstract class AbstractException extends \Exception { $class = get_called_class(); $codeID = str_replace("\\", "/", str_replace(NS_BASE, "", $class)).".$msgID"; if(!array_key_exists($codeID, self::CODES)) { - throw new Exception("uncoded"); + throw new Exception("uncoded", $codeID); } else { $code = self::CODES[$codeID]; $msg = "Exception.".str_replace("\\", "/", $class).".$msgID"; diff --git a/lib/Database.php b/lib/Database.php index aa710f48..e29b8337 100644 --- a/lib/Database.php +++ b/lib/Database.php @@ -199,7 +199,7 @@ class Database { $domain = "%@".$domain; $set = $this->db->prepare("SELECT id from newssync_users where id like ?", "str")->run($domain); } else { - if(!$this->data->user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "all users"]); + if(!$this->data->user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); $set = $this->db->prepare("SELECT id from newssync_users")->run(); } $out = []; diff --git a/lib/User.php b/lib/User.php index a008b61b..36e332e5 100644 --- a/lib/User.php +++ b/lib/User.php @@ -193,7 +193,7 @@ class User { } } - public function passwordSet(string $user, string $newPassword = null, $oldPassword = null): bool { + public function passwordSet(string $user, string $newPassword = null, $oldPassword = null): string { $func = "userPasswordSet"; switch($this->u->driverFunctions($func)) { case User\Driver::FUNC_EXTERNAL: diff --git a/locale/en.php b/locale/en.php index 38d9a8af..e96b13be 100644 --- a/locale/en.php +++ b/locale/en.php @@ -50,7 +50,21 @@ return [ 'Exception.JKingWeb/NewsSync/User/Exception.doesNotExist' => 'Could not perform action "{action}" because the user {user} does not exist', 'Exception.JKingWeb/NewsSync/User/Exception.authMissing' => 'Please log in to proceed', 'Exception.JKingWeb/NewsSync/User/Exception.authFailed' => 'Authentication failed', - 'Exception.JKingWeb/NewsSync/User/ExceptionAuthz.notAuthorized' => 'Authenticated user is not authorized to perform the action "{action}" on behalf of {user}', + 'Exception.JKingWeb/NewsSync/User/ExceptionAuthz.notAuthorized' => + /*'{action, select, + userList {{user, select, + "*" {Authenticated user is not authorized to view the global user list} + other {Authenticated user is not authorized to view the user list for domain {user}} + }} + other {Authenticated user is not authorized to perform the action "{action}" on behalf of {user}} + }',*/ + '{action, select, + userList {{user, select, + global {Authenticated user is not authorized to view the global user list} + other {Authenticated user is not authorized to view the user list for domain {user}} + }} + other {Authenticated user is not authorized to perform the action "{action}" on behalf of {user}} + }', 'Exception.JKingWeb/NewsSync/Feed/Exception.invalidCertificate' => 'Could not download feed "{url}" because its server is serving an invalid SSL certificate', 'Exception.JKingWeb/NewsSync/Feed/Exception.invalidURL' => 'Feed URL "{url}" is invalid', diff --git a/tests/User/TestUser.php b/tests/User/TestUser.php index 609b1c8a..b51e0086 100644 --- a/tests/User/TestUser.php +++ b/tests/User/TestUser.php @@ -18,6 +18,7 @@ class TestUser extends \PHPUnit\Framework\TestCase { $conf->userAuthPreferHTTP = true; $this->data = new Test\RuntimeData($conf); $this->data->user = new User($this->data); + $this->data->user->authorizationEnabled(false); $_SERVER['PHP_AUTH_USER'] = self::USER1; $_SERVER['PHP_AUTH_PW'] = "secret"; } @@ -69,4 +70,25 @@ class TestUser extends \PHPUnit\Framework\TestCase { $this->assertTrue($this->data->user->auth(self::USER1, "secret")); $this->assertFalse($this->data->user->auth(self::USER1, "superman")); } + + function testChangeAPassword() { + $this->data->user->add(self::USER1, "secret"); + $this->assertEquals("superman", $this->data->user->passwordSet(self::USER1, "superman")); + $this->assertTrue($this->data->user->auth(self::USER1, "superman")); + $this->assertFalse($this->data->user->auth(self::USER1, "secret")); + $this->assertEquals($this->data->conf->userTempPasswordLength, strlen($this->data->user->passwordSet(self::USER1))); + } + + function testChangeAPasswordForAMissingUser() { + $this->assertException("doesNotExist", "User"); + $this->data->user->passwordSet(self::USER1, "superman"); + } + + function testGetThePropertiesOfAUser() { + $this->data->user->add(self::USER1, "secret"); + $p = $this->data->user->propertiesGet(self::USER1); + $this->assertArrayHasKey('name', $p); + $this->assertArrayNotHasKey('password', $p); + $this->assertEquals(self::USER1, $p['name']); + } } diff --git a/tests/lib/User/DriverInternalMock.php b/tests/lib/User/DriverInternalMock.php index f592daf3..d8d182ba 100644 --- a/tests/lib/User/DriverInternalMock.php +++ b/tests/lib/User/DriverInternalMock.php @@ -1,9 +1,13 @@ data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return array_key_exists($user, $this->db); } function userAdd(string $user, string $password = null): string { + if(!$this->data->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($this->data->conf->userTempPasswordLength)->get(); $u = [ @@ -68,6 +74,7 @@ final class DriverInternalMock implements Driver { } function userRemove(string $user): bool { + if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); unset($this->db[$user]); return true; @@ -76,10 +83,12 @@ final class DriverInternalMock implements Driver { function userList(string $domain = null): array { $list = array_keys($this->db); if($domain===null) { + if(!$this->data->user->authorize("", __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]); return $list; } else { $suffix = '@'.$domain; $len = -1 * strlen($suffix); + if(!$this->data->user->authorize($suffix, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); return array_filter($list, function($user) use($suffix, $len) { return substr_compare($user, $suffix, $len); }); @@ -87,6 +96,7 @@ final class DriverInternalMock implements Driver { } function userPasswordSet(string $user, string $newPassword = null, string $oldPassword = null): string { + if(!$this->data->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($this->data->conf->userTempPasswordLength)->get(); $this->db[$user]['password'] = password_hash($newPassword, \PASSWORD_DEFAULT); @@ -94,22 +104,28 @@ final class DriverInternalMock implements Driver { } function userPropertiesGet(string $user): array { + if(!$this->data->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]; + $out = $this->db[$user]; + unset($out['password']); + return $out; } function userPropertiesSet(string $user, array $properties): array { + if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); $this->db[$user] = array_merge($this->db[$user], $properties); return $this->userPropertiesGet($user); } function userRightsGet(string $user): int { + if(!$this->data->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]['rights']; } function userRightsSet(string $user, int $level): bool { + if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]); $this->db[$user]['rights'] = $level; return true;