419 lines
16 KiB
PHP
419 lines
16 KiB
PHP
<?php
|
|
/* Icinga Web 2 | (c) 2014 Icinga Development Team | GPLv2+ */
|
|
|
|
namespace Icinga\Forms\Security;
|
|
|
|
use Icinga\Application\Hook\ConfigFormEventsHook;
|
|
use Icinga\Application\Icinga;
|
|
use Icinga\Application\Modules\Manager;
|
|
use Icinga\Data\Filter\Filter;
|
|
use Icinga\Forms\ConfigForm;
|
|
use Icinga\Forms\RepositoryForm;
|
|
use Icinga\Util\StringHelper;
|
|
use Icinga\Web\Notification;
|
|
use Zend_Form_Element;
|
|
|
|
/**
|
|
* Form for managing roles
|
|
*/
|
|
class RoleForm extends RepositoryForm
|
|
{
|
|
/**
|
|
* The name to use instead of `*`
|
|
*/
|
|
const WILDCARD_NAME = 'allAndEverything';
|
|
|
|
/**
|
|
* Provided permissions by currently installed modules
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $providedPermissions;
|
|
|
|
/**
|
|
* Provided restrictions by currently installed modules
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $providedRestrictions;
|
|
|
|
public function init()
|
|
{
|
|
$this->setAttrib('class', self::DEFAULT_CLASSES . ' role-form');
|
|
|
|
$helper = new Zend_Form_Element('bogus');
|
|
$view = $this->getView();
|
|
|
|
$this->providedPermissions['application'] = [
|
|
$helper->filterName('no-user/password-change') => [
|
|
'name' => 'no-user/password-change',
|
|
'description' => $this->translate('Prohibit password changes in the account preferences')
|
|
],
|
|
$helper->filterName('application/share/navigation') => [
|
|
'name' => 'application/share/navigation',
|
|
'description' => $this->translate('Allow to share navigation items')
|
|
],
|
|
$helper->filterName('application/stacktraces') => [
|
|
'name' => 'application/stacktraces',
|
|
'description' => $this->translate(
|
|
'Allow to adjust in the preferences whether to show stacktraces'
|
|
)
|
|
],
|
|
$helper->filterName('application/log') => [
|
|
'name' => 'application/log',
|
|
'description' => $this->translate('Allow to view the application log')
|
|
],
|
|
$helper->filterName('admin') => [
|
|
'name' => 'admin',
|
|
'description' => $this->translate(
|
|
'Grant admin permissions, e.g. manage announcements'
|
|
)
|
|
],
|
|
$helper->filterName('config/*') => [
|
|
'name' => 'config/*',
|
|
'description' => $this->translate('Allow config access')
|
|
]
|
|
];
|
|
|
|
$this->providedRestrictions['application'] = [
|
|
$helper->filterName('application/share/users') => [
|
|
'name' => 'application/share/users',
|
|
'description' => $this->translate(
|
|
'Restrict which users this role can share items and information with'
|
|
)
|
|
],
|
|
$helper->filterName('application/share/groups') => [
|
|
'name' => 'application/share/groups',
|
|
'description' => $this->translate(
|
|
'Restrict which groups this role can share items and information with'
|
|
)
|
|
]
|
|
];
|
|
|
|
$mm = Icinga::app()->getModuleManager();
|
|
foreach ($mm->listInstalledModules() as $moduleName) {
|
|
$modulePermission = Manager::MODULE_PERMISSION_NS . $moduleName;
|
|
$this->providedPermissions[$moduleName][$helper->filterName($modulePermission)] = [
|
|
'isUsagePerm' => true,
|
|
'name' => $modulePermission,
|
|
'label' => $view->escape($this->translate('General Module Access')),
|
|
'description' => sprintf($this->translate('Allow access to module %s'), $moduleName)
|
|
];
|
|
|
|
$module = $mm->getModule($moduleName, false);
|
|
$permissions = $module->getProvidedPermissions();
|
|
|
|
$this->providedPermissions[$moduleName][$helper->filterName($moduleName . '/*')] = [
|
|
'isFullPerm' => true,
|
|
'name' => $moduleName . '/*',
|
|
'label' => $view->escape($this->translate('Full Module Access'))
|
|
];
|
|
|
|
foreach ($permissions as $permission) {
|
|
/** @var object $permission */
|
|
$this->providedPermissions[$moduleName][$helper->filterName($permission->name)] = [
|
|
'name' => $permission->name,
|
|
'label' => preg_replace(
|
|
'~^(\w+)(\/.*)~',
|
|
'<em>$1</em>$2',
|
|
$view->escape($permission->name)
|
|
),
|
|
'description' => $permission->description
|
|
];
|
|
}
|
|
|
|
foreach ($module->getProvidedRestrictions() as $restriction) {
|
|
$this->providedRestrictions[$moduleName][$helper->filterName($restriction->name)] = [
|
|
'name' => $restriction->name,
|
|
'label' => preg_replace(
|
|
'~^(\w+)(\/.*)~',
|
|
'<em>$1</em>$2',
|
|
$view->escape($restriction->name)
|
|
),
|
|
'description' => $restriction->description
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function createFilter()
|
|
{
|
|
return Filter::where('name', $this->getIdentifier());
|
|
}
|
|
|
|
public function createInsertElements(array $formData = array())
|
|
{
|
|
$this->addElement(
|
|
'text',
|
|
'name',
|
|
[
|
|
'required' => true,
|
|
'label' => $this->translate('Role Name'),
|
|
'description' => $this->translate('The name of the role')
|
|
]
|
|
);
|
|
$this->addElement(
|
|
'textarea',
|
|
'users',
|
|
[
|
|
'label' => $this->translate('Users'),
|
|
'description' => $this->translate('Comma-separated list of users that are assigned to the role')
|
|
]
|
|
);
|
|
$this->addElement(
|
|
'textarea',
|
|
'groups',
|
|
[
|
|
'label' => $this->translate('Groups'),
|
|
'description' => $this->translate('Comma-separated list of groups that are assigned to the role')
|
|
]
|
|
);
|
|
$this->addElement(
|
|
'checkbox',
|
|
self::WILDCARD_NAME,
|
|
[
|
|
'autosubmit' => true,
|
|
'label' => $this->translate('Administrative Access'),
|
|
'description' => $this->translate('Everything is allowed')
|
|
]
|
|
);
|
|
|
|
if (! isset($formData[self::WILDCARD_NAME]) || ! $formData[self::WILDCARD_NAME]) {
|
|
foreach ($this->providedPermissions as $moduleName => $permissionList) {
|
|
$this->sortPermissions($permissionList);
|
|
|
|
$elements = [$moduleName . '_header'];
|
|
$this->addElement(
|
|
'note',
|
|
$moduleName . '_header',
|
|
[
|
|
'decorators' => ['ViewHelper'],
|
|
'value' => '<h3>' . ($moduleName !== 'application'
|
|
? sprintf('%s <em>%s</em>', $moduleName, $this->translate('Module'))
|
|
: 'Icinga Web 2') . '</h3>'
|
|
]
|
|
);
|
|
|
|
$elements[] = 'permission_header';
|
|
$this->addElement('note', 'permission_header', [
|
|
'value' => '<h4>' . $this->translate('Permissions') . '</h4>',
|
|
'decorators' => ['ViewHelper']
|
|
]);
|
|
|
|
$hasFullPerm = false;
|
|
foreach ($permissionList as $name => $spec) {
|
|
$elements[] = $name;
|
|
$this->addElement(
|
|
'checkbox',
|
|
$name,
|
|
[
|
|
'ignore' => isset($spec['isUsagePerm']) ? false : $hasFullPerm,
|
|
'autosubmit' => isset($spec['isFullPerm']),
|
|
'disabled' => $hasFullPerm ?: null,
|
|
'value' => $hasFullPerm,
|
|
'label' => preg_replace(
|
|
// Adds a zero-width char after each slash to help browsers break onto newlines
|
|
'~(?<!<)/~',
|
|
'/​',
|
|
isset($spec['label']) ? $spec['label'] : $spec['name']
|
|
),
|
|
'description' => isset($spec['description']) ? $spec['description'] : $spec['name']
|
|
]
|
|
)
|
|
->getElement($name)
|
|
->getDecorator('Label')
|
|
->setOption('escape', false);
|
|
if (isset($spec['isFullPerm'])) {
|
|
$hasFullPerm = isset($formData[$name]) && $formData[$name];
|
|
}
|
|
}
|
|
|
|
if (isset($this->providedRestrictions[$moduleName])) {
|
|
$elements[] = 'restriction_header';
|
|
$this->addElement('note', 'restriction_header', [
|
|
'value' => '<h4>' . $this->translate('Restrictions') . '</h4>',
|
|
'decorators' => ['ViewHelper']
|
|
]);
|
|
|
|
foreach ($this->providedRestrictions[$moduleName] as $name => $spec) {
|
|
$elements[] = $name;
|
|
$this->addElement(
|
|
'text',
|
|
$name,
|
|
[
|
|
'label' => preg_replace(
|
|
// Adds a zero-width char after each slash to help browsers break onto newlines
|
|
'~(?<!<)/~',
|
|
'/​',
|
|
isset($spec['label']) ? $spec['label'] : $spec['name']
|
|
),
|
|
'description' => $spec['description']
|
|
]
|
|
)
|
|
->getElement($name)
|
|
->getDecorator('Label')
|
|
->setOption('escape', false);
|
|
}
|
|
}
|
|
|
|
$this->addDisplayGroup($elements, $moduleName . '_elements', [
|
|
'decorators' => [
|
|
'FormElements',
|
|
['Fieldset', [
|
|
'class' => 'collapsible',
|
|
'data-toggle-element' => 'h3',
|
|
'data-visible-height' => 0
|
|
]]
|
|
]
|
|
]);
|
|
}
|
|
} else {
|
|
// Previously it was possible to define restrictions for super users, so make sure
|
|
// to not remove any restrictions which were set before the enforced separation
|
|
foreach ($this->providedRestrictions as $restrictionList) {
|
|
foreach ($restrictionList as $name => $_) {
|
|
$this->addElement('hidden', $name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function createDeleteElements(array $formData)
|
|
{
|
|
}
|
|
|
|
public function fetchEntry()
|
|
{
|
|
$role = parent::fetchEntry();
|
|
if ($role === false) {
|
|
return false;
|
|
}
|
|
|
|
$values = [
|
|
'name' => $role->name,
|
|
'users' => $role->users,
|
|
'groups' => $role->groups,
|
|
self::WILDCARD_NAME => $role->permissions === '*'
|
|
];
|
|
|
|
if (! empty($role->permissions) && $role->permissions !== '*') {
|
|
$permissions = StringHelper::trimSplit($role->permissions);
|
|
foreach ($this->providedPermissions as $moduleName => $permissionList) {
|
|
foreach ($permissionList as $name => $spec) {
|
|
if (in_array($spec['name'], $permissions, true)) {
|
|
$values[$name] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($this->providedRestrictions as $moduleName => $restrictionList) {
|
|
foreach ($restrictionList as $name => $spec) {
|
|
if (isset($role->{$spec['name']})) {
|
|
$values[$name] = $role->{$spec['name']};
|
|
}
|
|
}
|
|
}
|
|
|
|
return (object) $values;
|
|
}
|
|
|
|
public function getValues($suppressArrayNotation = false)
|
|
{
|
|
$values = parent::getValues($suppressArrayNotation);
|
|
|
|
foreach ($this->providedRestrictions as $moduleName => $restrictionList) {
|
|
foreach ($restrictionList as $name => $spec) {
|
|
if (isset($values[$name])) {
|
|
$values[$spec['name']] = $values[$name];
|
|
unset($values[$name]);
|
|
}
|
|
}
|
|
}
|
|
|
|
$permissions = [];
|
|
if (isset($values[self::WILDCARD_NAME]) && $values[self::WILDCARD_NAME]) {
|
|
$permissions[] = '*';
|
|
} else {
|
|
foreach ($this->providedPermissions as $moduleName => $permissionList) {
|
|
foreach ($permissionList as $name => $spec) {
|
|
if (isset($values[$name]) && $values[$name]) {
|
|
$permissions[] = $spec['name'];
|
|
}
|
|
|
|
unset($values[$name]);
|
|
}
|
|
}
|
|
}
|
|
|
|
unset($values[self::WILDCARD_NAME]);
|
|
$values['permissions'] = join(',', $permissions);
|
|
return ConfigForm::transformEmptyValuesToNull($values);
|
|
}
|
|
|
|
protected function getInsertMessage($success)
|
|
{
|
|
return $success ? $this->translate('Role created') : $this->translate('Role creation failed');
|
|
}
|
|
|
|
protected function getUpdateMessage($success)
|
|
{
|
|
return $success ? $this->translate('Role updated') : $this->translate('Role update failed');
|
|
}
|
|
|
|
protected function getDeleteMessage($success)
|
|
{
|
|
return $success ? $this->translate('Role removed') : $this->translate('Role removal failed');
|
|
}
|
|
|
|
protected function sortPermissions(&$permissions)
|
|
{
|
|
return uasort($permissions, function ($a, $b) {
|
|
if (isset($a['isUsagePerm'])) {
|
|
return isset($b['isFullPerm']) ? 1 : -1;
|
|
} elseif (isset($b['isUsagePerm'])) {
|
|
return isset($a['isFullPerm']) ? -1 : 1;
|
|
}
|
|
|
|
$aParts = explode('/', $a['name']);
|
|
$bParts = explode('/', $b['name']);
|
|
|
|
do {
|
|
$a = array_shift($aParts);
|
|
$b = array_shift($bParts);
|
|
} while ($a === $b);
|
|
|
|
return strnatcmp($a, $b);
|
|
});
|
|
}
|
|
|
|
public function isValid($formData)
|
|
{
|
|
$valid = parent::isValid($formData);
|
|
|
|
if ($valid && ConfigFormEventsHook::runIsValid($this) === false) {
|
|
foreach (ConfigFormEventsHook::getLastErrors() as $msg) {
|
|
$this->error($msg);
|
|
}
|
|
|
|
$valid = false;
|
|
}
|
|
|
|
return $valid;
|
|
}
|
|
|
|
public function onSuccess()
|
|
{
|
|
if (parent::onSuccess() === false) {
|
|
return false;
|
|
}
|
|
|
|
if (ConfigFormEventsHook::runOnSuccess($this) === false) {
|
|
Notification::error($this->translate(
|
|
'Configuration successfully stored. Though, one or more module hooks failed to run.'
|
|
. ' See logs for details'
|
|
));
|
|
}
|
|
}
|
|
}
|