Skip to content
Snippets Groups Projects
Commit 10ce0acf authored by Jaime Perez Crespo's avatar Jaime Perez Crespo
Browse files

Reformat and enhance error reports.

parent 8293337e
No related branches found
No related tags found
No related merge requests found
<?php
/**
* Class which wraps simpleSAMLphp errors in exceptions.
* Class that wraps SimpleSAMLphp errors in exceptions.
*
* @author Olav Morken, UNINETT AS.
* @package simpleSAMLphp
* @package SimpleSAMLphp
*/
class SimpleSAML_Error_Error extends SimpleSAML_Error_Exception {
/**
* The error code.
*
* @var string
*/
private $errorCode;
/**
* The http code.
*
* @var integer
*/
protected $httpCode = 500;
/**
* The error title tag in dictionary.
*
* @var string
*/
private $dictTitle;
/**
* The error description tag in dictionary.
*
* @var string
*/
private $dictDescr;
/**
* The name of module which throw error.
*
* @var string|NULL
*/
private $module = NULL;
/**
* The parameters for the error.
*
* @var array
*/
private $parameters;
/**
* Name of custom include template for the error.
*
* @var string|NULL
*/
protected $includeTemplate = NULL;
/**
* Constructor for this error.
*
* The error can either be given as a string, or as an array. If it is an array, the
* first element in the array (with index 0), is the error code, while the other elements
* are replacements for the error text.
*
* @param mixed $errorCode One of the error codes defined in the errors dictionary.
* @param Exception $cause The exception which caused this fatal error (if any).
*/
public function __construct($errorCode, Exception $cause = NULL, $httpCode = NULL) {
assert('is_string($errorCode) || is_array($errorCode)');
if (is_array($errorCode)) {
$this->parameters = $errorCode;
unset($this->parameters[0]);
$this->errorCode = $errorCode[0];
} else {
$this->parameters = array();
$this->errorCode = $errorCode;
}
if (isset($httpCode)) {
$this->httpCode = $httpCode;
}
$moduleCode = explode(':', $this->errorCode, 2);
if (count($moduleCode) === 2) {
$this->module = $moduleCode[0];
$this->dictTitle = '{' . $this->module . ':errors:title_' . $moduleCode[1] . '}';
$this->dictDescr = '{' . $this->module . ':errors:descr_' . $moduleCode[1] . '}';
} else {
$this->dictTitle = '{errors:title_' . $this->errorCode . '}';
$this->dictDescr = '{errors:descr_' . $this->errorCode . '}';
}
if (!empty($this->parameters)) {
$msg = $this->errorCode . '(';
foreach ($this->parameters as $k => $v) {
if ($k === 0) {
continue;
}
$msg .= var_export($k, TRUE) . ' => ' . var_export($v, TRUE) . ', ';
}
$msg = substr($msg, 0, -2) . ')';
} else {
$msg = $this->errorCode;
}
parent::__construct($msg, -1, $cause);
}
/**
* Retrieve the error code given when throwing this error.
*
* @return string The error code.
*/
public function getErrorCode() {
return $this->errorCode;
}
/**
* Retrieve the error parameters given when throwing this error.
*
* @return array The parameters.
*/
public function getParameters() {
return $this->parameters;
}
/**
* Retrieve the error title tag in dictionary.
*
* @return string The error title tag.
*/
public function getDictTitle() {
return $this->dictTitle;
}
/**
* Retrieve the error description tag in dictionary.
*
* @return string The error description tag.
*/
public function getDictDescr() {
return $this->dictDescr;
}
/**
* Set the HTTP return code for this error.
*
* This should be overridden by subclasses who want a different return code than 500 Internal Server Error.
*/
protected function setHTTPCode() {
// Some mostly used HTTP codes.
$httpCodesMap = array(
400 => 'HTTP/1.0 400 Bad Request',
403 => 'HTTP/1.0 403 Forbidden',
404 => 'HTTP/1.0 404 Not Found',
405 => 'HTTP/1.0 405 Method Not Allowed',
500 => 'HTTP/1.0 500 Internal Server Error',
501 => 'HTTP/1.0 501 Method Not Implemented',
503 => 'HTTP/1.0 503 Service Temporarily Unavailable',
);
$httpCode = $this->httpCode;
if (function_exists('http_response_code')) {
http_response_code($httpCode);
return;
}
if (!array_key_exists($this->httpCode, $httpCodesMap)) {
$httpCode = 500;
SimpleSAML_Logger::warning('HTTP response code not defined: ' . var_export($this->httpCode, TRUE));
}
header($httpCodesMap[$httpCode]);
}
/**
* Save an error report.
*
* @return array The array with the error report data.
*/
protected function saveError() {
$data = $this->format();
$emsg = array_shift($data);
$etrace = implode("\n", $data);
$reportId = bin2hex(openssl_random_pseudo_bytes(4));
SimpleSAML_Logger::error('Error report with id ' . $reportId . ' generated.');
$config = SimpleSAML_Configuration::getInstance();
$session = SimpleSAML_Session::getSessionFromRequest();
if (isset($_SERVER['HTTP_REFERER'])) {
$referer = $_SERVER['HTTP_REFERER'];
/*
* Remove anything after the first '?' or ';', just
* in case it contains any sensitive data.
*/
$referer = explode('?', $referer, 2);
$referer = $referer[0];
$referer = explode(';', $referer, 2);
$referer = $referer[0];
} else {
$referer = 'unknown';
}
$errorData = array(
'exceptionMsg' => $emsg,
'exceptionTrace' => $etrace,
'reportId' => $reportId,
'trackId' => $session->getTrackID(),
'url' => \SimpleSAML\Utils\HTTP::getSelfURLNoQuery(),
'version' => $config->getVersion(),
'referer' => $referer,
);
$session->setData('core:errorreport', $reportId, $errorData);
return $errorData;
}
/**
* Display this error.
*
* This method displays a standard simpleSAMLphp error page and exits.
*/
public function show() {
$this->setHTTPCode();
/* Log the error message. */
$this->logError();
$errorData = $this->saveError();
$config = SimpleSAML_Configuration::getInstance();
$data['showerrors'] = $config->getBoolean('showerrors', true);
$data['error'] = $errorData;
$data['errorCode'] = $this->errorCode;
$data['parameters'] = $this->parameters;
$data['module'] = $this->module;
$data['dictTitle'] = $this->dictTitle;
$data['dictDescr'] = $this->dictDescr;
$data['includeTemplate'] = $this->includeTemplate;
/* Check if there is a valid technical contact email address. */
if($config->getBoolean('errorreporting', TRUE) &&
$config->getString('technicalcontact_email', 'na@example.org') !== 'na@example.org') {
/* Enable error reporting. */
$baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
$data['errorReportAddress'] = $baseurl . 'errorreport.php';
}
$data['email'] = '';
$session = SimpleSAML_Session::getSessionFromRequest();
$authorities = $session->getAuthorities();
foreach ($authorities as $authority) {
$attributes = $session->getAuthData($authority, 'Attributes');
if ($attributes !== NULL && array_key_exists('mail', $attributes) && count($attributes['mail']) > 0) {
$data['email'] = $attributes['mail'][0];
break; // enough, don't need to get all available mails, if more than one
}
}
$show_function = $config->getArray('errors.show_function', NULL);
if (isset($show_function)) {
assert('is_callable($show_function)');
call_user_func($show_function, $config, $data);
assert('FALSE');
} else {
$t = new SimpleSAML_XHTML_Template($config, 'error.php', 'errors');
$t->data = array_merge($t->data, $data);
$t->show();
}
exit;
}
class SimpleSAML_Error_Error extends SimpleSAML_Error_Exception
{
/**
* The error code.
*
* @var string
*/
private $errorCode;
/**
* The http code.
*
* @var integer
*/
protected $httpCode = 500;
/**
* The error title tag in dictionary.
*
* @var string
*/
private $dictTitle;
/**
* The error description tag in dictionary.
*
* @var string
*/
private $dictDescr;
/**
* The name of module which throw error.
*
* @var string|NULL
*/
private $module = null;
/**
* The parameters for the error.
*
* @var array
*/
private $parameters;
/**
* Name of custom include template for the error.
*
* @var string|NULL
*/
protected $includeTemplate = null;
/**
* Constructor for this error.
*
* The error can either be given as a string, or as an array. If it is an array, the first element in the array
* (with index 0), is the error code, while the other elements are replacements for the error text.
*
* @param mixed $errorCode One of the error codes defined in the errors dictionary.
* @param Exception $cause The exception which caused this fatal error (if any). Optional.
* @param int|null $httpCode The HTTP response code to use. Optional.
*/
public function __construct($errorCode, Exception $cause = null, $httpCode = null)
{
assert('is_string($errorCode) || is_array($errorCode)');
if (is_array($errorCode)) {
$this->parameters = $errorCode;
unset($this->parameters[0]);
$this->errorCode = $errorCode[0];
} else {
$this->parameters = array();
$this->errorCode = $errorCode;
}
if (isset($httpCode)) {
$this->httpCode = $httpCode;
}
$moduleCode = explode(':', $this->errorCode, 2);
if (count($moduleCode) === 2) {
$this->module = $moduleCode[0];
$this->dictTitle = '{'.$this->module.':errors:title_'.$moduleCode[1].'}';
$this->dictDescr = '{'.$this->module.':errors:descr_'.$moduleCode[1].'}';
} else {
$this->dictTitle = '{errors:title_'.$this->errorCode.'}';
$this->dictDescr = '{errors:descr_'.$this->errorCode.'}';
}
if (!empty($this->parameters)) {
$msg = $this->errorCode.'(';
foreach ($this->parameters as $k => $v) {
if ($k === 0) {
continue;
}
$msg .= var_export($k, true).' => '.var_export($v, true).', ';
}
$msg = substr($msg, 0, -2).')';
} else {
$msg = $this->errorCode;
}
parent::__construct($msg, -1, $cause);
}
/**
* Retrieve the error code given when throwing this error.
*
* @return string The error code.
*/
public function getErrorCode()
{
return $this->errorCode;
}
/**
* Retrieve the error parameters given when throwing this error.
*
* @return array The parameters.
*/
public function getParameters()
{
return $this->parameters;
}
/**
* Retrieve the error title tag in dictionary.
*
* @return string The error title tag.
*/
public function getDictTitle()
{
return $this->dictTitle;
}
/**
* Retrieve the error description tag in dictionary.
*
* @return string The error description tag.
*/
public function getDictDescr()
{
return $this->dictDescr;
}
/**
* Set the HTTP return code for this error.
*
* This should be overridden by subclasses who want a different return code than 500 Internal Server Error.
*/
protected function setHTTPCode()
{
// Some mostly used HTTP codes.
$httpCodesMap = array(
400 => 'HTTP/1.0 400 Bad Request',
403 => 'HTTP/1.0 403 Forbidden',
404 => 'HTTP/1.0 404 Not Found',
405 => 'HTTP/1.0 405 Method Not Allowed',
500 => 'HTTP/1.0 500 Internal Server Error',
501 => 'HTTP/1.0 501 Method Not Implemented',
503 => 'HTTP/1.0 503 Service Temporarily Unavailable',
);
$httpCode = $this->httpCode;
if (function_exists('http_response_code')) {
http_response_code($httpCode);
return;
}
if (!array_key_exists($this->httpCode, $httpCodesMap)) {
$httpCode = 500;
SimpleSAML_Logger::warning('HTTP response code not defined: '.var_export($this->httpCode, true));
}
header($httpCodesMap[$httpCode]);
}
/**
* Save an error report.
*
* @return array The array with the error report data.
*/
protected function saveError()
{
$data = $this->format();
$emsg = array_shift($data);
$etrace = implode("\n", $data);
$reportId = bin2hex(openssl_random_pseudo_bytes(4));
SimpleSAML_Logger::error('Error report with id '.$reportId.' generated.');
$config = SimpleSAML_Configuration::getInstance();
$session = SimpleSAML_Session::getSessionFromRequest();
if (isset($_SERVER['HTTP_REFERER'])) {
$referer = $_SERVER['HTTP_REFERER'];
// remove anything after the first '?' or ';', just in case it contains any sensitive data
$referer = explode('?', $referer, 2);
$referer = $referer[0];
$referer = explode(';', $referer, 2);
$referer = $referer[0];
} else {
$referer = 'unknown';
}
$errorData = array(
'exceptionMsg' => $emsg,
'exceptionTrace' => $etrace,
'reportId' => $reportId,
'trackId' => $session->getTrackID(),
'url' => \SimpleSAML\Utils\HTTP::getSelfURLNoQuery(),
'version' => $config->getVersion(),
'referer' => $referer,
);
$session->setData('core:errorreport', $reportId, $errorData);
return $errorData;
}
/**
* Display this error.
*
* This method displays a standard simpleSAMLphp error page and exits.
*/
public function show()
{
$this->setHTTPCode();
// log the error message
$this->logError();
$errorData = $this->saveError();
$config = SimpleSAML_Configuration::getInstance();
$data['showerrors'] = $config->getBoolean('showerrors', true);
$data['error'] = $errorData;
$data['errorCode'] = $this->errorCode;
$data['parameters'] = $this->parameters;
$data['module'] = $this->module;
$data['dictTitle'] = $this->dictTitle;
$data['dictDescr'] = $this->dictDescr;
$data['includeTemplate'] = $this->includeTemplate;
$data['clipboard.js'] = true;
// check if there is a valid technical contact email address
if ($config->getBoolean('errorreporting', true) &&
$config->getString('technicalcontact_email', 'na@example.org') !== 'na@example.org'
) {
// enable error reporting
$baseurl = \SimpleSAML\Utils\HTTP::getBaseURL();
$data['errorReportAddress'] = $baseurl.'errorreport.php';
}
$data['email'] = '';
$session = SimpleSAML_Session::getSessionFromRequest();
$authorities = $session->getAuthorities();
foreach ($authorities as $authority) {
$attributes = $session->getAuthData($authority, 'Attributes');
if ($attributes !== null && array_key_exists('mail', $attributes) && count($attributes['mail']) > 0) {
$data['email'] = $attributes['mail'][0];
break; // enough, don't need to get all available mails, if more than one
}
}
$show_function = $config->getArray('errors.show_function', null);
if (isset($show_function)) {
assert('is_callable($show_function)');
call_user_func($show_function, $config, $data);
assert('FALSE');
} else {
$t = new SimpleSAML_XHTML_Template($config, 'error.php', 'errors');
$t->data = array_merge($t->data, $data);
$t->show();
}
exit;
}
}
<?php
$this->data['header'] = $this->t($this->data['dictTitle']);
$this->data['head'] = '
<meta name="robots" content="noindex, nofollow" />
<meta name="googlebot" content="noarchive, nofollow" />';
$this->includeAtTemplateBase('includes/header.php');
?>
<?php
$this->data['header'] = $this->t($this->data['dictTitle']);
<h2><?php echo $this->t($this->data['dictTitle']); ?></h2>
$this->data['head'] = <<<EOF
<meta name="robots" content="noindex, nofollow" />
<meta name="googlebot" content="noarchive, nofollow" />
EOF;
$this->includeAtTemplateBase('includes/header.php');
?>
<h2><?php echo $this->t($this->data['dictTitle']); ?></h2>
<?php
echo htmlspecialchars($this->t($this->data['dictDescr'], $this->data['parameters']));
/* Include optional information for error. */
// include optional information for error
if (isset($this->data['includeTemplate'])) {
$this->includeAtTemplateBase($this->data['includeTemplate']);
$this->includeAtTemplateBase($this->data['includeTemplate']);
}
?>
<div class="trackidtext">
<?php echo $this->t('report_trackid'); ?>
<span class="trackid"><?php echo $this->data['error']['trackId']; ?></span>
</div>
<div class="trackidtext">
<p><?php echo $this->t('report_trackid'); ?></p>
<div class="input-group" style="width: 1em;">
<pre id="trackid" class="input-left"><?php echo $this->data['error']['trackId']; ?></pre>
<button data-clipboard-target="#trackid" id="btntrackid" class="btnaddonright">
<img src="<?php echo $this->data['baseurlpath'].'/resources/icons/clipboard.svg'; ?>"
alt="Copy to clipboard">
</button>
</div>
</div>
<?php
/* Print out exception only if the exception is available. */
// print out exception only if the exception is available
if ($this->data['showerrors']) {
?>
<h2><?php echo $this->t('debuginfo_header'); ?></h2>
<p><?php echo $this->t('debuginfo_text'); ?></p>
<div style="border: 1px solid #eee; padding: 1em; font-size: x-small">
<p style="margin: 1px"><?php echo htmlspecialchars($this->data['error']['exceptionMsg']); ?></p>
<pre style=" padding: 1em; font-family: monospace; "><?php echo htmlspecialchars($this->data['error']['exceptionTrace']); ?></pre>
</div>
<h2><?php echo $this->t('debuginfo_header'); ?></h2>
<p><?php echo $this->t('debuginfo_text'); ?></p>
<div style="border: 1px solid #eee; padding: 1em; font-size: x-small">
<p style="margin: 1px"><?php echo htmlspecialchars($this->data['error']['exceptionMsg']); ?></p>
<pre style="padding: 1em; font-family: monospace;"><?php
echo htmlspecialchars($this->data['error']['exceptionTrace']); ?></pre>
</div>
<?php
}
?>
<?php
/* Add error report submit section if we have a valid technical contact. 'errorreportaddress' will only be set if
* the technical contact email address has been set.
*/
if (isset($this->data['errorReportAddress'])) {
?>
<h2><?php echo $this->t('report_header'); ?></h2>
<form action="<?php echo htmlspecialchars($this->data['errorReportAddress']); ?>" method="post">
<p><?php echo $this->t('report_text'); ?></p>
<p><?php echo $this->t('report_email'); ?> <input type="text" size="25" name="email" value="<?php echo htmlspecialchars($this->data['email']); ?>" /></p>
<p>
<textarea name="text" rows="6" cols="43"><?php echo $this->t('report_explain'); ?></textarea>
</p><p>
<input type="hidden" name="reportId" value="<?php echo $this->data['error']['reportId']; ?>" />
<input type="submit" name="send" value="<?php echo $this->t('report_submit'); ?>" />
</p>
</form>
<?php
<h2><?php echo $this->t('report_header'); ?></h2>
<form action="<?php echo htmlspecialchars($this->data['errorReportAddress']); ?>" method="post">
<p><?php echo $this->t('report_text'); ?></p>
<p><?php echo $this->t('report_email'); ?>
<input type="text" size="25" name="email" value="<?php echo htmlspecialchars($this->data['email']); ?>" />
</p>
<p>
<textarea class="metadatabox" name="text" rows="6" style="width: 100%; padding: 0.5em;"><?php
echo $this->t('report_explain'); ?></textarea>
</p>
<p>
<input type="hidden" name="reportId" value="<?php echo $this->data['error']['reportId']; ?>"/>
<button type="submit" name="send" class="btn"><?php echo $this->t('report_submit'); ?></button>
</p>
</form>
<?php
}
?>
<h2 style="clear: both"><?php echo $this->t('howto_header'); ?></h2>
<p><?php echo $this->t('howto_text'); ?></p>
<?php $this->includeAtTemplateBase('includes/footer.php');
<h2 style="clear: both"><?php echo $this->t('howto_header'); ?></h2>
<p><?php echo $this->t('howto_text'); ?></p>
<script type="text/javascript">
var clipboard = new Clipboard('#btntrackid');
</script>
<?php
$this->includeAtTemplateBase('includes/footer.php');
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment