Ensure that form ids are unique

Add an unique prefix to each Form- or FormElement id, unless id protection is disabled explicitly, to prevent id collisions between different containers.

fixes #8460
This commit is contained in:
Matthias Jentsch 2015-02-25 18:00:28 +01:00
parent cc403806f7
commit 89451f3086
6 changed files with 90 additions and 4 deletions

View File

@ -0,0 +1,12 @@
<?php
/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
/**
* Class Zend_View_Helper_Util
*/
class Zend_View_Helper_ProtectId extends Zend_View_Helper_Abstract
{
public function protectId($id) {
return Zend_Controller_Front::getInstance()->getRequest()->protectId($id);
}
}

View File

@ -2,7 +2,7 @@
<?= $tabs; ?>
</div>
<div class="content" data-base-target="_next">
<h1 tabindex="-1" id="authentication-reorder">
<h1 tabindex="-1" id="authentication-configuration">
<?= $this->translate('Authentication Configuration'); ?>
</h1>
<h2 tabindex="-1" id="authentication-new-backend" class="sr-only">

View File

@ -9,8 +9,8 @@ use Icinga\Web\Url;
if ($this->pageCount <= 1) return;
?><p id="paginationlabel" class="audible"><?= t('Pagination') ?></p>
<ul class="pagination" aria-labelledby="paginationlabel" role="navigation"
?><p id="<?= $this->protectId('paginationlabel'); ?>" class="audible"><?= t('Pagination') ?></p>
<ul class="pagination" aria-labelledby="<?= $this->protectId('paginationlabel'); ?>" role="navigation"
<?php
$fromto = t('Show rows %u to %u out of %u');

View File

@ -112,6 +112,13 @@ class Form extends Zend_Form
*/
protected $validatePartial = false;
/**
* Whether element ids will be protected against collisions by appending a request-specific unique identifier
*
* @var bool
*/
protected $protectIds = true;
/**
* Authentication manager
*
@ -903,6 +910,28 @@ class Form extends Zend_Form
public function render(Zend_View_Interface $view = null)
{
$this->create();
if ($this->protectIds) {
if (null !== $this->getAttrib('id')) {
$this->setAttrib('id', $this->getRequest()->protectId($this->getAttrib('id')));
} else {
$this->setAttrib('id', $this->getRequest()->protectId($this->name));
}
/** @var Zend_Form_Element $element */
foreach ($this->getElements() as $element) {
if (null !== $element->getAttrib('id')) {
$element->setAttrib(
'id',
$this->getRequest()->protectId($this->getName() . '-' . $element->getAttrib('id'))
);
} else {
$element->setAttrib(
'id',
$this->getRequest()->protectId($this->getName() . '-' . $element->getName())
);
}
}
}
return parent::render($view);
}
@ -944,4 +973,29 @@ class Form extends Zend_Form
throw new SecurityException('No permission for %s', $permission);
}
}
/**
* Enable or disable whether ids should be altered to guard them against duplications
*
* @param $value boolean Whether or not protect ids against collisions through other requests
*/
public function setProtectIds($value)
{
$this->protectIds = $value;
}
/**
* Get the id that is written into the output html when rendering this form
*
* This will return the protected id, in case $protectIds is enabled.
*
* @return string The id
*/
public function getId()
{
if ($this->protectIds) {
return $this->getRequest()->protectId($this->getName());
}
return $this->getName();
}
}

View File

@ -18,6 +18,11 @@ class Request extends Zend_Controller_Request_Http
*/
private $user;
/**
* @var string
*/
private $uniqueId;
private $url;
public function getUrl()
@ -47,4 +52,19 @@ class Request extends Zend_Controller_Request_Http
{
return $this->user;
}
/**
* Makes an ID unique to this request, to prevent id collisions in different containers
*
* Call this whenever an ID might show up multiple times in different containers. This function is useful
* for ensuring unique ids on sites, even if we combine the HTML of different requests into one site,
* while still being able to reference elements uniquely in the same request.
*/
public function protectId($id)
{
if (! isset($this->uniqueId)) {
$this->uniqueId = Window::generateId();
}
return $id . '-' . $this->uniqueId;
}
}

View File

@ -702,7 +702,7 @@ class Monitoring_ListController extends Controller
private function setupSortControl(array $columns)
{
$this->view->sortControl = new SortBox(
$this->getRequest()->getActionName(),
'sortbox-' . $this->getRequest()->getActionName(),
$columns
);
$this->view->sortControl->applyRequest($this->getRequest());