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