]> git.mxchange.org Git - friendica.git/commitdiff
Migrate PermissionSet to Depository paradigm
authorPhilipp <admin@philipp.info>
Tue, 5 Oct 2021 21:30:10 +0000 (23:30 +0200)
committerPhilipp <admin@philipp.info>
Thu, 7 Oct 2021 17:53:38 +0000 (19:53 +0200)
21 files changed:
src/BaseDepository.php
src/Capabilities/ICanCreateFromTableRow.php
src/Collection/PermissionSets.php [deleted file]
src/Core/UserImport.php
src/DI.php
src/Factory/Api/Mastodon/Account.php
src/Model/Item.php
src/Model/PermissionSet.php [deleted file]
src/Model/ProfileField.php
src/Module/ActivityPub/Objects.php
src/Module/Api/Friendica/Profile/Show.php
src/Module/PermissionTooltip.php
src/Module/Profile/Status.php
src/Module/Settings/Profile/Index.php
src/Protocol/ActivityPub/Transmitter.php
src/Repository/PermissionSet.php [deleted file]
src/Repository/ProfileField.php
src/Security/PermissionSet/Collection/PermissionSets.php [new file with mode: 0644]
src/Security/PermissionSet/Depository/PermissionSet.php [new file with mode: 0644]
src/Security/PermissionSet/Entity/PermissionSet.php [new file with mode: 0644]
src/Security/PermissionSet/Factory/PermissionSet.php [new file with mode: 0644]

index 18cca9d30e2c4baa1d9f8048b9988dfa45b12603..69bd93eef584d709c9cd3386365b36bca999dfbb 100644 (file)
@@ -13,7 +13,7 @@ use Psr\Log\LoggerInterface;
  * Depositories are meant to store and retrieve Entities from the database.
  *
  * The reason why there are methods prefixed with an underscore is because PHP doesn't support generic polymorphism
- * which means we can't direcly overload base methods and make parameters more strict (from a parent class to a child
+ * which means we can't directly overload base methods and make parameters more strict (from a parent class to a child
  * class for example)
  *
  * Similarly, we can't make an overloaded method return type more strict until we only support PHP version 7.4 but this
index bdb6d662da57dd85d09e174ba5d89152ebefbfb8..ad6053beb8f66fd4f665e84b5963b95a943cb0bf 100644 (file)
@@ -7,7 +7,7 @@ use Friendica\BaseEntity;
 interface ICanCreateFromTableRow
 {
        /**
-        * Returns the correcponding Entity given a table row record
+        * Returns the corresponding Entity given a table row record
         *
         * @param array $row
         * @return BaseEntity
diff --git a/src/Collection/PermissionSets.php b/src/Collection/PermissionSets.php
deleted file mode 100644 (file)
index 66dad17..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2010-2021, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Collection;
-
-use Friendica\BaseCollection;
-
-class PermissionSets extends BaseCollection
-{
-
-}
index f0d8f762e214cbaac022b311bb4c752451d27d7e..c0725bee8a4fd085bd24e68efafcc6aad5f68f78 100644 (file)
 
 namespace Friendica\Core;
 
-use Friendica\App;
 use Friendica\Database\DBA;
 use Friendica\Database\DBStructure;
 use Friendica\DI;
-use Friendica\Model\Contact;
 use Friendica\Model\Photo;
 use Friendica\Object\Image;
-use Friendica\Repository\PermissionSet;
+use Friendica\Security\PermissionSet\Depository\PermissionSet;
 use Friendica\Util\Strings;
 use Friendica\Worker\Delivery;
 
@@ -283,16 +281,13 @@ class UserImport
                        DI::profileField()->migrateFromLegacyProfile($profile);
                }
 
-               ///@TODO Replace with permissionset import
-               $self_contact = Contact::selectFirst(['id'], ['uid' => $newuid, 'self' => true]);
-               $allow_cid = DI::aclFormatter()->toString($self_contact['id']);
-               $self_psid = DI::permissionSet()->getIdFromACL($newuid, $allow_cid);
+               $permissionSet = DI::permissionSet()->selectDefaultForUser($newuid);
 
                foreach ($account['profile_fields'] ?? [] as $profile_field) {
                        $profile_field['uid'] = $newuid;
 
                        ///@TODO Replace with permissionset import
-                       $profile_field['psid'] = $profile_field['psid'] ? $self_psid : PermissionSet::PUBLIC;
+                       $profile_field['psid'] = $profile_field['psid'] ? $permissionSet->uid : PermissionSet::PUBLIC;
 
                        if (self::dbImportAssoc('profile_field', $profile_field) === false) {
                                Logger::info("uimport:insert profile field " . $profile_field['id'] . " : ERROR : " . DBA::errorMessage());
index ea7310feedca3805eacd15e9dcb4577f2b3a8216..1ba46e23edf37ccfc8dd0f2b331a443f1702bb18 100644 (file)
@@ -22,6 +22,7 @@
 namespace Friendica;
 
 use Dice\Dice;
+use Friendica\Security\PermissionSet\Depository\PermissionSet;
 use Psr\Log\LoggerInterface;
 
 /**
@@ -443,11 +444,19 @@ abstract class DI
        }
 
        /**
-        * @return Repository\PermissionSet
+        * @return PermissionSet
         */
        public static function permissionSet()
        {
-               return self::$dice->create(Repository\PermissionSet::class);
+               return self::$dice->create(PermissionSet::class);
+       }
+
+       /**
+        * @return \Friendica\Security\PermissionSet\Factory\PermissionSet
+        */
+       public static function permissionSetFactory()
+       {
+               return self::$dice->create(\Friendica\Security\PermissionSet\Factory\PermissionSet::class);
        }
 
        /**
index 85b3d4316b0c0ae25c61f5c22348317c195df45b..1d067352a63e67709d688b78bb484321fd128acf 100644 (file)
@@ -27,8 +27,8 @@ use Friendica\Collection\Api\Mastodon\Fields;
 use Friendica\Model\APContact;
 use Friendica\Model\Contact;
 use Friendica\Network\HTTPException;
-use Friendica\Repository\PermissionSet;
 use Friendica\Repository\ProfileField;
+use Friendica\Security\PermissionSet\Depository\PermissionSet;
 use ImagickException;
 use Psr\Log\LoggerInterface;
 
index db1d088883b060fd32cff4ea6cb12fe5524e677d..285187e5fbe70c6db44de2c32b8394917f8b2209 100644 (file)
@@ -980,13 +980,14 @@ class Item
                }
 
                // Creates or assigns the permission set
-               $item['psid'] = PermissionSet::getIdFromACL(
-                       $item['uid'],
-                       $item['allow_cid'],
-                       $item['allow_gid'],
-                       $item['deny_cid'],
-                       $item['deny_gid']
-               );
+               $item['psid'] = DI::permissionSet()->selectOrCreate(
+                       DI::permissionSetFactory()->createFromString(
+                               $item['uid'],
+                               $item['allow_cid'],
+                               $item['allow_gid'],
+                               $item['deny_cid'],
+                               $item['deny_gid']
+               ))->id;
 
                if (!empty($item['extid'])) {
                        $item['external-id'] = ItemURI::getIdByURI($item['extid']);
@@ -1952,18 +1953,19 @@ class Item
                        $private = self::PUBLIC;
                }
 
-               $psid = PermissionSet::getIdFromACL(
-                       $user['uid'],
-                       $user['allow_cid'],
-                       $user['allow_gid'],
-                       $user['deny_cid'],
-                       $user['deny_gid']
-               );
+               $permissionSet = DI::permissionSet()->selectOrCreate(
+                       DI::permissionSetFactory()->createFromString(
+                               $user['uid'],
+                               $user['allow_cid'],
+                               $user['allow_gid'],
+                               $user['deny_cid'],
+                               $user['deny_gid']
+                       ));
 
                $forum_mode = ($prvgroup ? 2 : 1);
 
                $fields = ['wall' => true, 'origin' => true, 'forum_mode' => $forum_mode, 'contact-id' => $self['id'],
-                       'owner-id' => $owner_id, 'private' => $private, 'psid' => $psid];
+                       'owner-id' => $owner_id, 'private' => $private, 'psid' => $permissionSet->id];
                self::update($fields, ['id' => $item['id']]);
 
                Worker::add(['priority' => PRIORITY_HIGH, 'dont_fork' => true], 'Notifier', Delivery::POST, (int)$item['uri-id'], (int)$item['uid']);
@@ -2549,12 +2551,12 @@ class Item
                        $condition = [];
                } elseif ($remote_user) {
                         // Authenticated visitor - fetch the matching permissionsets
-                       $set = PermissionSet::get($owner_id, $remote_user);
+                       $permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id);
                        if (!empty($set)) {
                                $condition = ["(`private` != ? OR (`private` = ? AND `wall`
                                        AND `psid` IN (" . implode(', ', array_fill(0, count($set), '?')) . ")))",
                                        self::PRIVATE, self::PRIVATE];
-                               $condition = array_merge($condition, $set);
+                               $condition = array_merge($condition, $permissionSets->column('id'));
                        }
                }
 
@@ -2595,10 +2597,10 @@ class Item
                         * If pre-verified, the caller is expected to have already
                         * done this and passed the groups into this function.
                         */
-                       $set = PermissionSet::get($owner_id, $remote_user);
+                       $permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id);
 
                        if (!empty($set)) {
-                               $sql_set = sprintf(" OR (" . $table . "`private` = %d AND " . $table . "`wall` AND " . $table . "`psid` IN (", self::PRIVATE) . implode(',', $set) . "))";
+                               $sql_set = sprintf(" OR (" . $table . "`private` = %d AND " . $table . "`wall` AND " . $table . "`psid` IN (", self::PRIVATE) . implode(',', $permissionSets->column('id')) . "))";
                        } else {
                                $sql_set = '';
                        }
diff --git a/src/Model/PermissionSet.php b/src/Model/PermissionSet.php
deleted file mode 100644 (file)
index 9138d46..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2010-2021, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Model;
-
-use Friendica\BaseModel;
-use Friendica\DI;
-
-/**
- * functions for interacting with the permission set of an object (item, photo, event, ...)
- *
- * @property int uid
- * @property string allow_cid
- * @property string allow_gid
- * @property string deny_cid
- * @property string deny_gid
- */
-class PermissionSet extends BaseModel
-{
-       /**
-        * Fetch the id of a given permission set. Generate a new one when needed
-        *
-        * @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
-        * @deprecated since 2020.03, use Repository\PermissionSet instead
-        * @see \Friendica\Repository\PermissionSet->getIdFromACL
-        */
-       public static function getIdFromACL(
-               int $uid,
-               string $allow_cid = null,
-               string $allow_gid = null,
-               string $deny_cid = null,
-               string $deny_gid = null
-       ) {
-               return DI::permissionSet()->getIdFromACL($uid, $allow_cid, $allow_gid, $deny_cid, $deny_gid);
-       }
-
-       /**
-        * Returns a permission set for a given contact
-        *
-        * @param integer $uid        User id whom the items belong
-        * @param integer $contact_id Contact id of the visitor
-        *
-        * @return array of permission set ids.
-        * @throws \Exception
-        * @deprecated since 2020.03, use Repository\PermissionSet instead
-        * @see \Friendica\Repository\PermissionSet->selectByContactId
-        */
-       public static function get($uid, $contact_id)
-       {
-               $permissionSets = DI::permissionSet()->selectByContactId($contact_id, $uid);
-
-               return $permissionSets->column('id');
-       }
-}
index 3fc33b2c80144c59d5e055a0954745ffae56771d..c69f5e26290c79e642202ebcf5758fe3842dad19 100644 (file)
@@ -23,7 +23,7 @@ namespace Friendica\Model;
 
 use Friendica\BaseModel;
 use Friendica\Database\Database;
-use Friendica\Network\HTTPException;
+use Friendica\Security\PermissionSet\Entity\PermissionSet;
 use Psr\Log\LoggerInterface;
 
 /**
@@ -46,14 +46,14 @@ class ProfileField extends BaseModel
        /** @var PermissionSet */
        private $permissionset;
 
-       /** @var \Friendica\Repository\PermissionSet */
-       private $permissionSetRepository;
+       /** @var \Friendica\Security\PermissionSet\Depository\PermissionSet */
+       private $permissionSetDepository;
 
-       public function __construct(Database $dba, LoggerInterface $logger, \Friendica\Repository\PermissionSet $permissionSetRepository, array $data = [])
+       public function __construct(Database $dba, LoggerInterface $logger,\Friendica\Security\PermissionSet\Depository\PermissionSet $permissionSetDepository, array $data = [])
        {
                parent::__construct($dba, $logger, $data);
 
-               $this->permissionSetRepository = $permissionSetRepository;
+               $this->permissionSetDepository = $permissionSetDepository;
        }
 
        public function __get($name)
@@ -62,9 +62,7 @@ class ProfileField extends BaseModel
 
                switch ($name) {
                        case 'permissionset':
-                               $this->permissionset =
-                                       $this->permissionset ??
-                                               $this->permissionSetRepository->selectFirst(['id' => $this->psid, 'uid' => $this->uid]);
+                               $this->permissionset = $this->permissionset ?? $this->permissionSetDepository->selectOneById($this->psid);
 
                                $return = $this->permissionset;
                                break;
index f597b0905943e5f010f0521d8ba2f8242623bcdc..7e2ceb8b0ef7dc78ba2092a12799726b2eefc324 100644 (file)
@@ -86,7 +86,7 @@ class Objects extends BaseModule
                                        $permissionSets = DI::permissionSet()->selectByContactId($requester_id, $item['uid']);
                                        if (!empty($permissionSets)) {
                                                $psid = array_merge($permissionSets->column('id'),
-                                                       [DI::permissionSet()->getIdFromACL($item['uid'], '', '', '', '')]);
+                                                       [DI::permissionSet()->selectEmptyForUser($item['uid'])]);
                                                $validated = in_array($item['psid'], $psid);
                                        }
                                }
index 9058ba5214121c0e02127fa0f57ffe8753b34f59..b1d4314b7ff79ac70111ba34bbe1139461056446 100644 (file)
@@ -28,7 +28,7 @@ use Friendica\Model\Contact;
 use Friendica\Model\Profile;
 use Friendica\Module\BaseApi;
 use Friendica\Network\HTTPException;
-use Friendica\Repository\PermissionSet;
+use Friendica\Security\PermissionSet\Depository\PermissionSet;
 
 /**
  * API endpoint: /api/friendica/profile/show
index b9429fce231ee65f560dc610b761b5050e7f0dab..3f23032d6d838b4618f1806357d99e9f16515ba9 100644 (file)
@@ -39,7 +39,7 @@ class PermissionTooltip extends \Friendica\BaseModule
                }
 
                if (isset($model['psid'])) {
-                       $permissionSet = DI::permissionSet()->selectFirst(['id' => $model['psid']]);
+                       $permissionSet = DI::permissionSet()->selectOneById($model['psid']);
                        $model['allow_cid'] = $permissionSet->allow_cid;
                        $model['allow_gid'] = $permissionSet->allow_gid;
                        $model['deny_cid']  = $permissionSet->deny_cid;
@@ -61,12 +61,10 @@ class PermissionTooltip extends \Friendica\BaseModule
                        exit;
                }
 
-               $aclFormatter = DI::aclFormatter();
-
-               $allowed_users  = $aclFormatter->expand($model['allow_cid']);
-               $allowed_groups = $aclFormatter->expand($model['allow_gid']);
-               $deny_users     = $aclFormatter->expand($model['deny_cid']);
-               $deny_groups    = $aclFormatter->expand($model['deny_gid']);
+               $allowed_users  = $model['allow_cid'];
+               $allowed_groups = $model['allow_gid'];
+               $deny_users     = $model['deny_cid'];
+               $deny_groups    = $model['deny_gid'];
 
                $o = DI::l10n()->t('Visible to:') . '<br />';
                $l = [];
index 8235c3265db6077d622888d07ae09fa77371d55d..d7e4ca671faab25804e75957fb5a1bc09c01e005 100644 (file)
@@ -213,7 +213,7 @@ class Status extends BaseProfile
                                $permissionSets = DI::permissionSet()->selectByContactId($remote_user, $profile['uid']);
                                if (!empty($permissionSets)) {
                                        $condition = ['psid' => array_merge($permissionSets->column('id'),
-                                                       [DI::permissionSet()->getIdFromACL($profile['uid'], '', '', '', '')])];
+                                                       [DI::permissionSet()->selectEmptyForUser($profile['uid'])])];
                                }
                        } elseif ($profile['uid'] == local_user()) {
                                $condition = [];
index b850a8273c8639d64d115daed115507e7bb29a10..1f869aebf4b2f0280db1473519c986ec79f2c606 100644 (file)
@@ -28,6 +28,7 @@ use Friendica\Core\Renderer;
 use Friendica\Core\Theme;
 use Friendica\Database\DBA;
 use Friendica\DI;
+use Friendica\Model\Contact;
 use Friendica\Model\Profile;
 use Friendica\Model\ProfileField;
 use Friendica\Model\User;
@@ -161,7 +162,9 @@ class Index extends BaseSettings
                $profileFields = DI::profileField()->selectByUserId(local_user());
                foreach ($profileFields as $profileField) {
                        /** @var ProfileField $profileField */
-                       $defaultPermissions = ACL::getDefaultUserPermissions($profileField->permissionset->toArray());
+                       $defaultPermissions = $profileField->permissionset->withAllowedContacts(
+                               Contact::pruneUnavailable($profileField->permissionset->allow_cid)
+                       );
 
                        $custom_fields[] = [
                                'id' => $profileField->id,
@@ -173,7 +176,7 @@ class Index extends BaseSettings
                                                DI::page(),
                                                $a->getLoggedInUserId(),
                                                false,
-                                               $defaultPermissions,
+                                               $defaultPermissions->toArray(),
                                                ['network' => Protocol::DFRN],
                                                'profile_field[' . $profileField->id . ']'
                                        ),
index 5c9142a5d392ae695766858da05f3fcb04c5fcec..9bf846c28330f2bb14c2c4c4d94ea9c7938e071e 100644 (file)
@@ -242,7 +242,7 @@ class Transmitter
                                $permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']);
                                if (!empty($permissionSets)) {
                                        $condition = ['psid' => array_merge($permissionSets->column('id'),
-                                                       [DI::permissionSet()->getIdFromACL($owner['uid'], '', '', '', '')])];
+                                                       [DI::permissionSet()->selectEmptyForUser($owner['uid'])])];
                                }
                        }
                }
diff --git a/src/Repository/PermissionSet.php b/src/Repository/PermissionSet.php
deleted file mode 100644 (file)
index 828247f..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2010-2021, the Friendica project
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Repository;
-
-use Friendica\BaseRepository;
-use Friendica\Collection;
-use Friendica\Database\Database;
-use Friendica\Model;
-use Friendica\Network\HTTPException;
-use Friendica\Util\ACLFormatter;
-use Psr\Log\LoggerInterface;
-
-class PermissionSet extends BaseRepository
-{
-       /** @var int Virtual permission set id for public permission */
-       const PUBLIC = 0;
-
-       protected static $table_name = 'permissionset';
-
-       protected static $model_class = Model\PermissionSet::class;
-
-       protected static $collection_class = Collection\PermissionSets::class;
-
-       /** @var ACLFormatter */
-       private $aclFormatter;
-
-       public function __construct(Database $dba, LoggerInterface $logger, ACLFormatter $aclFormatter)
-       {
-               parent::__construct($dba, $logger);
-
-               $this->aclFormatter = $aclFormatter;
-       }
-
-       /**
-        * @param array $data
-        * @return Model\PermissionSet
-        */
-       protected function create(array $data)
-       {
-               return new Model\PermissionSet($this->dba, $this->logger, $data);
-       }
-
-       /**
-        * @param array $condition
-        * @return Model\PermissionSet
-        * @throws \Friendica\Network\HTTPException\NotFoundException
-        */
-       public function selectFirst(array $condition)
-       {
-               if (isset($condition['id']) && !$condition['id']) {
-                       return $this->create([
-                               'id' => self::PUBLIC,
-                               'uid' => $condition['uid'] ?? 0,
-                               'allow_cid' => '',
-                               'allow_gid' => '',
-                               'deny_cid' => '',
-                               'deny_gid' => '',
-                       ]);
-               }
-
-               return parent::selectFirst($condition);
-       }
-
-       /**
-        * @param array $condition
-        * @param array $params
-        * @return Collection\PermissionSets
-        * @throws \Exception
-        */
-       public function select(array $condition = [], array $params = [])
-       {
-               return parent::select($condition, $params);
-       }
-
-       /**
-        * @param array    $condition
-        * @param array    $params
-        * @param int|null $min_id
-        * @param int|null $max_id
-        * @param int      $limit
-        * @return Collection\PermissionSets
-        * @throws \Exception
-        */
-       public function selectByBoundaries(array $condition = [], array $params = [], int $min_id = null, int $max_id = null, int $limit = self::LIMIT)
-       {
-               return parent::selectByBoundaries($condition, $params, $min_id, $max_id, $limit);
-       }
-
-       /**
-        * Fetch the id of a given permission set. Generate a new one when needed
-        *
-        * @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
-        */
-       public function getIdFromACL(
-               int $uid,
-               string $allow_cid = null,
-               string $allow_gid = null,
-               string $deny_cid = null,
-               string $deny_gid = null
-       ) {
-               $allow_cid = $this->aclFormatter->sanitize($allow_cid);
-               $allow_gid = $this->aclFormatter->sanitize($allow_gid);
-               $deny_cid = $this->aclFormatter->sanitize($deny_cid);
-               $deny_gid = $this->aclFormatter->sanitize($deny_gid);
-
-               // Public permission
-               if (!$allow_cid && !$allow_gid && !$deny_cid && !$deny_gid) {
-                       return self::PUBLIC;
-               }
-
-               $condition = [
-                       'uid' => $uid,
-                       'allow_cid' => $allow_cid,
-                       'allow_gid' => $allow_gid,
-                       'deny_cid'  => $deny_cid,
-                       'deny_gid'  => $deny_gid
-               ];
-
-               try {
-                       $permissionset = $this->selectFirst($condition);
-               } catch(HTTPException\NotFoundException $exception) {
-                       $permissionset = $this->insert($condition);
-               }
-
-               return $permissionset->id;
-       }
-
-       /**
-        * Returns a permission set collection for a given contact
-        *
-        * @param integer $contact_id Contact id of the visitor
-        * @param integer $uid        User id whom the items belong, used for ownership check.
-        *
-        * @return Collection\PermissionSets
-        * @throws \Exception
-        */
-       public function selectByContactId($contact_id, $uid)
-       {
-               $cdata = Model\Contact::getPublicAndUserContactID($contact_id, $uid);
-               if (!empty($cdata)) {
-                       $public_contact_str = '<' . $cdata['public'] . '>';
-                       $user_contact_str = '<' . $cdata['user'] . '>';
-                       $contact_id = $cdata['user'];
-               } else {
-                       $public_contact_str = '<' . $contact_id . '>';
-                       $user_contact_str = '';
-               }
-
-               $groups = [];
-               if (!empty($user_contact_str) && $this->dba->exists('contact', ['id' => $contact_id, 'uid' => $uid, 'blocked' => false])) {
-                       $groups = Model\Group::getIdsByContactId($contact_id);
-               }
-
-               $group_str = '<<>>'; // should be impossible to match
-               foreach ($groups as $group_id) {
-                       $group_str .= '|<' . preg_quote($group_id) . '>';
-               }
-
-               if (!empty($user_contact_str)) {
-                       $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR `deny_cid` REGEXP ? OR deny_gid REGEXP ?)
-                               AND (allow_cid REGEXP ? OR allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))",
-                               $uid, $user_contact_str, $public_contact_str, $group_str,
-                               $user_contact_str, $public_contact_str, $group_str];
-               } else {
-                       $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR deny_gid REGEXP ?)
-                               AND (allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))",
-                               $uid, $public_contact_str, $group_str, $public_contact_str, $group_str];
-               }
-
-               return $this->select($condition);
-       }
-}
index bf7ac69d6131f28ed101c33dcd4d042dc2156805..40417be486ca25b463fddced4040a3fc08322c83 100644 (file)
@@ -28,7 +28,7 @@ use Friendica\Core\L10n;
 use Friendica\Database\Database;
 use Friendica\Database\DBA;
 use Friendica\Model;
-use Friendica\Util\ACLFormatter;
+use Friendica\Security\PermissionSet\Depository\PermissionSet;
 use Friendica\Util\DateTimeFormat;
 use Psr\Log\LoggerInterface;
 
@@ -42,18 +42,18 @@ class ProfileField extends BaseRepository
 
        /** @var PermissionSet */
        private $permissionSet;
-       /** @var ACLFormatter */
-       private $aclFormatter;
+       /** @var \Friendica\Security\PermissionSet\Factory\PermissionSet */
+       private $permissionSetFactory;
        /** @var L10n */
        private $l10n;
 
-       public function __construct(Database $dba, LoggerInterface $logger, PermissionSet $permissionSet, ACLFormatter $aclFormatter, L10n $l10n)
+       public function __construct(Database $dba, LoggerInterface $logger, PermissionSet $permissionSet, \Friendica\Security\PermissionSet\Factory\PermissionSet $permissionSetFactory, L10n $l10n)
        {
                parent::__construct($dba, $logger);
 
-               $this->permissionSet = $permissionSet;
-               $this->aclFormatter = $aclFormatter;
-               $this->l10n = $l10n;
+               $this->permissionSet        = $permissionSet;
+               $this->permissionSetFactory = $permissionSetFactory;
+               $this->l10n                 = $l10n;
        }
 
        /**
@@ -160,7 +160,7 @@ class ProfileField extends BaseRepository
 
                return parent::update($model);
        }
-       
+
        /**
         * @param int                      $uid                User Id
         * @param Collection\ProfileFields $profileFields      Collection of existing profile fields
@@ -176,24 +176,24 @@ class ProfileField extends BaseRepository
 
                // Creation of the new field
                if (!empty($profileFieldInputs['new']['label'])) {
-                       $psid = $this->permissionSet->getIdFromACL(
+                       $psid = $this->permissionSet->selectOrCreate($this->permissionSetFactory->createFromString(
                                $uid,
-                               $this->aclFormatter->toString($profileFieldInputs['new']['contact_allow'] ?? ''),
-                               $this->aclFormatter->toString($profileFieldInputs['new']['group_allow'] ?? ''),
-                               $this->aclFormatter->toString($profileFieldInputs['new']['contact_deny'] ?? ''),
-                               $this->aclFormatter->toString($profileFieldInputs['new']['group_deny'] ?? '')
-                       );
+                               $profileFieldInputs['new']['contact_allow'] ?? '',
+                               $profileFieldInputs['new']['group_allow'] ?? '',
+                               $profileFieldInputs['new']['contact_deny'] ?? '',
+                               $profileFieldInputs['new']['group_deny'] ?? ''
+                       ))->id;
 
                        $newProfileField = $this->insert([
-                               'uid' => $uid,
+                               'uid'   => $uid,
                                'label' => $profileFieldInputs['new']['label'],
                                'value' => $profileFieldInputs['new']['value'],
-                               'psid' => $psid,
+                               'psid'  => $psid,
                                'order' => $profileFieldOrder['new'],
                        ]);
 
                        $profileFieldInputs[$newProfileField->id] = $profileFieldInputs['new'];
-                       $profileFieldOrder[$newProfileField->id] = $profileFieldOrder['new'];
+                       $profileFieldOrder[$newProfileField->id]  = $profileFieldOrder['new'];
 
                        $profileFields[] = $newProfileField;
                }
@@ -220,15 +220,15 @@ class ProfileField extends BaseRepository
                // Update existing profile fields from form values
                $profileFields = $profileFields->map(function (Model\ProfileField $profileField) use ($uid, &$profileFieldInputs, &$profileFieldOrder) {
                        if (isset($profileFieldInputs[$profileField->id]) && isset($profileFieldOrder[$profileField->id])) {
-                               $psid = $this->permissionSet->getIdFromACL(
+                               $psid = $this->permissionSet->selectOrCreate($this->permissionSetFactory->createFromString(
                                        $uid,
-                                       $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['contact_allow'] ?? ''),
-                                       $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['group_allow'] ?? ''),
-                                       $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['contact_deny'] ?? ''),
-                                       $this->aclFormatter->toString($profileFieldInputs[$profileField->id]['group_deny'] ?? '')
-                               );
+                                       $profileFieldInputs[$profileField->id]['contact_allow'] ?? '',
+                                       $profileFieldInputs[$profileField->id]['group_allow'] ?? '',
+                                       $profileFieldInputs[$profileField->id]['contact_deny'] ?? '',
+                                       $profileFieldInputs[$profileField->id]['group_deny'] ?? ''
+                               ))->id;
 
-                               $profileField->psid  = $psid;
+                               $profileField->psid = $psid;
                                $profileField->label = $profileFieldInputs[$profileField->id]['label'];
                                $profileField->value = $profileFieldInputs[$profileField->id]['value'];
                                $profileField->order = $profileFieldOrder[$profileField->id];
@@ -257,17 +257,22 @@ class ProfileField extends BaseRepository
                        return;
                }
 
+               $contacts = [];
+
                if (!$profile['is-default']) {
                        $contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'profile-id' => $profile['id']]);
                        if (!count($contacts)) {
                                // No contact visibility selected defaults to user-only permission
                                $contacts = Model\Contact::selectToArray(['id'], ['uid' => $profile['uid'], 'self' => true]);
                        }
-
-                       $allow_cid = $this->aclFormatter->toString(array_column($contacts, 'id'));
                }
 
-               $psid = $this->permissionSet->getIdFromACL($profile['uid'], $allow_cid ?? '');
+               $psid = $this->permissionSet->selectOrCreate(
+                       new \Friendica\Security\PermissionSet\Entity\PermissionSet(
+                               $profile['uid'],
+                               array_column($contacts, 'id') ?? []
+                       )
+               )->id;
 
                $order = 1;
 
@@ -297,8 +302,8 @@ class ProfileField extends BaseRepository
                foreach ($custom_fields as $field => $label) {
                        if (!empty($profile[$field]) && $profile[$field] > DBA::NULL_DATE && $profile[$field] > DBA::NULL_DATETIME) {
                                $this->insert([
-                                       'uid' => $profile['uid'],
-                                       'psid' => $psid,
+                                       'uid'   => $profile['uid'],
+                                       'psid'  => $psid,
                                        'order' => $order++,
                                        'label' => trim($label, ':'),
                                        'value' => $profile[$field],
@@ -310,7 +315,7 @@ class ProfileField extends BaseRepository
 
                if ($profile['is-default']) {
                        $profile['profile-name'] = null;
-                       $profile['is-default'] = null;
+                       $profile['is-default']   = null;
                        $this->dba->update('profile', $profile, ['id' => $profile['id']]);
                } elseif (!empty($profile['id'])) {
                        $this->dba->delete('profile', ['id' => $profile['id']]);
diff --git a/src/Security/PermissionSet/Collection/PermissionSets.php b/src/Security/PermissionSet/Collection/PermissionSets.php
new file mode 100644 (file)
index 0000000..033c4cf
--- /dev/null
@@ -0,0 +1,10 @@
+<?php
+
+namespace Friendica\Security\PermissionSet\Collection;
+
+use Friendica\BaseCollection;
+
+class PermissionSets extends BaseCollection
+{
+
+}
diff --git a/src/Security/PermissionSet/Depository/PermissionSet.php b/src/Security/PermissionSet/Depository/PermissionSet.php
new file mode 100644 (file)
index 0000000..b02f0e4
--- /dev/null
@@ -0,0 +1,210 @@
+<?php
+/**
+ * @copyright Copyright (C) 2010-2021, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Security\PermissionSet\Depository;
+
+use Exception;
+use Friendica\BaseDepository;
+use Friendica\Database\Database;
+use Friendica\Model\Contact;
+use Friendica\Model\Group;
+use Friendica\Network\HTTPException\NotFoundException;
+use Friendica\Security\PermissionSet\Factory;
+use Friendica\Security\PermissionSet\Collection;
+use Friendica\Security\PermissionSet\Entity;
+use Friendica\Util\ACLFormatter;
+use Psr\Log\LoggerInterface;
+
+class PermissionSet extends BaseDepository
+{
+       /** @var int Virtual permission set id for public permission */
+       const PUBLIC = 0;
+
+       /** @var Factory\PermissionSet */
+       protected $factory;
+
+       protected static $table_name = 'permissionset';
+
+       /** @var ACLFormatter */
+       private $aclFormatter;
+
+       public function __construct(Database $database, LoggerInterface $logger, Factory\PermissionSet $factory, ACLFormatter $aclFormatter)
+       {
+               parent::__construct($database, $logger, $factory);
+
+               $this->aclFormatter = $aclFormatter;
+       }
+
+       /**
+        * @param array $condition
+        * @param array $params
+        *
+        * @return Entity\PermissionSet
+        * @throws NotFoundException
+        */
+       private function selectOne(array $condition, array $params = []): Entity\PermissionSet
+       {
+               return parent::_selectOne($condition, $params);
+       }
+
+       private function select(array $condition, array $params = []): Collection\PermissionSets
+       {
+               return new Collection\PermissionSets(parent::_select($condition, $params)->getArrayCopy());
+       }
+
+       /**
+        * @param int $id
+        *
+        * @return Entity\PermissionSet
+        * @throws NotFoundException
+        */
+       public function selectOneById(int $id): Entity\PermissionSet
+       {
+               return $this->selectOne(['id' => $id]);
+       }
+
+       /**
+        * Returns a permission set collection for a given contact
+        *
+        * @param int $cid Contact id of the visitor
+        * @param int $uid User id whom the items belong, used for ownership check.
+        *
+        * @return Collection\PermissionSets
+        */
+       public function selectByContactId(int $cid, int $uid): Collection\PermissionSets
+       {
+               $cdata = Contact::getPublicAndUserContactID($cid, $uid);
+               if (!empty($cdata)) {
+                       $public_contact_str = $this->aclFormatter->toString($cdata['public']);
+                       $user_contact_str   = $this->aclFormatter->toString($cdata['user']);
+                       $cid                = $cdata['user'];
+               } else {
+                       $public_contact_str = $this->aclFormatter->toString($cid);
+                       $user_contact_str   = '';
+               }
+
+               $groups = [];
+               if (!empty($user_contact_str) && $this->db->exists('contact', [
+                       'id' => $cid,
+                       'uid' => $uid,
+                       'blocked' => false
+               ])) {
+                       $groups = Group::getIdsByContactId($cid);
+               }
+
+               $group_str = '<<>>'; // should be impossible to match
+               foreach ($groups as $group_id) {
+                       $group_str .= '|<' . preg_quote($group_id) . '>';
+               }
+
+               if (!empty($user_contact_str)) {
+                       $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR `deny_cid` REGEXP ? OR deny_gid REGEXP ?)
+                               AND (allow_cid REGEXP ? OR allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))",
+                               $uid, $user_contact_str, $public_contact_str, $group_str,
+                               $user_contact_str, $public_contact_str, $group_str];
+               } else {
+                       $condition = ["`uid` = ? AND (NOT (`deny_cid` REGEXP ? OR deny_gid REGEXP ?)
+                               AND (allow_cid REGEXP ? OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))",
+                               $uid, $public_contact_str, $group_str, $public_contact_str, $group_str];
+               }
+
+               return $this->select($condition);
+       }
+
+       /**
+        * Fetch the default PermissionSet for a given user, create it if it doesn't exist
+        *
+        * @param int $uid
+        *
+        * @return Entity\PermissionSet
+        * @throws Exception
+        */
+       public function selectDefaultForUser(int $uid): Entity\PermissionSet
+       {
+               $self_contact = Contact::selectFirst(['id'], ['uid' => $uid, 'self' => true]);
+
+               return $this->selectOrCreate($this->factory->createFromString(
+                       $uid,
+                       $this->aclFormatter->toString($self_contact['id'])
+               ));
+       }
+
+       /**
+        * Fetch the empty PermissionSet for a given user, create it if it doesn't exist
+        *
+        * @param int $uid
+        *
+        * @return Entity\PermissionSet
+        */
+       public function selectEmptyForUser(int $uid): Entity\PermissionSet
+       {
+               return $this->selectOrCreate($this->factory->createFromString($uid));
+       }
+
+       /**
+        * Selects or creates a PermissionSet based on it's fields
+        *
+        * @param Entity\PermissionSet $permissionSet
+        *
+        * @return Entity\PermissionSet
+        */
+       public function selectOrCreate(Entity\PermissionSet $permissionSet): Entity\PermissionSet
+       {
+               if ($permissionSet->id) {
+                       return $permissionSet;
+               }
+
+               $fields = [
+                       'uid'       => $permissionSet->uid,
+                       'allow_cid' => $this->aclFormatter->toString($permissionSet->allow_cid),
+                       'allow_gid' => $this->aclFormatter->toString($permissionSet->allow_gid),
+                       'deny_cid'  => $this->aclFormatter->toString($permissionSet->deny_cid),
+                       'deny_gid'  => $this->aclFormatter->toString($permissionSet->deny_gid),
+               ];
+
+               try {
+                       return $this->selectOne($fields);
+               } catch (NotFoundException $exception) {
+                       return $this->save($permissionSet);
+               }
+       }
+
+       public function save(Entity\PermissionSet $permissionSet): Entity\PermissionSet
+       {
+               $fields = [
+                       'uid'       => $permissionSet->uid,
+                       'allow_cid' => $this->aclFormatter->toString($permissionSet->allow_cid),
+                       'allow_gid' => $this->aclFormatter->toString($permissionSet->allow_gid),
+                       'deny_cid'  => $this->aclFormatter->toString($permissionSet->deny_cid),
+                       'deny_gid'  => $this->aclFormatter->toString($permissionSet->deny_gid),
+               ];
+
+               if ($permissionSet->id) {
+                       $this->db->update(self::$table_name, $fields, ['id' => $permissionSet->id]);
+               } else {
+                       $this->db->insert(self::$table_name, $fields);
+
+                       $permissionSet = $this->selectOneById($this->db->lastInsertId());
+               }
+
+               return $permissionSet;
+       }
+}
diff --git a/src/Security/PermissionSet/Entity/PermissionSet.php b/src/Security/PermissionSet/Entity/PermissionSet.php
new file mode 100644 (file)
index 0000000..8498716
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+
+namespace Friendica\Security\PermissionSet\Entity;
+
+use Friendica\BaseEntity;
+
+/**
+ * @property-read int|null $id
+ * @property-read int      $uid
+ * @property-read string[] $allow_cid
+ * @property-read string[] $allow_gid
+ * @property-read string[] $deny_cid
+ * @property-read string[] $deny_gid
+ */
+class PermissionSet extends BaseEntity
+{
+       /** @var int|null */
+       protected $id;
+       /** @var int */
+       protected $uid;
+       /** @var string[] */
+       protected $allow_cid;
+       /** @var string[] */
+       protected $allow_gid;
+       /** @var string[] */
+       protected $deny_cid;
+       /** @var string[] */
+       protected $deny_gid;
+
+       /**
+        * @param int|null $id
+        * @param int      $uid
+        * @param string[] $allow_cid
+        * @param string[] $allow_gid
+        * @param string[] $deny_cid
+        * @param string[] $deny_gid
+        *
+        * @see \Friendica\Security\PermissionSet\Factory\PermissionSet
+        */
+       public function __construct(int $uid, array $allow_cid = [], array $allow_gid = [], array $deny_cid = [], array $deny_gid = [], int $id = null)
+       {
+               $this->id        = $id;
+               $this->uid       = $uid;
+               $this->allow_cid = $allow_cid;
+               $this->allow_gid = $allow_gid;
+               $this->deny_cid  = $deny_cid;
+               $this->deny_gid  = $deny_gid;
+       }
+
+       /**
+        * Creates a new Entity with a new allowed_cid list (wipes the id because it isn't the same entity anymore)
+        *
+        * @param array $allow_cid
+        *
+        * @return $this
+        */
+       public function withAllowedContacts(array $allow_cid): PermissionSet
+       {
+               $clone = clone $this;
+
+               $clone->allow_cid = $allow_cid;
+               $clone->id        = null;
+
+               return $clone;
+       }
+}
diff --git a/src/Security/PermissionSet/Factory/PermissionSet.php b/src/Security/PermissionSet/Factory/PermissionSet.php
new file mode 100644 (file)
index 0000000..9887ffe
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+
+namespace Friendica\Security\PermissionSet\Factory;
+
+use Friendica\BaseFactory;
+use Friendica\Capabilities\ICanCreateFromTableRow;
+use Friendica\Security\PermissionSet\Entity;
+use Friendica\Util\ACLFormatter;
+use Psr\Log\LoggerInterface;
+
+class PermissionSet extends BaseFactory implements ICanCreateFromTableRow
+{
+       /** @var ACLFormatter */
+       protected $formatter;
+
+       public function __construct(LoggerInterface $logger, ACLFormatter $formatter)
+       {
+               parent::__construct($logger);
+
+               $this->formatter = $formatter;
+       }
+
+       public function createFromTableRow(array $row): Entity\PermissionSet
+       {
+               return new Entity\PermissionSet(
+                       $row['uid'],
+                       $this->formatter->expand($row['allow_cid'] ?? []),
+                       $this->formatter->expand($row['allow_gid'] ?? []),
+                       $this->formatter->expand($row['deny_cid'] ?? []),
+                       $this->formatter->expand($row['deny_gid'] ?? []),
+                       $row['id'] ?? null
+               );
+       }
+
+       public function createFromString(
+               int $uid,
+               string $allow_cid = '',
+               string $allow_gid = '',
+               string $deny_cid = '',
+               string $deny_gid = '')
+       {
+               return new Entity\PermissionSet(
+                       $uid,
+                       $this->formatter->expand($allow_cid),
+                       $this->formatter->expand($allow_gid),
+                       $this->formatter->expand($deny_cid),
+                       $this->formatter->expand($deny_gid)
+               );
+       }
+
+       public function createPrototypeForUser(int $uid, string $allowCid): Entity\PermissionSet
+       {
+               return new Entity\PermissionSet(
+                       $uid,
+                       $this->formatter->expand($allowCid)
+               );
+       }
+}