mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-04-08 17:15:08 +02:00
Merge b786e0c314d7c265f946b9aac20588fba502db8b into 2864e60d7879a648e915d870c6a7bb3153ea5c19
This commit is contained in:
commit
4c41265d4f
application
controllers
forms
doc
library/Icinga
Authentication
Common
Model
Web/Widget
modules/setup
schema
@ -3,8 +3,13 @@
|
||||
|
||||
namespace Icinga\Controllers;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Icinga\Application\Version;
|
||||
use Icinga\Common\Database;
|
||||
use Icinga\Forms\Security\RoleForm;
|
||||
use Icinga\Model\Role;
|
||||
use Icinga\Util\StringHelper;
|
||||
use InvalidArgumentException;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Icinga;
|
||||
@ -23,12 +28,18 @@ use Icinga\Web\Controller;
|
||||
use Icinga\Web\Notification;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\Widget;
|
||||
use ipl\Sql\Connection;
|
||||
use ipl\Sql\Insert;
|
||||
use ipl\Sql\Select;
|
||||
use ipl\Sql\Update;
|
||||
|
||||
/**
|
||||
* Application and module configuration
|
||||
*/
|
||||
class ConfigController extends Controller
|
||||
{
|
||||
use Database;
|
||||
|
||||
/**
|
||||
* Create and return the tabs to display when showing application configuration
|
||||
*/
|
||||
@ -99,13 +110,21 @@ class ConfigController extends Controller
|
||||
$form->setOnSuccess(function (GeneralConfigForm $form) {
|
||||
$config = Config::app();
|
||||
$useStrictCsp = (bool) $config->get('security', 'use_strict_csp', false);
|
||||
$storeRolesInDb = (bool) $config->get('global', 'store_roles_in_db', false);
|
||||
if ($form->onSuccess() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$appConfigForm = $form->getSubForm('form_config_general_application');
|
||||
if ($appConfigForm && (bool) $appConfigForm->getValue('security_use_strict_csp') !== $useStrictCsp) {
|
||||
$this->getResponse()->setReloadWindow(true);
|
||||
|
||||
if ($appConfigForm) {
|
||||
if ((bool) $appConfigForm->getValue('security_use_strict_csp') !== $useStrictCsp) {
|
||||
$this->getResponse()->setReloadWindow(true);
|
||||
}
|
||||
|
||||
if (! $storeRolesInDb && $appConfigForm->getValue('global_store_roles_in_db')) {
|
||||
$this->migrateRolesToFreshDb();
|
||||
}
|
||||
}
|
||||
})->handleRequest();
|
||||
|
||||
@ -114,6 +133,106 @@ class ConfigController extends Controller
|
||||
$this->createApplicationTabs()->activate('general');
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate roles.ini to database if the latter contains no roles
|
||||
*/
|
||||
private function migrateRolesToFreshDb(): void
|
||||
{
|
||||
$roles = Config::app('roles');
|
||||
$now = (new DateTime())->getTimestamp() * 1000;
|
||||
|
||||
$this->getDb()->transaction(function (Connection $db) use ($roles, $now) {
|
||||
if (Role::on($db)->columns('id')->first()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($roles as $name => $role) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role')
|
||||
->columns(['name', 'unrestricted', 'ctime'])
|
||||
->values([$name, $role->unrestricted ? 'y' : 'n', $now])
|
||||
);
|
||||
|
||||
$id = $db->lastInsertId();
|
||||
$permissions = StringHelper::trimSplit($role->permissions);
|
||||
$refusals = StringHelper::trimSplit($role->refusals);
|
||||
$permissionsAndRefusals = [];
|
||||
|
||||
foreach (StringHelper::trimSplit($role->users) as $user) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_user')
|
||||
->columns(['role_id', 'user_name'])
|
||||
->values([$id, $user])
|
||||
);
|
||||
}
|
||||
|
||||
foreach (StringHelper::trimSplit($role->groups) as $group) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_group')
|
||||
->columns(['role_id', 'group_name'])
|
||||
->values([$id, $group])
|
||||
);
|
||||
}
|
||||
|
||||
foreach ([$permissions, $refusals] as $permissionsOrRefusals) {
|
||||
foreach ($permissionsOrRefusals as $permissionOrRefusal) {
|
||||
$permissionsAndRefusals[$permissionOrRefusal] = ['allowed' => 'n', 'denied' => 'n'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
$permissionsAndRefusals[$permission]['allowed'] = 'y';
|
||||
}
|
||||
|
||||
foreach ($refusals as $refusal) {
|
||||
$permissionsAndRefusals[$refusal]['denied'] = 'y';
|
||||
}
|
||||
|
||||
foreach ($permissionsAndRefusals as $permission => $authz) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_permission')
|
||||
->columns(['role_id', 'permission', 'allowed', 'denied'])
|
||||
->values([$id, $permission, $authz['allowed'], $authz['denied']])
|
||||
);
|
||||
}
|
||||
|
||||
foreach (RoleForm::collectProvidedPrivileges()[1] as $restrictionList) {
|
||||
foreach ($restrictionList as $restriction => $_) {
|
||||
if (isset($role->$restriction)) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_restriction')
|
||||
->columns(['role_id', 'restriction', 'filter'])
|
||||
->values([$id, $restriction, $role->$restriction])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($roles as $name => $role) {
|
||||
if (isset($role->parent)) {
|
||||
$db->prepexec(
|
||||
(new Update())
|
||||
->table('icingaweb_role')
|
||||
->set([
|
||||
'parent_id' => (new Select())
|
||||
->from('icingaweb_role')
|
||||
->where(['name = ?' => $role->parent])
|
||||
->columns(['id'])
|
||||
,
|
||||
])
|
||||
->where(['name = ?' => $name])
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the list of all modules
|
||||
*/
|
||||
|
@ -5,21 +5,28 @@ namespace Icinga\Controllers;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\Psr7\ServerRequest;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Authentication\AdmissionLoader;
|
||||
use Icinga\Authentication\Auth;
|
||||
use Icinga\Authentication\RolesConfig;
|
||||
use Icinga\Authentication\User\DomainAwareInterface;
|
||||
use Icinga\Common\Database;
|
||||
use Icinga\Data\Selectable;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Forms\Security\RoleDbForm;
|
||||
use Icinga\Forms\Security\RoleForm;
|
||||
use Icinga\Model\Role;
|
||||
use Icinga\Repository\Repository;
|
||||
use Icinga\Security\SecurityException;
|
||||
use Icinga\User;
|
||||
use Icinga\Web\Controller\AuthBackendController;
|
||||
use Icinga\Web\View\PrivilegeAudit;
|
||||
use Icinga\Web\Widget\RolesTable;
|
||||
use Icinga\Web\Widget\SingleValueSearchControl;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlString;
|
||||
use ipl\Web\Compat\SearchControls;
|
||||
use ipl\Web\Filter\QueryString;
|
||||
use ipl\Web\Url;
|
||||
use ipl\Web\Widget\Link;
|
||||
|
||||
@ -30,6 +37,9 @@ use ipl\Web\Widget\Link;
|
||||
*/
|
||||
class RoleController extends AuthBackendController
|
||||
{
|
||||
use Database;
|
||||
use SearchControls;
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->assertPermission('config/access-control/roles');
|
||||
@ -59,20 +69,70 @@ class RoleController extends AuthBackendController
|
||||
public function listAction()
|
||||
{
|
||||
$this->createListTabs()->activate('role/list');
|
||||
$this->view->roles = (new RolesConfig())
|
||||
->select();
|
||||
|
||||
$sortAndFilterColumns = [
|
||||
'name' => $this->translate('Name'),
|
||||
'users' => $this->translate('Users'),
|
||||
'groups' => $this->translate('Groups'),
|
||||
'permissions' => $this->translate('Permissions')
|
||||
];
|
||||
if (Config::app()->get('global', 'store_roles_in_db')) {
|
||||
$db = $this->getDb();
|
||||
$query = Role::on($db)->with('parent');
|
||||
|
||||
$this->setupFilterControl($this->view->roles, $sortAndFilterColumns, ['name']);
|
||||
$this->setupLimitControl();
|
||||
$this->setupPaginationControl($this->view->roles);
|
||||
$this->setupSortControl($sortAndFilterColumns, $this->view->roles, ['name']);
|
||||
$limitControl = $this->createLimitControl();
|
||||
$sortControl = $this->createSortControl($query, ['name' => $this->translate('Name')]);
|
||||
$paginationControl = $this->createPaginationControl($query);
|
||||
$searchBar = $this->createSearchBar($query, [$limitControl->getLimitParam(), $sortControl->getSortParam()]);
|
||||
|
||||
if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) {
|
||||
if ($searchBar->hasBeenSubmitted()) {
|
||||
$filter = QueryString::parse((string) $this->params);
|
||||
} else {
|
||||
$this->addControl($searchBar);
|
||||
$this->sendMultipartUpdate();
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$filter = $searchBar->getFilter();
|
||||
}
|
||||
|
||||
$query->filter($filter);
|
||||
|
||||
$this->addControl($paginationControl);
|
||||
$this->addControl($limitControl);
|
||||
$this->addControl($sortControl);
|
||||
$this->addControl($searchBar);
|
||||
|
||||
$this->addControl(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => Url::fromPath('role/add'),
|
||||
'data-base-target' => '_next',
|
||||
'class' => 'button-link icon-plus'
|
||||
],
|
||||
$this->translate('Create a New Role')
|
||||
));
|
||||
|
||||
if ($query->count()) {
|
||||
$this->addContent((new RolesTable())->setRoles($query));
|
||||
} else {
|
||||
$this->addContent(Html::tag('p', $this->translate('No roles found.')));
|
||||
}
|
||||
|
||||
if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) {
|
||||
$this->sendMultipartUpdate();
|
||||
}
|
||||
} else {
|
||||
$this->view->roles = (new RolesConfig())->select();
|
||||
|
||||
$sortAndFilterColumns = [
|
||||
'name' => $this->translate('Name'),
|
||||
'users' => $this->translate('Users'),
|
||||
'groups' => $this->translate('Groups'),
|
||||
'permissions' => $this->translate('Permissions')
|
||||
];
|
||||
|
||||
$this->setupFilterControl($this->view->roles, $sortAndFilterColumns, ['name']);
|
||||
$this->setupLimitControl();
|
||||
$this->setupPaginationControl($this->view->roles);
|
||||
$this->setupSortControl($sortAndFilterColumns, $this->view->roles, ['name']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -82,9 +142,8 @@ class RoleController extends AuthBackendController
|
||||
*/
|
||||
public function addAction()
|
||||
{
|
||||
$role = new RoleForm();
|
||||
$role = $this->prepareForm();
|
||||
$role->setRedirectUrl('__CLOSE__');
|
||||
$role->setRepository(new RolesConfig());
|
||||
$role->setSubmitLabel($this->translate('Create Role'));
|
||||
$role->add()->handleRequest();
|
||||
|
||||
@ -99,9 +158,8 @@ class RoleController extends AuthBackendController
|
||||
public function editAction()
|
||||
{
|
||||
$name = $this->params->getRequired('role');
|
||||
$role = new RoleForm();
|
||||
$role = $this->prepareForm();
|
||||
$role->setRedirectUrl('__CLOSE__');
|
||||
$role->setRepository(new RolesConfig());
|
||||
$role->setSubmitLabel($this->translate('Update Role'));
|
||||
$role->edit($name);
|
||||
|
||||
@ -120,9 +178,8 @@ class RoleController extends AuthBackendController
|
||||
public function removeAction()
|
||||
{
|
||||
$name = $this->params->getRequired('role');
|
||||
$role = new RoleForm();
|
||||
$role = $this->prepareForm();
|
||||
$role->setRedirectUrl('__CLOSE__');
|
||||
$role->setRepository(new RolesConfig());
|
||||
$role->setSubmitLabel($this->translate('Remove Role'));
|
||||
$role->remove($name);
|
||||
|
||||
@ -389,4 +446,16 @@ class RoleController extends AuthBackendController
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a form for role addition/modification/deletion and set the storage
|
||||
*
|
||||
* @return RoleForm
|
||||
*/
|
||||
private function prepareForm(): RoleForm
|
||||
{
|
||||
return Config::app()->get('global', 'store_roles_in_db')
|
||||
? (new RoleDbForm())->setDb($this->getDb())
|
||||
: (new RoleForm())->setRepository(new RolesConfig());
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +100,17 @@ class ApplicationConfigForm extends Form
|
||||
)
|
||||
);
|
||||
|
||||
$this->addElement(
|
||||
'checkbox',
|
||||
'global_store_roles_in_db',
|
||||
[
|
||||
'label' => $this->translate('Store Roles in Database'),
|
||||
'description' => $this->translate(
|
||||
'Set whether to store roles used for access control in the database selected above.'
|
||||
)
|
||||
]
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
330
application/forms/Security/RoleDbForm.php
Normal file
330
application/forms/Security/RoleDbForm.php
Normal file
@ -0,0 +1,330 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Forms\Security;
|
||||
|
||||
use DateTime;
|
||||
use Icinga\Application\Modules\Manager;
|
||||
use Icinga\Model\Role;
|
||||
use Icinga\Util\StringHelper;
|
||||
use ipl\Sql\Connection;
|
||||
use ipl\Sql\Delete;
|
||||
use ipl\Sql\Insert;
|
||||
use ipl\Sql\Select;
|
||||
use ipl\Sql\Update;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
/**
|
||||
* Form for managing roles stored in the database
|
||||
*/
|
||||
class RoleDbForm extends RoleForm
|
||||
{
|
||||
/**
|
||||
* Database where the roles are stored
|
||||
*
|
||||
* @var ?Connection
|
||||
*/
|
||||
private $db = null;
|
||||
|
||||
public function fetchEntry()
|
||||
{
|
||||
$role = Role::on($this->db)->with('parent')->filter(Filter::equal('name', $this->getIdentifier()))->first();
|
||||
|
||||
if (! $role) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$values = [
|
||||
'name' => $role->name,
|
||||
'unrestricted' => (int) $role->unrestricted
|
||||
];
|
||||
|
||||
$users = [];
|
||||
$groups = [];
|
||||
$permissions = [];
|
||||
$refusals = [];
|
||||
$restrictions = [];
|
||||
|
||||
foreach ($role->users as $user) {
|
||||
$users[] = $user->user_name;
|
||||
}
|
||||
|
||||
foreach ($role->groups as $group) {
|
||||
$groups[] = $group->group_name;
|
||||
}
|
||||
|
||||
foreach ($role->permissions as $permission) {
|
||||
if ($permission->allowed) {
|
||||
$permissions[$permission->permission] = true;
|
||||
|
||||
if ($permission->permission === '*') {
|
||||
$values[self::WILDCARD_NAME] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($permission->denied) {
|
||||
$refusals[$permission->permission] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($role->restrictions as $restriction) {
|
||||
$restrictions[$restriction->restriction] = $restriction->filter;
|
||||
}
|
||||
|
||||
if ($role->parent) {
|
||||
$values['parent'] = $role->parent->name;
|
||||
}
|
||||
|
||||
if ($users) {
|
||||
sort($users);
|
||||
$values['users'] = implode(',', $users);
|
||||
}
|
||||
|
||||
if ($groups) {
|
||||
sort($groups);
|
||||
$values['groups'] = implode(',', $groups);
|
||||
}
|
||||
|
||||
if ($permissions || $refusals) {
|
||||
foreach ($this->providedPermissions as $moduleName => $permissionList) {
|
||||
$hasFullPerm = false;
|
||||
|
||||
foreach ($permissionList as $name => $spec) {
|
||||
if (array_key_exists($name, $permissions)) {
|
||||
$values[$this->filterName($name)] = 1;
|
||||
|
||||
if (isset($spec['isFullPerm'])) {
|
||||
$hasFullPerm = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists($name, $refusals)) {
|
||||
$values[$this->filterName(self::DENY_PREFIX . $name)] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasFullPerm) {
|
||||
unset($values[$this->filterName(Manager::MODULE_PERMISSION_NS . $moduleName)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($restrictions) {
|
||||
foreach ($this->providedRestrictions as $restrictionList) {
|
||||
foreach ($restrictionList as $name => $spec) {
|
||||
if (array_key_exists($name, $restrictions)) {
|
||||
$values[$this->filterName($name)] = $restrictions[$name];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (object) $values;
|
||||
}
|
||||
|
||||
protected function entryExists(): bool
|
||||
{
|
||||
return Role::on($this->db)->filter(Filter::equal('name', $this->getIdentifier()))->count() > 0;
|
||||
}
|
||||
|
||||
protected function insertEntry(): void
|
||||
{
|
||||
$values = $this->getValues();
|
||||
|
||||
$this->db->transaction(function (Connection $db) use ($values) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role')
|
||||
->columns(['parent_id', 'name', 'unrestricted', 'ctime'])
|
||||
->values([
|
||||
$this->queryRoleId($db, $values['parent']),
|
||||
$values['name'],
|
||||
$values['unrestricted'] ? 'y' : 'n',
|
||||
(new DateTime())->getTimestamp() * 1000
|
||||
])
|
||||
);
|
||||
|
||||
$this->insertChildTables($db, $db->lastInsertId(), $values);
|
||||
});
|
||||
}
|
||||
|
||||
protected function updateEntry(): void
|
||||
{
|
||||
$values = $this->getValues();
|
||||
|
||||
$this->db->transaction(function (Connection $db) use ($values) {
|
||||
$id = $this->queryRoleId($db, $this->getIdentifier());
|
||||
|
||||
$db->prepexec(
|
||||
(new Update())
|
||||
->table('icingaweb_role')
|
||||
->set([
|
||||
'parent_id' => $this->queryRoleId($db, $values['parent']),
|
||||
'name' => $values['name'],
|
||||
'unrestricted' => $values['unrestricted'] ? 'y' : 'n',
|
||||
'mtime' => (new DateTime())->getTimestamp() * 1000
|
||||
])
|
||||
->where(['id = ?' => $id])
|
||||
);
|
||||
|
||||
$db->prepexec((new Delete())->from('icingaweb_role_user')->where(['role_id = ?' => $id]));
|
||||
$db->prepexec((new Delete())->from('icingaweb_role_group')->where(['role_id = ?' => $id]));
|
||||
$db->prepexec((new Delete())->from('icingaweb_role_permission')->where(['role_id = ?' => $id]));
|
||||
$db->prepexec((new Delete())->from('icingaweb_role_restriction')->where(['role_id = ?' => $id]));
|
||||
|
||||
$this->insertChildTables($db, $id, $values);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the ID of a role
|
||||
*
|
||||
* @param Connection $db Database to operate on
|
||||
* @param ?string $name Target role name
|
||||
*
|
||||
* @return ?int Target role ID or null
|
||||
*/
|
||||
private function queryRoleId(Connection $db, ?string $name): ?int
|
||||
{
|
||||
if ($name !== null) {
|
||||
$role = Role::on($db)->filter(Filter::equal('name', $name))->columns('id')->first();
|
||||
|
||||
if ($role) {
|
||||
return $role->id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate icingaweb_role_* tables for a new role
|
||||
*
|
||||
* @param Connection $db Database to operate on
|
||||
* @param int $id Role ID
|
||||
* @param array $values Role data as from {@link getValues()}
|
||||
*/
|
||||
private function insertChildTables(Connection $db, int $id, array $values): void
|
||||
{
|
||||
$permissions = StringHelper::trimSplit($values['permissions']);
|
||||
$refusals = StringHelper::trimSplit($values['refusals']);
|
||||
$permissionsAndRefusals = [];
|
||||
|
||||
foreach (StringHelper::trimSplit($values['users']) as $user) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_user')
|
||||
->columns(['role_id', 'user_name'])
|
||||
->values([$id, $user])
|
||||
);
|
||||
}
|
||||
|
||||
foreach (StringHelper::trimSplit($values['groups']) as $group) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_group')
|
||||
->columns(['role_id', 'group_name'])
|
||||
->values([$id, $group])
|
||||
);
|
||||
}
|
||||
|
||||
foreach ([$permissions, $refusals] as $permissionsOrRefusals) {
|
||||
foreach ($permissionsOrRefusals as $permissionOrRefusal) {
|
||||
$permissionsAndRefusals[$permissionOrRefusal] = ['allowed' => 'n', 'denied' => 'n'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($permissions as $permission) {
|
||||
$permissionsAndRefusals[$permission]['allowed'] = 'y';
|
||||
}
|
||||
|
||||
foreach ($refusals as $refusal) {
|
||||
$permissionsAndRefusals[$refusal]['denied'] = 'y';
|
||||
}
|
||||
|
||||
foreach ($permissionsAndRefusals as $name => $authz) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_permission')
|
||||
->columns(['role_id', 'permission', 'allowed', 'denied'])
|
||||
->values([$id, $name, $authz['allowed'], $authz['denied']])
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($this->providedRestrictions as $restrictionList) {
|
||||
foreach ($restrictionList as $name => $_) {
|
||||
if (isset($values[$name])) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_restriction')
|
||||
->columns(['role_id', 'restriction', 'filter'])
|
||||
->values([$id, $name, $values[$name]])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function deleteEntry(): void
|
||||
{
|
||||
$this->db->prepexec((new Delete())->from('icingaweb_role')->where(['name = ?' => $this->getIdentifier()]));
|
||||
}
|
||||
|
||||
protected function collectRoles(): array
|
||||
{
|
||||
$roles = [];
|
||||
$name = $this->getIdentifier();
|
||||
|
||||
if ($name === null) {
|
||||
foreach (Role::on($this->db)->orderBy('name')->columns('name') as $role) {
|
||||
$roles[$role->name] = $role->name;
|
||||
}
|
||||
} else {
|
||||
$query = (new Select())
|
||||
->with(
|
||||
(new Select())
|
||||
->from('icingaweb_role')
|
||||
->where(['name = ?' => $name])
|
||||
->columns(['id', 'parent_id'])
|
||||
->union( // handle circular relationships
|
||||
(new Select())
|
||||
->from(['r' => 'icingaweb_role'])
|
||||
->join('rl', 'rl.id = r.parent_id')
|
||||
->columns(['r.id', 'r.parent_id'])
|
||||
),
|
||||
'rl',
|
||||
true
|
||||
)
|
||||
->from('icingaweb_role')
|
||||
->where(['id NOT IN ?' => (new Select())->from('rl')->columns('id')])
|
||||
->orderBy('name')
|
||||
->columns('name');
|
||||
|
||||
foreach ($this->db->select($query) as $row) {
|
||||
$roles[$row->name] = $row->name;
|
||||
}
|
||||
}
|
||||
|
||||
return $roles;
|
||||
}
|
||||
|
||||
protected function onRenameSuccess(string $oldName, ?string $newName): void
|
||||
{
|
||||
// Already handled by the database schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the database where the roles are stored
|
||||
*
|
||||
* @param Connection $db
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setDb(Connection $db): self
|
||||
{
|
||||
$this->db = $db;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -511,11 +511,7 @@ class RoleForm extends RepositoryForm
|
||||
}
|
||||
|
||||
if ($this->getIdentifier() && ($newName = $this->getValue('name')) !== $this->getIdentifier()) {
|
||||
$this->repository->update(
|
||||
$this->getBaseTable(),
|
||||
['parent' => $newName],
|
||||
Filter::where('parent', $this->getIdentifier())
|
||||
);
|
||||
$this->onRenameSuccess($this->getIdentifier(), $newName);
|
||||
}
|
||||
|
||||
if (ConfigFormEventsHook::runOnSuccess($this) === false) {
|
||||
@ -526,6 +522,17 @@ class RoleForm extends RepositoryForm
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update child roles of role $oldName, set their parent to $newName
|
||||
*
|
||||
* @param string $oldName
|
||||
* @param string $newName
|
||||
*/
|
||||
protected function onRenameSuccess(string $oldName, ?string $newName): void
|
||||
{
|
||||
$this->repository->update($this->getBaseTable(), ['parent' => $newName], Filter::where('parent', $oldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect permissions and restrictions provided by Icinga Web 2 and modules
|
||||
*
|
||||
|
@ -29,14 +29,16 @@ Option | Description
|
||||
show\_stacktraces | **Optional.** Whether to show debug stacktraces. Defaults to `0`.
|
||||
module\_path | **Optional.** Specifies the directories where modules can be installed. Multiple directories must be separated with colons.
|
||||
config\_resource | **Required.** Specify a defined [resource](04-Resources.md#resources-configuration-database) name.
|
||||
store\_roles\_in\_db | **Optional.** Whether to store roles used for access control in the database specified above. Defaults to `0`.
|
||||
|
||||
|
||||
Example for storing the user preferences in the database resource `icingaweb_db`:
|
||||
Example for storing the user preferences and roles in the database resource `icingaweb_db`:
|
||||
|
||||
```
|
||||
[global]
|
||||
show_stacktraces = "0"
|
||||
config_resource = "icingaweb_db"
|
||||
store_roles_in_db = "1"
|
||||
module_path = "/usr/share/icingaweb2/modules"
|
||||
```
|
||||
|
||||
|
@ -3,20 +3,29 @@
|
||||
|
||||
namespace Icinga\Authentication;
|
||||
|
||||
use Exception;
|
||||
use Generator;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Application\Logger;
|
||||
use Icinga\Common\Database;
|
||||
use Icinga\Exception\ConfigurationError;
|
||||
use Icinga\Exception\NotReadableError;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Model\Role as RoleModel;
|
||||
use Icinga\Model\RolePermission;
|
||||
use Icinga\Model\RoleRestriction;
|
||||
use Icinga\User;
|
||||
use Icinga\Util\StringHelper;
|
||||
use ipl\Sql\Connection;
|
||||
use ipl\Sql\Select;
|
||||
use ipl\Stdlib\Filter;
|
||||
|
||||
/**
|
||||
* Retrieve restrictions and permissions for users
|
||||
*/
|
||||
class AdmissionLoader
|
||||
{
|
||||
use Database;
|
||||
|
||||
const LEGACY_PERMISSIONS = [
|
||||
'admin' => 'application/announcements',
|
||||
'application/stacktraces' => 'user/application/stacktraces',
|
||||
@ -52,12 +61,27 @@ class AdmissionLoader
|
||||
/** @var ConfigObject */
|
||||
protected $roleConfig;
|
||||
|
||||
/**
|
||||
* Database where the roles are stored
|
||||
*
|
||||
* @var ?Connection
|
||||
*/
|
||||
protected $rolesDb = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
try {
|
||||
$this->roleConfig = Config::app('roles');
|
||||
} catch (NotReadableError $e) {
|
||||
Logger::error('Can\'t access roles configuration. An exception was thrown:', $e);
|
||||
if (Config::app()->get('global', 'store_roles_in_db')) {
|
||||
$db = $this->getDb();
|
||||
|
||||
RoleModel::on($db)->limit(1)->columns('id')->first();
|
||||
|
||||
$this->rolesDb = $db;
|
||||
} else {
|
||||
$this->roleConfig = Config::app('roles');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Logger::error('Can\'t access roles storage. An exception was thrown:', $e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,6 +194,10 @@ class AdmissionLoader
|
||||
*/
|
||||
public function applyRoles(User $user)
|
||||
{
|
||||
if ($this->rolesDb !== null) {
|
||||
$this->applyDbRoles($user);
|
||||
}
|
||||
|
||||
if ($this->roleConfig === null) {
|
||||
return;
|
||||
}
|
||||
@ -229,6 +257,138 @@ class AdmissionLoader
|
||||
$user->setRoles(array_values($roles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply permissions, restrictions and roles from the database to the given user
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
private function applyDbRoles(User $user): void
|
||||
{
|
||||
$direct = (new Select())
|
||||
->from('icingaweb_role')
|
||||
->where([
|
||||
'id IN ?' => (new Select())
|
||||
->from('icingaweb_role_user')
|
||||
->where(['user_name IN (?)' => [$user->getUsername(), '*']])
|
||||
->columns('role_id')
|
||||
])
|
||||
->columns(['id', 'parent_id', 'name', 'unrestricted', 'direct' => '1']);
|
||||
|
||||
$userGroups = $user->getGroups();
|
||||
$roleData = [];
|
||||
$roles = [];
|
||||
$assignedRoles = [];
|
||||
$unrestricted = false;
|
||||
|
||||
if ($userGroups) {
|
||||
$userGroups = array_values($userGroups);
|
||||
|
||||
$direct->orWhere([
|
||||
'id IN ?' => (new Select())
|
||||
->from('icingaweb_role_group')
|
||||
->where(['group_name IN (?)' => $userGroups])
|
||||
->columns('role_id')
|
||||
]);
|
||||
}
|
||||
|
||||
// Not a UNION ALL to handle circular relationships.
|
||||
// Due to the "direct" column such may still appear twice.
|
||||
// Hence ORDER BY direct, so that the last one (direct=1) wins.
|
||||
$query = (new Select())
|
||||
->with(
|
||||
$direct->union(
|
||||
(new Select())
|
||||
->from(['r' => 'icingaweb_role'])
|
||||
->join('rl', 'rl.parent_id = r.id')
|
||||
->columns(['r.id', 'r.parent_id', 'r.name', 'r.unrestricted', 'direct' => '0'])
|
||||
),
|
||||
'rl',
|
||||
true
|
||||
)
|
||||
->from('rl')
|
||||
->orderBy('direct')
|
||||
->columns(['id', 'parent_id', 'name', 'unrestricted', 'direct']);
|
||||
|
||||
foreach ($this->rolesDb->select($query) as $row) {
|
||||
$roleData[$row->id] = $row;
|
||||
}
|
||||
|
||||
foreach ($roleData as $row) {
|
||||
$roles[$row->id] = (new Role())
|
||||
->setName($row->name)
|
||||
->setIsUnrestricted($row->unrestricted);
|
||||
|
||||
if ($row->direct) {
|
||||
$assignedRoles[] = $row->name;
|
||||
}
|
||||
|
||||
if ($row->unrestricted) {
|
||||
$unrestricted = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($roleData as $row) {
|
||||
if ($row->parent_id) {
|
||||
$parent = $roles[$row->parent_id];
|
||||
$child = $roles[$row->id];
|
||||
|
||||
$child->setParent($parent);
|
||||
$parent->addChild($child);
|
||||
}
|
||||
}
|
||||
|
||||
$filter = Filter::equal('role_id', array_keys($roles));
|
||||
$permissions = [];
|
||||
$allPermissions = [];
|
||||
$refusals = [];
|
||||
$restrictions = [];
|
||||
$allRestrictions = [];
|
||||
|
||||
foreach (RolePermission::on($this->rolesDb)->filter($filter) as $row) {
|
||||
if ($row->allowed) {
|
||||
$permissions[$row->role_id][] = $row->permission;
|
||||
}
|
||||
|
||||
if ($row->denied) {
|
||||
$refusals[$row->role_id][] = $row->permission;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($permissions as $roleId => & $rolePermissions) {
|
||||
list($rolePermissions, $newRefusals) = $this->migrateLegacyPermissions($rolePermissions);
|
||||
|
||||
if ($newRefusals) {
|
||||
array_push($refusals[$roleId], ...$newRefusals);
|
||||
}
|
||||
|
||||
$roles[$roleId]->setPermissions($rolePermissions);
|
||||
array_push($allPermissions, ...$rolePermissions);
|
||||
}
|
||||
|
||||
foreach ($refusals as $roleId => $roleRefusals) {
|
||||
$roles[$roleId]->setRefusals($roleRefusals);
|
||||
}
|
||||
|
||||
foreach (RoleRestriction::on($this->rolesDb)->filter($filter) as $row) {
|
||||
$restrictions[$row->role_id][$row->restriction] = $row->filter;
|
||||
}
|
||||
|
||||
foreach ($restrictions as $roleId => & $roleRestrictions) {
|
||||
foreach ($roleRestrictions as $name => & $restriction) {
|
||||
$restriction = str_replace('$user.local_name$', $user->getLocalUsername(), $restriction);
|
||||
$allRestrictions[$name][] = $restriction;
|
||||
}
|
||||
|
||||
$roles[$roleId]->setRestrictions($roleRestrictions);
|
||||
}
|
||||
|
||||
$user->setAdditional('assigned_roles', $assignedRoles);
|
||||
$user->setIsUnrestricted($unrestricted);
|
||||
$user->setRestrictions($unrestricted ? [] : $allRestrictions);
|
||||
$user->setPermissions(array_values(array_unique($allPermissions)));
|
||||
$user->setRoles(array_values($roles));
|
||||
}
|
||||
|
||||
public static function migrateLegacyPermissions(array $permissions)
|
||||
{
|
||||
$migratedGrants = [];
|
||||
|
@ -4,6 +4,7 @@
|
||||
namespace Icinga\Common;
|
||||
|
||||
use Icinga\Application\Config as IcingaConfig;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use ipl\Sql\Config as SqlConfig;
|
||||
use ipl\Sql\Connection;
|
||||
@ -18,17 +19,19 @@ trait Database
|
||||
/**
|
||||
* Get a connection to the Icinga Web database
|
||||
*
|
||||
* @param ?ConfigObject $params Optional custom database specification
|
||||
*
|
||||
* @return Connection
|
||||
*
|
||||
* @throws \Icinga\Exception\ConfigurationError
|
||||
*/
|
||||
protected function getDb(): Connection
|
||||
protected function getDb(ConfigObject $params = null): Connection
|
||||
{
|
||||
if (! $this->hasDb()) {
|
||||
if ($params === null && ! $this->hasDb()) {
|
||||
throw new LogicException('Please check if a db instance exists at all');
|
||||
}
|
||||
|
||||
$config = new SqlConfig(ResourceFactory::getResourceConfig(
|
||||
$config = new SqlConfig($params ?? ResourceFactory::getResourceConfig(
|
||||
IcingaConfig::app()->get('global', 'config_resource')
|
||||
));
|
||||
if ($config->db === 'mysql') {
|
||||
|
79
library/Icinga/Model/Role.php
Normal file
79
library/Icinga/Model/Role.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Model;
|
||||
|
||||
use DateTime;
|
||||
use ipl\Orm\Behavior\BoolCast;
|
||||
use ipl\Orm\Behavior\MillisecondTimestamp;
|
||||
use ipl\Orm\Behaviors;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\Relations;
|
||||
|
||||
/**
|
||||
* A database model for Icinga Web role table
|
||||
*
|
||||
* @property int $id Unique identifier
|
||||
* @property ?int $parent_id Inherited role identifier (optional)
|
||||
* @property string $name Unique name
|
||||
* @property bool $unrestricted Whether restrictions don't apply
|
||||
* @property DateTime $ctime The insert time
|
||||
* @property ?DateTime $mtime The modification time (optional)
|
||||
* @property ?Role $parent Inherited role (optional)
|
||||
* @property Role[] $children Inheriting roles
|
||||
* @property RoleUser[] $users Users this role applies to
|
||||
* @property RoleGroup[] $groups Groups this role applies to
|
||||
* @property RolePermission[] $permissions Permissions this role allows/denies
|
||||
* @property RoleRestriction[] $restrictions Restrictions this role imposes
|
||||
*/
|
||||
class Role extends Model
|
||||
{
|
||||
public function getTableName(): string
|
||||
{
|
||||
return 'icingaweb_role';
|
||||
}
|
||||
|
||||
public function getKeyName(): string
|
||||
{
|
||||
return 'id';
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return ['parent_id', 'name', 'unrestricted', 'ctime', 'mtime'];
|
||||
}
|
||||
|
||||
public function createBehaviors(Behaviors $behaviors): void
|
||||
{
|
||||
$behaviors->add(new BoolCast(['unrestricted']));
|
||||
$behaviors->add(new MillisecondTimestamp(['ctime', 'mtime']));
|
||||
}
|
||||
|
||||
public function createRelations(Relations $relations): void
|
||||
{
|
||||
$relations->belongsTo('parent', self::class)
|
||||
->setCandidateKey('parent_id')
|
||||
->setJoinType('LEFT');
|
||||
|
||||
$relations->hasMany('children', self::class)
|
||||
->setForeignKey('parent_id')
|
||||
->setJoinType('LEFT');
|
||||
|
||||
$relations->hasMany('users', RoleUser::class)
|
||||
->setForeignKey('role_id')
|
||||
->setJoinType('LEFT');
|
||||
|
||||
$relations->hasMany('groups', RoleGroup::class)
|
||||
->setForeignKey('role_id')
|
||||
->setJoinType('LEFT');
|
||||
|
||||
$relations->hasMany('permissions', RolePermission::class)
|
||||
->setForeignKey('role_id')
|
||||
->setJoinType('LEFT');
|
||||
|
||||
$relations->hasMany('restrictions', RoleRestriction::class)
|
||||
->setForeignKey('role_id')
|
||||
->setJoinType('LEFT');
|
||||
}
|
||||
}
|
40
library/Icinga/Model/RoleGroup.php
Normal file
40
library/Icinga/Model/RoleGroup.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Model;
|
||||
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\Relations;
|
||||
|
||||
/**
|
||||
* A database model for Icinga Web role-group table
|
||||
*
|
||||
* @property int $role_id Role identifier
|
||||
* @property string $group_name Group name
|
||||
* @property Role $role Role object
|
||||
*/
|
||||
class RoleGroup extends Model
|
||||
{
|
||||
public function getTableName(): string
|
||||
{
|
||||
return 'icingaweb_role_group';
|
||||
}
|
||||
|
||||
public function getKeyName(): array
|
||||
{
|
||||
return ['group_name', 'role_id'];
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function createRelations(Relations $relations): void
|
||||
{
|
||||
$relations->belongsTo('icingaweb_role', Role::class) // TODO(ak): make 'role' working
|
||||
->setCandidateKey('role_id')
|
||||
->setJoinType('INNER');
|
||||
}
|
||||
}
|
49
library/Icinga/Model/RolePermission.php
Normal file
49
library/Icinga/Model/RolePermission.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Model;
|
||||
|
||||
use ipl\Orm\Behavior\BoolCast;
|
||||
use ipl\Orm\Behaviors;
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\Relations;
|
||||
|
||||
/**
|
||||
* A database model for Icinga Web role-permission table
|
||||
*
|
||||
* @property int $role_id Role identifier
|
||||
* @property string $permission Permission name
|
||||
* @property bool $allowed Whether the permission is allowed
|
||||
* @property bool $denied Whether the permission is denied
|
||||
* @property Role $role Role object
|
||||
*/
|
||||
class RolePermission extends Model
|
||||
{
|
||||
public function getTableName(): string
|
||||
{
|
||||
return 'icingaweb_role_permission';
|
||||
}
|
||||
|
||||
public function getKeyName(): array
|
||||
{
|
||||
return ['role_id', 'permission'];
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return ['allowed', 'denied'];
|
||||
}
|
||||
|
||||
public function createBehaviors(Behaviors $behaviors): void
|
||||
{
|
||||
$behaviors->add(new BoolCast(['allowed', 'denied']));
|
||||
}
|
||||
|
||||
public function createRelations(Relations $relations): void
|
||||
{
|
||||
$relations->belongsTo('icingaweb_role', Role::class) // TODO(ak): make 'role' working
|
||||
->setCandidateKey('role_id')
|
||||
->setJoinType('INNER');
|
||||
}
|
||||
}
|
41
library/Icinga/Model/RoleRestriction.php
Normal file
41
library/Icinga/Model/RoleRestriction.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Model;
|
||||
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\Relations;
|
||||
|
||||
/**
|
||||
* A database model for Icinga Web role-restriction table
|
||||
*
|
||||
* @property int $role_id Role identifier
|
||||
* @property string $restriction Restriction name
|
||||
* @property string $filter Filter of things the role is restricted to
|
||||
* @property Role $role Role object
|
||||
*/
|
||||
class RoleRestriction extends Model
|
||||
{
|
||||
public function getTableName(): string
|
||||
{
|
||||
return 'icingaweb_role_restriction';
|
||||
}
|
||||
|
||||
public function getKeyName(): array
|
||||
{
|
||||
return ['role_id', 'restriction'];
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return ['filter'];
|
||||
}
|
||||
|
||||
public function createRelations(Relations $relations): void
|
||||
{
|
||||
$relations->belongsTo('icingaweb_role', Role::class) // TODO(ak): make 'role' working
|
||||
->setCandidateKey('role_id')
|
||||
->setJoinType('INNER');
|
||||
}
|
||||
}
|
40
library/Icinga/Model/RoleUser.php
Normal file
40
library/Icinga/Model/RoleUser.php
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Model;
|
||||
|
||||
use ipl\Orm\Model;
|
||||
use ipl\Orm\Relations;
|
||||
|
||||
/**
|
||||
* A database model for Icinga Web role-user table
|
||||
*
|
||||
* @property int $role_id Role identifier
|
||||
* @property string $user_name User name
|
||||
* @property Role $role Role object
|
||||
*/
|
||||
class RoleUser extends Model
|
||||
{
|
||||
public function getTableName(): string
|
||||
{
|
||||
return 'icingaweb_role_user';
|
||||
}
|
||||
|
||||
public function getKeyName(): array
|
||||
{
|
||||
return ['user_name', 'role_id'];
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function createRelations(Relations $relations): void
|
||||
{
|
||||
$relations->belongsTo('icingaweb_role', Role::class) // TODO(ak): make 'role' working
|
||||
->setCandidateKey('role_id')
|
||||
->setJoinType('INNER');
|
||||
}
|
||||
}
|
101
library/Icinga/Web/Widget/RolesTable.php
Normal file
101
library/Icinga/Web/Widget/RolesTable.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/* Icinga Web 2 | (c) 2024 Icinga GmbH | GPLv2+ */
|
||||
|
||||
namespace Icinga\Web\Widget;
|
||||
|
||||
use Icinga\Web\Url;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\I18n\Translation;
|
||||
|
||||
/**
|
||||
* Render roles as table
|
||||
*/
|
||||
class RolesTable extends BaseHtmlElement
|
||||
{
|
||||
use Translation;
|
||||
|
||||
protected $tag = 'table';
|
||||
|
||||
/**
|
||||
* The roles to display
|
||||
*
|
||||
* @var iterable
|
||||
*/
|
||||
protected $roles = [];
|
||||
|
||||
protected function assemble(): void
|
||||
{
|
||||
$this->setAttributes([
|
||||
'class' => 'table-row-selectable common-table',
|
||||
'data-base-target' => '_next'
|
||||
]);
|
||||
|
||||
$this->addHtml(Html::tag('thead', [], [
|
||||
Html::tag('tr', [], [
|
||||
Html::tag('th', [], [$this->translate('Name')]),
|
||||
Html::tag('th', [], [$this->translate('Users')]),
|
||||
Html::tag('th', [], [$this->translate('Groups')]),
|
||||
Html::tag('th', [], [$this->translate('Inherits From')]),
|
||||
Html::tag('th')
|
||||
])
|
||||
]));
|
||||
|
||||
$tbody = Html::tag('tbody');
|
||||
|
||||
$this->addHtml($tbody);
|
||||
|
||||
foreach ($this->roles as $role) {
|
||||
$users = [];
|
||||
$groups = [];
|
||||
|
||||
foreach ($role->users as $user) {
|
||||
$users[] = $user->user_name;
|
||||
}
|
||||
|
||||
foreach ($role->groups as $group) {
|
||||
$groups[] = $group->group_name;
|
||||
}
|
||||
|
||||
sort($users);
|
||||
sort($groups);
|
||||
|
||||
$tbody->addHtml(Html::tag('tr', [], [
|
||||
Html::tag('td', [], [Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => Url::fromPath('role/edit', ['role' => $role->name]),
|
||||
'title' => sprintf($this->translate('Edit role %s'), $role->name)
|
||||
],
|
||||
$role->name
|
||||
)]),
|
||||
Html::tag('td', [], [implode(',', $users)]),
|
||||
Html::tag('td', [], [implode(',', $groups)]),
|
||||
Html::tag('td', [], $role->parent ? [$role->parent->name] : null),
|
||||
Html::tag('td', ['class' => 'icon-col'], [Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => Url::fromPath('role/remove', ['role' => $role->name]),
|
||||
'class' => 'action-link icon-cancel',
|
||||
'title' => sprintf($this->translate('Remove role %s'), $role->name)
|
||||
]
|
||||
)])
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the roles to display
|
||||
*
|
||||
* @param iterable $roles
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setRoles(iterable $roles): self
|
||||
{
|
||||
$this->roles = $roles;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
@ -33,6 +33,8 @@ class GeneralConfigPage extends Form
|
||||
$appConfigForm->createElements($formData);
|
||||
$appConfigForm->removeElement('global_module_path');
|
||||
$appConfigForm->removeElement('global_config_resource');
|
||||
$appConfigForm->removeElement('global_store_roles_in_db');
|
||||
$this->addElement('hidden', 'global_store_roles_in_db', ['disabled' => true, 'value' => 1]);
|
||||
$this->addElements($appConfigForm->getElements());
|
||||
|
||||
$loggingConfigForm = new LoggingConfigForm();
|
||||
|
@ -3,23 +3,29 @@
|
||||
|
||||
namespace Icinga\Module\Setup\Steps;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Common\Database;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Data\ResourceFactory;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Authentication\User\DbUserBackend;
|
||||
use Icinga\Module\Setup\Step;
|
||||
use ipl\Sql\Connection;
|
||||
use ipl\Sql\Insert;
|
||||
|
||||
class AuthenticationStep extends Step
|
||||
{
|
||||
use Database;
|
||||
|
||||
protected $data;
|
||||
|
||||
protected $dbError;
|
||||
|
||||
protected $authIniError;
|
||||
|
||||
protected $permIniError;
|
||||
protected $roleError;
|
||||
|
||||
public function __construct(array $data)
|
||||
{
|
||||
@ -29,11 +35,15 @@ class AuthenticationStep extends Step
|
||||
public function apply()
|
||||
{
|
||||
$success = $this->createAuthenticationIni();
|
||||
|
||||
if (isset($this->data['adminAccountData']['resourceConfig'])) {
|
||||
$success &= $this->createAccount();
|
||||
}
|
||||
|
||||
$success &= $this->createRolesIni();
|
||||
if (isset($this->data['rolesResourceConfig'])) {
|
||||
$success &= $this->createRoles();
|
||||
}
|
||||
|
||||
return $success;
|
||||
}
|
||||
|
||||
@ -61,34 +71,59 @@ class AuthenticationStep extends Step
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function createRolesIni()
|
||||
protected function createRoles(): bool
|
||||
{
|
||||
if (isset($this->data['adminAccountData']['username'])) {
|
||||
$config = array(
|
||||
'users' => $this->data['adminAccountData']['username'],
|
||||
'permissions' => '*'
|
||||
);
|
||||
|
||||
if ($this->data['backendConfig']['backend'] === 'db') {
|
||||
$config['groups'] = mt('setup', 'Administrators', 'setup.role.name');
|
||||
}
|
||||
} else { // isset($this->data['adminAccountData']['groupname'])
|
||||
$config = array(
|
||||
'groups' => $this->data['adminAccountData']['groupname'],
|
||||
'permissions' => '*'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
Config::fromArray(array(mt('setup', 'Administrators', 'setup.role.name') => $config))
|
||||
->setConfigFile(Config::resolvePath('roles.ini'))
|
||||
->saveIni();
|
||||
$this->getDb(new ConfigObject($this->data['rolesResourceConfig']))->transaction(function (Connection $db) {
|
||||
$admins = mt('setup', 'Administrators', 'setup.role.name');
|
||||
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role')
|
||||
->columns(['name', 'ctime'])
|
||||
->values([$admins, (new DateTime())->getTimestamp() * 1000])
|
||||
);
|
||||
|
||||
$id = $db->lastInsertId();
|
||||
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_permission')
|
||||
->columns(['role_id', 'permission', 'allowed'])
|
||||
->values([$id, '*', 'y'])
|
||||
);
|
||||
|
||||
if (isset($this->data['adminAccountData']['username'])) {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_user')
|
||||
->columns(['role_id', 'user_name'])
|
||||
->values([$id, $this->data['adminAccountData']['username']])
|
||||
);
|
||||
|
||||
if ($this->data['backendConfig']['backend'] === 'db') {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_group')
|
||||
->columns(['role_id', 'group_name'])
|
||||
->values([$id, $admins])
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$db->prepexec(
|
||||
(new Insert())
|
||||
->into('icingaweb_role_group')
|
||||
->columns(['role_id', 'group_name'])
|
||||
->values([$id, $this->data['adminAccountData']['groupname']])
|
||||
);
|
||||
}
|
||||
});
|
||||
} catch (Exception $e) {
|
||||
$this->permIniError = $e;
|
||||
$this->roleError = $e;
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->permIniError = false;
|
||||
$this->roleError = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -211,7 +246,7 @@ class AuthenticationStep extends Step
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->dbError));
|
||||
}
|
||||
|
||||
if ($this->permIniError === false) {
|
||||
if ($this->roleError === false) {
|
||||
$report[] = isset($this->data['adminAccountData']['username']) ? sprintf(
|
||||
mt('setup', 'Account "%s" has been successfully defined as initial administrator.'),
|
||||
$this->data['adminAccountData']['username']
|
||||
@ -219,7 +254,7 @@ class AuthenticationStep extends Step
|
||||
mt('setup', 'The members of the user group "%s" were successfully defined as initial administrators.'),
|
||||
$this->data['adminAccountData']['groupname']
|
||||
);
|
||||
} elseif ($this->permIniError !== null) {
|
||||
} elseif ($this->roleError !== null) {
|
||||
$report[] = isset($this->data['adminAccountData']['username']) ? sprintf(
|
||||
mt('setup', 'Unable to define account "%s" as initial administrator. An error occured:'),
|
||||
$this->data['adminAccountData']['username']
|
||||
@ -230,7 +265,7 @@ class AuthenticationStep extends Step
|
||||
),
|
||||
$this->data['adminAccountData']['groupname']
|
||||
);
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->permIniError));
|
||||
$report[] = sprintf(mt('setup', 'ERROR: %s'), IcingaException::describe($this->roleError));
|
||||
}
|
||||
|
||||
return $report;
|
||||
|
@ -97,6 +97,11 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
'icingaweb_group',
|
||||
'icingaweb_group_membership',
|
||||
'icingaweb_user',
|
||||
'icingaweb_role',
|
||||
'icingaweb_role_user',
|
||||
'icingaweb_role_group',
|
||||
'icingaweb_role_permission',
|
||||
'icingaweb_role_restriction',
|
||||
'icingaweb_user_preference',
|
||||
'icingaweb_rememberme',
|
||||
'icingaweb_schema'
|
||||
@ -514,11 +519,13 @@ class WebWizard extends Wizard implements SetupWizard
|
||||
$authType = $pageData['setup_authentication_type']['type'];
|
||||
$setup->addStep(
|
||||
new AuthenticationStep(array(
|
||||
'adminAccountData' => $adminAccountData,
|
||||
'backendConfig' => $pageData['setup_authentication_backend'],
|
||||
'resourceName' => $authType === 'db' ? $pageData['setup_auth_db_resource']['name'] : (
|
||||
'adminAccountData' => $adminAccountData,
|
||||
'backendConfig' => $pageData['setup_authentication_backend'],
|
||||
'resourceName' => $authType === 'db' ? $pageData['setup_auth_db_resource']['name'] : (
|
||||
$authType === 'ldap' ? $pageData['setup_ldap_resource']['name'] : null
|
||||
)
|
||||
),
|
||||
'rolesResourceConfig' => $pageData['setup_auth_db_resource']
|
||||
?? $pageData['setup_config_db_resource'] ?? null
|
||||
))
|
||||
);
|
||||
|
||||
|
57
schema/mysql-upgrades/2.13.0.sql
Normal file
57
schema/mysql-upgrades/2.13.0.sql
Normal file
@ -0,0 +1,57 @@
|
||||
CREATE TABLE icingaweb_role (
|
||||
id int unsigned NOT NULL AUTO_INCREMENT,
|
||||
parent_id int unsigned DEFAULT NULL,
|
||||
name varchar(254) NOT NULL,
|
||||
unrestricted enum('n', 'y') NOT NULL DEFAULT 'n',
|
||||
ctime bigint unsigned NOT NULL,
|
||||
mtime bigint unsigned DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT fk_icingaweb_role_parent_id FOREIGN KEY (parent_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE SET NULL,
|
||||
CONSTRAINT idx_icingaweb_role_name UNIQUE (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_user (
|
||||
role_id int unsigned NOT NULL,
|
||||
user_name varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
|
||||
PRIMARY KEY (role_id, user_name),
|
||||
INDEX idx_icingaweb_role_user_user_name (user_name),
|
||||
CONSTRAINT fk_icingaweb_role_user_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_group (
|
||||
role_id int unsigned NOT NULL,
|
||||
group_name varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
|
||||
PRIMARY KEY (role_id, group_name),
|
||||
INDEX idx_icingaweb_role_group_group_name (group_name),
|
||||
CONSTRAINT fk_icingaweb_role_group_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_permission (
|
||||
role_id int unsigned NOT NULL,
|
||||
permission varchar(254) NOT NULL,
|
||||
allowed enum('n', 'y') NOT NULL DEFAULT 'n',
|
||||
denied enum('n', 'y') NOT NULL DEFAULT 'n',
|
||||
|
||||
PRIMARY KEY (role_id, permission),
|
||||
CONSTRAINT fk_icingaweb_role_permission_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_restriction (
|
||||
role_id int unsigned NOT NULL,
|
||||
restriction varchar(254) NOT NULL,
|
||||
filter text NOT NULL,
|
||||
|
||||
PRIMARY KEY (role_id, restriction),
|
||||
CONSTRAINT fk_icingaweb_role_restriction_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
INSERT INTO icingaweb_schema (version, timestamp, success, reason)
|
||||
VALUES('2.13.0', UNIX_TIMESTAMP() * 1000, 'y', NULL);
|
@ -31,6 +31,61 @@ CREATE TABLE `icingaweb_user`(
|
||||
PRIMARY KEY (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE icingaweb_role (
|
||||
id int unsigned NOT NULL AUTO_INCREMENT,
|
||||
parent_id int unsigned DEFAULT NULL,
|
||||
name varchar(254) NOT NULL,
|
||||
unrestricted enum('n', 'y') NOT NULL DEFAULT 'n',
|
||||
ctime bigint unsigned NOT NULL,
|
||||
mtime bigint unsigned DEFAULT NULL,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
CONSTRAINT fk_icingaweb_role_parent_id FOREIGN KEY (parent_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE SET NULL,
|
||||
CONSTRAINT idx_icingaweb_role_name UNIQUE (name)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_user (
|
||||
role_id int unsigned NOT NULL,
|
||||
user_name varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
|
||||
PRIMARY KEY (role_id, user_name),
|
||||
INDEX idx_icingaweb_role_user_user_name (user_name),
|
||||
CONSTRAINT fk_icingaweb_role_user_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_group (
|
||||
role_id int unsigned NOT NULL,
|
||||
group_name varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
|
||||
PRIMARY KEY (role_id, group_name),
|
||||
INDEX idx_icingaweb_role_group_group_name (group_name),
|
||||
CONSTRAINT fk_icingaweb_role_group_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_permission (
|
||||
role_id int unsigned NOT NULL,
|
||||
permission varchar(254) NOT NULL,
|
||||
allowed enum('n', 'y') NOT NULL DEFAULT 'n',
|
||||
denied enum('n', 'y') NOT NULL DEFAULT 'n',
|
||||
|
||||
PRIMARY KEY (role_id, permission),
|
||||
CONSTRAINT fk_icingaweb_role_permission_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE icingaweb_role_restriction (
|
||||
role_id int unsigned NOT NULL,
|
||||
restriction varchar(254) NOT NULL,
|
||||
filter text NOT NULL,
|
||||
|
||||
PRIMARY KEY (role_id, restriction),
|
||||
CONSTRAINT fk_icingaweb_role_restriction_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
|
||||
|
||||
CREATE TABLE `icingaweb_user_preference`(
|
||||
`username` varchar(254) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`section` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
@ -65,4 +120,4 @@ CREATE TABLE icingaweb_schema (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=DYNAMIC;
|
||||
|
||||
INSERT INTO icingaweb_schema (version, timestamp, success)
|
||||
VALUES ('2.12.0', UNIX_TIMESTAMP() * 1000, 'y');
|
||||
VALUES ('2.13.0', UNIX_TIMESTAMP() * 1000, 'y');
|
||||
|
59
schema/pgsql-upgrades/2.13.0.sql
Normal file
59
schema/pgsql-upgrades/2.13.0.sql
Normal file
@ -0,0 +1,59 @@
|
||||
CREATE TABLE icingaweb_role (
|
||||
id serial,
|
||||
parent_id int DEFAULT NULL,
|
||||
name varchar(254) NOT NULL,
|
||||
unrestricted boolenum NOT NULL DEFAULT 'n',
|
||||
ctime bigint NOT NULL,
|
||||
mtime bigint DEFAULT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role PRIMARY KEY (id),
|
||||
CONSTRAINT fk_icingaweb_role_parent_id FOREIGN KEY (parent_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE SET NULL,
|
||||
CONSTRAINT idx_icingaweb_role_name UNIQUE (name)
|
||||
);
|
||||
|
||||
CREATE TABLE icingaweb_role_user (
|
||||
role_id int NOT NULL,
|
||||
user_name citext NOT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_user PRIMARY KEY (role_id, user_name),
|
||||
CONSTRAINT fk_icingaweb_role_user_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_icingaweb_role_user_user_name ON icingaweb_role_user(user_name);
|
||||
|
||||
CREATE TABLE icingaweb_role_group (
|
||||
role_id int NOT NULL,
|
||||
group_name citext NOT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_group PRIMARY KEY (role_id, group_name),
|
||||
CONSTRAINT fk_icingaweb_role_group_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_icingaweb_role_group_group_name ON icingaweb_role_group(group_name);
|
||||
|
||||
CREATE TABLE icingaweb_role_permission (
|
||||
role_id int NOT NULL,
|
||||
permission varchar(254) NOT NULL,
|
||||
allowed boolenum NOT NULL DEFAULT 'n',
|
||||
denied boolenum NOT NULL DEFAULT 'n',
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_permission PRIMARY KEY (role_id, permission),
|
||||
CONSTRAINT fk_icingaweb_role_permission_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE icingaweb_role_restriction (
|
||||
role_id int NOT NULL,
|
||||
restriction varchar(254) NOT NULL,
|
||||
filter text NOT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_restriction PRIMARY KEY (role_id, restriction),
|
||||
CONSTRAINT fk_icingaweb_role_restriction_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO icingaweb_schema (version, timestamp, success, reason)
|
||||
VALUES('2.13.0', EXTRACT(EPOCH FROM now()) * 1000, 'y', NULL);
|
@ -1,5 +1,7 @@
|
||||
/* Icinga Web 2 | (c) 2014 Icinga GmbH | GPLv2+ */
|
||||
|
||||
CREATE TYPE boolenum AS ENUM ('n', 'y');
|
||||
|
||||
CREATE OR REPLACE FUNCTION unix_timestamp(timestamp with time zone) RETURNS bigint AS '
|
||||
SELECT EXTRACT(EPOCH FROM $1)::bigint AS result
|
||||
' LANGUAGE sql;
|
||||
@ -76,6 +78,63 @@ CREATE UNIQUE INDEX idx_icingaweb_user
|
||||
lower((name)::text)
|
||||
);
|
||||
|
||||
CREATE TABLE icingaweb_role (
|
||||
id serial,
|
||||
parent_id int DEFAULT NULL,
|
||||
name varchar(254) NOT NULL,
|
||||
unrestricted boolenum NOT NULL DEFAULT 'n',
|
||||
ctime bigint NOT NULL,
|
||||
mtime bigint DEFAULT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role PRIMARY KEY (id),
|
||||
CONSTRAINT fk_icingaweb_role_parent_id FOREIGN KEY (parent_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE SET NULL,
|
||||
CONSTRAINT idx_icingaweb_role_name UNIQUE (name)
|
||||
);
|
||||
|
||||
CREATE TABLE icingaweb_role_user (
|
||||
role_id int NOT NULL,
|
||||
user_name citext NOT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_user PRIMARY KEY (role_id, user_name),
|
||||
CONSTRAINT fk_icingaweb_role_user_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_icingaweb_role_user_user_name ON icingaweb_role_user(user_name);
|
||||
|
||||
CREATE TABLE icingaweb_role_group (
|
||||
role_id int NOT NULL,
|
||||
group_name citext NOT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_group PRIMARY KEY (role_id, group_name),
|
||||
CONSTRAINT fk_icingaweb_role_group_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_icingaweb_role_group_group_name ON icingaweb_role_group(group_name);
|
||||
|
||||
CREATE TABLE icingaweb_role_permission (
|
||||
role_id int NOT NULL,
|
||||
permission varchar(254) NOT NULL,
|
||||
allowed boolenum NOT NULL DEFAULT 'n',
|
||||
denied boolenum NOT NULL DEFAULT 'n',
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_permission PRIMARY KEY (role_id, permission),
|
||||
CONSTRAINT fk_icingaweb_role_permission_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE icingaweb_role_restriction (
|
||||
role_id int NOT NULL,
|
||||
restriction varchar(254) NOT NULL,
|
||||
filter text NOT NULL,
|
||||
|
||||
CONSTRAINT pk_icingaweb_role_restriction PRIMARY KEY (role_id, restriction),
|
||||
CONSTRAINT fk_icingaweb_role_restriction_role_id FOREIGN KEY (role_id)
|
||||
REFERENCES icingaweb_role (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE "icingaweb_user_preference" (
|
||||
"username" character varying(254) NOT NULL,
|
||||
"name" character varying(64) NOT NULL,
|
||||
@ -118,8 +177,6 @@ ALTER TABLE ONLY "icingaweb_rememberme"
|
||||
"id"
|
||||
);
|
||||
|
||||
CREATE TYPE boolenum AS ENUM ('n', 'y');
|
||||
|
||||
CREATE TABLE "icingaweb_schema" (
|
||||
"id" serial,
|
||||
"version" varchar(64) NOT NULL,
|
||||
@ -132,4 +189,4 @@ CREATE TABLE "icingaweb_schema" (
|
||||
);
|
||||
|
||||
INSERT INTO icingaweb_schema (version, timestamp, success)
|
||||
VALUES ('2.12.0', extract(epoch from now()) * 1000, 'y');
|
||||
VALUES ('2.13.0', extract(epoch from now()) * 1000, 'y');
|
||||
|
Loading…
x
Reference in New Issue
Block a user