xref: /webtrees/app/Auth.php (revision 677aaceaa3f30bf689cca08894f5a34cdf4056b6)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2019 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees;
19
20use Fisharebest\Webtrees\Exceptions\FamilyAccessDeniedException;
21use Fisharebest\Webtrees\Exceptions\FamilyNotFoundException;
22use Fisharebest\Webtrees\Exceptions\IndividualAccessDeniedException;
23use Fisharebest\Webtrees\Exceptions\IndividualNotFoundException;
24use Fisharebest\Webtrees\Exceptions\MediaAccessDeniedException;
25use Fisharebest\Webtrees\Exceptions\MediaNotFoundException;
26use Fisharebest\Webtrees\Exceptions\NoteAccessDeniedException;
27use Fisharebest\Webtrees\Exceptions\NoteNotFoundException;
28use Fisharebest\Webtrees\Exceptions\RecordAccessDeniedException;
29use Fisharebest\Webtrees\Exceptions\RecordNotFoundException;
30use Fisharebest\Webtrees\Exceptions\RepositoryAccessDeniedException;
31use Fisharebest\Webtrees\Exceptions\RepositoryNotFoundException;
32use Fisharebest\Webtrees\Exceptions\SourceAccessDeniedException;
33use Fisharebest\Webtrees\Exceptions\SourceNotFoundException;
34use stdClass;
35
36/**
37 * Authentication.
38 */
39class Auth
40{
41    // Privacy constants
42    public const PRIV_PRIVATE = 2; // Allows visitors to view the item
43    public const PRIV_USER    = 1; // Allows members to access the item
44    public const PRIV_NONE    = 0; // Allows managers to access the item
45    public const PRIV_HIDE    = -1; // Hide the item to all users
46
47    /**
48     * Are we currently logged in?
49     *
50     * @return bool
51     */
52    public static function check(): bool
53    {
54        return self::id() !== null;
55    }
56
57    /**
58     * Is the specified/current user an administrator?
59     *
60     * @param User|null $user
61     *
62     * @return bool
63     */
64    public static function isAdmin(User $user = null): bool
65    {
66        $user = $user ?? self::user();
67
68        return $user->getPreference('canadmin') === '1';
69    }
70
71    /**
72     * Is the specified/current user a manager of a tree?
73     *
74     * @param Tree      $tree
75     * @param User|null $user
76     *
77     * @return bool
78     */
79    public static function isManager(Tree $tree, User $user = null): bool
80    {
81        $user = $user ?? self::user();
82
83        return self::isAdmin($user) || $tree->getUserPreference($user, 'canedit') === 'admin';
84    }
85
86    /**
87     * Is the specified/current user a moderator of a tree?
88     *
89     * @param Tree      $tree
90     * @param User|null $user
91     *
92     * @return bool
93     */
94    public static function isModerator(Tree $tree, User $user = null): bool
95    {
96        $user = $user ?? self::user();
97
98        return self::isManager($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'accept';
99    }
100
101    /**
102     * Is the specified/current user an editor of a tree?
103     *
104     * @param Tree      $tree
105     * @param User|null $user
106     *
107     * @return bool
108     */
109    public static function isEditor(Tree $tree, User $user = null): bool
110    {
111        $user = $user ?? self::user();
112
113        return self::isModerator($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'edit';
114    }
115
116    /**
117     * Is the specified/current user a member of a tree?
118     *
119     * @param Tree      $tree
120     * @param User|null $user
121     *
122     * @return bool
123     */
124    public static function isMember(Tree $tree, User $user = null): bool
125    {
126        $user = $user ?? self::user();
127
128        return self::isEditor($tree, $user) || $tree->getUserPreference($user, 'canedit') === 'access';
129    }
130
131    /**
132     * What is the specified/current user's access level within a tree?
133     *
134     * @param Tree      $tree
135     * @param User|null $user
136     *
137     * @return int
138     */
139    public static function accessLevel(Tree $tree, User $user = null)
140    {
141        $user = $user ?? self::user();
142
143        if (self::isManager($tree, $user)) {
144            return self::PRIV_NONE;
145        }
146
147        if (self::isMember($tree, $user)) {
148            return self::PRIV_USER;
149        }
150
151        return self::PRIV_PRIVATE;
152    }
153
154    /**
155     * The ID of the authenticated user, from the current session.
156     *
157     * @return int|null
158     */
159    public static function id()
160    {
161        return Session::get('wt_user');
162    }
163
164    /**
165     * The authenticated user, from the current session.
166     *
167     * @return User
168     */
169    public static function user()
170    {
171        return User::find(self::id()) ?? User::visitor();
172    }
173
174    /**
175     * Login directly as an explicit user - for masquerading.
176     *
177     * @param User $user
178     *
179     * @return void
180     */
181    public static function login(User $user)
182    {
183        Session::regenerate(false);
184        Session::put('wt_user', $user->id());
185    }
186
187    /**
188     * End the session for the current user.
189     *
190     * @return void
191     */
192    public static function logout()
193    {
194        Session::regenerate(true);
195    }
196
197    /**
198     * @param Family|null $family
199     * @param bool|null   $edit
200     *
201     * @return void
202     * @throws FamilyNotFoundException
203     * @throws FamilyAccessDeniedException
204     */
205    public static function checkFamilyAccess(Family $family = null, $edit = false)
206    {
207        if ($family === null) {
208            throw new FamilyNotFoundException();
209        }
210
211        if (!$family->canShow() || $edit && (!$family->canEdit() || $family->isPendingDeletion())) {
212            throw new FamilyAccessDeniedException();
213        }
214    }
215
216    /**
217     * @param Individual|null $individual
218     * @param bool|null       $edit
219     *
220     * @return void
221     * @throws IndividualNotFoundException
222     * @throws IndividualAccessDeniedException
223     */
224    public static function checkIndividualAccess(Individual $individual = null, $edit = false)
225    {
226        if ($individual === null) {
227            throw new IndividualNotFoundException();
228        }
229
230        if (!$individual->canShow() || $edit && (!$individual->canEdit() || $individual->isPendingDeletion())) {
231            throw new IndividualAccessDeniedException();
232        }
233    }
234
235    /**
236     * @param Media|null $media
237     * @param bool|null  $edit
238     *
239     * @return void
240     * @throws MediaNotFoundException
241     * @throws MediaAccessDeniedException
242     */
243    public static function checkMediaAccess(Media $media = null, $edit = false)
244    {
245        if ($media === null) {
246            throw new MediaNotFoundException();
247        }
248
249        if (!$media->canShow() || $edit && (!$media->canEdit() || $media->isPendingDeletion())) {
250            throw new MediaAccessDeniedException();
251        }
252    }
253
254    /**
255     * @param Note|null $note
256     * @param bool|null $edit
257     *
258     * @return void
259     * @throws NoteNotFoundException
260     * @throws NoteAccessDeniedException
261     */
262    public static function checkNoteAccess(Note $note = null, $edit = false)
263    {
264        if ($note === null) {
265            throw new NoteNotFoundException();
266        }
267
268        if (!$note->canShow() || $edit && (!$note->canEdit() || $note->isPendingDeletion())) {
269            throw new NoteAccessDeniedException();
270        }
271    }
272
273    /**
274     * @param GedcomRecord|null $record
275     * @param bool|null         $edit
276     *
277     * @return void
278     * @throws RecordNotFoundException
279     * @throws RecordAccessDeniedException
280     */
281    public static function checkRecordAccess(GedcomRecord $record = null, $edit = false)
282    {
283        if ($record === null) {
284            throw new RecordNotFoundException();
285        }
286
287        if (!$record->canShow() || $edit && (!$record->canEdit() || $record->isPendingDeletion())) {
288            throw new RecordAccessDeniedException();
289        }
290    }
291
292    /**
293     * @param Repository|null $repository
294     * @param bool|null       $edit
295     *
296     * @return void
297     * @throws RepositoryNotFoundException
298     * @throws RepositoryAccessDeniedException
299     */
300    public static function checkRepositoryAccess(Repository $repository = null, $edit = false)
301    {
302        if ($repository === null) {
303            throw new RepositoryNotFoundException();
304        }
305
306        if (!$repository->canShow() || $edit && (!$repository->canEdit() || $repository->isPendingDeletion())) {
307            throw new RepositoryAccessDeniedException();
308        }
309    }
310
311    /**
312     * @param Source|null $source
313     * @param bool|null   $edit
314     *
315     * @return void
316     * @throws SourceNotFoundException
317     * @throws SourceAccessDeniedException
318     */
319    public static function checkSourceAccess(Source $source = null, $edit = false)
320    {
321        if ($source === null) {
322            throw new SourceNotFoundException();
323        }
324
325        if (!$source->canShow() || $edit && (!$source->canEdit() || $source->isPendingDeletion())) {
326            throw new SourceAccessDeniedException();
327        }
328    }
329}
330