Browse Source

Strip out remnants of the authorizer

rpm
J. King 3 years ago
parent
commit
771f79323c
  1. 1
      lib/AbstractException.php
  2. 166
      lib/Database.php
  3. 31
      lib/User.php
  4. 2
      lib/User/Driver.php
  5. 4
      lib/User/Internal/Driver.php
  6. 5
      locale/en.php
  7. 1
      tests/cases/Database/AbstractTest.php
  8. 42
      tests/cases/Database/SeriesArticle.php
  9. 42
      tests/cases/Database/SeriesFolder.php
  10. 6
      tests/cases/Database/SeriesIcon.php
  11. 49
      tests/cases/Database/SeriesLabel.php
  12. 15
      tests/cases/Database/SeriesSession.php
  13. 81
      tests/cases/Database/SeriesSubscription.php
  14. 55
      tests/cases/Database/SeriesTag.php
  15. 15
      tests/cases/Database/SeriesToken.php
  16. 43
      tests/cases/Database/SeriesUser.php
  17. 1
      tests/cases/ImportExport/TestImportExport.php
  18. 52
      tests/cases/User/TestInternal.php
  19. 132
      tests/cases/User/TestUser.php

1
lib/AbstractException.php

@ -73,7 +73,6 @@ abstract class AbstractException extends \Exception {
"User/Exception.alreadyExists" => 10403,
"User/Exception.authMissing" => 10411,
"User/Exception.authFailed" => 10412,
"User/ExceptionAuthz.notAuthorized" => 10421,
"User/ExceptionSession.invalid" => 10431,
"User/ExceptionInput.invalidTimezone" => 10441,
"User/ExceptionInput.invalidBoolean" => 10442,

166
lib/Database.php

@ -242,9 +242,6 @@ class Database {
/** Returns whether the specified user exists in the database */
public function userExists(string $user): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
return (bool) $this->db->prepare("SELECT count(*) from arsse_users where id = ?", "str")->run($user)->getValue();
}
@ -254,9 +251,7 @@ class Database {
* @param string $passwordThe user's password in cleartext. It will be stored hashed
*/
public function userAdd(string $user, string $password): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
} elseif ($this->userExists($user)) {
if ($this->userExists($user)) {
throw new User\Exception("alreadyExists", ["action" => __FUNCTION__, "user" => $user]);
}
$hash = (strlen($password) > 0) ? password_hash($password, \PASSWORD_DEFAULT) : "";
@ -267,9 +262,6 @@ class Database {
/** Removes a user from the database */
public function userRemove(string $user): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if ($this->db->prepare("DELETE from arsse_users where id = ?", "str")->run($user)->changes() < 1) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
@ -279,9 +271,6 @@ class Database {
/** Returns a flat, indexed array of all users in the database */
public function userList(): array {
$out = [];
if (!Arsse::$user->authorize("", __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => ""]);
}
foreach ($this->db->query("SELECT id from arsse_users") as $user) {
$out[] = $user['id'];
}
@ -290,9 +279,7 @@ class Database {
/** Retrieves the hashed password of a user */
public function userPasswordGet(string $user): ?string {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
} elseif (!$this->userExists($user)) {
if (!$this->userExists($user)) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
return $this->db->prepare("SELECT password from arsse_users where id = ?", "str")->run($user)->getValue();
@ -304,9 +291,7 @@ class Database {
* @param string $password The new password, in cleartext. The password will be stored hashed. If null is passed, the password is unset and authentication not possible
*/
public function userPasswordSet(string $user, string $password = null): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
} elseif (!$this->userExists($user)) {
if (!$this->userExists($user)) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
$hash = (strlen($password ?? "") > 0) ? password_hash($password, \PASSWORD_DEFAULT) : $password;
@ -315,9 +300,7 @@ class Database {
}
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)) {
if (!$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();
@ -327,9 +310,7 @@ class Database {
}
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)) {
if (!$this->userExists($user)) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
$allowed = [
@ -339,16 +320,12 @@ class Database {
'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();
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 {
// If the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// generate a new session ID and expiry date
$id = UUID::mint()->hex;
$expires = Date::add(Arsse::$conf->userSessionTimeout);
@ -367,10 +344,6 @@ class Database {
* @param string|null $id The identifier of the session to destroy
*/
public function sessionDestroy(string $user, string $id = null): bool {
// If the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if (is_null($id)) {
// delete all sessions and report success unconditionally if no identifier was specified
$this->db->prepare("DELETE FROM arsse_sessions where \"user\" = ?", "str")->run($user);
@ -424,10 +397,7 @@ class Database {
* @param \DateTimeInterface|null $expires An optional expiry date and time for the token
*/
public function tokenCreate(string $user, string $class, string $id = null, \DateTimeInterface $expires = null): string {
// If the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
} elseif (!$this->userExists($user)) {
if (!$this->userExists($user)) {
throw new User\Exception("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
// generate a token if it's not provided
@ -445,10 +415,6 @@ class Database {
* @param string|null $id The ID of a specific token, or null for all tokens in the class
*/
public function tokenRevoke(string $user, string $class, string $id = null): bool {
// If the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if (is_null($id)) {
$out = $this->db->prepare("DELETE FROM arsse_tokens where \"user\" = ? and class = ?", "str", "str")->run($user, $class)->changes();
} else {
@ -484,10 +450,6 @@ class Database {
* @param array $data An associative array defining the folder
*/
public function folderAdd(string $user, array $data): int {
// If the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// normalize folder's parent, if there is one
$parent = array_key_exists("parent", $data) ? $this->folderValidateId($user, $data['parent'])['id'] : null;
// validate the folder name and parent (if specified); this also checks for duplicates
@ -512,10 +474,6 @@ class Database {
* @param boolean $recursive Whether to list all descendents (true) or only direct children (false)
*/
public function folderList(string $user, $parent = null, bool $recursive = true): Db\Result {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// check to make sure the parent exists, if one is specified
$parent = $this->folderValidateId($user, $parent)['id'];
$q = new Query(
@ -548,9 +506,6 @@ class Database {
* @param integer $id The identifier of the folder to delete
*/
public function folderRemove(string $user, $id): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if (!ValueInfo::id($id)) {
throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "folder", 'type' => "int > 0"]);
}
@ -563,9 +518,6 @@ class Database {
/** Returns the identifier, name, and parent of the given folder as an associative array */
public function folderPropertiesGet(string $user, $id): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if (!ValueInfo::id($id)) {
throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "folder", 'type' => "int > 0"]);
}
@ -590,9 +542,6 @@ class Database {
* @param array $data An associative array of properties to modify. Anything not specified will remain unchanged
*/
public function folderPropertiesSet(string $user, $id, array $data): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// verify the folder belongs to the user
$in = $this->folderValidateId($user, $id, true);
$name = array_key_exists("name", $data);
@ -739,9 +688,6 @@ class Database {
* @param boolean $discover Whether to perform newsfeed discovery if $url points to an HTML document
*/
public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = "", bool $discover = true): int {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// get the ID of the underlying feed, or add it if it's not yet in the database
$feedID = $this->feedAdd($url, $fetchUser, $fetchPassword, $discover);
// Add the feed to the user's subscriptions and return the new subscription's ID.
@ -756,9 +702,6 @@ class Database {
* @param integer|null $id The numeric identifier of a particular subscription; used internally by subscriptionPropertiesGet
*/
public function subscriptionList(string $user, $folder = null, bool $recursive = true, int $id = null): Db\Result {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// validate inputs
$folder = $this->folderValidateId($user, $folder)['id'];
// create a complex query
@ -804,9 +747,6 @@ class Database {
/** Returns the number of subscriptions in a folder, counting recursively */
public function subscriptionCount(string $user, $folder = null): int {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// validate inputs
$folder = $this->folderValidateId($user, $folder)['id'];
// create a complex query
@ -829,9 +769,6 @@ class Database {
* configurable retention period for newsfeeds
*/
public function subscriptionRemove(string $user, $id): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if (!ValueInfo::id($id)) {
throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "feed", 'type' => "int > 0"]);
}
@ -861,9 +798,6 @@ class Database {
* - "unread": The number of unread articles associated with the subscription
*/
public function subscriptionPropertiesGet(string $user, $id): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
if (!ValueInfo::id($id)) {
throw new Db\ExceptionInput("typeViolation", ["action" => __FUNCTION__, "field" => "feed", 'type' => "int > 0"]);
}
@ -888,9 +822,6 @@ class Database {
* @param array $data An associative array of properties to modify; any keys not specified will be left unchanged
*/
public function subscriptionPropertiesSet(string $user, $id, array $data): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$tr = $this->db->begin();
// validate the ID
$id = $this->subscriptionValidateId($user, $id, true)['id'];
@ -934,9 +865,6 @@ class Database {
* @param boolean $byName Whether to return the tag names (true) instead of the numeric tag identifiers (false)
*/
public function subscriptionTagsGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->subscriptionValidateId($user, $id, true);
$field = !$byName ? "id" : "name";
$out = $this->db->prepare("SELECT $field from arsse_tags where id in (select tag from arsse_tag_members where subscription = ? and assigned = 1) order by $field", "int")->run($id)->getAll();
@ -961,9 +889,6 @@ class Database {
$q = new Query("SELECT i.id, i.url, i.type, $data from arsse_subscriptions as s join arsse_feeds as f on s.feed = f.id left join arsse_icons as i on f.icon = i.id");
$q->setWhere("s.id = ?", "int", $id);
if (isset($user)) {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$q->setWhere("s.owner = ?", "str", $user);
}
$out = $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getRow();
@ -975,9 +900,6 @@ class Database {
/** Returns the time at which any of a user's subscriptions (or a specific subscription) was last refreshed, as a DateTimeImmutable object */
public function subscriptionRefreshed(string $user, int $id = null): ?\DateTimeImmutable {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$q = new Query("SELECT max(arsse_feeds.updated) from arsse_feeds join arsse_subscriptions on arsse_subscriptions.feed = arsse_feeds.id");
$q->setWhere("arsse_subscriptions.owner = ?", "str", $user);
if ($id) {
@ -1304,9 +1226,6 @@ class Database {
* @param string $user The user whose subscription icons are to be retrieved
*/
public function iconList(string $user): Db\Result {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
return $this->db->prepare("SELECT distinct i.id, i.url, i.type, i.data from arsse_icons as i join arsse_feeds as f on i.id = f.icon join arsse_subscriptions as s on s.feed = f.id where s.owner = ?", "str")->run($user);
}
@ -1646,9 +1565,6 @@ class Database {
* @param array $sort The columns to sort the result by eg. "edition desc" in decreasing order of importance
*/
public function articleList(string $user, Context $context = null, array $fields = ["id"], array $sort = []): Db\Result {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// make a base query based on context and output columns
$context = $context ?? new Context;
$q = $this->articleQuery($user, $context, $fields);
@ -1693,9 +1609,6 @@ class Database {
* @param Context $context The search context
*/
public function articleCount(string $user, Context $context = null): int {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$context = $context ?? new Context;
$q = $this->articleQuery($user, $context, []);
return (int) $this->db->prepare($q->getQuery(), $q->getTypes())->run($q->getValues())->getValue();
@ -1714,9 +1627,6 @@ class Database {
* @param Context $context The query context to match articles against
*/
public function articleMark(string $user, array $data, Context $context = null): int {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$data = [
'read' => $data['read'] ?? null,
'starred' => $data['starred'] ?? null,
@ -1800,9 +1710,6 @@ class Database {
* - "read": The count of starred articles which are read
*/
public function articleStarred(string $user): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
return $this->db->prepare(
"SELECT
count(*) as total,
@ -1822,9 +1729,6 @@ class Database {
* @param boolean $byName Whether to return the label names (true) instead of the numeric label identifiers (false)
*/
public function articleLabelsGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$id = $this->articleValidateId($user, $id)['article'];
$field = !$byName ? "id" : "name";
$out = $this->db->prepare("SELECT $field from arsse_labels join arsse_label_members on arsse_label_members.label = arsse_labels.id where owner = ? and article = ? and assigned = 1 order by $field", "str", "int")->run($user, $id)->getAll();
@ -1833,9 +1737,6 @@ class Database {
/** Returns the author-supplied categories associated with an article */
public function articleCategoriesGet(string $user, $id): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$id = $this->articleValidateId($user, $id)['article'];
$out = $this->db->prepare("SELECT name from arsse_categories where article = ? order by name", "int")->run($id)->getAll();
if (!$out) {
@ -1937,9 +1838,6 @@ class Database {
/** Returns the numeric identifier of the most recent edition of an article matching the given context */
public function editionLatest(string $user, Context $context = null): int {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$context = $context ?? new Context;
$q = new Query("SELECT max(arsse_editions.id) from arsse_editions left join arsse_articles on article = arsse_articles.id join arsse_subscriptions on arsse_articles.feed = arsse_subscriptions.feed and arsse_subscriptions.owner = ?", "str", $user);
if ($context->subscription()) {
@ -1968,10 +1866,6 @@ class Database {
* @param array $data An associative array defining the label's properties; currently only "name" is understood
*/
public function labelAdd(string $user, array $data): int {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// validate the label name
$name = array_key_exists("name", $data) ? $data['name'] : "";
$this->labelValidateName($name, true);
@ -1992,10 +1886,6 @@ class Database {
* @param boolean $includeEmpty Whether to include (true) or supress (false) labels which have no articles assigned to them
*/
public function labelList(string $user, bool $includeEmpty = true): Db\Result {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
return $this->db->prepare(
"SELECT * FROM (
SELECT
@ -2032,9 +1922,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelRemove(string $user, $id, bool $byName = false): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->labelValidateId($user, $id, $byName, false);
$field = $byName ? "name" : "id";
$type = $byName ? "str" : "int";
@ -2059,9 +1946,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelPropertiesGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->labelValidateId($user, $id, $byName, false);
$field = $byName ? "name" : "id";
$type = $byName ? "str" : "int";
@ -2101,9 +1985,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelPropertiesSet(string $user, $id, array $data, bool $byName = false): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->labelValidateId($user, $id, $byName, false);
if (isset($data['name'])) {
$this->labelValidateName($data['name']);
@ -2132,9 +2013,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the label's name (true) or identifier (false)
*/
public function labelArticlesGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// just do a syntactic check on the label ID
$this->labelValidateId($user, $id, $byName, false);
$field = !$byName ? "id" : "name";
@ -2161,9 +2039,6 @@ class Database {
*/
public function labelArticlesSet(string $user, $id, Context $context, int $mode = self::ASSOC_ADD, bool $byName = false): int {
assert(in_array($mode, [self::ASSOC_ADD, self::ASSOC_REMOVE, self::ASSOC_REPLACE]), new Exception("constantUnknown", $mode));
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// validate the tag ID, and get the numeric ID if matching by name
$id = $this->labelValidateId($user, $id, $byName, true)['id'];
// get the list of articles matching the context
@ -2269,10 +2144,6 @@ class Database {
* @param array $data An associative array defining the tag's properties; currently only "name" is understood
*/
public function tagAdd(string $user, array $data): int {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// validate the tag name
$name = array_key_exists("name", $data) ? $data['name'] : "";
$this->tagValidateName($name, true);
@ -2292,10 +2163,6 @@ class Database {
* @param boolean $includeEmpty Whether to include (true) or supress (false) tags which have no subscriptions assigned to them
*/
public function tagList(string $user, bool $includeEmpty = true): Db\Result {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
return $this->db->prepare(
"SELECT * FROM (
SELECT
@ -2323,10 +2190,6 @@ class Database {
* @param string $user The user whose tags are to be listed
*/
public function tagSummarize(string $user): Db\Result {
// if the user isn't authorized to perform this action then throw an exception.
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
return $this->db->prepare(
"SELECT
arsse_tags.id as id,
@ -2348,9 +2211,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the tag's name (true) or identifier (false)
*/
public function tagRemove(string $user, $id, bool $byName = false): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->tagValidateId($user, $id, $byName, false);
$field = $byName ? "name" : "id";
$type = $byName ? "str" : "int";
@ -2374,9 +2234,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the tag's name (true) or identifier (false)
*/
public function tagPropertiesGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->tagValidateId($user, $id, $byName, false);
$field = $byName ? "name" : "id";
$type = $byName ? "str" : "int";
@ -2404,9 +2261,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the tag's name (true) or identifier (false)
*/
public function tagPropertiesSet(string $user, $id, array $data, bool $byName = false): bool {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
$this->tagValidateId($user, $id, $byName, false);
if (isset($data['name'])) {
$this->tagValidateName($data['name']);
@ -2435,9 +2289,6 @@ class Database {
* @param boolean $byName Whether to interpret the $id parameter as the tag's name (true) or identifier (false)
*/
public function tagSubscriptionsGet(string $user, $id, bool $byName = false): array {
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// just do a syntactic check on the tag ID
$this->tagValidateId($user, $id, $byName, false);
$field = !$byName ? "id" : "name";
@ -2464,9 +2315,6 @@ class Database {
*/
public function tagSubscriptionsSet(string $user, $id, array $subscriptions, int $mode = self::ASSOC_ADD, bool $byName = false): int {
assert(in_array($mode, [self::ASSOC_ADD, self::ASSOC_REMOVE, self::ASSOC_REPLACE]), new Exception("constantUnknown", $mode));
if (!Arsse::$user->authorize($user, __FUNCTION__)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]);
}
// validate the tag ID, and get the numeric ID if matching by name
$id = $this->tagValidateId($user, $id, $byName, true)['id'];
// an empty subscription list is a special case

31
lib/User.php

@ -27,11 +27,6 @@ class User {
return (string) $this->id;
}
public function authorize(string $affectedUser, string $action): bool {
// at one time there was a complicated authorization system; it exists vestigially to support a later revival if desired
return $this->u->authorize($affectedUser, $action);
}
public function auth(string $user, string $password): bool {
$prevUser = $this->id;
$this->id = $user;
@ -50,34 +45,18 @@ class User {
}
public function list(): array {
$func = "userList";
if (!$this->authorize("", $func)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => ""]);
}
return $this->u->userList();
}
public function exists(string $user): bool {
$func = "userExists";
if (!$this->authorize($user, $func)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
}
return $this->u->userExists($user);
}
public function add($user, $password = null): string {
$func = "userAdd";
if (!$this->authorize($user, $func)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
}
return $this->u->userAdd($user, $password) ?? $this->u->userAdd($user, $this->generatePassword());
}
public function remove(string $user): bool {
$func = "userRemove";
if (!$this->authorize($user, $func)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
}
try {
return $this->u->userRemove($user);
} finally { // @codeCoverageIgnore
@ -89,10 +68,6 @@ class User {
}
public function passwordSet(string $user, string $newPassword = null, $oldPassword = null): string {
$func = "userPasswordSet";
if (!$this->authorize($user, $func)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
}
$out = $this->u->userPasswordSet($user, $newPassword, $oldPassword) ?? $this->u->userPasswordSet($user, $this->generatePassword(), $oldPassword);
if (Arsse::$db->userExists($user)) {
// if the password change was successful and the user exists, set the internal password to the same value
@ -104,10 +79,6 @@ class User {
}
public function passwordUnset(string $user, $oldPassword = null): bool {
$func = "userPasswordUnset";
if (!$this->authorize($user, $func)) {
throw new User\ExceptionAuthz("notAuthorized", ["action" => $func, "user" => $user]);
}
$out = $this->u->userPasswordUnset($user, $oldPassword);
if (Arsse::$db->userExists($user)) {
// if the password change was successful and the user exists, set the internal password to the same value
@ -154,7 +125,7 @@ class User {
}
}
if (array_key_exists("lang", $data)) {
$in['lang'] = V::normalize($data['lang'], V::T_STRING | M_NULL);
$in['lang'] = V::normalize($data['lang'], V::T_STRING | V::M_NULL);
}
$out = $this->u->userPropertiesSet($user, $in);
// synchronize the internal database

2
lib/User/Driver.php

@ -17,8 +17,6 @@ interface Driver {
public static function driverName(): string;
// authenticates a user against their name and password
public function auth(string $user, string $password): bool;
// check whether a user is authorized to perform a certain action; not currently used and subject to change
public function authorize(string $affectedUser, string $action): bool;
// checks whether a user exists
public function userExists(string $user): bool;
// adds a user

4
lib/User/Internal/Driver.php

@ -32,10 +32,6 @@ class Driver implements \JKingWeb\Arsse\User\Driver {
return password_verify($password, $hash);
}
public function authorize(string $affectedUser, string $action): bool {
return true;
}
public function userExists(string $user): bool {
return Arsse::$db->userExists($user);
}

5
locale/en.php

@ -138,11 +138,6 @@ return [
'Exception.JKingWeb/Arsse/User/Exception.doesNotExist' => 'Could not perform action "{action}" because the user {user} does not exist',
'Exception.JKingWeb/Arsse/User/Exception.authMissing' => 'Please log in to proceed',
'Exception.JKingWeb/Arsse/User/Exception.authFailed' => 'Authentication failed',
'Exception.JKingWeb/Arsse/User/ExceptionAuthz.notAuthorized' =>
'{action, select,
userList {Authenticated user is not authorized to view the user list}
other {Authenticated user is not authorized to perform the action "{action}" on behalf of {user}}
}',
'Exception.JKingWeb/Arsse/User/ExceptionSession.invalid' => 'Session with ID {0} does not exist',
'Exception.JKingWeb/Arsse/Feed/Exception.internalError' => 'Could not download feed "{url}" because of an internal error which is probably a bug',
'Exception.JKingWeb/Arsse/Feed/Exception.invalidCertificate' => 'Could not download feed "{url}" because its server is serving an invalid SSL certificate',

1
tests/cases/Database/AbstractTest.php

@ -74,7 +74,6 @@ abstract class AbstractTest extends \JKingWeb\Arsse\Test\AbstractTest {
Arsse::$db->driverSchemaUpdate();
// create a mock user manager
Arsse::$user = \Phake::mock(User::class);
\Phake::when(Arsse::$user)->authorize->thenReturn(true);
// call the series-specific setup method
$setUp = "setUp".$this->series;
$this->$setUp();

42
tests/cases/Database/SeriesArticle.php

@ -597,12 +597,6 @@ trait SeriesArticle {
];
}
public function testListArticlesWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->articleList($this->user);
}
public function testMarkNothing(): void {
$this->assertSame(0, Arsse::$db->articleMark($this->user, []));
}
@ -967,12 +961,6 @@ trait SeriesArticle {
$this->compareExpectations(static::$drv, $state);
}
public function testMarkArticlesWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->articleMark($this->user, ['read' => false]);
}
public function testCountArticles(): void {
$setSize = (new \ReflectionClassConstant(Database::class, "LIMIT_SET_SIZE"))->getValue();
$this->assertSame(2, Arsse::$db->articleCount("john.doe@example.com", (new Context)->starred(true)));
@ -981,12 +969,6 @@ trait SeriesArticle {
$this->assertSame(10, Arsse::$db->articleCount("john.doe@example.com", (new Context)->articles(range(1, $setSize * 3))));
}
public function testCountArticlesWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->articleCount($this->user);
}
public function testFetchStarredCounts(): void {
$exp1 = ['total' => 2, 'unread' => 1, 'read' => 1];
$exp2 = ['total' => 0, 'unread' => 0, 'read' => 0];
@ -994,12 +976,6 @@ trait SeriesArticle {
$this->assertEquals($exp2, Arsse::$db->articleStarred("jane.doe@example.com"));
}
public function testFetchStarredCountsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->articleStarred($this->user);
}
public function testFetchLatestEdition(): void {
$this->assertSame(1001, Arsse::$db->editionLatest($this->user));
$this->assertSame(4, Arsse::$db->editionLatest($this->user, (new Context)->subscription(12)));
@ -1010,12 +986,6 @@ trait SeriesArticle {
Arsse::$db->editionLatest($this->user, (new Context)->subscription(1));
}
public function testFetchLatestEditionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->editionLatest($this->user);
}
public function testListTheLabelsOfAnArticle(): void {
$this->assertEquals([1,2], Arsse::$db->articleLabelsGet("john.doe@example.com", 1));
$this->assertEquals([2], Arsse::$db->articleLabelsGet("john.doe@example.com", 5));
@ -1030,12 +1000,6 @@ trait SeriesArticle {
Arsse::$db->articleLabelsGet($this->user, 101);
}
public function testListTheLabelsOfAnArticleWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->articleLabelsGet("john.doe@example.com", 1);
}
public function testListTheCategoriesOfAnArticle(): void {
$exp = ["Fascinating", "Logical"];
$this->assertSame($exp, Arsse::$db->articleCategoriesGet($this->user, 19));
@ -1050,12 +1014,6 @@ trait SeriesArticle {
Arsse::$db->articleCategoriesGet($this->user, 101);
}
public function testListTheCategoriesOfAnArticleWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->articleCategoriesGet($this->user, 19);
}
/** @dataProvider provideArrayContextOptions */
public function testUseTooFewValuesInArrayContext(string $option): void {
$this->assertException("tooShort", "Db", "ExceptionInput");

42
tests/cases/Database/SeriesFolder.php

@ -102,7 +102,6 @@ trait SeriesFolder {
$user = "john.doe@example.com";
$folderID = $this->nextID("arsse_folders");
$this->assertSame($folderID, Arsse::$db->folderAdd($user, ['name' => "Entertainment"]));
\Phake::verify(Arsse::$user)->authorize($user, "folderAdd");
$state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'name']]);
$state['arsse_folders']['rows'][] = [$folderID, $user, null, "Entertainment"];
$this->compareExpectations(static::$drv, $state);
@ -117,7 +116,6 @@ trait SeriesFolder {
$user = "john.doe@example.com";
$folderID = $this->nextID("arsse_folders");
$this->assertSame($folderID, Arsse::$db->folderAdd($user, ['name' => "GNOME", 'parent' => 2]));
\Phake::verify(Arsse::$user)->authorize($user, "folderAdd");
$state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'name']]);
$state['arsse_folders']['rows'][] = [$folderID, $user, 2, "GNOME"];
$this->compareExpectations(static::$drv, $state);
@ -153,12 +151,6 @@ trait SeriesFolder {
Arsse::$db->folderAdd("john.doe@example.com", ['name' => " "]);
}
public function testAddAFolderWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->folderAdd("john.doe@example.com", ['name' => "Sociology"]);
}
public function testListRootFolders(): void {
$exp = [
['id' => 5, 'name' => "Politics", 'parent' => null, 'children' => 0, 'feeds' => 2],
@ -171,9 +163,6 @@ trait SeriesFolder {
$this->assertResult($exp, Arsse::$db->folderList("jane.doe@example.com", null, false));
$exp = [];
$this->assertResult($exp, Arsse::$db->folderList("admin@example.net", null, false));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "folderList");
\Phake::verify(Arsse::$user)->authorize("jane.doe@example.com", "folderList");
\Phake::verify(Arsse::$user)->authorize("admin@example.net", "folderList");
}
public function testListFoldersRecursively(): void {
@ -193,8 +182,6 @@ trait SeriesFolder {
$this->assertResult($exp, Arsse::$db->folderList("john.doe@example.com", 1, true));
$exp = [];
$this->assertResult($exp, Arsse::$db->folderList("jane.doe@example.com", 4, true));
\Phake::verify(Arsse::$user, \Phake::times(2))->authorize("john.doe@example.com", "folderList");
\Phake::verify(Arsse::$user)->authorize("jane.doe@example.com", "folderList");
}
public function testListFoldersOfAMissingParent(): void {
@ -207,15 +194,8 @@ trait SeriesFolder {
Arsse::$db->folderList("john.doe@example.com", 4); // folder ID 4 belongs to Jane
}
public function testListFoldersWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->folderList("john.doe@example.com");
}
public function testRemoveAFolder(): void {
$this->assertTrue(Arsse::$db->folderRemove("john.doe@example.com", 6));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "folderRemove");
$state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'name']]);
array_pop($state['arsse_folders']['rows']);
$this->compareExpectations(static::$drv, $state);
@ -223,7 +203,6 @@ trait SeriesFolder {
public function testRemoveAFolderTree(): void {
$this->assertTrue(Arsse::$db->folderRemove("john.doe@example.com", 1));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "folderRemove");
$state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'name']]);
foreach ([0,1,2,5] as $index) {
unset($state['arsse_folders']['rows'][$index]);
@ -246,12 +225,6 @@ trait SeriesFolder {
Arsse::$db->folderRemove("john.doe@example.com", 4); // folder ID 4 belongs to Jane
}
public function testRemoveAFolderWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->folderRemove("john.doe@example.com", 1);
}
public function testGetThePropertiesOfAFolder(): void {
$exp = [
'id' => 6,
@ -259,7 +232,6 @@ trait SeriesFolder {
'parent' => 2,
];
$this->assertArraySubset($exp, Arsse::$db->folderPropertiesGet("john.doe@example.com", 6));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "folderPropertiesGet");
}
public function testGetThePropertiesOfAMissingFolder(): void {
@ -277,19 +249,12 @@ trait SeriesFolder {
Arsse::$db->folderPropertiesGet("john.doe@example.com", 4); // folder ID 4 belongs to Jane
}
public function testGetThePropertiesOfAFolderWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->folderPropertiesGet("john.doe@example.com", 1);
}
public function testMakeNoChangesToAFolder(): void {
$this->assertFalse(Arsse::$db->folderPropertiesSet("john.doe@example.com", 6, []));
}
public function testRenameAFolder(): void {
$this->assertTrue(Arsse::$db->folderPropertiesSet("john.doe@example.com", 6, ['name' => "Opinion"]));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "folderPropertiesSet");
$state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'name']]);
$state['arsse_folders']['rows'][5][3] = "Opinion";
$this->compareExpectations(static::$drv, $state);
@ -316,7 +281,6 @@ trait SeriesFolder {
public function testMoveAFolder(): void {
$this->assertTrue(Arsse::$db->folderPropertiesSet("john.doe@example.com", 6, ['parent' => 5]));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "folderPropertiesSet");
$state = $this->primeExpectations($this->data, ['arsse_folders' => ['id','owner', 'parent', 'name']]);
$state['arsse_folders']['rows'][5][2] = 5; // parent should have changed
$this->compareExpectations(static::$drv, $state);
@ -371,10 +335,4 @@ trait SeriesFolder {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->folderPropertiesSet("john.doe@example.com", 4, ['parent' => null]); // folder ID 4 belongs to Jane
}
public function testSetThePropertiesOfAFolderWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->folderPropertiesSet("john.doe@example.com", 1, ['parent' => null]);
}
}

6
tests/cases/Database/SeriesIcon.php

@ -94,10 +94,4 @@ trait SeriesIcon {
];
$this->assertResult($exp, Arsse::$db->iconList("jane.doe@example.com"));
}
public function testListTheIconsOfAUserWithoutAuthority() {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->iconList("jane.doe@example.com");
}
}

49
tests/cases/Database/SeriesLabel.php

@ -253,7 +253,6 @@ trait SeriesLabel {
$user = "john.doe@example.com";
$labelID = $this->nextID("arsse_labels");
$this->assertSame($labelID, Arsse::$db->labelAdd($user, ['name' => "Entertaining"]));
\Phake::verify(Arsse::$user)->authorize($user, "labelAdd");
$state = $this->primeExpectations($this->data, $this->checkLabels);
$state['arsse_labels']['rows'][] = [$labelID, $user, "Entertaining"];
$this->compareExpectations(static::$drv, $state);
@ -279,12 +278,6 @@ trait SeriesLabel {
Arsse::$db->labelAdd("john.doe@example.com", ['name' => " "]);
}
public function testAddALabelWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelAdd("john.doe@example.com", ['name' => "Boring"]);
}
public function testListLabels(): void {
$exp = [
['id' => 2, 'name' => "Fascinating", 'articles' => 3, 'read' => 1],
@ -298,18 +291,10 @@ trait SeriesLabel {
$this->assertResult($exp, Arsse::$db->labelList("jane.doe@example.com"));
$exp = [];
$this->assertResult($exp, Arsse::$db->labelList("jane.doe@example.com", false));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "labelList");
}
public function testListLabelsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelList("john.doe@example.com");
}
public function testRemoveALabel(): void {
$this->assertTrue(Arsse::$db->labelRemove("john.doe@example.com", 1));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "labelRemove");
$state = $this->primeExpectations($this->data, $this->checkLabels);
array_shift($state['arsse_labels']['rows']);
$this->compareExpectations(static::$drv, $state);
@ -317,7 +302,6 @@ trait SeriesLabel {
public function testRemoveALabelByName(): void {
$this->assertTrue(Arsse::$db->labelRemove("john.doe@example.com", "Interesting", true));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "labelRemove");
$state = $this->primeExpectations($this->data, $this->checkLabels);
array_shift($state['arsse_labels']['rows']);
$this->compareExpectations(static::$drv, $state);
@ -343,12 +327,6 @@ trait SeriesLabel {
Arsse::$db->labelRemove("john.doe@example.com", 3); // label ID 3 belongs to Jane
}
public function testRemoveALabelWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelRemove("john.doe@example.com", 1);
}
public function testGetThePropertiesOfALabel(): void {
$exp = [
'id' => 2,
@ -358,7 +336,6 @@ trait SeriesLabel {
];
$this->assertArraySubset($exp, Arsse::$db->labelPropertiesGet("john.doe@example.com", 2));
$this->assertArraySubset($exp, Arsse::$db->labelPropertiesGet("john.doe@example.com", "Fascinating", true));
\Phake::verify(Arsse::$user, \Phake::times(2))->authorize("john.doe@example.com", "labelPropertiesGet");
}
public function testGetThePropertiesOfAMissingLabel(): void {
@ -381,19 +358,12 @@ trait SeriesLabel {
Arsse::$db->labelPropertiesGet("john.doe@example.com", 3); // label ID 3 belongs to Jane
}
public function testGetThePropertiesOfALabelWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelPropertiesGet("john.doe@example.com", 1);
}
public function testMakeNoChangesToALabel(): void {
$this->assertFalse(Arsse::$db->labelPropertiesSet("john.doe@example.com", 1, []));
}
public function testRenameALabel(): void {
$this->assertTrue(Arsse::$db->labelPropertiesSet("john.doe@example.com", 1, ['name' => "Curious"]));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "labelPropertiesSet");
$state = $this->primeExpectations($this->data, $this->checkLabels);
$state['arsse_labels']['rows'][0][2] = "Curious";
$this->compareExpectations(static::$drv, $state);
@ -401,7 +371,6 @@ trait SeriesLabel {
public function testRenameALabelByName(): void {
$this->assertTrue(Arsse::$db->labelPropertiesSet("john.doe@example.com", "Interesting", ['name' => "Curious"], true));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "labelPropertiesSet");
$state = $this->primeExpectations($this->data, $this->checkLabels);
$state['arsse_labels']['rows'][0][2] = "Curious";
$this->compareExpectations(static::$drv, $state);
@ -447,12 +416,6 @@ trait SeriesLabel {
Arsse::$db->labelPropertiesSet("john.doe@example.com", 3, ['name' => "Exciting"]); // label ID 3 belongs to Jane
}
public function testSetThePropertiesOfALabelWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelPropertiesSet("john.doe@example.com", 1, ['name' => "Exciting"]);
}
public function testListLabelledArticles(): void {
$exp = [1,19];
$this->assertEquals($exp, Arsse::$db->labelArticlesGet("john.doe@example.com", 1));
@ -475,12 +438,6 @@ trait SeriesLabel {
Arsse::$db->labelArticlesGet("john.doe@example.com", -1);
}
public function testListLabelledArticlesWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelArticlesGet("john.doe@example.com", 1);
}
public function testApplyALabelToArticles(): void {
Arsse::$db->labelArticlesSet("john.doe@example.com", 1, (new Context)->articles([2,5]));
$state = $this->primeExpectations($this->data, $this->checkMembers);
@ -540,10 +497,4 @@ trait SeriesLabel {
$state['arsse_label_members']['rows'][2][3] = 0;
$this->compareExpectations(static::$drv, $state);
}
public function testApplyALabelToArticlesWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->labelArticlesSet("john.doe@example.com", 1, (new Context)->articles([2,5]));
}
}

15
tests/cases/Database/SeriesSession.php

@ -70,9 +70,6 @@ trait SeriesSession {
$state = $this->primeExpectations($this->data, ['arsse_sessions' => ["id", "created", "expires", "user"]]);
$state['arsse_sessions']['rows'][3][2] = Date::transform(Date::add(Arsse::$conf->userSessionTimeout, $now), "sql");
$this->compareExpectations(static::$drv, $state);
// session resumption should not check authorization
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertArraySubset($exp1, Arsse::$db->sessionResume("80fa94c1a11f11e78667001e673b2560"));
}
public function testResumeAMissingSession(): void {
@ -99,12 +96,6 @@ trait SeriesSession {
$this->compareExpectations(static::$drv, $state);
}
public function testCreateASessionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->sessionCreate("jane.doe@example.com");
}
public function testDestroyASession(): void {
$user = "jane.doe@example.com";
$id = "80fa94c1a11f11e78667001e673b2560";
@ -131,10 +122,4 @@ trait SeriesSession {
$id = "80fa94c1a11f11e78667001e673b2560";
$this->assertFalse(Arsse::$db->sessionDestroy($user, $id));
}
public function testDestroyASessionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->sessionDestroy("jane.doe@example.com", "80fa94c1a11f11e78667001e673b2560");
}
}

81
tests/cases/Database/SeriesSubscription.php

@ -61,7 +61,11 @@ trait SeriesSubscription {
'next_fetch' => "datetime",
'icon' => "int",
],
'rows' => [], // filled in the series setup
'rows' => [
[1,"http://example.com/feed1", "Ook", "", "",strtotime("now"),strtotime("now"),null],
[2,"http://example.com/feed2", "eek", "", "",strtotime("now - 1 hour"),strtotime("now - 1 hour"),1],
[3,"http://example.com/feed3", "Ack", "", "",strtotime("now + 1 hour"),strtotime("now + 1 hour"),null],
],
],
'arsse_subscriptions' => [
'columns' => [
@ -144,11 +148,6 @@ trait SeriesSubscription {
],
],
];
$this->data['arsse_feeds']['rows'] = [
[1,"http://example.com/feed1", "Ook", "", "",strtotime("now"),strtotime("now"),null],
[2,"http://example.com/feed2", "eek", "", "",strtotime("now - 1 hour"),strtotime("now - 1 hour"),1],
[3,"http://example.com/feed3", "Ack", "", "",strtotime("now + 1 hour"),strtotime("now + 1 hour"),null],
];
// initialize a partial mock of the Database object to later manipulate the feedUpdate method
Arsse::$db = \Phake::partialMock(Database::class, static::$drv);
$this->user = "john.doe@example.com";
@ -163,7 +162,6 @@ trait SeriesSubscription {
$subID = $this->nextID("arsse_subscriptions");
\Phake::when(Arsse::$db)->feedUpdate->thenReturn(true);
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url));
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionAdd");
\Phake::verify(Arsse::$db, \Phake::times(0))->feedUpdate(1, true);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
@ -179,7 +177,6 @@ trait SeriesSubscription {
$subID = $this->nextID("arsse_subscriptions");
\Phake::when(Arsse::$db)->feedUpdate->thenReturn(true);
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url, "", "", false));
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionAdd");
\Phake::verify(Arsse::$db)->feedUpdate($feedID, true);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
@ -197,7 +194,6 @@ trait SeriesSubscription {
$subID = $this->nextID("arsse_subscriptions");
\Phake::when(Arsse::$db)->feedUpdate->thenReturn(true);
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url, "", "", true));
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionAdd");
\Phake::verify(Arsse::$db)->feedUpdate($feedID, true);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
@ -216,7 +212,6 @@ trait SeriesSubscription {
try {
Arsse::$db->subscriptionAdd($this->user, $url, "", "", false);
} finally {
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionAdd");
\Phake::verify(Arsse::$db)->feedUpdate($feedID, true);
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
@ -246,16 +241,8 @@ trait SeriesSubscription {
$this->assertSame($subID, Arsse::$db->subscriptionAdd($this->user, $url));
}
public function testAddASubscriptionWithoutAuthority(): void {
$url = "http://example.com/feed1";
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionAdd($this->user, $url);
}
public function testRemoveASubscription(): void {
$this->assertTrue(Arsse::$db->subscriptionRemove($this->user, 1));
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionRemove");
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password'],
'arsse_subscriptions' => ['id','owner','feed'],
@ -280,12 +267,6 @@ trait SeriesSubscription {
Arsse::$db->subscriptionRemove($this->user, 1);
}
public function testRemoveASubscriptionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionRemove($this->user, 1);
}
public function testListSubscriptions(): void {
$exp = [
[
@ -308,9 +289,7 @@ trait SeriesSubscription {
],
];
$this->assertResult($exp, Arsse::$db->subscriptionList($this->user));
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionList");
$this->assertArraySubset($exp[0], Arsse::$db->subscriptionPropertiesGet($this->user, 1));
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionPropertiesGet");
$this->assertArraySubset($exp[1], Arsse::$db->subscriptionPropertiesGet($this->user, 3));
}
@ -349,12 +328,6 @@ trait SeriesSubscription {
Arsse::$db->subscriptionList($this->user, 4);
}
public function testListSubscriptionsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionList($this->user);
}
public function testCountSubscriptions(): void {
$this->assertSame(2, Arsse::$db->subscriptionCount($this->user));
$this->assertSame(1, Arsse::$db->subscriptionCount($this->user, 2));
@ -365,12 +338,6 @@ trait SeriesSubscription {
Arsse::$db->subscriptionCount($this->user, 4);
}
public function testCountSubscriptionsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionCount($this->user);
}
public function testGetThePropertiesOfAMissingSubscription(): void {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
Arsse::$db->subscriptionPropertiesGet($this->user, 2112);
@ -381,12 +348,6 @@ trait SeriesSubscription {
Arsse::$db->subscriptionPropertiesGet($this->user, -1);
}
public function testGetThePropertiesOfASubscriptionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionPropertiesGet($this->user, 1);
}
public function testSetThePropertiesOfASubscription(): void {
Arsse::$db->subscriptionPropertiesSet($this->user, 1, [
'title' => "Ook Ook",
@ -394,7 +355,6 @@ trait SeriesSubscription {
'pinned' => false,
'order_type' => 0,
]);
\Phake::verify(Arsse::$user)->authorize($this->user, "subscriptionPropertiesSet");
$state = $this->primeExpectations($this->data, [
'arsse_feeds' => ['id','url','username','password','title'],
'arsse_subscriptions' => ['id','owner','feed','title','folder','pinned','order_type'],
@ -454,22 +414,11 @@ trait SeriesSubscription {
Arsse::$db->subscriptionPropertiesSet($this->user, -1, ['folder' => null]);
}
public function testSetThePropertiesOfASubscriptionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionPropertiesSet($this->user, 1, ['folder' => null]);
}
public function testRetrieveTheFaviconOfASubscription(): void {
$exp = "http://example.com/favicon.ico";
$this->assertSame($exp, Arsse::$db->subscriptionIcon(null, 1)['url']);
$this->assertSame($exp, Arsse::$db->subscriptionIcon(null, 2)['url']);
$this->assertSame(null, Arsse::$db->subscriptionIcon(null, 3)['url']);
// authorization shouldn't have any bearing on this function
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertSame($exp, Arsse::$db->subscriptionIcon(null, 1)['url']);
$this->assertSame($exp, Arsse::$db->subscriptionIcon(null, 2)['url']);
$this->assertSame(null, Arsse::$db->subscriptionIcon(null, 3)['url']);
}
public function testRetrieveTheFaviconOfAMissingSubscription(): void {
@ -493,14 +442,6 @@ trait SeriesSubscription {
$this->assertSame(null, Arsse::$db->subscriptionIcon($user, 2)['url']);
}
public function testRetrieveTheFaviconOfASubscriptionWithUserWithoutAuthority(): void {
$exp = "http://example.com/favicon.ico";
$user = "john.doe@example.com";
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionIcon($user, -2112);
}
public function testListTheTagsOfASubscription(): void {
$this->assertEquals([1,2], Arsse::$db->subscriptionTagsGet("john.doe@example.com", 1));
$this->assertEquals([2], Arsse::$db->subscriptionTagsGet("john.doe@example.com", 3));
@ -513,12 +454,6 @@ trait SeriesSubscription {
Arsse::$db->subscriptionTagsGet($this->user, 101);
}
public function testListTheTagsOfASubscriptionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->subscriptionTagsGet("john.doe@example.com", 1);
}
public function testGetRefreshTimeOfASubscription(): void {
$user = "john.doe@example.com";
$this->assertTime(strtotime("now + 1 hour"), Arsse::$db->subscriptionRefreshed($user));
@ -529,10 +464,4 @@ trait SeriesSubscription {
$this->assertException("subjectMissing", "Db", "ExceptionInput");
$this->assertTime(strtotime("now - 1 hour"), Arsse::$db->subscriptionRefreshed("john.doe@example.com", 2));
}
public function testGetRefreshTimeOfASubscriptionWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
$this->assertTime(strtotime("now + 1 hour"), Arsse::$db->subscriptionRefreshed("john.doe@example.com"));
}
}

55
tests/cases/Database/SeriesTag.php

@ -113,7 +113,6 @@ trait SeriesTag {
$user = "john.doe@example.com";
$tagID = $this->nextID("arsse_tags");
$this->assertSame($tagID, Arsse::$db->tagAdd($user, ['name' => "Entertaining"]));
\Phake::verify(Arsse::$user)->authorize($user, "tagAdd");
$state = $this->primeExpectations($this->data, $this->checkTags);
$state['arsse_tags']['rows'][] = [$tagID, $user, "Entertaining"];
$this->compareExpectations(static::$drv, $state);
@ -139,12 +138,6 @@ trait SeriesTag {
Arsse::$db->tagAdd("john.doe@example.com", ['name' => " "]);
}
public function testAddATagWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagAdd("john.doe@example.com", ['name' => "Boring"]);
}
public function testListTags(): void {
$exp = [
['id' => 2, 'name' => "Fascinating"],
@ -158,18 +151,10 @@ trait SeriesTag {
$this->assertResult($exp, Arsse::$db->tagList("jane.doe@example.com"));
$exp = [];
$this->assertResult($exp, Arsse::$db->tagList("jane.doe@example.com", false));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "tagList");
}
public function testListTagsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagList("john.doe@example.com");
}
public function testRemoveATag(): void {
$this->assertTrue(Arsse::$db->tagRemove("john.doe@example.com", 1));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "tagRemove");
$state = $this->primeExpectations($this->data, $this->checkTags);
array_shift($state['arsse_tags']['rows']);
$this->compareExpectations(static::$drv, $state);
@ -177,7 +162,6 @@ trait SeriesTag {
public function testRemoveATagByName(): void {
$this->assertTrue(Arsse::$db->tagRemove("john.doe@example.com", "Interesting", true));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "tagRemove");
$state = $this->primeExpectations($this->data, $this->checkTags);
array_shift($state['arsse_tags']['rows']);
$this->compareExpectations(static::$drv, $state);
@ -203,12 +187,6 @@ trait SeriesTag {
Arsse::$db->tagRemove("john.doe@example.com", 3); // tag ID 3 belongs to Jane
}
public function testRemoveATagWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagRemove("john.doe@example.com", 1);
}
public function testGetThePropertiesOfATag(): void {
$exp = [
'id' => 2,
@ -216,7 +194,6 @@ trait SeriesTag {
];
$this->assertArraySubset($exp, Arsse::$db->tagPropertiesGet("john.doe@example.com", 2));
$this->assertArraySubset($exp, Arsse::$db->tagPropertiesGet("john.doe@example.com", "Fascinating", true));
\Phake::verify(Arsse::$user, \Phake::times(2))->authorize("john.doe@example.com", "tagPropertiesGet");
}
public function testGetThePropertiesOfAMissingTag(): void {
@ -239,19 +216,12 @@ trait SeriesTag {
Arsse::$db->tagPropertiesGet("john.doe@example.com", 3); // tag ID 3 belongs to Jane
}
public function testGetThePropertiesOfATagWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagPropertiesGet("john.doe@example.com", 1);
}
public function testMakeNoChangesToATag(): void {
$this->assertFalse(Arsse::$db->tagPropertiesSet("john.doe@example.com", 1, []));
}
public function testRenameATag(): void {
$this->assertTrue(Arsse::$db->tagPropertiesSet("john.doe@example.com", 1, ['name' => "Curious"]));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "tagPropertiesSet");
$state = $this->primeExpectations($this->data, $this->checkTags);
$state['arsse_tags']['rows'][0][2] = "Curious";
$this->compareExpectations(static::$drv, $state);
@ -259,7 +229,6 @@ trait SeriesTag {
public function testRenameATagByName(): void {
$this->assertTrue(Arsse::$db->tagPropertiesSet("john.doe@example.com", "Interesting", ['name' => "Curious"], true));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.com", "tagPropertiesSet");
$state = $this->primeExpectations($this->data, $this->checkTags);
$state['arsse_tags']['rows'][0][2] = "Curious";
$this->compareExpectations(static::$drv, $state);
@ -305,12 +274,6 @@ trait SeriesTag {
Arsse::$db->tagPropertiesSet("john.doe@example.com", 3, ['name' => "Exciting"]); // tag ID 3 belongs to Jane
}
public function testSetThePropertiesOfATagWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagPropertiesSet("john.doe@example.com", 1, ['name' => "Exciting"]);
}
public function testListTaggedSubscriptions(): void {
$exp = [1,5];
$this->assertEquals($exp, Arsse::$db->tagSubscriptionsGet("john.doe@example.com", 1));
@ -333,12 +296,6 @@ trait SeriesTag {
Arsse::$db->tagSubscriptionsGet("john.doe@example.com", -1);
}
public function testListTaggedSubscriptionsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagSubscriptionsGet("john.doe@example.com", 1);
}
public function testApplyATagToSubscriptions(): void {
Arsse::$db->tagSubscriptionsSet("john.doe@example.com", 1, [3,4]);
$state = $this->primeExpectations($this->data, $this->checkMembers);
@ -399,12 +356,6 @@ trait SeriesTag {
$this->compareExpectations(static::$drv, $state);
}
public function testApplyATagToSubscriptionsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagSubscriptionsSet("john.doe@example.com", 1, [3,4]);
}
public function testSummarizeTags(): void {
$exp = [
['id' => 1, 'name' => "Interesting", 'subscription' => 1],
@ -415,10 +366,4 @@ trait SeriesTag {
];
$this->assertResult($exp, Arsse::$db->tagSummarize("john.doe@example.com"));
}
public function testSummarizeTagsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tagSummarize("john.doe@example.com");
}
}

15
tests/cases/Database/SeriesToken.php

@ -67,9 +67,6 @@ trait SeriesToken {
$this->assertArraySubset($exp1, Arsse::$db->tokenLookup("fever.login", "80fa94c1a11f11e78667001e673b2560"));
$this->assertArraySubset($exp2, Arsse::$db->tokenLookup("class.class", "da772f8fa13c11e78667001e673b2560"));
$this->assertArraySubset($exp3, Arsse::$db->tokenLookup("class.class", "ab3b3eb8a13311e78667001e673b2560"));
// token lookup should not check authorization
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertArraySubset($exp1, Arsse::$db->tokenLookup("fever.login", "80fa94c1a11f11e78667001e673b2560"));
}
public function testLookUpAMissingToken(): void {
@ -106,12 +103,6 @@ trait SeriesToken {
Arsse::$db->tokenCreate("fever.login", "jane.doe@example.biz");
}
public function testCreateATokenWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tokenCreate("fever.login", "jane.doe@example.com");
}
public function testRevokeAToken(): void {
$user = "jane.doe@example.com";
$id = "80fa94c1a11f11e78667001e673b2560";
@ -136,10 +127,4 @@ trait SeriesToken {
// revoking tokens which do not exist is not an error
$this->assertFalse(Arsse::$db->tokenRevoke($user, "unknown.class"));
}
public function testRevokeATokenWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->tokenRevoke("jane.doe@example.com", "fever.login");
}
}

43
tests/cases/Database/SeriesUser.php

@ -33,21 +33,12 @@ trait SeriesUser {
public function testCheckThatAUserExists(): void {
$this->assertTrue(Arsse::$db->userExists("jane.doe@example.com"));
$this->assertFalse(Arsse::$db->userExists("jane.doe@example.org"));
\Phake::verify(Arsse::$user)->authorize("jane.doe@example.com", "userExists");
\Phake::verify(Arsse::$user)->authorize("jane.doe@example.org", "userExists");
$this->compareExpectations(static::$drv, $this->data);
}
public function testCheckThatAUserExistsWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->userExists("jane.doe@example.com");
}
public function testGetAPassword(): void {
$hash = Arsse::$db->userPasswordGet("admin@example.net");
$this->assertSame('$2y$10$PbcG2ZR3Z8TuPzM7aHTF8.v61dtCjzjK78gdZJcp4UePE8T9jEgBW', $hash);
\Phake::verify(Arsse::$user)->authorize("admin@example.net", "userPasswordGet");
$this->assertTrue(password_verify("secret", $hash));
}
@ -56,15 +47,8 @@ trait SeriesUser {
Arsse::$db->userPasswordGet("john.doe@example.org");
}
public function testGetAPasswordWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->userPasswordGet("admin@example.net");
}
public function testAddANewUser(): void {
$this->assertTrue(Arsse::$db->userAdd("john.doe@example.org", ""));
\Phake::verify(Arsse::$user)->authorize("john.doe@example.org", "userAdd");
$state = $this->primeExpectations($this->data, ['arsse_users' => ['id']]);
$state['arsse_users']['rows'][] = ["john.doe@example.org"];
$this->compareExpectations(static::$drv, $state);
@ -75,15 +59,8 @@ trait SeriesUser {
Arsse::$db->userAdd("john.doe@example.com", "");
}
public function testAddANewUserWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->userAdd("john.doe@example.org", "");
}
public function testRemoveAUser(): void {
$this->assertTrue(Arsse::$db->userRemove("admin@example.net"));
\Phake::verify(Arsse::$user)->authorize("admin@example.net", "userRemove");
$state = $this->primeExpectations($this->data, ['arsse_users' => ['id']]);
array_shift($state['arsse_users']['rows']);
$this->compareExpectations(static::$drv, $state);
@ -94,22 +71,9 @@ trait SeriesUser {
Arsse::$db->userRemove("john.doe@example.org");
}
public function testRemoveAUserWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->userRemove("admin@example.net");
}
public function testListAllUsers(): void {
$users = ["admin@example.net", "jane.doe@example.com", "john.doe@example.com"];
$this->assertSame($users, Arsse::$db->userList());
\Phake::verify(Arsse::$user)->authorize("", "userList");
}
public function testListAllUsersWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->userList();
}
/**
@ -122,7 +86,6 @@ trait SeriesUser {
$this->assertTrue(Arsse::$db->userPasswordSet($user, $pass));
$hash = Arsse::$db->userPasswordGet($user);
$this->assertNotEquals("", $hash);
\Phake::verify(Arsse::$user)->authorize($user, "userPasswordSet");
$this->assertTrue(password_verify($pass, $hash), "Failed verifying password of $user '$pass' against hash '$hash'.");
}
@ -137,10 +100,4 @@ trait SeriesUser {
$this->assertException("doesNotExist", "User");
Arsse::$db->userPasswordSet("john.doe@example.org", "secret");
}
public function testSetAPasswordWithoutAuthority(): void {
\Phake::when(Arsse::$user)->authorize->thenReturn(false);
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
Arsse::$db->userPasswordSet("john.doe@example.com", "secret");
}
}

1
tests/cases/ImportExport/TestImportExport.php

@ -28,7 +28,6 @@ class TestImportExport extends \JKingWeb\Arsse\Test\AbstractTest {
// create a mock user manager
Arsse::$user = \Phake::mock(\JKingWeb\Arsse\User::class);
\Phake::when(Arsse::$user)->exists->thenReturn(true);
\Phake::when(Arsse::$user)->authorize->thenReturn(true);
// create a mock Import/Export processor
$this->proc = \Phake::partialMock(AbstractImportExport::class);
// initialize an SQLite memeory database

52
tests/cases/User/TestInternal.php

@ -33,16 +33,12 @@ class TestInternal extends \JKingWeb\Arsse\Test\AbstractTest {
* @dataProvider provideAuthentication
* @group slow
*/
public function testAuthenticateAUser(bool $authorized, string $user, $password, bool $exp): void {
if ($authorized) {
\Phake::when(Arsse::$db)->userPasswordGet("john.doe@example.com")->thenReturn('$2y$10$1zbqRJhxM8uUjeSBPp4IhO90xrqK0XjEh9Z16iIYEFRV4U.zeAFom'); // hash of "secret"
\Phake::when(Arsse::$db)->userPasswordGet("jane.doe@example.com")->thenReturn('$2y$10$bK1ljXfTSyc2D.NYvT.Eq..OpehLRXVbglW.23ihVuyhgwJCd.7Im'); // hash of "superman"
\Phake::when(Arsse::$db)->userPasswordGet("owen.hardy@example.com")->thenReturn("");
\Phake::when(Arsse::$db)->userPasswordGet("kira.nerys@example.com")->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist"));
\Phake::when(Arsse::$db)->userPasswordGet("007@example.com")->thenReturn(null);
} else {
\Phake::when(Arsse::$db)->userPasswordGet->thenThrow(new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized"));
}
public function testAuthenticateAUser(string $user, $password, bool $exp): void {
\Phake::when(Arsse::$db)->userPasswordGet("john.doe@example.com")->thenReturn('$2y$10$1zbqRJhxM8uUjeSBPp4IhO90xrqK0XjEh9Z16iIYEFRV4U.zeAFom'); // hash of "secret"
\Phake::when(Arsse::$db)->userPasswordGet("jane.doe@example.com")->thenReturn('$2y$10$bK1ljXfTSyc2D.NYvT.Eq..OpehLRXVbglW.23ihVuyhgwJCd.7Im'); // hash of "superman"
\Phake::when(Arsse::$db)->userPasswordGet("owen.hardy@example.com")->thenReturn("");
\Phake::when(Arsse::$db)->userPasswordGet("kira.nerys@example.com")->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist"));
\Phake::when(Arsse::$db)->userPasswordGet("007@example.com")->thenReturn(null);
$this->assertSame($exp, (new Driver)->auth($user, $password));
}
@ -53,32 +49,22 @@ class TestInternal extends \JKingWeb\Arsse\Test\AbstractTest {
$kira = "kira.nerys@example.com";
$bond = "007@example.com";
return [
[false, $john, "secret", false],
[false, $jane, "superman", false],
[false, $owen, "", false],
[false, $kira, "ashalla", false],
[false, $bond, "", false],
[true, $john, "secret", true],
[true, $jane, "superman", true],
[true, $owen, "", true],
[true, $kira, "ashalla", false],
[true, $john, "top secret", false],
[true, $jane, "clark kent", false],
[true, $owen, "watchmaker", false],
[true, $kira, "singha", false],
[true, $john, "", false],
[true, $jane, "", false],
[true, $kira, "", false],
[true, $bond, "for England", false],
[true, $bond, "", false],
[$john, "secret", true],
[$jane, "superman", true],
[$owen, "", true],
[$kira, "ashalla", false],
[$john, "top secret", false],
[$jane, "clark kent", false],
[$owen, "watchmaker", false],
[$kira, "singha", false],
[$john, "", false],
[$jane, "", false],
[$kira, "", false],
[$bond, "for England", false],
[$bond, "", false],
];
}
public function testAuthorizeAnAction(): void {
\Phake::verifyNoFurtherInteraction(Arsse::$db);
$this->assertTrue((new Driver)->authorize("someone", "something"));
}
public function testListUsers(): void {
$john = "john.doe@example.com";
$jane = "jane.doe@example.com";

132
tests/cases/User/TestUser.php

@ -69,13 +69,9 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
}
/** @dataProvider provideUserList */
public function testListUsers(bool $authorized, $exp): void {
public function testListUsers($exp): void {
$u = new User($this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userList->thenReturn(["john.doe@example.com", "jane.doe@example.com"]);
if ($exp instanceof Exception) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
}
$this->assertSame($exp, $u->list());
}
@ -83,20 +79,15 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$john = "john.doe@example.com";
$jane = "jane.doe@example.com";
return [
[false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[true, [$john, $jane]],
[[$john, $jane]],
];
}
/** @dataProvider provideExistence */
public function testCheckThatAUserExists(bool $authorized, string $user, $exp): void {
public function testCheckThatAUserExists(string $user, $exp): void {
$u = new User($this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userExists("john.doe@example.com")->thenReturn(true);
\Phake::when($this->drv)->userExists("jane.doe@example.com")->thenReturn(false);
if ($exp instanceof Exception) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
}
$this->assertSame($exp, $u->exists($user));
}
@ -104,48 +95,35 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$john = "john.doe@example.com";
$jane = "jane.doe@example.com";
return [
[false, $john, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[false, $jane, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[true, $john, true],
[true, $jane, false],
[$john, true],
[$jane, false],
];
}
/** @dataProvider provideAdditions */
public function testAddAUser(bool $authorized, string $user, $password, $exp): void {
public function testAddAUser(string $user, $password, $exp): void {
$u = new User($this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userAdd("john.doe@example.com", $this->anything())->thenThrow(new \JKingWeb\Arsse\User\Exception("alreadyExists"));
\Phake::when($this->drv)->userAdd("jane.doe@example.com", $this->anything())->thenReturnCallback(function($user, $pass) {
return $pass ?? "random password";
});
if ($exp instanceof Exception) {
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
} else {
$this->assertException("alreadyExists", "User");
}
$this->assertException("alreadyExists", "User");
}
$this->assertSame($exp, $u->add($user, $password));
}
/** @dataProvider provideAdditions */
public function testAddAUserWithARandomPassword(bool $authorized, string $user, $password, $exp): void {
public function testAddAUserWithARandomPassword(string $user, $password, $exp): void {
$u = \Phake::partialMock(User::class, $this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userAdd($this->anything(), $this->isNull())->thenReturn(null);
\Phake::when($this->drv)->userAdd("john.doe@example.com", $this->logicalNot($this->isNull()))->thenThrow(new \JKingWeb\Arsse\User\Exception("alreadyExists"));
\Phake::when($this->drv)->userAdd("jane.doe@example.com", $this->logicalNot($this->isNull()))->thenReturnCallback(function($user, $pass) {
return $pass;
});
if ($exp instanceof Exception) {
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
$calls = 0;
} else {
$this->assertException("alreadyExists", "User");
$calls = 2;
}
$this->assertException("alreadyExists", "User");
$calls = 2;
} else {
$calls = 4;
}
@ -163,34 +141,27 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$john = "john.doe@example.com";
$jane = "jane.doe@example.com";
return [
[false, $john, "secret", new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[false, $jane, "superman", new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[true, $john, "secret", new \JKingWeb\Arsse\User\Exception("alreadyExists")],
[true, $jane, "superman", "superman"],
[true, $jane, null, "random password"],
[$john, "secret", new \JKingWeb\Arsse\User\Exception("alreadyExists")],
[$jane, "superman", "superman"],
[$jane, null, "random password"],
];
}
/** @dataProvider provideRemovals */
public function testRemoveAUser(bool $authorized, string $user, bool $exists, $exp): void {
public function testRemoveAUser(string $user, bool $exists, $exp): void {
$u = new User($this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userRemove("john.doe@example.com")->thenReturn(true);
\Phake::when($this->drv)->userRemove("jane.doe@example.com")->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist"));
\Phake::when(Arsse::$db)->userExists->thenReturn($exists);
\Phake::when(Arsse::$db)->userRemove->thenReturn(true);
if ($exp instanceof Exception) {
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
} else {
$this->assertException("doesNotExist", "User");
}
$this->assertException("doesNotExist", "User");
}
try {
$this->assertSame($exp, $u->remove($user));
} finally {
\Phake::verify(Arsse::$db, \Phake::times((int) $authorized))->userExists($user);
\Phake::verify(Arsse::$db, \Phake::times((int) ($authorized && $exists)))->userRemove($user);
\Phake::verify(Arsse::$db, \Phake::times(1))->userExists($user);
\Phake::verify(Arsse::$db, \Phake::times((int) $exists))->userRemove($user);
}
}
@ -198,32 +169,23 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$john = "john.doe@example.com";
$jane = "jane.doe@example.com";
return [
[false, $john, true, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[false, $john, false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[false, $jane, true, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[false, $jane, false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[true, $john, true, true],
[true, $john, false, true],
[true, $jane, true, new \JKingWeb\Arsse\User\Exception("doesNotExist")],
[true, $jane, false, new \JKingWeb\Arsse\User\Exception("doesNotExist")],
[$john, true, true],
[$john, false, true],
[$jane, true, new \JKingWeb\Arsse\User\Exception("doesNotExist")],
[$jane, false, new \JKingWeb\Arsse\User\Exception("doesNotExist")],
];
}
/** @dataProvider providePasswordChanges */
public function testChangeAPassword(bool $authorized, string $user, $password, bool $exists, $exp): void {
public function testChangeAPassword(string $user, $password, bool $exists, $exp): void {
$u = new User($this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userPasswordSet("john.doe@example.com", $this->anything(), $this->anything())->thenReturnCallback(function($user, $pass, $old) {
return $pass ?? "random password";
});
\Phake::when($this->drv)->userPasswordSet("jane.doe@example.com", $this->anything(), $this->anything())->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist"));
\Phake::when(Arsse::$db)->userExists->thenReturn($exists);
if ($exp instanceof Exception) {
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
} else {
$this->assertException("doesNotExist", "User");
}
$this->assertException("doesNotExist", "User");
$calls = 0;
} else {
$calls = 1;
@ -237,9 +199,8 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
}
/** @dataProvider providePasswordChanges */
public function testChangeAPasswordToARandomPassword(bool $authorized, string $user, $password, bool $exists, $exp): void {
public function testChangeAPasswordToARandomPassword(string $user, $password, bool $exists, $exp): void {
$u = \Phake::partialMock(User::class, $this->drv);
\Phake::when($this->drv)->authorize->thenReturn($authorized);
\Phake::when($this->drv)->userPasswordSet($this->anything(), $this->isNull(), $this->anything())->thenReturn(null);
\Phake::when($this->drv)->userPasswordSet("john.doe@example.com", $this->logicalNot($this->isNull()), $this->anything())->thenReturnCallback(function($user, $pass, $old) {
return $pass ?? "random password";
@ -247,13 +208,8 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
\Phake::when($this->drv)->userPasswordSet("jane.doe@example.com", $this->logicalNot($this->isNull()), $this->anything())->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist"));
\Phake::when(Arsse::$db)->userExists->thenReturn($exists);
if ($exp instanceof Exception) {
if ($exp instanceof \JKingWeb\Arsse\User\ExceptionAuthz) {
$this->assertException("notAuthorized", "User", "ExceptionAuthz");
$calls = 0;
} else {
$this->assertException("doesNotExist", "User");
$calls = 2;
}
$this->assertException("doesNotExist", "User");
$calls = 2;
} else {
$calls = 4;
}
@ -278,19 +234,16 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$john = "john.doe@example.com";
$jane = "jane.doe@example.com";
return [
[false, $john, "secret", true, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[false, $jane, "superman", false, new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized")],
[true, $john, "superman", true, "superman"],
[true, $john, null, true, "random password"],
[true, $john, "superman", false, "superman"],
[true, $john, null, false, "random password"],
[true, $jane, "secret", true, new \JKingWeb\Arsse\User\Exception("doesNotExist")],
[$john, "superman", true, "superman"],
[$john, null, true, "random password"],
[$john, "superman", false, "superman"],
[$john, null, false, "random password"],
[$jane, "secret", true, new \JKingWeb\Arsse\User\Exception("doesNotExist")],
];
}
/** @dataProvider providePasswordClearings */
public function testClearAPassword(bool $authorized, bool $exists, string $user, $exp): void {
\Phake::when($this->drv)->authorize->thenReturn($authorized);
public function testClearAPassword(bool $exists, string $user, $exp): void {
\Phake::when($this->drv)->userPasswordUnset->thenReturn(true);
\Phake::when($this->drv)->userPasswordUnset("jane.doe@example.net", null)->thenThrow(new \JKingWeb\Arsse\User\Exception("doesNotExist"));
\Phake::when(Arsse::$db)->userExists->thenReturn($exists);
@ -303,26 +256,19 @@ class TestUser extends \JKingWeb\Arsse\Test\AbstractTest {
$this->assertSame($exp, $u->passwordUnset($user));
}
} finally {
\Phake::verify(Arsse::$db, \Phake::times((int) ($authorized && $exists && is_bool($exp))))->userPasswordSet($user, null);
\Phake::verify(Arsse::$db, \Phake::times((int) ($exists && is_bool($exp))))->userPasswordSet($user, null);
}
}
public function providePasswordClearings(): iterable {
$forbidden = new \JKingWeb\Arsse\User\ExceptionAuthz("notAuthorized");
$missing = new \JKingWeb\Arsse\User\Exception("doesNotExist");
return [
[false, true, "jane.doe@example.com", $forbidden],
[false, true, "john.doe@example.com", $forbidden],
[false, true, "jane.doe@example.net", $forbidden],
[false, false, "jane.doe@example.com", $forbidden],
[false, false, "john.doe@example.com", $forbidden],
[false, false, "jane.doe@example.net", $forbidden],
[true, true, "jane.doe@example.com", true],
[true, true, "john.doe@example.com", true],
[true, true, "jane.doe@example.net", $missing],
[true, false, "jane.doe@example.com", true],
[true, false, "john.doe@example.com", true],
[true, false, "jane.doe@example.net", $missing],
[true, "jane.doe@example.com", true],
[true, "john.doe@example.com", true],
[true, "jane.doe@example.net", $missing],
[false, "jane.doe@example.com", true],
[false, "john.doe@example.com", true],
[false, "jane.doe@example.net", $missing],
];
}
}

Loading…
Cancel
Save