1a25f0a04SGreg Roach<?php 2*3976b470SGreg 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 */ 17e7f56f2aSGreg Roachdeclare(strict_types=1); 18e7f56f2aSGreg Roach 1976692c8bSGreg Roachnamespace Fisharebest\Webtrees; 20a25f0a04SGreg Roach 21e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 22e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException; 23e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\FamilyNotFoundException; 24e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException; 25e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\IndividualNotFoundException; 26e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException; 27e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\MediaNotFoundException; 28e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException; 29e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\NoteNotFoundException; 30e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException; 31e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RecordNotFoundException; 32e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException; 33e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException; 34e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException; 35e539f5c6SGreg Roachuse Fisharebest\Webtrees\Exceptions\SourceNotFoundException; 369867b2f0SGreg Roachuse Fisharebest\Webtrees\Module\ModuleInterface; 37e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Services\UserService; 389867b2f0SGreg Roachuse Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; 3979529c87SGreg Roach 40a25f0a04SGreg Roach/** 4176692c8bSGreg Roach * Authentication. 42a25f0a04SGreg Roach */ 43c1010edaSGreg Roachclass Auth 44c1010edaSGreg Roach{ 454b9ff166SGreg Roach // Privacy constants 4616d6367aSGreg Roach public const PRIV_PRIVATE = 2; // Allows visitors to view the item 4716d6367aSGreg Roach public const PRIV_USER = 1; // Allows members to access the item 4816d6367aSGreg Roach public const PRIV_NONE = 0; // Allows managers to access the item 4916d6367aSGreg Roach public const PRIV_HIDE = -1; // Hide the item to all users 504b9ff166SGreg Roach 51a25f0a04SGreg Roach /** 52a25f0a04SGreg Roach * Are we currently logged in? 53a25f0a04SGreg Roach * 54cbc1590aSGreg Roach * @return bool 55a25f0a04SGreg Roach */ 568f53f488SRico Sonntag public static function check(): bool 57c1010edaSGreg Roach { 584b9ff166SGreg Roach return self::id() !== null; 59a25f0a04SGreg Roach } 60a25f0a04SGreg Roach 61a25f0a04SGreg Roach /** 62a25f0a04SGreg Roach * Is the specified/current user an administrator? 63a25f0a04SGreg Roach * 64e5a6b4d4SGreg Roach * @param UserInterface|null $user 65a25f0a04SGreg Roach * 66cbc1590aSGreg Roach * @return bool 67a25f0a04SGreg Roach */ 68e5a6b4d4SGreg Roach public static function isAdmin(UserInterface $user = null): bool 69c1010edaSGreg Roach { 70cb923727SGreg Roach $user = $user ?? self::user(); 71a25f0a04SGreg Roach 72cb923727SGreg Roach return $user->getPreference('canadmin') === '1'; 73a25f0a04SGreg Roach } 74a25f0a04SGreg Roach 75a25f0a04SGreg Roach /** 764b9ff166SGreg Roach * Is the specified/current user a manager of a tree? 77a25f0a04SGreg Roach * 7884caa210SGreg Roach * @param Tree $tree 79e5a6b4d4SGreg Roach * @param UserInterface|null $user 80a25f0a04SGreg Roach * 81cbc1590aSGreg Roach * @return bool 82a25f0a04SGreg Roach */ 83e5a6b4d4SGreg Roach public static function isManager(Tree $tree, UserInterface $user = null): bool 84c1010edaSGreg Roach { 85cb923727SGreg Roach $user = $user ?? self::user(); 86a25f0a04SGreg Roach 87cb923727SGreg Roach return self::isAdmin($user) || $tree->getUserPreference($user, 'canedit') === 'admin'; 88a25f0a04SGreg Roach } 89a25f0a04SGreg Roach 90a25f0a04SGreg Roach /** 914b9ff166SGreg Roach * Is the specified/current user a moderator of a tree? 92a25f0a04SGreg Roach * 9384caa210SGreg Roach * @param Tree $tree 94e5a6b4d4SGreg Roach * @param UserInterface|null $user 95a25f0a04SGreg Roach * 96cbc1590aSGreg Roach * @return bool 97a25f0a04SGreg Roach */ 98e5a6b4d4SGreg Roach public static function isModerator(Tree $tree, UserInterface $user = null): bool 99c1010edaSGreg Roach { 100cb923727SGreg Roach $user = $user ?? self::user(); 101a25f0a04SGreg Roach 102cb923727SGreg Roach return self::isManager($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'accept'; 103a25f0a04SGreg Roach } 104a25f0a04SGreg Roach 105a25f0a04SGreg Roach /** 1064b9ff166SGreg Roach * Is the specified/current user an editor of a tree? 107a25f0a04SGreg Roach * 10884caa210SGreg Roach * @param Tree $tree 109e5a6b4d4SGreg Roach * @param UserInterface|null $user 110a25f0a04SGreg Roach * 111cbc1590aSGreg Roach * @return bool 112a25f0a04SGreg Roach */ 113e5a6b4d4SGreg Roach public static function isEditor(Tree $tree, UserInterface $user = null): bool 114c1010edaSGreg Roach { 115cb923727SGreg Roach $user = $user ?? self::user(); 116a25f0a04SGreg Roach 117cb923727SGreg Roach return self::isModerator($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'edit'; 118a25f0a04SGreg Roach } 119a25f0a04SGreg Roach 120a25f0a04SGreg Roach /** 1214b9ff166SGreg Roach * Is the specified/current user a member of a tree? 122a25f0a04SGreg Roach * 12384caa210SGreg Roach * @param Tree $tree 124e5a6b4d4SGreg Roach * @param UserInterface|null $user 125a25f0a04SGreg Roach * 126cbc1590aSGreg Roach * @return bool 127a25f0a04SGreg Roach */ 128e5a6b4d4SGreg Roach public static function isMember(Tree $tree, UserInterface $user = null): bool 129c1010edaSGreg Roach { 130cb923727SGreg Roach $user = $user ?? self::user(); 131a25f0a04SGreg Roach 132cb923727SGreg Roach return self::isEditor($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'access'; 133a25f0a04SGreg Roach } 134a25f0a04SGreg Roach 135a25f0a04SGreg Roach /** 1364b9ff166SGreg Roach * What is the specified/current user's access level within a tree? 1374b9ff166SGreg Roach * 1384b9ff166SGreg Roach * @param Tree $tree 139e5a6b4d4SGreg Roach * @param UserInterface|null $user 1404b9ff166SGreg Roach * 141cbc1590aSGreg Roach * @return int 1424b9ff166SGreg Roach */ 143e364afe4SGreg Roach public static function accessLevel(Tree $tree, UserInterface $user = null): int 144c1010edaSGreg Roach { 145cb923727SGreg Roach $user = $user ?? self::user(); 1464b9ff166SGreg Roach 1474b9ff166SGreg Roach if (self::isManager($tree, $user)) { 1484b9ff166SGreg Roach return self::PRIV_NONE; 1494b9ff166SGreg Roach } 150b2ce94c6SRico Sonntag 151b2ce94c6SRico Sonntag if (self::isMember($tree, $user)) { 152b2ce94c6SRico Sonntag return self::PRIV_USER; 153b2ce94c6SRico Sonntag } 154b2ce94c6SRico Sonntag 155b2ce94c6SRico Sonntag return self::PRIV_PRIVATE; 1564b9ff166SGreg Roach } 1574b9ff166SGreg Roach 1584b9ff166SGreg Roach /** 159a25f0a04SGreg Roach * The ID of the authenticated user, from the current session. 160a25f0a04SGreg Roach * 161c3ffc4cbSGreg Roach * @return int|null 162a25f0a04SGreg Roach */ 163e364afe4SGreg Roach public static function id(): ?int 164c1010edaSGreg Roach { 165adbb839bSGreg Roach $id = Session::get('wt_user'); 166adbb839bSGreg Roach 167adbb839bSGreg Roach if ($id !== null) { 168adbb839bSGreg Roach // In webtrees 1.x, the ID may have been a string. 169adbb839bSGreg Roach $id = (int) $id; 170adbb839bSGreg Roach } 171adbb839bSGreg Roach 172adbb839bSGreg Roach return $id; 173a25f0a04SGreg Roach } 174a25f0a04SGreg Roach 175a25f0a04SGreg Roach /** 176a25f0a04SGreg Roach * The authenticated user, from the current session. 177a25f0a04SGreg Roach * 178e5a6b4d4SGreg Roach * @return UserInterface 179a25f0a04SGreg Roach */ 180e5a6b4d4SGreg Roach public static function user(): UserInterface 181c1010edaSGreg Roach { 182e5a6b4d4SGreg Roach return (new UserService())->find(self::id()) ?? new GuestUser(); 183a25f0a04SGreg Roach } 184a25f0a04SGreg Roach 185a25f0a04SGreg Roach /** 186a25f0a04SGreg Roach * Login directly as an explicit user - for masquerading. 187a25f0a04SGreg Roach * 188e5a6b4d4SGreg Roach * @param UserInterface $user 189cb923727SGreg Roach * 190cb923727SGreg Roach * @return void 191a25f0a04SGreg Roach */ 192e364afe4SGreg Roach public static function login(UserInterface $user): void 193c1010edaSGreg Roach { 194e988f922SGreg Roach Session::regenerate(false); 195895230eeSGreg Roach Session::put('wt_user', $user->id()); 196a25f0a04SGreg Roach } 197a25f0a04SGreg Roach 198a25f0a04SGreg Roach /** 199a25f0a04SGreg Roach * End the session for the current user. 200cb923727SGreg Roach * 201cb923727SGreg Roach * @return void 202a25f0a04SGreg Roach */ 203e364afe4SGreg Roach public static function logout(): void 204c1010edaSGreg Roach { 20531bc7874SGreg Roach Session::regenerate(true); 206a25f0a04SGreg Roach } 207e539f5c6SGreg Roach 208e539f5c6SGreg Roach /** 2099867b2f0SGreg Roach * @param ModuleInterface $module 2109867b2f0SGreg Roach * @param string $component 2119867b2f0SGreg Roach * @param Tree $tree 212e5a6b4d4SGreg Roach * @param UserInterface $user 2139867b2f0SGreg Roach * 2149867b2f0SGreg Roach * @return void 2159867b2f0SGreg Roach */ 216e364afe4SGreg Roach public static function checkComponentAccess(ModuleInterface $module, string $component, Tree $tree, UserInterface $user): void 2179867b2f0SGreg Roach { 218e364afe4SGreg Roach if ($module->accessLevel($tree, $component) < self::accessLevel($tree, $user)) { 21996d794e7SGreg Roach throw new AccessDeniedHttpException('Access denied'); 2209867b2f0SGreg Roach } 2219867b2f0SGreg Roach } 2229867b2f0SGreg Roach 2239867b2f0SGreg Roach /** 224e539f5c6SGreg Roach * @param Family|null $family 225e539f5c6SGreg Roach * @param bool|null $edit 226e539f5c6SGreg Roach * 227e539f5c6SGreg Roach * @return void 228e539f5c6SGreg Roach * @throws FamilyNotFoundException 229e539f5c6SGreg Roach * @throws FamilyAccessDeniedException 230e539f5c6SGreg Roach */ 231e364afe4SGreg Roach public static function checkFamilyAccess(Family $family = null, $edit = false): void 232e539f5c6SGreg Roach { 233e539f5c6SGreg Roach if ($family === null) { 234e539f5c6SGreg Roach throw new FamilyNotFoundException(); 235e539f5c6SGreg Roach } 236e539f5c6SGreg Roach 2371450f098SGreg Roach if (!$family->canShow()) { 2381450f098SGreg Roach throw new FamilyAccessDeniedException(); 2391450f098SGreg Roach } 2401450f098SGreg Roach 2411450f098SGreg Roach if ($edit && !$family->canEdit()) { 242e539f5c6SGreg Roach throw new FamilyAccessDeniedException(); 243e539f5c6SGreg Roach } 244e539f5c6SGreg Roach } 245e539f5c6SGreg Roach 246e539f5c6SGreg Roach /** 247e539f5c6SGreg Roach * @param Individual|null $individual 248e539f5c6SGreg Roach * @param bool|null $edit 249e539f5c6SGreg Roach * 250e539f5c6SGreg Roach * @return void 251e539f5c6SGreg Roach * @throws IndividualNotFoundException 252e539f5c6SGreg Roach * @throws IndividualAccessDeniedException 253e539f5c6SGreg Roach */ 254e364afe4SGreg Roach public static function checkIndividualAccess(Individual $individual = null, $edit = false): void 255e539f5c6SGreg Roach { 256e539f5c6SGreg Roach if ($individual === null) { 257e539f5c6SGreg Roach throw new IndividualNotFoundException(); 258e539f5c6SGreg Roach } 259e539f5c6SGreg Roach 2601450f098SGreg Roach if (!$individual->canShow()) { 2611450f098SGreg Roach throw new IndividualAccessDeniedException(); 2621450f098SGreg Roach } 2631450f098SGreg Roach 2641450f098SGreg Roach if ($edit && !$individual->canEdit()) { 265e539f5c6SGreg Roach throw new IndividualAccessDeniedException(); 266e539f5c6SGreg Roach } 267e539f5c6SGreg Roach } 268e539f5c6SGreg Roach 269e539f5c6SGreg Roach /** 270e539f5c6SGreg Roach * @param Media|null $media 271e539f5c6SGreg Roach * @param bool|null $edit 272e539f5c6SGreg Roach * 273e539f5c6SGreg Roach * @return void 274e539f5c6SGreg Roach * @throws MediaNotFoundException 275e539f5c6SGreg Roach * @throws MediaAccessDeniedException 276e539f5c6SGreg Roach */ 277e364afe4SGreg Roach public static function checkMediaAccess(Media $media = null, $edit = false): void 278e539f5c6SGreg Roach { 279e539f5c6SGreg Roach if ($media === null) { 280e539f5c6SGreg Roach throw new MediaNotFoundException(); 281e539f5c6SGreg Roach } 282e539f5c6SGreg Roach 2831450f098SGreg Roach if (!$media->canShow()) { 2841450f098SGreg Roach throw new MediaAccessDeniedException(); 2851450f098SGreg Roach } 2861450f098SGreg Roach 2871450f098SGreg Roach if ($edit && !$media->canEdit()) { 288e539f5c6SGreg Roach throw new MediaAccessDeniedException(); 289e539f5c6SGreg Roach } 290e539f5c6SGreg Roach } 291e539f5c6SGreg Roach 292e539f5c6SGreg Roach /** 293e539f5c6SGreg Roach * @param Note|null $note 294e539f5c6SGreg Roach * @param bool|null $edit 295e539f5c6SGreg Roach * 296e539f5c6SGreg Roach * @return void 297e539f5c6SGreg Roach * @throws NoteNotFoundException 298e539f5c6SGreg Roach * @throws NoteAccessDeniedException 299e539f5c6SGreg Roach */ 300e364afe4SGreg Roach public static function checkNoteAccess(Note $note = null, $edit = false): void 301e539f5c6SGreg Roach { 302e539f5c6SGreg Roach if ($note === null) { 303e539f5c6SGreg Roach throw new NoteNotFoundException(); 304e539f5c6SGreg Roach } 305e539f5c6SGreg Roach 3061450f098SGreg Roach if (!$note->canShow()) { 3071450f098SGreg Roach throw new NoteAccessDeniedException(); 3081450f098SGreg Roach } 3091450f098SGreg Roach 3101450f098SGreg Roach if ($edit && !$note->canEdit()) { 311e539f5c6SGreg Roach throw new NoteAccessDeniedException(); 312e539f5c6SGreg Roach } 313e539f5c6SGreg Roach } 314e539f5c6SGreg Roach 315e539f5c6SGreg Roach /** 316e539f5c6SGreg Roach * @param GedcomRecord|null $record 317e539f5c6SGreg Roach * @param bool|null $edit 318e539f5c6SGreg Roach * 319e539f5c6SGreg Roach * @return void 320e539f5c6SGreg Roach * @throws RecordNotFoundException 321e539f5c6SGreg Roach * @throws RecordAccessDeniedException 322e539f5c6SGreg Roach */ 323e364afe4SGreg Roach public static function checkRecordAccess(GedcomRecord $record = null, $edit = false): void 324e539f5c6SGreg Roach { 325e539f5c6SGreg Roach if ($record === null) { 326e539f5c6SGreg Roach throw new RecordNotFoundException(); 327e539f5c6SGreg Roach } 328e539f5c6SGreg Roach 3291450f098SGreg Roach if (!$record->canShow()) { 3301450f098SGreg Roach throw new RecordAccessDeniedException(); 3311450f098SGreg Roach } 3321450f098SGreg Roach 3331450f098SGreg Roach if ($edit && !$record->canEdit()) { 334e539f5c6SGreg Roach throw new RecordAccessDeniedException(); 335e539f5c6SGreg Roach } 336e539f5c6SGreg Roach } 337e539f5c6SGreg Roach 338e539f5c6SGreg Roach /** 339e539f5c6SGreg Roach * @param Repository|null $repository 340e539f5c6SGreg Roach * @param bool|null $edit 341e539f5c6SGreg Roach * 342e539f5c6SGreg Roach * @return void 343e539f5c6SGreg Roach * @throws RepositoryNotFoundException 344e539f5c6SGreg Roach * @throws RepositoryAccessDeniedException 345e539f5c6SGreg Roach */ 346e364afe4SGreg Roach public static function checkRepositoryAccess(Repository $repository = null, $edit = false): void 347e539f5c6SGreg Roach { 348e539f5c6SGreg Roach if ($repository === null) { 349e539f5c6SGreg Roach throw new RepositoryNotFoundException(); 350e539f5c6SGreg Roach } 351e539f5c6SGreg Roach 3521450f098SGreg Roach if (!$repository->canShow()) { 3531450f098SGreg Roach throw new RepositoryAccessDeniedException(); 3541450f098SGreg Roach } 3551450f098SGreg Roach 3561450f098SGreg Roach if ($edit && !$repository->canEdit()) { 357e539f5c6SGreg Roach throw new RepositoryAccessDeniedException(); 358e539f5c6SGreg Roach } 359e539f5c6SGreg Roach } 360e539f5c6SGreg Roach 361e539f5c6SGreg Roach /** 362e539f5c6SGreg Roach * @param Source|null $source 363e539f5c6SGreg Roach * @param bool|null $edit 364e539f5c6SGreg Roach * 365e539f5c6SGreg Roach * @return void 366e539f5c6SGreg Roach * @throws SourceNotFoundException 367e539f5c6SGreg Roach * @throws SourceAccessDeniedException 368e539f5c6SGreg Roach */ 369e364afe4SGreg Roach public static function checkSourceAccess(Source $source = null, $edit = false): void 370e539f5c6SGreg Roach { 371e539f5c6SGreg Roach if ($source === null) { 372e539f5c6SGreg Roach throw new SourceNotFoundException(); 373e539f5c6SGreg Roach } 374e539f5c6SGreg Roach 3751450f098SGreg Roach if (!$source->canShow()) { 3761450f098SGreg Roach throw new SourceAccessDeniedException(); 3771450f098SGreg Roach } 3781450f098SGreg Roach 3791450f098SGreg Roach if ($edit && !$source->canEdit()) { 380e539f5c6SGreg Roach throw new SourceAccessDeniedException(); 381e539f5c6SGreg Roach } 382e539f5c6SGreg Roach } 383a25f0a04SGreg Roach} 384