Merge branch 'master' of ssh://git.icinga.org/icinga2-web into feature/history-api-4303

Conflicts:
	public/js/icinga/components/actionTable.js
	public/js/icinga/components/mainDetail.js
	public/js/icinga/container.js
	public/js/icinga/util/async.js
This commit is contained in:
Jannis Moßhammer 2013-06-27 10:20:14 +02:00
commit d71d15c822
47 changed files with 1462 additions and 343 deletions

3
.gitignore vendored
View File

@ -12,3 +12,6 @@ config.log
# cmd tester
test/php/bin/extcmd_test
# misc test output
test/frontend/static/public

View File

@ -6,8 +6,9 @@
# namespace Icinga\Application\Controllers;
use Icinga\Web\ActionController;
use Icinga\Authentication\Auth;
use Icinga\Web\Notification;
use Icinga\Authentication\Credentials as Credentials;
use Icinga\Authentication\Manager as AuthManager;
use Icinga\Form\Builder as FormBuilder;
/**
* Class AuthenticationController
@ -25,13 +26,67 @@ class AuthenticationController extends ActionController
*/
protected $modifiesSession = true;
private function getAuthForm()
{
return array(
'username' => array(
'text',
array(
'label' => t('Username'),
'required' => true,
)
),
'password' => array(
'password',
array(
'label' => t('Password'),
'required' => true
)
),
'submit' => array(
'submit',
array(
'label' => t('Login'),
'class' => 'pull-right'
)
)
);
}
/**
*
*/
public function loginAction()
{
$this->replaceLayout = true;
$this->view->form = $this->widget('form', array('name' => 'login'));
$credentials = new Credentials();
$this->view->form = FormBuilder::fromArray(
$this->getAuthForm(),
array(
"CSRFProtection" => false, // makes no sense here
"model" => &$credentials
)
);
try {
$auth = AuthManager::getInstance(null, array(
"writeSession" => true
));
if ($auth->isAuthenticated()) {
$this->redirectNow('index?_render=body');
}
if ($this->getRequest()->isPost() && $this->view->form->isSubmitted()) {
$this->view->form->repopulate();
if ($this->view->form->isValid()) {
if (!$auth->authenticate($credentials)) {
$this->view->form->getElement('password')->addError(t('Please provide a valid username and password'));
} else {
$this->redirectNow('index?_render=body');
}
}
}
} catch (\Icinga\Exception\ConfigurationError $configError) {
$this->view->errorInfo = $configError->getMessage();
}
}
/**
@ -39,11 +94,13 @@ class AuthenticationController extends ActionController
*/
public function logoutAction()
{
$auth = AuthManager::getInstance(null, array(
"writeSession" => true
));
$this->replaceLayout = true;
Auth::getInstance()->forgetAuthentication();
Notification::success('You have been logged out');
$auth->removeAuthorization();
$this->_forward('login');
}
}
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreEnd

View File

@ -34,7 +34,6 @@ class ErrorController extends ActionController
$this->view->message = 'Application error';
break;
}
// conditionally display exceptions
if ($this->getInvokeArg('displayExceptions') == true) {
$this->view->exception = $errors->exception;
@ -44,4 +43,4 @@ class ErrorController extends ActionController
}
}
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreEnd

View File

@ -0,0 +1,51 @@
<?php
// @codingStandardsIgnoreStart
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
# namespace Icinga\Application\Controllers;
use Icinga\Web\ActionController;
use Icinga\Application\Icinga;
class ModulesController extends ActionController
{
protected $manager;
public function init()
{
$this->manager = Icinga::app()->moduleManager();
}
public function indexAction()
{
$this->view->modules = $this->manager->select()
->from('modules')
->order('name');
$this->render('overview');
}
public function overviewAction()
{
$this->indexAction();
}
public function enableAction()
{
$this->manager->enableModule($this->_getParam('name'));
$this->manager->loadModule($this->_getParam('name'));
$this->getResponse()->setHeader('X-Icinga-Enable-Module', $this->_getParam('name'));
$this->replaceLayout = true;
$this->indexAction();
}
public function disableAction()
{
$this->manager->disableModule($this->_getParam('name'));
$this->redirectNow('modules/overview?_render=body');
}
}

View File

@ -9,6 +9,7 @@ use Icinga\Web\ActionController;
use Icinga\Application\Icinga;
use Zend_Controller_Action_Exception as ActionException;
use Icinga\Application\Benchmark;
/**
* Class StaticController
* @package Icinga\Web\Form
@ -69,7 +70,7 @@ class StaticController extends ActionController
*/
public function javascriptAction()
{
$module = $this->_getParam('moduleName');
$module = $this->_getParam('module_name');
$file = $this->_getParam('file');
$basedir = Icinga::app()->getModule($module)->getBaseDir();
@ -96,15 +97,16 @@ class StaticController extends ActionController
) . ' GMT'
);
$hash = md5_file($filePath);
if ($hash === $this->getRequest()->getHeader('If-None-Match')) {
$response->setHttpResponseCode(304);
return;
} else {
readfile($filePath);
}
// TODO: get rid of exit:
exit;
return;
}
}
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreEnd

View File

@ -12,14 +12,6 @@
</div>
<div id="icinga-detail" class="icinga-container " container-id="icinga-detail">
<div class="container-controls">
<a class="container-expand-link" title="expand" target="_self" href="/develop/monitoring/list/services">
<i class="icon-fullscreen"></i>
</a>
<a class="container-detach-link" title="detach" target="popup" href="/develop/develop/monitoring/list/services">
<i class="icon-share"></i>
</a>
</div>
</div>
</div>

View File

@ -21,12 +21,12 @@
ICINGA_DEBUG = true;
</script>
<script src="<?php echo $this->baseUrl('js/vendor/modernizr-2.6.2.min.js') ?>"></script>
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/icinga.css') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css.php') ?>">
<link rel="stylesheet" href="<?php echo $this->baseUrl('css/vendor/bootstrap-responsive.min.css') ?>">
<script data-main="<?php echo $this->baseUrl('js/main.js')?>" src="<?php echo $this->baseUrl('js/vendor/require.js') ?>"></script>
</head>
<body class="cranberry">
<body class="cranberry">
<?php echo $this->render('parts/topbar.phtml') ?>
<div class="main">
<div class="tabbable tabs-left" style="height:100%;">

View File

@ -5,7 +5,8 @@
$item = $this->navigation->listAll("menu");
?>
<?php if ($this->auth()->isAuthenticated()): ?>
<?php if (true || $this->auth()->isAuthenticated()): ?>
<ul class="nav nav-tabs icinga-navigation" >
<?php
$activeSet = false;

View File

@ -12,7 +12,7 @@
</li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><?php echo
$this->escape($this->auth()->getUsername())
$this->escape($this->auth()->getUser()->getUsername())
?> <i class="icon-user icon-white" style="margin-top:0.2em"></i>
<b class="caret"></b>
</a>

View File

@ -36,6 +36,9 @@ class Zend_View_Helper_Qlink extends Zend_View_Helper_Abstract
$quote = $val;
continue;
}
if ($key === 'target') {
$attibutes[] = 'target="'.$val.'"';
}
if ($key === 'style' && is_array($val)) {
if (empty($val)) {
continue;
@ -93,4 +96,4 @@ class Zend_View_Helper_Qlink extends Zend_View_Helper_Abstract
return is_null($baseUrl) ? $this->view->baseUrl($url) : $baseUrl . $url;
}
}
// @codingStandardsIgnoreEnd
// @codingStandardsIgnoreEnd

View File

@ -1 +1,10 @@
<?php echo $this->form ?>
<div class="well" style="margin:auto;width: 40%;max-width:25em;margin-top:10%;">
<h1> Login </h1>
<?php echo $this->form->render() ?>
<?php if (isset($this->errorInfo)): ?>
<div class="alert alert-error">
<?= $this->errorInfo ?>
</div>
<?php endif ?>
</div>

View File

@ -9,8 +9,15 @@
<div style="text-align:left;">
<h3>Exception information:</h3>
<p >
<b>Message:</b> <?php echo $this->exception->getMessage() ?>
<p>
<b>Message:</b> <?php echo $this->exception->getMessage() ?>
<?php if (isset($this->exception->action)): ?>
<br/><b>Action</b>: <?= $this->exception->action; ?>
<?php endif ?>
<?php if (isset($this->exception->action)): ?>
<br/><b>Target</b>: <?= $this->exception->target; ?>
<?php endif ?>
</p>
<h3>Stack trace:</h3>

View File

@ -0,0 +1,3 @@
<?= $this->layout()->moduleStart ?>
<?= $this->layout()->content ?>
<?= $this->layout()->moduleEnd ?>

View File

@ -0,0 +1,61 @@
<? if ($this->pageCount > 1): ?>
<?php
if (is_array($this->preserve)) {
$preserve = $this->preserve;
} else {
$preserve = array();
}
$fromto = t('%d to %d of %d');
$total = $this->totalItemCount;
?>
<div class="pagination pagination-mini" style="margin:0px">
<ul>
<!-- Previous page link -->
<? if (isset($this->previous)): ?>
<li><a href="<?= $this->url($preserve + array('page' => $this->previous)); ?>" title="<?=
sprintf($fromto,
($this->current - 2) * $this->itemCountPerPage + 1,
($this->current - 1) * $this->itemCountPerPage,
$this->totalItemCount)
?>">« <?= t('Back') ?></a></li>
<? else: ?>
<li class="disabled"><span>« <?= t('Back') ?></span></li>
<?php
endif;
?>
<!-- Numbered page links -->
<? foreach ($this->pagesInRange as $page): ?>
<?php
$start = ($page - 1) * $this->itemCountPerPage + 1;
$end = $page * $this->itemCountPerPage;
if ($end > $total) {
$end = $total;
}
$title = sprintf($fromto, $start, $end, $total);
$active_class = $page === $this->current ? ' class="active"' : '';
?>
<?php if ($page === '...'): ?>
<li class="disabled"><span>...</span></li>
<?php else: ?>
<li<?= $active_class ?>><a href="<?= $this->url(
$preserve + array('page' => $page)
); ?>" title="<?= $title ?>"><?= $page; ?></a></li>
<? endif ?>
<? endforeach ?>
<!-- Next page link -->
<? if (isset($this->next)): ?>
<li><a href="<?= $this->url($preserve + array('page' => $this->next)); ?>" title="<?=
sprintf($fromto,
($this->current) * $this->itemCountPerPage + 1,
($this->current + 1) * $this->itemCountPerPage,
$total)
?>"><?= t('Next') ?> »</a></li>
<? else: ?>
<li class="disabled"><span><?= t('Next') ?> »</span></li>
<? endif ?>
</ul>
</div>
<? endif ?>

View File

@ -0,0 +1,38 @@
<?php
$this->modules->limit(10);
$modules = $this->modules->paginate();
?>
<h3>Installed Modules</h3>
<?= $this->paginationControl($modules, null, null, array(
'preserve' => $this->preserve
)); ?>
<table >
<tbody>
<? foreach ($modules as $module): ?>
<tr>
<td>
<? if ($module->enabled): ?>
<i class="icon-ok-sign"></i> <?= $this->qlink(
$module->name,
'modules/disable',
array('name' => $module->name),
array('target'=>'body')
) ?>
<? else: ?>
<i class="icon-remove-sign"></i> <?= $this->qlink(
$module->name,
'modules/enable',
array('name' => $module->name),
array('target'=>'body')
) ?>
<? endif ?>
(<?=
$module->enabled
? ($module->loaded ? 'enabled' : 'failed')
: 'disabled'
?>)</td>
</tr>
<? endforeach ?>
</tbody>
</table>

View File

@ -0,0 +1,24 @@
<?php $container = $this->container('modules-container',array(
"class" => "expanded_absolute"
))->beginContent() ?>
<table class="table table-bordered" style="width:100%">
<thead>
<tr style="text-align:left">
<th width="70%">Module</th>
<th width="15%">Type</th>
<th width="15%">Active</th>
</tr>
</thead>
<tbody>
<?php foreach($this->modules as $module): ?>
<tr>
<td><?= $module["name"] ?></td>
<td><?= $module["type"] ?></td>
<td><?= $module["active"] ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?= $container; ?>

View File

@ -1,2 +1,9 @@
[users]
backend=ldap
hostname=localhost
root_dn="ou=people,dc=icinga,dc=org"
bind_dn="cn=admin,cn=config"
bind_pw=admin
user_class=inetOrgPerson
user_name_attribute=uid

View File

@ -3,6 +3,7 @@ environment = development
timezone = "Europe/Berlin"
indexModule = monitoring
indexController = dashboard
moduleFolder = "/usr/etc/icinga-modules"
[logging]
; General log

View File

@ -18,6 +18,7 @@ use Icinga\Exception\ConfigurationError;
* Usage example for CLI:
* <code>
* use Icinga\Application\Cli;
* Cli::start();
* </code>
*
@ -99,7 +100,7 @@ abstract class ApplicationBootstrap
public function moduleManager()
{
if ($this->moduleManager === null) {
$this->moduleManager = new ModuleManager($this);
$this->moduleManager = new ModuleManager($this, $this->config->global->moduleFolder);
}
return $this->moduleManager;
}

View File

@ -2,35 +2,45 @@
namespace Icinga\Application\Modules;
use Icinga\Application\ApplicationBootstrap;
use Icinga\Data\ArrayDatasource;
use Icinga\Web\Notification;
use Icinga\Exception\ConfigurationError;
use Icinga\Exception\SystemPermissionException;
use Icinga\Exception\ProgrammingError;
// TODO: show whether enabling/disabling modules is allowed by checking enableDir
// perms
class Manager
{
protected $installedBaseDirs;
protected $installedBaseDirs = null;
protected $enabledDirs = array();
protected $loadedModules = array();
protected $index;
protected $app;
protected $enableDir;
public function __construct(ApplicationBootstrap $app)
protected $modulePaths = array();
/**
* @param $app : The applicaiton bootstrap. This one needs a properly defined interface
* In order to test it correctly, the application now only requires a stdClass
* @param $dir :
**/
public function __construct($app, $enabledDir = null, array $availableDirs = array())
{
$this->app = $app;
$this->prepareEssentials();
if (empty($availableDirs)) {
$availableDirs = array(ICINGA_APPDIR."/../modules");
}
$this->modulePaths = $availableDirs;
if ($enabledDir === null) {
$enabledDir = $this->app->getConfig()->getConfigDir()
. '/enabledModules';
}
$this->prepareEssentials($enabledDir);
$this->detectEnabledModules();
}
protected function prepareEssentials()
protected function prepareEssentials($moduleDir)
{
$this->enableDir = $this->app->getConfig()->getConfigDir()
. '/enabledModules';
$this->enableDir = $moduleDir;
if (! file_exists($this->enableDir) || ! is_dir($this->enableDir)) {
throw new ProgrammingError(
@ -42,10 +52,17 @@ class Manager
}
}
public function select()
{
$source = new \Icinga\Data\ArrayDatasource($this->getModuleInfo());
return $source->select();
}
protected function detectEnabledModules()
{
$fh = opendir($this->enableDir);
$this->enabledDirs = array();
while (false !== ($file = readdir($fh))) {
if ($file[0] === '.') {
@ -74,12 +91,18 @@ class Manager
return $this;
}
public function loadModule($name)
public function loadModule($name, $moduleBase = null)
{
if ($this->hasLoaded($name)) {
return $this;
}
$module = new Module($this->app, $name, $this->getModuleDir($name));
$module = null;
if ($moduleBase === null) {
$module = new Module($this->app, $name, $this->getModuleDir($name));
} else {
$module = new $moduleBase($this->app, $name, $this->getModuleDir($name));
}
$module->register();
$this->loadedModules[$name] = $module;
return $this;
@ -96,17 +119,26 @@ class Manager
);
return $this;
}
clearstatcache(true);
$target = $this->installedBaseDirs[$name];
$link = $this->enableDir . '/' . $name;
if (! is_writable($this->enableDir)) {
Notification::error("I do not have permissions to enable modules");
throw new SystemPermissionException(
"Insufficient system permissions for enabling modules",
"write",
$this->enableDir
);
}
if (file_exists($link) && is_link($link)) {
return $this;
}
if (@symlink($target, $link)) {
Notification::success("The module $name has been enabled");
} else {
Notification::error("Enabling module $name failed");
if (!@symlink($target, $link)) {
$error = error_get_last();
if (strstr($error["message"], "File exists") === false) {
throw new SystemPermissionException($error["message"], "symlink", $link);
}
}
$this->enabledDirs[$name] = $link;
return $this;
}
@ -116,17 +148,31 @@ class Manager
return $this;
}
if (! is_writable($this->enableDir)) {
Notification::error("I do not have permissions to disable modules");
throw new SystemPermissionException("Can't write the module directory", "write", $this->enableDir);
return $this;
}
$link = $this->enableDir . '/' . $name;
if (file_exists($link) && is_link($link)) {
if (@unlink($link)) {
Notification::success("The module $name has been disabled");
} else {
Notification::error("Disabling module $name failed");
}
if (!file_exists($link)) {
throw new ConfigurationError("The module $name could not be found, can't disable it");
}
if (!is_link($link)) {
throw new ConfigurationError(
"The module $name can't be disabled as this would delete the whole module. ".
"It looks like you have installed this module manually and moved it to your module folder.".
"In order to dynamically enable and disable modules, you have to create a symlink to ".
"the enabled_modules folder"
);
}
if (file_exists($link) && is_link($link)) {
if (!@unlink($link)) {
$error = error_get_last();
throw new SystemPermissionException($error["message"], "unlink", $link);
}
} else {
}
unset($this->enabledDirs[$name]);
return $this;
}
@ -192,7 +238,12 @@ class Manager
public function getModuleInfo()
{
$installed = $this->listInstalledModules();
$info = array();
if ($installed === null) {
return $info;
}
foreach ($installed as $name) {
$info[] = (object) array(
'name' => $name,
@ -204,12 +255,6 @@ class Manager
return $info;
}
public function select()
{
$ds = new ArrayDatasource($this->getModuleInfo());
return $ds->select();
}
public function listEnabledModules()
{
return array_keys($this->enabledDirs);
@ -225,24 +270,27 @@ class Manager
if ($this->installedBaseDirs === null) {
$this->detectInstalledModules();
}
return array_keys($this->installedBaseDirs);
if ($this->installedBaseDirs !== null) {
return array_keys($this->installedBaseDirs);
}
}
public function detectInstalledModules()
{
// TODO: Allow multiple paths for installed modules (e.g. web vs pkg)
$basedir = realpath(ICINGA_APPDIR . '/../modules');
$fh = @opendir($basedir);
if ($fh === false) {
return $this;
}
while ($name = readdir($fh)) {
if ($name[0] === '.') {
continue;
foreach ($this->modulePaths as $basedir) {
$fh = opendir($basedir);
if ($fh === false) {
return $this;
}
if (is_dir($basedir . '/' . $name)) {
$this->installedBaseDirs[$name] = $basedir . '/' . $name;
while ($name = readdir($fh)) {
if ($name[0] === '.') {
continue;
}
if (is_dir($basedir . '/' . $name)) {
$this->installedBaseDirs[$name] = $basedir . '/' . $name;
}
}
}
}

View File

@ -38,7 +38,6 @@ class Web extends ApplicationBootstrap
return $this->loadConfig()
->configureErrorHandling()
->setTimezone()
->configureSession()
->configureCache()
->prepareZendMvc()
->loadTranslations()
@ -60,6 +59,17 @@ class Web extends ApplicationBootstrap
)
)
);
$this->frontController->getRouter()->addRoute(
'module_javascript',
new Route(
'js/modules/:module_name/:file',
array(
'controller' => 'static',
'action' => 'javascript'
)
)
);
return $this;
}
@ -80,16 +90,6 @@ class Web extends ApplicationBootstrap
$this->dispatchFrontController();
}
/**
* Configure web session settings
*
* @return self
*/
protected function configureSession()
{
Manager::getInstance();
return $this;
}
protected function loadTranslations()
{
@ -173,6 +173,7 @@ class Web extends ApplicationBootstrap
*/
protected function configurePagination()
{
Paginator::addScrollingStylePrefixPath(
'Icinga_Web_Paginator_ScrollingStyle',
'Icinga/Web/Paginator/ScrollingStyle'

View File

@ -6,6 +6,9 @@ namespace Icinga\Authentication;
class Backend
{
/**
* @var UserBackend
*/
protected $userBackend;
public function __construct($config)

View File

@ -5,7 +5,10 @@
namespace Icinga\Authentication\Backend;
use Icinga\Authentication\User as User;
use Icinga\Authentication\UserBackend;
use Icinga\Authentication\Credentials;
use Icinga\Protocol\Ldap;
use Icinga\Application\Config;
class LdapUserBackend implements UserBackend
{
@ -16,14 +19,11 @@ class LdapUserBackend implements UserBackend
$this->connection = new Ldap\Connection($config);
}
public function hasUsername($username)
public function hasUsername(Credentials $credential)
{
if (!$username) {
return false;
}
return $this->connection->fetchOne(
$this->selectUsername($username)
) === $username;
$this->selectUsername($credential->getUsername())
) === $credential->getUsername();
}
protected function stripAsterisks($string)
@ -34,23 +34,28 @@ class LdapUserBackend implements UserBackend
protected function selectUsername($username)
{
return $this->connection->select()
->from('user', array('sAMAccountName'))
->where('sAMAccountName', $this->stripAsterisks($username));
->from(
Config::getInstance()->authentication->users->user_class,
array(
Config::getInstance()->authentication->users->user_name_attribute
)
)
->where(
Config::getInstance()->authentication->users->user_name_attribute,
$this->stripAsterisks($username)
);
}
public function authenticate($username, $password = null)
public function authenticate(Credentials $credentials)
{
if (empty($username) || empty($password)) {
return false;
}
if (!$this->connection->testCredentials(
$this->connection->fetchDN($this->selectUsername($username)),
$password
) ) {
$this->connection->fetchDN($this->selectUsername($credentials->getUsername())),
$credentials->getPassword()
)
) {
return false;
}
$user = new User($username);
$user = new User($credentials->getUsername());
return $user;
}

View File

@ -6,6 +6,7 @@ namespace Icinga\Authentication;
use Icinga\Application\Logger as Logger;
use Icinga\Application\Config as Config;
use Icinga\Exception\ConfigurationError as ConfigError;
class Manager
{
@ -25,7 +26,6 @@ class Manager
if ($config === null) {
$config = Config::getInstance()->authentication;
}
if (isset($options["userBackendClass"])) {
$this->userBackend = $options["userBackendClass"];
} elseif ($config->users !== null) {
@ -77,6 +77,15 @@ class Manager
public function authenticate(Credentials $credentials, $persist = true)
{
if (!$this->userBackend) {
Logger::error("No authentication backend provided, your users will never be able to login.");
throw new ConfigError(
"No authentication backend set - login will never succeed as icinga-web ".
"doesn't know how to determine your user. \n".
"To fix this error, setup your authentication.ini with a valid authentication backend."
);
return false;
}
if (!$this->userBackend->hasUsername($credentials)) {
Logger::info("Unknown user %s tried to log in", $credentials->getUsername());
return false;
@ -115,7 +124,7 @@ class Manager
public function removeAuthorization()
{
$this->user = null;
$this->session->delete();
$this->session->purge();
}
public function getUser()

View File

@ -28,7 +28,6 @@ class PhpSession extends Session
private static $DEFAULT_COOKIEOPTIONS = array(
'use_trans_sid' => false,
'use_cookies' => true,
'use_only_cooies' => true,
'cookie_httponly' => true,
'use_only_cookies' => true,
'hash_function' => true,
@ -51,6 +50,9 @@ class PhpSession extends Session
);
}
}
if (!is_writable(session_save_path())) {
throw new \Icinga\Exception\ConfigurationError("Can't save session");
}
}
private function sessionCanBeChanged()
@ -78,12 +80,7 @@ class PhpSession extends Session
}
session_name(PhpSession::SESSION_NAME);
/*
* @todo This is not right
*/
\Zend_Session::start();
// session_start();
session_start();
$this->isOpen = true;
$this->setAll($_SESSION);
return true;
@ -138,16 +135,18 @@ class PhpSession extends Session
public function purge()
{
if ($this->ensureOpen() && !$this->isFlushed) {
if ($this->ensureOpen()) {
$_SESSION = array();
session_destroy();
$this->clearCookies();
$this->close();
}
}
private function clearCookies()
{
if (ini_get("session.use_cookies")) {
Logger::debug("Clearing cookies");
$params = session_get_cookie_params();
setcookie(
session_name(),

View File

@ -20,15 +20,15 @@ namespace Icinga\Authentication;
*/
class User
{
private $username = "";
private $firstname = "";
private $lastname = "";
private $email = "";
private $domain = "";
private $additionalInformation = array();
public $username = "";
public $firstname = "";
public $lastname = "";
public $email = "";
public $domain = "";
public $additionalInformation = array();
private $permissions = array();
private $groups = array();
public $permissions = array();
public $groups = array();
public function __construct($username, $firstname = null, $lastname = null, $email = null)
{

View File

@ -1,15 +1,28 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}}
namespace Icinga\Authentication;
interface UserBackend
{
/**
* Creates a new object
* @param $config
*/
public function __construct($config);
/**
* Test if the username exists
* @param Credentials $credentials
* @return boolean
*/
public function hasUsername(Credentials $credentials);
/**
* Authenticate
* @param Credentials $credentials
* @return User
*/
public function authenticate(Credentials $credentials);
}

View File

@ -0,0 +1,22 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Exception;
/**
* Class ProgrammingError
* @package Icinga\Exception
*/
class SystemPermissionException extends \Exception
{
public $action;
public $target;
public function __construct($message, $action, $target = "")
{
parent::__construct($message);
$this->action = $action;
$this->target = $target;
}
}

View File

@ -0,0 +1,385 @@
<?php
// {{{ICINGA_LICENSE_HEADER}}}
// {{{ICINGA_LICENSE_HEADER}}}
namespace Icinga\Form;
/**
* Class that helps building and validating forms and offers a rudimentary
* data-binding mechanismn.
*
* The underlying form can be accessed by expicitly calling $builder->getForm() or
* by directly calling the forms method (which is, in case of populate() the preferred way)
* like: $builder->getElements()
*
* @method \Zend_Form_Element getElement(string $name)
* @method \Zend_Form addElement(\string $element, string $name = null, array $options = null)
* @method \Zend_Form setView(\Zend_View $view)
* @package Icinga\Form
*/
class Builder
{
const CSRF_ID = "icinga_csrf_id";
/**
* @var \Zend_Form
*/
private $form;
/**
* @var null
*/
private $boundModel = null;
/**
* @var bool
*/
private $disableCSRF = false;
/**
* Constructrs a new Formbuilder, containing an empty form if no
* $form parameter is given or the Zend form from the $form parameter.
*
* @param \Zend_Form $form The form to use with this Builder
* @param Array $options an optional array of Options:
* - CSRFProtection true to add a crsf token to the
* form (default), false to remove it
* - model An referenced array or object to use
* for value binding
**/
public function __construct(\Zend_Form $form = null, array $options = array())
{
if ($form === null) {
$myModel = array(
"username" => "",
"password" => ""
);
$form = new \Zend_Form();
}
$this->setZendForm($form);
if (isset($options["CSRFProtection"])) {
$this->disableCSRF = !$options["CSRFProtection"];
}
if (isset($options["model"])) {
$this->bindToModel($options["model"]);
}
}
/**
* Setter for Zend_Form
* @param \Zend_Form $form
*/
public function setZendForm(\Zend_Form $form)
{
$this->form = $form;
}
/**
* Getter for Zent_Form
* @return \Zend_Form
*/
public function getForm()
{
if (!$this->disableCSRF) {
$this->addCSRFFieldToForm();
}
if (!$this->form) {
return new \Zend_Form();
}
return $this->form;
}
/**
* Add elements to form
* @param array $elements
*/
public function addElementsFromConfig(array $elements)
{
foreach ($elements as $key => $values) {
$this->addElement($values[0], $key, $values[1]);
}
}
/**
* Quick add elements to a new builder instance
* @param array $elements
* @param array $options
* @return Builder
*/
public static function fromArray(array $elements, $options = array())
{
$builder = new Builder(null, $options);
$builder->addElementsFromConfig($elements);
return $builder;
}
/**
* Test that the form is valid
*
* @param array $data
* @return bool
*/
public function isValid(array $data = null)
{
if ($data === null) {
$data = $_POST;
}
return $this->hasValidToken() && $this->form->isValid($data);
}
/**
* Test if the form was submitted
* @param string $btnName
* @return bool
*/
public function isSubmitted($btnName = 'submit')
{
$btn = $this->getElement($btnName);
if (!$btn || !isset($_POST[$btnName])) {
return false;
}
return $_POST[$btnName] === $btn->getLabel();
}
/**
* Render the form
* @return string
*/
public function render()
{
return $this->getForm()->render();
}
public function __toString()
{
return $this->getForm()->__toString();
}
/**
* Enable CSRF token field
*/
public function enableCSRF()
{
$this->disableCSRF = false;
}
/**
* Disable CSRF token field
*/
public function disableCSRF()
{
$this->disableCSRF = true;
$this->form->removeElement(self::CSRF_ID);
$this->form->removeElement(self::CSRF_ID."_seed");
}
/**
* Add CSRF field to form
*/
private function addCSRFFieldToForm()
{
if (!$this->form || $this->disableCSRF || $this->form->getElement(self::CSRF_ID)) {
return;
}
list($seed, $token) = $this->getSeedTokenPair();
$this->form->addElement("hidden", self::CSRF_ID);
$this->form->getElement(self::CSRF_ID)
->setValue($token)
->setDecorators(array('ViewHelper'));
$this->form->addElement("hidden", self::CSRF_ID."_seed");
$this->form->getElement(self::CSRF_ID."_seed")
->setValue($seed)
->setDecorators(array('ViewHelper'));
}
/**
* Bind model to a form
* @param $model
*/
public function bindToModel(&$model)
{
$this->boundModel = &$model;
}
/**
* Repopulate
*/
public function repopulate()
{
if (!empty($_POST)) {
$this->populate($_POST);
}
}
/**
* Populate form
* @param $data
* @param bool $ignoreModel
* @throws \InvalidArgumentException
*/
public function populate($data, $ignoreModel = false)
{
if (is_array($data)) {
$this->form->populate($data);
} elseif (is_object($data)) {
$this->populateFromObject($data);
} else {
throw new \InvalidArgumentException("Builder::populate() expects and object or array, $data given");
}
if ($this->boundModel === null || $ignoreModel) {
return;
}
$this->updateModel();
}
/**
* Populate form object
* @param $data
*/
private function populateFromObject($data)
{
/** @var \Zend_Form_Element $element */
foreach ($this->form->getElements() as $name => $element) {
if (isset($data->$name)) {
$element->setValue($data->$name);
} else {
$getter = "get".ucfirst($name);
if (method_exists($data, $getter)) {
$element->setValue($data->$getter());
}
}
}
}
/**
* Update model instance
*/
public function updateModel()
{
if (is_array($this->boundModel)) {
$this->updateArrayModel();
} elseif (is_object($this->boundModel)) {
$this->updateObjectModel();
}
}
/**
* Updater for objects
*/
private function updateObjectModel()
{
/** @var \Zend_Form_Element $element */
foreach ($this->form->getElements() as $name => $element) {
if (isset($this->boundModel->$name)) {
$this->boundModel->$name = $element->getValue();
} else {
$setter = "set".ucfirst($name);
if (method_exists($this->boundModel, $setter)) {
$this->boundModel->$setter($element->getValue());
}
}
}
}
/**
* Updater for arrays
*/
private function updateArrayModel()
{
/** @var \Zend_Form_Element $element */
foreach ($this->form->getElements() as $name => $element) {
if (isset($this->boundModel[$name])) {
$this->boundModel[$name] = $element->getValue();
}
}
}
/**
* Synchronize model with form
*/
public function syncWithModel()
{
$this->populate($this->boundModel, true);
}
/**
* Magic caller, pass through method calls to form
* @param $fn
* @param array $args
* @return mixed
* @throws \BadMethodCallException
*/
public function __call($fn, array $args)
{
if (method_exists($this->form, $fn)) {
return call_user_func_array(array($this->form, $fn), $args);
} else {
throw new \BadMethodCallException(
"Method $fn does not exist either ".
"in \Icinga\Form\Builder nor in Zend_Form"
);
}
}
/**
* Whether the token parameter is valid
*
* @param int $maxAge Max allowed token age
* @param string $sessionId A specific session id (useful for tests?)
*
* @return bool
*/
public function hasValidToken($maxAge = 600, $sessionId = null)
{
if ($this->disableCSRF) {
return true;
}
if ($this->form->getElement(self::CSRF_ID) == null) {
return false;
}
$sessionId = $sessionId ? $sessionId : session_id();
$seed = $this->form->getElement(self::CSRF_ID.'_seed')->getValue();
if (! is_numeric($seed)) {
return false;
}
// Remove quantitized timestamp portion so maxAge applies
$seed -= (intval(time() / $maxAge) * $maxAge);
$token = $this->getElement(self::CSRF_ID)->getValue();
return $token === hash('sha256', $sessionId . $seed);
}
/**
* Get a new seed/token pair
*
* @param int $maxAge Max allowed token age
* @param string $sessionId A specific session id (useful for tests?)
*
* @return array
*/
public function getSeedTokenPair($maxAge = 600, $sessionId = null)
{
$sessionId = $sessionId ? $sessionId : session_id();
$seed = mt_rand();
$hash = hash('sha256', $sessionId . $seed);
// Add quantitized timestamp portion to apply maxAge
$seed += (intval(time() / $maxAge) * $maxAge);
return array($seed, $hash);
}
}

View File

@ -4,6 +4,7 @@
namespace Icinga\Protocol\Ldap;
use Icinga\Exception\ConfigurationError as ConfigError;
use Icinga\Application\Platform;
use Icinga\Application\Config;
use Icinga\Application\Logger as Log;
@ -69,6 +70,10 @@ class Connection
// '1.3.6.1.1.8' => '8', // Cancel Extended Request
);
protected $use_tls = false;
protected $force_tls = false;
/**
* @var array
*/
@ -116,7 +121,8 @@ class Connection
$this->bind_dn = $config->bind_dn;
$this->bind_pw = $config->bind_pw;
$this->root_dn = $config->root_dn;
$this->use_tls = isset($config->tls) ? $config->tls : false;
$this->force_tls = $this->use_tls;
}
/**
@ -266,6 +272,7 @@ class Connection
// We do not support pagination right now, and there is no chance to
// do so for PHP < 5.4. Warnings about "Sizelimit exceeded" will
// therefore not be hidden right now.
Log::debug("Query: %s", $query->__toString());
$results = ldap_search(
$this->ds,
$this->root_dn,
@ -405,7 +412,6 @@ class Connection
if (isset($result->supportedExtension)) {
foreach ($result->supportedExtension as $oid) {
if (array_key_exists($oid, $this->ldap_extension)) {
echo $this->ldap_extension[$oid] . "\n";
// STARTTLS -> läuft mit OpenLDAP
}
}
@ -429,23 +435,23 @@ class Connection
if ($this->ds !== null) {
return;
}
$use_tls = true;
$force_tls = true;
if ($use_tls) {
if ($this->use_tls) {
$this->prepareTlsEnvironment();
}
Log::debug("Trying to connect to %s", $this->hostname);
$this->ds = ldap_connect($this->hostname, 389);
$this->discoverCapabilities();
Log::debug("Trying ldap_start_tls()");
if (ldap_start_tls($this->ds)) {
Log::debug("Trying ldap_start_tls() succeeded");
} else {
Log::warn(
"ldap_start_tls() failed: %s. Does your ldap_ca.conf point to the certificate? ",
ldap_error($this->ds)
);
if ($this->use_tls) {
Log::debug("Trying ldap_start_tls()");
if (@ldap_start_tls($this->ds)) {
Log::debug("Trying ldap_start_tls() succeeded");
} else {
Log::warn(
"ldap_start_tls() failed: %s. Does your ldap_ca.conf point to the certificate? ",
ldap_error($this->ds)
);
}
}
@ -470,12 +476,10 @@ class Connection
'***',
ldap_error($this->ds)
);
throw new Exception(
throw new ConfigError(
sprintf(
'LDAP connection (%s / %s) failed: %s',
$this->bind_dn,
'***' /* $this->bind_pw */,
ldap_error($this->ds)
'Could not connect to the authentication server, please '.
'review your LDAP connection settings.'
)
);
}

View File

@ -110,10 +110,6 @@ class ActionController extends ZfController
* @todo remove this!
*/
$this->allowAccess = true;
$this->init();
return null;
if ($this->handlesAuthentication() ||
Manager::getInstance(
@ -338,48 +334,4 @@ class ActionController extends ZfController
}*/
}
/**
* Whether the token parameter is valid
*
* TODO: Could this make use of Icinga\Web\Session once done?
*
* @param int $maxAge Max allowed token age
* @param string $sessionId A specific session id (useful for tests?)
* @return bool
*/
public function hasValidToken($maxAge = 600, $sessionId = null)
{
$sessionId = $sessionId ? $sessionId : session_id();
$seed = $this->_getParam('seed');
if (!is_numeric($seed)) {
return false;
}
// Remove quantitized timestamp portion so maxAge applies
$seed -= (intval(time() / $maxAge) * $maxAge);
$token = $this->_getParam('token');
return $token === hash('sha256', $sessionId . $seed);
}
/**
* Get a new seed/token pair
*
* TODO: Could this make use of Icinga\Web\Session once done?
*
* @param int $maxAge Max allowed token age
* @param string $sessionId A specific session id (useful for tests?)
*
* @return array
*/
public function getSeedTokenPair($maxAge = 600, $sessionId = null)
{
$sessionId = $sessionId ? $sessionId : session_id();
$seed = mt_rand();
$hash = hash('sha256', $sessionId . $seed);
// Add quantitized timestamp portion to apply maxAge
$seed += (intval(time() / $maxAge) * $maxAge);
return array($seed, $hash);
}
}

View File

@ -7,7 +7,6 @@ namespace Icinga\Web;
use Icinga\Exception\ProgrammingError;
use Icinga\Application\Platform;
use Icinga\Application\Logger as Log;
use Zend_Session_Namespace as SessionNamespace;
/**
* Class Notification
@ -149,10 +148,10 @@ class Notification
*/
final private function __construct()
{
$this->session = new SessionNamespace('IcingaNotification');
if (!is_array($this->session->messages)) {
//$this->session = new SessionNamespace('IcingaNotification');
//if (!is_array($this->session->messages)) {
$this->session->messages = array();
}
//}
if (Platform::isCli()) {
$this->cliFlag = true;

View File

@ -16,45 +16,52 @@ define(['jquery','logging','icinga/util/async'],function($,log,async) {
href = a.attr("href");
ev.stopImmediatePropagation();
collapseDetailView();
async.loadToTarget("icinga-main",href);
return false;
};
var onLinkTagClick = function(ev) {
var a = $(ev.currentTarget),
target = a.attr("target"),
href = a.attr("href");
// check for protocol://
if(/^[A-Z]{2,10}\:\/\//i.test(href)) {
window.open(href);
ev.stopImmediatePropagation();
return false;
}
// check for link in table header
if(a.parents('th').length > 0) {
ev.stopImmediatePropagation();
return false;
}
if(typeof target === "undefined") {
if(a.parents("#icinga-detail").length) {
log.debug("Parent is detail, loading into detail");
async.loadToTarget("icinga-detail",href);
} else {
log.debug("Parent is not detail, loading into main");
async.loadToTarget("icinga-main",href);
return false;
};
var onLinkTagClick = function(ev) {
var a = $(ev.currentTarget),
target = a.attr("target"),
href = a.attr("href");
// check for protocol://
if(/^[A-Z]{2,10}\:\/\//i.test(href)) {
window.open(href);
ev.stopImmediatePropagation();
return false;
}
// check for link in table header
if(a.parents('th').length > 0) {
ev.stopImmediatePropagation();
return false;
}
if(typeof target === "undefined") {
if(a.parents("#icinga-detail").length) {
async.loadToTarget("icinga-detail",href);
} else {
async.loadToTarget("icinga-main",href);
}
} else {
switch(target) {
}
} else {
switch(target) {
case "body":
async.loadToTarget("body", href);
break;
case "main":
async.loadToTarget("icinga-main",href);
collapseDetailView();
break;
case "detail":
log.debug("Target: detail");
async.loadToTarget("icinga-detail",href);
break;
case "popup":
log.debug("No target");
async.loadToTarget(null,href);
break;
default:
@ -67,7 +74,7 @@ define(['jquery','logging','icinga/util/async'],function($,log,async) {
var expandDetailView = function() {
$("#icinga-detail").parents(".collapsed").removeClass('collapsed');
};
};
var collapseDetailView = function(elementInDetailView) {
$("#icinga-detail").parents(".layout-main-detail").addClass('collapsed');
@ -88,7 +95,5 @@ define(['jquery','logging','icinga/util/async'],function($,log,async) {
}
};
};
return new MainDetailBehaviour();
});

View File

@ -5,7 +5,7 @@
var containerMgrInstance = null;
var async;
var ContainerMgr = function($,log,Widgets,SubTable,holder) {
var ContainerMgr = function($,log,Widgets,SubTable) {
var enhanceDetachLinks = function() {
@ -24,7 +24,7 @@
this.updateContainer = function(id,content,req) {
var target = id;
if (typeof id === "string") {
target = $('div[container-id='+id+']');
target = this.getContainer(id);
}
var ctrl = $('.container-controls',target);
target.html(content);
@ -34,14 +34,12 @@
}
target.focus();
this.initializeContainers(target);
};
this.updateControlTargets = function(ctrl, req) {
$('a',ctrl).each(function() {
$(this).attr("href",req.url);
});
};
this.initControlBehaviour = function(root) {
@ -86,7 +84,6 @@
this.initExpandables(root);
this.drawImplicitWidgets(root);
this.loadAsyncContainers(root);
};
this.createPopupContainer = function(content,req) {
@ -96,10 +93,12 @@
.append($("<div>").addClass('modal-body').html(content)).appendTo(document.body);
closeButton.on("click",function() {container.remove();});
};
this.getContainer = function(id) {
if(id == 'body') {
return $(document.body);
}
return $('div[container-id='+id+']');
};
@ -109,6 +108,5 @@
containerMgrInstance = new ContainerMgr($,log,widgets,subTable);
}
return containerMgrInstance;
});
})();

View File

@ -13,22 +13,25 @@ define([
* Icinga prototype
*/
var Icinga = function() {
var internalModules = ['icinga/modules/actionTable','icinga/modules/mainDetail'];
var internalModules = ['icinga/components/actionTable','icinga/components/mainDetail'];
this.modules = {};
var failedModules = [];
var initialize = function () {
registerLazyModuleLoading();
enableInternalModules();
containerMgr.registerAsyncMgr(async);
containerMgr.initializeContainers(document);
log.debug("Initialization finished");
enableModules();
};
var registerLazyModuleLoading = function() {
async.registerHeaderListener("X-Icinga-Enable-Module", loadModuleScript, this);
};
var enableInternalModules = function() {
$.each(internalModules,function(idx,module) {
@ -37,7 +40,8 @@ define([
};
var loadModuleScript = function(name) {
moduleMgr.enableModule("modules/"+name, function(error) {
console.log("Loading ", name);
moduleMgr.enableModule("modules/"+name+"/"+name, function(error) {
failedModules.push({
name: name,
errorMessage: error
@ -81,5 +85,3 @@ define([
};
return new Icinga();
});

View File

@ -5,6 +5,7 @@
define(['icinga/container','logging','jquery'],function(containerMgr,log,$) {
var headerListeners = {};
var pending = {
@ -38,7 +39,7 @@
var getDOMForDestination = function(destination) {
var target = destination;
if(typeof destination === "string") {
if (typeof destination === "string") {
target = containerMgr.getContainer(destination)[0];
} else if(typeof destination.context !== "undefined") {
target = destination[0];
@ -46,11 +47,30 @@
return target;
};
var handleResponse = function(html) {
var applyHeaderListeners = function(headers) {
for (var header in headerListeners) {
if (headers.getResponseHeader(header) === null) {
// see if the browser/server converts headers to lowercase
if (headers.getResponseHeader(header.toLowerCase()) === null) {
continue;
}
header = header.toLowerCase();
}
var value = headers.getResponseHeader(header);
var listeners = headerListeners[header];
for (var i=0;i<listeners.length;i++) {
listeners[i].fn.apply(listeners[i].scope, [value, header, headers]);
}
}
};
var handleResponse = function(html, status, response) {
applyHeaderListeners(response);
if(this.destination) {
containerMgr.updateContainer(this.destination,html,this);
} else {
containerMgr.createPopupContainer(html,this);
// tbd
// containerMgr.createPopupContainer(html,this);
}
};
@ -76,6 +96,8 @@
var CallInterface = function() {
this.__internalXHRImplementation = $.ajax;
this.clearPendingRequestsFor = function(destination) {
if(!$.isArray(pending)) {
pending = [];
@ -95,7 +117,7 @@
};
this.createRequest = function(url,data) {
var req = $.ajax({
var req = this.__internalXHRImplementation({
type : data ? 'POST' : 'GET',
url : url,
data : data,
@ -109,6 +131,7 @@
this.loadToTarget = function(destination,url,data) {
if(destination) {
log.debug("Laoding to container", destination, url);
this.clearPendingRequestsFor(destination);
}
var req = this.createRequest(url,data);
@ -132,8 +155,12 @@
this.loadCSS = function(name) {
};
this.registerHeaderListener = function(header, fn, scope) {
headerListeners[header] = headerListeners[header] || [];
headerListeners[header].push({fn: fn, scope:scope});
};
};
return new CallInterface();
});
})();

View File

@ -24,4 +24,3 @@ define(['jquery','Holder'], function ($) {
window.Icinga = Icinga;
});
});

View File

@ -0,0 +1,35 @@
// {{LICENSE_HEADER}}
// {{LICENSE_HEADER}}
var should = require("should");
var rjsmock = require("requiremock.js");
var asyncMock = require("asyncmock.js");
GLOBAL.document = $('body');
describe('The async module', function() {
it("Allows to react on specific headers", function(done) {
rjsmock.purgeDependencies();
rjsmock.registerDependencies({
'icinga/container' : {
updateContainer : function() {},
createPopupContainer: function() {}
}
});
requireNew("icinga/util/async.js");
var async = rjsmock.getDefine();
var headerValue = null;
asyncMock.setNextAsyncResult(async, "result", false, {
'X-Dont-Care' : 'Ignore-me',
'X-Test-Header' : 'Testme123'
});
async.registerHeaderListener("X-Test-Header", function(value, header) {
should.equal("Testme123", value);
done();
},this);
var test = async.createRequest();
});
});

View File

@ -9,12 +9,12 @@
// {{LICENSE_HEADER}}
var should = require("should");
var rjsmock = require("requiremock.js");
var asyncMock = require("asyncmock.js");
var BASE = "../../../../public/js/";
require(BASE+"icinga/module.js");
requireNew("icinga/module.js");
var module = rjsmock.getDefine();
GLOBAL.document = $('body');
/**
* Test module that only uses eventhandlers and
* no custom logic
@ -195,7 +195,10 @@ describe('The icinga module bootstrap', function() {
var testClick = false;
rjsmock.registerDependencies({
"icinga/module": module,
"modules/test" : {
"icinga/util/async" : {
registerHeaderListener: function() {}
},
"modules/test/test" : {
eventHandler: {
"a.test" : {
click : function() {
@ -214,7 +217,7 @@ describe('The icinga module bootstrap', function() {
]
});
tearDownTestDOM();
require(BASE+"icinga/icinga.js");
requireNew("icinga/icinga.js");
var icinga = rjsmock.getDefine();
$('body').append($("<a class='test'></a>"));
$('a.test').click();
@ -223,4 +226,55 @@ describe('The icinga module bootstrap', function() {
should.equal(icinga.getFailedModules()[0].name, "test2");
tearDownTestDOM();
});
it("Should load modules lazily when discovering a X-Icinga-Enable-Module header", function() {
rjsmock.purgeDependencies();
requireNew("icinga/util/async.js");
var async = rjsmock.getDefine();
rjsmock.registerDependencies({
"icinga/module": module,
"icinga/util/async": async,
"modules/test/test" : {
eventHandler: {
"a.test" : {
click : function() {
testClick = true;
}
}
}
},
"icinga/container" : {
registerAsyncMgr: function() {},
initializeContainers: function() {}
},
"modules/list" : [
]
});
tearDownTestDOM();
requireNew("icinga/icinga.js");
var icinga = rjsmock.getDefine();
var testClick = false;
// The module shouldn't be loaded
$('body').append($("<a class='test'></a>"));
$('a.test').click();
should.equal(testClick, false, "Unregistered module was loaded");
asyncMock.setNextAsyncResult(async,"result", false, {
"X-Icinga-Enable-Module" : "test"
});
async.createRequest();
// The module shouldn't be loaded
$('body').append($("<a class='test'></a>"));
$('a.test').click();
should.equal(testClick, true, "Module wasn't automatically loaded on header!");
tearDownTestDOM();
});
});

View File

@ -0,0 +1,33 @@
/**
* Helper for mocking $.async's XHR requests
*
*/
var getCallback = function(empty, response, succeed, headers) {
if (empty)
return function() {};
return function(callback) {
callback(response, succeed, {
getAllResponseHeaders: function() {
return headers;
},
getResponseHeader: function(header) {
return headers[header] || null;
}
});
};
};
module.exports = {
setNextAsyncResult: function(async, response, fails, headers) {
headers = headers || {};
var succeed = fails ? "fail" : "success";
async.__internalXHRImplementation = function(config) {
return {
done: getCallback(fails, response, succeed, headers),
fail: getCallback(!fails, response, succeed, headers)
};
};
}
};

View File

@ -13,6 +13,7 @@
* to console.
*
**/
var path = require('path');
var registeredDependencies = {};
/**
@ -21,11 +22,13 @@ var registeredDependencies = {};
* in dependencies and calls fn with them as the parameter
*
**/
var debug = false;
var requireJsMock = function(dependencies, fn) {
var fnArgs = [];
for (var i=0;i<dependencies.length;i++) {
if (typeof registeredDependencies[dependencies[i]] === "undefined") {
console.warn("Unknown dependency "+dependencies[i]+" in define()");
if (debug === true)
console.warn("Unknown dependency "+dependencies[i]+" in define()");
}
fnArgs.push(registeredDependencies[dependencies[i]]);
}
@ -55,7 +58,7 @@ var defineMock = function() {
var argList = arguments[currentArg];
fn = arguments[currentArg+1];
for (var i=0;i<argList.length;i++) {
if (typeof registerDependencies[argList[i]] === "undefined") {
if (typeof registerDependencies[argList[i]] === "undefined" && debug) {
console.warn("Unknown dependency "+argList[i]+" in define()");
}
@ -91,7 +94,6 @@ initRequireMethods();
function purgeDependencies() {
registeredDependencies = {
'jquery' : GLOBAL.$,
'__define__' : registeredDependencies.__define__,
'logging' : console
};
}
@ -107,6 +109,12 @@ function registerDependencies(obj) {
registeredDependencies[name] = obj[name];
}
}
var base = path.normalize(__dirname+"../../../../public/js");
GLOBAL.requireNew = function(key) {
key = path.normalize(base+"/"+key);
delete require.cache[key];
return require(key);
};
/**
* The API for this module
@ -122,3 +130,4 @@ module.exports = {
}
}
};

View File

@ -1,21 +1,10 @@
<?php
if(!class_exists('Zend_View_Helper_Abstract')) {
abstract class Zend_View_Helper_Abstract {
public $basename;
public function __construct($basename = '') {
$this->view = $this;
$this->basename = $basename;
}
require_once('Zend/View/Helper/Abstract.php');
require_once('Zend/View.php');
public function baseUrl($url) {
return $this->basename.$url;
}
};
}
require('../../application/views/helpers/Qlink.php');
require_once('../../application/views/helpers/Qlink.php');
/**
@ -30,30 +19,37 @@ class Zend_View_Helper_QlinkTest extends \PHPUnit_Framework_TestCase
public function testURLPathParameter()
{
$view = new Zend_View();
$helper = new Zend_View_Helper_Qlink();
$pathTpl = "path/%s/to/%s";
$helper->setView($view);
$pathTpl = "/path/%s/to/%s";
$this->assertEquals(
"path/param1/to/param2",
"/path/param1/to/param2",
$helper->getFormattedURL($pathTpl,array('param1','param2'))
);
}
public function testUrlGETParameter()
{
$view = new Zend_View();
$helper = new Zend_View_Helper_Qlink();
$helper->setView($view);
$pathTpl = 'path';
$this->assertEquals(
'path?param1=value1&amp;param2=value2',
'/path?param1=value1&amp;param2=value2',
$helper->getFormattedURL($pathTpl,array('param1'=>'value1','param2'=>'value2'))
);
}
public function testMixedParameters()
{
$view = new Zend_View();
$helper = new Zend_View_Helper_Qlink();
$helper->setView($view);
$pathTpl = 'path/%s/to/%s';
$this->assertEquals(
'path/path1/to/path2?param1=value1&amp;param2=value2',
'/path/path1/to/path2?param1=value1&amp;param2=value2',
$helper->getFormattedURL($pathTpl,array(
'path1','path2',
'param1'=>'value1',

View File

@ -0,0 +1,111 @@
<?php
namespace Tests\Icinga\Application\Module\Manager;
require_once("../../library/Icinga/Application/Modules/Manager.php");
require_once("../../library/Icinga/Exception/ProgrammingError.php");
require_once("../../library/Icinga/Exception/ConfigurationError.php");
require_once("../../library/Icinga/Exception/SystemPermissionException.php");
use Icinga\Application\Modules\Manager as ModuleManager;
class ModuleMock
{
public $name = "";
public $dir = "";
public function __construct($app, $name, $dir)
{
$this->name = $name;
$this->dir = $dir;
}
public function register()
{
}
}
class ManagerTest extends \PHPUnit_Framework_TestCase
{
const MODULE_TARGET = "/tmp";
protected function setUp()
{
$moduleDir = self::MODULE_TARGET;
if (!is_writable($moduleDir)) {
$this->markTestSkipped("Temporary folder not writable for this user");
return;
}
if (is_dir($moduleDir."/enabledModules")) {
exec("rm -r $moduleDir/enabledModules");
}
mkdir($moduleDir."/enabledModules");
}
public function testDetectEnabledModules()
{
$manager = new ModuleManager(null, "/tmp/enabledModules", array("none"));
$this->assertEmpty($manager->listEnabledModules());
symlink(getcwd()."/res/testModules/module1", "/tmp/enabledModules/module1");
$manager = new ModuleManager(null, "/tmp/enabledModules", array("none"));
$this->assertEquals(array("module1"), $manager->listEnabledModules());
symlink(getcwd()."/res/testModules/module2", "/tmp/enabledModules/module2");
symlink(getcwd()."/res/???", "/tmp/enabledModules/module3");
$manager = new ModuleManager(null, "/tmp/enabledModules", array("none"));
$this->assertEquals(array("module1", "module2"), $manager->listEnabledModules());
}
public function testLoadModule()
{
$manager = new ModuleManager(null, "/tmp/enabledModules", array("./res/testModules"));
$this->assertEmpty($manager->getLoadedModules());
$manager->loadModule("module1", "Tests\Icinga\Application\Module\Manager\ModuleMock");
$elems = $manager->getLoadedModules();
$this->assertNotEmpty($elems);
$this->assertTrue(isset($elems["module1"]));
// assert the changes not to be permanent:
$manager = new ModuleManager(null, "/tmp/enabledModules", array("./res/testModules"));
$this->assertEmpty($manager->getLoadedModules());
}
public function testEnableModule()
{
$manager = new ModuleManager(null, "/tmp/enabledModules", array(getcwd()."/res/testModules"));
$this->assertEmpty($manager->listEnabledModules());
$manager->enableModule("module1");
$elems = $manager->listEnabledModules();
$this->assertNotEmpty($elems);
$this->assertEquals($elems[0], "module1");
$this->assertTrue(is_link("/tmp/enabledModules/module1"));
// assert the changes to be permanent:
$manager = new ModuleManager(null, "/tmp/enabledModules", array("./res/testModules"));
$this->assertNotEmpty($manager->listEnabledModules());
}
public function testDisableModule()
{
clearstatcache(true);
symlink(getcwd()."/res/testModules/module1", "/tmp/enabledModules/module1");
$manager = new ModuleManager(null, "/tmp/enabledModules", array(getcwd()."/res/testModules"));
$elems = $manager->listEnabledModules();
$this->assertNotEmpty($elems);
$this->assertEquals($elems[0], "module1");
$manager->disableModule("module1");
$this->assertFalse(file_exists("/tmp/enabledModules/module1"));
$this->assertEmpty($manager->listEnabledModules());
// assert the changes to be permanent:
$manager = new ModuleManager(null, "/tmp/enabledModules", array("./res/testModules"));
$this->assertEmpty($manager->listEnabledModules());
}
protected function tearDown()
{
$moduleDir = self::MODULE_TARGET;
exec("rm -r $moduleDir/enabledModules");
}
}

View File

@ -7,6 +7,7 @@ namespace Tests\Icinga\Authentication;
require_once("../../library/Icinga/Authentication/Session.php");
require_once("../../library/Icinga/Authentication/PhpSession.php");
require_once("../../library/Icinga/Application/Logger.php");
require_once("../../library/Icinga/Exception/ConfigurationError.php");
require_once("Zend/Log.php");
use Icinga\Authentication\PhpSession as PhpSession;

View File

@ -0,0 +1,224 @@
<?php
namespace Test\Icinga\Form;
require_once("Zend/Form.php");
require_once("Zend/View.php");
require_once("../../library/Icinga/Form/Builder.php");
use Icinga\Form\Builder as Builder;
class BuilderTestModel
{
public $username = '';
public $password = '';
private $test;
public function getTest()
{
return $this->test;
}
public function setTest($test)
{
$this->test = $test;
}
}
class BuilderTest extends \PHPUnit_Framework_TestCase
{
/**
*
**/
public function testFormCreation()
{
$builder = new Builder(null, array("CSRFProtection" => false));
$this->assertInstanceOf("Zend_Form", $builder->getForm());
}
/**
*
**/
public function testCSRFProtectionTokenCreation()
{
$view = new \Zend_View();
$builder = new Builder(); // when no token is given, a CRSF field should be added
$builder->setView($view);
$DOM = new \DOMDocument;
$DOM->loadHTML($builder);
$this->assertNotNull($DOM->getElementById(Builder::CSRF_ID));
$builder->disableCSRF();
$DOM->loadHTML($builder);
$this->assertNull($DOM->getElementById(Builder::CSRF_ID));
}
/**
* Test whether form methods are passed to the Zend_Form object
* When called in the Builder instance
*
**/
public function testMethodPassing()
{
$DOM = new \DOMDocument;
$view = new \Zend_View();
$builder = new Builder(null, array("CSRFProtection" => false));
$builder->setView($view);
$DOM->loadHTML($builder);
$this->assertEquals(0, $DOM->getElementsByTagName("input")->length);
$builder->addElement("text", "username");
$DOM->loadHTML($builder);
$inputEls = $DOM->getElementsByTagName("input");
$this->assertEquals(1, $inputEls->length);
$this->assertEquals("username", $inputEls->item(0)->attributes->getNamedItem("name")->value);
}
/**
*
*
**/
public function testCreateByArray()
{
$DOM = new \DOMDocument;
$view = new \Zend_View();
$builder = Builder::fromArray(
array(
'username' => array(
'text',
array(
'label' => 'Username',
'required' => true,
)
),
'password' => array(
'password',
array(
'label' => 'Password',
'required' => true,
)
),
'submit' => array(
'submit',
array(
'label' => 'Login'
)
)
),
array(
"CSRFProtection" => false
)
);
$builder->setView($view);
$DOM->loadHTML($builder);
$inputEls = $DOM->getElementsByTagName("input");
$this->assertEquals(3, $inputEls->length);
$username = $inputEls->item(0);
$this->assertEquals("username", $username->attributes->getNamedItem("name")->value);
$password= $inputEls->item(1);
$this->assertEquals("password", $password->attributes->getNamedItem("name")->value);
$this->assertEquals("password", $password->attributes->getNamedItem("type")->value);
$submitBtn= $inputEls->item(2);
$this->assertEquals("submit", $submitBtn->attributes->getNamedItem("name")->value);
$this->assertEquals("submit", $submitBtn->attributes->getNamedItem("type")->value);
}
/**
*
*
*/
public function testModelBindingWithArray()
{
$view = new \Zend_View();
$myModel = array(
"username" => "",
"password" => ""
);
$builder = new Builder(
null,
array(
"CSRFProtection" => false,
"model" => &$myModel
)
);
$builder->setView($view);
// $builder->bindToModel($myModel);
$builder->addElement("text", "username");
$builder->addElement("password", "password");
// test sync from form to model
$builder->populate(
array(
"username" => "User input<html>",
"password" => "Secret$123"
)
);
$this->assertEquals("User input<html>", $myModel["username"]);
$this->assertEquals("Secret$123", $myModel["password"]);
// test sync from model to form
$myModel["username"] = "Another user";
$myModel["password"] = "Another pass";
$builder->syncWithModel();
$this->assertEquals("Another user", $builder->getElement("username")->getValue());
$this->assertEquals("Another pass", $builder->getElement("password")->getValue());
}
/**
*
*
*/
public function testModelBindingWithObject()
{
$view = new \Zend_View();
$builder = new Builder(null, array("CSRFProtection" => false));
$builder->setView($view);
$myModel = new BuilderTestModel();
$builder->bindToModel($myModel);
$builder->addElement("text", "username");
$builder->addElement("password", "password");
$builder->addElement("text", "test");
// test sync from form to model
$builder->populate(
(object) array(
"username" => "User input<html>",
"password" => "Secret$123",
"test" => 'test334'
)
);
$this->assertEquals("User input<html>", $myModel->username);
$this->assertEquals("Secret$123", $myModel->password);
$this->assertEquals("test334", $myModel->getTest());
// test sync from model to form
$myModel->username = "Another user";
$myModel->password = "Another pass";
$builder->syncWithModel();
$this->assertEquals("Another user", $builder->getElement("username")->getValue());
$this->assertEquals("Another pass", $builder->getElement("password")->getValue());
}
/**
* @expectedException \BadMethodCallException
* @expectedExceptionMessage Method doesNotExist123 does not exist either in \Icinga\Form\Builder nor in Zend_Form
*/
public function testBadCall1()
{
$builder = new Builder(null, array("CSRFProtection" => false));
$builder->doesNotExist123();
}
}

View File

@ -1,80 +1,6 @@
<?php
namespace Tests\Icinga\Web\ActionController;
use Icinga\Web\ActionController as Action;
require_once('Zend/Controller/Action.php');
require_once('../../library/Icinga/Web/ActionController.php');
/**
* This is not a nice hack, but doesn't affect the behaviour of
* the tested methods, allowing us to avoid bootstrapping
* the request/response System for every test
*
* Class ActionTestWrap
* @package Tests\Icinga\Mvc\Controller
*/
class ActionTestWrap extends Action {
private $args;
public function __construct(\Zend_Controller_Request_Abstract $request = null,
\Zend_Controller_Response_Abstract $response = null, array $invokeArgs = array())
{}
public function setArguments($args) {
$this->args = $args;
}
protected function _getParam($paramName, $default = null) {
if(isset($this->args[$paramName]))
return $this->args[$paramName];
return $default;
}
}
class ActionTest extends \PHPUnit_Framework_TestCase
{
public function testSeedGeneration()
{
$action = new ActionTestWrap();
list($seed1,$token1) = $action->getSeedTokenPair(600,"test");
list($seed2,$token2) = $action->getSeedTokenPair(600,"test");
list($seed3,$token3) = $action->getSeedTokenPair(600,"test");
$this->assertTrue($seed1 != $seed2 && $seed2 != $seed3 && $seed1 != $seed3);
$this->assertTrue($token1 != $token2 && $token2 != $token3 && $token1 != $token3);
}
public function testSeedValidation()
{
$action = new ActionTestWrap();
list($seed,$token) = $action->getSeedTokenPair(600,"test");
$action->setArguments(array(
"seed" => $seed,
"token" => $token
));
$this->assertTrue($action->hasValidToken(600,"test"));
$this->assertFalse($action->hasValidToken(600,"test2"));
$action->setArguments(array(
"seed" => $seed."ds",
"token" => $token
));
$this->assertFalse($action->hasValidToken(600,"test"));
$action->setArguments(array(
"seed" => $seed,
"token" => $token."afs"
));
$this->assertFalse($action->hasValidToken(600,"test"));
}
public function testMaxAge()
{
$action = new ActionTestWrap();
list($seed,$token) = $action->getSeedTokenPair(1,"test");
$action->setArguments(array(
"seed" => $seed,
"token" => $token
));
$this->assertTrue($action->hasValidToken(1,"test"));
sleep(1);
$this->assertFalse($action->hasValidToken(1,"test"));
}
}