mirror of
https://code.mensbeam.com/MensBeam/Arsse.git
synced 2024-12-22 13:12:41 +00:00
Added authorization checks throughout
This commit is contained in:
parent
646b44c9cf
commit
9ed4bb6f5b
10 changed files with 219 additions and 65 deletions
|
@ -44,4 +44,5 @@ return [
|
||||||
'Exception.JKingWeb/NewsSync/User/Exception.doesNotExist' => 'Could not perform action "{action}" because the user {user} does not exist',
|
'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.authMissing' => 'Please log in to proceed',
|
||||||
'Exception.JKingWeb/NewsSync/User/Exception.authFailed' => 'Authentication failed',
|
'Exception.JKingWeb/NewsSync/User/Exception.authFailed' => 'Authentication failed',
|
||||||
|
'Exception.JKingWeb/NewsSync/User/ExceptionAuthz.notAuthorized' => 'Authenticated user is not authorized to perform the action "{action}" on behalf of {user}',
|
||||||
];
|
];
|
|
@ -6,7 +6,7 @@ create table newssync_feeds(
|
||||||
favicon TEXT, -- URL of favicon
|
favicon TEXT, -- URL of favicon
|
||||||
source TEXT, -- URL of site to which the feed belongs
|
source TEXT, -- URL of site to which the feed belongs
|
||||||
updated datetime, -- time at which the feed was last fetched
|
updated datetime, -- time at which the feed was last fetched
|
||||||
modified datetime not null default CURRENT_TIMESTAMP, --
|
modified datetime, -- time at which the feed last actually changed
|
||||||
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 not null default '', -- HTTP authentication username
|
username TEXT not null default '', -- HTTP authentication username
|
||||||
|
@ -62,9 +62,7 @@ create table newssync_users(
|
||||||
avatar_url TEXT, -- external URL to avatar
|
avatar_url TEXT, -- external URL to avatar
|
||||||
avatar_type TEXT, -- internal avatar image's MIME content type
|
avatar_type TEXT, -- internal avatar image's MIME content type
|
||||||
avatar_data BLOB, -- internal avatar image's binary data
|
avatar_data BLOB, -- internal avatar image's binary data
|
||||||
admin TEXT check(
|
rights integer not null default 0 -- any administrative rights the user may have
|
||||||
admin in('global', 'domain', null)
|
|
||||||
) -- whether the user is an administrator
|
|
||||||
);
|
);
|
||||||
|
|
||||||
-- TT-RSS categories and ownCloud folders
|
-- TT-RSS categories and ownCloud folders
|
||||||
|
|
84
vendor/JKingWeb/NewsSync/Database.php
vendored
84
vendor/JKingWeb/NewsSync/Database.php
vendored
|
@ -166,33 +166,34 @@ class Database {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userExists(string $username): bool {
|
public function userExists(string $user): bool {
|
||||||
return (bool) $this->db->prepare("SELECT count(*) from newssync_users where id is ?", "str")->run($username)->getSingle();
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
return (bool) $this->db->prepare("SELECT count(*) from newssync_users where id is ?", "str")->run($user)->getSingle();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userAdd(string $username, string $password = null): bool {
|
public function userAdd(string $user, string $password = null): bool {
|
||||||
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
if($this->userExists($user)) return false;
|
||||||
if(strlen($password) > 0) $password = password_hash($password, \PASSWORD_DEFAULT);
|
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
|
$this->db->prepare("INSERT INTO newssync_users(id,password) values(?,?)", "str", "str", "str")->run($user,$password,$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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userRemove(string $username): bool {
|
public function userRemove(string $user): bool {
|
||||||
$this->db->prepare("DELETE from newssync_users where id is ?", "str")->run($username);
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
$this->db->prepare("DELETE from newssync_users where id is ?", "str")->run($user);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userList(string $domain = null): array {
|
public function userList(string $domain = null): array {
|
||||||
if($domain !== null) {
|
if($domain !== null) {
|
||||||
|
if(!$this->data->user->authorize("@".$domain, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]);
|
||||||
$domain = str_replace(["\\","%","_"],["\\\\", "\\%", "\\_"], $domain);
|
$domain = str_replace(["\\","%","_"],["\\\\", "\\%", "\\_"], $domain);
|
||||||
$domain = "%@".$domain;
|
$domain = "%@".$domain;
|
||||||
$set = $this->db->prepare("SELECT id from newssync_users where id like ?", "str")->run($domain);
|
$set = $this->db->prepare("SELECT id from newssync_users where id like ?", "str")->run($domain);
|
||||||
} else {
|
} else {
|
||||||
$set = $this->db->query("SELECT id from newssync_users");
|
if(!$this->data->user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "all users"]);
|
||||||
|
$set = $this->db->prepare("SELECT id from newssync_users")->run();
|
||||||
}
|
}
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach($set as $row) {
|
foreach($set as $row) {
|
||||||
|
@ -201,45 +202,74 @@ class Database {
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userPasswordSet($username, $password): bool {
|
public function userPasswordGet(string $user): string {
|
||||||
if(!$this->userExists($username)) return false;
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
if(!$this->userExists($user)) return "";
|
||||||
|
return (string) $this->db->prepare("SELECT password from newssync_users where id is ?", "str")->run($user)->getSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userPasswordSet(string $user, string $password = null): bool {
|
||||||
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
if(!$this->userExists($user)) return false;
|
||||||
if(strlen($password > 0)) $password = password_hash($password);
|
if(strlen($password > 0)) $password = password_hash($password);
|
||||||
$this->db->prepare("UPDATE newssync_users set password = ? where id is ?", "str", "str")->run($password, $username);
|
$this->db->prepare("UPDATE newssync_users set password = ? where id is ?", "str", "str")->run($password, $user);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userPropertiesGet(string $username): array {
|
public function userPropertiesGet(string $user): array {
|
||||||
$prop = $this->db->prepare("SELECT name,admin from newssync_users where id is ?", "str")->run($username)->get();
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
$prop = $this->db->prepare("SELECT name,rights from newssync_users where id is ?", "str")->run($user)->get();
|
||||||
if(!$prop) return [];
|
if(!$prop) return [];
|
||||||
return $prop;
|
return $prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function userPropertiesSet(string $username, array &$properties): array {
|
public function userPropertiesSet(string $user, array &$properties): array {
|
||||||
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
$valid = [ // FIXME: add future properties
|
$valid = [ // FIXME: add future properties
|
||||||
"name" => "str",
|
"name" => "str",
|
||||||
"admin" => "str",
|
|
||||||
];
|
];
|
||||||
|
if(!$this->userExists($user)) return [];
|
||||||
$this->db->begin();
|
$this->db->begin();
|
||||||
foreach($valid as $prop => $type) {
|
foreach($valid as $prop => $type) {
|
||||||
if(!array_key_exists($prop, $properties)) continue;
|
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->prepare("UPDATE newssync_users set $prop = ? where id is ?", $type, "str")->run($properties[$prop], $user);
|
||||||
}
|
}
|
||||||
$this->db->commit();
|
$this->db->commit();
|
||||||
return $this->userPropertiesGet($username);
|
return $this->userPropertiesGet($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userRightsGet(string $user): int {
|
||||||
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
return (int) $this->db->prepare("SELECT rights from newssync_users where id is ?", "str")->run($user)->getSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function userRightsSet(string $user, int $rights): bool {
|
||||||
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
if(!$this->userExists($user)) return false;
|
||||||
|
$this->db->prepare("UPDATE newssync_users set rights = ? where id is ?", "int", "str")->run($rights, $user);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = ""): int {
|
public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = ""): int {
|
||||||
|
if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
|
||||||
|
if(!$this->userExists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]);
|
||||||
$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");
|
||||||
$id = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle();
|
$feed = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle();
|
||||||
if($id===null) {
|
if($feed===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();
|
$feed = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle();
|
||||||
var_export($id);
|
|
||||||
}
|
}
|
||||||
$this->db->prepare("INSERT INTO newssync_subscriptions(owner,feed) values(?,?)", "str", "int")->run($user,$id);
|
$this->db->prepare("INSERT INTO newssync_subscriptions(owner,feed) values(?,?)", "str", "int")->run($user,$feed);
|
||||||
|
$sub = $this->db->prepare("SELECT id from newssync_subscriptions where owner is ? and feed is ?", "str", "int")->run($user, $feed)->getSingle();
|
||||||
$this->db->commit();
|
$this->db->commit();
|
||||||
return 0;
|
return $sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function subscriptionRemove(int $id): bool {
|
||||||
|
$this->db->begin();
|
||||||
|
$feed = $this->db->prepare("SELECT feed from newssync_subscriptions where id is ?", "int")->run($id)->getSingle();
|
||||||
|
$this->db->prepare("DELETE from newssync_subscriptions where id is ?", "int")->run($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
2
vendor/JKingWeb/NewsSync/Db/Common.php
vendored
2
vendor/JKingWeb/NewsSync/Db/Common.php
vendored
|
@ -6,7 +6,7 @@ use JKingWeb\DrUUID\UUID as UUID;
|
||||||
Trait Common {
|
Trait Common {
|
||||||
protected $transDepth = 0;
|
protected $transDepth = 0;
|
||||||
|
|
||||||
public function schemaVersion(): integer {
|
public function schemaVersion(): int {
|
||||||
try {
|
try {
|
||||||
return $this->data->db->settingGet("schema_version");
|
return $this->data->db->settingGet("schema_version");
|
||||||
} catch(\Throwable $e) {
|
} catch(\Throwable $e) {
|
||||||
|
|
1
vendor/JKingWeb/NewsSync/Exception.php
vendored
1
vendor/JKingWeb/NewsSync/Exception.php
vendored
|
@ -35,6 +35,7 @@ class Exception extends \Exception {
|
||||||
"User/Exception.alreadyExists" => 10403,
|
"User/Exception.alreadyExists" => 10403,
|
||||||
"User/Exception.authMissing" => 10411,
|
"User/Exception.authMissing" => 10411,
|
||||||
"User/Exception.authFailed" => 10412,
|
"User/Exception.authFailed" => 10412,
|
||||||
|
"User/Exception.notAuthorized" => 10421,
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {
|
||||||
|
|
117
vendor/JKingWeb/NewsSync/User.php
vendored
117
vendor/JKingWeb/NewsSync/User.php
vendored
|
@ -7,7 +7,9 @@ class User {
|
||||||
|
|
||||||
protected $data;
|
protected $data;
|
||||||
protected $u;
|
protected $u;
|
||||||
protected $logged = [];
|
protected $authz = true;
|
||||||
|
protected $existSupported = 0;
|
||||||
|
protected $authzSupported = 0;
|
||||||
|
|
||||||
static public function listDrivers(): array {
|
static public function listDrivers(): array {
|
||||||
$sep = \DIRECTORY_SEPARATOR;
|
$sep = \DIRECTORY_SEPARATOR;
|
||||||
|
@ -27,6 +29,8 @@ class User {
|
||||||
$this->data = $data;
|
$this->data = $data;
|
||||||
$driver = $data->conf->userDriver;
|
$driver = $data->conf->userDriver;
|
||||||
$this->u = $driver::create($data);
|
$this->u = $driver::create($data);
|
||||||
|
$this->existSupported = $this->u->driverFunctions("userExists");
|
||||||
|
$this->authzSupported = $this->u->driverFunctions("authorize");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __toString() {
|
public function __toString() {
|
||||||
|
@ -52,11 +56,11 @@ class User {
|
||||||
if($_SERVER['PHP_AUTH_USER']) {
|
if($_SERVER['PHP_AUTH_USER']) {
|
||||||
$out = ["user" => $_SERVER['PHP_AUTH_USER'], "password" => $_SERVER['PHP_AUTH_PW']];
|
$out = ["user" => $_SERVER['PHP_AUTH_USER'], "password" => $_SERVER['PHP_AUTH_PW']];
|
||||||
} else if($_SERVER['REMOTE_USER']) {
|
} else if($_SERVER['REMOTE_USER']) {
|
||||||
$out = ["user" => $_SERVER['REMOTE_USER'], "password" => null];
|
$out = ["user" => $_SERVER['REMOTE_USER'], "password" => ""];
|
||||||
} else {
|
} else {
|
||||||
$out = ["user" => null, "password" => null];
|
$out = ["user" => "", "password" => ""];
|
||||||
}
|
}
|
||||||
if($this->data->conf->userComposeNames && $out["user"] !== null) {
|
if($this->data->conf->userComposeNames && $out["user"] != "") {
|
||||||
$out["user"] = $this->composeName($out["user"]);
|
$out["user"] = $this->composeName($out["user"]);
|
||||||
}
|
}
|
||||||
$this->id = $out["user"];
|
$this->id = $out["user"];
|
||||||
|
@ -65,18 +69,14 @@ class User {
|
||||||
|
|
||||||
public function auth(string $user = null, string $password = null): bool {
|
public function auth(string $user = null, string $password = null): bool {
|
||||||
if($user===null) {
|
if($user===null) {
|
||||||
if($this->data->conf->userAuthPreferHTTP) {
|
if($this->data->conf->userAuthPreferHTTP) return $this->authHTTP();
|
||||||
return $this->authHTTP();
|
return $this->authForm();
|
||||||
} else {
|
|
||||||
return $this->authForm();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if($this->u->auth($user, $password)) {
|
if($this->u->auth($user, $password)) {
|
||||||
$this->authPostprocess($user);
|
$this->authPostProcess($user, $password);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ class User {
|
||||||
$cred = $this->credentialsForm();
|
$cred = $this->credentialsForm();
|
||||||
if(!$cred["user"]) return $this->challengeForm();
|
if(!$cred["user"]) return $this->challengeForm();
|
||||||
if(!$this->u->auth($cred["user"], $cred["password"])) return $this->challengeForm();
|
if(!$this->u->auth($cred["user"], $cred["password"])) return $this->challengeForm();
|
||||||
$this->authPostprocess($cred["user"]);
|
$this->authPostProcess($cred["user"], $cred["password"]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ class User {
|
||||||
$cred = $this->credentialsHTTP();
|
$cred = $this->credentialsHTTP();
|
||||||
if(!$cred["user"]) return $this->challengeHTTP();
|
if(!$cred["user"]) return $this->challengeHTTP();
|
||||||
if(!$this->u->auth($cred["user"], $cred["password"])) return $this->challengeHTTP();
|
if(!$this->u->auth($cred["user"], $cred["password"])) return $this->challengeHTTP();
|
||||||
$this->authPostprocess($cred["user"]);
|
$this->authPostProcess($cred["user"], $cred["password"]);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,19 +101,50 @@ class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function list(string $domain = null): array {
|
public function list(string $domain = null): array {
|
||||||
if($this->u->driveFunctions("userList") != Driver::FUNC_NOT_IMPLEMENTED) {
|
if($this->u->driverFunctions("userList")==User\Driver::FUNC_EXTERNAL) {
|
||||||
|
if($domain===null) {
|
||||||
|
if(!$this->data->user->authorize("@".$domain, "userList")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userList", "user" => $domain]);
|
||||||
|
} else {
|
||||||
|
if(!$this->data->user->authorize("", "userList")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userList", "user" => "all users"]);
|
||||||
|
}
|
||||||
return $this->u->userList($domain);
|
return $this->u->userList($domain);
|
||||||
} else {
|
} else {
|
||||||
// N.B. this does not do any authorization checks
|
|
||||||
return $this->data->db->userList($domain);
|
return $this->data->db->userList($domain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function authorize(string $affectedUser, string $action, int $promoteLevel = 0): bool {
|
||||||
|
if(!$this->authz) return true;
|
||||||
|
if($this->id===null) $this->credentials();
|
||||||
|
if($this->authzSupported) return $this->u->authorize($affectedUser, $action, $promoteLevel);
|
||||||
|
// if the driver does not implement authorization, only allow operation for the current user (this means no new users can be added)
|
||||||
|
if($affectedUser==$this->id && $action != "userRightsSet") return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorizationEnabled(bool $setting = null): bool {
|
||||||
|
if($setting===null) return $this->authz;
|
||||||
|
$this->authz = $setting;
|
||||||
|
return $setting;
|
||||||
|
}
|
||||||
|
|
||||||
public function exists(string $user): bool {
|
public function exists(string $user): bool {
|
||||||
return $this->u->userExists($user);
|
if($this->u->driverFunctions("userExists") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userExists")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userExists", "user" => $user]);
|
||||||
|
}
|
||||||
|
if(!$this->existSupported) return true;
|
||||||
|
$out = $this->u->userExists($user);
|
||||||
|
if($out && $this->existSupported==User\Driver::FUNC_EXTERNAL && !$this->data->db->userExist($user)) {
|
||||||
|
try {$this->data->db->userAdd($user);} catch(\Throwable $e) {}
|
||||||
|
}
|
||||||
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function add($user, $password = null): bool {
|
public function add($user, $password = null): bool {
|
||||||
|
if($this->u->driverFunctions("userAdd") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userAdd")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userAdd", "user" => $user]);
|
||||||
|
}
|
||||||
|
if($this->exists($user)) throw new User\Exception("alreadyExists", ["user" => $user, "action" => "userAdd"]);
|
||||||
$out = $this->u->userAdd($user, $password);
|
$out = $this->u->userAdd($user, $password);
|
||||||
if($out && $this->u->driverFunctions("userAdd") != User\Driver::FUNC_INTERNAL) {
|
if($out && $this->u->driverFunctions("userAdd") != User\Driver::FUNC_INTERNAL) {
|
||||||
try {
|
try {
|
||||||
|
@ -124,6 +155,10 @@ class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remove(string $user): bool {
|
public function remove(string $user): bool {
|
||||||
|
if($this->u->driverFunctions("userRemove") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userRemove")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userRemove", "user" => $user]);
|
||||||
|
}
|
||||||
|
if(!$this->exists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => "userRemove"]);
|
||||||
$out = $this->u->userRemove($user);
|
$out = $this->u->userRemove($user);
|
||||||
if($out && $this->u->driverFunctions("userRemove") != User\Driver::FUNC_INTERNAL) {
|
if($out && $this->u->driverFunctions("userRemove") != User\Driver::FUNC_INTERNAL) {
|
||||||
try {
|
try {
|
||||||
|
@ -134,17 +169,57 @@ class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function passwordSet(string $user, string $password): bool {
|
public function passwordSet(string $user, string $password): bool {
|
||||||
|
if($this->u->driverFunctions("userPasswordSet") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userPasswordSet")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userPasswordSet", "user" => $user]);
|
||||||
|
}
|
||||||
|
if(!$this->exists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => "userPasswordSet"]);
|
||||||
return $this->u->userPasswordSet($user, $password);
|
return $this->u->userPasswordSet($user, $password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function propertiesGet(string $user): array {
|
public function propertiesGet(string $user): array {
|
||||||
return $this->u->userPropertiesGet($user);
|
if($this->u->driverFunctions("userPropertiesGet") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userPropertiesGet")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userPropertiesGet", "user" => $user]);
|
||||||
|
}
|
||||||
|
if(!$this->exists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => "userPropertiesGet"]);
|
||||||
|
$domain = null;
|
||||||
|
if($this->data->conf->userComposeNames) $domain = substr($user,strrpos($user,"@")+1);
|
||||||
|
$init = [
|
||||||
|
"id" => $user,
|
||||||
|
"name" => $user,
|
||||||
|
"rights" => User\Driver::RIGHTS_NONE,
|
||||||
|
"domain" => $domain
|
||||||
|
];
|
||||||
|
if($this->u->driverFunctions("userPropertiesGet") != User\Driver::FUNC_NOT_IMPLEMENTED) {
|
||||||
|
return array_merge($init, $this->u->userPropertiesGet($user));
|
||||||
|
}
|
||||||
|
return $init;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function propertiesSet(string $user, array $properties): array {
|
public function propertiesSet(string $user, array $properties): array {
|
||||||
|
if($this->u->driverFunctions("userPropertiesSet") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userPropertiesSet")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userPropertiesSet", "user" => $user]);
|
||||||
|
}
|
||||||
|
if(!$this->exists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => "userPropertiesSet"]);
|
||||||
return $this->u->userPropertiesSet($user, $properties);
|
return $this->u->userPropertiesSet($user, $properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function rightsGet(string $user): int {
|
||||||
|
if($this->u->driverFunctions("userRightsGet") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userRightsGet")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userRightsGet", "user" => $user]);
|
||||||
|
}
|
||||||
|
// we do not throw an exception here if the user does not exist, because it makes no material difference
|
||||||
|
if(!$this->exists($user)) return User\Driver::RIGHTS_NONE;
|
||||||
|
return $this->u->userRightsGet($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rightsSet(string $user, int $level): bool {
|
||||||
|
if($this->u->driverFunctions("userRightsSet") != User\Driver::FUNC_INTERNAL) {
|
||||||
|
if(!$this->data->user->authorize($user, "userRightsSet")) throw new User\ExceptionAuthz("notAuthorized", ["action" => "userRightsSet", "user" => $user]);
|
||||||
|
}
|
||||||
|
if(!$this->exists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => "userPromote"]);
|
||||||
|
return $this->u->userRightsSet($user, $level);
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: stubs
|
// FIXME: stubs
|
||||||
public function challenge(): bool {throw new User\Exception("authFailed");}
|
public function challenge(): bool {throw new User\Exception("authFailed");}
|
||||||
public function challengeForm(): bool {throw new User\Exception("authFailed");}
|
public function challengeForm(): bool {throw new User\Exception("authFailed");}
|
||||||
|
@ -158,7 +233,11 @@ class User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function authPostprocess(string $user): bool {
|
protected function authPostprocess(string $user, string $password): bool {
|
||||||
|
if($this->u->driverFunctions("auth") != User\Driver::FUNC_INTERNAL && !$this->data->db->userExists($user)) {
|
||||||
|
if($password=="") $password = null;
|
||||||
|
try {$this->data->db->userAdd($user, $password);} catch(\Throwable $e) {}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
9
vendor/JKingWeb/NewsSync/User/Driver.php
vendored
9
vendor/JKingWeb/NewsSync/User/Driver.php
vendored
|
@ -7,10 +7,17 @@ Interface Driver {
|
||||||
const FUNC_INTERNAL = 1;
|
const FUNC_INTERNAL = 1;
|
||||||
const FUNC_EXTERNAL = 2;
|
const FUNC_EXTERNAL = 2;
|
||||||
|
|
||||||
|
const RIGHTS_NONE = 0;
|
||||||
|
const RIGHTS_DOMAIN_MANAGER = 25;
|
||||||
|
const RIGHTS_DOMAIN_ADMIN = 50;
|
||||||
|
const RIGHTS_GLOBAL_MANAGER = 75;
|
||||||
|
const RIGHTS_GLOBAL_ADMIN = 100;
|
||||||
|
|
||||||
static function create(\JKingWeb\NewsSync\RuntimeData $data): Driver;
|
static function create(\JKingWeb\NewsSync\RuntimeData $data): Driver;
|
||||||
static function driverName(): string;
|
static function driverName(): string;
|
||||||
function driverFunctions(string $function = null);
|
function driverFunctions(string $function = null);
|
||||||
function auth(string $user, string $password): bool;
|
function auth(string $user, string $password): bool;
|
||||||
|
function authorize(string $affectedUser, string $action);
|
||||||
function userExists(string $user): bool;
|
function userExists(string $user): bool;
|
||||||
function userAdd(string $user, string $password = null): bool;
|
function userAdd(string $user, string $password = null): bool;
|
||||||
function userRemove(string $user): bool;
|
function userRemove(string $user): bool;
|
||||||
|
@ -18,4 +25,6 @@ Interface Driver {
|
||||||
function userPasswordSet(string $user, string $newPassword, string $oldPassword): bool;
|
function userPasswordSet(string $user, string $newPassword, string $oldPassword): bool;
|
||||||
function userPropertiesGet(string $user): array;
|
function userPropertiesGet(string $user): array;
|
||||||
function userPropertiesSet(string $user, array $properties): array;
|
function userPropertiesSet(string $user, array $properties): array;
|
||||||
|
function userRightsGet(string $user): int;
|
||||||
|
function userRightsSet(string $user, int $level): bool;
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ class DriverInternal implements Driver {
|
||||||
protected $db;
|
protected $db;
|
||||||
protected $functions = [
|
protected $functions = [
|
||||||
"auth" => Driver::FUNC_INTERNAL,
|
"auth" => Driver::FUNC_INTERNAL,
|
||||||
|
"authorize" => Driver::FUNC_INTERNAL,
|
||||||
"userList" => Driver::FUNC_INTERNAL,
|
"userList" => Driver::FUNC_INTERNAL,
|
||||||
"userExists" => Driver::FUNC_INTERNAL,
|
"userExists" => Driver::FUNC_INTERNAL,
|
||||||
"userAdd" => Driver::FUNC_INTERNAL,
|
"userAdd" => Driver::FUNC_INTERNAL,
|
||||||
|
@ -16,6 +17,8 @@ class DriverInternal implements Driver {
|
||||||
"userPasswordSet" => Driver::FUNC_INTERNAL,
|
"userPasswordSet" => Driver::FUNC_INTERNAL,
|
||||||
"userPropertiesGet" => Driver::FUNC_INTERNAL,
|
"userPropertiesGet" => Driver::FUNC_INTERNAL,
|
||||||
"userPropertiesSet" => Driver::FUNC_INTERNAL,
|
"userPropertiesSet" => Driver::FUNC_INTERNAL,
|
||||||
|
"userRightsGet" => Driver::FUNC_INTERNAL,
|
||||||
|
"userRightsSet" => Driver::FUNC_INTERNAL,
|
||||||
];
|
];
|
||||||
|
|
||||||
static public function create(\JKingWeb\NewsSync\RuntimeData $data): Driver {
|
static public function create(\JKingWeb\NewsSync\RuntimeData $data): Driver {
|
||||||
|
|
6
vendor/JKingWeb/NewsSync/User/ExceptionAuthz.php
vendored
Normal file
6
vendor/JKingWeb/NewsSync/User/ExceptionAuthz.php
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
namespace JKingWeb\NewsSync\User;
|
||||||
|
|
||||||
|
class ExceptionAuthz extends Exception {
|
||||||
|
}
|
|
@ -2,51 +2,78 @@
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
namespace JKingWeb\NewsSync\User;
|
namespace JKingWeb\NewsSync\User;
|
||||||
|
|
||||||
trait InternalFunctions {
|
trait InternalFunctions {
|
||||||
|
protected $actor = [];
|
||||||
|
|
||||||
function auth(string $user, string $password): bool {
|
function auth(string $user, string $password): bool {
|
||||||
if(!$this->userExists($user)) return false;
|
if(!$this->data->user->exists($user)) return false;
|
||||||
return true;
|
|
||||||
$hash = $this->db->userPasswordGet($user);
|
$hash = $this->db->userPasswordGet($user);
|
||||||
if(!$hash) return false;
|
if(!$hash) return false;
|
||||||
return password_verify($password, $hash);
|
return password_verify($password, $hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function authorize(string $affectedUser, string $action, int $newRightsLevel = 0): bool {
|
||||||
|
// 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;
|
||||||
|
// get properties of actor if not already available
|
||||||
|
if(!sizeof($this->actor)) $this->actor = $this->data->user->propertiesGet($this->data->user->id);
|
||||||
|
$rights =& $this->actor["rights"];
|
||||||
|
// if actor is a global admin, accept the request
|
||||||
|
if($rights==self::RIGHTS_GLOBAL_ADMIN) return true;
|
||||||
|
// if actor is a common user, deny the request
|
||||||
|
if($rights==self::RIGHTS_NONE) return false;
|
||||||
|
// if actor is not some other sort of admin, deny the request
|
||||||
|
if(!in_array($rights,[self::RIGHTS_GLOBAL_MANAGER,self::RIGHTS_DOMAIN_MANAGER,self::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 != self::RIGHTS_GLOBAL_MANAGER) {
|
||||||
|
$test = "@".$this->actor["domain"];
|
||||||
|
if(substr($affectedUser,-1*strlen($test)) != $test) return false;
|
||||||
|
}
|
||||||
|
// certain actions shouldn't check affected user's rights
|
||||||
|
if(in_array($action, ["userRightsGet","userExists","userList"], true)) return true;
|
||||||
|
if($action=="userRightsSet") {
|
||||||
|
// setting rights above your own (or equal to your own, for managers) is not allowed
|
||||||
|
if($newRightsLevel > $rights || ($rights != self::RIGHTS_DOMAIN_ADMIN && $newRightsLevel==$rights)) return false;
|
||||||
|
}
|
||||||
|
$affectedRights = $this->data->user->rightsGet($affectedUser);
|
||||||
|
// acting for users with rights greater than your own (or equal, for managers) is not allowed
|
||||||
|
if($affectedRights > $rights || ($rights != self::RIGHTS_DOMAIN_ADMIN && $affectedRights==$rights)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function userExists(string $user): bool {
|
function userExists(string $user): bool {
|
||||||
return $this->db->userExists($user);
|
return $this->db->userExists($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
function userAdd(string $user, string $password = null): bool {
|
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);
|
return $this->db->userAdd($user, $password);
|
||||||
}
|
}
|
||||||
|
|
||||||
function userRemove(string $user): bool {
|
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);
|
return $this->db->userRemove($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
function userList(string $domain = null): array {
|
function userList(string $domain = null): array {
|
||||||
// FIXME: add authorization checks
|
|
||||||
return $this->db->userList($domain);
|
return $this->db->userList($domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
function userPasswordSet(string $user, string $newPassword, string $oldPassword): bool {
|
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);
|
return $this->db->userPasswordSet($user, $newPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
function userPropertiesGet(string $user): array {
|
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);
|
return $this->db->userPropertiesGet($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
function userPropertiesSet(string $user, array $properties): array {
|
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);
|
return $this->db->userPropertiesSet($user, $properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function userRightsGet(string $user): int {
|
||||||
|
return $this->db->userRightsGet($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
function userRightsSet(string $user, int $level): bool {
|
||||||
|
return $this->db->userRightsSet($user, $level);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue