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 20e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 21e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException; 22e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyNotFoundException; 23e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException; 24e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualNotFoundException; 25e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException; 26e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaNotFoundException; 27e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException; 28e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteNotFoundException; 29e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException; 30e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordNotFoundException; 31e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException; 32e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException; 33e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException; 34e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceNotFoundException; 359867b2f0SGreg Roachuse Fisharebest\Webtrees\Module\ModuleInterface; 36e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 379867b2f0SGreg Roachuse Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; 3879529c87SGreg Roach 39a25f0a04SGreg Roach/** 4076692c8bSGreg Roach * Authentication. 41a25f0a04SGreg Roach */ 42c1010edaSGreg Roachclass Auth 43c1010edaSGreg Roach{ 444b9ff166SGreg Roach // Privacy constants 4516d6367aSGreg Roach public const PRIV_PRIVATE = 2; // Allows visitors to view the item 4616d6367aSGreg Roach public const PRIV_USER = 1; // Allows members to access the item 4716d6367aSGreg Roach public const PRIV_NONE = 0; // Allows managers to access the item 4816d6367aSGreg Roach public const PRIV_HIDE = -1; // Hide the item to all users 494b9ff166SGreg Roach 50a25f0a04SGreg Roach /** 51a25f0a04SGreg Roach * Are we currently logged in? 52a25f0a04SGreg Roach * 53cbc1590aSGreg Roach * @return bool 54a25f0a04SGreg Roach */ 558f53f488SRico Sonntag public static function check(): bool 56c1010edaSGreg Roach { 574b9ff166SGreg Roach return self::id() !== null; 58a25f0a04SGreg Roach } 59a25f0a04SGreg Roach 60a25f0a04SGreg Roach /** 61a25f0a04SGreg Roach * Is the specified/current user an administrator? 62a25f0a04SGreg Roach * 63e5a6b4d4SGreg Roach * @param UserInterface|null $user 64a25f0a04SGreg Roach * 65cbc1590aSGreg Roach * @return bool 66a25f0a04SGreg Roach */ 67e5a6b4d4SGreg Roach public static function isAdmin(UserInterface $user = null): bool 68c1010edaSGreg Roach { 69cb923727SGreg Roach $user = $user ?? self::user(); 70a25f0a04SGreg Roach 71cb923727SGreg Roach return $user->getPreference('canadmin') === '1'; 72a25f0a04SGreg Roach } 73a25f0a04SGreg Roach 74a25f0a04SGreg Roach /** 754b9ff166SGreg Roach * Is the specified/current user a manager of a tree? 76a25f0a04SGreg Roach * 7784caa210SGreg Roach * @param Tree $tree 78e5a6b4d4SGreg Roach * @param UserInterface|null $user 79a25f0a04SGreg Roach * 80cbc1590aSGreg Roach * @return bool 81a25f0a04SGreg Roach */ 82e5a6b4d4SGreg Roach public static function isManager(Tree $tree, UserInterface $user = null): bool 83c1010edaSGreg Roach { 84cb923727SGreg Roach $user = $user ?? self::user(); 85a25f0a04SGreg Roach 86cb923727SGreg Roach return self::isAdmin($user) || $tree->getUserPreference($user, 'canedit') === 'admin'; 87a25f0a04SGreg Roach } 88a25f0a04SGreg Roach 89a25f0a04SGreg Roach /** 904b9ff166SGreg Roach * Is the specified/current user a moderator of a tree? 91a25f0a04SGreg Roach * 9284caa210SGreg Roach * @param Tree $tree 93e5a6b4d4SGreg Roach * @param UserInterface|null $user 94a25f0a04SGreg Roach * 95cbc1590aSGreg Roach * @return bool 96a25f0a04SGreg Roach */ 97e5a6b4d4SGreg Roach public static function isModerator(Tree $tree, UserInterface $user = null): bool 98c1010edaSGreg Roach { 99cb923727SGreg Roach $user = $user ?? self::user(); 100a25f0a04SGreg Roach 101cb923727SGreg Roach return self::isManager($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'accept'; 102a25f0a04SGreg Roach } 103a25f0a04SGreg Roach 104a25f0a04SGreg Roach /** 1054b9ff166SGreg Roach * Is the specified/current user an editor of a tree? 106a25f0a04SGreg Roach * 10784caa210SGreg Roach * @param Tree $tree 108e5a6b4d4SGreg Roach * @param UserInterface|null $user 109a25f0a04SGreg Roach * 110cbc1590aSGreg Roach * @return bool 111a25f0a04SGreg Roach */ 112e5a6b4d4SGreg Roach public static function isEditor(Tree $tree, UserInterface $user = null): bool 113c1010edaSGreg Roach { 114cb923727SGreg Roach $user = $user ?? self::user(); 115a25f0a04SGreg Roach 116cb923727SGreg Roach return self::isModerator($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'edit'; 117a25f0a04SGreg Roach } 118a25f0a04SGreg Roach 119a25f0a04SGreg Roach /** 1204b9ff166SGreg Roach * Is the specified/current user a member of a tree? 121a25f0a04SGreg Roach * 12284caa210SGreg Roach * @param Tree $tree 123e5a6b4d4SGreg Roach * @param UserInterface|null $user 124a25f0a04SGreg Roach * 125cbc1590aSGreg Roach * @return bool 126a25f0a04SGreg Roach */ 127e5a6b4d4SGreg Roach public static function isMember(Tree $tree, UserInterface $user = null): bool 128c1010edaSGreg Roach { 129cb923727SGreg Roach $user = $user ?? self::user(); 130a25f0a04SGreg Roach 131cb923727SGreg Roach return self::isEditor($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'access'; 132a25f0a04SGreg Roach } 133a25f0a04SGreg Roach 134a25f0a04SGreg Roach /** 1354b9ff166SGreg Roach * What is the specified/current user's access level within a tree? 1364b9ff166SGreg Roach * 1374b9ff166SGreg Roach * @param Tree $tree 138e5a6b4d4SGreg Roach * @param UserInterface|null $user 1394b9ff166SGreg Roach * 140cbc1590aSGreg Roach * @return int 1414b9ff166SGreg Roach */ 142e364afe4SGreg Roach public static function accessLevel(Tree $tree, UserInterface $user = null): int 143c1010edaSGreg Roach { 144cb923727SGreg Roach $user = $user ?? self::user(); 1454b9ff166SGreg Roach 1464b9ff166SGreg Roach if (self::isManager($tree, $user)) { 1474b9ff166SGreg Roach return self::PRIV_NONE; 1484b9ff166SGreg Roach } 149b2ce94c6SRico Sonntag 150b2ce94c6SRico Sonntag if (self::isMember($tree, $user)) { 151b2ce94c6SRico Sonntag return self::PRIV_USER; 152b2ce94c6SRico Sonntag } 153b2ce94c6SRico Sonntag 154b2ce94c6SRico Sonntag return self::PRIV_PRIVATE; 1554b9ff166SGreg Roach } 1564b9ff166SGreg Roach 1574b9ff166SGreg Roach /** 158a25f0a04SGreg Roach * The ID of the authenticated user, from the current session. 159a25f0a04SGreg Roach * 160c3ffc4cbSGreg Roach * @return int|null 161a25f0a04SGreg Roach */ 162e364afe4SGreg Roach public static function id(): ?int 163c1010edaSGreg Roach { 164adbb839bSGreg Roach $id = Session::get('wt_user'); 165adbb839bSGreg Roach 166adbb839bSGreg Roach if ($id !== null) { 167adbb839bSGreg Roach // In webtrees 1.x, the ID may have been a string. 168adbb839bSGreg Roach $id = (int) $id; 169adbb839bSGreg Roach } 170adbb839bSGreg Roach 171adbb839bSGreg Roach return $id; 172a25f0a04SGreg Roach } 173a25f0a04SGreg Roach 174a25f0a04SGreg Roach /** 175a25f0a04SGreg Roach * The authenticated user, from the current session. 176a25f0a04SGreg Roach * 177e5a6b4d4SGreg Roach * @return UserInterface 178a25f0a04SGreg Roach */ 179e5a6b4d4SGreg Roach public static function user(): UserInterface 180c1010edaSGreg Roach { 181e5a6b4d4SGreg Roach return (new UserService())->find(self::id()) ?? new GuestUser(); 182a25f0a04SGreg Roach } 183a25f0a04SGreg Roach 184a25f0a04SGreg Roach /** 185a25f0a04SGreg Roach * Login directly as an explicit user - for masquerading. 186a25f0a04SGreg Roach * 187e5a6b4d4SGreg Roach * @param UserInterface $user 188cb923727SGreg Roach * 189cb923727SGreg Roach * @return void 190a25f0a04SGreg Roach */ 191e364afe4SGreg Roach public static function login(UserInterface $user): void 192c1010edaSGreg Roach { 193e988f922SGreg Roach Session::regenerate(false); 194895230eeSGreg Roach Session::put('wt_user', $user->id()); 195a25f0a04SGreg Roach } 196a25f0a04SGreg Roach 197a25f0a04SGreg Roach /** 198a25f0a04SGreg Roach * End the session for the current user. 199cb923727SGreg Roach * 200cb923727SGreg Roach * @return void 201a25f0a04SGreg Roach */ 202e364afe4SGreg Roach public static function logout(): void 203c1010edaSGreg Roach { 20431bc7874SGreg Roach Session::regenerate(true); 205a25f0a04SGreg Roach } 206e539f5c6SGreg Roach 207e539f5c6SGreg Roach /** 2089867b2f0SGreg Roach * @param ModuleInterface $module 2099867b2f0SGreg Roach * @param string $component 2109867b2f0SGreg Roach * @param Tree $tree 211e5a6b4d4SGreg Roach * @param UserInterface $user 2129867b2f0SGreg Roach * 2139867b2f0SGreg Roach * @return void 2149867b2f0SGreg Roach */ 215e364afe4SGreg Roach public static function checkComponentAccess(ModuleInterface $module, string $component, Tree $tree, UserInterface $user): void 2169867b2f0SGreg Roach { 217e364afe4SGreg Roach if ($module->accessLevel($tree, $component) < self::accessLevel($tree, $user)) { 218*96d794e7SGreg Roach throw new AccessDeniedHttpException('Access denied'); 2199867b2f0SGreg Roach } 2209867b2f0SGreg Roach } 2219867b2f0SGreg Roach 2229867b2f0SGreg Roach /** 223e539f5c6SGreg Roach * @param Family|null $family 224e539f5c6SGreg Roach * @param bool|null $edit 225e539f5c6SGreg Roach * 226e539f5c6SGreg Roach * @return void 227e539f5c6SGreg Roach * @throws FamilyNotFoundException 228e539f5c6SGreg Roach * @throws FamilyAccessDeniedException 229e539f5c6SGreg Roach */ 230e364afe4SGreg Roach public static function checkFamilyAccess(Family $family = null, $edit = false): void 231e539f5c6SGreg Roach { 232e539f5c6SGreg Roach if ($family === null) { 233e539f5c6SGreg Roach throw new FamilyNotFoundException(); 234e539f5c6SGreg Roach } 235e539f5c6SGreg Roach 2361450f098SGreg Roach if (!$family->canShow()) { 2371450f098SGreg Roach throw new FamilyAccessDeniedException(); 2381450f098SGreg Roach } 2391450f098SGreg Roach 2401450f098SGreg Roach if ($edit && !$family->canEdit()) { 241e539f5c6SGreg Roach throw new FamilyAccessDeniedException(); 242e539f5c6SGreg Roach } 243e539f5c6SGreg Roach } 244e539f5c6SGreg Roach 245e539f5c6SGreg Roach /** 246e539f5c6SGreg Roach * @param Individual|null $individual 247e539f5c6SGreg Roach * @param bool|null $edit 248e539f5c6SGreg Roach * 249e539f5c6SGreg Roach * @return void 250e539f5c6SGreg Roach * @throws IndividualNotFoundException 251e539f5c6SGreg Roach * @throws IndividualAccessDeniedException 252e539f5c6SGreg Roach */ 253e364afe4SGreg Roach public static function checkIndividualAccess(Individual $individual = null, $edit = false): void 254e539f5c6SGreg Roach { 255e539f5c6SGreg Roach if ($individual === null) { 256e539f5c6SGreg Roach throw new IndividualNotFoundException(); 257e539f5c6SGreg Roach } 258e539f5c6SGreg Roach 2591450f098SGreg Roach if (!$individual->canShow()) { 2601450f098SGreg Roach throw new IndividualAccessDeniedException(); 2611450f098SGreg Roach } 2621450f098SGreg Roach 2631450f098SGreg Roach if ($edit && !$individual->canEdit()) { 264e539f5c6SGreg Roach throw new IndividualAccessDeniedException(); 265e539f5c6SGreg Roach } 266e539f5c6SGreg Roach } 267e539f5c6SGreg Roach 268e539f5c6SGreg Roach /** 269e539f5c6SGreg Roach * @param Media|null $media 270e539f5c6SGreg Roach * @param bool|null $edit 271e539f5c6SGreg Roach * 272e539f5c6SGreg Roach * @return void 273e539f5c6SGreg Roach * @throws MediaNotFoundException 274e539f5c6SGreg Roach * @throws MediaAccessDeniedException 275e539f5c6SGreg Roach */ 276e364afe4SGreg Roach public static function checkMediaAccess(Media $media = null, $edit = false): void 277e539f5c6SGreg Roach { 278e539f5c6SGreg Roach if ($media === null) { 279e539f5c6SGreg Roach throw new MediaNotFoundException(); 280e539f5c6SGreg Roach } 281e539f5c6SGreg Roach 2821450f098SGreg Roach if (!$media->canShow()) { 2831450f098SGreg Roach throw new MediaAccessDeniedException(); 2841450f098SGreg Roach } 2851450f098SGreg Roach 2861450f098SGreg Roach if ($edit && !$media->canEdit()) { 287e539f5c6SGreg Roach throw new MediaAccessDeniedException(); 288e539f5c6SGreg Roach } 289e539f5c6SGreg Roach } 290e539f5c6SGreg Roach 291e539f5c6SGreg Roach /** 292e539f5c6SGreg Roach * @param Note|null $note 293e539f5c6SGreg Roach * @param bool|null $edit 294e539f5c6SGreg Roach * 295e539f5c6SGreg Roach * @return void 296e539f5c6SGreg Roach * @throws NoteNotFoundException 297e539f5c6SGreg Roach * @throws NoteAccessDeniedException 298e539f5c6SGreg Roach */ 299e364afe4SGreg Roach public static function checkNoteAccess(Note $note = null, $edit = false): void 300e539f5c6SGreg Roach { 301e539f5c6SGreg Roach if ($note === null) { 302e539f5c6SGreg Roach throw new NoteNotFoundException(); 303e539f5c6SGreg Roach } 304e539f5c6SGreg Roach 3051450f098SGreg Roach if (!$note->canShow()) { 3061450f098SGreg Roach throw new NoteAccessDeniedException(); 3071450f098SGreg Roach } 3081450f098SGreg Roach 3091450f098SGreg Roach if ($edit && !$note->canEdit()) { 310e539f5c6SGreg Roach throw new NoteAccessDeniedException(); 311e539f5c6SGreg Roach } 312e539f5c6SGreg Roach } 313e539f5c6SGreg Roach 314e539f5c6SGreg Roach /** 315e539f5c6SGreg Roach * @param GedcomRecord|null $record 316e539f5c6SGreg Roach * @param bool|null $edit 317e539f5c6SGreg Roach * 318e539f5c6SGreg Roach * @return void 319e539f5c6SGreg Roach * @throws RecordNotFoundException 320e539f5c6SGreg Roach * @throws RecordAccessDeniedException 321e539f5c6SGreg Roach */ 322e364afe4SGreg Roach public static function checkRecordAccess(GedcomRecord $record = null, $edit = false): void 323e539f5c6SGreg Roach { 324e539f5c6SGreg Roach if ($record === null) { 325e539f5c6SGreg Roach throw new RecordNotFoundException(); 326e539f5c6SGreg Roach } 327e539f5c6SGreg Roach 3281450f098SGreg Roach if (!$record->canShow()) { 3291450f098SGreg Roach throw new RecordAccessDeniedException(); 3301450f098SGreg Roach } 3311450f098SGreg Roach 3321450f098SGreg Roach if ($edit && !$record->canEdit()) { 333e539f5c6SGreg Roach throw new RecordAccessDeniedException(); 334e539f5c6SGreg Roach } 335e539f5c6SGreg Roach } 336e539f5c6SGreg Roach 337e539f5c6SGreg Roach /** 338e539f5c6SGreg Roach * @param Repository|null $repository 339e539f5c6SGreg Roach * @param bool|null $edit 340e539f5c6SGreg Roach * 341e539f5c6SGreg Roach * @return void 342e539f5c6SGreg Roach * @throws RepositoryNotFoundException 343e539f5c6SGreg Roach * @throws RepositoryAccessDeniedException 344e539f5c6SGreg Roach */ 345e364afe4SGreg Roach public static function checkRepositoryAccess(Repository $repository = null, $edit = false): void 346e539f5c6SGreg Roach { 347e539f5c6SGreg Roach if ($repository === null) { 348e539f5c6SGreg Roach throw new RepositoryNotFoundException(); 349e539f5c6SGreg Roach } 350e539f5c6SGreg Roach 3511450f098SGreg Roach if (!$repository->canShow()) { 3521450f098SGreg Roach throw new RepositoryAccessDeniedException(); 3531450f098SGreg Roach } 3541450f098SGreg Roach 3551450f098SGreg Roach if ($edit && !$repository->canEdit()) { 356e539f5c6SGreg Roach throw new RepositoryAccessDeniedException(); 357e539f5c6SGreg Roach } 358e539f5c6SGreg Roach } 359e539f5c6SGreg Roach 360e539f5c6SGreg Roach /** 361e539f5c6SGreg Roach * @param Source|null $source 362e539f5c6SGreg Roach * @param bool|null $edit 363e539f5c6SGreg Roach * 364e539f5c6SGreg Roach * @return void 365e539f5c6SGreg Roach * @throws SourceNotFoundException 366e539f5c6SGreg Roach * @throws SourceAccessDeniedException 367e539f5c6SGreg Roach */ 368e364afe4SGreg Roach public static function checkSourceAccess(Source $source = null, $edit = false): void 369e539f5c6SGreg Roach { 370e539f5c6SGreg Roach if ($source === null) { 371e539f5c6SGreg Roach throw new SourceNotFoundException(); 372e539f5c6SGreg Roach } 373e539f5c6SGreg Roach 3741450f098SGreg Roach if (!$source->canShow()) { 3751450f098SGreg Roach throw new SourceAccessDeniedException(); 3761450f098SGreg Roach } 3771450f098SGreg Roach 3781450f098SGreg Roach if ($edit && !$source->canEdit()) { 379e539f5c6SGreg Roach throw new SourceAccessDeniedException(); 380e539f5c6SGreg Roach } 381e539f5c6SGreg Roach } 382a25f0a04SGreg Roach} 383