]> git.mxchange.org Git - friendica.git/blob - src/Security/PermissionSet/Repository/PermissionSet.php
Fixing tests - maybe
[friendica.git] / src / Security / PermissionSet / Repository / PermissionSet.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Security\PermissionSet\Repository;
23
24 use Exception;
25 use Friendica\BaseRepository;
26 use Friendica\Database\Database;
27 use Friendica\Model\Contact;
28 use Friendica\Model\Group;
29 use Friendica\Network\HTTPException\NotFoundException;
30 use Friendica\Security\PermissionSet\Exception\PermissionSetNotFoundException;
31 use Friendica\Security\PermissionSet\Exception\PermissionSetPersistenceException;
32 use Friendica\Security\PermissionSet\Factory;
33 use Friendica\Security\PermissionSet\Collection;
34 use Friendica\Security\PermissionSet\Entity;
35 use Friendica\Util\ACLFormatter;
36 use Psr\Log\LoggerInterface;
37
38 class PermissionSet extends BaseRepository
39 {
40         /** @var int Virtual permission set id for public permission */
41         const PUBLIC = 0;
42
43         /** @var Factory\PermissionSet */
44         protected $factory;
45
46         protected static $table_name = 'permissionset';
47
48         /** @var ACLFormatter */
49         private $aclFormatter;
50
51         public function __construct(Database $database, LoggerInterface $logger, Factory\PermissionSet $factory, ACLFormatter $aclFormatter)
52         {
53                 parent::__construct($database, $logger, $factory);
54
55                 $this->aclFormatter = $aclFormatter;
56         }
57
58         /**
59          * @param array $condition
60          * @param array $params
61          *
62          * @return Entity\PermissionSet
63          * @throws NotFoundException
64          * @throws Exception
65          */
66         private function selectOne(array $condition, array $params = []): Entity\PermissionSet
67         {
68                 return parent::_selectOne($condition, $params);
69         }
70
71         /**
72          * @throws Exception
73          */
74         private function select(array $condition, array $params = []): Collection\PermissionSets
75         {
76                 return new Collection\PermissionSets(parent::_select($condition, $params)->getArrayCopy());
77         }
78
79         /**
80          * Converts a given PermissionSet into a DB compatible row array
81          *
82          * @param Entity\PermissionSet $permissionSet
83          *
84          * @return array
85          */
86         protected function convertToTableRow(Entity\PermissionSet $permissionSet): array
87         {
88                 return [
89                         'uid'       => $permissionSet->uid,
90                         'allow_cid' => $this->aclFormatter->toString($permissionSet->allow_cid),
91                         'allow_gid' => $this->aclFormatter->toString($permissionSet->allow_gid),
92                         'deny_cid'  => $this->aclFormatter->toString($permissionSet->deny_cid),
93                         'deny_gid'  => $this->aclFormatter->toString($permissionSet->deny_gid),
94                 ];
95         }
96
97         /**
98          * @param int $id  A PermissionSet table row id or self::PUBLIC
99          * @param int $uid The owner of the PermissionSet
100          * @return Entity\PermissionSet
101          *
102          * @throws PermissionSetNotFoundException
103          * @throws PermissionSetPersistenceException
104          */
105         public function selectOneById(int $id, int $uid): Entity\PermissionSet
106         {
107                 if ($id === self::PUBLIC) {
108                         return $this->factory->createFromString($uid);
109                 }
110
111                 try {
112                         return $this->selectOne(['id' => $id, 'uid' => $uid]);
113                 } catch (NotFoundException $exception) {
114                         throw new PermissionSetNotFoundException(sprintf('PermissionSet with id %d for user %u doesn\'t exist.', $id, $uid), $exception);
115                 } catch (Exception $exception) {
116                         throw new PermissionSetPersistenceException(sprintf('Cannot select PermissionSet %d for user %d', $id, $uid), $exception);
117                 }
118         }
119
120         /**
121          * Returns a permission set collection for a given contact
122          *
123          * @param int $cid Contact id of the visitor
124          * @param int $uid User id whom the items belong, used for ownership check.
125          *
126          * @return Collection\PermissionSets
127          *
128          * @throws PermissionSetPersistenceException
129          */
130         public function selectByContactId(int $cid, int $uid): Collection\PermissionSets
131         {
132                 try {
133                         $cdata = Contact::getPublicAndUserContactID($cid, $uid);
134                         if (!empty($cdata)) {
135                                 $public_contact_str = $this->aclFormatter->toString($cdata['public']);
136                                 $user_contact_str   = $this->aclFormatter->toString($cdata['user']);
137                                 $cid                = $cdata['user'];
138                         } else {
139                                 $public_contact_str = $this->aclFormatter->toString($cid);
140                                 $user_contact_str   = '';
141                         }
142
143                         $groups = [];
144                         if (!empty($user_contact_str) && $this->db->exists('contact', [
145                                 'id' => $cid,
146                                 'uid' => $uid,
147                                 'blocked' => false
148                         ])) {
149                                 $groups = Group::getIdsByContactId($cid);
150                         }
151
152                         $group_str = '<<>>'; // should be impossible to match
153                         foreach ($groups as $group_id) {
154                                 $group_str .= '|<' . preg_quote($group_id) . '>';
155                         }
156
157                         if (!empty($user_contact_str)) {
158                                 $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR LOCATE(?, `deny_cid`) OR deny_gid REGEXP ?)
159                                 AND (LOCATE(?, allow_cid) OR LOCATE(?, allow_cid) OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))",
160                                         $uid, $user_contact_str, $public_contact_str, $group_str,
161                                         $user_contact_str, $public_contact_str, $group_str];
162                         } else {
163                                 $condition = ["`uid` = ? AND (NOT (LOCATE(?, `deny_cid`) OR deny_gid REGEXP ?)
164                                 AND (LOCATE(?, allow_cid) OR allow_gid REGEXP ? OR (allow_cid = '' AND allow_gid = '')))",
165                                         $uid, $public_contact_str, $group_str, $public_contact_str, $group_str];
166                         }
167
168                         return $this->select($condition);
169                 } catch (Exception $exception) {
170                         throw new PermissionSetPersistenceException(sprintf('Cannot select PermissionSet for contact %d and user %d', $cid, $uid), $exception);
171                 }
172         }
173
174         /**
175          * Fetch the default PermissionSet for a given user, create it if it doesn't exist
176          *
177          * @param int $uid
178          *
179          * @return Entity\PermissionSet
180          *
181          * @throws PermissionSetPersistenceException
182          */
183         public function selectDefaultForUser(int $uid): Entity\PermissionSet
184         {
185                 try {
186                         $self_contact = Contact::selectFirst(['id'], ['uid' => $uid, 'self' => true]);
187                 } catch (Exception $exception) {
188                         throw new PermissionSetPersistenceException(sprintf('Cannot select Contact for user %d', $uid));
189                 }
190
191                 if (!$this->db->isResult($self_contact)) {
192                         throw new PermissionSetPersistenceException(sprintf('No "self" contact found for user %d', $uid));
193                 }
194
195                 return $this->selectOrCreate($this->factory->createFromString(
196                         $uid,
197                         $this->aclFormatter->toString($self_contact['id'])
198                 ));
199         }
200
201         /**
202          * Fetch the public PermissionSet
203          *
204          * @param int $uid
205          *
206          * @return Entity\PermissionSet
207          */
208         public function selectPublicForUser(int $uid): Entity\PermissionSet
209         {
210                 return $this->factory->createFromString($uid, '', '', '', '', self::PUBLIC);
211         }
212
213         /**
214          * Selects or creates a PermissionSet based on it's fields
215          *
216          * @param Entity\PermissionSet $permissionSet
217          *
218          * @return Entity\PermissionSet
219          *
220          * @throws PermissionSetPersistenceException
221          */
222         public function selectOrCreate(Entity\PermissionSet $permissionSet): Entity\PermissionSet
223         {
224                 if ($permissionSet->id) {
225                         return $permissionSet;
226                 }
227
228                 // Don't select/update Public permission sets
229                 if ($permissionSet->isPublic()) {
230                         return $this->selectPublicForUser($permissionSet->uid);
231                 }
232
233                 try {
234                         return $this->selectOne($this->convertToTableRow($permissionSet));
235                 } catch (NotFoundException $exception) {
236                         return $this->save($permissionSet);
237                 } catch (Exception $exception) {
238                         throw new PermissionSetPersistenceException(sprintf('Cannot select PermissionSet %d', $permissionSet->id ?? 0), $exception);
239                 }
240         }
241
242         /**
243          * @param Entity\PermissionSet $permissionSet
244          *
245          * @return Entity\PermissionSet
246          *
247          * @throws PermissionSetPersistenceException
248          */
249         public function save(Entity\PermissionSet $permissionSet): Entity\PermissionSet
250         {
251                 // Don't save/update the common public PermissionSet
252                 if ($permissionSet->isPublic()) {
253                         return $this->selectPublicForUser($permissionSet->uid);
254                 }
255
256                 $fields = $this->convertToTableRow($permissionSet);
257
258                 try {
259                         if ($permissionSet->id) {
260                                 $this->db->update(self::$table_name, $fields, ['id' => $permissionSet->id]);
261                         } else {
262                                 $this->db->insert(self::$table_name, $fields);
263
264                                 $permissionSet = $this->selectOneById($this->db->lastInsertId(), $permissionSet->uid);
265                         }
266                 } catch (Exception $exception) {
267                         throw new PermissionSetPersistenceException(sprintf('Cannot save PermissionSet %d', $permissionSet->id ?? 0), $exception);
268                 }
269
270                 return $permissionSet;
271         }
272 }