J. King
8 years ago
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', |
||||
]; |
]; |
@ -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; |
|
||||
} |
|
@ -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 { |
|
||||
|
|
||||
} |
|
||||
} |
|
@ -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); |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
<?php |
||||
|
declare(strict_types=1); |
||||
|
namespace JKingWeb\NewsSync\User; |
||||
|
|
||||
|
class Exception extends \JKingWeb\NewsSync\Exception { |
||||
|
} |
@ -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 new issue