diff --git a/src/Database/PostUpdate.php b/src/Database/PostUpdate.php index 64737f604f..94d00853ab 100644 --- a/src/Database/PostUpdate.php +++ b/src/Database/PostUpdate.php @@ -204,14 +204,20 @@ class PostUpdate } if (empty($item['psid'])) { - $item['psid'] = PermissionSet::fetchIDForPost($item); - } else { - $item['allow_cid'] = null; - $item['allow_gid'] = null; - $item['deny_cid'] = null; - $item['deny_gid'] = null; + $item['psid'] = PermissionSet::getIdFromACL( + $item['uid'], + $item['allow_cid'], + $item['allow_gid'], + $item['deny_cid'], + $item['deny_gid'] + ); } + $item['allow_cid'] = null; + $item['allow_gid'] = null; + $item['deny_cid'] = null; + $item['deny_gid'] = null; + if ($item['post-type'] == 0) { if (!empty($item['type']) && ($item['type'] == 'note')) { $item['post-type'] = Item::PT_PERSONAL_NOTE; diff --git a/src/Model/Item.php b/src/Model/Item.php index 9f2f45e74a..38557c52ce 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -1856,7 +1856,18 @@ class Item } // Creates or assigns the permission set - $item['psid'] = PermissionSet::fetchIDForPost($item); + $item['psid'] = PermissionSet::getIdFromACL( + $item['uid'], + $item['allow_cid'], + $item['allow_gid'], + $item['deny_cid'], + $item['deny_gid'] + ); + + $item['allow_cid'] = null; + $item['allow_gid'] = null; + $item['deny_cid'] = null; + $item['deny_gid'] = null; // We are doing this outside of the transaction to avoid timing problems if (!self::insertActivity($item)) { @@ -2729,7 +2740,13 @@ class Item $private = ($user['allow_cid'] || $user['allow_gid'] || $user['deny_cid'] || $user['deny_gid']) ? 1 : 0; - $psid = PermissionSet::fetchIDForPost($user); + $psid = PermissionSet::getIdFromACL( + $user['uid'], + $user['allow_cid'], + $user['allow_gid'], + $user['deny_cid'], + $user['deny_gid'] + ); $forum_mode = ($prvgroup ? 2 : 1); diff --git a/src/Model/PermissionSet.php b/src/Model/PermissionSet.php index d0e256d153..de943c977c 100644 --- a/src/Model/PermissionSet.php +++ b/src/Model/PermissionSet.php @@ -2,9 +2,13 @@ /** * @file src/Model/PermissionSet.php */ + namespace Friendica\Model; +use Friendica\Core\L10n; use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Network\HTTPException; /** * functions for interacting with the permission set of an object (item, photo, event, ...) @@ -14,51 +18,53 @@ class PermissionSet /** * Fetch the id of a given permission set. Generate a new one when needed * - * @param array $postarray The array from an item, picture or event post + * @param int $uid + * @param string|null $allow_cid Allowed contact IDs - empty = everyone + * @param string|null $allow_gid Allowed group IDs - empty = everyone + * @param string|null $deny_cid Disallowed contact IDs - empty = no one + * @param string|null $deny_gid Disallowed group IDs - empty = no one * @return int id - * @throws \Exception + * @throws HTTPException\InternalServerErrorException */ - public static function fetchIDForPost(&$postarray) - { - $condition = ['uid' => $postarray['uid'], - 'allow_cid' => self::sortPermissions($postarray['allow_cid'] ?? ''), - 'allow_gid' => self::sortPermissions($postarray['allow_gid'] ?? ''), - 'deny_cid' => self::sortPermissions($postarray['deny_cid'] ?? ''), - 'deny_gid' => self::sortPermissions($postarray['deny_gid'] ?? '')]; + public static function getIdFromACL( + int $uid, + string $allow_cid = null, + string $allow_gid = null, + string $deny_cid = null, + string $deny_gid = null + ) { + $ACLFormatter = DI::aclFormatter(); - $set = DBA::selectFirst('permissionset', ['id'], $condition); + $allow_cid = $ACLFormatter->sanitize($allow_cid); + $allow_gid = $ACLFormatter->sanitize($allow_gid); + $deny_cid = $ACLFormatter->sanitize($deny_cid); + $deny_gid = $ACLFormatter->sanitize($deny_gid); - if (!DBA::isResult($set)) { - DBA::insert('permissionset', $condition, true); - - $set = DBA::selectFirst('permissionset', ['id'], $condition); + // Public permission + if (!$allow_cid && !$allow_gid && !$deny_cid && !$deny_gid) { + return 0; } - $postarray['allow_cid'] = null; - $postarray['allow_gid'] = null; - $postarray['deny_cid'] = null; - $postarray['deny_gid'] = null; + $condition = [ + 'uid' => $uid, + 'allow_cid' => $allow_cid, + 'allow_gid' => $allow_gid, + 'deny_cid' => $deny_cid, + 'deny_gid' => $deny_gid + ]; + $permissionset = DBA::selectFirst('permissionset', ['id'], $condition); - return $set['id']; - } - - private static function sortPermissions($permissionlist) - { - $cleaned_list = trim($permissionlist, '<>'); - - if (empty($cleaned_list)) { - return $permissionlist; + if (DBA::isResult($permissionset)) { + $psid = $permissionset['id']; + } else { + if (DBA::insert('permissionset', $condition, true)) { + $psid = DBA::lastInsertId(); + } else { + throw new HTTPException\InternalServerErrorException(L10n::t('Unable to create a new permission set.')); + } } - $elements = explode('><', $cleaned_list); - - if (count($elements) <= 1) { - return $permissionlist; - } - - asort($elements); - - return '<' . implode('><', $elements) . '>'; + return $psid; } /** diff --git a/src/Util/ACLFormatter.php b/src/Util/ACLFormatter.php index a7d851508d..d79a73298b 100644 --- a/src/Util/ACLFormatter.php +++ b/src/Util/ACLFormatter.php @@ -12,30 +12,57 @@ final class ACLFormatter /** * Turn user/group ACLs stored as angle bracketed text into arrays * - * @param string|null $ids A angle-bracketed list of IDs + * @param string|null $acl_string A angle-bracketed list of IDs * * @return array The array based on the IDs (empty in case there is no list) */ - public function expand(string $ids = null) + public function expand(string $acl_string = null) { // In case there is no ID list, return empty array (=> no ACL set) - if (!isset($ids)) { + if (!isset($acl_string)) { return []; } // turn string array of angle-bracketed elements into numeric array // e.g. "<1><2><3>" => array(1,2,3); - preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $ids, $matches, PREG_PATTERN_ORDER); + preg_match_all('/<(' . Group::FOLLOWERS . '|'. Group::MUTUALS . '|[0-9]+)>/', $acl_string, $matches, PREG_PATTERN_ORDER); return $matches[1]; } + /** + * Takes an arbitrary ACL string and sanitizes it for storage + * + * @param string|null $acl_string + * @return string + */ + public function sanitize(string $acl_string = null) + { + if (empty($acl_string)) { + return ''; + } + + $cleaned_list = trim($acl_string, '<>'); + + if (empty($cleaned_list)) { + return ''; + } + + $elements = explode('><', $cleaned_list); + + sort($elements); + + array_walk($elements, [$this, 'sanitizeItem']); + + return implode('', $elements); + } + /** * Wrap ACL elements in angle brackets for storage * * @param string $item The item to sanitise */ - private function sanitize(string &$item) { + private function sanitizeItem(string &$item) { // The item is an ACL int value if (intval($item)) { $item = '<' . intval(Strings::escapeTags(trim($item))) . '>'; @@ -70,7 +97,7 @@ final class ACLFormatter } if (is_array($item)) { - array_walk($item, [$this, 'sanitize']); + array_walk($item, [$this, 'sanitizeItem']); $return = implode('', $item); } return $return;