mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Eliminated passing of RuntimeData instances
- RuntimeData has now been replaced by a single static Data class - The Data class has a load() method which fills the same role as the constructor of RuntimeData - The static Lang class is now an instantiable class and is a member of Data - All tests have been adjusted and pass - The Exception tests no longer require convoluted workarounds: a simple mock for Data::$l suffices; Lang tests also use a mock to prevent loops now instead of using a workaround
This commit is contained in:
parent
2b8aa75ee3
commit
f902346b6c
40 changed files with 472 additions and 504 deletions
|
@ -1,3 +1,3 @@
|
|||
<?php
|
||||
require_once __DIR__.DIRECTORY_SEPARATOR."bootstrap.php";
|
||||
$data = new RuntimeData(new Conf());
|
||||
Data::load(new Conf());
|
|
@ -6,7 +6,6 @@ abstract class AbstractException extends \Exception {
|
|||
|
||||
const CODES = [
|
||||
"Exception.uncoded" => -1,
|
||||
"Exception.invalid" => 1, // this exception MUST NOT have a message string defined
|
||||
"Exception.unknown" => 10000,
|
||||
"Lang/Exception.defaultFileMissing" => 10101,
|
||||
"Lang/Exception.fileMissing" => 10102,
|
||||
|
@ -79,7 +78,7 @@ abstract class AbstractException extends \Exception {
|
|||
$code = self::CODES[$codeID];
|
||||
$msg = "Exception.".str_replace("\\", "/", $class).".$msgID";
|
||||
}
|
||||
$msg = Lang::msg($msg, $vars);
|
||||
$msg = Data::$l->msg($msg, $vars);
|
||||
}
|
||||
parent::__construct($msg, $code, $e);
|
||||
}
|
||||
|
|
18
lib/Data.php
Normal file
18
lib/Data.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
|
||||
class Data {
|
||||
public static $l;
|
||||
public static $conf;
|
||||
public static $db;
|
||||
public static $user;
|
||||
|
||||
static function load(Conf $conf) {
|
||||
static::$l = new Lang();
|
||||
static::$conf = $conf;
|
||||
static::$l->set($conf->lang);
|
||||
static::$db = new Database();
|
||||
static::$user = new User();
|
||||
}
|
||||
}
|
|
@ -18,10 +18,9 @@ class Database {
|
|||
return (string) preg_filter("[^0-9a-zA-Z_\.]", "", $name);
|
||||
}
|
||||
|
||||
public function __construct(RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
$this->driver = $driver = $data->conf->dbDriver;
|
||||
$this->db = new $driver($data, INSTALL);
|
||||
public function __construct() {
|
||||
$this->driver = $driver = Data::$conf->dbDriver;
|
||||
$this->db = new $driver(INSTALL);
|
||||
$ver = $this->db->schemaVersion();
|
||||
if(!INSTALL && $ver < self::SCHEMA_VERSION) {
|
||||
$this->db->schemaUpdate(self::SCHEMA_VERSION);
|
||||
|
@ -166,14 +165,14 @@ class Database {
|
|||
}
|
||||
|
||||
public function userExists(string $user): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
return (bool) $this->db->prepare("SELECT count(*) from arsse_users where id is ?", "str")->run($user)->getValue();
|
||||
}
|
||||
|
||||
public function userAdd(string $user, string $password = null): string {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if($this->userExists($user)) throw new User\Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if($password===null) $password = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get();
|
||||
if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get();
|
||||
$hash = "";
|
||||
if(strlen($password) > 0) $hash = password_hash($password, \PASSWORD_DEFAULT);
|
||||
$this->db->prepare("INSERT INTO arsse_users(id,password) values(?,?)", "str", "str")->runArray([$user,$hash]);
|
||||
|
@ -181,33 +180,33 @@ class Database {
|
|||
}
|
||||
|
||||
public function userRemove(string $user): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if($this->db->prepare("DELETE from arsse_users where id is ?", "str")->run($user)->changes() < 1) throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function userList(string $domain = null): array {
|
||||
if($domain !== null) {
|
||||
if(!$this->data->user->authorize("@".$domain, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]);
|
||||
if(!Data::$user->authorize("@".$domain, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]);
|
||||
$domain = str_replace(["\\","%","_"],["\\\\", "\\%", "\\_"], $domain);
|
||||
$domain = "%@".$domain;
|
||||
return $this->db->prepare("SELECT id from arsse_users where id like ?", "str")->run($domain)->getAll();
|
||||
} else {
|
||||
if(!$this->data->user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]);
|
||||
if(!Data::$user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]);
|
||||
return $this->db->prepare("SELECT id from arsse_users")->run()->getAll();
|
||||
}
|
||||
}
|
||||
|
||||
public function userPasswordGet(string $user): string {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!$this->userExists($user)) throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
|
||||
return (string) $this->db->prepare("SELECT password from arsse_users where id is ?", "str")->run($user)->getValue();
|
||||
}
|
||||
|
||||
public function userPasswordSet(string $user, string $password = null): string {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!$this->userExists($user)) throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if($password===null) $password = (new PassGen)->length($this->data->conf->userTempPasswordLength)->get();
|
||||
if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get();
|
||||
$hash = "";
|
||||
if(strlen($password > 0)) $hash = password_hash($password, \PASSWORD_DEFAULT);
|
||||
$this->db->prepare("UPDATE arsse_users set password = ? where id is ?", "str", "str")->run($hash, $user);
|
||||
|
@ -215,14 +214,14 @@ class Database {
|
|||
}
|
||||
|
||||
public function userPropertiesGet(string $user): array {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
$prop = $this->db->prepare("SELECT name,rights from arsse_users where id is ?", "str")->run($user)->getRow();
|
||||
if(!$prop) return [];
|
||||
return $prop;
|
||||
}
|
||||
|
||||
public function userPropertiesSet(string $user, array &$properties): array {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
$valid = [ // FIXME: add future properties
|
||||
"name" => "str",
|
||||
];
|
||||
|
@ -237,12 +236,12 @@ class Database {
|
|||
}
|
||||
|
||||
public function userRightsGet(string $user): int {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
return (int) $this->db->prepare("SELECT rights from arsse_users where id is ?", "str")->run($user)->getValue();
|
||||
}
|
||||
|
||||
public function userRightsSet(string $user, int $rights): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__, $rights)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__, $rights)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!$this->userExists($user)) return false;
|
||||
$this->db->prepare("UPDATE arsse_users set rights = ? where id is ?", "int", "str")->run($rights, $user);
|
||||
return true;
|
||||
|
@ -250,7 +249,7 @@ class Database {
|
|||
|
||||
public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = ""): int {
|
||||
// If the user isn't authorized to perform this action then throw an exception.
|
||||
if (!$this->data->user->authorize($user, __FUNCTION__)) {
|
||||
if (!Data::$user->authorize($user, __FUNCTION__)) {
|
||||
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
}
|
||||
// If the user doesn't exist throw an exception.
|
||||
|
@ -299,13 +298,13 @@ class Database {
|
|||
}
|
||||
|
||||
public function subscriptionRemove(string $user, int $id): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
return (bool) $this->db->prepare("DELETE from arsse_subscriptions where id is ?", "int")->run($id)->changes();
|
||||
}
|
||||
|
||||
public function folderAdd(string $user, array $data): int {
|
||||
// If the user isn't authorized to perform this action then throw an exception.
|
||||
if (!$this->data->user->authorize($user, __FUNCTION__)) {
|
||||
if (!Data::$user->authorize($user, __FUNCTION__)) {
|
||||
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
}
|
||||
// If the user doesn't exist throw an exception.
|
||||
|
@ -345,7 +344,7 @@ class Database {
|
|||
|
||||
public function folderList(string $user, int $parent = null, bool $recursive = true): Db\Result {
|
||||
// if the user isn't authorized to perform this action then throw an exception.
|
||||
if (!$this->data->user->authorize($user, __FUNCTION__)) {
|
||||
if (!Data::$user->authorize($user, __FUNCTION__)) {
|
||||
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
}
|
||||
// if the user doesn't exist throw an exception.
|
||||
|
@ -487,7 +486,7 @@ class Database {
|
|||
}
|
||||
|
||||
public function folderRemove(string $user, int $id): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
// common table expression to list all descendant folders of the target folder
|
||||
$cte = "RECURSIVE folders(id) as (SELECT id from arsse_folders where owner is ? and id is ? union select arsse_folders.id from arsse_folders join folders on arsse_folders.parent=folders.id) ";
|
||||
$changes = 0;
|
||||
|
|
|
@ -3,7 +3,7 @@ declare(strict_types=1);
|
|||
namespace JKingWeb\Arsse\Db;
|
||||
|
||||
interface Driver {
|
||||
function __construct(\JKingWeb\Arsse\RuntimeData $data, bool $install = false);
|
||||
function __construct(bool $install = false);
|
||||
// returns a human-friendly name for the driver (for display in installer, for example)
|
||||
static function driverName(): string;
|
||||
// returns the version of the scheme of the opened database; if uninitialized should return 0
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Db\SQLite3;
|
||||
use JKingWeb\Arsse\Lang;
|
||||
use JKingWeb\Arsse\Data;
|
||||
use JKingWeb\Arsse\Db\Exception;
|
||||
use JKingWeb\Arsse\Db\ExceptionInput;
|
||||
use JKingWeb\Arsse\Db\ExceptionTimeout;
|
||||
|
@ -15,16 +15,14 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
|||
const SQLITE_MISMATCH = 20;
|
||||
|
||||
protected $db;
|
||||
protected $data;
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\RuntimeData $data, bool $install = false) {
|
||||
public function __construct(bool $install = false) {
|
||||
// check to make sure required extension is loaded
|
||||
if(!class_exists("SQLite3")) throw new Exception("extMissing", self::driverName());
|
||||
$this->data = $data;
|
||||
$file = $data->conf->dbSQLite3File;
|
||||
$file = Data::$conf->dbSQLite3File;
|
||||
// if the file exists (or we're initializing the database), try to open it
|
||||
try {
|
||||
$this->db = new \SQLite3($file, ($install) ? \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE : \SQLITE3_OPEN_READWRITE, $data->conf->dbSQLite3Key);
|
||||
$this->db = new \SQLite3($file, ($install) ? \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE : \SQLITE3_OPEN_READWRITE, Data::$conf->dbSQLite3Key);
|
||||
} catch(\Throwable $e) {
|
||||
// if opening the database doesn't work, check various pre-conditions to find out what the problem might be
|
||||
if(!file_exists($file)) {
|
||||
|
@ -57,7 +55,7 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
|||
|
||||
|
||||
static public function driverName(): string {
|
||||
return Lang::msg("Driver.Db.SQLite3.Name");
|
||||
return Data::$l->msg("Driver.Db.SQLite3.Name");
|
||||
}
|
||||
|
||||
public function schemaVersion(): int {
|
||||
|
@ -66,10 +64,10 @@ class Driver extends \JKingWeb\Arsse\Db\AbstractDriver {
|
|||
|
||||
public function schemaUpdate(int $to): bool {
|
||||
$ver = $this->schemaVersion();
|
||||
if(!$this->data->conf->dbSQLite3AutoUpd) throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]);
|
||||
if(!Data::$conf->dbSQLite3AutoUpd) throw new Exception("updateManual", ['version' => $ver, 'driver_name' => $this->driverName()]);
|
||||
if($ver >= $to) throw new Exception("updateTooNew", ['difference' => ($ver - $to), 'current' => $ver, 'target' => $to, 'driver_name' => $this->driverName()]);
|
||||
$sep = \DIRECTORY_SEPARATOR;
|
||||
$path = $this->data->conf->dbSchemaBase.$sep."SQLite3".$sep;
|
||||
$path = Data::$conf->dbSchemaBase.$sep."SQLite3".$sep;
|
||||
$this->lock();
|
||||
$this->begin();
|
||||
for($a = $ver; $a < $to; $a++) {
|
||||
|
|
114
lib/Lang.php
114
lib/Lang.php
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
use \Webmozart\Glob\Glob;
|
||||
use Webmozart\Glob\Glob;
|
||||
|
||||
class Lang {
|
||||
const DEFAULT = "en"; // fallback locale
|
||||
|
@ -16,61 +16,67 @@ class Lang {
|
|||
'Exception.JKingWeb/Arsse/Lang/Exception.stringInvalid' => 'Message string "{msgID}" is not a valid ICU message string (language files loaded: {fileList})',
|
||||
];
|
||||
|
||||
static public $path = BASE."locale".DIRECTORY_SEPARATOR; // path to locale files; this is a public property to facilitate unit testing
|
||||
public $path; // path to locale files; this is a public property to facilitate unit testing
|
||||
static protected $requirementsMet = false; // whether the Intl extension is loaded
|
||||
static protected $synched = false; // whether the wanted locale is actually loaded (lazy loading is used by default)
|
||||
static protected $wanted = self::DEFAULT; // the currently requested locale
|
||||
static protected $locale = ""; // the currently loaded locale
|
||||
static protected $loaded = []; // the cascade of loaded locale file names
|
||||
static protected $strings = self::REQUIRED; // the loaded locale strings, merged
|
||||
protected $synched = false; // whether the wanted locale is actually loaded (lazy loading is used by default)
|
||||
protected $wanted = self::DEFAULT; // the currently requested locale
|
||||
protected $locale = ""; // the currently loaded locale
|
||||
protected $loaded = []; // the cascade of loaded locale file names
|
||||
protected $strings = self::REQUIRED; // the loaded locale strings, merged
|
||||
|
||||
protected function __construct() {}
|
||||
function __construct(string $path = BASE."locale".DIRECTORY_SEPARATOR) {
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
static public function set(string $locale, bool $immediate = false): string {
|
||||
public function set(string $locale, bool $immediate = false): string {
|
||||
// make sure the Intl extension is loaded
|
||||
if(!self::$requirementsMet) self::checkRequirements();
|
||||
if(!static::$requirementsMet) static::checkRequirements();
|
||||
// if requesting the same locale as already wanted, just return (but load first if we've requested an immediate load)
|
||||
if($locale==self::$wanted) {
|
||||
if($immediate && !self::$synched) self::load();
|
||||
if($locale==$this->wanted) {
|
||||
if($immediate && !$this->synched) $this->load();
|
||||
return $locale;
|
||||
}
|
||||
// if we've requested a locale other than the null locale, fetch the list of available files and find the closest match e.g. en_ca_somedialect -> en_ca
|
||||
if($locale != "") {
|
||||
$list = self::listFiles();
|
||||
$list = $this->listFiles();
|
||||
// if the default locale is unavailable, this is (for now) an error
|
||||
if(!in_array(self::DEFAULT, $list)) throw new Lang\Exception("defaultFileMissing", self::DEFAULT);
|
||||
self::$wanted = self::match($locale, $list);
|
||||
$this->wanted = $this->match($locale, $list);
|
||||
} else {
|
||||
self::$wanted = "";
|
||||
$this->wanted = "";
|
||||
}
|
||||
self::$synched = false;
|
||||
$this->synched = false;
|
||||
// load right now if asked to, otherwise load later when actually required
|
||||
if($immediate) self::load();
|
||||
return self::$wanted;
|
||||
if($immediate) $this->load();
|
||||
return $this->wanted;
|
||||
}
|
||||
|
||||
static public function get(bool $loaded = false): string {
|
||||
public function get(bool $loaded = false): string {
|
||||
// we can either return the wanted locale (default) or the currently loaded locale
|
||||
return $loaded ? self::$locale : self::$wanted;
|
||||
return $loaded ? $this->locale : $this->wanted;
|
||||
}
|
||||
|
||||
static public function dump(): array {
|
||||
return self::$strings;
|
||||
public function dump(): array {
|
||||
return $this->strings;
|
||||
}
|
||||
|
||||
static public function msg(string $msgID, $vars = null): string {
|
||||
public function msg(string $msgID, $vars = null): string {
|
||||
return $this($msgID, $vars);
|
||||
}
|
||||
|
||||
public function __invoke(string $msgID, $vars = null): string {
|
||||
// if we're trying to load the system default language and it fails, we have a chicken and egg problem, so we catch the exception and load no language file instead
|
||||
if(!self::$synched) try {self::load();} catch(Lang\Exception $e) {
|
||||
if(self::$wanted==self::DEFAULT) {
|
||||
self::set("", true);
|
||||
if(!$this->synched) try {$this->load();} catch(Lang\Exception $e) {
|
||||
if($this->wanted==self::DEFAULT) {
|
||||
$this->set("", true);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
// if the requested message is not present in any of the currently loaded language files, throw an exception
|
||||
// note that this is indicative of a programming error since the default locale should have all strings
|
||||
if(!array_key_exists($msgID, self::$strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]);
|
||||
$msg = self::$strings[$msgID];
|
||||
if(!array_key_exists($msgID, $this->strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",$this->loaded)]);
|
||||
$msg = $this->strings[$msgID];
|
||||
// variables fed to MessageFormatter must be contained in an array
|
||||
if($vars===null) {
|
||||
// even though strings not given parameters will not get formatted, we do not optimize this case away: we still want to catch invalid strings
|
||||
|
@ -78,36 +84,36 @@ class Lang {
|
|||
} else if(!is_array($vars)) {
|
||||
$vars = [$vars];
|
||||
}
|
||||
$msg = \MessageFormatter::formatMessage(self::$locale, $msg, $vars);
|
||||
if($msg===false) throw new Lang\Exception("stringInvalid", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]);
|
||||
$msg = \MessageFormatter::formatMessage($this->locale, $msg, $vars);
|
||||
if($msg===false) throw new Lang\Exception("stringInvalid", ['msgID' => $msgID, 'fileList' => implode(", ",$this->loaded)]);
|
||||
return $msg;
|
||||
}
|
||||
|
||||
static public function list(string $locale = ""): array {
|
||||
public function list(string $locale = ""): array {
|
||||
$out = [];
|
||||
$files = self::listFiles();
|
||||
$files = $this->listFiles();
|
||||
foreach($files as $tag) {
|
||||
$out[$tag] = \Locale::getDisplayName($tag, ($locale=="") ? $tag : $locale);
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
static public function match(string $locale, array $list = null): string {
|
||||
if($list===null) $list = self::listFiles();
|
||||
$default = (self::$locale=="") ? self::DEFAULT : self::$locale;
|
||||
public function match(string $locale, array $list = null): string {
|
||||
if($list===null) $list = $this->listFiles();
|
||||
$default = ($this->locale=="") ? self::DEFAULT : $this->locale;
|
||||
return \Locale::lookup($list,$locale, true, $default);
|
||||
}
|
||||
|
||||
static protected function checkRequirements(): bool {
|
||||
if(!extension_loaded("intl")) throw new ExceptionFatal("The \"Intl\" extension is required, but not loaded");
|
||||
self::$requirementsMet = true;
|
||||
static::$requirementsMet = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static protected function listFiles(): array {
|
||||
$out = glob(self::$path."*.php");
|
||||
protected function listFiles(): array {
|
||||
$out = glob($this->path."*.php");
|
||||
// built-in glob doesn't work with vfsStream (and this other glob doesn't seem to work with Windows paths), so we try both
|
||||
if(empty($out)) $out = Glob::glob(self::$path."*.php"); // FIXME: we should just mock glob() in tests instead and make this a dev dependency
|
||||
if(empty($out)) $out = Glob::glob($this->path."*.php"); // FIXME: we should just mock glob() in tests instead and make this a dev dependency
|
||||
// trim the returned file paths to return just the language tag
|
||||
$out = array_map(function($file) {
|
||||
$file = str_replace(DIRECTORY_SEPARATOR, "/", $file);
|
||||
|
@ -119,17 +125,17 @@ class Lang {
|
|||
return $out;
|
||||
}
|
||||
|
||||
static protected function load(): bool {
|
||||
protected function load(): bool {
|
||||
if(!self::$requirementsMet) self::checkRequirements();
|
||||
// if we've requested no locale (""), just load the fallback strings and return
|
||||
if(self::$wanted=="") {
|
||||
self::$strings = self::REQUIRED;
|
||||
self::$locale = self::$wanted;
|
||||
self::$synched = true;
|
||||
if($this->wanted=="") {
|
||||
$this->strings = self::REQUIRED;
|
||||
$this->locale = $this->wanted;
|
||||
$this->synched = true;
|
||||
return true;
|
||||
}
|
||||
// decompose the requested locale from specific to general, building a list of files to load
|
||||
$tags = \Locale::parseLocale(self::$wanted);
|
||||
$tags = \Locale::parseLocale($this->wanted);
|
||||
$files = [];
|
||||
while(sizeof($tags) > 0) {
|
||||
$files[] = strtolower(\Locale::composeLocale($tags));
|
||||
|
@ -142,7 +148,7 @@ class Lang {
|
|||
// reduce the list of files to be loaded to the minimum necessary (e.g. if we go from "fr" to "fr_ca", we don't need to load "fr" or "en")
|
||||
$files = [];
|
||||
foreach($loaded as $file) {
|
||||
if($file==self::$locale) break;
|
||||
if($file==$this->locale) break;
|
||||
$files[] = $file;
|
||||
}
|
||||
// if we need to load all files, start with the fallback strings
|
||||
|
@ -151,17 +157,17 @@ class Lang {
|
|||
$strings[] = self::REQUIRED;
|
||||
} else {
|
||||
// otherwise start with the strings we already have if we're going from e.g. "fr" to "fr_ca"
|
||||
$strings[] = self::$strings;
|
||||
$strings[] = $this->strings;
|
||||
}
|
||||
// read files in reverse order
|
||||
$files = array_reverse($files);
|
||||
foreach($files as $file) {
|
||||
if(!file_exists(self::$path."$file.php")) throw new Lang\Exception("fileMissing", $file);
|
||||
if(!is_readable(self::$path."$file.php")) throw new Lang\Exception("fileUnreadable", $file);
|
||||
if(!file_exists($this->path."$file.php")) throw new Lang\Exception("fileMissing", $file);
|
||||
if(!is_readable($this->path."$file.php")) throw new Lang\Exception("fileUnreadable", $file);
|
||||
try {
|
||||
// we use output buffering in case the language file is corrupted
|
||||
ob_start();
|
||||
$arr = (include self::$path."$file.php");
|
||||
$arr = (include $this->path."$file.php");
|
||||
} catch(\Throwable $e) {
|
||||
$arr = null;
|
||||
} finally {
|
||||
|
@ -171,10 +177,10 @@ class Lang {
|
|||
$strings[] = $arr;
|
||||
}
|
||||
// apply the results and return
|
||||
self::$strings = call_user_func_array("array_replace_recursive", $strings);
|
||||
self::$loaded = $loaded;
|
||||
self::$locale = self::$wanted;
|
||||
self::$synched = true;
|
||||
$this->strings = call_user_func_array("array_replace_recursive", $strings);
|
||||
$this->loaded = $loaded;
|
||||
$this->locale = $this->wanted;
|
||||
$this->synched = true;
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -3,22 +3,4 @@ declare(strict_types=1);
|
|||
namespace JKingWeb\Arsse\Lang;
|
||||
|
||||
class Exception extends \JKingWeb\Arsse\AbstractException {
|
||||
static $test = false; // used during PHPUnit testing only
|
||||
|
||||
function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
||||
if(!self::$test) {
|
||||
parent::__construct($msgID, $vars, $e);
|
||||
} else {
|
||||
$codeID = "Lang/Exception.$msgID";
|
||||
if(!array_key_exists($codeID,self::CODES)) {
|
||||
$code = -1;
|
||||
$msg = "Exception.".str_replace("\\","/",parent::class).".uncoded";
|
||||
$vars = $msgID;
|
||||
} else {
|
||||
$code = self::CODES[$codeID];
|
||||
$msg = "Exception.".str_replace("\\","/",__CLASS__).".$msgID";
|
||||
}
|
||||
\Exception::__construct($msg, $code, $e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,8 +26,7 @@ class REST {
|
|||
];
|
||||
protected $data;
|
||||
|
||||
function __construct(RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function dispatch(REST\Request $req = null): bool {
|
||||
|
@ -35,7 +34,7 @@ class REST {
|
|||
$api = $this->apiMatch($url, $this->apis);
|
||||
$req->url = substr($url,strlen($this->apis[$api]['strip']));
|
||||
$class = $this->apis[$api]['class'];
|
||||
$drv = new $class($this->data);
|
||||
$drv = new $class();
|
||||
$drv->dispatch($req);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ declare(strict_types=1);
|
|||
namespace JKingWeb\Arsse\REST;
|
||||
|
||||
abstract class AbstractHandler implements Handler {
|
||||
abstract function __construct(\JKingWeb\Arsse\RuntimeData $data);
|
||||
abstract function __construct();
|
||||
abstract function dispatch(Request $req): Response;
|
||||
|
||||
protected function parseURL(string $url): array {
|
||||
|
|
|
@ -3,6 +3,6 @@ declare(strict_types=1);
|
|||
namespace JKingWeb\Arsse\REST;
|
||||
|
||||
interface Handler {
|
||||
function __construct(\JKingWeb\Arsse\RuntimeData $data);
|
||||
function __construct();
|
||||
function dispatch(Request $req): Response;
|
||||
}
|
|
@ -4,8 +4,7 @@ namespace JKingWeb\Arsse\REST\NextCloudNews;
|
|||
use JKingWeb\Arsse\REST\Response;
|
||||
|
||||
class Versions extends \JKingWeb\Arsse\REST\AbstractHandler {
|
||||
function __construct(\JKingWeb\Arsse\RuntimeData $data) {
|
||||
// runtime data is not needed; this method is deliberately empty
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function dispatch(\JKingWeb\Arsse\REST\Request $req): \JKingWeb\Arsse\REST\Response {
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
|
||||
class RuntimeData {
|
||||
public $conf;
|
||||
public $db;
|
||||
public $user;
|
||||
|
||||
public function __construct(Conf $conf) {
|
||||
$this->conf = $conf;
|
||||
Lang::set($conf->lang);
|
||||
$this->db = new Database($this);
|
||||
$this->user = new User($this);
|
||||
}
|
||||
}
|
56
lib/User.php
56
lib/User.php
|
@ -5,7 +5,6 @@ namespace JKingWeb\Arsse;
|
|||
class User {
|
||||
public $id = null;
|
||||
|
||||
protected $data;
|
||||
protected $u;
|
||||
protected $authz = true;
|
||||
protected $authzSupported = 0;
|
||||
|
@ -23,10 +22,9 @@ class User {
|
|||
return $classes;
|
||||
}
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
$driver = $data->conf->userDriver;
|
||||
$this->u = new $driver($data);
|
||||
public function __construct() {
|
||||
$driver = Data::$conf->userDriver;
|
||||
$this->u = new $driver();
|
||||
$this->authzSupported = $this->u->driverFunctions("authorize");
|
||||
}
|
||||
|
||||
|
@ -42,9 +40,9 @@ class User {
|
|||
// if we don't have a logged-in user, fetch credentials
|
||||
if($this->id===null) $this->credentials();
|
||||
// if the affected user is the actor and the actor is not trying to grant themselves rights, accept the request
|
||||
if($affectedUser==$this->data->user->id && $action != "userRightsSet") return true;
|
||||
if($affectedUser==Data::$user->id && $action != "userRightsSet") return true;
|
||||
// get properties of actor if not already available
|
||||
if(!sizeof($this->actor)) $this->actor = $this->propertiesGet($this->data->user->id);
|
||||
if(!sizeof($this->actor)) $this->actor = $this->propertiesGet(Data::$user->id);
|
||||
$rights =& $this->actor["rights"];
|
||||
// if actor is a global admin, accept the request
|
||||
if($rights==User\Driver::RIGHTS_GLOBAL_ADMIN) return true;
|
||||
|
@ -53,7 +51,7 @@ class User {
|
|||
// if actor is not some other sort of admin, deny the request
|
||||
if(!in_array($rights,[User\Driver::RIGHTS_GLOBAL_MANAGER,User\Driver::RIGHTS_DOMAIN_MANAGER,User\Driver::RIGHTS_DOMAIN_ADMIN],true)) return false;
|
||||
// if actor is a domain admin/manager and domains don't match, deny the request
|
||||
if($this->data->conf->userComposeNames && $this->actor["domain"] && $rights != User\Driver::RIGHTS_GLOBAL_MANAGER) {
|
||||
if(Data::$conf->userComposeNames && $this->actor["domain"] && $rights != User\Driver::RIGHTS_GLOBAL_MANAGER) {
|
||||
$test = "@".$this->actor["domain"];
|
||||
if(substr($affectedUser,-1*strlen($test)) != $test) return false;
|
||||
}
|
||||
|
@ -79,7 +77,7 @@ class User {
|
|||
}
|
||||
|
||||
public function credentials(): array {
|
||||
if($this->data->conf->userAuthPreferHTTP) {
|
||||
if(Data::$conf->userAuthPreferHTTP) {
|
||||
return $this->credentialsHTTP();
|
||||
} else {
|
||||
return $this->credentialsForm();
|
||||
|
@ -100,7 +98,7 @@ class User {
|
|||
} else {
|
||||
$out = ["user" => "", "password" => ""];
|
||||
}
|
||||
if($this->data->conf->userComposeNames && $out["user"] != "") {
|
||||
if(Data::$conf->userComposeNames && $out["user"] != "") {
|
||||
$out["user"] = $this->composeName($out["user"]);
|
||||
}
|
||||
$this->id = $out["user"];
|
||||
|
@ -109,7 +107,7 @@ class User {
|
|||
|
||||
public function auth(string $user = null, string $password = null): bool {
|
||||
if($user===null) {
|
||||
if($this->data->conf->userAuthPreferHTTP) return $this->authHTTP();
|
||||
if(Data::$conf->userAuthPreferHTTP) return $this->authHTTP();
|
||||
return $this->authForm();
|
||||
} else {
|
||||
$this->id = $user;
|
||||
|
@ -117,7 +115,7 @@ class User {
|
|||
switch($this->u->driverFunctions("auth")) {
|
||||
case User\Driver::FUNC_EXTERNAL:
|
||||
$out = $this->u->auth($user, $password);
|
||||
if($out && !$this->data->db->userExists($user)) $this->autoProvision($user, $password);
|
||||
if($out && !Data::$db->userExists($user)) $this->autoProvision($user, $password);
|
||||
return $out;
|
||||
case User\Driver::FUNC_INTERNAL:
|
||||
return $this->u->auth($user, $password);
|
||||
|
@ -176,7 +174,7 @@ class User {
|
|||
// we handle authorization checks for external drivers
|
||||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$out = $this->u->userExists($user);
|
||||
if($out && !$this->data->db->userExists($user)) $this->autoProvision($user, "");
|
||||
if($out && !Data::$db->userExists($user)) $this->autoProvision($user, "");
|
||||
return $out;
|
||||
case User\Driver::FUNC_INTERNAL:
|
||||
// internal functions handle their own authorization
|
||||
|
@ -195,7 +193,7 @@ class User {
|
|||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$newPassword = $this->u->userAdd($user, $password);
|
||||
// if there was no exception and we don't have the user in the internal database, add it
|
||||
if(!$this->data->db->userExists($user)) $this->autoProvision($user, $newPassword);
|
||||
if(!Data::$db->userExists($user)) $this->autoProvision($user, $newPassword);
|
||||
return $newPassword;
|
||||
case User\Driver::FUNC_INTERNAL:
|
||||
// internal functions handle their own authorization
|
||||
|
@ -212,9 +210,9 @@ class User {
|
|||
// we handle authorization checks for external drivers
|
||||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$out = $this->u->userRemove($user);
|
||||
if($out && $this->data->db->userExists($user)) {
|
||||
if($out && Data::$db->userExists($user)) {
|
||||
// if the user was removed and we have it in our data, remove it there
|
||||
if(!$this->data->db->userExists($user)) $this->data->db->userRemove($user);
|
||||
if(!Data::$db->userExists($user)) Data::$db->userRemove($user);
|
||||
}
|
||||
return $out;
|
||||
case User\Driver::FUNC_INTERNAL:
|
||||
|
@ -232,9 +230,9 @@ class User {
|
|||
// we handle authorization checks for external drivers
|
||||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$out = $this->u->userPasswordSet($user, $newPassword, $oldPassword);
|
||||
if($this->data->db->userExists($user)) {
|
||||
if(Data::$db->userExists($user)) {
|
||||
// if the password change was successful and the user exists, set the internal password to the same value
|
||||
$this->data->db->userPasswordSet($user, $out);
|
||||
Data::$db->userPasswordSet($user, $out);
|
||||
} else {
|
||||
// if the user does not exists in the internal database, create it
|
||||
$this->autoProvision($user, $out);
|
||||
|
@ -251,7 +249,7 @@ class User {
|
|||
public function propertiesGet(string $user): array {
|
||||
// prepare default values
|
||||
$domain = null;
|
||||
if($this->data->conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1);
|
||||
if(Data::$conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1);
|
||||
$init = [
|
||||
"id" => $user,
|
||||
"name" => $user,
|
||||
|
@ -267,7 +265,7 @@ class User {
|
|||
// remove password if it is return (not exhaustive, but...)
|
||||
if(array_key_exists('password', $out)) unset($out['password']);
|
||||
// if the user does not exist in the internal database, add it
|
||||
if(!$this->data->db->userExists($user)) $this->autoProvision($user, "", $out);
|
||||
if(!Data::$db->userExists($user)) $this->autoProvision($user, "", $out);
|
||||
return $out;
|
||||
case User\Driver::FUNC_INTERNAL:
|
||||
// internal functions handle their own authorization
|
||||
|
@ -289,9 +287,9 @@ class User {
|
|||
// we handle authorization checks for external drivers
|
||||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$out = $this->u->userPropertiesSet($user, $properties);
|
||||
if($this->data->db->userExists($user)) {
|
||||
if(Data::$db->userExists($user)) {
|
||||
// if the property change was successful and the user exists, set the internal properties to the same values
|
||||
$this->data->db->userPropertiesSet($user, $out);
|
||||
Data::$db->userPropertiesSet($user, $out);
|
||||
} else {
|
||||
// if the user does not exists in the internal database, create it
|
||||
$this->autoProvision($user, "", $out);
|
||||
|
@ -313,7 +311,7 @@ class User {
|
|||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$out = $this->u->userRightsGet($user);
|
||||
// if the user does not exist in the internal database, add it
|
||||
if(!$this->data->db->userExists($user)) $this->autoProvision($user, "", null, $out);
|
||||
if(!Data::$db->userExists($user)) $this->autoProvision($user, "", null, $out);
|
||||
return $out;
|
||||
case User\Driver::FUNC_INTERNAL:
|
||||
// internal functions handle their own authorization
|
||||
|
@ -332,10 +330,10 @@ class User {
|
|||
if(!$this->authorize($user, $func)) throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
|
||||
$out = $this->u->userRightsSet($user, $level);
|
||||
// if the user does not exist in the internal database, add it
|
||||
if($out && $this->data->db->userExists($user)) {
|
||||
if($out && Data::$db->userExists($user)) {
|
||||
$authz = $this->authorizationEnabled();
|
||||
$this->authorizationEnabled(false);
|
||||
$this->data->db->userRightsSet($user, $level);
|
||||
Data::$db->userRightsSet($user, $level);
|
||||
$this->authorizationEnabled($authz);
|
||||
} else if($out) {
|
||||
$this->autoProvision($user, "", null, $level);
|
||||
|
@ -367,18 +365,18 @@ class User {
|
|||
$authz = $this->authorizationEnabled();
|
||||
$this->authorizationEnabled(false);
|
||||
// create the user
|
||||
$out = $this->data->db->userAdd($user, $password);
|
||||
$out = Data::$db->userAdd($user, $password);
|
||||
// set the user rights
|
||||
$this->data->db->userRightsSet($user, $rights);
|
||||
Data::$db->userRightsSet($user, $rights);
|
||||
// set the user properties...
|
||||
if($properties===null) {
|
||||
// if nothing is provided but the driver uses an external function, try to get the current values from the external source
|
||||
try {
|
||||
if($this->u->driverFunctions("userPropertiesGet")==User\Driver::FUNC_EXTERNAL) $this->data->db->userPropertiesSet($user, $this->u->userPropertiesGet($user));
|
||||
if($this->u->driverFunctions("userPropertiesGet")==User\Driver::FUNC_EXTERNAL) Data::$db->userPropertiesSet($user, $this->u->userPropertiesGet($user));
|
||||
} catch(\Throwable $e) {}
|
||||
} else {
|
||||
// otherwise if values are provided, use those
|
||||
$this->data->db->userPropertiesSet($user, $properties);
|
||||
Data::$db->userPropertiesSet($user, $properties);
|
||||
}
|
||||
// re-enable authorization and return
|
||||
$this->authorizationEnabled($authz);
|
||||
|
|
|
@ -13,8 +13,8 @@ Interface Driver {
|
|||
const RIGHTS_GLOBAL_MANAGER = 75; // able to act for any normal users on any domain; cannot elevate other users
|
||||
const RIGHTS_GLOBAL_ADMIN = 100; // is completely unrestricted
|
||||
|
||||
// returns an instance of a class implementing this interface. Implemented as a static method for consistency with database classes
|
||||
function __construct(\JKingWeb\Arsse\RuntimeData $data);
|
||||
// returns an instance of a class implementing this interface.
|
||||
function __construct();
|
||||
// returns a human-friendly name for the driver (for display in installer, for example)
|
||||
static function driverName(): string;
|
||||
// returns an array (or single queried member of same) of methods defined by this interface and whether the class implements the internal function or a custom version
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\User\Internal;
|
||||
use JKingWeb\Arsse\Lang;
|
||||
use JKingWeb\Arsse\User\Driver as Iface;
|
||||
|
||||
final class Driver implements Iface {
|
||||
use InternalFunctions;
|
||||
|
||||
protected $data;
|
||||
protected $db;
|
||||
protected $functions = [
|
||||
"auth" => Iface::FUNC_INTERNAL,
|
||||
|
@ -22,12 +20,8 @@ final class Driver implements Iface {
|
|||
"userRightsSet" => Iface::FUNC_INTERNAL,
|
||||
];
|
||||
|
||||
static public function create(\JKingWeb\Arsse\RuntimeData $data): Driver {
|
||||
return new static($data);
|
||||
}
|
||||
|
||||
static public function driverName(): string {
|
||||
return Lang::msg("Driver.User.Internal.Name");
|
||||
return Data::$l->msg("Driver.User.Internal.Name");
|
||||
}
|
||||
|
||||
public function driverFunctions(string $function = null) {
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\User\Internal;
|
||||
use JKingWeb\Arsse\Data;
|
||||
|
||||
trait InternalFunctions {
|
||||
protected $actor = [];
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
$this->db = $this->data->db;
|
||||
public function __construct() {
|
||||
$this->db = Data::$db;
|
||||
}
|
||||
|
||||
function auth(string $user, string $password): bool {
|
||||
if(!$this->data->user->exists($user)) return false;
|
||||
if(!Data::$user->exists($user)) return false;
|
||||
$hash = $this->db->userPasswordGet($user);
|
||||
if($password==="" && $hash==="") return true;
|
||||
return password_verify($password, $hash);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
use \org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
|
||||
class TestConf extends \PHPUnit\Framework\TestCase {
|
||||
|
@ -10,7 +10,8 @@ class TestConf extends \PHPUnit\Framework\TestCase {
|
|||
static $vfs;
|
||||
static $path;
|
||||
|
||||
static function setUpBeforeClass() {
|
||||
function setUp() {
|
||||
$this->clearData();
|
||||
self::$vfs = vfsStream::setup("root", null, [
|
||||
'confGood' => '<?php return Array("lang" => "xx");',
|
||||
'confNotArray' => '<?php return 0;',
|
||||
|
@ -24,9 +25,10 @@ class TestConf extends \PHPUnit\Framework\TestCase {
|
|||
chmod(self::$path."confUnreadable", 0000);
|
||||
}
|
||||
|
||||
static function tearDownAfterClass() {
|
||||
function tearDown() {
|
||||
self::$path = null;
|
||||
self::$vfs = null;
|
||||
$this->clearData();
|
||||
}
|
||||
|
||||
function testLoadDefaultValues() {
|
||||
|
|
|
@ -10,20 +10,22 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
protected $drv;
|
||||
|
||||
function setUp() {
|
||||
$this->clearData();
|
||||
$conf = new Conf();
|
||||
$conf->dbDriver = Db\SQLite3\Driver::class;
|
||||
$conf->dbSQLite3File = tempnam(sys_get_temp_dir(), 'ook');
|
||||
$this->data = new Test\RuntimeData($conf);
|
||||
$this->drv = new Db\SQLite3\Driver($this->data, true);
|
||||
Data::$conf = $conf;
|
||||
$this->drv = new Db\SQLite3\Driver(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
unset($this->drv);
|
||||
unlink($this->data->conf->dbSQLite3File);
|
||||
unlink(Data::$conf->dbSQLite3File);
|
||||
$this->clearData();
|
||||
}
|
||||
|
||||
function testFetchDriverName() {
|
||||
$class = $this->data->conf->dbDriver;
|
||||
$class = Data::$conf->dbDriver;
|
||||
$this->assertTrue(strlen($class::driverName()) > 0);
|
||||
}
|
||||
|
||||
|
@ -38,12 +40,12 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
|
||||
function testExecMultipleStatements() {
|
||||
$this->assertTrue($this->drv->exec("CREATE TABLE test(id integer primary key); INSERT INTO test(id) values(2112)"));
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->assertEquals(2112, $ch->querySingle("SELECT id from test"));
|
||||
}
|
||||
|
||||
function testExecTimeout() {
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$ch->exec("BEGIN EXCLUSIVE TRANSACTION");
|
||||
$this->assertException("general", "Db", "ExceptionTimeout");
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
|
@ -71,7 +73,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
}
|
||||
|
||||
function testQueryTimeout() {
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$ch->exec("BEGIN EXCLUSIVE TRANSACTION");
|
||||
$this->assertException("general", "Db", "ExceptionTimeout");
|
||||
$this->drv->query("CREATE TABLE test(id integer primary key)");
|
||||
|
@ -102,7 +104,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testBeginTransaction() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -116,7 +118,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testCommitTransaction() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -130,7 +132,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testRollbackTransaction() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -144,7 +146,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testBeginChainedTransactions() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -159,7 +161,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testCommitChainedTransactions() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -178,7 +180,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testRollbackChainedTransactions() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -199,7 +201,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testPartiallyRollbackChainedTransactions() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -220,7 +222,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testFullyRollbackChainedTransactions() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
@ -238,7 +240,7 @@ class TestDbDriverSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
function testFullyCommitChainedTransactions() {
|
||||
$select = "SELECT count(*) FROM test";
|
||||
$insert = "INSERT INTO test(id) values(null)";
|
||||
$ch = new \SQLite3($this->data->conf->dbSQLite3File);
|
||||
$ch = new \SQLite3(Data::$conf->dbSQLite3File);
|
||||
$this->drv->exec("CREATE TABLE test(id integer primary key)");
|
||||
$this->drv->begin();
|
||||
$this->drv->query($insert);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
use \org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
|
||||
class TestDbUpdateSQLite3 extends \PHPUnit\Framework\TestCase {
|
||||
|
@ -16,20 +16,22 @@ class TestDbUpdateSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
const MINIMAL2 = "pragma user_version=2";
|
||||
|
||||
function setUp() {
|
||||
$this->clearData();
|
||||
$this->vfs = vfsStream::setup("schemata", null, ['SQLite3' => []]);
|
||||
$conf = new Conf();
|
||||
$conf->dbDriver = Db\SQLite3\Driver::class;
|
||||
$conf->dbSchemaBase = $this->vfs->url();
|
||||
$this->base = $this->vfs->url()."/SQLite3/";
|
||||
$conf->dbSQLite3File = ":memory:";
|
||||
$this->data = new Test\RuntimeData($conf);
|
||||
$this->drv = new Db\SQLite3\Driver($this->data, true);
|
||||
Data::$conf = $conf;
|
||||
$this->drv = new Db\SQLite3\Driver(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
unset($this->drv);
|
||||
unset($this->data);
|
||||
unset($this->vfs);
|
||||
$this->clearData();
|
||||
}
|
||||
|
||||
function testLoadMissingFile() {
|
||||
|
@ -82,7 +84,7 @@ class TestDbUpdateSQLite3 extends \PHPUnit\Framework\TestCase {
|
|||
}
|
||||
|
||||
function testPerformActualUpdate() {
|
||||
$this->data->conf->dbSchemaBase = (new Conf())->dbSchemaBase;
|
||||
Data::$conf->dbSchemaBase = (new Conf())->dbSchemaBase;
|
||||
$this->drv->schemaUpdate(Database::SCHEMA_VERSION);
|
||||
$this->assertEquals(Database::SCHEMA_VERSION, $this->drv->schemaVersion());
|
||||
}
|
||||
|
|
|
@ -6,12 +6,15 @@ namespace JKingWeb\Arsse;
|
|||
class TestException extends \PHPUnit\Framework\TestCase {
|
||||
use Test\Tools;
|
||||
|
||||
static function setUpBeforeClass() {
|
||||
Lang::set("");
|
||||
function setUp() {
|
||||
$this->clearData(false);
|
||||
$m = $this->getMockBuilder(Lang::class)->setMethods(['__invoke'])->getMock();
|
||||
$m->expects($this->any())->method("__invoke")->with($this->anything(), $this->anything())->will($this->returnValue(""));
|
||||
Data::$l = $m;
|
||||
}
|
||||
|
||||
static function tearDownAfterClass() {
|
||||
Lang::set(Lang::DEFAULT);
|
||||
function tearDown() {
|
||||
$this->clearData(true);
|
||||
}
|
||||
|
||||
function testBaseClass() {
|
||||
|
@ -51,14 +54,6 @@ class TestException extends \PHPUnit\Framework\TestCase {
|
|||
throw new Exception("testThisExceptionMessageDoesNotExist");
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testBaseClass
|
||||
*/
|
||||
function testBaseClassWithMissingMessage() {
|
||||
$this->assertException("stringMissing", "Lang");
|
||||
throw new Exception("invalid");
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testBaseClassWithUnknownCode
|
||||
*/
|
||||
|
@ -66,5 +61,4 @@ class TestException extends \PHPUnit\Framework\TestCase {
|
|||
$this->assertException("uncoded");
|
||||
throw new Lang\Exception("testThisExceptionMessageDoesNotExist");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,48 +1,47 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
use \org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
|
||||
class TestLang extends \PHPUnit\Framework\TestCase {
|
||||
use Test\Tools, Test\Lang\Setup;
|
||||
|
||||
static $vfs;
|
||||
static $path;
|
||||
static $files;
|
||||
static $defaultPath;
|
||||
public $files;
|
||||
public $path;
|
||||
public $l;
|
||||
|
||||
function testListLanguages() {
|
||||
$this->assertCount(sizeof(self::$files), Lang::list("en"));
|
||||
$this->assertCount(sizeof($this->files), $this->l->list("en"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testListLanguages
|
||||
*/
|
||||
function testSetLanguage() {
|
||||
$this->assertEquals("en", Lang::set("en"));
|
||||
$this->assertEquals("en_ca", Lang::set("en_ca"));
|
||||
$this->assertEquals("de", Lang::set("de_ch"));
|
||||
$this->assertEquals("en", Lang::set("en_gb_hixie"));
|
||||
$this->assertEquals("en_ca", Lang::set("en_ca_jking"));
|
||||
$this->assertEquals("en", Lang::set("es"));
|
||||
$this->assertEquals("", Lang::set(""));
|
||||
$this->assertEquals("en", $this->l->set("en"));
|
||||
$this->assertEquals("en_ca", $this->l->set("en_ca"));
|
||||
$this->assertEquals("de", $this->l->set("de_ch"));
|
||||
$this->assertEquals("en", $this->l->set("en_gb_hixie"));
|
||||
$this->assertEquals("en_ca", $this->l->set("en_ca_jking"));
|
||||
$this->assertEquals("en", $this->l->set("es"));
|
||||
$this->assertEquals("", $this->l->set(""));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSetLanguage
|
||||
*/
|
||||
function testLoadInternalStrings() {
|
||||
$this->assertEquals("", Lang::set("", true));
|
||||
$this->assertCount(sizeof(Lang::REQUIRED), Lang::dump());
|
||||
$this->assertEquals("", $this->l->set("", true));
|
||||
$this->assertCount(sizeof(Lang::REQUIRED), $this->l->dump());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testLoadInternalStrings
|
||||
*/
|
||||
function testLoadDefaultLanguage() {
|
||||
$this->assertEquals(Lang::DEFAULT, Lang::set(Lang::DEFAULT, true));
|
||||
$str = Lang::dump();
|
||||
$this->assertEquals(Lang::DEFAULT, $this->l->set(Lang::DEFAULT, true));
|
||||
$str = $this->l->dump();
|
||||
$this->assertArrayHasKey('Exception.JKingWeb/Arsse/Exception.uncoded', $str);
|
||||
$this->assertArrayHasKey('Test.presentText', $str);
|
||||
}
|
||||
|
@ -51,9 +50,9 @@ class TestLang extends \PHPUnit\Framework\TestCase {
|
|||
* @depends testLoadDefaultLanguage
|
||||
*/
|
||||
function testLoadSupplementaryLanguage() {
|
||||
Lang::set(Lang::DEFAULT, true);
|
||||
$this->assertEquals("ja", Lang::set("ja", true));
|
||||
$str = Lang::dump();
|
||||
$this->l->set(Lang::DEFAULT, true);
|
||||
$this->assertEquals("ja", $this->l->set("ja", true));
|
||||
$str = $this->l->dump();
|
||||
$this->assertArrayHasKey('Exception.JKingWeb/Arsse/Exception.uncoded', $str);
|
||||
$this->assertArrayHasKey('Test.presentText', $str);
|
||||
$this->assertArrayHasKey('Test.absentText', $str);
|
||||
|
|
|
@ -1,66 +1,64 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
use \org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
|
||||
class TestLangErrors extends \PHPUnit\Framework\TestCase {
|
||||
use Test\Tools, Test\Lang\Setup;
|
||||
|
||||
static $vfs;
|
||||
static $path;
|
||||
static $files;
|
||||
static $defaultPath;
|
||||
public $files;
|
||||
public $path;
|
||||
public $l;
|
||||
|
||||
function setUp() {
|
||||
Lang::set("", true);
|
||||
function setUpSeries() {
|
||||
$this->l->set("", true);
|
||||
}
|
||||
|
||||
function testLoadEmptyFile() {
|
||||
$this->assertException("fileCorrupt", "Lang");
|
||||
Lang::set("fr_ca", true);
|
||||
$this->l->set("fr_ca", true);
|
||||
}
|
||||
|
||||
function testLoadFileWhichDoesNotReturnAnArray() {
|
||||
$this->assertException("fileCorrupt", "Lang");
|
||||
Lang::set("it", true);
|
||||
$this->l->set("it", true);
|
||||
}
|
||||
|
||||
function testLoadFileWhichIsNotPhp() {
|
||||
$this->assertException("fileCorrupt", "Lang");
|
||||
Lang::set("ko", true);
|
||||
$this->l->set("ko", true);
|
||||
}
|
||||
|
||||
function testLoadFileWhichIsCorrupt() {
|
||||
$this->assertException("fileCorrupt", "Lang");
|
||||
Lang::set("zh", true);
|
||||
$this->l->set("zh", true);
|
||||
}
|
||||
|
||||
function testLoadFileWithooutReadPermission() {
|
||||
$this->assertException("fileUnreadable", "Lang");
|
||||
Lang::set("ru", true);
|
||||
$this->l->set("ru", true);
|
||||
}
|
||||
|
||||
function testLoadSubtagOfMissingLanguage() {
|
||||
$this->assertException("fileMissing", "Lang");
|
||||
Lang::set("pt_br", true);
|
||||
$this->l->set("pt_br", true);
|
||||
}
|
||||
|
||||
function testFetchInvalidMessage() {
|
||||
$this->assertException("stringInvalid", "Lang");
|
||||
Lang::set("vi", true);
|
||||
$txt = Lang::msg('Test.presentText');
|
||||
$this->l->set("vi", true);
|
||||
$txt = $this->l->msg('Test.presentText');
|
||||
}
|
||||
|
||||
function testFetchMissingMessage() {
|
||||
$this->assertException("stringMissing", "Lang");
|
||||
$txt = Lang::msg('Test.absentText');
|
||||
$txt = $this->l->msg('Test.absentText');
|
||||
}
|
||||
|
||||
function testLoadMissingDefaultLanguage() {
|
||||
// this should be the last test of the series
|
||||
unlink(self::$path.Lang::DEFAULT.".php");
|
||||
unlink($this->path.Lang::DEFAULT.".php");
|
||||
$this->assertException("defaultFileMissing", "Lang");
|
||||
Lang::set("fr", true);
|
||||
$this->l->set("fr", true);
|
||||
}
|
||||
}
|
|
@ -1,40 +1,39 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse;
|
||||
use \org\bovigo\vfs\vfsStream;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
|
||||
|
||||
class TestLangComplex extends \PHPUnit\Framework\TestCase {
|
||||
use Test\Tools, Test\Lang\Setup;
|
||||
|
||||
static $vfs;
|
||||
static $path;
|
||||
static $files;
|
||||
static $defaultPath;
|
||||
public $files;
|
||||
public $path;
|
||||
public $l;
|
||||
|
||||
function setUp() {
|
||||
Lang::set(Lang::DEFAULT, true);
|
||||
function setUpSeries() {
|
||||
$this->l->set(Lang::DEFAULT, true);
|
||||
}
|
||||
|
||||
function testLazyLoad() {
|
||||
Lang::set("ja");
|
||||
$this->assertArrayNotHasKey('Test.absentText', Lang::dump());
|
||||
$this->l->set("ja");
|
||||
$this->assertArrayNotHasKey('Test.absentText', $this->l->dump());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testLazyLoad
|
||||
*/
|
||||
function testGetWantedAndLoadedLocale() {
|
||||
Lang::set("en", true);
|
||||
Lang::set("ja");
|
||||
$this->assertEquals("ja", Lang::get());
|
||||
$this->assertEquals("en", Lang::get(true));
|
||||
$this->l->set("en", true);
|
||||
$this->l->set("ja");
|
||||
$this->assertEquals("ja", $this->l->get());
|
||||
$this->assertEquals("en", $this->l->get(true));
|
||||
}
|
||||
|
||||
function testLoadCascadeOfFiles() {
|
||||
Lang::set("ja", true);
|
||||
$this->assertEquals("de", Lang::set("de", true));
|
||||
$str = Lang::dump();
|
||||
$this->l->set("ja", true);
|
||||
$this->assertEquals("de", $this->l->set("de", true));
|
||||
$str = $this->l->dump();
|
||||
$this->assertArrayNotHasKey('Test.absentText', $str);
|
||||
$this->assertEquals('und der Stein der Weisen', $str['Test.presentText']);
|
||||
}
|
||||
|
@ -43,62 +42,62 @@ class TestLangComplex extends \PHPUnit\Framework\TestCase {
|
|||
* @depends testLoadCascadeOfFiles
|
||||
*/
|
||||
function testLoadSubtag() {
|
||||
$this->assertEquals("en_ca", Lang::set("en_ca", true));
|
||||
$this->assertEquals("en_ca", $this->l->set("en_ca", true));
|
||||
}
|
||||
|
||||
function testFetchAMessage() {
|
||||
Lang::set("de", true);
|
||||
$this->assertEquals('und der Stein der Weisen', Lang::msg('Test.presentText'));
|
||||
$this->l->set("de", true);
|
||||
$this->assertEquals('und der Stein der Weisen', $this->l->msg('Test.presentText'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFetchAMessage
|
||||
*/
|
||||
function testFetchAMessageWithMissingParameters() {
|
||||
Lang::set("en_ca", true);
|
||||
$this->assertEquals('{0} and {1}', Lang::msg('Test.presentText'));
|
||||
$this->l->set("en_ca", true);
|
||||
$this->assertEquals('{0} and {1}', $this->l->msg('Test.presentText'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFetchAMessage
|
||||
*/
|
||||
function testFetchAMessageWithSingleNumericParameter() {
|
||||
Lang::set("en_ca", true);
|
||||
$this->assertEquals('Default language file "en" missing', Lang::msg('Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing', Lang::DEFAULT));
|
||||
$this->l->set("en_ca", true);
|
||||
$this->assertEquals('Default language file "en" missing', $this->l->msg('Exception.JKingWeb/Arsse/Lang/Exception.defaultFileMissing', Lang::DEFAULT));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFetchAMessage
|
||||
*/
|
||||
function testFetchAMessageWithMultipleNumericParameters() {
|
||||
Lang::set("en_ca", true);
|
||||
$this->assertEquals('Happy Rotter and the Philosopher\'s Stone', Lang::msg('Test.presentText', ['Happy Rotter', 'the Philosopher\'s Stone']));
|
||||
$this->l->set("en_ca", true);
|
||||
$this->assertEquals('Happy Rotter and the Philosopher\'s Stone', $this->l->msg('Test.presentText', ['Happy Rotter', 'the Philosopher\'s Stone']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFetchAMessage
|
||||
*/
|
||||
function testFetchAMessageWithNamedParameters() {
|
||||
$this->assertEquals('Message string "Test.absentText" missing from all loaded language files (en)', Lang::msg('Exception.JKingWeb/Arsse/Lang/Exception.stringMissing', ['msgID' => 'Test.absentText', 'fileList' => 'en']));
|
||||
$this->assertEquals('Message string "Test.absentText" missing from all loaded language files (en)', $this->l->msg('Exception.JKingWeb/Arsse/Lang/Exception.stringMissing', ['msgID' => 'Test.absentText', 'fileList' => 'en']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFetchAMessage
|
||||
*/
|
||||
function testReloadDefaultStrings() {
|
||||
Lang::set("de", true);
|
||||
Lang::set("en", true);
|
||||
$this->assertEquals('and the Philosopher\'s Stone', Lang::msg('Test.presentText'));
|
||||
$this->l->set("de", true);
|
||||
$this->l->set("en", true);
|
||||
$this->assertEquals('and the Philosopher\'s Stone', $this->l->msg('Test.presentText'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testFetchAMessage
|
||||
*/
|
||||
function testReloadGeneralTagAfterSubtag() {
|
||||
Lang::set("en", true);
|
||||
Lang::set("en_us", true);
|
||||
$this->assertEquals('and the Sorcerer\'s Stone', Lang::msg('Test.presentText'));
|
||||
Lang::set("en", true);
|
||||
$this->assertEquals('and the Philosopher\'s Stone', Lang::msg('Test.presentText'));
|
||||
$this->l->set("en", true);
|
||||
$this->l->set("en_us", true);
|
||||
$this->assertEquals('and the Sorcerer\'s Stone', $this->l->msg('Test.presentText'));
|
||||
$this->l->set("en", true);
|
||||
$this->assertEquals('and the Philosopher\'s Stone', $this->l->msg('Test.presentText'));
|
||||
}
|
||||
}
|
|
@ -9,13 +9,12 @@ class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase {
|
|||
use Test\Tools;
|
||||
|
||||
function setUp() {
|
||||
$conf = new Conf();
|
||||
$this->data = new Test\RuntimeData($conf);
|
||||
$this->clearData();
|
||||
}
|
||||
|
||||
function testFetchVersionList() {
|
||||
$exp = new Response(200, ['apiLevels' => ['v1-2']]);
|
||||
$h = new Rest\NextCloudNews\Versions($this->data);
|
||||
$h = new Rest\NextCloudNews\Versions();
|
||||
$req = new Request("GET", "/");
|
||||
$res = $h->dispatch($req);
|
||||
$this->assertEquals($exp, $res);
|
||||
|
@ -29,7 +28,7 @@ class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase {
|
|||
|
||||
function testUseIncorrectMethod() {
|
||||
$exp = new Response(405);
|
||||
$h = new Rest\NextCloudNews\Versions($this->data);
|
||||
$h = new Rest\NextCloudNews\Versions();
|
||||
$req = new Request("POST", "/");
|
||||
$res = $h->dispatch($req);
|
||||
$this->assertEquals($exp, $res);
|
||||
|
@ -37,7 +36,7 @@ class TestNCNVersionDiscovery extends \PHPUnit\Framework\TestCase {
|
|||
|
||||
function testUseIncorrectPath() {
|
||||
$exp = new Response(404);
|
||||
$h = new Rest\NextCloudNews\Versions($this->data);
|
||||
$h = new Rest\NextCloudNews\Versions();
|
||||
$req = new Request("GET", "/ook");
|
||||
$res = $h->dispatch($req);
|
||||
$this->assertEquals($exp, $res);
|
||||
|
|
|
@ -46,53 +46,58 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
protected $data;
|
||||
|
||||
function setUp(string $drv = Test\User\DriverInternalMock::class, string $db = null) {
|
||||
$this->clearData();
|
||||
$conf = new Conf();
|
||||
$conf->userDriver = $drv;
|
||||
$conf->userAuthPreferHTTP = true;
|
||||
$conf->userComposeNames = true;
|
||||
$this->data = new Test\RuntimeData($conf);
|
||||
Data::$conf = $conf;
|
||||
if($db !== null) {
|
||||
$this->data->db = new $db($this->data);
|
||||
Data::$db = new $db();
|
||||
}
|
||||
$this->data->user = new User($this->data);
|
||||
$this->data->user->authorizationEnabled(false);
|
||||
Data::$user = new User();
|
||||
Data::$user->authorizationEnabled(false);
|
||||
foreach(self::USERS as $user => $level) {
|
||||
$this->data->user->add($user, "");
|
||||
$this->data->user->rightsSet($user, $level);
|
||||
Data::$user->add($user, "");
|
||||
Data::$user->rightsSet($user, $level);
|
||||
}
|
||||
$this->data->user->authorizationEnabled(true);
|
||||
Data::$user->authorizationEnabled(true);
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
$this->clearData();
|
||||
}
|
||||
|
||||
function testSelfActionLogic() {
|
||||
foreach(array_keys(self::USERS) as $user) {
|
||||
$this->data->user->auth($user, "");
|
||||
Data::$user->auth($user, "");
|
||||
// users should be able to do basic actions for themselves
|
||||
$this->assertTrue($this->data->user->authorize($user, "userExists"), "User $user could not act for themselves.");
|
||||
$this->assertTrue($this->data->user->authorize($user, "userRemove"), "User $user could not act for themselves.");
|
||||
$this->assertTrue(Data::$user->authorize($user, "userExists"), "User $user could not act for themselves.");
|
||||
$this->assertTrue(Data::$user->authorize($user, "userRemove"), "User $user could not act for themselves.");
|
||||
}
|
||||
}
|
||||
|
||||
function testRegularUserLogic() {
|
||||
foreach(self::USERS as $actor => $rights) {
|
||||
if($rights != User\Driver::RIGHTS_NONE) continue;
|
||||
$this->data->user->auth($actor, "");
|
||||
Data::$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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,36 +106,36 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
foreach(self::USERS as $actor => $actorRights) {
|
||||
if($actorRights != User\Driver::RIGHTS_DOMAIN_MANAGER) continue;
|
||||
$actorDomain = substr($actor,strrpos($actor,"@")+1);
|
||||
$this->data->user->auth($actor, "");
|
||||
Data::$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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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, User\Driver::RIGHTS_DOMAIN_MANAGER])) {
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,37 +145,37 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
foreach(self::USERS as $actor => $actorRights) {
|
||||
if($actorRights != User\Driver::RIGHTS_DOMAIN_ADMIN) continue;
|
||||
$actorDomain = substr($actor,strrpos($actor,"@")+1);
|
||||
$this->data->user->auth($actor, "");
|
||||
Data::$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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,29 +185,29 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
foreach(self::USERS as $actor => $actorRights) {
|
||||
if($actorRights != User\Driver::RIGHTS_GLOBAL_MANAGER) continue;
|
||||
$actorDomain = substr($actor,strrpos($actor,"@")+1);
|
||||
$this->data->user->auth($actor, "");
|
||||
Data::$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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$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($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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, User\Driver::RIGHTS_GLOBAL_MANAGER])) {
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,17 +215,17 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
function testGlobalAdministratorLogic() {
|
||||
foreach(self::USERS as $actor => $actorRights) {
|
||||
if($actorRights != User\Driver::RIGHTS_GLOBAL_ADMIN) continue;
|
||||
$this->data->user->auth($actor, "");
|
||||
Data::$user->auth($actor, "");
|
||||
// global admins can do anything
|
||||
foreach(self::USERS as $affected => $affectedRights) {
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
foreach(self::LEVELS as $level) {
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted properly for $affected settings rights level $level, but the action was denied.");
|
||||
$this->assertTrue(Data::$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($this->data->user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($domain, "userList"), "User $actor properly checked user list for domain '$domain', but the action was denied.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -228,24 +233,24 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
function testInvalidLevelLogic() {
|
||||
foreach(self::USERS as $actor => $rights) {
|
||||
if(in_array($rights, self::LEVELS)) continue;
|
||||
$this->data->user->auth($actor, "");
|
||||
Data::$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($this->data->user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue($this->data->user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userExists"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
$this->assertTrue(Data::$user->authorize($affected, "userRemove"), "User $actor acted properly for $affected, but the action was denied.");
|
||||
} else {
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse($this->data->user->authorize($affected, "userRemove"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$user->authorize($affected, "userExists"), "User $actor acted improperly for $affected, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($affected, "userRightsSet", $level), "User $actor acted improperly for $affected settings rights level $level, but the action was allowed.");
|
||||
$this->assertFalse(Data::$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($this->data->user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
$this->assertFalse(Data::$user->authorize($domain, "userList"), "User $actor improperly checked user list for domain '$domain', but the action was allowed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -264,10 +269,10 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
'list' => [],
|
||||
];
|
||||
// try first with a global admin (there should be no exception)
|
||||
$this->data->user->auth("gadm@example.com", "");
|
||||
Data::$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)
|
||||
$this->data->user->auth("user@example.com", "");
|
||||
Data::$user->auth("user@example.com", "");
|
||||
$this->assertCount(sizeof($tests), $this->checkExceptions("user@example.org", $tests));
|
||||
}
|
||||
|
||||
|
@ -286,7 +291,7 @@ class TestAuthorization extends \PHPUnit\Framework\TestCase {
|
|||
// 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($this->data->user, $func), $args);
|
||||
call_user_func_array(array(Data::$user, $func), $args);
|
||||
} catch(User\ExceptionAuthz $e) {
|
||||
$err[] = $func;
|
||||
}
|
||||
|
|
|
@ -9,18 +9,5 @@ class TestUserInternalDriver extends \PHPUnit\Framework\TestCase {
|
|||
const USER1 = "john.doe@example.com";
|
||||
const USER2 = "jane.doe@example.com";
|
||||
|
||||
protected $data;
|
||||
|
||||
function setUp() {
|
||||
$drv = User\Internal\Driver::class;
|
||||
$conf = new Conf();
|
||||
$conf->userDriver = $drv;
|
||||
$conf->userAuthPreferHTTP = true;
|
||||
$this->data = new Test\RuntimeData($conf);
|
||||
$this->data->db = new Test\User\Database($this->data);
|
||||
$this->data->user = new User($this->data);
|
||||
$this->data->user->authorizationEnabled(false);
|
||||
$_SERVER['PHP_AUTH_USER'] = self::USER1;
|
||||
$_SERVER['PHP_AUTH_PW'] = "secret";
|
||||
}
|
||||
public $drv = User\Internal\Driver::class;
|
||||
}
|
||||
|
|
|
@ -9,18 +9,5 @@ class TestUserMockExternal extends \PHPUnit\Framework\TestCase {
|
|||
const USER1 = "john.doe@example.com";
|
||||
const USER2 = "jane.doe@example.com";
|
||||
|
||||
protected $data;
|
||||
|
||||
function setUp() {
|
||||
$drv = Test\User\DriverExternalMock::class;
|
||||
$conf = new Conf();
|
||||
$conf->userDriver = $drv;
|
||||
$conf->userAuthPreferHTTP = true;
|
||||
$this->data = new Test\RuntimeData($conf);
|
||||
$this->data->db = new Test\User\Database($this->data);
|
||||
$this->data->user = new User($this->data);
|
||||
$this->data->user->authorizationEnabled(false);
|
||||
$_SERVER['PHP_AUTH_USER'] = self::USER1;
|
||||
$_SERVER['PHP_AUTH_PW'] = "secret";
|
||||
}
|
||||
public $drv = Test\User\DriverExternalMock::class;
|
||||
}
|
|
@ -9,17 +9,9 @@ class TestUserMockInternal extends \PHPUnit\Framework\TestCase {
|
|||
const USER1 = "john.doe@example.com";
|
||||
const USER2 = "jane.doe@example.com";
|
||||
|
||||
protected $data;
|
||||
public $drv = Test\User\DriverInternalMock::class;
|
||||
|
||||
function setUp() {
|
||||
$drv = Test\User\DriverInternalMock::class;
|
||||
$conf = new Conf();
|
||||
$conf->userDriver = $drv;
|
||||
$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";
|
||||
function setUpSeries() {
|
||||
Data::$db = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,16 @@ declare(strict_types=1);
|
|||
namespace JKingWeb\Arsse\Test\Db;
|
||||
|
||||
trait Tools {
|
||||
function prime(\JKingWeb\Arsse\Db\Driver $drv, array $data): bool {
|
||||
protected $drv;
|
||||
|
||||
|
||||
function prime(array $data): bool {
|
||||
$drv->begin();
|
||||
foreach($data as $table => $info) {
|
||||
$cols = implode(",", array_keys($info['columns']));
|
||||
$bindings = array_values($info['columns']);
|
||||
$params = implode(",", array_fill(0, sizeof($info['columns']), "?"));
|
||||
$s = $drv->prepareArray("INSERT INTO $table($cols) values($params)", $bindings);
|
||||
$s = $this->drv->prepareArray("INSERT INTO $table($cols) values($params)", $bindings);
|
||||
foreach($info['rows'] as $row) {
|
||||
$this->assertEquals(1, $s->runArray($row)->changes());
|
||||
}
|
||||
|
@ -18,13 +21,17 @@ trait Tools {
|
|||
return true;
|
||||
}
|
||||
|
||||
function compare(\JKingWeb\Arsse\Db\Driver $drv, array $expected): bool {
|
||||
function compare(array $expected): bool {
|
||||
foreach($expected as $table => $info) {
|
||||
$cols = implode(",", array_keys($info['columns']));
|
||||
foreach($drv->prepare("SELECT $cols from $table")->run() as $num => $row) {
|
||||
foreach($this->drv->prepare("SELECT $cols from $table")->run() as $num => $row) {
|
||||
$row = array_values($row);
|
||||
$assertSame($expected[$table]['rows'][$num], $row, "Row $num of table $table does not match expectation.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,16 +1,16 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test\Lang;
|
||||
use \org\bovigo\vfs\vfsStream, \JKingWeb\Arsse\Lang;
|
||||
use org\bovigo\vfs\vfsStream;
|
||||
use JKingWeb\Arsse\Lang;
|
||||
use JKingWeb\Arsse\Data;
|
||||
|
||||
|
||||
|
||||
trait Setup {
|
||||
static function setUpBeforeClass() {
|
||||
// this is required to keep from having exceptions in Lang::msg() in turn calling Lang::msg() and looping
|
||||
\JKingWeb\Arsse\Lang\Exception::$test = true;
|
||||
function setUp() {
|
||||
// test files
|
||||
self::$files = [
|
||||
$this->files = [
|
||||
'en.php' => '<?php return ["Test.presentText" => "and the Philosopher\'s Stone"];',
|
||||
'en_ca.php' => '<?php return ["Test.presentText" => "{0} and {1}"];',
|
||||
'en_us.php' => '<?php return ["Test.presentText" => "and the Sorcerer\'s Stone"];',
|
||||
|
@ -28,22 +28,24 @@ trait Setup {
|
|||
// unreadable file
|
||||
'ru.php' => '',
|
||||
];
|
||||
self::$vfs = vfsStream::setup("langtest", 0777, self::$files);
|
||||
self::$path = self::$vfs->url()."/";
|
||||
$vfs = vfsStream::setup("langtest", 0777, $this->files);
|
||||
$this->path = $vfs->url()."/";
|
||||
// set up a file without read access
|
||||
chmod(self::$path."ru.php", 0000);
|
||||
// make the Lang class use the vfs files
|
||||
self::$defaultPath = Lang::$path;
|
||||
Lang::$path = self::$path;
|
||||
chmod($this->path."ru.php", 0000);
|
||||
// make the test Lang class use the vfs files
|
||||
$this->l = new Lang($this->path);
|
||||
// create a mock Lang object to keep exceptions from creating loops
|
||||
$this->clearData(false);
|
||||
$m = $this->getMockBuilder(Lang::class)->setMethods(['__invoke'])->getMock();
|
||||
$m->expects($this->any())->method("__invoke")->with($this->anything(), $this->anything())->will($this->returnValue(""));
|
||||
Data::$l = $m;
|
||||
// call the additional setup method if it exists
|
||||
if(method_exists($this, "setUpSeries")) $this->setUpSeries();
|
||||
}
|
||||
|
||||
static function tearDownAfterClass() {
|
||||
\JKingWeb\Arsse\Lang\Exception::$test = false;
|
||||
Lang::$path = self::$defaultPath;
|
||||
self::$path = null;
|
||||
self::$vfs = null;
|
||||
self::$files = null;
|
||||
Lang::set("", true);
|
||||
Lang::set(Lang::DEFAULT);
|
||||
function tearDown() {
|
||||
$this->clearData(true);
|
||||
// call the additional teardiwn method if it exists
|
||||
if(method_exists($this, "tearDownSeries")) $this->tearDownSeries();
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test;
|
||||
|
||||
class RuntimeData extends \JKingWeb\Arsse\RuntimeData {
|
||||
public $conf;
|
||||
public $db;
|
||||
public $user;
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\Conf $conf = null) {
|
||||
$this->conf = $conf;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test;
|
||||
use \JKingWeb\Arsse\Exception;
|
||||
use JKingWeb\Arsse\Exception;
|
||||
use JKingWeb\Arsse\Data;
|
||||
|
||||
trait Tools {
|
||||
function assertException(string $msg, string $prefix = "", string $type = "Exception") {
|
||||
|
@ -15,4 +16,16 @@ trait Tools {
|
|||
$this->expectException($class);
|
||||
$this->expectExceptionCode($code);
|
||||
}
|
||||
|
||||
function clearData(bool $loadLang = true): bool {
|
||||
$r = new \ReflectionClass(\JKingWeb\Arsse\Data::class);
|
||||
$props = array_keys($r->getStaticProperties());
|
||||
foreach($props as $prop) {
|
||||
Data::$$prop = null;
|
||||
}
|
||||
if($loadLang) {
|
||||
Data::$l = new \JKingWeb\Arsse\Lang();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,80 +1,105 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test\User;
|
||||
use JKingWeb\Arsse\Data;
|
||||
use JKingWeb\Arsse\Conf;
|
||||
use JKingWeb\Arsse\User;
|
||||
use JKingWeb\Arsse\User\Driver;
|
||||
|
||||
trait CommonTests {
|
||||
|
||||
function setUp() {
|
||||
$this->clearData();
|
||||
$conf = new Conf();
|
||||
$conf->userDriver = $this->drv;
|
||||
$conf->userAuthPreferHTTP = true;
|
||||
Data::$conf = $conf;
|
||||
Data::$db = new Database();
|
||||
Data::$user = new User();
|
||||
Data::$user->authorizationEnabled(false);
|
||||
$_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();
|
||||
}
|
||||
|
||||
function tearDown() {
|
||||
$this->clearData();
|
||||
// call the additional teardiwn method if it exists
|
||||
if(method_exists($this, "tearDownSeries")) $this->tearDownSeries();
|
||||
}
|
||||
|
||||
function testListUsers() {
|
||||
$this->assertCount(0,$this->data->user->list());
|
||||
$this->assertCount(0,Data::$user->list());
|
||||
}
|
||||
|
||||
function testCheckIfAUserDoesNotExist() {
|
||||
$this->assertFalse($this->data->user->exists(self::USER1));
|
||||
$this->assertFalse(Data::$user->exists(self::USER1));
|
||||
}
|
||||
|
||||
function testAddAUser() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
$this->assertCount(1,$this->data->user->list());
|
||||
Data::$user->add(self::USER1, "");
|
||||
$this->assertCount(1,Data::$user->list());
|
||||
}
|
||||
|
||||
function testCheckIfAUserDoesExist() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
$this->assertTrue($this->data->user->exists(self::USER1));
|
||||
Data::$user->add(self::USER1, "");
|
||||
$this->assertTrue(Data::$user->exists(self::USER1));
|
||||
}
|
||||
|
||||
function testAddADuplicateUser() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
Data::$user->add(self::USER1, "");
|
||||
$this->assertException("alreadyExists", "User");
|
||||
$this->data->user->add(self::USER1, "");
|
||||
Data::$user->add(self::USER1, "");
|
||||
}
|
||||
|
||||
function testAddMultipleUsers() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
$this->data->user->add(self::USER2, "");
|
||||
$this->assertCount(2,$this->data->user->list());
|
||||
Data::$user->add(self::USER1, "");
|
||||
Data::$user->add(self::USER2, "");
|
||||
$this->assertCount(2,Data::$user->list());
|
||||
}
|
||||
|
||||
function testRemoveAUser() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
$this->assertCount(1,$this->data->user->list());
|
||||
$this->data->user->remove(self::USER1);
|
||||
$this->assertCount(0,$this->data->user->list());
|
||||
Data::$user->add(self::USER1, "");
|
||||
$this->assertCount(1,Data::$user->list());
|
||||
Data::$user->remove(self::USER1);
|
||||
$this->assertCount(0,Data::$user->list());
|
||||
}
|
||||
|
||||
function testRemoveAMissingUser() {
|
||||
$this->assertException("doesNotExist", "User");
|
||||
$this->data->user->remove(self::USER1);
|
||||
Data::$user->remove(self::USER1);
|
||||
}
|
||||
|
||||
function testAuthenticateAUser() {
|
||||
$_SERVER['PHP_AUTH_USER'] = self::USER1;
|
||||
$_SERVER['PHP_AUTH_PW'] = "secret";
|
||||
$this->data->user->add(self::USER1, "secret");
|
||||
$this->data->user->add(self::USER2, "");
|
||||
$this->assertTrue($this->data->user->auth());
|
||||
$this->assertTrue($this->data->user->auth(self::USER1, "secret"));
|
||||
$this->assertFalse($this->data->user->auth(self::USER1, "superman"));
|
||||
$this->assertTrue($this->data->user->auth(self::USER2, ""));
|
||||
Data::$user->add(self::USER1, "secret");
|
||||
Data::$user->add(self::USER2, "");
|
||||
$this->assertTrue(Data::$user->auth());
|
||||
$this->assertTrue(Data::$user->auth(self::USER1, "secret"));
|
||||
$this->assertFalse(Data::$user->auth(self::USER1, "superman"));
|
||||
$this->assertTrue(Data::$user->auth(self::USER2, ""));
|
||||
}
|
||||
|
||||
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->user->passwordSet(self::USER1, ""));
|
||||
$this->assertTrue($this->data->user->auth(self::USER1, ""));
|
||||
$this->assertEquals($this->data->conf->userTempPasswordLength, strlen($this->data->user->passwordSet(self::USER1)));
|
||||
Data::$user->add(self::USER1, "secret");
|
||||
$this->assertEquals("superman", Data::$user->passwordSet(self::USER1, "superman"));
|
||||
$this->assertTrue(Data::$user->auth(self::USER1, "superman"));
|
||||
$this->assertFalse(Data::$user->auth(self::USER1, "secret"));
|
||||
$this->assertEquals("", Data::$user->passwordSet(self::USER1, ""));
|
||||
$this->assertTrue(Data::$user->auth(self::USER1, ""));
|
||||
$this->assertEquals(Data::$conf->userTempPasswordLength, strlen(Data::$user->passwordSet(self::USER1)));
|
||||
}
|
||||
|
||||
function testChangeAPasswordForAMissingUser() {
|
||||
$this->assertException("doesNotExist", "User");
|
||||
$this->data->user->passwordSet(self::USER1, "superman");
|
||||
Data::$user->passwordSet(self::USER1, "superman");
|
||||
}
|
||||
|
||||
function testGetThePropertiesOfAUser() {
|
||||
$this->data->user->add(self::USER1, "secret");
|
||||
$p = $this->data->user->propertiesGet(self::USER1);
|
||||
Data::$user->add(self::USER1, "secret");
|
||||
$p = Data::$user->propertiesGet(self::USER1);
|
||||
$this->assertArrayHasKey('id', $p);
|
||||
$this->assertArrayHasKey('name', $p);
|
||||
$this->assertArrayHasKey('domain', $p);
|
||||
|
@ -97,22 +122,22 @@ trait CommonTests {
|
|||
'domain' => 'example.com',
|
||||
'rights' => Driver::RIGHTS_NONE,
|
||||
];
|
||||
$this->data->user->add(self::USER1, "secret");
|
||||
$this->data->user->propertiesSet(self::USER1, $pSet);
|
||||
$p = $this->data->user->propertiesGet(self::USER1);
|
||||
Data::$user->add(self::USER1, "secret");
|
||||
Data::$user->propertiesSet(self::USER1, $pSet);
|
||||
$p = Data::$user->propertiesGet(self::USER1);
|
||||
$this->assertArraySubset($pGet, $p);
|
||||
$this->assertArrayNotHasKey('password', $p);
|
||||
$this->assertFalse($this->data->user->auth(self::USER1, "superman"));
|
||||
$this->assertFalse(Data::$user->auth(self::USER1, "superman"));
|
||||
}
|
||||
|
||||
function testGetTheRightsOfAUser() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
$this->assertEquals(Driver::RIGHTS_NONE, $this->data->user->rightsGet(self::USER1));
|
||||
Data::$user->add(self::USER1, "");
|
||||
$this->assertEquals(Driver::RIGHTS_NONE, Data::$user->rightsGet(self::USER1));
|
||||
}
|
||||
|
||||
function testSetTheRightsOfAUser() {
|
||||
$this->data->user->add(self::USER1, "");
|
||||
$this->data->user->rightsSet(self::USER1, Driver::RIGHTS_GLOBAL_ADMIN);
|
||||
$this->assertEquals(Driver::RIGHTS_GLOBAL_ADMIN, $this->data->user->rightsGet(self::USER1));
|
||||
Data::$user->add(self::USER1, "");
|
||||
Data::$user->rightsSet(self::USER1, Driver::RIGHTS_GLOBAL_ADMIN);
|
||||
$this->assertEquals(Driver::RIGHTS_GLOBAL_ADMIN, Data::$user->rightsGet(self::USER1));
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test\User;
|
||||
use JKingWeb\Arsse\Data;
|
||||
use JKingWeb\Arsse\User\Driver;
|
||||
use JKingWeb\Arsse\User\Exception;
|
||||
use JKingWeb\Arsse\User\ExceptionAuthz;
|
||||
|
@ -10,48 +11,47 @@ class Database extends DriverSkeleton {
|
|||
|
||||
public $db = [];
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
function userExists(string $user): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!Data::$user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
return parent::userExists($user);
|
||||
}
|
||||
|
||||
function userAdd(string $user, string $password = null): string {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!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();
|
||||
if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get();
|
||||
return parent::userAdd($user, $password);
|
||||
}
|
||||
|
||||
function userRemove(string $user): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!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 parent::userRemove($user);
|
||||
}
|
||||
|
||||
function userList(string $domain = null): array {
|
||||
if($domain===null) {
|
||||
if(!$this->data->user->authorize("", __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]);
|
||||
if(!Data::$user->authorize("", __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "global"]);
|
||||
return parent::userList();
|
||||
} else {
|
||||
$suffix = '@'.$domain;
|
||||
if(!$this->data->user->authorize($suffix, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]);
|
||||
if(!Data::$user->authorize($suffix, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]);
|
||||
return parent::userList($domain);
|
||||
}
|
||||
}
|
||||
|
||||
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(!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();
|
||||
if($newPassword===null) $newPassword = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get();
|
||||
return parent::userPasswordSet($user, $newPassword);
|
||||
}
|
||||
|
||||
function userPropertiesGet(string $user): array {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!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]);
|
||||
$out = parent::userPropertiesGet($user);
|
||||
unset($out['password']);
|
||||
|
@ -59,20 +59,20 @@ class Database extends DriverSkeleton {
|
|||
}
|
||||
|
||||
function userPropertiesSet(string $user, array $properties): array {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!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]);
|
||||
parent::userPropertiesSet($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(!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 parent::userRightsGet($user);
|
||||
}
|
||||
|
||||
function userRightsSet(string $user, int $level): bool {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!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 parent::userRightsSet($user, $level);
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ class Database extends DriverSkeleton {
|
|||
// specific to mock database
|
||||
|
||||
function userPasswordGet(string $user): string {
|
||||
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||
if(!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]['password'];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
namespace JKingWeb\Arsse\Test\User;
|
||||
use JKingWeb\Arsse\Data;
|
||||
use JKingWeb\Arsse\User\Driver;
|
||||
use JKingWeb\Arsse\User\Exception;
|
||||
use PasswordGenerator\Generator as PassGen;
|
||||
|
@ -8,7 +9,6 @@ use PasswordGenerator\Generator as PassGen;
|
|||
class DriverExternalMock extends DriverSkeleton implements Driver {
|
||||
|
||||
public $db = [];
|
||||
protected $data;
|
||||
protected $functions = [
|
||||
"auth" => Driver::FUNC_EXTERNAL,
|
||||
"userList" => Driver::FUNC_EXTERNAL,
|
||||
|
@ -22,10 +22,6 @@ class DriverExternalMock extends DriverSkeleton implements Driver {
|
|||
"userRightsSet" => Driver::FUNC_EXTERNAL,
|
||||
];
|
||||
|
||||
static public function create(\JKingWeb\Arsse\RuntimeData $data): Driver {
|
||||
return new static($data);
|
||||
}
|
||||
|
||||
static public function driverName(): string {
|
||||
return "Mock External Driver";
|
||||
}
|
||||
|
@ -39,8 +35,7 @@ class DriverExternalMock extends DriverSkeleton implements Driver {
|
|||
}
|
||||
}
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
function auth(string $user, string $password): bool {
|
||||
|
@ -56,7 +51,7 @@ class DriverExternalMock extends DriverSkeleton implements Driver {
|
|||
|
||||
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($this->data->conf->userTempPasswordLength)->get();
|
||||
if($password===null) $password = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get();
|
||||
return parent::userAdd($user, $password);
|
||||
}
|
||||
|
||||
|
@ -75,7 +70,7 @@ class DriverExternalMock extends DriverSkeleton implements Driver {
|
|||
|
||||
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($this->data->conf->userTempPasswordLength)->get();
|
||||
if($newPassword===null) $newPassword = (new PassGen)->length(Data::$conf->userTempPasswordLength)->get();
|
||||
return parent::userPasswordSet($user, $newPassword);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ use JKingWeb\Arsse\User\Driver;
|
|||
class DriverInternalMock extends Database implements Driver {
|
||||
|
||||
public $db = [];
|
||||
protected $data;
|
||||
protected $functions = [
|
||||
"auth" => Driver::FUNC_INTERNAL,
|
||||
"userList" => Driver::FUNC_INTERNAL,
|
||||
|
@ -20,10 +19,6 @@ class DriverInternalMock extends Database implements Driver {
|
|||
"userRightsSet" => Driver::FUNC_INTERNAL,
|
||||
];
|
||||
|
||||
static public function create(\JKingWeb\Arsse\RuntimeData $data): Driver {
|
||||
return new static($data);
|
||||
}
|
||||
|
||||
static public function driverName(): string {
|
||||
return "Mock Internal Driver";
|
||||
}
|
||||
|
@ -37,8 +32,7 @@ class DriverInternalMock extends Database implements Driver {
|
|||
}
|
||||
}
|
||||
|
||||
public function __construct(\JKingWeb\Arsse\RuntimeData $data) {
|
||||
$this->data = $data;
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
function auth(string $user, string $password): bool {
|
||||
|
|
|
@ -10,7 +10,6 @@ use PasswordGenerator\Generator as PassGen;
|
|||
abstract class DriverSkeleton {
|
||||
|
||||
protected $db = [];
|
||||
protected $data;
|
||||
|
||||
function userExists(string $user): bool {
|
||||
return array_key_exists($user, $this->db);
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
beStrictAboutTestSize="true"
|
||||
stopOnError="true">
|
||||
|
||||
<testsuite name="Localization and exceptions">
|
||||
<testsuite name="Exceptions">
|
||||
<file>Exception/TestException.php</file>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Localization">
|
||||
<file>Lang/TestLang.php</file>
|
||||
<file>Lang/TestLangComplex.php</file>
|
||||
<file>Lang/TestException.php</file>
|
||||
<file>Lang/TestLangErrors.php</file>
|
||||
</testsuite>
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ namespace JKingWeb\Arsse;
|
|||
const INSTALL = true;
|
||||
require_once "../bootstrap.php";
|
||||
|
||||
|
||||
$user = "john.doe@example.com";
|
||||
$pass = "secret";
|
||||
$_SERVER['PHP_AUTH_USER'] = $user;
|
||||
|
@ -10,13 +11,13 @@ $_SERVER['PHP_AUTH_PW'] = $pass;
|
|||
$conf = new Conf();
|
||||
$conf->dbSQLite3File = ":memory:";
|
||||
$conf->userAuthPreferHTTP = true;
|
||||
$data = new RuntimeData($conf);
|
||||
$data->db->schemaUpdate();
|
||||
Data::load($conf);
|
||||
Data::$db->schemaUpdate();
|
||||
|
||||
$data->user->add($user, $pass);
|
||||
$data->user->auth();
|
||||
$data->user->authorizationEnabled(false);
|
||||
$data->user->rightsSet($user, User\Driver::RIGHTS_GLOBAL_ADMIN);
|
||||
$data->user->authorizationEnabled(true);
|
||||
$data->db->folderAdd($user, ['name' => 'ook']);
|
||||
$data->db->subscriptionAdd($user, "http://www.tbray.org/ongoing/ongoing.atom");
|
||||
Data::$user->add($user, $pass);
|
||||
Data::$user->auth();
|
||||
Data::$user->authorizationEnabled(false);
|
||||
Data::$user->rightsSet($user, User\Driver::RIGHTS_GLOBAL_ADMIN);
|
||||
Data::$user->authorizationEnabled(true);
|
||||
Data::$db->folderAdd($user, ['name' => 'ook']);
|
||||
Data::$db->subscriptionAdd($user, "http://www.tbray.org/ongoing/ongoing.atom");
|
Loading…
Reference in a new issue