Improved exception handling:

- exceptions get now always logged to the error_log
- in the webgui it's now configurable, if the message contains a
  stacktrace (incl. function arguments) - default no (security)
- command line interfaces get detected and contain no html anymore
- webdav and groupdav send the exceptions as basic auth realms to the
  client
- webdav and groupdav login failures contain the reason as part of the
  basic auth realm
This commit is contained in:
Ralf Becker 2008-10-26 12:13:01 +00:00
parent 80324c6c63
commit fa73ad5339
8 changed files with 117 additions and 90 deletions

View File

@ -43,14 +43,12 @@ $GLOBALS['egw_info'] = array(
'currentapp' => 'admin', 'currentapp' => 'admin',
'noheader' => true, 'noheader' => true,
'autocreate_session_callback' => 'user_pass_from_argv', 'autocreate_session_callback' => 'user_pass_from_argv',
'no_exception_handler' => 'cli',
) )
); );
include('../header.inc.php'); include('../header.inc.php');
// set our own exception handler, to not get the html from eGW's default one
set_exception_handler('admin_cli_exception_handler');
switch($action) switch($action)
{ {
case '--edit-user': case '--edit-user':
@ -359,18 +357,6 @@ function do_change_account_id($args)
run_command(new admin_cmd_change_account_id($ids2change)); run_command(new admin_cmd_change_account_id($ids2change));
} }
/**
* Exit the script with a numeric exit code and an error-message, does NOT return
*
* @param int $exit_code
* @param string $message
*/
function admin_cli_exception_handler(Exception $e)
{
echo $e->getMessage()."\n";
exit($e->getCode());
}
/** /**
* List all exit codes used by the command line interface * List all exit codes used by the command line interface
* *

View File

@ -232,6 +232,7 @@ error: %1 not found or other error !!! admin de Fehler: %1 nicht gefunden oder a
expires admin de abgelaufen expires admin de abgelaufen
explanation of ldapman admin de Dieses Modul ist derzeit nur für folgende Konfiguration getestet: Postfix, LDAP, Courier-Imap, Procmail und erfordert die Schemas: core und qmail(OID 7914). Weitere Konfigurationshinweise sind im README.ldapman im DOC Verzeichnis des Moduls ADMIN zu finden. explanation of ldapman admin de Dieses Modul ist derzeit nur für folgende Konfiguration getestet: Postfix, LDAP, Courier-Imap, Procmail und erfordert die Schemas: core und qmail(OID 7914). Weitere Konfigurationshinweise sind im README.ldapman im DOC Verzeichnis des Moduls ADMIN zu finden.
fallback (after each pageview) admin de Ausweichmöglichkeit (nach jedem Seitenaufbau) fallback (after each pageview) admin de Ausweichmöglichkeit (nach jedem Seitenaufbau)
false admin de Falsch
field '%1' already exists !!! admin de Feld '%1' existiert bereits !!! field '%1' already exists !!! admin de Feld '%1' existiert bereits !!!
file space admin de Dateiraum file space admin de Dateiraum
file space must be an integer admin de Speicherplatz muss eine Zahl sein file space must be an integer admin de Speicherplatz muss eine Zahl sein
@ -398,6 +399,7 @@ server type(mode) admin de Server-Typ (Modus)
server url admin de Server-URL server url admin de Server-URL
server username admin de Server-Benutzername server username admin de Server-Benutzername
set preference values. admin de Einstellungswert wurde geändert set preference values. admin de Einstellungswert wurde geändert
should exceptions contain a trace (including function arguments) admin de Sollen Ausnahmefehler eine Rückverfolgung enthalten (einschl. Funktionsargumente)
should the login page include a language selectbox (useful for demo-sites) ? admin de Soll die Anmeldeseite eine Sprachauswahl beinhalten (nützlich für Demosites) ? should the login page include a language selectbox (useful for demo-sites) ? admin de Soll die Anmeldeseite eine Sprachauswahl beinhalten (nützlich für Demosites) ?
show 'powered by' logo on admin de Zeige "powered by" Logo show 'powered by' logo on admin de Zeige "powered by" Logo
show access log admin de Zugangsprotokoll anzeigen show access log admin de Zugangsprotokoll anzeigen
@ -450,6 +452,7 @@ times admin de Zeiten
top admin de oben top admin de oben
total of %1 id's changed. admin de Die Gesamtanzahl von %1 Ids wurde geändert. total of %1 id's changed. admin de Die Gesamtanzahl von %1 Ids wurde geändert.
total records admin de Anzahl Datensätze insgesamt total records admin de Anzahl Datensätze insgesamt
true admin de Wahr
trust level admin de Grad des Vertrauens trust level admin de Grad des Vertrauens
trust relationship admin de Vertrauensverhältnis trust relationship admin de Vertrauensverhältnis
type '%1' already exists !!! admin de Typ '%1' existiert bereits !!! type '%1' already exists !!! admin de Typ '%1' existiert bereits !!!

View File

@ -397,6 +397,7 @@ server type(mode) admin en Server Type(mode)
server url admin en Server URL server url admin en Server URL
server username admin en Server Username server username admin en Server Username
set preference values. admin en Set preference values. set preference values. admin en Set preference values.
should exceptions contain a trace (including function arguments) admin en Should exceptions contain a trace (including function arguments)
should the login page include a language selectbox (useful for demo-sites) ? admin en Should the login page include a language selectbox (useful for demo-sites) ? should the login page include a language selectbox (useful for demo-sites) ? admin en Should the login page include a language selectbox (useful for demo-sites) ?
show 'powered by' logo on admin en Show 'powered by' logo on show 'powered by' logo on admin en Show 'powered by' logo on
show access log admin en Show access log show access log admin en Show access log

View File

@ -273,6 +273,16 @@
<td>{lang_Allow_remote_administration_from_following_install_ID's_(comma_separated)}:<br />{lang_Own_install_ID:_}{value_install_id}</td> <td>{lang_Allow_remote_administration_from_following_install_ID's_(comma_separated)}:<br />{lang_Own_install_ID:_}{value_install_id}</td>
<td><input name="newsettings[allow_remote_admin]" value="{value_allow_remote_admin}" size="40"></td> <td><input name="newsettings[allow_remote_admin]" value="{value_allow_remote_admin}" size="40"></td>
</tr> </tr>
<tr class="row_on">
<td>{lang_Should_exceptions_contain_a_trace_(including_function_arguments)}:</td>
<td>
<select name="newsettings[exception_show_trace]">
<option value="">{lang_No} - {lang_more_secure}</option>
<option value="True"{selected_exception_show_trace_True}>{lang_Yes}</option>
</select>
</td>
</tr>
</tr>
<!-- END body --> <!-- END body -->

View File

@ -13,6 +13,8 @@
* @version $Id$ * @version $Id$
*/ */
$starttime = microtime(true);
/** /**
* check if the given user has access * check if the given user has access
* *
@ -41,9 +43,11 @@ function check_access(&$account)
} }
//error_log("GroupDAV PHP_AUTH_USER={$_SERVER['PHP_AUTH_USER']}, HTTP_USER_AGENT={$_SERVER['HTTP_USER_AGENT']} --> no_session=".(int)$no_session); //error_log("GroupDAV PHP_AUTH_USER={$_SERVER['PHP_AUTH_USER']}, HTTP_USER_AGENT={$_SERVER['HTTP_USER_AGENT']} --> no_session=".(int)$no_session);
if (!($sessionid = $GLOBALS['egw']->session->create($account,'','',$no_session))) if (!isset($_SERVER['PHP_AUTH_USER']) || !($sessionid = $GLOBALS['egw']->session->create($account,'','',$no_session)))
{ {
header('WWW-Authenticate: Basic realm="'.groupdav::REALM.'"'); header('WWW-Authenticate: Basic realm="'.groupdav::REALM.
// if the session class gives a reason why the login failed --> append it to the REALM
($GLOBALS['egw']->session->reason ? ': '.$GLOBALS['egw']->session->reason : '').'"');
header('HTTP/1.1 401 Unauthorized'); header('HTTP/1.1 401 Unauthorized');
header('X-WebDAV-Status: 401 Unauthorized', true); header('X-WebDAV-Status: 401 Unauthorized', true);
exit; exit;
@ -55,9 +59,13 @@ $GLOBALS['egw_info']['flags'] = array(
'noheader' => True, 'noheader' => True,
'currentapp' => 'groupdav', 'currentapp' => 'groupdav',
'autocreate_session_callback' => 'check_access', 'autocreate_session_callback' => 'check_access',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
); );
// if you move this file somewhere else, you need to adapt the path to the header! // if you move this file somewhere else, you need to adapt the path to the header!
include(dirname(__FILE__).'/header.inc.php'); include(dirname(__FILE__).'/header.inc.php');
$headertime = microtime(true);
$groupdav = new groupdav(); $groupdav = new groupdav();
$groupdav->ServeRequest(); $groupdav->ServeRequest();
//error_log(sprintf("GroupDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime));

View File

@ -1404,10 +1404,29 @@ function egw_exception_handler(Exception $e)
{ {
$headline = try_lang('An error happend'); $headline = try_lang('An error happend');
} }
$message = '<h3>'.$headline."</h3>\n". // logging all exceptions to the error_log
'<pre><b>'.$e->getMessage()."</b>\n\n". error_log($headline.': '.$e->getMessage());
$e->getTraceAsString()."</pre>\n"; foreach(explode("\n",$e->getTraceAsString()) as $line) error_log($line);
// exception handler for cli (command line interface) clients, no html
if(!isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] == 'cli')
{
echo $headline.': '.$e->getMessage()."\n";
echo $e->getTraceAsString()."\n";
exit($e->getCode() ? $e->getCode() : 9999); // allways give a non-zero exit code
}
// regular GUI exception
elseif (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']))
{
$message = '<h3>'.$headline."</h3>\n".
'<pre><b>'.$e->getMessage()."</b>\n\n";
// only show trace (incl. function arguments) if explicitly enabled, eg. on a development system
if ($GLOBALS['egw_info']['server']['exception_show_trace'])
{
$message .= $e->getTraceAsString();
}
$message .= "</pre>\n";
if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->session) && method_exists($GLOBALS['egw'],'link')) if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->session) && method_exists($GLOBALS['egw'],'link'))
{ {
$message .= '<p><a href="'.$GLOBALS['egw']->link('/index.php').'">'.try_lang('Click here to resume your eGroupWare Session.').'</a></p>'; $message .= '<p><a href="'.$GLOBALS['egw']->link('/index.php').'">'.try_lang('Click here to resume your eGroupWare Session.').'</a></p>';
@ -1420,6 +1439,14 @@ function egw_exception_handler(Exception $e)
{ {
echo "<html>\n<head>\n<title>$headline</title>\n</head>\n<body>\n$message\n</body>\n</html>\n"; echo "<html>\n<head>\n<title>$headline</title>\n</head>\n<body>\n$message\n</body>\n</html>\n";
} }
}
// exception handler sending message back to the client as basic auth message
elseif($GLOBALS['egw_info']['flags']['no_exception_handler'] == 'basic_auth')
{
header('WWW-Authenticate: Basic realm="'.$headline.' '.$e->getMessage().'"');
header('HTTP/1.1 401 Unauthorized');
header('X-WebDAV-Status: 401 Unauthorized', true);
}
if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->common)) if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->common))
{ {
$GLOBALS['egw']->common->egw_exit(); $GLOBALS['egw']->common->egw_exit();
@ -1427,7 +1454,7 @@ function egw_exception_handler(Exception $e)
exit; exit;
} }
if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']) || !$GLOBALS['egw_info']['flags']['no_exception_handler']) if (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] !== true)
{ {
set_exception_handler('egw_exception_handler'); set_exception_handler('egw_exception_handler');
} }

View File

@ -41,27 +41,11 @@ $GLOBALS['egw_info'] = array(
'flags' => array( 'flags' => array(
'currentapp' => 'home', 'currentapp' => 'home',
'noapi' => true, 'noapi' => true,
'no_exception_handler' => 'cli',
)); ));
include('inc/functions.inc.php'); include('inc/functions.inc.php');
$GLOBALS['egw_setup']->translation->no_translation_marker = '';
$GLOBALS['egw_setup']->system_charset = $charset; $GLOBALS['egw_setup']->system_charset = $charset;
/**
* Echo the exception message and exit the script with a numeric code, does NOT return
*
* @param Exception $e
*/
function cli_exception_handler(Exception $e)
{
echo $e->getMessage()."\n";
// if ($e instanceof egw_exception_assertion_failed && !($e instanceof egw_exception_wrong_userinput))
{
echo $e->getTraceAsString()."\n";
}
exit($e->getCode() ? $e->getCode() : 9999); // always give a non-zero exist status
}
set_exception_handler('cli_exception_handler');
if ((float) PHP_VERSION < $GLOBALS['egw_setup']->required_php_version) if ((float) PHP_VERSION < $GLOBALS['egw_setup']->required_php_version)
{ {
fail(98,lang('You are using PHP version %1. eGroupWare now requires %2 or later, recommended is PHP %3.',PHP_VERSION,$GLOBALS['egw_setup']->required_php_version,$GLOBALS['egw_setup']->recommended_php_version)); fail(98,lang('You are using PHP version %1. eGroupWare now requires %2 or later, recommended is PHP %3.',PHP_VERSION,$GLOBALS['egw_setup']->required_php_version,$GLOBALS['egw_setup']->recommended_php_version));

View File

@ -13,6 +13,8 @@
* @version $Id$ * @version $Id$
*/ */
$starttime = microtime(true);
/** /**
* check if the given user has access * check if the given user has access
* *
@ -28,9 +30,11 @@ function check_access(&$account)
'passwd' => $_SERVER['PHP_AUTH_PW'], 'passwd' => $_SERVER['PHP_AUTH_PW'],
'passwd_type' => 'text', 'passwd_type' => 'text',
); );
if (!($sessionid = $GLOBALS['egw']->session->create($account))) if (!isset($_SERVER['PHP_AUTH_USER']) || !($sessionid = $GLOBALS['egw']->session->create($account)))
{ {
header('WWW-Authenticate: Basic realm="'.vfs_webdav_server::REALM.'"'); header('WWW-Authenticate: Basic realm="'.vfs_webdav_server::REALM.
// if the session class gives a reason why the login failed --> append it to the REALM
($GLOBALS['egw']->session->reason ? ': '.$GLOBALS['egw']->session->reason : '').'"');
header("HTTP/1.1 401 Unauthorized"); header("HTTP/1.1 401 Unauthorized");
header("X-WebDAV-Status: 401 Unauthorized", true); header("X-WebDAV-Status: 401 Unauthorized", true);
exit; exit;
@ -48,10 +52,14 @@ $GLOBALS['egw_info'] = array(
'noheader' => True, 'noheader' => True,
'currentapp' => $app, 'currentapp' => $app,
'autocreate_session_callback' => 'check_access', 'autocreate_session_callback' => 'check_access',
'no_exception_handler' => 'basic_auth', // we use a basic auth exception handler (sends exception message as basic auth realm)
) )
); );
// if you move this file somewhere else, you need to adapt the path to the header! // if you move this file somewhere else, you need to adapt the path to the header!
include(dirname(__FILE__).'/header.inc.php'); include(dirname(__FILE__).'/header.inc.php');
$headertime = microtime(true);
$webdav_server = new vfs_webdav_server(); $webdav_server = new vfs_webdav_server();
$webdav_server->ServeRequest(); $webdav_server->ServeRequest();
//error_log(sprintf("GroupDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime));