moved access checks into the class with a default implementation, so commands can override it, to be eg. anonymous or under other restrictions available

This commit is contained in:
Ralf Becker 2007-12-18 23:11:53 +00:00
parent 40a68b6cfd
commit e195efadeb
2 changed files with 95 additions and 46 deletions

View File

@ -19,18 +19,21 @@ abstract class admin_cmd
const scheduled = 1; const scheduled = 1;
const successful = 2; const successful = 2;
const failed = 3; const failed = 3;
const pending = 4;
/** /**
* The status of the command, one of either scheduled, successful, failed or deleted * The status of the command, one of either scheduled, successful, failed or deleted
* *
* @var int * @var int
*/ */
private $status; protected $status;
static $stati = array( static $stati = array(
admin_cmd::scheduled => 'scheduled', admin_cmd::scheduled => 'scheduled',
admin_cmd::successful => 'successful', admin_cmd::successful => 'successful',
admin_cmd::failed => 'failed', admin_cmd::failed => 'failed',
admin_cmd::deleted => 'deleted', admin_cmd::deleted => 'deleted',
admin_cmd::pending => 'pending',
); );
protected $created; protected $created;
@ -40,8 +43,8 @@ abstract class admin_cmd
private $modified; private $modified;
private $modifier; private $modifier;
private $modifier_email; private $modifier_email;
private $error; protected $error;
private $errno; protected $errno;
public $requested; public $requested;
public $requested_email; public $requested_email;
public $comment; public $comment;
@ -167,7 +170,7 @@ abstract class admin_cmd
{ {
$ret = $this->remote_exec(); $ret = $this->remote_exec();
} }
$this->status = admin_cmd::successful; if (is_null($this->status)) $this->status = admin_cmd::successful;
} }
catch (Exception $e) { catch (Exception $e) {
$this->error = $e->getMessage(); $this->error = $e->getMessage();
@ -229,7 +232,10 @@ abstract class admin_cmd
); );
$url = $remote['remote_url'].'/admin/remote.php?domain='.urlencode($remote['remote_domain']).'&secret='.urlencode($secret); $url = $remote['remote_url'].'/admin/remote.php?domain='.urlencode($remote['remote_domain']).'&secret='.urlencode($secret);
//echo "sending command to $url\n"; _debug_array($opts); //echo "sending command to $url\n"; _debug_array($opts);
$message = file_get_contents($url, false, stream_context_create($opts)); if (!($message = @file_get_contents($url, false, stream_context_create($opts))))
{
throw new egw_exception(lang('Could not remote execute the command').': '.$http_response_header[0]);
}
//echo "got: $message\n"; //echo "got: $message\n";
if (($value = unserialize($message)) !== false && $message !== serialize(false)) if (($value = unserialize($message)) !== false && $message !== serialize(false))
@ -283,7 +289,7 @@ abstract class admin_cmd
{ {
return false; return false;
} }
if (is_null($this->id)) if (!$this->id)
{ {
$this->id = admin_cmd::$sql->data['id']; $this->id = admin_cmd::$sql->data['id'];
// if the cmd has no uid yet, we create one from our id and the install-id of this eGW instance // if the cmd has no uid yet, we create one from our id and the install-id of this eGW instance
@ -429,6 +435,12 @@ abstract class admin_cmd
{ {
return $this->$property; // making all (non static) class vars readonly available return $this->$property; // making all (non static) class vars readonly available
} }
switch($property)
{
case 'accounts':
self::_instanciate_accounts();
return self::$accounts;
}
return $this->data[$property]; return $this->data[$property];
} }
@ -658,7 +670,7 @@ abstract class admin_cmd
* @todo accounts class instanciation for setup * @todo accounts class instanciation for setup
* @throws egw_exception_assertion_failed(lang('%1 class not instanciated','accounts'),999); * @throws egw_exception_assertion_failed(lang('%1 class not instanciated','accounts'),999);
*/ */
protected function _instanciate_accounts() static function _instanciate_accounts()
{ {
if (!is_object(admin_cmd::$accounts)) if (!is_object(admin_cmd::$accounts))
{ {
@ -857,7 +869,7 @@ abstract class admin_cmd
{ {
$data['remote_hash'] = self::remote_hash($data['install_id'],$data['config_passwd']); $data['remote_hash'] = self::remote_hash($data['install_id'],$data['config_passwd']);
} }
elseif ($data['install_id'] || $data['config_passwd'] || !$data['remote_hash']) elseif (!$data['remote_hash'] && !($data['install_id'] && $data['config_passwd']))
{ {
throw new egw_exception_wrong_userinput(lang('Either Install ID AND config password needed OR the remote hash!')); throw new egw_exception_wrong_userinput(lang('Either Install ID AND config password needed OR the remote hash!'));
} }
@ -914,4 +926,41 @@ abstract class admin_cmd
{ {
return preg_match('/^[0-9a-f]{32}$/',$str); return preg_match('/^[0-9a-f]{32}$/',$str);
} }
/**
* Check if the current command has the right crediential to be excuted remotely
*
* Command can reimplement that method, to allow eg. anonymous execution.
*
* This default implementation use a secret to authenticate with the installation,
* which is a md5 hash build from the uid of the command (to not allow to send new
* commands with an earsdroped secret) and the md5 hash of the md5 hash of the
* config password and the install_id (egw_admin_remote.remote_hash)
*
* @param string $secret hash used to authenticate the command (
* @param string $config_passwd of the current domain
* @throws egw_exception_no_permission
*/
function check_remote_access($secret,$config_passwd)
{
// as a security measure remote administration need to be enabled under Admin > Site configuration
list(,$remote_admin_install_id) = explode('-',$this->uid);
$allowed_remote_admin_ids = $GLOBALS['egw_info']['server']['allow_remote_admin'] ? explode(',',$GLOBALS['egw_info']['server']['allow_remote_admin']) : array();
// to authenticate with the installation we use a secret, which is a md5 hash build from the uid
// of the command (to not allow to send new commands with an earsdroped secret) and the md5 hash
// of the md5 hash of the config password and the install_id (egw_admin_remote.remote_hash)
if (is_null($config_passwd) || is_numeric($this->uid) || !in_array($remote_admin_install_id,$allowed_remote_admin_ids) ||
$secret != ($md5=md5($this->uid.$this->remote_hash($GLOBALS['egw_info']['server']['install_id'],$config_passwd))))
{
//die("secret='$secret' != '$md5', is_null($config_passwd)=".is_null($config_passwd).", uid=$this->uid, remote_install_id=$remote_admin_install_id, allowed: ".implode(', ',$allowed_remote_admin_ids));
$msg = lang('Permission denied!');
if (!in_array($remote_admin_install_id,$allowed_remote_admin_ids))
{
$msg .= "\n".lang('Remote administration need to be enabled in the remote instance under Admin > Site configuration!');
}
throw new egw_exception_no_permission($msg,0);
}
}
} }
admin_cmd::_instanciate_accounts();

View File

@ -22,6 +22,20 @@ $GLOBALS['egw_info'] = array(
include('../header.inc.php'); include('../header.inc.php');
// install an own exception handler to forward exceptions back to the remote side
function remote_exception_handler(Exception $e)
{
$msg = $e->getMessage();
if (is_object($GLOBALS['egw']->translation))
{
$msg = $GLOBALS['egw']->translation->convert($msg,$GLOBALS['egw']->translation->charset(),'utf-8');
}
header('HTTP/1.1 200 '.$msg);
echo $e->getCode().' '.$msg;
$GLOBALS['egw']->common->egw_exit();
}
set_exception_handler('remote_exception_handler');
$GLOBALS['egw']->applications->read_installed_apps(); // set $GLOBALS['egw_info']['apps'] (not set for login) $GLOBALS['egw']->applications->read_installed_apps(); // set $GLOBALS['egw_info']['apps'] (not set for login)
$instance = isset($_GET['domain']) ? $_GET['domain'] : $_REQUEST['domain']; // use GET before the rest $instance = isset($_GET['domain']) ? $_GET['domain'] : $_REQUEST['domain']; // use GET before the rest
@ -29,27 +43,8 @@ if (!isset($GLOBALS['egw_domain'][$instance]))
{ {
$instance = $GLOBALS['egw_info']['server']['default_domain']; $instance = $GLOBALS['egw_info']['server']['default_domain'];
} }
$domain_data = $GLOBALS['egw_domain'][$instance]; $config_passwd = $GLOBALS['egw_domain'][$instance]['config_passwd'];
//echo $instance; _debug_array($domain_data); unset($GLOBALS['egw_domain']);
// as a security measure remote administration need to be enabled under Admin > Site configuration
list(,$remote_admin_install_id) = explode('-',$_REQUEST['uid']);
$allowed_remote_admin_ids = $GLOBALS['egw_info']['server']['allow_remote_admin'] ? explode(',',$GLOBALS['egw_info']['server']['allow_remote_admin']) : array();
// to authenticate with the installation we use a secret, which is a md5 hash build from the uid
// of the command (to not allow to send new commands with an earsdroped secret) and the md5 hash
// of the md5 hash of the config password and the install_id (egw_admin_remote.remote_hash)
if (!$domain_data || is_numeric($_REQUEST['uid']) || !in_array($remote_admin_install_id,$allowed_remote_admin_ids) ||
$_REQUEST['secret'] != ($md5=md5($_REQUEST['uid'].admin_cmd::remote_hash($GLOBALS['egw_info']['server']['install_id'],$domain_data['config_passwd']))))
{
header("HTTP/1.1 200 Unauthorized");
//die("0 secret != '$md5'");
echo lang('0 Permission denied!');
if (!in_array($remote_admin_install_id,$allowed_remote_admin_ids))
{
echo "\n".lang('Remote administration need to be enabled in the remote instance under Admin > Site configuration!');
}
$GLOBALS['egw']->common->egw_exit();
}
require_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd.inc.php'); require_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd.inc.php');
@ -58,7 +53,15 @@ require_once(EGW_INCLUDE_ROOT.'/admin/inc/class.admin_cmd.inc.php');
$cmd = admin_cmd::read($_REQUEST['uid']); $cmd = admin_cmd::read($_REQUEST['uid']);
if (is_object($cmd)) if (is_object($cmd))
{ {
exit_with_status($cmd); $cmd->check_remote_access($_REQUEST['secret'],$config_passwd);
$success_msg = 'Successful';
// if the comand object has a rerun method, call it
if (method_exists($cmd,'rerun'))
{
$success_msg = $cmd->rerun();
}
exit_with_status($cmd,$success_msg);
} }
// check if requests contains a reasonable looking admin command to be queued // check if requests contains a reasonable looking admin command to be queued
@ -67,7 +70,7 @@ if (!$_REQUEST['uid'] || // no uid
!$_REQUEST['creator_email']) // no creator email !$_REQUEST['creator_email']) // no creator email
{ {
header("HTTP/1.1 200 Bad format!"); header("HTTP/1.1 200 Bad format!");
echo lang('0 Bad format!'); echo '0 Bad format!';
$GLOBALS['egw']->common->egw_exit(); $GLOBALS['egw']->common->egw_exit();
} }
@ -81,22 +84,18 @@ if (isset($data['modifier'])) $data['modifier'] = 0;
if (isset($data['requested'])) $data['requested'] = 0; if (isset($data['requested'])) $data['requested'] = 0;
// instanciate comand and run it // instanciate comand and run it
try { $cmd = admin_cmd::instanciate($data);
$cmd = admin_cmd::instanciate($data);
//_debug_array($cmd); exit;
$success_msg = $cmd->run();
$GLOBALS['egw']->translation->convert($success_msg,$GLOBALS['egw']->translation->charset(),'utf-8');
if (!is_string($success_msg)) $cmd->check_remote_access($_REQUEST['secret'],$config_passwd);
{
$success_msg = serialize($success_msg); //_debug_array($cmd); exit;
} $success_msg = $cmd->run();
}
catch (Exception $e) { $GLOBALS['egw']->translation->convert($success_msg,$GLOBALS['egw']->translation->charset(),'utf-8');
header('HTTP/1.1 200 '.$e->getMessage());
echo $e->getCode().' '.$e->getMessage(); if (!is_string($success_msg))
$GLOBALS['egw']->common->egw_exit(); {
$success_msg = serialize($success_msg);
} }
exit_with_status($cmd,$success_msg); exit_with_status($cmd,$success_msg);
@ -112,6 +111,7 @@ function exit_with_status($cmd,$success_msg='Successful')
default: // everything else is returned as 200 HTTP status default: // everything else is returned as 200 HTTP status
$success_msg = $cmd->stati[$cmd->status]; $success_msg = $cmd->stati[$cmd->status];
// fall through // fall through
case admin_cmd::pending:
case admin_cmd::successful: case admin_cmd::successful:
header('HTTP/1.1 200 '.$cmd->stati[$cmd->status]); header('HTTP/1.1 200 '.$cmd->stati[$cmd->status]);
header('Content-type: text/plain; charset=utf-8'); header('Content-type: text/plain; charset=utf-8');