mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Functioning (but still incomplete) user management
This commit is contained in:
parent
793a5e2c1b
commit
646b44c9cf
20 changed files with 454 additions and 93 deletions
|
@ -1,41 +1,47 @@
|
||||||
<?php
|
<?php
|
||||||
return [
|
return [
|
||||||
"Exception.JKingWeb/NewsSync/Lang/Exception.defaultFileMissing" => "Default language file \"{0}\" missing",
|
'Exception.JKingWeb/NewsSync/Lang/Exception.defaultFileMissing' => 'Default language file "{0}" missing',
|
||||||
"Exception.JKingWeb/NewsSync/Lang/Exception.fileMissing" => "Language file \"{0}\" is not available",
|
'Exception.JKingWeb/NewsSync/Lang/Exception.fileMissing' => 'Language file "{0}" is not available',
|
||||||
"Exception.JKingWeb/NewsSync/Lang/Exception.fileUnreadable" => "Insufficient permissions to read language file \"{0}\"",
|
'Exception.JKingWeb/NewsSync/Lang/Exception.fileUnreadable' => 'Insufficient permissions to read language file "{0}"',
|
||||||
"Exception.JKingWeb/NewsSync/Lang/Exception.fileCorrupt" => "Language file \"{0}\" is corrupt or does not conform to expected format",
|
'Exception.JKingWeb/NewsSync/Lang/Exception.fileCorrupt' => 'Language file "{0}" is corrupt or does not conform to expected format',
|
||||||
"Exception.JKingWeb/NewsSync/Lang/Exception.stringMissing" => "Message string \"{msgID}\" missing from all loaded language files ({fileList})",
|
'Exception.JKingWeb/NewsSync/Lang/Exception.stringMissing' => 'Message string "{msgID}" missing from all loaded language files ({fileList})',
|
||||||
"Exception.JKingWeb/NewsSync/Lang/Exception.stringInvalid" => "Message string \"{msgID}\" is not a valid ICU message string (language files loaded: {fileList})",
|
'Exception.JKingWeb/NewsSync/Lang/Exception.stringInvalid' => 'Message string "{msgID}" is not a valid ICU message string (language files loaded: {fileList})',
|
||||||
|
|
||||||
"Exception.JKingWeb/NewsSync/Conf/Exception.fileMissing" => "Configuration file \"{0}\" does not exist",
|
'Exception.JKingWeb/NewsSync/Conf/Exception.fileMissing' => 'Configuration file "{0}" does not exist',
|
||||||
"Exception.JKingWeb/NewsSync/Conf/Exception.fileUnreadable" => "Insufficient permissions to read configuration file \"{0}\"",
|
'Exception.JKingWeb/NewsSync/Conf/Exception.fileUnreadable' => 'Insufficient permissions to read configuration file "{0}"',
|
||||||
"Exception.JKingWeb/NewsSync/Conf/Exception.fileUncreatable" => "Insufficient permissions to write new configuration file \"{0}\"",
|
'Exception.JKingWeb/NewsSync/Conf/Exception.fileUncreatable' => 'Insufficient permissions to write new configuration file "{0}"',
|
||||||
"Exception.JKingWeb/NewsSync/Conf/Exception.fileUnwritable" => "Insufficient permissions to overwrite configuration file \"{0}\"",
|
'Exception.JKingWeb/NewsSync/Conf/Exception.fileUnwritable' => 'Insufficient permissions to overwrite configuration file "{0}"',
|
||||||
"Exception.JKingWeb/NewsSync/Conf/Exception.fileCorrupt" => "Configuration file \"{0}\" is corrupt or does not conform to expected format",
|
'Exception.JKingWeb/NewsSync/Conf/Exception.fileCorrupt' => 'Configuration file "{0}" is corrupt or does not conform to expected format',
|
||||||
|
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.extMissing" => "Required PHP extension for driver \"{0}\" not installed",
|
'Exception.JKingWeb/NewsSync/Db/Exception.extMissing' => 'Required PHP extension for driver "{0}" not installed',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.fileMissing" => "Database file \"{0}\" does not exist",
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileMissing' => 'Database file "{0}" does not exist',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.fileUnreadable" => "Insufficient permissions to open database file \"{0}\" for reading",
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileUnreadable' => 'Insufficient permissions to open database file "{0}" for reading',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.fileUnwritable" => "Insufficient permissions to open database file \"{0}\" for writing",
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileUnwritable' => 'Insufficient permissions to open database file "{0}" for writing',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.fileUnusable" => "Insufficient permissions to open database file \"{0}\" for reading or writing",
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileUnusable' => 'Insufficient permissions to open database file "{0}" for reading or writing',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.fileUncreatable" => "Insufficient permissions to create new database file \"{0}\"",
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileUncreatable' => 'Insufficient permissions to create new database file "{0}"',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Exception.fileCorrupt" => "Database file \"{0}\" is corrupt or not a valid database",
|
'Exception.JKingWeb/NewsSync/Db/Exception.fileCorrupt' => 'Database file "{0}" is corrupt or not a valid database',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Update/Exception.manual" =>
|
|
||||||
"{from_version, select,
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.manual' =>
|
||||||
|
'{from_version, select,
|
||||||
0 {{driver_name} database is configured for manual updates and is not initialized; please populate the database with the base schema}
|
0 {{driver_name} database is configured for manual updates and is not initialized; please populate the database with the base schema}
|
||||||
other {{driver_name} database is configured for manual updates; please update from schema version {current} to version {target}}
|
other {{driver_name} database is configured for manual updates; please update from schema version {current} to version {target}}
|
||||||
}",
|
}',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Update/Exception.manualOnly" =>
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.manualOnly' =>
|
||||||
"{from_version, select,
|
'{from_version, select,
|
||||||
0 {{driver_name} database must be updated manually and is not initialized; please populate the database with the base schema}
|
0 {{driver_name} database must be updated manually and is not initialized; please populate the database with the base schema}
|
||||||
other {{driver_name} database must be updated manually; please update from schema version {current} to version {target}}
|
other {{driver_name} database must be updated manually; please update from schema version {current} to version {target}}
|
||||||
}",
|
}',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Update/Exception.missing" => "Automatic updating of the {driver_name} database failed due to instructions for updating from version {current} not being available",
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.fileMissing' => 'Automatic updating of the {driver_name} database failed due to instructions for updating from version {current} not being available',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Update/Exception.unreadable" => "Automatic updating of the {driver_name} database failed due to insufficient permissions to read instructions for updating from version {current}",
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.fileUnreadable' => 'Automatic updating of the {driver_name} database failed due to insufficient permissions to read instructions for updating from version {current}',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Update/Exception.unusable" => "Automatic updating of the {driver_name} database failed due to an error reading instructions for updating from version {current}",
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.fileUnusable' => 'Automatic updating of the {driver_name} database failed due to an error reading instructions for updating from version {current}',
|
||||||
"Exception.JKingWeb/NewsSync/Db/Update/Exception.tooNew" =>
|
'Exception.JKingWeb/NewsSync/Db/Update/Exception.tooNew' =>
|
||||||
"{difference, select,
|
'{difference, select,
|
||||||
0 {Automatic updating of the {driver_name} database failed because it is already up to date with the requested version, {target}}
|
0 {Automatic updating of the {driver_name} database failed because it is already up to date with the requested version, {target}}
|
||||||
other {Automatic updating of the {driver_name} database failed because its version, {current}, is newer than the requested version, {target}}
|
other {Automatic updating of the {driver_name} database failed because its version, {current}, is newer than the requested version, {target}}
|
||||||
}",
|
}',
|
||||||
|
|
||||||
|
'Exception.JKingWeb/NewsSync/User/Exception.alreadyExists' => 'Could not perform action "{action}" because the user {user} already exists',
|
||||||
|
'Exception.JKingWeb/NewsSync/User/Exception.doesNotExist' => 'Could not perform action "{action}" because the user {user} does not exist',
|
||||||
|
'Exception.JKingWeb/NewsSync/User/Exception.authMissing' => 'Please log in to proceed',
|
||||||
|
'Exception.JKingWeb/NewsSync/User/Exception.authFailed' => 'Authentication failed',
|
||||||
];
|
];
|
|
@ -9,8 +9,8 @@ create table newssync_feeds(
|
||||||
modified datetime not null default CURRENT_TIMESTAMP, --
|
modified datetime not null default CURRENT_TIMESTAMP, --
|
||||||
err_count integer not null default 0, -- count of successive times update resulted in error since last successful update
|
err_count integer not null default 0, -- count of successive times update resulted in error since last successful update
|
||||||
err_msg TEXT, -- last error message
|
err_msg TEXT, -- last error message
|
||||||
username TEXT, -- HTTP authentication username
|
username TEXT not null default '', -- HTTP authentication username
|
||||||
password TEXT, -- HTTP authentication password (this is stored in plain text)
|
password TEXT not null default '', -- HTTP authentication password (this is stored in plain text)
|
||||||
unique(url,username,password) -- a URL with particular credentials should only appear once
|
unique(url,username,password) -- a URL with particular credentials should only appear once
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -59,9 +59,12 @@ create table newssync_users(
|
||||||
id TEXT primary key not null, -- user id
|
id TEXT primary key not null, -- user id
|
||||||
password TEXT, -- password, salted and hashed; if using external authentication this would be blank
|
password TEXT, -- password, salted and hashed; if using external authentication this would be blank
|
||||||
name TEXT, -- display name
|
name TEXT, -- display name
|
||||||
avatar_type TEXT, -- avatar image's MIME content type
|
avatar_url TEXT, -- external URL to avatar
|
||||||
avatar_data BLOB, -- avatar image's binary data
|
avatar_type TEXT, -- internal avatar image's MIME content type
|
||||||
admin boolean not null default 0 -- whether the user is an administrator
|
avatar_data BLOB, -- internal avatar image's binary data
|
||||||
|
admin TEXT check(
|
||||||
|
admin in('global', 'domain', null)
|
||||||
|
) -- whether the user is an administrator
|
||||||
);
|
);
|
||||||
|
|
||||||
-- TT-RSS categories and ownCloud folders
|
-- TT-RSS categories and ownCloud folders
|
||||||
|
|
10
vendor/JKingWeb/NewsSync/Auth/Driver.php
vendored
10
vendor/JKingWeb/NewsSync/Auth/Driver.php
vendored
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types=1);
|
|
||||||
namespace JKingWeb\NewsSync\Auth;
|
|
||||||
|
|
||||||
Interface Driver {
|
|
||||||
public function __construct($conf, $db);
|
|
||||||
public function auth(): bool;
|
|
||||||
public function authHTTP(): bool;
|
|
||||||
public function isAdmin(): bool;
|
|
||||||
}
|
|
24
vendor/JKingWeb/NewsSync/Auth/DriverInternal.php
vendored
24
vendor/JKingWeb/NewsSync/Auth/DriverInternal.php
vendored
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
declare(strict_types=1);
|
|
||||||
namespace JKingWeb\NewsSync\Auth;
|
|
||||||
|
|
||||||
class Internal implements Driver {
|
|
||||||
protected $conf;
|
|
||||||
protected $db;
|
|
||||||
|
|
||||||
public function __construct($conf, $db) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function auth(): bool {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function authHTTP(): bool {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isAdmin(): bool {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
8
vendor/JKingWeb/NewsSync/Conf.php
vendored
8
vendor/JKingWeb/NewsSync/Conf.php
vendored
|
@ -5,7 +5,7 @@ namespace JKingWeb\NewsSync;
|
||||||
class Conf {
|
class Conf {
|
||||||
public $lang = "en";
|
public $lang = "en";
|
||||||
|
|
||||||
public $dbClass = NS_BASE."Db\\DriverSQLite3";
|
public $dbDriver = NS_BASE."Db\\DriverSQLite3";
|
||||||
public $dbSQLite3File = BASE."newssync.db";
|
public $dbSQLite3File = BASE."newssync.db";
|
||||||
public $dbSQLite3Key = "";
|
public $dbSQLite3Key = "";
|
||||||
public $dbSQLite3AutoUpd = true;
|
public $dbSQLite3AutoUpd = true;
|
||||||
|
@ -23,9 +23,9 @@ class Conf {
|
||||||
public $dbMySQLDb = "newssync";
|
public $dbMySQLDb = "newssync";
|
||||||
public $dbMySQLAutoUpd = false;
|
public $dbMySQLAutoUpd = false;
|
||||||
|
|
||||||
public $authClass = NS_BASE."Auth\\DriverInternal";
|
public $userDriver = NS_BASE."User\\DriverInternal";
|
||||||
public $authPreferHTTP = false;
|
public $userAuthPreferHTTP = false;
|
||||||
public $authAutoAdd = false;
|
public $userComposeNames = true;
|
||||||
|
|
||||||
public $simplepieCache = BASE.".cache";
|
public $simplepieCache = BASE.".cache";
|
||||||
|
|
||||||
|
|
74
vendor/JKingWeb/NewsSync/Database.php
vendored
74
vendor/JKingWeb/NewsSync/Database.php
vendored
|
@ -9,7 +9,7 @@ class Database {
|
||||||
const FORMAT_TIME = "h:i:s";
|
const FORMAT_TIME = "h:i:s";
|
||||||
|
|
||||||
protected $data;
|
protected $data;
|
||||||
protected $db;
|
public $db;
|
||||||
|
|
||||||
protected function cleanName(string $name): string {
|
protected function cleanName(string $name): string {
|
||||||
return (string) preg_filter("[^0-9a-zA-Z_\.]", "", $name);
|
return (string) preg_filter("[^0-9a-zA-Z_\.]", "", $name);
|
||||||
|
@ -17,7 +17,7 @@ class Database {
|
||||||
|
|
||||||
public function __construct(RuntimeData $data) {
|
public function __construct(RuntimeData $data) {
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
$driver = $data->conf->dbClass;
|
$driver = $data->conf->dbDriver;
|
||||||
$this->db = $driver::create($data, INSTALL);
|
$this->db = $driver::create($data, INSTALL);
|
||||||
$ver = $this->db->schemaVersion();
|
$ver = $this->db->schemaVersion();
|
||||||
if(!INSTALL && $ver < self::SCHEMA_VERSION) {
|
if(!INSTALL && $ver < self::SCHEMA_VERSION) {
|
||||||
|
@ -161,20 +161,78 @@ class Database {
|
||||||
$this->db->prepare("REPLACE INTO newssync_settings(key,value,type) values(?,?,?)", "str", (($type=="null") ? "null" : "str"), "str")->run($key, $value, "text");
|
$this->db->prepare("REPLACE INTO newssync_settings(key,value,type) values(?,?,?)", "str", (($type=="null") ? "null" : "str"), "str")->run($key, $value, "text");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function settingClear(string $key): bool {
|
public function settingRemove(string $key): bool {
|
||||||
$this->db->prepare("DELETE from newssync_settings where key = ?", "str")->run($key);
|
$this->db->prepare("DELETE from newssync_settings where key = ?", "str")->run($key);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userAdd(string $username, string $password = null, bool $admin = false): string {
|
public function userExists(string $username): bool {
|
||||||
$this->db->prepare("INSERT INTO newssync_users(id,password,admin) values(?,?,?)", "str", "str", "bool")->run($username,$password,$admin);
|
return (bool) $this->db->prepare("SELECT count(*) from newssync_users where id is ?", "str")->run($username)->getSingle();
|
||||||
return $username;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscriptionAdd(string $user, string $url, string $fetchUser = null, string $fetchPassword = null): int {
|
public function userAdd(string $username, string $password = null): bool {
|
||||||
|
if(strlen($password) > 0) $password = password_hash($password, \PASSWORD_DEFAULT);
|
||||||
|
if($this->db->prepare("SELECT count(*) from newssync_users")->run()->getSingle() < 1) { //if there are no users, the first user should be made a global admin
|
||||||
|
$admin = "global";
|
||||||
|
} else {
|
||||||
|
$admin = null;
|
||||||
|
}
|
||||||
|
$this->db->prepare("INSERT INTO newssync_users(id,password,admin) values(?,?,?)", "str", "str", "str")->run($username,$password,$admin);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userRemove(string $username): bool {
|
||||||
|
$this->db->prepare("DELETE from newssync_users where id is ?", "str")->run($username);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userList(string $domain = null): array {
|
||||||
|
if($domain !== null) {
|
||||||
|
$domain = str_replace(["\\","%","_"],["\\\\", "\\%", "\\_"], $domain);
|
||||||
|
$domain = "%@".$domain;
|
||||||
|
$set = $this->db->prepare("SELECT id from newssync_users where id like ?", "str")->run($domain);
|
||||||
|
} else {
|
||||||
|
$set = $this->db->query("SELECT id from newssync_users");
|
||||||
|
}
|
||||||
|
$out = [];
|
||||||
|
foreach($set as $row) {
|
||||||
|
$out[] = $row["id"];
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userPasswordSet($username, $password): bool {
|
||||||
|
if(!$this->userExists($username)) return false;
|
||||||
|
if(strlen($password > 0)) $password = password_hash($password);
|
||||||
|
$this->db->prepare("UPDATE newssync_users set password = ? where id is ?", "str", "str")->run($password, $username);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userPropertiesGet(string $username): array {
|
||||||
|
$prop = $this->db->prepare("SELECT name,admin from newssync_users where id is ?", "str")->run($username)->get();
|
||||||
|
if(!$prop) return [];
|
||||||
|
return $prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userPropertiesSet(string $username, array &$properties): array {
|
||||||
|
$valid = [ // FIXME: add future properties
|
||||||
|
"name" => "str",
|
||||||
|
"admin" => "str",
|
||||||
|
];
|
||||||
|
$this->db->begin();
|
||||||
|
foreach($valid as $prop => $type) {
|
||||||
|
if(!array_key_exists($prop, $properties)) continue;
|
||||||
|
$this->db->prepare("UPDATE newssync_users set $prop = ? where id is ?", $type, "str")->run($properties[$prop], $username);
|
||||||
|
}
|
||||||
|
$this->db->commit();
|
||||||
|
return $this->userPropertiesGet($username);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = ""): int {
|
||||||
$this->db->begin();
|
$this->db->begin();
|
||||||
$qFeed = $this->db->prepare("SELECT id from newssync_feeds where url is ? and username is ? and password is ?", "str", "str", "str");
|
$qFeed = $this->db->prepare("SELECT id from newssync_feeds where url is ? and username is ? and password is ?", "str", "str", "str");
|
||||||
if(is_null($id = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle())) {
|
$id = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle();
|
||||||
|
if($id===null) {
|
||||||
$this->db->prepare("INSERT INTO newssync_feeds(url,username,password) values(?,?,?)", "str", "str", "str")->run($url, $fetchUser, $fetchPassword);
|
$this->db->prepare("INSERT INTO newssync_feeds(url,username,password) values(?,?,?)", "str", "str", "str")->run($url, $fetchUser, $fetchPassword);
|
||||||
$id = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle();
|
$id = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle();
|
||||||
var_export($id);
|
var_export($id);
|
||||||
|
|
10
vendor/JKingWeb/NewsSync/Db/Common.php
vendored
10
vendor/JKingWeb/NewsSync/Db/Common.php
vendored
|
@ -6,6 +6,14 @@ use JKingWeb\DrUUID\UUID as UUID;
|
||||||
Trait Common {
|
Trait Common {
|
||||||
protected $transDepth = 0;
|
protected $transDepth = 0;
|
||||||
|
|
||||||
|
public function schemaVersion(): integer {
|
||||||
|
try {
|
||||||
|
return $this->data->db->settingGet("schema_version");
|
||||||
|
} catch(\Throwable $e) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function begin(): bool {
|
public function begin(): bool {
|
||||||
$this->exec("SAVEPOINT newssync_".($this->transDepth));
|
$this->exec("SAVEPOINT newssync_".($this->transDepth));
|
||||||
$this->transDepth += 1;
|
$this->transDepth += 1;
|
||||||
|
@ -50,7 +58,7 @@ Trait Common {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unlock(): bool {
|
public function unlock(): bool {
|
||||||
return $this->data->db->settingClear("lock");
|
return $this->data->db->settingRemove("lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isLocked(): bool {
|
public function isLocked(): bool {
|
||||||
|
|
|
@ -24,10 +24,10 @@ Trait CommonSQLite3 {
|
||||||
$this->begin();
|
$this->begin();
|
||||||
try {
|
try {
|
||||||
$file = $path.$a.".sql";
|
$file = $path.$a.".sql";
|
||||||
if(!file_exists($file)) throw new Update\Exception("missing", ['file' => $file, 'driver_name' => $this->driverName()]);
|
if(!file_exists($file)) throw new Update\Exception("fileMissing", ['file' => $file, 'driver_name' => $this->driverName()]);
|
||||||
if(!is_readable($file)) throw new Update\Exception("unreadable", ['file' => $file, 'driver_name' => $this->driverName()]);
|
if(!is_readable($file)) throw new Update\Exception("fileUnreadable", ['file' => $file, 'driver_name' => $this->driverName()]);
|
||||||
$sql = @file_get_contents($file);
|
$sql = @file_get_contents($file);
|
||||||
if($sql===false) throw new Update\Exception("unusable", ['file' => $file, 'driver_name' => $this->driverName()]);
|
if($sql===false) throw new Update\Exception("fileUnusable", ['file' => $file, 'driver_name' => $this->driverName()]);
|
||||||
$this->exec($sql);
|
$this->exec($sql);
|
||||||
} catch(\Throwable $e) {
|
} catch(\Throwable $e) {
|
||||||
// undo any partial changes from the failed update
|
// undo any partial changes from the failed update
|
||||||
|
|
|
@ -3,7 +3,9 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync\Db;
|
namespace JKingWeb\NewsSync\Db;
|
||||||
|
|
||||||
class DriverSQLite3 implements Driver {
|
class DriverSQLite3 implements Driver {
|
||||||
use Common, CommonSQLite3;
|
use Common, CommonSQLite3 {
|
||||||
|
CommonSQLite3::schemaVersion insteadof Common;
|
||||||
|
}
|
||||||
|
|
||||||
protected $db;
|
protected $db;
|
||||||
protected $data;
|
protected $data;
|
||||||
|
|
|
@ -3,12 +3,14 @@ declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync\Db;
|
namespace JKingWeb\NewsSync\Db;
|
||||||
|
|
||||||
class ResultSQLite3 implements Result {
|
class ResultSQLite3 implements Result {
|
||||||
|
protected $st;
|
||||||
protected $set;
|
protected $set;
|
||||||
protected $pos = 0;
|
protected $pos = 0;
|
||||||
protected $cur = null;
|
protected $cur = null;
|
||||||
|
|
||||||
public function __construct(\SQLite3Result $resultObj) {
|
public function __construct($result, $statement = null) {
|
||||||
$this->set = $resultObj;
|
$this->st = $statement; //keeps the statement from being destroyed, invalidating the result set
|
||||||
|
$this->set = $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
|
|
|
@ -66,6 +66,6 @@ class StatementSQLite3 implements Statement {
|
||||||
}
|
}
|
||||||
$this->st->bindParam($a+1, $values[$a], $type);
|
$this->st->bindParam($a+1, $values[$a], $type);
|
||||||
}
|
}
|
||||||
return new ResultSQLite3($this->st->execute());
|
return new ResultSQLite3($this->st->execute(), $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
17
vendor/JKingWeb/NewsSync/Exception.php
vendored
17
vendor/JKingWeb/NewsSync/Exception.php
vendored
|
@ -18,6 +18,23 @@ class Exception extends \Exception {
|
||||||
"Db/Exception.fileUnwritable" => 10205,
|
"Db/Exception.fileUnwritable" => 10205,
|
||||||
"Db/Exception.fileUncreatable" => 10206,
|
"Db/Exception.fileUncreatable" => 10206,
|
||||||
"Db/Exception.fileCorrupt" => 10207,
|
"Db/Exception.fileCorrupt" => 10207,
|
||||||
|
"Db/Update/Exception.tooNew" => 10211,
|
||||||
|
"Db/Update/Exception.fileMissing" => 10212,
|
||||||
|
"Db/Update/Exception.fileUnusable" => 10213,
|
||||||
|
"Db/Update/Exception.fileUnreadable" => 10214,
|
||||||
|
"Db/Update/Exception.manual" => 10215,
|
||||||
|
"Db/Update/Exception.manualOnly" => 10216,
|
||||||
|
"Conf/Exception.fileMissing" => 10302,
|
||||||
|
"Conf/Exception.fileUnusable" => 10303,
|
||||||
|
"Conf/Exception.fileUnreadable" => 10304,
|
||||||
|
"Conf/Exception.fileUnwritable" => 10305,
|
||||||
|
"Conf/Exception.fileUncreatable" => 10306,
|
||||||
|
"Conf/Exception.fileCorrupt" => 10307,
|
||||||
|
"User/Exception.functionNotImplemented" => 10401,
|
||||||
|
"User/Exception.doesNotExist" => 10402,
|
||||||
|
"User/Exception.alreadyExists" => 10403,
|
||||||
|
"User/Exception.authMissing" => 10411,
|
||||||
|
"User/Exception.authFailed" => 10412,
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
||||||
|
|
9
vendor/JKingWeb/NewsSync/ExceptionFatal.php
vendored
Normal file
9
vendor/JKingWeb/NewsSync/ExceptionFatal.php
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync;
|
||||||
|
|
||||||
|
class ExceptionFatal extends Exception {
|
||||||
|
public function __construct($msg = "", $code = 0, $e = null) {
|
||||||
|
\Exception::__construct($msg, $code, $e);
|
||||||
|
}
|
||||||
|
}
|
11
vendor/JKingWeb/NewsSync/Lang.php
vendored
11
vendor/JKingWeb/NewsSync/Lang.php
vendored
|
@ -51,8 +51,13 @@ class Lang {
|
||||||
}
|
}
|
||||||
if(!array_key_exists($msgID, self::$strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]);
|
if(!array_key_exists($msgID, self::$strings)) throw new Lang\Exception("stringMissing", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]);
|
||||||
// variables fed to MessageFormatter must be contained in array
|
// variables fed to MessageFormatter must be contained in array
|
||||||
if($vars !== null && !is_array($vars)) $vars = [$vars];
|
$msg = self::$strings[$msgID];
|
||||||
$msg = \MessageFormatter::formatMessage(self::$locale, self::$strings[$msgID], $vars);
|
if($vars===null) {
|
||||||
|
return $msg;
|
||||||
|
} 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)]);
|
if($msg===false) throw new Lang\Exception("stringInvalid", ['msgID' => $msgID, 'fileList' => implode(", ",self::$loaded)]);
|
||||||
return $msg;
|
return $msg;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +78,7 @@ class Lang {
|
||||||
}
|
}
|
||||||
|
|
||||||
static protected function checkRequirements(): bool {
|
static protected function checkRequirements(): bool {
|
||||||
if(!extension_loaded("intl")) throw new FatalException("The \"Intl\" extension is required, but not loaded");
|
if(!extension_loaded("intl")) throw new ExceptionFatal("The \"Intl\" extension is required, but not loaded");
|
||||||
self::$requirementsMet = true;
|
self::$requirementsMet = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
2
vendor/JKingWeb/NewsSync/RuntimeData.php
vendored
2
vendor/JKingWeb/NewsSync/RuntimeData.php
vendored
|
@ -11,6 +11,6 @@ class RuntimeData {
|
||||||
$this->conf = $conf;
|
$this->conf = $conf;
|
||||||
Lang::set($conf->lang);
|
Lang::set($conf->lang);
|
||||||
$this->db = new Database($this);
|
$this->db = new Database($this);
|
||||||
//$this->auth = new Authenticator($this);
|
$this->user = new User($this);
|
||||||
}
|
}
|
||||||
}
|
}
|
164
vendor/JKingWeb/NewsSync/User.php
vendored
Normal file
164
vendor/JKingWeb/NewsSync/User.php
vendored
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync;
|
||||||
|
|
||||||
|
class User {
|
||||||
|
public $id = null;
|
||||||
|
|
||||||
|
protected $data;
|
||||||
|
protected $u;
|
||||||
|
protected $logged = [];
|
||||||
|
|
||||||
|
static public function listDrivers(): array {
|
||||||
|
$sep = \DIRECTORY_SEPARATOR;
|
||||||
|
$path = __DIR__.$sep."User".$sep;
|
||||||
|
$classes = [];
|
||||||
|
foreach(glob($path."Driver?*.php") as $file) {
|
||||||
|
$name = basename($file, ".php");
|
||||||
|
$name = NS_BASE."Db\\$name";
|
||||||
|
if(class_exists($name)) {
|
||||||
|
$classes[$name] = $name::driverName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(\JKingWeb\NewsSync\RuntimeData $data) {
|
||||||
|
$this->data = $data;
|
||||||
|
$driver = $data->conf->userDriver;
|
||||||
|
$this->u = $driver::create($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() {
|
||||||
|
if($this->id===null) $this->credentials();
|
||||||
|
return (string) $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function credentials(): array {
|
||||||
|
if($this->data->conf->userAuthPreferHTTP) {
|
||||||
|
return $this->credentialsHTTP();
|
||||||
|
} else {
|
||||||
|
return $this->credentialsForm();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function credentialsForm(): array {
|
||||||
|
// FIXME: stub
|
||||||
|
$this->id = "john.doe@example.com";
|
||||||
|
return ["user" => "john.doe@example.com", "password" => "secret"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function credentialsHTTP(): array {
|
||||||
|
if($_SERVER['PHP_AUTH_USER']) {
|
||||||
|
$out = ["user" => $_SERVER['PHP_AUTH_USER'], "password" => $_SERVER['PHP_AUTH_PW']];
|
||||||
|
} else if($_SERVER['REMOTE_USER']) {
|
||||||
|
$out = ["user" => $_SERVER['REMOTE_USER'], "password" => null];
|
||||||
|
} else {
|
||||||
|
$out = ["user" => null, "password" => null];
|
||||||
|
}
|
||||||
|
if($this->data->conf->userComposeNames && $out["user"] !== null) {
|
||||||
|
$out["user"] = $this->composeName($out["user"]);
|
||||||
|
}
|
||||||
|
$this->id = $out["user"];
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function auth(string $user = null, string $password = null): bool {
|
||||||
|
if($user===null) {
|
||||||
|
if($this->data->conf->userAuthPreferHTTP) {
|
||||||
|
return $this->authHTTP();
|
||||||
|
} else {
|
||||||
|
return $this->authForm();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if($this->u->auth($user, $password)) {
|
||||||
|
$this->authPostprocess($user);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authForm(): bool {
|
||||||
|
$cred = $this->credentialsForm();
|
||||||
|
if(!$cred["user"]) return $this->challengeForm();
|
||||||
|
if(!$this->u->auth($cred["user"], $cred["password"])) return $this->challengeForm();
|
||||||
|
$this->authPostprocess($cred["user"]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authHTTP(): bool {
|
||||||
|
$cred = $this->credentialsHTTP();
|
||||||
|
if(!$cred["user"]) return $this->challengeHTTP();
|
||||||
|
if(!$this->u->auth($cred["user"], $cred["password"])) return $this->challengeHTTP();
|
||||||
|
$this->authPostprocess($cred["user"]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function driverFunctions(string $function = null) {
|
||||||
|
return $this->u->driverFunctions($function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list(string $domain = null): array {
|
||||||
|
if($this->u->driveFunctions("userList") != Driver::FUNC_NOT_IMPLEMENTED) {
|
||||||
|
return $this->u->userList($domain);
|
||||||
|
} else {
|
||||||
|
// N.B. this does not do any authorization checks
|
||||||
|
return $this->data->db->userList($domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists(string $user): bool {
|
||||||
|
return $this->u->userExists($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add($user, $password = null): bool {
|
||||||
|
$out = $this->u->userAdd($user, $password);
|
||||||
|
if($out && $this->u->driverFunctions("userAdd") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
try {
|
||||||
|
if(!$this->data->db->userExists($user)) $this->data->db->userAdd($user, $password);
|
||||||
|
} catch(\Throwable $e) {}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function remove(string $user): bool {
|
||||||
|
$out = $this->u->userRemove($user);
|
||||||
|
if($out && $this->u->driverFunctions("userRemove") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
try {
|
||||||
|
if($this->data->db->userExists($user)) $this->data->db->userRemove($user);
|
||||||
|
} catch(\Throwable $e) {}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function passwordSet(string $user, string $password): bool {
|
||||||
|
return $this->u->userPasswordSet($user, $password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function propertiesGet(string $user): array {
|
||||||
|
return $this->u->userPropertiesGet($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function propertiesSet(string $user, array $properties): array {
|
||||||
|
return $this->u->userPropertiesSet($user, $properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: stubs
|
||||||
|
public function challenge(): bool {throw new User\Exception("authFailed");}
|
||||||
|
public function challengeForm(): bool {throw new User\Exception("authFailed");}
|
||||||
|
public function challengeHTTP(): bool {throw new User\Exception("authFailed");}
|
||||||
|
|
||||||
|
protected function composeName(string $user): string {
|
||||||
|
if(preg_match("/.+?@[^@]+$/",$user)) {
|
||||||
|
return $user;
|
||||||
|
} else {
|
||||||
|
return $user."@".$_SERVER['HTTP_HOST'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function authPostprocess(string $user): bool {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
21
vendor/JKingWeb/NewsSync/User/Driver.php
vendored
Normal file
21
vendor/JKingWeb/NewsSync/User/Driver.php
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\User;
|
||||||
|
|
||||||
|
Interface Driver {
|
||||||
|
const FUNC_NOT_IMPLEMENTED = 0;
|
||||||
|
const FUNC_INTERNAL = 1;
|
||||||
|
const FUNC_EXTERNAL = 2;
|
||||||
|
|
||||||
|
static function create(\JKingWeb\NewsSync\RuntimeData $data): Driver;
|
||||||
|
static function driverName(): string;
|
||||||
|
function driverFunctions(string $function = null);
|
||||||
|
function auth(string $user, string $password): bool;
|
||||||
|
function userExists(string $user): bool;
|
||||||
|
function userAdd(string $user, string $password = null): bool;
|
||||||
|
function userRemove(string $user): bool;
|
||||||
|
function userList(string $domain = null): array;
|
||||||
|
function userPasswordSet(string $user, string $newPassword, string $oldPassword): bool;
|
||||||
|
function userPropertiesGet(string $user): array;
|
||||||
|
function userPropertiesSet(string $user, array $properties): array;
|
||||||
|
}
|
42
vendor/JKingWeb/NewsSync/User/DriverInternal.php
vendored
Normal file
42
vendor/JKingWeb/NewsSync/User/DriverInternal.php
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\User;
|
||||||
|
|
||||||
|
class DriverInternal implements Driver {
|
||||||
|
use InternalFunctions;
|
||||||
|
|
||||||
|
protected $data;
|
||||||
|
protected $db;
|
||||||
|
protected $functions = [
|
||||||
|
"auth" => Driver::FUNC_INTERNAL,
|
||||||
|
"userList" => Driver::FUNC_INTERNAL,
|
||||||
|
"userExists" => Driver::FUNC_INTERNAL,
|
||||||
|
"userAdd" => Driver::FUNC_INTERNAL,
|
||||||
|
"userRemove" => Driver::FUNC_INTERNAL,
|
||||||
|
"userPasswordSet" => Driver::FUNC_INTERNAL,
|
||||||
|
"userPropertiesGet" => Driver::FUNC_INTERNAL,
|
||||||
|
"userPropertiesSet" => Driver::FUNC_INTERNAL,
|
||||||
|
];
|
||||||
|
|
||||||
|
static public function create(\JKingWeb\NewsSync\RuntimeData $data): Driver {
|
||||||
|
return new static($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct(\JKingWeb\NewsSync\RuntimeData $data) {
|
||||||
|
$this->data = $data;
|
||||||
|
$this->db = $this->data->db;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function driverName(): string {
|
||||||
|
return "Internal";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function driverFunctions(string $function = null) {
|
||||||
|
if($function===null) return $this->functions;
|
||||||
|
if(array_key_exists($function, $this->functions)) {
|
||||||
|
return $this->functions[$function];
|
||||||
|
} else {
|
||||||
|
return Driver::FUNC_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
vendor/JKingWeb/NewsSync/User/Exception.php
vendored
Normal file
6
vendor/JKingWeb/NewsSync/User/Exception.php
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\User;
|
||||||
|
|
||||||
|
class Exception extends \JKingWeb\NewsSync\Exception {
|
||||||
|
}
|
52
vendor/JKingWeb/NewsSync/User/InternalFunctions.php
vendored
Normal file
52
vendor/JKingWeb/NewsSync/User/InternalFunctions.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\User;
|
||||||
|
|
||||||
|
trait InternalFunctions {
|
||||||
|
function auth(string $user, string $password): bool {
|
||||||
|
if(!$this->userExists($user)) return false;
|
||||||
|
return true;
|
||||||
|
$hash = $this->db->userPasswordGet($user);
|
||||||
|
if(!$hash) return false;
|
||||||
|
return password_verify($password, $hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userExists(string $user): bool {
|
||||||
|
return $this->db->userExists($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userAdd(string $user, string $password = null): bool {
|
||||||
|
if($this->userExists($user)) throw new Exception("alreadyExists", ["user" => $user, "action" => __FUNCTION__]);
|
||||||
|
// FIXME: add authorization checks
|
||||||
|
return $this->db->userAdd($user, $password);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userRemove(string $user): bool {
|
||||||
|
if(!$this->userExists($user)) throw new Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]);
|
||||||
|
// FIXME: add authorization checks
|
||||||
|
return $this->db->userRemove($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userList(string $domain = null): array {
|
||||||
|
// FIXME: add authorization checks
|
||||||
|
return $this->db->userList($domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userPasswordSet(string $user, string $newPassword, string $oldPassword): bool {
|
||||||
|
if(!$this->userExists($user)) throw new Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]);
|
||||||
|
// FIXME: add authorization checks
|
||||||
|
return $this->db->userPasswordSet($user, $newPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userPropertiesGet(string $user): array {
|
||||||
|
if(!$this->userExists($user)) throw new Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]);
|
||||||
|
// FIXME: add authorization checks
|
||||||
|
return $this->db->userPropertiesGet($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userPropertiesSet(string $user, array $properties): array {
|
||||||
|
if(!$this->userExists($user)) throw new Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]);
|
||||||
|
// FIXME: add authorization checks
|
||||||
|
return $this->db->userPropertiesSet($user, $properties);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue