Passed
Push — master ( 466fee...8256f4 )
by Greg
06:43 queued 01:19
created

Auth   F

Complexity

Total Complexity 60

Size/Duplication

Total Lines 378
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 88
dl 0
loc 378
rs 3.6
c 0
b 0
f 0
wmc 60

20 Methods

Rating   Name   Duplication   Size   Complexity  
A isEditor() 0 5 2
A login() 0 4 1
A checkComponentAccess() 0 4 2
A logout() 0 3 1
A isAdmin() 0 5 1
A accessLevel() 0 13 3
A check() 0 3 1
A isModerator() 0 5 2
A user() 0 3 1
A isManager() 0 5 2
A isMember() 0 5 2
A id() 0 10 2
A checkSourceAccess() 0 15 5
A checkIndividualAccess() 0 15 5
A checkRepositoryAccess() 0 15 5
A checkRecordAccess() 0 15 5
A checkMediaAccess() 0 15 5
A checkNoteAccess() 0 15 5
A checkFamilyAccess() 0 15 5
A checkSubmitterAccess() 0 15 5

How to fix   Complexity   

Complex Class

Complex classes like Auth often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Auth, and based on these observations, apply Extract Interface, too.

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