xref: /webtrees/app/Auth.php (revision 3976b4703df669696105ed6b024b96d433c8fbdb)
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