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

@ -1,4 +1,4 @@
#!/usr/bin/php -qC
#!/usr/bin/php -qC
<?php
/**
* Admin - Command line interface
@ -43,47 +43,45 @@ $GLOBALS['egw_info'] = array(
'currentapp' => 'admin',
'noheader' => true,
'autocreate_session_callback' => 'user_pass_from_argv',
'no_exception_handler' => 'cli',
)
);
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)
{
case '--edit-user':
return do_edit_user($arg0s);
case '--change-pw':
return do_change_pw($arg0s);
case '--delete-user':
return do_delete_account($arg0s[2],$arg0s[3]);
case '--edit-group':
return do_edit_group($arg0s);
case '--delete-group':
return do_delete_account($arg0s[2],0,false);
case '--allow-app':
case '--deny-app':
return do_account_app($arg0s,$action == '--allow-app');
case '--change-account-id':
return do_change_account_id($arg0s);
case '--subscribe-other':
return do_subscribe_other($arg0s[2],$arg0s[3]);
case '--check-acl';
return do_check_acl();
case '--show-header';
return run_command(new setup_cmd_showheader($arg0s[2]));
case '--exit-codes':
return list_exit_codes();
@ -95,7 +93,7 @@ exit(0);
/**
* run a command object, after checking for additional arguments: sheduled, requested or comment
*
*
* Does not return! Echos success or error messsage and exits with either 0 (success) or the numerical error-code
*
* @param admin_cmd $cmd
@ -103,7 +101,7 @@ exit(0);
function run_command(admin_cmd $cmd)
{
global $arguments;
$skip_checks = false;
while ($arguments && ($extra = array_shift($arguments)))
{
@ -112,24 +110,24 @@ function run_command(admin_cmd $cmd)
case '--schedule': // schedule the command instead of running it directly
$time = admin_cmd::parse_date(array_shift($arguments));
break;
case '--requested': // note who requested to run the command
$cmd->requested = 0;
$cmd->requested_email = array_shift($arguments);
break;
case '--comment': // note a comment
$cmd->comment = array_shift($arguments);
break;
case '--remote': // run the command on a remote install
$cmd->remote_id = admin_cmd::parse_remote(array_shift($arguments));
break;
case '--skip-checks': //do not yet run the checks for scheduled local commands
$skip_checks = true;
break;
case '--header-access':
if ($cmd instanceof setup_cmd)
{
@ -154,7 +152,7 @@ function run_command(admin_cmd $cmd)
/**
* callback to authenticate with the user/pw specified on the commandline
*
*
* @param array &$account account_info with keys 'login', 'passwd' and optional 'passwd_type'
* @return boolean/string true if we allow the access and account is set, a sessionid or false otherwise
*/
@ -191,7 +189,7 @@ function usage($action=null,$ret=0)
{
$cmd = basename($_SERVER['argv'][0]);
echo "Usage: $cmd --command admin-account[@domain],admin-password,options,... [--schedule {YYYY-mm-dd|+1 week|+5 days}] [--requested 'Name <email>'] [--comment 'comment ...'] [--remote {id|name}] [--skip-checks]\n\n";
echo "--edit-user admin-account[@domain],admin-password,account[=new-account-name],first-name,last-name,password,email,expires{never(default)|YYYY-MM-DD|already},can-change-pw{yes(default)|no},anon-user{yes|no(default)},primary-group{Default(default)|...}[,groups,...]\n";
echo " Edit or add a user to eGroupWare. If you specify groups, they *replace* the exiting memberships!\n";
echo "--change-pw admin-account[@domain],admin-password,account,password\n";
@ -212,7 +210,7 @@ function usage($action=null,$ret=0)
echo "--exit-codes admin-account[@domain],admin-password\n";
echo " List all exit codes of the command line interface\n";
exit($ret);
exit($ret);
}
/**
@ -227,7 +225,7 @@ function do_account_app($args,$allow)
array_shift($args); // admin-account
array_shift($args); // admin-pw
$account = array_shift($args);
include_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd_account_app.inc.php');
run_command(new admin_cmd_account_app($allow,$account,$args));
}
@ -242,7 +240,7 @@ function do_edit_group($args)
array_shift($args); // admin-account
array_shift($args); // admin-pw
list($account,$new_account_name) = explode('=',array_shift($args)); // account[=new-account-name]
$data = array(
'account_lid' => $new_account_name,
'account_email' => array_shift($args),
@ -265,7 +263,7 @@ function do_edit_group($args)
/**
* Change/Set Password for a given user
* 1: 2: 3: 4:
* 1: 2: 3: 4:
* @param array $args admin-account[@domain],admin-password,account,password
*/
function do_change_pw($args)
@ -274,7 +272,7 @@ function do_change_pw($args)
array_shift($args); // admin-pw
$account = array_shift($args); // account
$password = array_shift($args); // pw
run_command(new admin_cmd_change_pw($account,$password));
}
@ -288,7 +286,7 @@ function do_edit_user($args)
array_shift($args); // admin-account
array_shift($args); // admin-pw
list($account,$new_account_name) = explode('=',array_shift($args)); // account[=new-account-name]
$data = array(
'account_lid' => $new_account_name,
'account_firstname' => array_shift($args),
@ -331,7 +329,7 @@ function do_delete_account($account,$new_user=0,$is_user=true)
/**
* Deletes ACL entries of not longer existing accounts
*
*
* @return int 0 allways
*/
function do_check_acl()
@ -348,7 +346,7 @@ function do_check_acl()
function do_change_account_id($args)
{
if (count($args) < 4) usage(); // 4 means at least user,pw,from1,to1
$ids2change = array();
for($n = 2; $n < count($args); $n += 2)
{
@ -359,24 +357,12 @@ function do_change_account_id($args)
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
*
* The list is generated by "greping" this file for calls to the fail() function.
* The list is generated by "greping" this file for calls to the fail() function.
* Calls to fail() have to be in one line, to be recogniced!
*
*
* @ToDo adapt it to the exceptions
*/
function list_exit_codes()
@ -409,7 +395,7 @@ function do_subscribe_other($account_lid,$pw=null)
{
if (!($account_id = $GLOBALS['egw']->accounts->name2id($account_lid)))
{
throw new egw_exception_wrong_userinput(lang("Unknown account: %1 !!!",$account_lid),15);
throw new egw_exception_wrong_userinput(lang("Unknown account: %1 !!!",$account_lid),15);
}
$GLOBALS['egw_info']['user'] = array(
'account_id' => $account_id,
@ -421,20 +407,20 @@ function do_subscribe_other($account_lid,$pw=null)
$emailadmin = new bo();
$user_profile = $emailadmin->getUserProfile('felamimail');
unset($emailadmin);
$icServer = new cyrusimap();
//$icServer =& $user_profile->ic_server[0];
//print_r($icServer);
$icServer->openConnection(!$pw);
$delimiter = $icServer->getHierarchyDelimiter();
$mailboxes = $icServer->getMailboxes();
//print_r($mailboxes);
$own_mbox = 'user'.$delimiter.$account_lid;
foreach($mailboxes as $n => $mailbox)
{
// if ($n < 1) continue;

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
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)
false admin de Falsch
field '%1' already exists !!! admin de Feld '%1' existiert bereits !!!
file space admin de Dateiraum
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 username admin de Server-Benutzername
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) ?
show 'powered by' logo on admin de Zeige "powered by" Logo
show access log admin de Zugangsprotokoll anzeigen
@ -450,6 +452,7 @@ times admin de Zeiten
top admin de oben
total of %1 id's changed. admin de Die Gesamtanzahl von %1 Ids wurde geändert.
total records admin de Anzahl Datensätze insgesamt
true admin de Wahr
trust level admin de Grad des Vertrauens
trust relationship admin de Vertrauensverhältnis
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 username admin en Server Username
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) ?
show 'powered by' logo on admin en Show 'powered by' logo on
show access log admin en Show access log

View File

@ -115,7 +115,7 @@
<input name="newsettings[link_list_thumbnail]" value="{value_link_list_thumbnail}" size="5">
</td>
</tr>
<tr class="row_on">
<td>{lang_Enable_the_spellcheck_in_the_ritch_text_editor_?}:</td>
<td>
@ -124,7 +124,7 @@
<option value="True"{selected_enabled_spellcheck_True}>{lang_Yes}</option>
</select>
</td>
</tr>
</tr>
<tr class="row_off">
<td>{lang_Complete_path_to_aspell_program}:</td>
@ -213,21 +213,21 @@
<input name="newsettings[num_unsuccessful_id]" value="{value_num_unsuccessful_id}" size="5">
</td>
</tr>
<tr class="row_off">
<td>{lang_After_how_many_unsuccessful_attempts_to_login,_an_IP_should_be_blocked_(default_3)_?}:</td>
<td>
<input name="newsettings[num_unsuccessful_ip]" value="{value_num_unsuccessful_ip}" size="5">
</td>
</tr>
<tr class="row_on">
<td>{lang_How_many_minutes_should_an_account_or_IP_be_blocked_(default_30)_?}:</td>
<td>
<input name="newsettings[block_time]" value="{value_block_time}" size="5">
</td>
</tr>
<tr class="row_off">
<td>{lang_Admin_email_addresses_(comma-separated)_to_be_notified_about_the_blocking_(empty_for_no_notify)}:</td>
<td>
@ -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><input name="newsettings[allow_remote_admin]" value="{value_allow_remote_admin}" size="40"></td>
</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 -->

View File

@ -13,6 +13,8 @@
* @version $Id$
*/
$starttime = microtime(true);
/**
* 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);
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('X-WebDAV-Status: 401 Unauthorized', true);
exit;
@ -55,9 +59,13 @@ $GLOBALS['egw_info']['flags'] = array(
'noheader' => True,
'currentapp' => 'groupdav',
'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!
include(dirname(__FILE__).'/header.inc.php');
$headertime = microtime(true);
$groupdav = new groupdav();
$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,21 +1404,48 @@ function egw_exception_handler(Exception $e)
{
$headline = try_lang('An error happend');
}
$message = '<h3>'.$headline."</h3>\n".
'<pre><b>'.$e->getMessage()."</b>\n\n".
$e->getTraceAsString()."</pre>\n";
// logging all exceptions to the error_log
error_log($headline.': '.$e->getMessage());
foreach(explode("\n",$e->getTraceAsString()) as $line) error_log($line);
if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->session) && method_exists($GLOBALS['egw'],'link'))
// exception handler for cli (command line interface) clients, no html
if(!isset($_SERVER['HTTP_HOST']) || $GLOBALS['egw_info']['flags']['no_exception_handler'] == 'cli')
{
$message .= '<p><a href="'.$GLOBALS['egw']->link('/index.php').'">'.try_lang('Click here to resume your eGroupWare Session.').'</a></p>';
echo $headline.': '.$e->getMessage()."\n";
echo $e->getTraceAsString()."\n";
exit($e->getCode() ? $e->getCode() : 9999); // allways give a non-zero exit code
}
if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->framework))
// regular GUI exception
elseif (!isset($GLOBALS['egw_info']['flags']['no_exception_handler']))
{
$GLOBALS['egw']->framework->render($message,$headline);
$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'))
{
$message .= '<p><a href="'.$GLOBALS['egw']->link('/index.php').'">'.try_lang('Click here to resume your eGroupWare Session.').'</a></p>';
}
if (is_object($GLOBALS['egw']) && isset($GLOBALS['egw']->framework))
{
$GLOBALS['egw']->framework->render($message,$headline);
}
else
{
echo "<html>\n<head>\n<title>$headline</title>\n</head>\n<body>\n$message\n</body>\n</html>\n";
}
}
else
// exception handler sending message back to the client as basic auth message
elseif($GLOBALS['egw_info']['flags']['no_exception_handler'] == 'basic_auth')
{
echo "<html>\n<head>\n<title>$headline</title>\n</head>\n<body>\n$message\n</body>\n</html>\n";
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))
{
@ -1427,7 +1454,7 @@ function egw_exception_handler(Exception $e)
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');
}

View File

@ -41,27 +41,11 @@ $GLOBALS['egw_info'] = array(
'flags' => array(
'currentapp' => 'home',
'noapi' => true,
'no_exception_handler' => 'cli',
));
include('inc/functions.inc.php');
$GLOBALS['egw_setup']->translation->no_translation_marker = '';
$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)
{
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$
*/
$starttime = microtime(true);
/**
* check if the given user has access
*
@ -28,12 +30,14 @@ function check_access(&$account)
'passwd' => $_SERVER['PHP_AUTH_PW'],
'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("HTTP/1.1 401 Unauthorized");
header("X-WebDAV-Status: 401 Unauthorized", true);
exit;
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("X-WebDAV-Status: 401 Unauthorized", true);
exit;
}
return $sessionid;
}
@ -48,10 +52,14 @@ $GLOBALS['egw_info'] = array(
'noheader' => True,
'currentapp' => $app,
'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!
include(dirname(__FILE__).'/header.inc.php');
$headertime = microtime(true);
$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));