Skip to content

Commit

Permalink
rework error-handling system
Browse files Browse the repository at this point in the history
  • Loading branch information
alexweissman committed May 30, 2017
1 parent 14464d6 commit d09de83
Show file tree
Hide file tree
Showing 26 changed files with 1,080 additions and 891 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php
/**
* UserFrosting (http://www.userfrosting.com)
*
* @link https://github.com/userfrosting/UserFrosting
* @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
*/
namespace UserFrosting\Sprinkle\Account\Error\Handler;

use UserFrosting\Sprinkle\Core\Error\Handler\HttpExceptionHandler;

/**
* Handler for AuthCompromisedExceptions.
*
* Warns the user that their account may have been compromised due to a stolen "remember me" cookie.
* @author Alex Weissman (https://alexanderweissman.com)
*/
class AuthCompromisedExceptionHandler extends HttpExceptionHandler
{
/**
* Render a generic, user-friendly response without sensitive debugging information.
*
* @return ResponseInterface
*/
public function renderGenericResponse()
{
$template = $this->ci->view->getEnvironment()->loadTemplate('pages/error/compromised.html.twig');

return $this->response
->withStatus($this->statusCode)
->withHeader('Content-type', $this->contentType)
->write($template->render());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php
/**
* UserFrosting (http://www.userfrosting.com)
*
* @link https://github.com/userfrosting/UserFrosting
* @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
*/
namespace UserFrosting\Sprinkle\Account\Error\Handler;

use UserFrosting\Sprinkle\Core\Error\Handler\HttpExceptionHandler;

/**
* Handler for AuthExpiredExceptions.
*
* Forwards the user to the login page when their session has expired.
* @author Alex Weissman (https://alexanderweissman.com)
*/
class AuthExpiredExceptionHandler extends HttpExceptionHandler
{
/**
* Custom handling for requests that did not pass authentication.
*/
public function handle()
{
// For auth expired exceptions, we always add messages to the alert stream.
$this->writeAlerts();

$response = $this->response;

// For non-AJAX requests, we forward the user to the login page.
if (!$this->request->isXhr()) {
$uri = $this->request->getUri();
$path = $uri->getPath();
$query = $uri->getQuery();
$fragment = $uri->getFragment();

$path = $path
. ($query ? '?' . $query : '')
. ($fragment ? '#' . $fragment : '');

$loginPage = $this->ci->router->pathFor('login', [], [
'redirect' => $path
]);

$response = $response->withRedirect($loginPage);
}

return $response;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
/**
* UserFrosting (http://www.userfrosting.com)
*
* @link https://github.com/userfrosting/UserFrosting
* @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
*/
namespace UserFrosting\Sprinkle\Account\Error\Handler;

use UserFrosting\Sprinkle\Core\Error\Handler\HttpExceptionHandler;
use UserFrosting\Support\Message\UserMessage;

/**
* Handler for ForbiddenExceptions. Only really needed to override the default error message.
*
* @author Alex Weissman (https://alexanderweissman.com)
*/
class ForbiddenExceptionHandler extends HttpExceptionHandler
{
/**
* Resolve a list of error messages to present to the end user.
*
* @return array
*/
protected function determineUserMessages()
{
return [
new UserMessage("ACCOUNT.ACCESS_DENIED")
];
}
}

This file was deleted.

63 changes: 0 additions & 63 deletions app/sprinkles/account/src/Handler/AuthExpiredExceptionHandler.php

This file was deleted.

66 changes: 0 additions & 66 deletions app/sprinkles/account/src/Handler/ForbiddenExceptionHandler.php

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,11 @@ public function register($container)
*/
$container->extend('errorHandler', function ($handler, $c) {
// Register the ForbiddenExceptionHandler.
$handler->registerHandler('\UserFrosting\Support\Exception\ForbiddenException', '\UserFrosting\Sprinkle\Account\Handler\ForbiddenExceptionHandler');
$handler->registerHandler('\UserFrosting\Support\Exception\ForbiddenException', '\UserFrosting\Sprinkle\Account\Error\Handler\ForbiddenExceptionHandler');
// Register the AuthExpiredExceptionHandler
$handler->registerHandler('\UserFrosting\Sprinkle\Account\Authenticate\Exception\AuthExpiredException', '\UserFrosting\Sprinkle\Account\Handler\AuthExpiredExceptionHandler');
$handler->registerHandler('\UserFrosting\Sprinkle\Account\Authenticate\Exception\AuthExpiredException', '\UserFrosting\Sprinkle\Account\Error\Handler\AuthExpiredExceptionHandler');
// Register the AuthCompromisedExceptionHandler.
$handler->registerHandler('\UserFrosting\Sprinkle\Account\Authenticate\Exception\AuthCompromisedException', '\UserFrosting\Sprinkle\Account\Error\Handler\AuthCompromisedExceptionHandler');
return $handler;
});

Expand Down
88 changes: 88 additions & 0 deletions app/sprinkles/core/src/Error/ExceptionHandlerManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php
/**
* UserFrosting (http://www.userfrosting.com)
*
* @link https://github.com/userfrosting/UserFrosting
* @license https://github.com/userfrosting/UserFrosting/blob/master/licenses/UserFrosting.md (MIT License)
*/
namespace UserFrosting\Sprinkle\Core\Error;

use Interop\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use UserFrosting\Sprinkle\Core\Handler\ExceptionHandlerInterface;

/**
* Default UserFrosting application error handler
*
* It outputs the error message and diagnostic information in either JSON, XML, or HTML based on the Accept header.
* @author Alex Weissman (https://alexanderweissman.com)
*/
class ExceptionHandlerManager extends \Slim\Handlers\Error
{
/**
* @var ContainerInterface The global container object, which holds all your services.
*/
protected $ci;

/**
* @var array[string] An array that maps Exception types to callbacks, for special processing of certain types of errors.
*/
protected $exceptionHandlers = [];

/**
* Constructor
*
* @param ContainerInterface $ci The global container object, which holds all your services.
* @param boolean $displayErrorDetails Set to true to display full details
*/
public function __construct(ContainerInterface $ci, $displayErrorDetails = false)
{
$this->ci = $ci;
$this->displayErrorDetails = (bool)$displayErrorDetails;
}

/**
* Invoke error handler
*
* @param ServerRequestInterface $request The most recent Request object
* @param ResponseInterface $response The most recent Response object
* @param Exception $exception The caught Exception object
*
* @return ResponseInterface
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, \Exception $exception)
{
// Default exception handler class
$handlerClass = '\UserFrosting\Sprinkle\Core\Error\Handler\ExceptionHandler';

// Get the last matching registered handler class, and instantiate it
foreach ($this->exceptionHandlers as $exceptionClass => $matchedHandlerClass) {
if ($exception instanceof $exceptionClass) {
$handlerClass = $matchedHandlerClass;
}
}

$handler = new $handlerClass($this->ci, $request, $response, $exception, $this->displayErrorDetails);

return $handler->handle();
}

/**
* Register an exception handler for a specified exception class.
*
* The exception handler must implement \UserFrosting\Sprinkle\Core\Handler\ExceptionHandlerInterface.
*
* @param string $exceptionClass The fully qualified class name of the exception to handle.
* @param string $handlerClass The fully qualified class name of the assigned handler.
* @throws InvalidArgumentException If the registered handler fails to implement ExceptionHandlerInterface
*/
public function registerHandler($exceptionClass, $handlerClass)
{
if (!is_a($handlerClass, '\UserFrosting\Sprinkle\Core\Error\Handler\ExceptionHandlerInterface', true)) {
throw new \InvalidArgumentException("Registered exception handler must implement ExceptionHandlerInterface!");
}

$this->exceptionHandlers[$exceptionClass] = $handlerClass;
}
}
Loading

0 comments on commit d09de83

Please sign in to comment.