Prototype changes to user management

The driver itself has not been expnaded; more is probably required to ensure
metadata is kept in sync and users created when the internal database does
not list a user an external database claims to have
This commit is contained in:
J. King 2020-11-09 13:43:07 -05:00
parent ee050e505c
commit 532ce4a502
4 changed files with 85 additions and 0 deletions

View file

@ -75,6 +75,8 @@ abstract class AbstractException extends \Exception {
"User/Exception.authFailed" => 10412,
"User/ExceptionAuthz.notAuthorized" => 10421,
"User/ExceptionSession.invalid" => 10431,
"User/ExceptionInput.invalidTimezone" => 10441,
"User/ExceptionInput.invalidBoolean" => 10442,
"Feed/Exception.internalError" => 10500,
"Feed/Exception.invalidCertificate" => 10501,
"Feed/Exception.invalidUrl" => 10502,

View file

@ -37,6 +37,9 @@ use JKingWeb\Arsse\Misc\URL;
* associations with articles. There has been an effort to keep public method
* names consistent throughout, but protected methods, having different
* concerns, will typically follow different conventions.
*
* Note that operations on users should be performed with the User class rather
* than the Database class directly. This is to allow for alternate user sources.
*/
class Database {
/** The version number of the latest schema the interface is aware of */
@ -310,6 +313,35 @@ class Database {
$this->db->prepare("UPDATE arsse_users set password = ? where id = ?", "str", "str")->run($hash, $user);
return true;
}
public function userPropertiesGet(string $user): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
} elseif (!$this->userExists($user)) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
$out = $this->db->prepare("SELECT num, admin, lang, tz, sort_asc from arsse_users where id = ?", "str")->run($user)->getRow();
settype($out['admin'], "bool");
settype($out['sort_asc'], "bool");
return $out;
}
public function userPropertiesSet(string $user, array $data): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
} elseif (!$this->userExists($user)) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
$allowed = [
'admin' => "strict bool",
'lang' => "str",
'tz' => "strict str",
'sort_asc' => "strict bool",
];
[$setClause, $setTypes, $setValues] = $this->generateSet($data, $allowed);
return (bool) $this->$db->prepare("UPDATE arsse_users set $setClause where user = ?", $setTypes, "str")->run($setValues, $user)->changes();
}
/** Creates a new session for the given user and returns the session identifier */
public function sessionCreate(string $user): string {

View file

@ -6,6 +6,7 @@
declare(strict_types=1);
namespace JKingWeb\Arsse;
use JKingWeb\Arsse\Misc\ValueInfo as V;
use PasswordGenerator\Generator as PassGen;
class User {
@ -120,4 +121,44 @@ class User {
public function generatePassword(): string {
return (new PassGen)->length(Arsse::$conf->userTempPasswordLength)->get();
}
public function propertiesGet(string $user): array {
// unconditionally retrieve from the database to get at least the user number, and anything else the driver does not provide
$out = Arsse::$db->userPropertiesGet($user);
// layer on the driver's data
$extra = $this->u->userPropertiesGet($user);
foreach (["lang", "tz", "admin", "sort_asc"] as $k) {
if (array_key_exists($k, $extra)) {
$out[$k] = $extra[$k] ?? $out[$k];
}
}
return $out;
}
public function propertiesSet(string $user, array $data): bool {
$in = [];
if (array_key_exists("tz", $data)) {
if (!is_string($data['tz'])) {
throw new User\ExceptionInput("invalidTimezone");
} elseif (!in_array($data['tz'], \DateTimeZone::listIdentifiers())) {
throw new User\ExceptionInput("invalidTimezone", $data['tz']);
}
$in['tz'] = $data['tz'];
}
foreach (["admin", "sort_asc"] as $k) {
if (array_key_exists($k, $data)) {
if (($v = V::normalize($data[$k], V::T_BOOL)) === null) {
throw new User\ExceptionInput("invalidBoolean", $k);
}
$in[$k] = $v;
}
}
if (array_key_exists("lang", $data)) {
$in['lang'] = V::normalize($data['lang'], V::T_STRING | M_NULL);
}
$out = $this->u->userPropertiesSet($user, $in);
// synchronize the internal database
Arsse::$db->userPropertiesSet($user, $in);
return $out;
}
}

View file

@ -0,0 +1,10 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\User;
class ExceptionInput extends Exception {
}