1a25f0a04SGreg Roach<?php 23976b470SGreg Roach 3a25f0a04SGreg Roach/** 4a25f0a04SGreg Roach * webtrees: online genealogy 58fcd0d32SGreg Roach * Copyright (C) 2019 webtrees development team 6a25f0a04SGreg Roach * This program is free software: you can redistribute it and/or modify 7a25f0a04SGreg Roach * it under the terms of the GNU General Public License as published by 8a25f0a04SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9a25f0a04SGreg Roach * (at your option) any later version. 10a25f0a04SGreg Roach * This program is distributed in the hope that it will be useful, 11a25f0a04SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12a25f0a04SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13a25f0a04SGreg Roach * GNU General Public License for more details. 14a25f0a04SGreg Roach * You should have received a copy of the GNU General Public License 15a25f0a04SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 16a25f0a04SGreg Roach */ 17fcfa147eSGreg Roach 18e7f56f2aSGreg Roachdeclare(strict_types=1); 19e7f56f2aSGreg Roach 2076692c8bSGreg Roachnamespace Fisharebest\Webtrees; 21a25f0a04SGreg Roach 22e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 23e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException; 24e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyNotFoundException; 25e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException; 26e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualNotFoundException; 27e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException; 28e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaNotFoundException; 29e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException; 30e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteNotFoundException; 31e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException; 32e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordNotFoundException; 33e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException; 34e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException; 35e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException; 36e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceNotFoundException; 379867b2f0SGreg Roachuse Fisharebest\Webtrees\Module\ModuleInterface; 38e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 399867b2f0SGreg Roachuse Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; 4079529c87SGreg Roach 41a25f0a04SGreg Roach/** 4276692c8bSGreg Roach * Authentication. 43a25f0a04SGreg Roach */ 44c1010edaSGreg Roachclass Auth 45c1010edaSGreg Roach{ 464b9ff166SGreg Roach // Privacy constants 4716d6367aSGreg Roach public const PRIV_PRIVATE = 2; // Allows visitors to view the item 4816d6367aSGreg Roach public const PRIV_USER = 1; // Allows members to access the item 4916d6367aSGreg Roach public const PRIV_NONE = 0; // Allows managers to access the item 5016d6367aSGreg Roach public const PRIV_HIDE = -1; // Hide the item to all users 514b9ff166SGreg Roach 52a25f0a04SGreg Roach /** 53a25f0a04SGreg Roach * Are we currently logged in? 54a25f0a04SGreg Roach * 55cbc1590aSGreg Roach * @return bool 56a25f0a04SGreg Roach */ 578f53f488SRico Sonntag public static function check(): bool 58c1010edaSGreg Roach { 594b9ff166SGreg Roach return self::id() !== null; 60a25f0a04SGreg Roach } 61a25f0a04SGreg Roach 62a25f0a04SGreg Roach /** 63a25f0a04SGreg Roach * Is the specified/current user an administrator? 64a25f0a04SGreg Roach * 65e5a6b4d4SGreg Roach * @param UserInterface|null $user 66a25f0a04SGreg Roach * 67cbc1590aSGreg Roach * @return bool 68a25f0a04SGreg Roach */ 69e5a6b4d4SGreg Roach public static function isAdmin(UserInterface $user = null): bool 70c1010edaSGreg Roach { 71cb923727SGreg Roach $user = $user ?? self::user(); 72a25f0a04SGreg Roach 73*7c4add84SGreg Roach return $user->getPreference(User::PREF_IS_ADMINISTRATOR) === '1'; 74a25f0a04SGreg Roach } 75a25f0a04SGreg Roach 76a25f0a04SGreg Roach /** 774b9ff166SGreg Roach * Is the specified/current user a manager of a tree? 78a25f0a04SGreg Roach * 7984caa210SGreg Roach * @param Tree $tree 80e5a6b4d4SGreg Roach * @param UserInterface|null $user 81a25f0a04SGreg Roach * 82cbc1590aSGreg Roach * @return bool 83a25f0a04SGreg Roach */ 84e5a6b4d4SGreg Roach public static function isManager(Tree $tree, UserInterface $user = null): bool 85c1010edaSGreg Roach { 86cb923727SGreg Roach $user = $user ?? self::user(); 87a25f0a04SGreg Roach 88*7c4add84SGreg Roach return self::isAdmin($user) || $tree->getUserPreference($user, User::PREF_TREE_ROLE) === User::ROLE_MANAGER; 89a25f0a04SGreg Roach } 90a25f0a04SGreg Roach 91a25f0a04SGreg Roach /** 924b9ff166SGreg Roach * Is the specified/current user a moderator of a tree? 93a25f0a04SGreg Roach * 9484caa210SGreg Roach * @param Tree $tree 95e5a6b4d4SGreg Roach * @param UserInterface|null $user 96a25f0a04SGreg Roach * 97cbc1590aSGreg Roach * @return bool 98a25f0a04SGreg Roach */ 99e5a6b4d4SGreg Roach public static function isModerator(Tree $tree, UserInterface $user = null): bool 100c1010edaSGreg Roach { 101cb923727SGreg Roach $user = $user ?? self::user(); 102a25f0a04SGreg Roach 103*7c4add84SGreg Roach return self::isManager($tree, $user) || $tree->getUserPreference($user, User::PREF_TREE_ROLE) === User::ROLE_MODERATOR; 104a25f0a04SGreg Roach } 105a25f0a04SGreg Roach 106a25f0a04SGreg Roach /** 1074b9ff166SGreg Roach * Is the specified/current user an editor of a tree? 108a25f0a04SGreg Roach * 10984caa210SGreg Roach * @param Tree $tree 110e5a6b4d4SGreg Roach * @param UserInterface|null $user 111a25f0a04SGreg Roach * 112cbc1590aSGreg Roach * @return bool 113a25f0a04SGreg Roach */ 114e5a6b4d4SGreg Roach public static function isEditor(Tree $tree, UserInterface $user = null): bool 115c1010edaSGreg Roach { 116cb923727SGreg Roach $user = $user ?? self::user(); 117a25f0a04SGreg Roach 118*7c4add84SGreg Roach return self::isModerator($tree, $user) || $tree->getUserPreference($user, User::PREF_TREE_ROLE) === 'edit'; 119a25f0a04SGreg Roach } 120a25f0a04SGreg Roach 121a25f0a04SGreg Roach /** 1224b9ff166SGreg Roach * Is the specified/current user a member of a tree? 123a25f0a04SGreg Roach * 12484caa210SGreg Roach * @param Tree $tree 125e5a6b4d4SGreg Roach * @param UserInterface|null $user 126a25f0a04SGreg Roach * 127cbc1590aSGreg Roach * @return bool 128a25f0a04SGreg Roach */ 129e5a6b4d4SGreg Roach public static function isMember(Tree $tree, UserInterface $user = null): bool 130c1010edaSGreg Roach { 131cb923727SGreg Roach $user = $user ?? self::user(); 132a25f0a04SGreg Roach 133*7c4add84SGreg Roach return self::isEditor($tree, $user) || $tree->getUserPreference($user, User::PREF_TREE_ROLE) === 'access'; 134a25f0a04SGreg Roach } 135a25f0a04SGreg Roach 136a25f0a04SGreg Roach /** 1374b9ff166SGreg Roach * What is the specified/current user's access level within a tree? 1384b9ff166SGreg Roach * 1394b9ff166SGreg Roach * @param Tree $tree 140e5a6b4d4SGreg Roach * @param UserInterface|null $user 1414b9ff166SGreg Roach * 142cbc1590aSGreg Roach * @return int 1434b9ff166SGreg Roach */ 144e364afe4SGreg Roach public static function accessLevel(Tree $tree, UserInterface $user = null): int 145c1010edaSGreg Roach { 146cb923727SGreg Roach $user = $user ?? self::user(); 1474b9ff166SGreg Roach 1484b9ff166SGreg Roach if (self::isManager($tree, $user)) { 1494b9ff166SGreg Roach return self::PRIV_NONE; 1504b9ff166SGreg Roach } 151b2ce94c6SRico Sonntag 152b2ce94c6SRico Sonntag if (self::isMember($tree, $user)) { 153b2ce94c6SRico Sonntag return self::PRIV_USER; 154b2ce94c6SRico Sonntag } 155b2ce94c6SRico Sonntag 156b2ce94c6SRico Sonntag return self::PRIV_PRIVATE; 1574b9ff166SGreg Roach } 1584b9ff166SGreg Roach 1594b9ff166SGreg Roach /** 160a25f0a04SGreg Roach * The ID of the authenticated user, from the current session. 161a25f0a04SGreg Roach * 162c3ffc4cbSGreg Roach * @return int|null 163a25f0a04SGreg Roach */ 164e364afe4SGreg Roach public static function id(): ?int 165c1010edaSGreg Roach { 166adbb839bSGreg Roach $id = Session::get('wt_user'); 167adbb839bSGreg Roach 168adbb839bSGreg Roach if ($id !== null) { 169adbb839bSGreg Roach // In webtrees 1.x, the ID may have been a string. 170adbb839bSGreg Roach $id = (int) $id; 171adbb839bSGreg Roach } 172adbb839bSGreg Roach 173adbb839bSGreg Roach return $id; 174a25f0a04SGreg Roach } 175a25f0a04SGreg Roach 176a25f0a04SGreg Roach /** 177a25f0a04SGreg Roach * The authenticated user, from the current session. 178a25f0a04SGreg Roach * 179e5a6b4d4SGreg Roach * @return UserInterface 180a25f0a04SGreg Roach */ 181e5a6b4d4SGreg Roach public static function user(): UserInterface 182c1010edaSGreg Roach { 18315674e31SGreg Roach return app(UserService::class)->find(self::id()) ?? new GuestUser(); 184a25f0a04SGreg Roach } 185a25f0a04SGreg Roach 186a25f0a04SGreg Roach /** 187a25f0a04SGreg Roach * Login directly as an explicit user - for masquerading. 188a25f0a04SGreg Roach * 189e5a6b4d4SGreg Roach * @param UserInterface $user 190cb923727SGreg Roach * 191cb923727SGreg Roach * @return void 192a25f0a04SGreg Roach */ 193e364afe4SGreg Roach public static function login(UserInterface $user): void 194c1010edaSGreg Roach { 195e988f922SGreg Roach Session::regenerate(false); 196895230eeSGreg Roach Session::put('wt_user', $user->id()); 197a25f0a04SGreg Roach } 198a25f0a04SGreg Roach 199a25f0a04SGreg Roach /** 200a25f0a04SGreg Roach * End the session for the current user. 201cb923727SGreg Roach * 202cb923727SGreg Roach * @return void 203a25f0a04SGreg Roach */ 204e364afe4SGreg Roach public static function logout(): void 205c1010edaSGreg Roach { 20631bc7874SGreg Roach Session::regenerate(true); 207a25f0a04SGreg Roach } 208e539f5c6SGreg Roach 209e539f5c6SGreg Roach /** 2109867b2f0SGreg Roach * @param ModuleInterface $module 2119867b2f0SGreg Roach * @param string $component 2129867b2f0SGreg Roach * @param Tree $tree 213e5a6b4d4SGreg Roach * @param UserInterface $user 2149867b2f0SGreg Roach * 2159867b2f0SGreg Roach * @return void 2169867b2f0SGreg Roach */ 217e364afe4SGreg Roach public static function checkComponentAccess(ModuleInterface $module, string $component, Tree $tree, UserInterface $user): void 2189867b2f0SGreg Roach { 219e364afe4SGreg Roach if ($module->accessLevel($tree, $component) < self::accessLevel($tree, $user)) { 22096d794e7SGreg Roach throw new AccessDeniedHttpException('Access denied'); 2219867b2f0SGreg Roach } 2229867b2f0SGreg Roach } 2239867b2f0SGreg Roach 2249867b2f0SGreg Roach /** 225e539f5c6SGreg Roach * @param Family|null $family 226e539f5c6SGreg Roach * @param bool|null $edit 227e539f5c6SGreg Roach * 228ddeb3354SGreg Roach * @return Family 229e539f5c6SGreg Roach * @throws FamilyNotFoundException 230e539f5c6SGreg Roach * @throws FamilyAccessDeniedException 231e539f5c6SGreg Roach */ 232ddeb3354SGreg Roach public static function checkFamilyAccess(Family $family = null, $edit = false): Family 233e539f5c6SGreg Roach { 234e539f5c6SGreg Roach if ($family === null) { 235e539f5c6SGreg Roach throw new FamilyNotFoundException(); 236e539f5c6SGreg Roach } 237e539f5c6SGreg Roach 2381450f098SGreg Roach if (!$family->canShow()) { 2391450f098SGreg Roach throw new FamilyAccessDeniedException(); 2401450f098SGreg Roach } 2411450f098SGreg Roach 2421450f098SGreg Roach if ($edit && !$family->canEdit()) { 243e539f5c6SGreg Roach throw new FamilyAccessDeniedException(); 244e539f5c6SGreg Roach } 245ddeb3354SGreg Roach 246ddeb3354SGreg Roach return $family; 247e539f5c6SGreg Roach } 248e539f5c6SGreg Roach 249e539f5c6SGreg Roach /** 250e539f5c6SGreg Roach * @param Individual|null $individual 251e539f5c6SGreg Roach * @param bool|null $edit 252e539f5c6SGreg Roach * 253ddeb3354SGreg Roach * @return Individual 254e539f5c6SGreg Roach * @throws IndividualNotFoundException 255e539f5c6SGreg Roach * @throws IndividualAccessDeniedException 256e539f5c6SGreg Roach */ 257ddeb3354SGreg Roach public static function checkIndividualAccess(Individual $individual = null, $edit = false): Individual 258e539f5c6SGreg Roach { 259e539f5c6SGreg Roach if ($individual === null) { 260e539f5c6SGreg Roach throw new IndividualNotFoundException(); 261e539f5c6SGreg Roach } 262e539f5c6SGreg Roach 2631450f098SGreg Roach if (!$individual->canShow()) { 2641450f098SGreg Roach throw new IndividualAccessDeniedException(); 2651450f098SGreg Roach } 2661450f098SGreg Roach 2671450f098SGreg Roach if ($edit && !$individual->canEdit()) { 268e539f5c6SGreg Roach throw new IndividualAccessDeniedException(); 269e539f5c6SGreg Roach } 270ddeb3354SGreg Roach 271ddeb3354SGreg Roach return $individual; 272e539f5c6SGreg Roach } 273e539f5c6SGreg Roach 274e539f5c6SGreg Roach /** 275e539f5c6SGreg Roach * @param Media|null $media 276e539f5c6SGreg Roach * @param bool|null $edit 277e539f5c6SGreg Roach * 278ddeb3354SGreg Roach * @return Media 279e539f5c6SGreg Roach * @throws MediaNotFoundException 280e539f5c6SGreg Roach * @throws MediaAccessDeniedException 281e539f5c6SGreg Roach */ 282ddeb3354SGreg Roach public static function checkMediaAccess(Media $media = null, $edit = false): Media 283e539f5c6SGreg Roach { 284e539f5c6SGreg Roach if ($media === null) { 285e539f5c6SGreg Roach throw new MediaNotFoundException(); 286e539f5c6SGreg Roach } 287e539f5c6SGreg Roach 2881450f098SGreg Roach if (!$media->canShow()) { 2891450f098SGreg Roach throw new MediaAccessDeniedException(); 2901450f098SGreg Roach } 2911450f098SGreg Roach 2921450f098SGreg Roach if ($edit && !$media->canEdit()) { 293e539f5c6SGreg Roach throw new MediaAccessDeniedException(); 294e539f5c6SGreg Roach } 295ddeb3354SGreg Roach 296ddeb3354SGreg Roach return $media; 297e539f5c6SGreg Roach } 298e539f5c6SGreg Roach 299e539f5c6SGreg Roach /** 300e539f5c6SGreg Roach * @param Note|null $note 301e539f5c6SGreg Roach * @param bool|null $edit 302e539f5c6SGreg Roach * 303ddeb3354SGreg Roach * @return Note 304e539f5c6SGreg Roach * @throws NoteNotFoundException 305e539f5c6SGreg Roach * @throws NoteAccessDeniedException 306e539f5c6SGreg Roach */ 307ddeb3354SGreg Roach public static function checkNoteAccess(Note $note = null, $edit = false): Note 308e539f5c6SGreg Roach { 309e539f5c6SGreg Roach if ($note === null) { 310e539f5c6SGreg Roach throw new NoteNotFoundException(); 311e539f5c6SGreg Roach } 312e539f5c6SGreg Roach 3131450f098SGreg Roach if (!$note->canShow()) { 3141450f098SGreg Roach throw new NoteAccessDeniedException(); 3151450f098SGreg Roach } 3161450f098SGreg Roach 3171450f098SGreg Roach if ($edit && !$note->canEdit()) { 318e539f5c6SGreg Roach throw new NoteAccessDeniedException(); 319e539f5c6SGreg Roach } 320ddeb3354SGreg Roach 321ddeb3354SGreg Roach return $note; 322e539f5c6SGreg Roach } 323e539f5c6SGreg Roach 324e539f5c6SGreg Roach /** 325e539f5c6SGreg Roach * @param GedcomRecord|null $record 326e539f5c6SGreg Roach * @param bool|null $edit 327e539f5c6SGreg Roach * 328ddeb3354SGreg Roach * @return GedcomRecord 329e539f5c6SGreg Roach * @throws RecordNotFoundException 330e539f5c6SGreg Roach * @throws RecordAccessDeniedException 331e539f5c6SGreg Roach */ 332ddeb3354SGreg Roach public static function checkRecordAccess(GedcomRecord $record = null, $edit = false): GedcomRecord 333e539f5c6SGreg Roach { 334e539f5c6SGreg Roach if ($record === null) { 335e539f5c6SGreg Roach throw new RecordNotFoundException(); 336e539f5c6SGreg Roach } 337e539f5c6SGreg Roach 3381450f098SGreg Roach if (!$record->canShow()) { 3391450f098SGreg Roach throw new RecordAccessDeniedException(); 3401450f098SGreg Roach } 3411450f098SGreg Roach 3421450f098SGreg Roach if ($edit && !$record->canEdit()) { 343e539f5c6SGreg Roach throw new RecordAccessDeniedException(); 344e539f5c6SGreg Roach } 345ddeb3354SGreg Roach 346ddeb3354SGreg Roach return $record; 347e539f5c6SGreg Roach } 348e539f5c6SGreg Roach 349e539f5c6SGreg Roach /** 350e539f5c6SGreg Roach * @param Repository|null $repository 351e539f5c6SGreg Roach * @param bool|null $edit 352e539f5c6SGreg Roach * 353ddeb3354SGreg Roach * @return Repository 354e539f5c6SGreg Roach * @throws RepositoryNotFoundException 355e539f5c6SGreg Roach * @throws RepositoryAccessDeniedException 356e539f5c6SGreg Roach */ 357ddeb3354SGreg Roach public static function checkRepositoryAccess(Repository $repository = null, $edit = false): Repository 358e539f5c6SGreg Roach { 359e539f5c6SGreg Roach if ($repository === null) { 360e539f5c6SGreg Roach throw new RepositoryNotFoundException(); 361e539f5c6SGreg Roach } 362e539f5c6SGreg Roach 3631450f098SGreg Roach if (!$repository->canShow()) { 3641450f098SGreg Roach throw new RepositoryAccessDeniedException(); 3651450f098SGreg Roach } 3661450f098SGreg Roach 3671450f098SGreg Roach if ($edit && !$repository->canEdit()) { 368e539f5c6SGreg Roach throw new RepositoryAccessDeniedException(); 369e539f5c6SGreg Roach } 370ddeb3354SGreg Roach 371ddeb3354SGreg Roach return $repository; 372e539f5c6SGreg Roach } 373e539f5c6SGreg Roach 374e539f5c6SGreg Roach /** 375e539f5c6SGreg Roach * @param Source|null $source 376e539f5c6SGreg Roach * @param bool|null $edit 377e539f5c6SGreg Roach * 378ddeb3354SGreg Roach * @return Source 379e539f5c6SGreg Roach * @throws SourceNotFoundException 380e539f5c6SGreg Roach * @throws SourceAccessDeniedException 381e539f5c6SGreg Roach */ 382ddeb3354SGreg Roach public static function checkSourceAccess(Source $source = null, $edit = false): Source 383e539f5c6SGreg Roach { 384e539f5c6SGreg Roach if ($source === null) { 385e539f5c6SGreg Roach throw new SourceNotFoundException(); 386e539f5c6SGreg Roach } 387e539f5c6SGreg Roach 3881450f098SGreg Roach if (!$source->canShow()) { 3891450f098SGreg Roach throw new SourceAccessDeniedException(); 3901450f098SGreg Roach } 3911450f098SGreg Roach 3921450f098SGreg Roach if ($edit && !$source->canEdit()) { 393e539f5c6SGreg Roach throw new SourceAccessDeniedException(); 394e539f5c6SGreg Roach } 395ddeb3354SGreg Roach 396ddeb3354SGreg Roach return $source; 397e539f5c6SGreg Roach } 398a25f0a04SGreg Roach} 399