1a25f0a04SGreg Roach<?php 2a25f0a04SGreg Roach/** 3a25f0a04SGreg Roach * webtrees: online genealogy 48fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team 5a25f0a04SGreg Roach * This program is free software: you can redistribute it and/or modify 6a25f0a04SGreg Roach * it under the terms of the GNU General Public License as published by 7a25f0a04SGreg Roach * the Free Software Foundation, either version 3 of the License, or 8a25f0a04SGreg Roach * (at your option) any later version. 9a25f0a04SGreg Roach * This program is distributed in the hope that it will be useful, 10a25f0a04SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 11a25f0a04SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12a25f0a04SGreg Roach * GNU General Public License for more details. 13a25f0a04SGreg Roach * You should have received a copy of the GNU General Public License 14a25f0a04SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 15a25f0a04SGreg Roach */ 16e7f56f2aSGreg Roachdeclare(strict_types=1); 17e7f56f2aSGreg Roach 1876692c8bSGreg Roachnamespace Fisharebest\Webtrees; 19a25f0a04SGreg Roach 20e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException; 21e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyNotFoundException; 22e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException; 23e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualNotFoundException; 24e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException; 25e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaNotFoundException; 26e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException; 27e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteNotFoundException; 28e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException; 29e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordNotFoundException; 30e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException; 31e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException; 32e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException; 33e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceNotFoundException; 34*9867b2f0SGreg Roachuse Fisharebest\Webtrees\Module\ModuleInterface; 3579529c87SGreg Roachuse stdClass; 36*9867b2f0SGreg Roachuse Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; 3779529c87SGreg Roach 38a25f0a04SGreg Roach/** 3976692c8bSGreg Roach * Authentication. 40a25f0a04SGreg Roach */ 41c1010edaSGreg Roachclass Auth 42c1010edaSGreg Roach{ 434b9ff166SGreg Roach // Privacy constants 4416d6367aSGreg Roach public const PRIV_PRIVATE = 2; // Allows visitors to view the item 4516d6367aSGreg Roach public const PRIV_USER = 1; // Allows members to access the item 4616d6367aSGreg Roach public const PRIV_NONE = 0; // Allows managers to access the item 4716d6367aSGreg Roach public const PRIV_HIDE = -1; // Hide the item to all users 484b9ff166SGreg Roach 49a25f0a04SGreg Roach /** 50a25f0a04SGreg Roach * Are we currently logged in? 51a25f0a04SGreg Roach * 52cbc1590aSGreg Roach * @return bool 53a25f0a04SGreg Roach */ 548f53f488SRico Sonntag public static function check(): bool 55c1010edaSGreg Roach { 564b9ff166SGreg Roach return self::id() !== null; 57a25f0a04SGreg Roach } 58a25f0a04SGreg Roach 59a25f0a04SGreg Roach /** 60a25f0a04SGreg Roach * Is the specified/current user an administrator? 61a25f0a04SGreg Roach * 62a25f0a04SGreg Roach * @param User|null $user 63a25f0a04SGreg Roach * 64cbc1590aSGreg Roach * @return bool 65a25f0a04SGreg Roach */ 668f53f488SRico Sonntag public static function isAdmin(User $user = null): bool 67c1010edaSGreg Roach { 68cb923727SGreg Roach $user = $user ?? self::user(); 69a25f0a04SGreg Roach 70cb923727SGreg Roach return $user->getPreference('canadmin') === '1'; 71a25f0a04SGreg Roach } 72a25f0a04SGreg Roach 73a25f0a04SGreg Roach /** 744b9ff166SGreg Roach * Is the specified/current user a manager of a tree? 75a25f0a04SGreg Roach * 7684caa210SGreg Roach * @param Tree $tree 77a25f0a04SGreg Roach * @param User|null $user 78a25f0a04SGreg Roach * 79cbc1590aSGreg Roach * @return bool 80a25f0a04SGreg Roach */ 818f53f488SRico Sonntag public static function isManager(Tree $tree, User $user = null): bool 82c1010edaSGreg Roach { 83cb923727SGreg Roach $user = $user ?? self::user(); 84a25f0a04SGreg Roach 85cb923727SGreg Roach return self::isAdmin($user) || $tree->getUserPreference($user, 'canedit') === 'admin'; 86a25f0a04SGreg Roach } 87a25f0a04SGreg Roach 88a25f0a04SGreg Roach /** 894b9ff166SGreg Roach * Is the specified/current user a moderator of a tree? 90a25f0a04SGreg Roach * 9184caa210SGreg Roach * @param Tree $tree 92a25f0a04SGreg Roach * @param User|null $user 93a25f0a04SGreg Roach * 94cbc1590aSGreg Roach * @return bool 95a25f0a04SGreg Roach */ 968f53f488SRico Sonntag public static function isModerator(Tree $tree, User $user = null): bool 97c1010edaSGreg Roach { 98cb923727SGreg Roach $user = $user ?? self::user(); 99a25f0a04SGreg Roach 100cb923727SGreg Roach return self::isManager($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'accept'; 101a25f0a04SGreg Roach } 102a25f0a04SGreg Roach 103a25f0a04SGreg Roach /** 1044b9ff166SGreg Roach * Is the specified/current user an editor of a tree? 105a25f0a04SGreg Roach * 10684caa210SGreg Roach * @param Tree $tree 107a25f0a04SGreg Roach * @param User|null $user 108a25f0a04SGreg Roach * 109cbc1590aSGreg Roach * @return bool 110a25f0a04SGreg Roach */ 1118f53f488SRico Sonntag public static function isEditor(Tree $tree, User $user = null): bool 112c1010edaSGreg Roach { 113cb923727SGreg Roach $user = $user ?? self::user(); 114a25f0a04SGreg Roach 115cb923727SGreg Roach return self::isModerator($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'edit'; 116a25f0a04SGreg Roach } 117a25f0a04SGreg Roach 118a25f0a04SGreg Roach /** 1194b9ff166SGreg Roach * Is the specified/current user a member of a tree? 120a25f0a04SGreg Roach * 12184caa210SGreg Roach * @param Tree $tree 122a25f0a04SGreg Roach * @param User|null $user 123a25f0a04SGreg Roach * 124cbc1590aSGreg Roach * @return bool 125a25f0a04SGreg Roach */ 1268f53f488SRico Sonntag public static function isMember(Tree $tree, User $user = null): bool 127c1010edaSGreg Roach { 128cb923727SGreg Roach $user = $user ?? self::user(); 129a25f0a04SGreg Roach 130cb923727SGreg Roach return self::isEditor($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'access'; 131a25f0a04SGreg Roach } 132a25f0a04SGreg Roach 133a25f0a04SGreg Roach /** 1344b9ff166SGreg Roach * What is the specified/current user's access level within a tree? 1354b9ff166SGreg Roach * 1364b9ff166SGreg Roach * @param Tree $tree 1374b9ff166SGreg Roach * @param User|null $user 1384b9ff166SGreg Roach * 139cbc1590aSGreg Roach * @return int 1404b9ff166SGreg Roach */ 141c1010edaSGreg Roach public static function accessLevel(Tree $tree, User $user = null) 142c1010edaSGreg Roach { 143cb923727SGreg Roach $user = $user ?? self::user(); 1444b9ff166SGreg Roach 1454b9ff166SGreg Roach if (self::isManager($tree, $user)) { 1464b9ff166SGreg Roach return self::PRIV_NONE; 1474b9ff166SGreg Roach } 148b2ce94c6SRico Sonntag 149b2ce94c6SRico Sonntag if (self::isMember($tree, $user)) { 150b2ce94c6SRico Sonntag return self::PRIV_USER; 151b2ce94c6SRico Sonntag } 152b2ce94c6SRico Sonntag 153b2ce94c6SRico Sonntag return self::PRIV_PRIVATE; 1544b9ff166SGreg Roach } 1554b9ff166SGreg Roach 1564b9ff166SGreg Roach /** 157a25f0a04SGreg Roach * The ID of the authenticated user, from the current session. 158a25f0a04SGreg Roach * 159c3ffc4cbSGreg Roach * @return int|null 160a25f0a04SGreg Roach */ 161c1010edaSGreg Roach public static function id() 162c1010edaSGreg Roach { 16331bc7874SGreg Roach return Session::get('wt_user'); 164a25f0a04SGreg Roach } 165a25f0a04SGreg Roach 166a25f0a04SGreg Roach /** 167a25f0a04SGreg Roach * The authenticated user, from the current session. 168a25f0a04SGreg Roach * 169a25f0a04SGreg Roach * @return User 170a25f0a04SGreg Roach */ 171c1010edaSGreg Roach public static function user() 172c1010edaSGreg Roach { 1738b67c11aSGreg Roach return User::find(self::id()) ?? User::visitor(); 174a25f0a04SGreg Roach } 175a25f0a04SGreg Roach 176a25f0a04SGreg Roach /** 177a25f0a04SGreg Roach * Login directly as an explicit user - for masquerading. 178a25f0a04SGreg Roach * 179a25f0a04SGreg Roach * @param User $user 180cb923727SGreg Roach * 181cb923727SGreg Roach * @return void 182a25f0a04SGreg Roach */ 183c1010edaSGreg Roach public static function login(User $user) 184c1010edaSGreg Roach { 185e988f922SGreg Roach Session::regenerate(false); 186895230eeSGreg Roach Session::put('wt_user', $user->id()); 187a25f0a04SGreg Roach } 188a25f0a04SGreg Roach 189a25f0a04SGreg Roach /** 190a25f0a04SGreg Roach * End the session for the current user. 191cb923727SGreg Roach * 192cb923727SGreg Roach * @return void 193a25f0a04SGreg Roach */ 194c1010edaSGreg Roach public static function logout() 195c1010edaSGreg Roach { 19631bc7874SGreg Roach Session::regenerate(true); 197a25f0a04SGreg Roach } 198e539f5c6SGreg Roach 199e539f5c6SGreg Roach /** 200*9867b2f0SGreg Roach * @param ModuleInterface $module 201*9867b2f0SGreg Roach * @param string $component 202*9867b2f0SGreg Roach * @param Tree $tree 203*9867b2f0SGreg Roach * @param User $user 204*9867b2f0SGreg Roach * 205*9867b2f0SGreg Roach * @return void 206*9867b2f0SGreg Roach */ 207*9867b2f0SGreg Roach public static function checkComponentAccess(ModuleInterface $module, string $component, Tree $tree, User $user) 208*9867b2f0SGreg Roach { 209*9867b2f0SGreg Roach if ($module->accessLevel($tree, $component) < Auth::accessLevel($tree, $user)) { 210*9867b2f0SGreg Roach throw new AccessDeniedHttpException(''); 211*9867b2f0SGreg Roach } 212*9867b2f0SGreg Roach } 213*9867b2f0SGreg Roach 214*9867b2f0SGreg Roach /** 215e539f5c6SGreg Roach * @param Family|null $family 216e539f5c6SGreg Roach * @param bool|null $edit 217e539f5c6SGreg Roach * 218e539f5c6SGreg Roach * @return void 219e539f5c6SGreg Roach * @throws FamilyNotFoundException 220e539f5c6SGreg Roach * @throws FamilyAccessDeniedException 221e539f5c6SGreg Roach */ 222e539f5c6SGreg Roach public static function checkFamilyAccess(Family $family = null, $edit = false) 223e539f5c6SGreg Roach { 224e539f5c6SGreg Roach if ($family === null) { 225e539f5c6SGreg Roach throw new FamilyNotFoundException(); 226e539f5c6SGreg Roach } 227e539f5c6SGreg Roach 228e539f5c6SGreg Roach if (!$family->canShow() || $edit && (!$family->canEdit() || $family->isPendingDeletion())) { 229e539f5c6SGreg Roach throw new FamilyAccessDeniedException(); 230e539f5c6SGreg Roach } 231e539f5c6SGreg Roach } 232e539f5c6SGreg Roach 233e539f5c6SGreg Roach /** 234e539f5c6SGreg Roach * @param Individual|null $individual 235e539f5c6SGreg Roach * @param bool|null $edit 236e539f5c6SGreg Roach * 237e539f5c6SGreg Roach * @return void 238e539f5c6SGreg Roach * @throws IndividualNotFoundException 239e539f5c6SGreg Roach * @throws IndividualAccessDeniedException 240e539f5c6SGreg Roach */ 241e539f5c6SGreg Roach public static function checkIndividualAccess(Individual $individual = null, $edit = false) 242e539f5c6SGreg Roach { 243e539f5c6SGreg Roach if ($individual === null) { 244e539f5c6SGreg Roach throw new IndividualNotFoundException(); 245e539f5c6SGreg Roach } 246e539f5c6SGreg Roach 247e539f5c6SGreg Roach if (!$individual->canShow() || $edit && (!$individual->canEdit() || $individual->isPendingDeletion())) { 248e539f5c6SGreg Roach throw new IndividualAccessDeniedException(); 249e539f5c6SGreg Roach } 250e539f5c6SGreg Roach } 251e539f5c6SGreg Roach 252e539f5c6SGreg Roach /** 253e539f5c6SGreg Roach * @param Media|null $media 254e539f5c6SGreg Roach * @param bool|null $edit 255e539f5c6SGreg Roach * 256e539f5c6SGreg Roach * @return void 257e539f5c6SGreg Roach * @throws MediaNotFoundException 258e539f5c6SGreg Roach * @throws MediaAccessDeniedException 259e539f5c6SGreg Roach */ 260e539f5c6SGreg Roach public static function checkMediaAccess(Media $media = null, $edit = false) 261e539f5c6SGreg Roach { 262e539f5c6SGreg Roach if ($media === null) { 263e539f5c6SGreg Roach throw new MediaNotFoundException(); 264e539f5c6SGreg Roach } 265e539f5c6SGreg Roach 266e539f5c6SGreg Roach if (!$media->canShow() || $edit && (!$media->canEdit() || $media->isPendingDeletion())) { 267e539f5c6SGreg Roach throw new MediaAccessDeniedException(); 268e539f5c6SGreg Roach } 269e539f5c6SGreg Roach } 270e539f5c6SGreg Roach 271e539f5c6SGreg Roach /** 272e539f5c6SGreg Roach * @param Note|null $note 273e539f5c6SGreg Roach * @param bool|null $edit 274e539f5c6SGreg Roach * 275e539f5c6SGreg Roach * @return void 276e539f5c6SGreg Roach * @throws NoteNotFoundException 277e539f5c6SGreg Roach * @throws NoteAccessDeniedException 278e539f5c6SGreg Roach */ 279e539f5c6SGreg Roach public static function checkNoteAccess(Note $note = null, $edit = false) 280e539f5c6SGreg Roach { 281e539f5c6SGreg Roach if ($note === null) { 282e539f5c6SGreg Roach throw new NoteNotFoundException(); 283e539f5c6SGreg Roach } 284e539f5c6SGreg Roach 285e539f5c6SGreg Roach if (!$note->canShow() || $edit && (!$note->canEdit() || $note->isPendingDeletion())) { 286e539f5c6SGreg Roach throw new NoteAccessDeniedException(); 287e539f5c6SGreg Roach } 288e539f5c6SGreg Roach } 289e539f5c6SGreg Roach 290e539f5c6SGreg Roach /** 291e539f5c6SGreg Roach * @param GedcomRecord|null $record 292e539f5c6SGreg Roach * @param bool|null $edit 293e539f5c6SGreg Roach * 294e539f5c6SGreg Roach * @return void 295e539f5c6SGreg Roach * @throws RecordNotFoundException 296e539f5c6SGreg Roach * @throws RecordAccessDeniedException 297e539f5c6SGreg Roach */ 298e539f5c6SGreg Roach public static function checkRecordAccess(GedcomRecord $record = null, $edit = false) 299e539f5c6SGreg Roach { 300e539f5c6SGreg Roach if ($record === null) { 301e539f5c6SGreg Roach throw new RecordNotFoundException(); 302e539f5c6SGreg Roach } 303e539f5c6SGreg Roach 304e539f5c6SGreg Roach if (!$record->canShow() || $edit && (!$record->canEdit() || $record->isPendingDeletion())) { 305e539f5c6SGreg Roach throw new RecordAccessDeniedException(); 306e539f5c6SGreg Roach } 307e539f5c6SGreg Roach } 308e539f5c6SGreg Roach 309e539f5c6SGreg Roach /** 310e539f5c6SGreg Roach * @param Repository|null $repository 311e539f5c6SGreg Roach * @param bool|null $edit 312e539f5c6SGreg Roach * 313e539f5c6SGreg Roach * @return void 314e539f5c6SGreg Roach * @throws RepositoryNotFoundException 315e539f5c6SGreg Roach * @throws RepositoryAccessDeniedException 316e539f5c6SGreg Roach */ 317e539f5c6SGreg Roach public static function checkRepositoryAccess(Repository $repository = null, $edit = false) 318e539f5c6SGreg Roach { 319e539f5c6SGreg Roach if ($repository === null) { 320e539f5c6SGreg Roach throw new RepositoryNotFoundException(); 321e539f5c6SGreg Roach } 322e539f5c6SGreg Roach 323e539f5c6SGreg Roach if (!$repository->canShow() || $edit && (!$repository->canEdit() || $repository->isPendingDeletion())) { 324e539f5c6SGreg Roach throw new RepositoryAccessDeniedException(); 325e539f5c6SGreg Roach } 326e539f5c6SGreg Roach } 327e539f5c6SGreg Roach 328e539f5c6SGreg Roach /** 329e539f5c6SGreg Roach * @param Source|null $source 330e539f5c6SGreg Roach * @param bool|null $edit 331e539f5c6SGreg Roach * 332e539f5c6SGreg Roach * @return void 333e539f5c6SGreg Roach * @throws SourceNotFoundException 334e539f5c6SGreg Roach * @throws SourceAccessDeniedException 335e539f5c6SGreg Roach */ 336e539f5c6SGreg Roach public static function checkSourceAccess(Source $source = null, $edit = false) 337e539f5c6SGreg Roach { 338e539f5c6SGreg Roach if ($source === null) { 339e539f5c6SGreg Roach throw new SourceNotFoundException(); 340e539f5c6SGreg Roach } 341e539f5c6SGreg Roach 342e539f5c6SGreg Roach if (!$source->canShow() || $edit && (!$source->canEdit() || $source->isPendingDeletion())) { 343e539f5c6SGreg Roach throw new SourceAccessDeniedException(); 344e539f5c6SGreg Roach } 345e539f5c6SGreg Roach } 346a25f0a04SGreg Roach} 347