@@ -21,308 +21,308 @@ |
||
| 21 | 21 | |
| 22 | 22 | class PageBan extends InternalPageBase |
| 23 | 23 | { |
| 24 | - /** |
|
| 25 | - * Main function for this page, when no specific actions are called. |
|
| 26 | - */ |
|
| 27 | - protected function main() |
|
| 28 | - { |
|
| 29 | - $this->assignCSRFToken(); |
|
| 30 | - |
|
| 31 | - $this->setHtmlTitle('Bans'); |
|
| 32 | - |
|
| 33 | - $bans = Ban::getActiveBans(null, $this->getDatabase()); |
|
| 34 | - |
|
| 35 | - $userIds = array_map( |
|
| 36 | - function(Ban $entry) { |
|
| 37 | - return $entry->getUser(); |
|
| 38 | - }, |
|
| 39 | - $bans); |
|
| 40 | - $userList = UserSearchHelper::get($this->getDatabase())->inIds($userIds)->fetchMap('username'); |
|
| 41 | - |
|
| 42 | - $user = User::getCurrent($this->getDatabase()); |
|
| 43 | - $this->assign('canSet', $this->barrierTest('set', $user)); |
|
| 44 | - $this->assign('canRemove', $this->barrierTest('remove', $user)); |
|
| 45 | - |
|
| 46 | - $this->assign('usernames', $userList); |
|
| 47 | - $this->assign('activebans', $bans); |
|
| 48 | - $this->setTemplate('bans/banlist.tpl'); |
|
| 49 | - } |
|
| 50 | - |
|
| 51 | - /** |
|
| 52 | - * Entry point for the ban set action |
|
| 53 | - */ |
|
| 54 | - protected function set() |
|
| 55 | - { |
|
| 56 | - $this->setHtmlTitle('Bans'); |
|
| 57 | - |
|
| 58 | - // dual-mode action |
|
| 59 | - if (WebRequest::wasPosted()) { |
|
| 60 | - try { |
|
| 61 | - $this->handlePostMethodForSetBan(); |
|
| 62 | - } |
|
| 63 | - catch (ApplicationLogicException $ex) { |
|
| 64 | - SessionAlert::error($ex->getMessage()); |
|
| 65 | - $this->redirect("bans", "set"); |
|
| 66 | - } |
|
| 67 | - } |
|
| 68 | - else { |
|
| 69 | - $this->handleGetMethodForSetBan(); |
|
| 70 | - } |
|
| 71 | - } |
|
| 72 | - |
|
| 73 | - /** |
|
| 74 | - * Entry point for the ban remove action |
|
| 75 | - */ |
|
| 76 | - protected function remove() |
|
| 77 | - { |
|
| 78 | - $this->setHtmlTitle('Bans'); |
|
| 79 | - |
|
| 80 | - $ban = $this->getBanForUnban(); |
|
| 81 | - |
|
| 82 | - // dual mode |
|
| 83 | - if (WebRequest::wasPosted()) { |
|
| 84 | - $this->validateCSRFToken(); |
|
| 85 | - $unbanReason = WebRequest::postString('unbanreason'); |
|
| 86 | - |
|
| 87 | - if ($unbanReason === null || trim($unbanReason) === "") { |
|
| 88 | - SessionAlert::error('No unban reason specified'); |
|
| 89 | - $this->redirect("bans", "remove", array('id' => $ban->getId())); |
|
| 90 | - } |
|
| 91 | - |
|
| 92 | - // set optimistic locking from delete form page load |
|
| 93 | - $updateVersion = WebRequest::postInt('updateversion'); |
|
| 94 | - $ban->setUpdateVersion($updateVersion); |
|
| 95 | - |
|
| 96 | - $database = $this->getDatabase(); |
|
| 97 | - $ban->setActive(false); |
|
| 98 | - $ban->save(); |
|
| 99 | - |
|
| 100 | - Logger::unbanned($database, $ban, $unbanReason); |
|
| 101 | - |
|
| 102 | - SessionAlert::quick('Disabled ban.'); |
|
| 103 | - $this->getNotificationHelper()->unbanned($ban, $unbanReason); |
|
| 104 | - |
|
| 105 | - $this->redirect('bans'); |
|
| 106 | - } |
|
| 107 | - else { |
|
| 108 | - $this->assignCSRFToken(); |
|
| 109 | - $this->assign('ban', $ban); |
|
| 110 | - $this->setTemplate('bans/unban.tpl'); |
|
| 111 | - } |
|
| 112 | - } |
|
| 113 | - |
|
| 114 | - /** |
|
| 115 | - * @throws ApplicationLogicException |
|
| 116 | - */ |
|
| 117 | - private function getBanDuration() |
|
| 118 | - { |
|
| 119 | - $duration = WebRequest::postString('duration'); |
|
| 120 | - if ($duration === "other") { |
|
| 121 | - $duration = strtotime(WebRequest::postString('otherduration')); |
|
| 122 | - |
|
| 123 | - if (!$duration) { |
|
| 124 | - throw new ApplicationLogicException('Invalid ban time'); |
|
| 125 | - } |
|
| 126 | - elseif (time() > $duration) { |
|
| 127 | - throw new ApplicationLogicException('Ban time has already expired!'); |
|
| 128 | - } |
|
| 129 | - |
|
| 130 | - return $duration; |
|
| 131 | - } |
|
| 132 | - elseif ($duration === "-1") { |
|
| 133 | - return null; |
|
| 134 | - } |
|
| 135 | - else { |
|
| 136 | - $duration = WebRequest::postInt('duration') + time(); |
|
| 137 | - |
|
| 138 | - return $duration; |
|
| 139 | - } |
|
| 140 | - } |
|
| 141 | - |
|
| 142 | - /** |
|
| 143 | - * @param string $type |
|
| 144 | - * @param string $target |
|
| 145 | - * |
|
| 146 | - * @throws ApplicationLogicException |
|
| 147 | - */ |
|
| 148 | - private function validateBanType($type, $target) |
|
| 149 | - { |
|
| 150 | - switch ($type) { |
|
| 151 | - case 'IP': |
|
| 152 | - $this->validateIpBan($target); |
|
| 153 | - |
|
| 154 | - return; |
|
| 155 | - case 'Name': |
|
| 156 | - // No validation needed here. |
|
| 157 | - return; |
|
| 158 | - case 'EMail': |
|
| 159 | - $this->validateEmailBanTarget($target); |
|
| 160 | - |
|
| 161 | - return; |
|
| 162 | - default: |
|
| 163 | - throw new ApplicationLogicException("Unknown ban type"); |
|
| 164 | - } |
|
| 165 | - } |
|
| 166 | - |
|
| 167 | - /** |
|
| 168 | - * Handles the POST method on the set action |
|
| 169 | - * |
|
| 170 | - * @throws ApplicationLogicException |
|
| 171 | - * @throws Exception |
|
| 172 | - */ |
|
| 173 | - private function handlePostMethodForSetBan() |
|
| 174 | - { |
|
| 175 | - $this->validateCSRFToken(); |
|
| 176 | - $reason = WebRequest::postString('banreason'); |
|
| 177 | - $target = WebRequest::postString('target'); |
|
| 178 | - |
|
| 179 | - // Checks whether there is a reason entered for ban. |
|
| 180 | - if ($reason === null || trim($reason) === "") { |
|
| 181 | - throw new ApplicationLogicException('You must specify a ban reason'); |
|
| 182 | - } |
|
| 183 | - |
|
| 184 | - // Checks whether there is a target entered to ban. |
|
| 185 | - if ($target === null || trim($target) === "") { |
|
| 186 | - throw new ApplicationLogicException('You must specify a target to be banned'); |
|
| 187 | - } |
|
| 188 | - |
|
| 189 | - // Validate ban duration |
|
| 190 | - $duration = $this->getBanDuration(); |
|
| 191 | - |
|
| 192 | - // Validate ban type & target for that type |
|
| 193 | - $type = WebRequest::postString('type'); |
|
| 194 | - $this->validateBanType($type, $target); |
|
| 195 | - |
|
| 196 | - $database = $this->getDatabase(); |
|
| 197 | - |
|
| 198 | - if (count(Ban::getActiveBans($target, $database)) > 0) { |
|
| 199 | - throw new ApplicationLogicException('This target is already banned!'); |
|
| 200 | - } |
|
| 201 | - |
|
| 202 | - $ban = new Ban(); |
|
| 203 | - $ban->setDatabase($database); |
|
| 204 | - $ban->setActive(true); |
|
| 205 | - $ban->setType($type); |
|
| 206 | - $ban->setTarget($target); |
|
| 207 | - $ban->setUser(User::getCurrent($database)->getId()); |
|
| 208 | - $ban->setReason($reason); |
|
| 209 | - $ban->setDuration($duration); |
|
| 210 | - |
|
| 211 | - $ban->save(); |
|
| 212 | - |
|
| 213 | - Logger::banned($database, $ban, $reason); |
|
| 214 | - |
|
| 215 | - $this->getNotificationHelper()->banned($ban); |
|
| 216 | - SessionAlert::quick('Ban has been set.'); |
|
| 217 | - |
|
| 218 | - $this->redirect('bans'); |
|
| 219 | - } |
|
| 220 | - |
|
| 221 | - /** |
|
| 222 | - * Handles the GET method on the set action |
|
| 223 | - */ |
|
| 224 | - protected function handleGetMethodForSetBan() |
|
| 225 | - { |
|
| 226 | - $this->setTemplate('bans/banform.tpl'); |
|
| 227 | - $this->assignCSRFToken(); |
|
| 228 | - |
|
| 229 | - $banType = WebRequest::getString('type'); |
|
| 230 | - $banTarget = WebRequest::getInt('request'); |
|
| 231 | - |
|
| 232 | - $database = $this->getDatabase(); |
|
| 233 | - |
|
| 234 | - // if the parameters are null, skip loading a request. |
|
| 235 | - if ($banType === null |
|
| 236 | - || !in_array($banType, array('IP', 'Name', 'EMail')) |
|
| 237 | - || $banTarget === null |
|
| 238 | - || $banTarget === 0 |
|
| 239 | - ) { |
|
| 240 | - $this->assign('bantarget', ''); |
|
| 241 | - $this->assign('bantype', ''); |
|
| 242 | - |
|
| 243 | - return; |
|
| 244 | - } |
|
| 245 | - |
|
| 246 | - // Set the ban type, which the user has indicated. |
|
| 247 | - $this->assign('bantype', $banType); |
|
| 248 | - |
|
| 249 | - // Attempt to resolve the correct target |
|
| 250 | - /** @var Request $request */ |
|
| 251 | - $request = Request::getById($banTarget, $database); |
|
| 252 | - if ($request === false) { |
|
| 253 | - $this->assign('bantarget', ''); |
|
| 254 | - |
|
| 255 | - return; |
|
| 256 | - } |
|
| 257 | - |
|
| 258 | - $realTarget = ''; |
|
| 259 | - switch ($banType) { |
|
| 260 | - case 'EMail': |
|
| 261 | - $realTarget = $request->getEmail(); |
|
| 262 | - break; |
|
| 263 | - case 'IP': |
|
| 264 | - $xffProvider = $this->getXffTrustProvider(); |
|
| 265 | - $realTarget = $xffProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp()); |
|
| 266 | - break; |
|
| 267 | - case 'Name': |
|
| 268 | - $realTarget = $request->getName(); |
|
| 269 | - break; |
|
| 270 | - } |
|
| 271 | - |
|
| 272 | - $this->assign('bantarget', $realTarget); |
|
| 273 | - } |
|
| 274 | - |
|
| 275 | - /** |
|
| 276 | - * Validates an IP ban target |
|
| 277 | - * |
|
| 278 | - * @param string $target |
|
| 279 | - * |
|
| 280 | - * @throws ApplicationLogicException |
|
| 281 | - */ |
|
| 282 | - private function validateIpBan($target) |
|
| 283 | - { |
|
| 284 | - $squidIpList = $this->getSiteConfiguration()->getSquidList(); |
|
| 285 | - |
|
| 286 | - if (filter_var($target, FILTER_VALIDATE_IP) === false) { |
|
| 287 | - throw new ApplicationLogicException('Invalid target - IP address expected.'); |
|
| 288 | - } |
|
| 289 | - |
|
| 290 | - if (in_array($target, $squidIpList)) { |
|
| 291 | - throw new ApplicationLogicException("This IP address is on the protected list of proxies, and cannot be banned."); |
|
| 292 | - } |
|
| 293 | - } |
|
| 294 | - |
|
| 295 | - /** |
|
| 296 | - * Validates an email address as a ban target |
|
| 297 | - * |
|
| 298 | - * @param string $target |
|
| 299 | - * |
|
| 300 | - * @throws ApplicationLogicException |
|
| 301 | - */ |
|
| 302 | - private function validateEmailBanTarget($target) |
|
| 303 | - { |
|
| 304 | - if (filter_var($target, FILTER_VALIDATE_EMAIL) !== $target) { |
|
| 305 | - throw new ApplicationLogicException('Invalid target - email address expected.'); |
|
| 306 | - } |
|
| 307 | - } |
|
| 308 | - |
|
| 309 | - /** |
|
| 310 | - * @return Ban |
|
| 311 | - * @throws ApplicationLogicException |
|
| 312 | - */ |
|
| 313 | - private function getBanForUnban() |
|
| 314 | - { |
|
| 315 | - $banId = WebRequest::getInt('id'); |
|
| 316 | - if ($banId === null || $banId === 0) { |
|
| 317 | - throw new ApplicationLogicException("The ban ID appears to be missing. This is probably a bug."); |
|
| 318 | - } |
|
| 319 | - |
|
| 320 | - $ban = Ban::getActiveId($banId, $this->getDatabase()); |
|
| 321 | - |
|
| 322 | - if ($ban === false) { |
|
| 323 | - throw new ApplicationLogicException("The specified ban is not currently active, or doesn't exist."); |
|
| 324 | - } |
|
| 325 | - |
|
| 326 | - return $ban; |
|
| 327 | - } |
|
| 24 | + /** |
|
| 25 | + * Main function for this page, when no specific actions are called. |
|
| 26 | + */ |
|
| 27 | + protected function main() |
|
| 28 | + { |
|
| 29 | + $this->assignCSRFToken(); |
|
| 30 | + |
|
| 31 | + $this->setHtmlTitle('Bans'); |
|
| 32 | + |
|
| 33 | + $bans = Ban::getActiveBans(null, $this->getDatabase()); |
|
| 34 | + |
|
| 35 | + $userIds = array_map( |
|
| 36 | + function(Ban $entry) { |
|
| 37 | + return $entry->getUser(); |
|
| 38 | + }, |
|
| 39 | + $bans); |
|
| 40 | + $userList = UserSearchHelper::get($this->getDatabase())->inIds($userIds)->fetchMap('username'); |
|
| 41 | + |
|
| 42 | + $user = User::getCurrent($this->getDatabase()); |
|
| 43 | + $this->assign('canSet', $this->barrierTest('set', $user)); |
|
| 44 | + $this->assign('canRemove', $this->barrierTest('remove', $user)); |
|
| 45 | + |
|
| 46 | + $this->assign('usernames', $userList); |
|
| 47 | + $this->assign('activebans', $bans); |
|
| 48 | + $this->setTemplate('bans/banlist.tpl'); |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + /** |
|
| 52 | + * Entry point for the ban set action |
|
| 53 | + */ |
|
| 54 | + protected function set() |
|
| 55 | + { |
|
| 56 | + $this->setHtmlTitle('Bans'); |
|
| 57 | + |
|
| 58 | + // dual-mode action |
|
| 59 | + if (WebRequest::wasPosted()) { |
|
| 60 | + try { |
|
| 61 | + $this->handlePostMethodForSetBan(); |
|
| 62 | + } |
|
| 63 | + catch (ApplicationLogicException $ex) { |
|
| 64 | + SessionAlert::error($ex->getMessage()); |
|
| 65 | + $this->redirect("bans", "set"); |
|
| 66 | + } |
|
| 67 | + } |
|
| 68 | + else { |
|
| 69 | + $this->handleGetMethodForSetBan(); |
|
| 70 | + } |
|
| 71 | + } |
|
| 72 | + |
|
| 73 | + /** |
|
| 74 | + * Entry point for the ban remove action |
|
| 75 | + */ |
|
| 76 | + protected function remove() |
|
| 77 | + { |
|
| 78 | + $this->setHtmlTitle('Bans'); |
|
| 79 | + |
|
| 80 | + $ban = $this->getBanForUnban(); |
|
| 81 | + |
|
| 82 | + // dual mode |
|
| 83 | + if (WebRequest::wasPosted()) { |
|
| 84 | + $this->validateCSRFToken(); |
|
| 85 | + $unbanReason = WebRequest::postString('unbanreason'); |
|
| 86 | + |
|
| 87 | + if ($unbanReason === null || trim($unbanReason) === "") { |
|
| 88 | + SessionAlert::error('No unban reason specified'); |
|
| 89 | + $this->redirect("bans", "remove", array('id' => $ban->getId())); |
|
| 90 | + } |
|
| 91 | + |
|
| 92 | + // set optimistic locking from delete form page load |
|
| 93 | + $updateVersion = WebRequest::postInt('updateversion'); |
|
| 94 | + $ban->setUpdateVersion($updateVersion); |
|
| 95 | + |
|
| 96 | + $database = $this->getDatabase(); |
|
| 97 | + $ban->setActive(false); |
|
| 98 | + $ban->save(); |
|
| 99 | + |
|
| 100 | + Logger::unbanned($database, $ban, $unbanReason); |
|
| 101 | + |
|
| 102 | + SessionAlert::quick('Disabled ban.'); |
|
| 103 | + $this->getNotificationHelper()->unbanned($ban, $unbanReason); |
|
| 104 | + |
|
| 105 | + $this->redirect('bans'); |
|
| 106 | + } |
|
| 107 | + else { |
|
| 108 | + $this->assignCSRFToken(); |
|
| 109 | + $this->assign('ban', $ban); |
|
| 110 | + $this->setTemplate('bans/unban.tpl'); |
|
| 111 | + } |
|
| 112 | + } |
|
| 113 | + |
|
| 114 | + /** |
|
| 115 | + * @throws ApplicationLogicException |
|
| 116 | + */ |
|
| 117 | + private function getBanDuration() |
|
| 118 | + { |
|
| 119 | + $duration = WebRequest::postString('duration'); |
|
| 120 | + if ($duration === "other") { |
|
| 121 | + $duration = strtotime(WebRequest::postString('otherduration')); |
|
| 122 | + |
|
| 123 | + if (!$duration) { |
|
| 124 | + throw new ApplicationLogicException('Invalid ban time'); |
|
| 125 | + } |
|
| 126 | + elseif (time() > $duration) { |
|
| 127 | + throw new ApplicationLogicException('Ban time has already expired!'); |
|
| 128 | + } |
|
| 129 | + |
|
| 130 | + return $duration; |
|
| 131 | + } |
|
| 132 | + elseif ($duration === "-1") { |
|
| 133 | + return null; |
|
| 134 | + } |
|
| 135 | + else { |
|
| 136 | + $duration = WebRequest::postInt('duration') + time(); |
|
| 137 | + |
|
| 138 | + return $duration; |
|
| 139 | + } |
|
| 140 | + } |
|
| 141 | + |
|
| 142 | + /** |
|
| 143 | + * @param string $type |
|
| 144 | + * @param string $target |
|
| 145 | + * |
|
| 146 | + * @throws ApplicationLogicException |
|
| 147 | + */ |
|
| 148 | + private function validateBanType($type, $target) |
|
| 149 | + { |
|
| 150 | + switch ($type) { |
|
| 151 | + case 'IP': |
|
| 152 | + $this->validateIpBan($target); |
|
| 153 | + |
|
| 154 | + return; |
|
| 155 | + case 'Name': |
|
| 156 | + // No validation needed here. |
|
| 157 | + return; |
|
| 158 | + case 'EMail': |
|
| 159 | + $this->validateEmailBanTarget($target); |
|
| 160 | + |
|
| 161 | + return; |
|
| 162 | + default: |
|
| 163 | + throw new ApplicationLogicException("Unknown ban type"); |
|
| 164 | + } |
|
| 165 | + } |
|
| 166 | + |
|
| 167 | + /** |
|
| 168 | + * Handles the POST method on the set action |
|
| 169 | + * |
|
| 170 | + * @throws ApplicationLogicException |
|
| 171 | + * @throws Exception |
|
| 172 | + */ |
|
| 173 | + private function handlePostMethodForSetBan() |
|
| 174 | + { |
|
| 175 | + $this->validateCSRFToken(); |
|
| 176 | + $reason = WebRequest::postString('banreason'); |
|
| 177 | + $target = WebRequest::postString('target'); |
|
| 178 | + |
|
| 179 | + // Checks whether there is a reason entered for ban. |
|
| 180 | + if ($reason === null || trim($reason) === "") { |
|
| 181 | + throw new ApplicationLogicException('You must specify a ban reason'); |
|
| 182 | + } |
|
| 183 | + |
|
| 184 | + // Checks whether there is a target entered to ban. |
|
| 185 | + if ($target === null || trim($target) === "") { |
|
| 186 | + throw new ApplicationLogicException('You must specify a target to be banned'); |
|
| 187 | + } |
|
| 188 | + |
|
| 189 | + // Validate ban duration |
|
| 190 | + $duration = $this->getBanDuration(); |
|
| 191 | + |
|
| 192 | + // Validate ban type & target for that type |
|
| 193 | + $type = WebRequest::postString('type'); |
|
| 194 | + $this->validateBanType($type, $target); |
|
| 195 | + |
|
| 196 | + $database = $this->getDatabase(); |
|
| 197 | + |
|
| 198 | + if (count(Ban::getActiveBans($target, $database)) > 0) { |
|
| 199 | + throw new ApplicationLogicException('This target is already banned!'); |
|
| 200 | + } |
|
| 201 | + |
|
| 202 | + $ban = new Ban(); |
|
| 203 | + $ban->setDatabase($database); |
|
| 204 | + $ban->setActive(true); |
|
| 205 | + $ban->setType($type); |
|
| 206 | + $ban->setTarget($target); |
|
| 207 | + $ban->setUser(User::getCurrent($database)->getId()); |
|
| 208 | + $ban->setReason($reason); |
|
| 209 | + $ban->setDuration($duration); |
|
| 210 | + |
|
| 211 | + $ban->save(); |
|
| 212 | + |
|
| 213 | + Logger::banned($database, $ban, $reason); |
|
| 214 | + |
|
| 215 | + $this->getNotificationHelper()->banned($ban); |
|
| 216 | + SessionAlert::quick('Ban has been set.'); |
|
| 217 | + |
|
| 218 | + $this->redirect('bans'); |
|
| 219 | + } |
|
| 220 | + |
|
| 221 | + /** |
|
| 222 | + * Handles the GET method on the set action |
|
| 223 | + */ |
|
| 224 | + protected function handleGetMethodForSetBan() |
|
| 225 | + { |
|
| 226 | + $this->setTemplate('bans/banform.tpl'); |
|
| 227 | + $this->assignCSRFToken(); |
|
| 228 | + |
|
| 229 | + $banType = WebRequest::getString('type'); |
|
| 230 | + $banTarget = WebRequest::getInt('request'); |
|
| 231 | + |
|
| 232 | + $database = $this->getDatabase(); |
|
| 233 | + |
|
| 234 | + // if the parameters are null, skip loading a request. |
|
| 235 | + if ($banType === null |
|
| 236 | + || !in_array($banType, array('IP', 'Name', 'EMail')) |
|
| 237 | + || $banTarget === null |
|
| 238 | + || $banTarget === 0 |
|
| 239 | + ) { |
|
| 240 | + $this->assign('bantarget', ''); |
|
| 241 | + $this->assign('bantype', ''); |
|
| 242 | + |
|
| 243 | + return; |
|
| 244 | + } |
|
| 245 | + |
|
| 246 | + // Set the ban type, which the user has indicated. |
|
| 247 | + $this->assign('bantype', $banType); |
|
| 248 | + |
|
| 249 | + // Attempt to resolve the correct target |
|
| 250 | + /** @var Request $request */ |
|
| 251 | + $request = Request::getById($banTarget, $database); |
|
| 252 | + if ($request === false) { |
|
| 253 | + $this->assign('bantarget', ''); |
|
| 254 | + |
|
| 255 | + return; |
|
| 256 | + } |
|
| 257 | + |
|
| 258 | + $realTarget = ''; |
|
| 259 | + switch ($banType) { |
|
| 260 | + case 'EMail': |
|
| 261 | + $realTarget = $request->getEmail(); |
|
| 262 | + break; |
|
| 263 | + case 'IP': |
|
| 264 | + $xffProvider = $this->getXffTrustProvider(); |
|
| 265 | + $realTarget = $xffProvider->getTrustedClientIp($request->getIp(), $request->getForwardedIp()); |
|
| 266 | + break; |
|
| 267 | + case 'Name': |
|
| 268 | + $realTarget = $request->getName(); |
|
| 269 | + break; |
|
| 270 | + } |
|
| 271 | + |
|
| 272 | + $this->assign('bantarget', $realTarget); |
|
| 273 | + } |
|
| 274 | + |
|
| 275 | + /** |
|
| 276 | + * Validates an IP ban target |
|
| 277 | + * |
|
| 278 | + * @param string $target |
|
| 279 | + * |
|
| 280 | + * @throws ApplicationLogicException |
|
| 281 | + */ |
|
| 282 | + private function validateIpBan($target) |
|
| 283 | + { |
|
| 284 | + $squidIpList = $this->getSiteConfiguration()->getSquidList(); |
|
| 285 | + |
|
| 286 | + if (filter_var($target, FILTER_VALIDATE_IP) === false) { |
|
| 287 | + throw new ApplicationLogicException('Invalid target - IP address expected.'); |
|
| 288 | + } |
|
| 289 | + |
|
| 290 | + if (in_array($target, $squidIpList)) { |
|
| 291 | + throw new ApplicationLogicException("This IP address is on the protected list of proxies, and cannot be banned."); |
|
| 292 | + } |
|
| 293 | + } |
|
| 294 | + |
|
| 295 | + /** |
|
| 296 | + * Validates an email address as a ban target |
|
| 297 | + * |
|
| 298 | + * @param string $target |
|
| 299 | + * |
|
| 300 | + * @throws ApplicationLogicException |
|
| 301 | + */ |
|
| 302 | + private function validateEmailBanTarget($target) |
|
| 303 | + { |
|
| 304 | + if (filter_var($target, FILTER_VALIDATE_EMAIL) !== $target) { |
|
| 305 | + throw new ApplicationLogicException('Invalid target - email address expected.'); |
|
| 306 | + } |
|
| 307 | + } |
|
| 308 | + |
|
| 309 | + /** |
|
| 310 | + * @return Ban |
|
| 311 | + * @throws ApplicationLogicException |
|
| 312 | + */ |
|
| 313 | + private function getBanForUnban() |
|
| 314 | + { |
|
| 315 | + $banId = WebRequest::getInt('id'); |
|
| 316 | + if ($banId === null || $banId === 0) { |
|
| 317 | + throw new ApplicationLogicException("The ban ID appears to be missing. This is probably a bug."); |
|
| 318 | + } |
|
| 319 | + |
|
| 320 | + $ban = Ban::getActiveId($banId, $this->getDatabase()); |
|
| 321 | + |
|
| 322 | + if ($ban === false) { |
|
| 323 | + throw new ApplicationLogicException("The specified ban is not currently active, or doesn't exist."); |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + return $ban; |
|
| 327 | + } |
|
| 328 | 328 | } |
@@ -33,7 +33,8 @@ discard block |
||
| 33 | 33 | $bans = Ban::getActiveBans(null, $this->getDatabase()); |
| 34 | 34 | |
| 35 | 35 | $userIds = array_map( |
| 36 | - function(Ban $entry) { |
|
| 36 | + function(Ban $entry) |
|
| 37 | + { |
|
| 37 | 38 | return $entry->getUser(); |
| 38 | 39 | }, |
| 39 | 40 | $bans); |
@@ -64,8 +65,7 @@ discard block |
||
| 64 | 65 | SessionAlert::error($ex->getMessage()); |
| 65 | 66 | $this->redirect("bans", "set"); |
| 66 | 67 | } |
| 67 | - } |
|
| 68 | - else { |
|
| 68 | + } else { |
|
| 69 | 69 | $this->handleGetMethodForSetBan(); |
| 70 | 70 | } |
| 71 | 71 | } |
@@ -103,8 +103,7 @@ discard block |
||
| 103 | 103 | $this->getNotificationHelper()->unbanned($ban, $unbanReason); |
| 104 | 104 | |
| 105 | 105 | $this->redirect('bans'); |
| 106 | - } |
|
| 107 | - else { |
|
| 106 | + } else { |
|
| 108 | 107 | $this->assignCSRFToken(); |
| 109 | 108 | $this->assign('ban', $ban); |
| 110 | 109 | $this->setTemplate('bans/unban.tpl'); |
@@ -122,17 +121,14 @@ discard block |
||
| 122 | 121 | |
| 123 | 122 | if (!$duration) { |
| 124 | 123 | throw new ApplicationLogicException('Invalid ban time'); |
| 125 | - } |
|
| 126 | - elseif (time() > $duration) { |
|
| 124 | + } elseif (time() > $duration) { |
|
| 127 | 125 | throw new ApplicationLogicException('Ban time has already expired!'); |
| 128 | 126 | } |
| 129 | 127 | |
| 130 | 128 | return $duration; |
| 131 | - } |
|
| 132 | - elseif ($duration === "-1") { |
|
| 129 | + } elseif ($duration === "-1") { |
|
| 133 | 130 | return null; |
| 134 | - } |
|
| 135 | - else { |
|
| 131 | + } else { |
|
| 136 | 132 | $duration = WebRequest::postInt('duration') + time(); |
| 137 | 133 | |
| 138 | 134 | return $duration; |
@@ -18,143 +18,143 @@ |
||
| 18 | 18 | |
| 19 | 19 | class PageSearch extends InternalPageBase |
| 20 | 20 | { |
| 21 | - use RequestListData; |
|
| 22 | - |
|
| 23 | - /** |
|
| 24 | - * Main function for this page, when no specific actions are called. |
|
| 25 | - */ |
|
| 26 | - protected function main() |
|
| 27 | - { |
|
| 28 | - $this->setHtmlTitle('Search'); |
|
| 29 | - |
|
| 30 | - // Dual-mode page |
|
| 31 | - if (WebRequest::wasPosted()) { |
|
| 32 | - $searchType = WebRequest::postString('type'); |
|
| 33 | - $searchTerm = WebRequest::postString('term'); |
|
| 34 | - |
|
| 35 | - $validationError = ""; |
|
| 36 | - if (!$this->validateSearchParameters($searchType, $searchTerm, $validationError)) { |
|
| 37 | - SessionAlert::error($validationError, "Search error"); |
|
| 38 | - $this->redirect("search"); |
|
| 39 | - |
|
| 40 | - return; |
|
| 41 | - } |
|
| 42 | - |
|
| 43 | - $results = array(); |
|
| 44 | - |
|
| 45 | - switch ($searchType) { |
|
| 46 | - case 'name': |
|
| 47 | - $results = $this->getNameSearchResults($searchTerm); |
|
| 48 | - break; |
|
| 49 | - case 'email': |
|
| 50 | - $results = $this->getEmailSearchResults($searchTerm); |
|
| 51 | - break; |
|
| 52 | - case 'ip': |
|
| 53 | - $results = $this->getIpSearchResults($searchTerm); |
|
| 54 | - break; |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - // deal with results |
|
| 58 | - $this->assign('requests', $this->prepareRequestData($results)); |
|
| 59 | - $this->assign('resultCount', count($results)); |
|
| 60 | - $this->assign('term', $searchTerm); |
|
| 61 | - $this->assign('target', $searchType); |
|
| 62 | - |
|
| 63 | - $this->assignCSRFToken(); |
|
| 64 | - $this->setTemplate('search/searchResult.tpl'); |
|
| 65 | - } |
|
| 66 | - else { |
|
| 67 | - $this->assignCSRFToken(); |
|
| 68 | - $this->setTemplate('search/searchForm.tpl'); |
|
| 69 | - } |
|
| 70 | - } |
|
| 71 | - |
|
| 72 | - /** |
|
| 73 | - * Gets search results by name |
|
| 74 | - * |
|
| 75 | - * @param string $searchTerm |
|
| 76 | - * |
|
| 77 | - * @return Request[] |
|
| 78 | - */ |
|
| 79 | - private function getNameSearchResults($searchTerm) |
|
| 80 | - { |
|
| 81 | - $padded = '%' . $searchTerm . '%'; |
|
| 82 | - |
|
| 83 | - /** @var Request[] $requests */ |
|
| 84 | - $requests = RequestSearchHelper::get($this->getDatabase()) |
|
| 85 | - ->byName($padded) |
|
| 86 | - ->fetch(); |
|
| 87 | - |
|
| 88 | - return $requests; |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | - /** |
|
| 92 | - * Gets search results by email |
|
| 93 | - * |
|
| 94 | - * @param string $searchTerm |
|
| 95 | - * |
|
| 96 | - * @return Request[] |
|
| 97 | - * @throws ApplicationLogicException |
|
| 98 | - */ |
|
| 99 | - private function getEmailSearchResults($searchTerm) |
|
| 100 | - { |
|
| 101 | - if ($searchTerm === "@") { |
|
| 102 | - throw new ApplicationLogicException('The search term "@" is not valid for email address searches!'); |
|
| 103 | - } |
|
| 104 | - |
|
| 105 | - $padded = '%' . $searchTerm . '%'; |
|
| 106 | - |
|
| 107 | - /** @var Request[] $requests */ |
|
| 108 | - $requests = RequestSearchHelper::get($this->getDatabase()) |
|
| 109 | - ->byEmailAddress($padded) |
|
| 110 | - ->excludingPurgedData($this->getSiteConfiguration()) |
|
| 111 | - ->fetch(); |
|
| 112 | - |
|
| 113 | - return $requests; |
|
| 114 | - } |
|
| 115 | - |
|
| 116 | - /** |
|
| 117 | - * Gets search results by IP address or XFF IP address |
|
| 118 | - * |
|
| 119 | - * @param string $searchTerm |
|
| 120 | - * |
|
| 121 | - * @return Request[] |
|
| 122 | - */ |
|
| 123 | - private function getIpSearchResults($searchTerm) |
|
| 124 | - { |
|
| 125 | - /** @var Request[] $requests */ |
|
| 126 | - $requests = RequestSearchHelper::get($this->getDatabase()) |
|
| 127 | - ->byIp($searchTerm) |
|
| 128 | - ->excludingPurgedData($this->getSiteConfiguration()) |
|
| 129 | - ->fetch(); |
|
| 130 | - |
|
| 131 | - return $requests; |
|
| 132 | - } |
|
| 133 | - |
|
| 134 | - /** |
|
| 135 | - * @param string $searchType |
|
| 136 | - * @param string $searchTerm |
|
| 137 | - * |
|
| 138 | - * @param string $errorMessage |
|
| 139 | - * |
|
| 140 | - * @return bool true if parameters are valid |
|
| 141 | - */ |
|
| 142 | - protected function validateSearchParameters($searchType, $searchTerm, &$errorMessage) |
|
| 143 | - { |
|
| 144 | - if (!in_array($searchType, array('name', 'email', 'ip'))) { |
|
| 145 | - $errorMessage = 'Unknown search type'; |
|
| 146 | - |
|
| 147 | - return false; |
|
| 148 | - } |
|
| 149 | - |
|
| 150 | - if ($searchTerm === '%' || $searchTerm === '' || $searchTerm === null) { |
|
| 151 | - $errorMessage = 'No search term specified entered'; |
|
| 152 | - |
|
| 153 | - return false; |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - $errorMessage = ""; |
|
| 157 | - |
|
| 158 | - return true; |
|
| 159 | - } |
|
| 21 | + use RequestListData; |
|
| 22 | + |
|
| 23 | + /** |
|
| 24 | + * Main function for this page, when no specific actions are called. |
|
| 25 | + */ |
|
| 26 | + protected function main() |
|
| 27 | + { |
|
| 28 | + $this->setHtmlTitle('Search'); |
|
| 29 | + |
|
| 30 | + // Dual-mode page |
|
| 31 | + if (WebRequest::wasPosted()) { |
|
| 32 | + $searchType = WebRequest::postString('type'); |
|
| 33 | + $searchTerm = WebRequest::postString('term'); |
|
| 34 | + |
|
| 35 | + $validationError = ""; |
|
| 36 | + if (!$this->validateSearchParameters($searchType, $searchTerm, $validationError)) { |
|
| 37 | + SessionAlert::error($validationError, "Search error"); |
|
| 38 | + $this->redirect("search"); |
|
| 39 | + |
|
| 40 | + return; |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + $results = array(); |
|
| 44 | + |
|
| 45 | + switch ($searchType) { |
|
| 46 | + case 'name': |
|
| 47 | + $results = $this->getNameSearchResults($searchTerm); |
|
| 48 | + break; |
|
| 49 | + case 'email': |
|
| 50 | + $results = $this->getEmailSearchResults($searchTerm); |
|
| 51 | + break; |
|
| 52 | + case 'ip': |
|
| 53 | + $results = $this->getIpSearchResults($searchTerm); |
|
| 54 | + break; |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + // deal with results |
|
| 58 | + $this->assign('requests', $this->prepareRequestData($results)); |
|
| 59 | + $this->assign('resultCount', count($results)); |
|
| 60 | + $this->assign('term', $searchTerm); |
|
| 61 | + $this->assign('target', $searchType); |
|
| 62 | + |
|
| 63 | + $this->assignCSRFToken(); |
|
| 64 | + $this->setTemplate('search/searchResult.tpl'); |
|
| 65 | + } |
|
| 66 | + else { |
|
| 67 | + $this->assignCSRFToken(); |
|
| 68 | + $this->setTemplate('search/searchForm.tpl'); |
|
| 69 | + } |
|
| 70 | + } |
|
| 71 | + |
|
| 72 | + /** |
|
| 73 | + * Gets search results by name |
|
| 74 | + * |
|
| 75 | + * @param string $searchTerm |
|
| 76 | + * |
|
| 77 | + * @return Request[] |
|
| 78 | + */ |
|
| 79 | + private function getNameSearchResults($searchTerm) |
|
| 80 | + { |
|
| 81 | + $padded = '%' . $searchTerm . '%'; |
|
| 82 | + |
|
| 83 | + /** @var Request[] $requests */ |
|
| 84 | + $requests = RequestSearchHelper::get($this->getDatabase()) |
|
| 85 | + ->byName($padded) |
|
| 86 | + ->fetch(); |
|
| 87 | + |
|
| 88 | + return $requests; |
|
| 89 | + } |
|
| 90 | + |
|
| 91 | + /** |
|
| 92 | + * Gets search results by email |
|
| 93 | + * |
|
| 94 | + * @param string $searchTerm |
|
| 95 | + * |
|
| 96 | + * @return Request[] |
|
| 97 | + * @throws ApplicationLogicException |
|
| 98 | + */ |
|
| 99 | + private function getEmailSearchResults($searchTerm) |
|
| 100 | + { |
|
| 101 | + if ($searchTerm === "@") { |
|
| 102 | + throw new ApplicationLogicException('The search term "@" is not valid for email address searches!'); |
|
| 103 | + } |
|
| 104 | + |
|
| 105 | + $padded = '%' . $searchTerm . '%'; |
|
| 106 | + |
|
| 107 | + /** @var Request[] $requests */ |
|
| 108 | + $requests = RequestSearchHelper::get($this->getDatabase()) |
|
| 109 | + ->byEmailAddress($padded) |
|
| 110 | + ->excludingPurgedData($this->getSiteConfiguration()) |
|
| 111 | + ->fetch(); |
|
| 112 | + |
|
| 113 | + return $requests; |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + /** |
|
| 117 | + * Gets search results by IP address or XFF IP address |
|
| 118 | + * |
|
| 119 | + * @param string $searchTerm |
|
| 120 | + * |
|
| 121 | + * @return Request[] |
|
| 122 | + */ |
|
| 123 | + private function getIpSearchResults($searchTerm) |
|
| 124 | + { |
|
| 125 | + /** @var Request[] $requests */ |
|
| 126 | + $requests = RequestSearchHelper::get($this->getDatabase()) |
|
| 127 | + ->byIp($searchTerm) |
|
| 128 | + ->excludingPurgedData($this->getSiteConfiguration()) |
|
| 129 | + ->fetch(); |
|
| 130 | + |
|
| 131 | + return $requests; |
|
| 132 | + } |
|
| 133 | + |
|
| 134 | + /** |
|
| 135 | + * @param string $searchType |
|
| 136 | + * @param string $searchTerm |
|
| 137 | + * |
|
| 138 | + * @param string $errorMessage |
|
| 139 | + * |
|
| 140 | + * @return bool true if parameters are valid |
|
| 141 | + */ |
|
| 142 | + protected function validateSearchParameters($searchType, $searchTerm, &$errorMessage) |
|
| 143 | + { |
|
| 144 | + if (!in_array($searchType, array('name', 'email', 'ip'))) { |
|
| 145 | + $errorMessage = 'Unknown search type'; |
|
| 146 | + |
|
| 147 | + return false; |
|
| 148 | + } |
|
| 149 | + |
|
| 150 | + if ($searchTerm === '%' || $searchTerm === '' || $searchTerm === null) { |
|
| 151 | + $errorMessage = 'No search term specified entered'; |
|
| 152 | + |
|
| 153 | + return false; |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + $errorMessage = ""; |
|
| 157 | + |
|
| 158 | + return true; |
|
| 159 | + } |
|
| 160 | 160 | } |
@@ -62,8 +62,7 @@ |
||
| 62 | 62 | |
| 63 | 63 | $this->assignCSRFToken(); |
| 64 | 64 | $this->setTemplate('search/searchResult.tpl'); |
| 65 | - } |
|
| 66 | - else { |
|
| 65 | + } else { |
|
| 67 | 66 | $this->assignCSRFToken(); |
| 68 | 67 | $this->setTemplate('search/searchForm.tpl'); |
| 69 | 68 | } |
@@ -20,54 +20,54 @@ discard block |
||
| 20 | 20 | |
| 21 | 21 | class PageMain extends InternalPageBase |
| 22 | 22 | { |
| 23 | - use RequestListData; |
|
| 24 | - |
|
| 25 | - /** |
|
| 26 | - * Main function for this page, when no actions are called. |
|
| 27 | - */ |
|
| 28 | - protected function main() |
|
| 29 | - { |
|
| 30 | - $this->assignCSRFToken(); |
|
| 31 | - |
|
| 32 | - $config = $this->getSiteConfiguration(); |
|
| 33 | - $database = $this->getDatabase(); |
|
| 34 | - $currentUser = User::getCurrent($database); |
|
| 35 | - |
|
| 36 | - // general template configuration |
|
| 37 | - $this->assign('defaultRequestState', $config->getDefaultRequestStateKey()); |
|
| 38 | - $this->assign('requestLimitShowOnly', $config->getMiserModeLimit()); |
|
| 39 | - |
|
| 40 | - $seeAllRequests = $this->barrierTest('seeAllRequests', $currentUser, PageViewRequest::class); |
|
| 41 | - |
|
| 42 | - // Fetch request data |
|
| 43 | - $requestSectionData = array(); |
|
| 44 | - if ($seeAllRequests) { |
|
| 45 | - $this->setupStatusSections($database, $config, $requestSectionData); |
|
| 46 | - $this->setupHospitalQueue($database, $config, $requestSectionData); |
|
| 47 | - $this->setupJobQueue($database, $config, $requestSectionData); |
|
| 48 | - } |
|
| 49 | - $this->setupLastFiveClosedData($database, $seeAllRequests); |
|
| 50 | - |
|
| 51 | - // Assign data to template |
|
| 52 | - $this->assign('requestSectionData', $requestSectionData); |
|
| 53 | - |
|
| 54 | - $this->setTemplate('mainpage/mainpage.tpl'); |
|
| 55 | - } |
|
| 56 | - |
|
| 57 | - /** |
|
| 58 | - * @param PdoDatabase $database |
|
| 59 | - * @param bool $seeAllRequests |
|
| 60 | - * |
|
| 61 | - * @internal param User $currentUser |
|
| 62 | - */ |
|
| 63 | - private function setupLastFiveClosedData(PdoDatabase $database, $seeAllRequests) |
|
| 64 | - { |
|
| 65 | - $this->assign('showLastFive', $seeAllRequests); |
|
| 66 | - if (!$seeAllRequests) { |
|
| 67 | - return; |
|
| 68 | - } |
|
| 69 | - |
|
| 70 | - $query = <<<SQL |
|
| 23 | + use RequestListData; |
|
| 24 | + |
|
| 25 | + /** |
|
| 26 | + * Main function for this page, when no actions are called. |
|
| 27 | + */ |
|
| 28 | + protected function main() |
|
| 29 | + { |
|
| 30 | + $this->assignCSRFToken(); |
|
| 31 | + |
|
| 32 | + $config = $this->getSiteConfiguration(); |
|
| 33 | + $database = $this->getDatabase(); |
|
| 34 | + $currentUser = User::getCurrent($database); |
|
| 35 | + |
|
| 36 | + // general template configuration |
|
| 37 | + $this->assign('defaultRequestState', $config->getDefaultRequestStateKey()); |
|
| 38 | + $this->assign('requestLimitShowOnly', $config->getMiserModeLimit()); |
|
| 39 | + |
|
| 40 | + $seeAllRequests = $this->barrierTest('seeAllRequests', $currentUser, PageViewRequest::class); |
|
| 41 | + |
|
| 42 | + // Fetch request data |
|
| 43 | + $requestSectionData = array(); |
|
| 44 | + if ($seeAllRequests) { |
|
| 45 | + $this->setupStatusSections($database, $config, $requestSectionData); |
|
| 46 | + $this->setupHospitalQueue($database, $config, $requestSectionData); |
|
| 47 | + $this->setupJobQueue($database, $config, $requestSectionData); |
|
| 48 | + } |
|
| 49 | + $this->setupLastFiveClosedData($database, $seeAllRequests); |
|
| 50 | + |
|
| 51 | + // Assign data to template |
|
| 52 | + $this->assign('requestSectionData', $requestSectionData); |
|
| 53 | + |
|
| 54 | + $this->setTemplate('mainpage/mainpage.tpl'); |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + /** |
|
| 58 | + * @param PdoDatabase $database |
|
| 59 | + * @param bool $seeAllRequests |
|
| 60 | + * |
|
| 61 | + * @internal param User $currentUser |
|
| 62 | + */ |
|
| 63 | + private function setupLastFiveClosedData(PdoDatabase $database, $seeAllRequests) |
|
| 64 | + { |
|
| 65 | + $this->assign('showLastFive', $seeAllRequests); |
|
| 66 | + if (!$seeAllRequests) { |
|
| 67 | + return; |
|
| 68 | + } |
|
| 69 | + |
|
| 70 | + $query = <<<SQL |
|
| 71 | 71 | SELECT request.id, request.name, request.updateversion |
| 72 | 72 | FROM request /* PageMain::main() */ |
| 73 | 73 | JOIN log ON log.objectid = request.id AND log.objecttype = 'Request' |
@@ -76,113 +76,113 @@ discard block |
||
| 76 | 76 | LIMIT 5; |
| 77 | 77 | SQL; |
| 78 | 78 | |
| 79 | - $statement = $database->prepare($query); |
|
| 80 | - $statement->execute(); |
|
| 81 | - |
|
| 82 | - $last5result = $statement->fetchAll(PDO::FETCH_ASSOC); |
|
| 83 | - |
|
| 84 | - $this->assign('lastFive', $last5result); |
|
| 85 | - } |
|
| 86 | - |
|
| 87 | - /** |
|
| 88 | - * @param PdoDatabase $database |
|
| 89 | - * @param SiteConfiguration $config |
|
| 90 | - * @param $requestSectionData |
|
| 91 | - */ |
|
| 92 | - private function setupHospitalQueue( |
|
| 93 | - PdoDatabase $database, |
|
| 94 | - SiteConfiguration $config, |
|
| 95 | - &$requestSectionData |
|
| 96 | - ) { |
|
| 97 | - $search = RequestSearchHelper::get($database) |
|
| 98 | - ->limit($config->getMiserModeLimit()) |
|
| 99 | - ->excludingStatus('Closed') |
|
| 100 | - ->isHospitalised(); |
|
| 101 | - |
|
| 102 | - if ($config->getEmailConfirmationEnabled()) { |
|
| 103 | - $search->withConfirmedEmail(); |
|
| 104 | - } |
|
| 105 | - |
|
| 106 | - /** @var Request[] $results */ |
|
| 107 | - $results = $search->getRecordCount($requestCount)->fetch(); |
|
| 108 | - |
|
| 109 | - if($requestCount > 0) { |
|
| 110 | - $requestSectionData['Hospital - Requests failed auto-creation'] = array( |
|
| 111 | - 'requests' => $this->prepareRequestData($results), |
|
| 112 | - 'total' => $requestCount, |
|
| 113 | - 'api' => 'hospital', |
|
| 114 | - 'type' => 'hospital', |
|
| 115 | - 'special' => 'Job Queue', |
|
| 116 | - 'help' => 'This queue lists all the requests which have been attempted to be created in the background, but for which this has failed for one reason or another. Check the job queue to find the error. Requests here may need to be created manually, or it may be possible to re-queue the request for auto-creation by the tool, or it may have been created already. Use your own technical discretion here.', |
|
| 117 | - 'showAll' => false |
|
| 118 | - ); |
|
| 119 | - } |
|
| 120 | - } |
|
| 121 | - |
|
| 122 | - /** |
|
| 123 | - * @param PdoDatabase $database |
|
| 124 | - * @param SiteConfiguration $config |
|
| 125 | - * @param $requestSectionData |
|
| 126 | - */ |
|
| 127 | - private function setupJobQueue( |
|
| 128 | - PdoDatabase $database, |
|
| 129 | - SiteConfiguration $config, |
|
| 130 | - &$requestSectionData |
|
| 131 | - ) { |
|
| 132 | - $search = RequestSearchHelper::get($database) |
|
| 133 | - ->limit($config->getMiserModeLimit()) |
|
| 134 | - ->byStatus(RequestStatus::JOBQUEUE); |
|
| 135 | - |
|
| 136 | - if ($config->getEmailConfirmationEnabled()) { |
|
| 137 | - $search->withConfirmedEmail(); |
|
| 138 | - } |
|
| 139 | - |
|
| 140 | - /** @var Request[] $results */ |
|
| 141 | - $results = $search->getRecordCount($requestCount)->fetch(); |
|
| 142 | - |
|
| 143 | - if($requestCount > 0) { |
|
| 144 | - $requestSectionData['Requests queued in the Job Queue'] = array( |
|
| 145 | - 'requests' => $this->prepareRequestData($results), |
|
| 146 | - 'total' => $requestCount, |
|
| 147 | - 'api' => 'JobQueue', |
|
| 148 | - 'type' => 'JobQueue', |
|
| 149 | - 'special' => 'Job Queue', |
|
| 150 | - 'help' => 'This section lists all the requests which are currently waiting to be created by the tool. Requests should automatically disappear from here within a few minutes.', |
|
| 151 | - 'showAll' => false |
|
| 152 | - ); |
|
| 153 | - } |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - /** |
|
| 157 | - * @param PdoDatabase $database |
|
| 158 | - * @param SiteConfiguration $config |
|
| 159 | - * @param $requestSectionData |
|
| 160 | - */ |
|
| 161 | - private function setupStatusSections( |
|
| 162 | - PdoDatabase $database, |
|
| 163 | - SiteConfiguration $config, |
|
| 164 | - &$requestSectionData |
|
| 165 | - ) { |
|
| 166 | - $search = RequestSearchHelper::get($database)->limit($config->getMiserModeLimit())->notHospitalised(); |
|
| 167 | - |
|
| 168 | - if ($config->getEmailConfirmationEnabled()) { |
|
| 169 | - $search->withConfirmedEmail(); |
|
| 170 | - } |
|
| 171 | - |
|
| 172 | - $allRequestStates = $config->getRequestStates(); |
|
| 173 | - $requestsByStatus = $search->fetchByStatus(array_keys($allRequestStates)); |
|
| 174 | - |
|
| 175 | - foreach ($allRequestStates as $requestState => $requestStateConfig) { |
|
| 176 | - |
|
| 177 | - $requestSectionData[$requestStateConfig['header']] = array( |
|
| 178 | - 'requests' => $this->prepareRequestData($requestsByStatus[$requestState]['data']), |
|
| 179 | - 'total' => $requestsByStatus[$requestState]['count'], |
|
| 180 | - 'api' => $requestStateConfig['api'], |
|
| 181 | - 'type' => $requestState, |
|
| 182 | - 'special' => null, |
|
| 183 | - 'help' => $requestStateConfig['queuehelp'], |
|
| 184 | - 'showAll' => true |
|
| 185 | - ); |
|
| 186 | - } |
|
| 187 | - } |
|
| 79 | + $statement = $database->prepare($query); |
|
| 80 | + $statement->execute(); |
|
| 81 | + |
|
| 82 | + $last5result = $statement->fetchAll(PDO::FETCH_ASSOC); |
|
| 83 | + |
|
| 84 | + $this->assign('lastFive', $last5result); |
|
| 85 | + } |
|
| 86 | + |
|
| 87 | + /** |
|
| 88 | + * @param PdoDatabase $database |
|
| 89 | + * @param SiteConfiguration $config |
|
| 90 | + * @param $requestSectionData |
|
| 91 | + */ |
|
| 92 | + private function setupHospitalQueue( |
|
| 93 | + PdoDatabase $database, |
|
| 94 | + SiteConfiguration $config, |
|
| 95 | + &$requestSectionData |
|
| 96 | + ) { |
|
| 97 | + $search = RequestSearchHelper::get($database) |
|
| 98 | + ->limit($config->getMiserModeLimit()) |
|
| 99 | + ->excludingStatus('Closed') |
|
| 100 | + ->isHospitalised(); |
|
| 101 | + |
|
| 102 | + if ($config->getEmailConfirmationEnabled()) { |
|
| 103 | + $search->withConfirmedEmail(); |
|
| 104 | + } |
|
| 105 | + |
|
| 106 | + /** @var Request[] $results */ |
|
| 107 | + $results = $search->getRecordCount($requestCount)->fetch(); |
|
| 108 | + |
|
| 109 | + if($requestCount > 0) { |
|
| 110 | + $requestSectionData['Hospital - Requests failed auto-creation'] = array( |
|
| 111 | + 'requests' => $this->prepareRequestData($results), |
|
| 112 | + 'total' => $requestCount, |
|
| 113 | + 'api' => 'hospital', |
|
| 114 | + 'type' => 'hospital', |
|
| 115 | + 'special' => 'Job Queue', |
|
| 116 | + 'help' => 'This queue lists all the requests which have been attempted to be created in the background, but for which this has failed for one reason or another. Check the job queue to find the error. Requests here may need to be created manually, or it may be possible to re-queue the request for auto-creation by the tool, or it may have been created already. Use your own technical discretion here.', |
|
| 117 | + 'showAll' => false |
|
| 118 | + ); |
|
| 119 | + } |
|
| 120 | + } |
|
| 121 | + |
|
| 122 | + /** |
|
| 123 | + * @param PdoDatabase $database |
|
| 124 | + * @param SiteConfiguration $config |
|
| 125 | + * @param $requestSectionData |
|
| 126 | + */ |
|
| 127 | + private function setupJobQueue( |
|
| 128 | + PdoDatabase $database, |
|
| 129 | + SiteConfiguration $config, |
|
| 130 | + &$requestSectionData |
|
| 131 | + ) { |
|
| 132 | + $search = RequestSearchHelper::get($database) |
|
| 133 | + ->limit($config->getMiserModeLimit()) |
|
| 134 | + ->byStatus(RequestStatus::JOBQUEUE); |
|
| 135 | + |
|
| 136 | + if ($config->getEmailConfirmationEnabled()) { |
|
| 137 | + $search->withConfirmedEmail(); |
|
| 138 | + } |
|
| 139 | + |
|
| 140 | + /** @var Request[] $results */ |
|
| 141 | + $results = $search->getRecordCount($requestCount)->fetch(); |
|
| 142 | + |
|
| 143 | + if($requestCount > 0) { |
|
| 144 | + $requestSectionData['Requests queued in the Job Queue'] = array( |
|
| 145 | + 'requests' => $this->prepareRequestData($results), |
|
| 146 | + 'total' => $requestCount, |
|
| 147 | + 'api' => 'JobQueue', |
|
| 148 | + 'type' => 'JobQueue', |
|
| 149 | + 'special' => 'Job Queue', |
|
| 150 | + 'help' => 'This section lists all the requests which are currently waiting to be created by the tool. Requests should automatically disappear from here within a few minutes.', |
|
| 151 | + 'showAll' => false |
|
| 152 | + ); |
|
| 153 | + } |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + /** |
|
| 157 | + * @param PdoDatabase $database |
|
| 158 | + * @param SiteConfiguration $config |
|
| 159 | + * @param $requestSectionData |
|
| 160 | + */ |
|
| 161 | + private function setupStatusSections( |
|
| 162 | + PdoDatabase $database, |
|
| 163 | + SiteConfiguration $config, |
|
| 164 | + &$requestSectionData |
|
| 165 | + ) { |
|
| 166 | + $search = RequestSearchHelper::get($database)->limit($config->getMiserModeLimit())->notHospitalised(); |
|
| 167 | + |
|
| 168 | + if ($config->getEmailConfirmationEnabled()) { |
|
| 169 | + $search->withConfirmedEmail(); |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + $allRequestStates = $config->getRequestStates(); |
|
| 173 | + $requestsByStatus = $search->fetchByStatus(array_keys($allRequestStates)); |
|
| 174 | + |
|
| 175 | + foreach ($allRequestStates as $requestState => $requestStateConfig) { |
|
| 176 | + |
|
| 177 | + $requestSectionData[$requestStateConfig['header']] = array( |
|
| 178 | + 'requests' => $this->prepareRequestData($requestsByStatus[$requestState]['data']), |
|
| 179 | + 'total' => $requestsByStatus[$requestState]['count'], |
|
| 180 | + 'api' => $requestStateConfig['api'], |
|
| 181 | + 'type' => $requestState, |
|
| 182 | + 'special' => null, |
|
| 183 | + 'help' => $requestStateConfig['queuehelp'], |
|
| 184 | + 'showAll' => true |
|
| 185 | + ); |
|
| 186 | + } |
|
| 187 | + } |
|
| 188 | 188 | } |
@@ -106,7 +106,7 @@ discard block |
||
| 106 | 106 | /** @var Request[] $results */ |
| 107 | 107 | $results = $search->getRecordCount($requestCount)->fetch(); |
| 108 | 108 | |
| 109 | - if($requestCount > 0) { |
|
| 109 | + if ($requestCount > 0) { |
|
| 110 | 110 | $requestSectionData['Hospital - Requests failed auto-creation'] = array( |
| 111 | 111 | 'requests' => $this->prepareRequestData($results), |
| 112 | 112 | 'total' => $requestCount, |
@@ -140,7 +140,7 @@ discard block |
||
| 140 | 140 | /** @var Request[] $results */ |
| 141 | 141 | $results = $search->getRecordCount($requestCount)->fetch(); |
| 142 | 142 | |
| 143 | - if($requestCount > 0) { |
|
| 143 | + if ($requestCount > 0) { |
|
| 144 | 144 | $requestSectionData['Requests queued in the Job Queue'] = array( |
| 145 | 145 | 'requests' => $this->prepareRequestData($results), |
| 146 | 146 | 'total' => $requestCount, |
@@ -13,31 +13,31 @@ |
||
| 13 | 13 | |
| 14 | 14 | class PagePasswordLogin extends LoginCredentialPageBase |
| 15 | 15 | { |
| 16 | - protected function providerSpecificSetup() |
|
| 17 | - { |
|
| 18 | - list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
|
| 19 | - |
|
| 20 | - if($partialId !== null && $partialStage > 1) { |
|
| 21 | - $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority'; |
|
| 22 | - $statement = $this->getDatabase()->prepare($sql); |
|
| 23 | - $statement->execute(array(':user' => $partialId, ':stage' => $partialStage)); |
|
| 24 | - $nextStage = $statement->fetchColumn(); |
|
| 25 | - $statement->closeCursor(); |
|
| 26 | - |
|
| 27 | - $this->redirect("login/" . $this->nextPageMap[$nextStage]); |
|
| 28 | - return; |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - $this->setTemplate('login/password.tpl'); |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - protected function getProviderCredentials() |
|
| 35 | - { |
|
| 36 | - $password = WebRequest::postString("password"); |
|
| 37 | - if ($password === null || $password === "") { |
|
| 38 | - throw new ApplicationLogicException("No password specified"); |
|
| 39 | - } |
|
| 40 | - |
|
| 41 | - return $password; |
|
| 42 | - } |
|
| 16 | + protected function providerSpecificSetup() |
|
| 17 | + { |
|
| 18 | + list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
|
| 19 | + |
|
| 20 | + if($partialId !== null && $partialStage > 1) { |
|
| 21 | + $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority'; |
|
| 22 | + $statement = $this->getDatabase()->prepare($sql); |
|
| 23 | + $statement->execute(array(':user' => $partialId, ':stage' => $partialStage)); |
|
| 24 | + $nextStage = $statement->fetchColumn(); |
|
| 25 | + $statement->closeCursor(); |
|
| 26 | + |
|
| 27 | + $this->redirect("login/" . $this->nextPageMap[$nextStage]); |
|
| 28 | + return; |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + $this->setTemplate('login/password.tpl'); |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + protected function getProviderCredentials() |
|
| 35 | + { |
|
| 36 | + $password = WebRequest::postString("password"); |
|
| 37 | + if ($password === null || $password === "") { |
|
| 38 | + throw new ApplicationLogicException("No password specified"); |
|
| 39 | + } |
|
| 40 | + |
|
| 41 | + return $password; |
|
| 42 | + } |
|
| 43 | 43 | } |
| 44 | 44 | \ No newline at end of file |
@@ -17,7 +17,7 @@ |
||
| 17 | 17 | { |
| 18 | 18 | list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
| 19 | 19 | |
| 20 | - if($partialId !== null && $partialStage > 1) { |
|
| 20 | + if ($partialId !== null && $partialStage > 1) { |
|
| 21 | 21 | $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority'; |
| 22 | 22 | $statement = $this->getDatabase()->prepare($sql); |
| 23 | 23 | $statement->execute(array(':user' => $partialId, ':stage' => $partialStage)); |
@@ -21,315 +21,315 @@ |
||
| 21 | 21 | |
| 22 | 22 | abstract class LoginCredentialPageBase extends InternalPageBase |
| 23 | 23 | { |
| 24 | - /** @var User */ |
|
| 25 | - protected $partialUser = null; |
|
| 26 | - protected $nextPageMap = array( |
|
| 27 | - 'yubikeyotp' => 'otp', |
|
| 28 | - 'totp' => 'otp', |
|
| 29 | - 'scratch' => 'otp', |
|
| 30 | - 'u2f' => 'u2f', |
|
| 31 | - ); |
|
| 32 | - protected $names = array( |
|
| 33 | - 'yubikeyotp' => 'Yubikey OTP', |
|
| 34 | - 'totp' => 'TOTP (phone code generator)', |
|
| 35 | - 'scratch' => 'scratch token', |
|
| 36 | - 'u2f' => 'U2F security token', |
|
| 37 | - ); |
|
| 38 | - |
|
| 39 | - /** |
|
| 40 | - * Main function for this page, when no specific actions are called. |
|
| 41 | - * @return void |
|
| 42 | - */ |
|
| 43 | - protected function main() |
|
| 44 | - { |
|
| 45 | - if (!$this->enforceHttps()) { |
|
| 46 | - return; |
|
| 47 | - } |
|
| 48 | - |
|
| 49 | - if (WebRequest::wasPosted()) { |
|
| 50 | - $this->validateCSRFToken(); |
|
| 51 | - |
|
| 52 | - $database = $this->getDatabase(); |
|
| 53 | - try { |
|
| 54 | - list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
|
| 55 | - |
|
| 56 | - if ($partialStage === null) { |
|
| 57 | - $partialStage = 1; |
|
| 58 | - } |
|
| 59 | - |
|
| 60 | - if ($partialId === null) { |
|
| 61 | - $username = WebRequest::postString('username'); |
|
| 62 | - |
|
| 63 | - if ($username === null || trim($username) === '') { |
|
| 64 | - throw new ApplicationLogicException('No username specified.'); |
|
| 65 | - } |
|
| 66 | - |
|
| 67 | - $user = User::getByUsername($username, $database); |
|
| 68 | - } |
|
| 69 | - else { |
|
| 70 | - $user = User::getById($partialId, $database); |
|
| 71 | - } |
|
| 72 | - |
|
| 73 | - if ($user === false) { |
|
| 74 | - throw new ApplicationLogicException("Authentication failed"); |
|
| 75 | - } |
|
| 76 | - |
|
| 77 | - $authMan = new AuthenticationManager($database, $this->getSiteConfiguration(), |
|
| 78 | - $this->getHttpHelper()); |
|
| 79 | - |
|
| 80 | - $credential = $this->getProviderCredentials(); |
|
| 81 | - |
|
| 82 | - $authResult = $authMan->authenticate($user, $credential, $partialStage); |
|
| 83 | - |
|
| 84 | - if ($authResult === AuthenticationManager::AUTH_FAIL) { |
|
| 85 | - throw new ApplicationLogicException("Authentication failed"); |
|
| 86 | - } |
|
| 87 | - |
|
| 88 | - if ($authResult === AuthenticationManager::AUTH_REQUIRE_NEXT_STAGE) { |
|
| 89 | - $this->processJumpNextStage($user, $partialStage, $database); |
|
| 90 | - |
|
| 91 | - return; |
|
| 92 | - } |
|
| 93 | - |
|
| 94 | - if ($authResult === AuthenticationManager::AUTH_OK) { |
|
| 95 | - $this->processLoginSuccess($user); |
|
| 96 | - |
|
| 97 | - return; |
|
| 98 | - } |
|
| 99 | - } |
|
| 100 | - catch (ApplicationLogicException $ex) { |
|
| 101 | - WebRequest::clearAuthPartialLogin(); |
|
| 102 | - |
|
| 103 | - SessionAlert::error($ex->getMessage()); |
|
| 104 | - $this->redirect('login'); |
|
| 105 | - |
|
| 106 | - return; |
|
| 107 | - } |
|
| 108 | - } |
|
| 109 | - else { |
|
| 110 | - $this->assign('showSignIn', true); |
|
| 111 | - |
|
| 112 | - $this->setupPartial(); |
|
| 113 | - $this->assignCSRFToken(); |
|
| 114 | - $this->providerSpecificSetup(); |
|
| 115 | - } |
|
| 116 | - } |
|
| 117 | - |
|
| 118 | - protected function isProtectedPage() |
|
| 119 | - { |
|
| 120 | - return false; |
|
| 121 | - } |
|
| 122 | - |
|
| 123 | - /** |
|
| 124 | - * Enforces HTTPS on the login form |
|
| 125 | - * |
|
| 126 | - * @return bool |
|
| 127 | - */ |
|
| 128 | - private function enforceHttps() |
|
| 129 | - { |
|
| 130 | - if ($this->getSiteConfiguration()->getUseStrictTransportSecurity() !== false) { |
|
| 131 | - if (WebRequest::isHttps()) { |
|
| 132 | - // Client can clearly use HTTPS, so let's enforce it for all connections. |
|
| 133 | - $this->headerQueue[] = "Strict-Transport-Security: max-age=15768000"; |
|
| 134 | - } |
|
| 135 | - else { |
|
| 136 | - // This is the login form, not the request form. We need protection here. |
|
| 137 | - $this->redirectUrl('https://' . WebRequest::serverName() . WebRequest::requestUri()); |
|
| 138 | - |
|
| 139 | - return false; |
|
| 140 | - } |
|
| 141 | - } |
|
| 142 | - |
|
| 143 | - return true; |
|
| 144 | - } |
|
| 145 | - |
|
| 146 | - protected abstract function providerSpecificSetup(); |
|
| 147 | - |
|
| 148 | - protected function setupPartial() |
|
| 149 | - { |
|
| 150 | - $database = $this->getDatabase(); |
|
| 151 | - |
|
| 152 | - // default stuff |
|
| 153 | - $this->assign('alternatives', array()); // 'u2f' => array('U2F token'), 'otp' => array('TOTP', 'scratch', 'yubiotp'))); |
|
| 154 | - |
|
| 155 | - // is this stage one? |
|
| 156 | - list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
|
| 157 | - if ($partialStage === null || $partialId === null) { |
|
| 158 | - WebRequest::clearAuthPartialLogin(); |
|
| 159 | - } |
|
| 160 | - |
|
| 161 | - // Check to see if we have a partial login in progress |
|
| 162 | - $username = null; |
|
| 163 | - if ($partialId !== null) { |
|
| 164 | - // Yes, enforce this username |
|
| 165 | - $this->partialUser = User::getById($partialId, $database); |
|
| 166 | - $username = $this->partialUser->getUsername(); |
|
| 167 | - |
|
| 168 | - $this->setupAlternates($this->partialUser, $partialStage, $database); |
|
| 169 | - } |
|
| 170 | - else { |
|
| 171 | - // No, see if we've preloaded a username |
|
| 172 | - $preloadUsername = WebRequest::getString('tplUsername'); |
|
| 173 | - if ($preloadUsername !== null) { |
|
| 174 | - $username = $preloadUsername; |
|
| 175 | - } |
|
| 176 | - } |
|
| 177 | - |
|
| 178 | - if ($partialStage === null) { |
|
| 179 | - $partialStage = 1; |
|
| 180 | - } |
|
| 181 | - |
|
| 182 | - $this->assign('partialStage', $partialStage); |
|
| 183 | - $this->assign('username', $username); |
|
| 184 | - } |
|
| 185 | - |
|
| 186 | - /** |
|
| 187 | - * Redirect the user back to wherever they came from after a successful login |
|
| 188 | - * |
|
| 189 | - * @param User $user |
|
| 190 | - */ |
|
| 191 | - protected function goBackWhenceYouCame(User $user) |
|
| 192 | - { |
|
| 193 | - // Redirect to wherever the user came from |
|
| 194 | - $redirectDestination = WebRequest::clearPostLoginRedirect(); |
|
| 195 | - if ($redirectDestination !== null) { |
|
| 196 | - $this->redirectUrl($redirectDestination); |
|
| 197 | - } |
|
| 198 | - else { |
|
| 199 | - if ($user->isNewUser()) { |
|
| 200 | - // home page isn't allowed, go to preferences instead |
|
| 201 | - $this->redirect('preferences'); |
|
| 202 | - } |
|
| 203 | - else { |
|
| 204 | - // go to the home page |
|
| 205 | - $this->redirect(''); |
|
| 206 | - } |
|
| 207 | - } |
|
| 208 | - } |
|
| 209 | - |
|
| 210 | - private function processLoginSuccess(User $user) |
|
| 211 | - { |
|
| 212 | - // Touch force logout |
|
| 213 | - $user->setForceLogout(false); |
|
| 214 | - $user->save(); |
|
| 215 | - |
|
| 216 | - $oauth = new OAuthUserHelper($user, $this->getDatabase(), $this->getOAuthProtocolHelper(), |
|
| 217 | - $this->getSiteConfiguration()); |
|
| 218 | - |
|
| 219 | - if ($oauth->isFullyLinked()) { |
|
| 220 | - try { |
|
| 221 | - // Reload the user's identity ticket. |
|
| 222 | - $oauth->refreshIdentity(); |
|
| 223 | - |
|
| 224 | - // Check for blocks |
|
| 225 | - if ($oauth->getIdentity()->getBlocked()) { |
|
| 226 | - // blocked! |
|
| 227 | - SessionAlert::error("You are currently blocked on-wiki. You will not be able to log in until you are unblocked."); |
|
| 228 | - $this->redirect('login'); |
|
| 229 | - |
|
| 230 | - return; |
|
| 231 | - } |
|
| 232 | - } |
|
| 233 | - catch (OAuthException $ex) { |
|
| 234 | - // Oops. Refreshing ticket failed. Force a re-auth. |
|
| 235 | - $authoriseUrl = $oauth->getRequestToken(); |
|
| 236 | - WebRequest::setOAuthPartialLogin($user); |
|
| 237 | - $this->redirectUrl($authoriseUrl); |
|
| 238 | - |
|
| 239 | - return; |
|
| 240 | - } |
|
| 241 | - } |
|
| 242 | - |
|
| 243 | - if (($this->getSiteConfiguration()->getEnforceOAuth() && !$oauth->isFullyLinked()) |
|
| 244 | - || $oauth->isPartiallyLinked() |
|
| 245 | - ) { |
|
| 246 | - $authoriseUrl = $oauth->getRequestToken(); |
|
| 247 | - WebRequest::setOAuthPartialLogin($user); |
|
| 248 | - $this->redirectUrl($authoriseUrl); |
|
| 249 | - |
|
| 250 | - return; |
|
| 251 | - } |
|
| 252 | - |
|
| 253 | - WebRequest::setLoggedInUser($user); |
|
| 254 | - |
|
| 255 | - $this->goBackWhenceYouCame($user); |
|
| 256 | - } |
|
| 257 | - |
|
| 258 | - protected abstract function getProviderCredentials(); |
|
| 259 | - |
|
| 260 | - /** |
|
| 261 | - * @param User $user |
|
| 262 | - * @param int $partialStage |
|
| 263 | - * @param PdoDatabase $database |
|
| 264 | - * |
|
| 265 | - * @throws ApplicationLogicException |
|
| 266 | - */ |
|
| 267 | - private function processJumpNextStage(User $user, $partialStage, PdoDatabase $database) |
|
| 268 | - { |
|
| 269 | - WebRequest::setAuthPartialLogin($user->getId(), $partialStage + 1); |
|
| 270 | - |
|
| 271 | - $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority'; |
|
| 272 | - $statement = $database->prepare($sql); |
|
| 273 | - $statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage + 1)); |
|
| 274 | - $nextStage = $statement->fetchColumn(); |
|
| 275 | - $statement->closeCursor(); |
|
| 276 | - |
|
| 277 | - if (!isset($this->nextPageMap[$nextStage])) { |
|
| 278 | - throw new ApplicationLogicException('Unknown page handler for next authentication stage.'); |
|
| 279 | - } |
|
| 280 | - |
|
| 281 | - $this->redirect("login/" . $this->nextPageMap[$nextStage]); |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - private function setupAlternates(User $user, $partialStage, PdoDatabase $database) |
|
| 285 | - { |
|
| 286 | - // get the providers available |
|
| 287 | - $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0'; |
|
| 288 | - $statement = $database->prepare($sql); |
|
| 289 | - $statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage)); |
|
| 290 | - $alternates = $statement->fetchAll(PDO::FETCH_COLUMN); |
|
| 291 | - |
|
| 292 | - $types = array(); |
|
| 293 | - foreach ($alternates as $item) { |
|
| 294 | - $type = $this->nextPageMap[$item]; |
|
| 295 | - if (!isset($types[$type])) { |
|
| 296 | - $types[$type] = array(); |
|
| 297 | - } |
|
| 298 | - |
|
| 299 | - $types[$type][] = $item; |
|
| 300 | - } |
|
| 301 | - |
|
| 302 | - $userOptions = array(); |
|
| 303 | - if (get_called_class() === PageOtpLogin::class) { |
|
| 304 | - $userOptions = $this->setupUserOptionsForType($types, 'u2f', $userOptions); |
|
| 305 | - } |
|
| 306 | - |
|
| 307 | - if (get_called_class() === PageU2FLogin::class) { |
|
| 308 | - $userOptions = $this->setupUserOptionsForType($types, 'otp', $userOptions); |
|
| 309 | - } |
|
| 310 | - |
|
| 311 | - $this->assign('alternatives', $userOptions); |
|
| 312 | - } |
|
| 313 | - |
|
| 314 | - /** |
|
| 315 | - * @param $types |
|
| 316 | - * @param $type |
|
| 317 | - * @param $userOptions |
|
| 318 | - * |
|
| 319 | - * @return mixed |
|
| 320 | - */ |
|
| 321 | - private function setupUserOptionsForType($types, $type, $userOptions) |
|
| 322 | - { |
|
| 323 | - if (isset($types[$type])) { |
|
| 324 | - $options = $types[$type]; |
|
| 325 | - |
|
| 326 | - array_walk($options, function(&$val) { |
|
| 327 | - $val = $this->names[$val]; |
|
| 328 | - }); |
|
| 329 | - |
|
| 330 | - $userOptions[$type] = $options; |
|
| 331 | - } |
|
| 332 | - |
|
| 333 | - return $userOptions; |
|
| 334 | - } |
|
| 24 | + /** @var User */ |
|
| 25 | + protected $partialUser = null; |
|
| 26 | + protected $nextPageMap = array( |
|
| 27 | + 'yubikeyotp' => 'otp', |
|
| 28 | + 'totp' => 'otp', |
|
| 29 | + 'scratch' => 'otp', |
|
| 30 | + 'u2f' => 'u2f', |
|
| 31 | + ); |
|
| 32 | + protected $names = array( |
|
| 33 | + 'yubikeyotp' => 'Yubikey OTP', |
|
| 34 | + 'totp' => 'TOTP (phone code generator)', |
|
| 35 | + 'scratch' => 'scratch token', |
|
| 36 | + 'u2f' => 'U2F security token', |
|
| 37 | + ); |
|
| 38 | + |
|
| 39 | + /** |
|
| 40 | + * Main function for this page, when no specific actions are called. |
|
| 41 | + * @return void |
|
| 42 | + */ |
|
| 43 | + protected function main() |
|
| 44 | + { |
|
| 45 | + if (!$this->enforceHttps()) { |
|
| 46 | + return; |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + if (WebRequest::wasPosted()) { |
|
| 50 | + $this->validateCSRFToken(); |
|
| 51 | + |
|
| 52 | + $database = $this->getDatabase(); |
|
| 53 | + try { |
|
| 54 | + list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
|
| 55 | + |
|
| 56 | + if ($partialStage === null) { |
|
| 57 | + $partialStage = 1; |
|
| 58 | + } |
|
| 59 | + |
|
| 60 | + if ($partialId === null) { |
|
| 61 | + $username = WebRequest::postString('username'); |
|
| 62 | + |
|
| 63 | + if ($username === null || trim($username) === '') { |
|
| 64 | + throw new ApplicationLogicException('No username specified.'); |
|
| 65 | + } |
|
| 66 | + |
|
| 67 | + $user = User::getByUsername($username, $database); |
|
| 68 | + } |
|
| 69 | + else { |
|
| 70 | + $user = User::getById($partialId, $database); |
|
| 71 | + } |
|
| 72 | + |
|
| 73 | + if ($user === false) { |
|
| 74 | + throw new ApplicationLogicException("Authentication failed"); |
|
| 75 | + } |
|
| 76 | + |
|
| 77 | + $authMan = new AuthenticationManager($database, $this->getSiteConfiguration(), |
|
| 78 | + $this->getHttpHelper()); |
|
| 79 | + |
|
| 80 | + $credential = $this->getProviderCredentials(); |
|
| 81 | + |
|
| 82 | + $authResult = $authMan->authenticate($user, $credential, $partialStage); |
|
| 83 | + |
|
| 84 | + if ($authResult === AuthenticationManager::AUTH_FAIL) { |
|
| 85 | + throw new ApplicationLogicException("Authentication failed"); |
|
| 86 | + } |
|
| 87 | + |
|
| 88 | + if ($authResult === AuthenticationManager::AUTH_REQUIRE_NEXT_STAGE) { |
|
| 89 | + $this->processJumpNextStage($user, $partialStage, $database); |
|
| 90 | + |
|
| 91 | + return; |
|
| 92 | + } |
|
| 93 | + |
|
| 94 | + if ($authResult === AuthenticationManager::AUTH_OK) { |
|
| 95 | + $this->processLoginSuccess($user); |
|
| 96 | + |
|
| 97 | + return; |
|
| 98 | + } |
|
| 99 | + } |
|
| 100 | + catch (ApplicationLogicException $ex) { |
|
| 101 | + WebRequest::clearAuthPartialLogin(); |
|
| 102 | + |
|
| 103 | + SessionAlert::error($ex->getMessage()); |
|
| 104 | + $this->redirect('login'); |
|
| 105 | + |
|
| 106 | + return; |
|
| 107 | + } |
|
| 108 | + } |
|
| 109 | + else { |
|
| 110 | + $this->assign('showSignIn', true); |
|
| 111 | + |
|
| 112 | + $this->setupPartial(); |
|
| 113 | + $this->assignCSRFToken(); |
|
| 114 | + $this->providerSpecificSetup(); |
|
| 115 | + } |
|
| 116 | + } |
|
| 117 | + |
|
| 118 | + protected function isProtectedPage() |
|
| 119 | + { |
|
| 120 | + return false; |
|
| 121 | + } |
|
| 122 | + |
|
| 123 | + /** |
|
| 124 | + * Enforces HTTPS on the login form |
|
| 125 | + * |
|
| 126 | + * @return bool |
|
| 127 | + */ |
|
| 128 | + private function enforceHttps() |
|
| 129 | + { |
|
| 130 | + if ($this->getSiteConfiguration()->getUseStrictTransportSecurity() !== false) { |
|
| 131 | + if (WebRequest::isHttps()) { |
|
| 132 | + // Client can clearly use HTTPS, so let's enforce it for all connections. |
|
| 133 | + $this->headerQueue[] = "Strict-Transport-Security: max-age=15768000"; |
|
| 134 | + } |
|
| 135 | + else { |
|
| 136 | + // This is the login form, not the request form. We need protection here. |
|
| 137 | + $this->redirectUrl('https://' . WebRequest::serverName() . WebRequest::requestUri()); |
|
| 138 | + |
|
| 139 | + return false; |
|
| 140 | + } |
|
| 141 | + } |
|
| 142 | + |
|
| 143 | + return true; |
|
| 144 | + } |
|
| 145 | + |
|
| 146 | + protected abstract function providerSpecificSetup(); |
|
| 147 | + |
|
| 148 | + protected function setupPartial() |
|
| 149 | + { |
|
| 150 | + $database = $this->getDatabase(); |
|
| 151 | + |
|
| 152 | + // default stuff |
|
| 153 | + $this->assign('alternatives', array()); // 'u2f' => array('U2F token'), 'otp' => array('TOTP', 'scratch', 'yubiotp'))); |
|
| 154 | + |
|
| 155 | + // is this stage one? |
|
| 156 | + list($partialId, $partialStage) = WebRequest::getAuthPartialLogin(); |
|
| 157 | + if ($partialStage === null || $partialId === null) { |
|
| 158 | + WebRequest::clearAuthPartialLogin(); |
|
| 159 | + } |
|
| 160 | + |
|
| 161 | + // Check to see if we have a partial login in progress |
|
| 162 | + $username = null; |
|
| 163 | + if ($partialId !== null) { |
|
| 164 | + // Yes, enforce this username |
|
| 165 | + $this->partialUser = User::getById($partialId, $database); |
|
| 166 | + $username = $this->partialUser->getUsername(); |
|
| 167 | + |
|
| 168 | + $this->setupAlternates($this->partialUser, $partialStage, $database); |
|
| 169 | + } |
|
| 170 | + else { |
|
| 171 | + // No, see if we've preloaded a username |
|
| 172 | + $preloadUsername = WebRequest::getString('tplUsername'); |
|
| 173 | + if ($preloadUsername !== null) { |
|
| 174 | + $username = $preloadUsername; |
|
| 175 | + } |
|
| 176 | + } |
|
| 177 | + |
|
| 178 | + if ($partialStage === null) { |
|
| 179 | + $partialStage = 1; |
|
| 180 | + } |
|
| 181 | + |
|
| 182 | + $this->assign('partialStage', $partialStage); |
|
| 183 | + $this->assign('username', $username); |
|
| 184 | + } |
|
| 185 | + |
|
| 186 | + /** |
|
| 187 | + * Redirect the user back to wherever they came from after a successful login |
|
| 188 | + * |
|
| 189 | + * @param User $user |
|
| 190 | + */ |
|
| 191 | + protected function goBackWhenceYouCame(User $user) |
|
| 192 | + { |
|
| 193 | + // Redirect to wherever the user came from |
|
| 194 | + $redirectDestination = WebRequest::clearPostLoginRedirect(); |
|
| 195 | + if ($redirectDestination !== null) { |
|
| 196 | + $this->redirectUrl($redirectDestination); |
|
| 197 | + } |
|
| 198 | + else { |
|
| 199 | + if ($user->isNewUser()) { |
|
| 200 | + // home page isn't allowed, go to preferences instead |
|
| 201 | + $this->redirect('preferences'); |
|
| 202 | + } |
|
| 203 | + else { |
|
| 204 | + // go to the home page |
|
| 205 | + $this->redirect(''); |
|
| 206 | + } |
|
| 207 | + } |
|
| 208 | + } |
|
| 209 | + |
|
| 210 | + private function processLoginSuccess(User $user) |
|
| 211 | + { |
|
| 212 | + // Touch force logout |
|
| 213 | + $user->setForceLogout(false); |
|
| 214 | + $user->save(); |
|
| 215 | + |
|
| 216 | + $oauth = new OAuthUserHelper($user, $this->getDatabase(), $this->getOAuthProtocolHelper(), |
|
| 217 | + $this->getSiteConfiguration()); |
|
| 218 | + |
|
| 219 | + if ($oauth->isFullyLinked()) { |
|
| 220 | + try { |
|
| 221 | + // Reload the user's identity ticket. |
|
| 222 | + $oauth->refreshIdentity(); |
|
| 223 | + |
|
| 224 | + // Check for blocks |
|
| 225 | + if ($oauth->getIdentity()->getBlocked()) { |
|
| 226 | + // blocked! |
|
| 227 | + SessionAlert::error("You are currently blocked on-wiki. You will not be able to log in until you are unblocked."); |
|
| 228 | + $this->redirect('login'); |
|
| 229 | + |
|
| 230 | + return; |
|
| 231 | + } |
|
| 232 | + } |
|
| 233 | + catch (OAuthException $ex) { |
|
| 234 | + // Oops. Refreshing ticket failed. Force a re-auth. |
|
| 235 | + $authoriseUrl = $oauth->getRequestToken(); |
|
| 236 | + WebRequest::setOAuthPartialLogin($user); |
|
| 237 | + $this->redirectUrl($authoriseUrl); |
|
| 238 | + |
|
| 239 | + return; |
|
| 240 | + } |
|
| 241 | + } |
|
| 242 | + |
|
| 243 | + if (($this->getSiteConfiguration()->getEnforceOAuth() && !$oauth->isFullyLinked()) |
|
| 244 | + || $oauth->isPartiallyLinked() |
|
| 245 | + ) { |
|
| 246 | + $authoriseUrl = $oauth->getRequestToken(); |
|
| 247 | + WebRequest::setOAuthPartialLogin($user); |
|
| 248 | + $this->redirectUrl($authoriseUrl); |
|
| 249 | + |
|
| 250 | + return; |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + WebRequest::setLoggedInUser($user); |
|
| 254 | + |
|
| 255 | + $this->goBackWhenceYouCame($user); |
|
| 256 | + } |
|
| 257 | + |
|
| 258 | + protected abstract function getProviderCredentials(); |
|
| 259 | + |
|
| 260 | + /** |
|
| 261 | + * @param User $user |
|
| 262 | + * @param int $partialStage |
|
| 263 | + * @param PdoDatabase $database |
|
| 264 | + * |
|
| 265 | + * @throws ApplicationLogicException |
|
| 266 | + */ |
|
| 267 | + private function processJumpNextStage(User $user, $partialStage, PdoDatabase $database) |
|
| 268 | + { |
|
| 269 | + WebRequest::setAuthPartialLogin($user->getId(), $partialStage + 1); |
|
| 270 | + |
|
| 271 | + $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0 ORDER BY priority'; |
|
| 272 | + $statement = $database->prepare($sql); |
|
| 273 | + $statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage + 1)); |
|
| 274 | + $nextStage = $statement->fetchColumn(); |
|
| 275 | + $statement->closeCursor(); |
|
| 276 | + |
|
| 277 | + if (!isset($this->nextPageMap[$nextStage])) { |
|
| 278 | + throw new ApplicationLogicException('Unknown page handler for next authentication stage.'); |
|
| 279 | + } |
|
| 280 | + |
|
| 281 | + $this->redirect("login/" . $this->nextPageMap[$nextStage]); |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + private function setupAlternates(User $user, $partialStage, PdoDatabase $database) |
|
| 285 | + { |
|
| 286 | + // get the providers available |
|
| 287 | + $sql = 'SELECT type FROM credential WHERE user = :user AND factor = :stage AND disabled = 0'; |
|
| 288 | + $statement = $database->prepare($sql); |
|
| 289 | + $statement->execute(array(':user' => $user->getId(), ':stage' => $partialStage)); |
|
| 290 | + $alternates = $statement->fetchAll(PDO::FETCH_COLUMN); |
|
| 291 | + |
|
| 292 | + $types = array(); |
|
| 293 | + foreach ($alternates as $item) { |
|
| 294 | + $type = $this->nextPageMap[$item]; |
|
| 295 | + if (!isset($types[$type])) { |
|
| 296 | + $types[$type] = array(); |
|
| 297 | + } |
|
| 298 | + |
|
| 299 | + $types[$type][] = $item; |
|
| 300 | + } |
|
| 301 | + |
|
| 302 | + $userOptions = array(); |
|
| 303 | + if (get_called_class() === PageOtpLogin::class) { |
|
| 304 | + $userOptions = $this->setupUserOptionsForType($types, 'u2f', $userOptions); |
|
| 305 | + } |
|
| 306 | + |
|
| 307 | + if (get_called_class() === PageU2FLogin::class) { |
|
| 308 | + $userOptions = $this->setupUserOptionsForType($types, 'otp', $userOptions); |
|
| 309 | + } |
|
| 310 | + |
|
| 311 | + $this->assign('alternatives', $userOptions); |
|
| 312 | + } |
|
| 313 | + |
|
| 314 | + /** |
|
| 315 | + * @param $types |
|
| 316 | + * @param $type |
|
| 317 | + * @param $userOptions |
|
| 318 | + * |
|
| 319 | + * @return mixed |
|
| 320 | + */ |
|
| 321 | + private function setupUserOptionsForType($types, $type, $userOptions) |
|
| 322 | + { |
|
| 323 | + if (isset($types[$type])) { |
|
| 324 | + $options = $types[$type]; |
|
| 325 | + |
|
| 326 | + array_walk($options, function(&$val) { |
|
| 327 | + $val = $this->names[$val]; |
|
| 328 | + }); |
|
| 329 | + |
|
| 330 | + $userOptions[$type] = $options; |
|
| 331 | + } |
|
| 332 | + |
|
| 333 | + return $userOptions; |
|
| 334 | + } |
|
| 335 | 335 | } |
@@ -65,8 +65,7 @@ discard block |
||
| 65 | 65 | } |
| 66 | 66 | |
| 67 | 67 | $user = User::getByUsername($username, $database); |
| 68 | - } |
|
| 69 | - else { |
|
| 68 | + } else { |
|
| 70 | 69 | $user = User::getById($partialId, $database); |
| 71 | 70 | } |
| 72 | 71 | |
@@ -105,8 +104,7 @@ discard block |
||
| 105 | 104 | |
| 106 | 105 | return; |
| 107 | 106 | } |
| 108 | - } |
|
| 109 | - else { |
|
| 107 | + } else { |
|
| 110 | 108 | $this->assign('showSignIn', true); |
| 111 | 109 | |
| 112 | 110 | $this->setupPartial(); |
@@ -131,8 +129,7 @@ discard block |
||
| 131 | 129 | if (WebRequest::isHttps()) { |
| 132 | 130 | // Client can clearly use HTTPS, so let's enforce it for all connections. |
| 133 | 131 | $this->headerQueue[] = "Strict-Transport-Security: max-age=15768000"; |
| 134 | - } |
|
| 135 | - else { |
|
| 132 | + } else { |
|
| 136 | 133 | // This is the login form, not the request form. We need protection here. |
| 137 | 134 | $this->redirectUrl('https://' . WebRequest::serverName() . WebRequest::requestUri()); |
| 138 | 135 | |
@@ -166,8 +163,7 @@ discard block |
||
| 166 | 163 | $username = $this->partialUser->getUsername(); |
| 167 | 164 | |
| 168 | 165 | $this->setupAlternates($this->partialUser, $partialStage, $database); |
| 169 | - } |
|
| 170 | - else { |
|
| 166 | + } else { |
|
| 171 | 167 | // No, see if we've preloaded a username |
| 172 | 168 | $preloadUsername = WebRequest::getString('tplUsername'); |
| 173 | 169 | if ($preloadUsername !== null) { |
@@ -194,13 +190,11 @@ discard block |
||
| 194 | 190 | $redirectDestination = WebRequest::clearPostLoginRedirect(); |
| 195 | 191 | if ($redirectDestination !== null) { |
| 196 | 192 | $this->redirectUrl($redirectDestination); |
| 197 | - } |
|
| 198 | - else { |
|
| 193 | + } else { |
|
| 199 | 194 | if ($user->isNewUser()) { |
| 200 | 195 | // home page isn't allowed, go to preferences instead |
| 201 | 196 | $this->redirect('preferences'); |
| 202 | - } |
|
| 203 | - else { |
|
| 197 | + } else { |
|
| 204 | 198 | // go to the home page |
| 205 | 199 | $this->redirect(''); |
| 206 | 200 | } |
@@ -323,7 +317,8 @@ discard block |
||
| 323 | 317 | if (isset($types[$type])) { |
| 324 | 318 | $options = $types[$type]; |
| 325 | 319 | |
| 326 | - array_walk($options, function(&$val) { |
|
| 320 | + array_walk($options, function(&$val) |
|
| 321 | + { |
|
| 327 | 322 | $val = $this->names[$val]; |
| 328 | 323 | }); |
| 329 | 324 | |
@@ -13,18 +13,18 @@ |
||
| 13 | 13 | |
| 14 | 14 | class PageOtpLogin extends LoginCredentialPageBase |
| 15 | 15 | { |
| 16 | - protected function providerSpecificSetup() |
|
| 17 | - { |
|
| 18 | - $this->setTemplate('login/otp.tpl'); |
|
| 19 | - } |
|
| 16 | + protected function providerSpecificSetup() |
|
| 17 | + { |
|
| 18 | + $this->setTemplate('login/otp.tpl'); |
|
| 19 | + } |
|
| 20 | 20 | |
| 21 | - protected function getProviderCredentials() |
|
| 22 | - { |
|
| 23 | - $otp = WebRequest::postString("otp"); |
|
| 24 | - if ($otp === null || $otp === "") { |
|
| 25 | - throw new ApplicationLogicException("No one-time code specified"); |
|
| 26 | - } |
|
| 21 | + protected function getProviderCredentials() |
|
| 22 | + { |
|
| 23 | + $otp = WebRequest::postString("otp"); |
|
| 24 | + if ($otp === null || $otp === "") { |
|
| 25 | + throw new ApplicationLogicException("No one-time code specified"); |
|
| 26 | + } |
|
| 27 | 27 | |
| 28 | - return $otp; |
|
| 29 | - } |
|
| 28 | + return $otp; |
|
| 29 | + } |
|
| 30 | 30 | } |
| 31 | 31 | \ No newline at end of file |
@@ -14,20 +14,20 @@ discard block |
||
| 14 | 14 | |
| 15 | 15 | class PageU2FLogin extends LoginCredentialPageBase |
| 16 | 16 | { |
| 17 | - protected function providerSpecificSetup() |
|
| 18 | - { |
|
| 19 | - $this->assign('showSignIn', false); |
|
| 20 | - $this->setTemplate('login/u2f.tpl'); |
|
| 17 | + protected function providerSpecificSetup() |
|
| 18 | + { |
|
| 19 | + $this->assign('showSignIn', false); |
|
| 20 | + $this->setTemplate('login/u2f.tpl'); |
|
| 21 | 21 | |
| 22 | - if ($this->partialUser === null) { |
|
| 23 | - throw new ApplicationLogicException("U2F cannot be first-stage authentication"); |
|
| 24 | - } |
|
| 22 | + if ($this->partialUser === null) { |
|
| 23 | + throw new ApplicationLogicException("U2F cannot be first-stage authentication"); |
|
| 24 | + } |
|
| 25 | 25 | |
| 26 | - $u2f = new U2FCredentialProvider($this->getDatabase(), $this->getSiteConfiguration()); |
|
| 27 | - $authData = json_encode($u2f->getAuthenticationData($this->partialUser)); |
|
| 26 | + $u2f = new U2FCredentialProvider($this->getDatabase(), $this->getSiteConfiguration()); |
|
| 27 | + $authData = json_encode($u2f->getAuthenticationData($this->partialUser)); |
|
| 28 | 28 | |
| 29 | - $this->addJs('/vendor/yubico/u2flib-server/examples/assets/u2f-api.js'); |
|
| 30 | - $this->setTailScript($this->getCspManager()->getNonce(), <<<JS |
|
| 29 | + $this->addJs('/vendor/yubico/u2flib-server/examples/assets/u2f-api.js'); |
|
| 30 | + $this->setTailScript($this->getCspManager()->getNonce(), <<<JS |
|
| 31 | 31 | var request = {$authData}; |
| 32 | 32 | u2f.sign(request, function(data) { |
| 33 | 33 | document.getElementById('authenticate').value=JSON.stringify(data); |
@@ -35,19 +35,19 @@ discard block |
||
| 35 | 35 | document.getElementById('loginForm').submit(); |
| 36 | 36 | }); |
| 37 | 37 | JS |
| 38 | - ); |
|
| 38 | + ); |
|
| 39 | 39 | |
| 40 | - } |
|
| 40 | + } |
|
| 41 | 41 | |
| 42 | - protected function getProviderCredentials() |
|
| 43 | - { |
|
| 44 | - $authenticate = WebRequest::postString("authenticate"); |
|
| 45 | - $request = WebRequest::postString("request"); |
|
| 42 | + protected function getProviderCredentials() |
|
| 43 | + { |
|
| 44 | + $authenticate = WebRequest::postString("authenticate"); |
|
| 45 | + $request = WebRequest::postString("request"); |
|
| 46 | 46 | |
| 47 | - if ($authenticate === null || $authenticate === "" || $request === null || $request === "") { |
|
| 48 | - throw new ApplicationLogicException("No authentication specified"); |
|
| 49 | - } |
|
| 47 | + if ($authenticate === null || $authenticate === "" || $request === null || $request === "") { |
|
| 48 | + throw new ApplicationLogicException("No authentication specified"); |
|
| 49 | + } |
|
| 50 | 50 | |
| 51 | - return array(json_decode($authenticate), json_decode($request), 'u2f'); |
|
| 52 | - } |
|
| 51 | + return array(json_decode($authenticate), json_decode($request), 'u2f'); |
|
| 52 | + } |
|
| 53 | 53 | } |
@@ -18,92 +18,92 @@ |
||
| 18 | 18 | |
| 19 | 19 | class PageOAuthCallback extends InternalPageBase |
| 20 | 20 | { |
| 21 | - /** |
|
| 22 | - * @return bool |
|
| 23 | - */ |
|
| 24 | - protected function isProtectedPage() |
|
| 25 | - { |
|
| 26 | - // This page is critical to ensuring OAuth functionality is operational. |
|
| 27 | - return false; |
|
| 28 | - } |
|
| 21 | + /** |
|
| 22 | + * @return bool |
|
| 23 | + */ |
|
| 24 | + protected function isProtectedPage() |
|
| 25 | + { |
|
| 26 | + // This page is critical to ensuring OAuth functionality is operational. |
|
| 27 | + return false; |
|
| 28 | + } |
|
| 29 | 29 | |
| 30 | - /** |
|
| 31 | - * Main function for this page, when no specific actions are called. |
|
| 32 | - * @return void |
|
| 33 | - */ |
|
| 34 | - protected function main() |
|
| 35 | - { |
|
| 36 | - // This should never get hit except by URL manipulation. |
|
| 37 | - $this->redirect(''); |
|
| 38 | - } |
|
| 30 | + /** |
|
| 31 | + * Main function for this page, when no specific actions are called. |
|
| 32 | + * @return void |
|
| 33 | + */ |
|
| 34 | + protected function main() |
|
| 35 | + { |
|
| 36 | + // This should never get hit except by URL manipulation. |
|
| 37 | + $this->redirect(''); |
|
| 38 | + } |
|
| 39 | 39 | |
| 40 | - /** |
|
| 41 | - * Registered endpoint for the account creation callback. |
|
| 42 | - * |
|
| 43 | - * If this ever gets hit, something is wrong somewhere. |
|
| 44 | - */ |
|
| 45 | - protected function create() |
|
| 46 | - { |
|
| 47 | - throw new Exception('OAuth account creation endpoint triggered.'); |
|
| 48 | - } |
|
| 40 | + /** |
|
| 41 | + * Registered endpoint for the account creation callback. |
|
| 42 | + * |
|
| 43 | + * If this ever gets hit, something is wrong somewhere. |
|
| 44 | + */ |
|
| 45 | + protected function create() |
|
| 46 | + { |
|
| 47 | + throw new Exception('OAuth account creation endpoint triggered.'); |
|
| 48 | + } |
|
| 49 | 49 | |
| 50 | - /** |
|
| 51 | - * Callback entry point |
|
| 52 | - * @throws ApplicationLogicException |
|
| 53 | - * @throws OptimisticLockFailedException |
|
| 54 | - */ |
|
| 55 | - protected function authorise() |
|
| 56 | - { |
|
| 57 | - $oauthToken = WebRequest::getString('oauth_token'); |
|
| 58 | - $oauthVerifier = WebRequest::getString('oauth_verifier'); |
|
| 50 | + /** |
|
| 51 | + * Callback entry point |
|
| 52 | + * @throws ApplicationLogicException |
|
| 53 | + * @throws OptimisticLockFailedException |
|
| 54 | + */ |
|
| 55 | + protected function authorise() |
|
| 56 | + { |
|
| 57 | + $oauthToken = WebRequest::getString('oauth_token'); |
|
| 58 | + $oauthVerifier = WebRequest::getString('oauth_verifier'); |
|
| 59 | 59 | |
| 60 | - $this->doCallbackValidation($oauthToken, $oauthVerifier); |
|
| 60 | + $this->doCallbackValidation($oauthToken, $oauthVerifier); |
|
| 61 | 61 | |
| 62 | - $database = $this->getDatabase(); |
|
| 62 | + $database = $this->getDatabase(); |
|
| 63 | 63 | |
| 64 | - $user = OAuthUserHelper::findUserByRequestToken($oauthToken, $database); |
|
| 65 | - $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration()); |
|
| 64 | + $user = OAuthUserHelper::findUserByRequestToken($oauthToken, $database); |
|
| 65 | + $oauth = new OAuthUserHelper($user, $database, $this->getOAuthProtocolHelper(), $this->getSiteConfiguration()); |
|
| 66 | 66 | |
| 67 | - try { |
|
| 68 | - $oauth->completeHandshake($oauthVerifier); |
|
| 69 | - } |
|
| 70 | - catch (CurlException $ex) { |
|
| 71 | - throw new ApplicationLogicException($ex->getMessage(), 0, $ex); |
|
| 72 | - } |
|
| 67 | + try { |
|
| 68 | + $oauth->completeHandshake($oauthVerifier); |
|
| 69 | + } |
|
| 70 | + catch (CurlException $ex) { |
|
| 71 | + throw new ApplicationLogicException($ex->getMessage(), 0, $ex); |
|
| 72 | + } |
|
| 73 | 73 | |
| 74 | - // OK, we're the same session that just did a partial login that was redirected to OAuth. Let's upgrade the |
|
| 75 | - // login to a full login |
|
| 76 | - if (WebRequest::getOAuthPartialLogin() === $user->getId()) { |
|
| 77 | - WebRequest::setLoggedInUser($user); |
|
| 78 | - } |
|
| 74 | + // OK, we're the same session that just did a partial login that was redirected to OAuth. Let's upgrade the |
|
| 75 | + // login to a full login |
|
| 76 | + if (WebRequest::getOAuthPartialLogin() === $user->getId()) { |
|
| 77 | + WebRequest::setLoggedInUser($user); |
|
| 78 | + } |
|
| 79 | 79 | |
| 80 | - // My thinking is there are three cases here: |
|
| 81 | - // a) new user => redirect to prefs - it's the only thing they can access other than stats |
|
| 82 | - // b) existing user hit the connect button in prefs => redirect to prefs since it's where they were |
|
| 83 | - // c) existing user logging in => redirect to wherever they came from |
|
| 84 | - $redirectDestination = WebRequest::clearPostLoginRedirect(); |
|
| 85 | - if ($redirectDestination !== null && !$user->isNewUser()) { |
|
| 86 | - $this->redirectUrl($redirectDestination); |
|
| 87 | - } |
|
| 88 | - else { |
|
| 89 | - $this->redirect('preferences', null, null, 'internal.php'); |
|
| 90 | - } |
|
| 91 | - } |
|
| 80 | + // My thinking is there are three cases here: |
|
| 81 | + // a) new user => redirect to prefs - it's the only thing they can access other than stats |
|
| 82 | + // b) existing user hit the connect button in prefs => redirect to prefs since it's where they were |
|
| 83 | + // c) existing user logging in => redirect to wherever they came from |
|
| 84 | + $redirectDestination = WebRequest::clearPostLoginRedirect(); |
|
| 85 | + if ($redirectDestination !== null && !$user->isNewUser()) { |
|
| 86 | + $this->redirectUrl($redirectDestination); |
|
| 87 | + } |
|
| 88 | + else { |
|
| 89 | + $this->redirect('preferences', null, null, 'internal.php'); |
|
| 90 | + } |
|
| 91 | + } |
|
| 92 | 92 | |
| 93 | - /** |
|
| 94 | - * @param string $oauthToken |
|
| 95 | - * @param string $oauthVerifier |
|
| 96 | - * |
|
| 97 | - * @throws ApplicationLogicException |
|
| 98 | - */ |
|
| 99 | - private function doCallbackValidation($oauthToken, $oauthVerifier) |
|
| 100 | - { |
|
| 101 | - if ($oauthToken === null) { |
|
| 102 | - throw new ApplicationLogicException('No token provided'); |
|
| 103 | - } |
|
| 93 | + /** |
|
| 94 | + * @param string $oauthToken |
|
| 95 | + * @param string $oauthVerifier |
|
| 96 | + * |
|
| 97 | + * @throws ApplicationLogicException |
|
| 98 | + */ |
|
| 99 | + private function doCallbackValidation($oauthToken, $oauthVerifier) |
|
| 100 | + { |
|
| 101 | + if ($oauthToken === null) { |
|
| 102 | + throw new ApplicationLogicException('No token provided'); |
|
| 103 | + } |
|
| 104 | 104 | |
| 105 | - if ($oauthVerifier === null) { |
|
| 106 | - throw new ApplicationLogicException('No oauth verifier provided.'); |
|
| 107 | - } |
|
| 108 | - } |
|
| 105 | + if ($oauthVerifier === null) { |
|
| 106 | + throw new ApplicationLogicException('No oauth verifier provided.'); |
|
| 107 | + } |
|
| 108 | + } |
|
| 109 | 109 | } |
| 110 | 110 | \ No newline at end of file |
@@ -116,8 +116,7 @@ |
||
| 116 | 116 | $redirectDestination = WebRequest::clearPostLoginRedirect(); |
| 117 | 117 | if ($redirectDestination !== null && !$user->isNewUser()) { |
| 118 | 118 | $this->redirectUrl($redirectDestination); |
| 119 | - } |
|
| 120 | - else { |
|
| 119 | + } else { |
|
| 121 | 120 | $this->redirect('preferences', null, null, 'internal.php'); |
| 122 | 121 | } |
| 123 | 122 | } |
@@ -17,71 +17,71 @@ |
||
| 17 | 17 | |
| 18 | 18 | class PageChangePassword extends InternalPageBase |
| 19 | 19 | { |
| 20 | - /** |
|
| 21 | - * Main function for this page, when no specific actions are called. |
|
| 22 | - * @return void |
|
| 23 | - */ |
|
| 24 | - protected function main() |
|
| 25 | - { |
|
| 26 | - $this->setHtmlTitle('Change Password'); |
|
| 20 | + /** |
|
| 21 | + * Main function for this page, when no specific actions are called. |
|
| 22 | + * @return void |
|
| 23 | + */ |
|
| 24 | + protected function main() |
|
| 25 | + { |
|
| 26 | + $this->setHtmlTitle('Change Password'); |
|
| 27 | 27 | |
| 28 | - if (WebRequest::wasPosted()) { |
|
| 29 | - $this->validateCSRFToken(); |
|
| 30 | - try { |
|
| 31 | - $oldPassword = WebRequest::postString('oldpassword'); |
|
| 32 | - $newPassword = WebRequest::postString('newpassword'); |
|
| 33 | - $newPasswordConfirmation = WebRequest::postString('newpasswordconfirm'); |
|
| 28 | + if (WebRequest::wasPosted()) { |
|
| 29 | + $this->validateCSRFToken(); |
|
| 30 | + try { |
|
| 31 | + $oldPassword = WebRequest::postString('oldpassword'); |
|
| 32 | + $newPassword = WebRequest::postString('newpassword'); |
|
| 33 | + $newPasswordConfirmation = WebRequest::postString('newpasswordconfirm'); |
|
| 34 | 34 | |
| 35 | - $user = User::getCurrent($this->getDatabase()); |
|
| 36 | - if (!$user instanceof User) { |
|
| 37 | - throw new ApplicationLogicException('User not found'); |
|
| 38 | - } |
|
| 35 | + $user = User::getCurrent($this->getDatabase()); |
|
| 36 | + if (!$user instanceof User) { |
|
| 37 | + throw new ApplicationLogicException('User not found'); |
|
| 38 | + } |
|
| 39 | 39 | |
| 40 | - $this->validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, $user); |
|
| 40 | + $this->validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, $user); |
|
| 41 | 41 | |
| 42 | - $passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration()); |
|
| 43 | - $passwordProvider->setCredential($user, 1, $newPassword); |
|
| 44 | - } |
|
| 45 | - catch (ApplicationLogicException $ex) { |
|
| 46 | - SessionAlert::error($ex->getMessage()); |
|
| 47 | - $this->redirect('changePassword'); |
|
| 42 | + $passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration()); |
|
| 43 | + $passwordProvider->setCredential($user, 1, $newPassword); |
|
| 44 | + } |
|
| 45 | + catch (ApplicationLogicException $ex) { |
|
| 46 | + SessionAlert::error($ex->getMessage()); |
|
| 47 | + $this->redirect('changePassword'); |
|
| 48 | 48 | |
| 49 | - return; |
|
| 50 | - } |
|
| 49 | + return; |
|
| 50 | + } |
|
| 51 | 51 | |
| 52 | - SessionAlert::success('Password changed successfully!'); |
|
| 52 | + SessionAlert::success('Password changed successfully!'); |
|
| 53 | 53 | |
| 54 | - $this->redirect('preferences'); |
|
| 55 | - } |
|
| 56 | - else { |
|
| 57 | - $this->assignCSRFToken(); |
|
| 58 | - $this->setTemplate('preferences/changePassword.tpl'); |
|
| 59 | - $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js"); |
|
| 60 | - } |
|
| 61 | - } |
|
| 54 | + $this->redirect('preferences'); |
|
| 55 | + } |
|
| 56 | + else { |
|
| 57 | + $this->assignCSRFToken(); |
|
| 58 | + $this->setTemplate('preferences/changePassword.tpl'); |
|
| 59 | + $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js"); |
|
| 60 | + } |
|
| 61 | + } |
|
| 62 | 62 | |
| 63 | - /** |
|
| 64 | - * @param string $oldPassword |
|
| 65 | - * @param string $newPassword |
|
| 66 | - * @param string $newPasswordConfirmation |
|
| 67 | - * @param User $user |
|
| 68 | - * |
|
| 69 | - * @throws ApplicationLogicException |
|
| 70 | - */ |
|
| 71 | - protected function validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, User $user) |
|
| 72 | - { |
|
| 73 | - if ($oldPassword === null || $newPassword === null || $newPasswordConfirmation === null) { |
|
| 74 | - throw new ApplicationLogicException('All three fields must be completed to change your password'); |
|
| 75 | - } |
|
| 63 | + /** |
|
| 64 | + * @param string $oldPassword |
|
| 65 | + * @param string $newPassword |
|
| 66 | + * @param string $newPasswordConfirmation |
|
| 67 | + * @param User $user |
|
| 68 | + * |
|
| 69 | + * @throws ApplicationLogicException |
|
| 70 | + */ |
|
| 71 | + protected function validateNewPassword($oldPassword, $newPassword, $newPasswordConfirmation, User $user) |
|
| 72 | + { |
|
| 73 | + if ($oldPassword === null || $newPassword === null || $newPasswordConfirmation === null) { |
|
| 74 | + throw new ApplicationLogicException('All three fields must be completed to change your password'); |
|
| 75 | + } |
|
| 76 | 76 | |
| 77 | - if ($newPassword !== $newPasswordConfirmation) { |
|
| 78 | - throw new ApplicationLogicException('Your new passwords did not match!'); |
|
| 79 | - } |
|
| 77 | + if ($newPassword !== $newPasswordConfirmation) { |
|
| 78 | + throw new ApplicationLogicException('Your new passwords did not match!'); |
|
| 79 | + } |
|
| 80 | 80 | |
| 81 | - // TODO: adapt for MFA support |
|
| 82 | - $passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration()); |
|
| 83 | - if (!$passwordProvider->authenticate($user, $oldPassword)) { |
|
| 84 | - throw new ApplicationLogicException('The password you entered was incorrect.'); |
|
| 85 | - } |
|
| 86 | - } |
|
| 81 | + // TODO: adapt for MFA support |
|
| 82 | + $passwordProvider = new PasswordCredentialProvider($this->getDatabase(), $this->getSiteConfiguration()); |
|
| 83 | + if (!$passwordProvider->authenticate($user, $oldPassword)) { |
|
| 84 | + throw new ApplicationLogicException('The password you entered was incorrect.'); |
|
| 85 | + } |
|
| 86 | + } |
|
| 87 | 87 | } |
@@ -52,8 +52,7 @@ |
||
| 52 | 52 | SessionAlert::success('Password changed successfully!'); |
| 53 | 53 | |
| 54 | 54 | $this->redirect('preferences'); |
| 55 | - } |
|
| 56 | - else { |
|
| 55 | + } else { |
|
| 57 | 56 | $this->assignCSRFToken(); |
| 58 | 57 | $this->setTemplate('preferences/changePassword.tpl'); |
| 59 | 58 | $this->addJs("/vendor/dropbox/zxcvbn/dist/zxcvbn.js"); |