. */ declare(strict_types=1); namespace Fisharebest\Webtrees; use Fisharebest\Webtrees\Contracts\UserInterface; use Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException; use Fisharebest\Webtrees\Exceptions\FamilyNotFoundException; use Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException; use Fisharebest\Webtrees\Exceptions\IndividualNotFoundException; use Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException; use Fisharebest\Webtrees\Exceptions\MediaNotFoundException; use Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException; use Fisharebest\Webtrees\Exceptions\NoteNotFoundException; use Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException; use Fisharebest\Webtrees\Exceptions\RecordNotFoundException; use Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException; use Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException; use Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException; use Fisharebest\Webtrees\Exceptions\SourceNotFoundException; use Fisharebest\Webtrees\Module\ModuleInterface; use Fisharebest\Webtrees\Services\UserService; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * Authentication. */ class Auth { // Privacy constants public const PRIV_PRIVATE = 2; // Allows visitors to view the item public const PRIV_USER = 1; // Allows members to access the item public const PRIV_NONE = 0; // Allows managers to access the item public const PRIV_HIDE = -1; // Hide the item to all users /** * Are we currently logged in? * * @return bool */ public static function check(): bool { return self::id() !== null; } /** * Is the specified/current user an administrator? * * @param UserInterface|null $user * * @return bool */ public static function isAdmin(UserInterface $user = null): bool { $user = $user ?? self::user(); return $user->getPreference('canadmin') === '1'; } /** * Is the specified/current user a manager of a tree? * * @param Tree $tree * @param UserInterface|null $user * * @return bool */ public static function isManager(Tree $tree, UserInterface $user = null): bool { $user = $user ?? self::user(); return self::isAdmin($user) || $tree->getUserPreference($user, 'canedit') === 'admin'; } /** * Is the specified/current user a moderator of a tree? * * @param Tree $tree * @param UserInterface|null $user * * @return bool */ public static function isModerator(Tree $tree, UserInterface $user = null): bool { $user = $user ?? self::user(); return self::isManager($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'accept'; } /** * Is the specified/current user an editor of a tree? * * @param Tree $tree * @param UserInterface|null $user * * @return bool */ public static function isEditor(Tree $tree, UserInterface $user = null): bool { $user = $user ?? self::user(); return self::isModerator($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'edit'; } /** * Is the specified/current user a member of a tree? * * @param Tree $tree * @param UserInterface|null $user * * @return bool */ public static function isMember(Tree $tree, UserInterface $user = null): bool { $user = $user ?? self::user(); return self::isEditor($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'access'; } /** * What is the specified/current user's access level within a tree? * * @param Tree $tree * @param UserInterface|null $user * * @return int */ public static function accessLevel(Tree $tree, UserInterface $user = null) { $user = $user ?? self::user(); if (self::isManager($tree, $user)) { return self::PRIV_NONE; } if (self::isMember($tree, $user)) { return self::PRIV_USER; } return self::PRIV_PRIVATE; } /** * The ID of the authenticated user, from the current session. * * @return int|null */ public static function id() { return Session::get('wt_user'); } /** * The authenticated user, from the current session. * * @return UserInterface */ public static function user(): UserInterface { return (new UserService())->find(self::id()) ?? new GuestUser(); } /** * Login directly as an explicit user - for masquerading. * * @param UserInterface $user * * @return void */ public static function login(UserInterface $user) { Session::regenerate(false); Session::put('wt_user', $user->id()); } /** * End the session for the current user. * * @return void */ public static function logout() { Session::regenerate(true); } /** * @param ModuleInterface $module * @param string $component * @param Tree $tree * @param UserInterface $user * * @return void */ public static function checkComponentAccess(ModuleInterface $module, string $component, Tree $tree, UserInterface $user) { if ($module->accessLevel($tree, $component) < Auth::accessLevel($tree, $user)) { throw new AccessDeniedHttpException(''); } } /** * @param Family|null $family * @param bool|null $edit * * @return void * @throws FamilyNotFoundException * @throws FamilyAccessDeniedException */ public static function checkFamilyAccess(Family $family = null, $edit = false) { if ($family === null) { throw new FamilyNotFoundException(); } if (!$family->canShow() || $edit && (!$family->canEdit() || $family->isPendingDeletion())) { throw new FamilyAccessDeniedException(); } } /** * @param Individual|null $individual * @param bool|null $edit * * @return void * @throws IndividualNotFoundException * @throws IndividualAccessDeniedException */ public static function checkIndividualAccess(Individual $individual = null, $edit = false) { if ($individual === null) { throw new IndividualNotFoundException(); } if (!$individual->canShow() || $edit && (!$individual->canEdit() || $individual->isPendingDeletion())) { throw new IndividualAccessDeniedException(); } } /** * @param Media|null $media * @param bool|null $edit * * @return void * @throws MediaNotFoundException * @throws MediaAccessDeniedException */ public static function checkMediaAccess(Media $media = null, $edit = false) { if ($media === null) { throw new MediaNotFoundException(); } if (!$media->canShow() || $edit && (!$media->canEdit() || $media->isPendingDeletion())) { throw new MediaAccessDeniedException(); } } /** * @param Note|null $note * @param bool|null $edit * * @return void * @throws NoteNotFoundException * @throws NoteAccessDeniedException */ public static function checkNoteAccess(Note $note = null, $edit = false) { if ($note === null) { throw new NoteNotFoundException(); } if (!$note->canShow() || $edit && (!$note->canEdit() || $note->isPendingDeletion())) { throw new NoteAccessDeniedException(); } } /** * @param GedcomRecord|null $record * @param bool|null $edit * * @return void * @throws RecordNotFoundException * @throws RecordAccessDeniedException */ public static function checkRecordAccess(GedcomRecord $record = null, $edit = false) { if ($record === null) { throw new RecordNotFoundException(); } if (!$record->canShow() || $edit && (!$record->canEdit() || $record->isPendingDeletion())) { throw new RecordAccessDeniedException(); } } /** * @param Repository|null $repository * @param bool|null $edit * * @return void * @throws RepositoryNotFoundException * @throws RepositoryAccessDeniedException */ public static function checkRepositoryAccess(Repository $repository = null, $edit = false) { if ($repository === null) { throw new RepositoryNotFoundException(); } if (!$repository->canShow() || $edit && (!$repository->canEdit() || $repository->isPendingDeletion())) { throw new RepositoryAccessDeniedException(); } } /** * @param Source|null $source * @param bool|null $edit * * @return void * @throws SourceNotFoundException * @throws SourceAccessDeniedException */ public static function checkSourceAccess(Source $source = null, $edit = false) { if ($source === null) { throw new SourceNotFoundException(); } if (!$source->canShow() || $edit && (!$source->canEdit() || $source->isPendingDeletion())) { throw new SourceAccessDeniedException(); } } }