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